blob: 83462f9a1f79c8a9e1df4aeef743098e756d4323 [file] [log] [blame]
Guido van Rossum504b0bf1999-01-02 21:28:54 +00001import string
2import os
3import re
4import fnmatch
5from Tkinter import *
6import tkMessageBox
7import SearchEngine
8from SearchDialogBase import SearchDialogBase
9
10def replace(text):
11 root = text._root()
12 engine = SearchEngine.get(root)
13 if not hasattr(engine, "_replacedialog"):
14 engine._replacedialog = ReplaceDialog(root, engine)
15 dialog = engine._replacedialog
16 dialog.open(text)
17
18class ReplaceDialog(SearchDialogBase):
19
20 title = "Replace Dialog"
21 icon = "Replace"
22
23 def __init__(self, root, engine):
24 SearchDialogBase.__init__(self, root, engine)
25 self.replvar = StringVar(root)
26
27 def open(self, text):
28 SearchDialogBase.open(self, text)
29 try:
30 first = text.index("sel.first")
31 except TclError:
32 first = None
33 try:
34 last = text.index("sel.last")
35 except TclError:
36 last = None
37 first = first or text.index("insert")
38 last = last or first
39 self.show_hit(first, last)
40 self.ok = 1
41
42 def create_entries(self):
43 SearchDialogBase.create_entries(self)
44 self.replent = self.make_entry("Replace with:", self.replvar)
45
46 def create_command_buttons(self):
47 SearchDialogBase.create_command_buttons(self)
48 self.make_button("Find", self.find_it)
49 self.make_button("Replace", self.replace_it)
50 self.make_button("Replace+Find", self.default_command, 1)
51 self.make_button("Replace All", self.replace_all)
52
53 def find_it(self, event=None):
54 self.do_find(0)
55
56 def replace_it(self, event=None):
57 if self.do_find(self.ok):
58 self.do_replace()
59
60 def default_command(self, event=None):
61 if self.do_find(self.ok):
62 self.do_replace()
63 self.do_find(0)
64
65 def replace_all(self, event=None):
66 prog = self.engine.getprog()
67 if not prog:
68 return
69 repl = self.replvar.get()
70 text = self.text
71 res = self.engine.search_text(text, prog)
72 if not res:
73 text.bell()
74 return
75 text.tag_remove("sel", "1.0", "end")
76 text.tag_remove("hit", "1.0", "end")
77 line = res[0]
78 col = res[1].start()
79 if self.engine.iswrap():
80 line = 1
81 col = 0
82 ok = 1
83 first = last = None
84 # XXX ought to replace circular instead of top-to-bottom when wrapping
Guido van Rossumab6a08a1999-06-08 13:06:07 +000085 text.undo_block_start()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000086 while 1:
87 res = self.engine.search_forward(text, prog, line, col, 0, ok)
88 if not res:
89 break
90 line, m = res
91 chars = text.get("%d.0" % line, "%d.0" % (line+1))
92 orig = m.group()
Guido van Rossumf8d07132000-09-19 20:51:17 +000093 new = self._expand(m, repl)
Guido van Rossum504b0bf1999-01-02 21:28:54 +000094 i, j = m.span()
95 first = "%d.%d" % (line, i)
96 last = "%d.%d" % (line, j)
97 if new == orig:
98 text.mark_set("insert", last)
99 else:
100 text.mark_set("insert", first)
101 if first != last:
102 text.delete(first, last)
103 if new:
104 text.insert(first, new)
105 col = i + len(new)
106 ok = 0
Guido van Rossumab6a08a1999-06-08 13:06:07 +0000107 text.undo_block_stop()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000108 if first and last:
109 self.show_hit(first, last)
110 self.close()
111
112 def do_find(self, ok=0):
113 if not self.engine.getprog():
114 return 0
115 text = self.text
116 res = self.engine.search_text(text, None, ok)
117 if not res:
118 text.bell()
119 return 0
120 line, m = res
121 i, j = m.span()
122 first = "%d.%d" % (line, i)
123 last = "%d.%d" % (line, j)
124 self.show_hit(first, last)
125 self.ok = 1
126 return 1
127
128 def do_replace(self):
129 prog = self.engine.getprog()
130 if not prog:
131 return 0
132 text = self.text
133 try:
134 first = pos = text.index("sel.first")
135 last = text.index("sel.last")
136 except TclError:
137 pos = None
138 if not pos:
139 first = last = pos = text.index("insert")
140 line, col = SearchEngine.get_line_col(pos)
141 chars = text.get("%d.0" % line, "%d.0" % (line+1))
142 m = prog.match(chars, col)
143 if not prog:
144 return 0
Guido van Rossumf8d07132000-09-19 20:51:17 +0000145 new = self._expand(m, self.replvar.get())
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000146 text.mark_set("insert", first)
Guido van Rossum9745f5a1999-06-08 12:54:56 +0000147 text.undo_block_start()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000148 if m.group():
149 text.delete(first, last)
150 if new:
151 text.insert(first, new)
Guido van Rossum9745f5a1999-06-08 12:54:56 +0000152 text.undo_block_stop()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000153 self.show_hit(first, text.index("insert"))
154 self.ok = 0
155 return 1
156
Guido van Rossumf8d07132000-09-19 20:51:17 +0000157 def _expand(self, m, template):
158 # XXX This code depends on internals of the regular expression
159 # engine! There's no standard API to do a substitution when you
160 # have already found the match. One should be added.
161 # The solution here is designed to be backwards compatible
162 # with previous Python versions, e.g. 1.5.2.
163 # XXX This dynamic test should be done only once.
164 if getattr(re, "engine", "pre") == "pre":
165 return re.pcre_expand(m, template)
166 else: # sre
167 # XXX This import should be avoidable...
168 import sre_parse
169 # XXX This parses the template over and over...
170 ptemplate = sre_parse.parse_template(template, m.re)
171 return sre_parse.expand_template(ptemplate, m)
172
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000173 def show_hit(self, first, last):
174 text = self.text
175 text.mark_set("insert", first)
176 text.tag_remove("sel", "1.0", "end")
177 text.tag_add("sel", first, last)
178 text.tag_remove("hit", "1.0", "end")
179 if first == last:
180 text.tag_add("hit", first)
181 else:
182 text.tag_add("hit", first, last)
183 text.see("insert")
184 text.update_idletasks()
185
186 def close(self, event=None):
187 SearchDialogBase.close(self, event)
188 self.text.tag_remove("hit", "1.0", "end")