blob: ae440353f7bb120a0226e6ab8fc77f199a6cb5b7 [file] [log] [blame]
Georg Brandl6634bf22008-05-20 07:13:37 +00001from Tkinter import *
Florent Xiclunad630c042010-04-02 07:24:52 +00002
3from idlelib import SearchEngine
4from idlelib.SearchDialogBase import SearchDialogBase
Andrew Svetlov8ba844a2012-08-04 21:45:23 +03005import re
6
David Scherer7aced172000-08-15 01:13:23 +00007
8def replace(text):
9 root = text._root()
10 engine = SearchEngine.get(root)
11 if not hasattr(engine, "_replacedialog"):
12 engine._replacedialog = ReplaceDialog(root, engine)
13 dialog = engine._replacedialog
14 dialog.open(text)
15
Andrew Svetlov8ba844a2012-08-04 21:45:23 +030016
David Scherer7aced172000-08-15 01:13:23 +000017class ReplaceDialog(SearchDialogBase):
18
19 title = "Replace Dialog"
20 icon = "Replace"
21
22 def __init__(self, root, engine):
23 SearchDialogBase.__init__(self, root, engine)
24 self.replvar = StringVar(root)
25
26 def open(self, text):
27 SearchDialogBase.open(self, text)
28 try:
29 first = text.index("sel.first")
30 except TclError:
31 first = None
32 try:
33 last = text.index("sel.last")
34 except TclError:
35 last = None
36 first = first or text.index("insert")
37 last = last or first
38 self.show_hit(first, last)
39 self.ok = 1
40
41 def create_entries(self):
42 SearchDialogBase.create_entries(self)
43 self.replent = self.make_entry("Replace with:", self.replvar)
44
45 def create_command_buttons(self):
46 SearchDialogBase.create_command_buttons(self)
47 self.make_button("Find", self.find_it)
48 self.make_button("Replace", self.replace_it)
49 self.make_button("Replace+Find", self.default_command, 1)
50 self.make_button("Replace All", self.replace_all)
51
52 def find_it(self, event=None):
53 self.do_find(0)
54
55 def replace_it(self, event=None):
56 if self.do_find(self.ok):
57 self.do_replace()
58
59 def default_command(self, event=None):
60 if self.do_find(self.ok):
Andrew Svetlov8ba844a2012-08-04 21:45:23 +030061 if self.do_replace(): # Only find next match if replace succeeded.
62 # A bad re can cause a it to fail.
63 self.do_find(0)
64
65 def _replace_expand(self, m, repl):
66 """ Helper function for expanding a regular expression
67 in the replace field, if needed. """
68 if self.engine.isre():
69 try:
70 new = m.expand(repl)
71 except re.error:
72 self.engine.report_error(repl, 'Invalid Replace Expression')
73 new = None
74 else:
75 new = repl
76 return new
David Scherer7aced172000-08-15 01:13:23 +000077
78 def replace_all(self, event=None):
79 prog = self.engine.getprog()
80 if not prog:
81 return
82 repl = self.replvar.get()
83 text = self.text
84 res = self.engine.search_text(text, prog)
85 if not res:
86 text.bell()
87 return
88 text.tag_remove("sel", "1.0", "end")
89 text.tag_remove("hit", "1.0", "end")
90 line = res[0]
91 col = res[1].start()
92 if self.engine.iswrap():
93 line = 1
94 col = 0
95 ok = 1
96 first = last = None
97 # XXX ought to replace circular instead of top-to-bottom when wrapping
98 text.undo_block_start()
99 while 1:
100 res = self.engine.search_forward(text, prog, line, col, 0, ok)
101 if not res:
102 break
103 line, m = res
104 chars = text.get("%d.0" % line, "%d.0" % (line+1))
105 orig = m.group()
Andrew Svetlov8ba844a2012-08-04 21:45:23 +0300106 new = self._replace_expand(m, repl)
107 if new is None:
108 break
David Scherer7aced172000-08-15 01:13:23 +0000109 i, j = m.span()
110 first = "%d.%d" % (line, i)
111 last = "%d.%d" % (line, j)
112 if new == orig:
113 text.mark_set("insert", last)
114 else:
115 text.mark_set("insert", first)
116 if first != last:
117 text.delete(first, last)
118 if new:
119 text.insert(first, new)
120 col = i + len(new)
121 ok = 0
122 text.undo_block_stop()
123 if first and last:
124 self.show_hit(first, last)
David Scherer7aced172000-08-15 01:13:23 +0000125
126 def do_find(self, ok=0):
127 if not self.engine.getprog():
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000128 return False
David Scherer7aced172000-08-15 01:13:23 +0000129 text = self.text
130 res = self.engine.search_text(text, None, ok)
131 if not res:
132 text.bell()
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000133 return False
David Scherer7aced172000-08-15 01:13:23 +0000134 line, m = res
135 i, j = m.span()
136 first = "%d.%d" % (line, i)
137 last = "%d.%d" % (line, j)
138 self.show_hit(first, last)
139 self.ok = 1
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000140 return True
David Scherer7aced172000-08-15 01:13:23 +0000141
142 def do_replace(self):
143 prog = self.engine.getprog()
144 if not prog:
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000145 return False
David Scherer7aced172000-08-15 01:13:23 +0000146 text = self.text
147 try:
148 first = pos = text.index("sel.first")
149 last = text.index("sel.last")
150 except TclError:
151 pos = None
152 if not pos:
153 first = last = pos = text.index("insert")
154 line, col = SearchEngine.get_line_col(pos)
155 chars = text.get("%d.0" % line, "%d.0" % (line+1))
156 m = prog.match(chars, col)
157 if not prog:
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000158 return False
Andrew Svetlov8ba844a2012-08-04 21:45:23 +0300159 new = self._replace_expand(m, self.replvar.get())
160 if new is None:
161 return False
David Scherer7aced172000-08-15 01:13:23 +0000162 text.mark_set("insert", first)
163 text.undo_block_start()
164 if m.group():
165 text.delete(first, last)
166 if new:
167 text.insert(first, new)
168 text.undo_block_stop()
169 self.show_hit(first, text.index("insert"))
170 self.ok = 0
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000171 return True
David Scherer7aced172000-08-15 01:13:23 +0000172
David Scherer7aced172000-08-15 01:13:23 +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")