1
1
"""Replace dialog for IDLE. Inherits SearchDialogBase for GUI.
2
- Uses idlelib.SearchEngine for search capability.
2
+ Uses idlelib.searchengine. SearchEngine for search capability.
3
3
Defines various replace related functions like replace, replace all,
4
- replace+find.
4
+ and replace+find.
5
5
"""
6
6
import re
7
7
10
10
from idlelib .searchbase import SearchDialogBase
11
11
from idlelib import searchengine
12
12
13
+
13
14
def replace (text ):
14
- """Returns a singleton ReplaceDialog instance.The single dialog
15
- saves user entries and preferences across instances."""
15
+ """Create or reuse a singleton ReplaceDialog instance.
16
+
17
+ The singleton dialog saves user entries and preferences
18
+ across instances.
19
+
20
+ Args:
21
+ text: Text widget containing the text to be searched.
22
+ """
16
23
root = text ._root ()
17
24
engine = searchengine .get (root )
18
25
if not hasattr (engine , "_replacedialog" ):
@@ -22,16 +29,36 @@ def replace(text):
22
29
23
30
24
31
class ReplaceDialog (SearchDialogBase ):
32
+ "Dialog for finding and replacing a pattern in text."
25
33
26
34
title = "Replace Dialog"
27
35
icon = "Replace"
28
36
29
37
def __init__ (self , root , engine ):
30
- SearchDialogBase .__init__ (self , root , engine )
38
+ """Create search dialog for finding and replacing text.
39
+
40
+ Uses SearchDialogBase as the basis for the GUI and a
41
+ searchengine instance to prepare the search.
42
+
43
+ Attributes:
44
+ replvar: StringVar containing 'Replace with:' value.
45
+ replent: Entry widget for replvar. Created in
46
+ create_entries().
47
+ ok: Boolean used in searchengine.search_text to indicate
48
+ whether the search includes the selection.
49
+ """
50
+ super ().__init__ (root , engine )
31
51
self .replvar = StringVar (root )
32
52
33
53
def open (self , text ):
34
- """Display the replace dialog"""
54
+ """Make dialog visible on top of others and ready to use.
55
+
56
+ Also, highlight the currently selected text and set the
57
+ search to include the current selection (self.ok).
58
+
59
+ Args:
60
+ text: Text widget being searched.
61
+ """
35
62
SearchDialogBase .open (self , text )
36
63
try :
37
64
first = text .index ("sel.first" )
@@ -44,37 +71,50 @@ def open(self, text):
44
71
first = first or text .index ("insert" )
45
72
last = last or first
46
73
self .show_hit (first , last )
47
- self .ok = 1
74
+ self .ok = True
48
75
49
76
def create_entries (self ):
50
- """ Create label and text entry widgets"" "
77
+ "Create base and additional label and text entry widgets. "
51
78
SearchDialogBase .create_entries (self )
52
79
self .replent = self .make_entry ("Replace with:" , self .replvar )[0 ]
53
80
54
81
def create_command_buttons (self ):
82
+ """Create base and additional command buttons.
83
+
84
+ The additional buttons are for Find, Replace,
85
+ Replace+Find, and Replace All.
86
+ """
55
87
SearchDialogBase .create_command_buttons (self )
56
88
self .make_button ("Find" , self .find_it )
57
89
self .make_button ("Replace" , self .replace_it )
58
- self .make_button ("Replace+Find" , self .default_command , 1 )
90
+ self .make_button ("Replace+Find" , self .default_command , isdef = True )
59
91
self .make_button ("Replace All" , self .replace_all )
60
92
61
93
def find_it (self , event = None ):
62
- self .do_find (0 )
94
+ "Handle the Find button."
95
+ self .do_find (False )
63
96
64
97
def replace_it (self , event = None ):
98
+ """Handle the Replace button.
99
+
100
+ If the find is successful, then perform replace.
101
+ """
65
102
if self .do_find (self .ok ):
66
103
self .do_replace ()
67
104
68
105
def default_command (self , event = None ):
69
- "Replace and find next."
106
+ """Handle the Replace+Find button as the default command.
107
+
108
+ First performs a replace and then, if the replace was
109
+ successful, a find next.
110
+ """
70
111
if self .do_find (self .ok ):
71
112
if self .do_replace (): # Only find next match if replace succeeded.
72
113
# A bad re can cause it to fail.
73
- self .do_find (0 )
114
+ self .do_find (False )
74
115
75
116
def _replace_expand (self , m , repl ):
76
- """ Helper function for expanding a regular expression
77
- in the replace field, if needed. """
117
+ "Expand replacement text if regular expression."
78
118
if self .engine .isre ():
79
119
try :
80
120
new = m .expand (repl )
@@ -87,7 +127,15 @@ def _replace_expand(self, m, repl):
87
127
return new
88
128
89
129
def replace_all (self , event = None ):
90
- """Replace all instances of patvar with replvar in text"""
130
+ """Handle the Replace All button.
131
+
132
+ Search text for occurrences of the Find value and replace
133
+ each of them. The 'wrap around' value controls the start
134
+ point for searching. If wrap isn't set, then the searching
135
+ starts at the first occurrence after the current selection;
136
+ if wrap is set, the replacement starts at the first line.
137
+ The replacement is always done top-to-bottom in the text.
138
+ """
91
139
prog = self .engine .getprog ()
92
140
if not prog :
93
141
return
@@ -104,12 +152,13 @@ def replace_all(self, event=None):
104
152
if self .engine .iswrap ():
105
153
line = 1
106
154
col = 0
107
- ok = 1
155
+ ok = True
108
156
first = last = None
109
157
# XXX ought to replace circular instead of top-to-bottom when wrapping
110
158
text .undo_block_start ()
111
- while 1 :
112
- res = self .engine .search_forward (text , prog , line , col , 0 , ok )
159
+ while True :
160
+ res = self .engine .search_forward (text , prog , line , col ,
161
+ wrap = False , ok = ok )
113
162
if not res :
114
163
break
115
164
line , m = res
@@ -130,13 +179,17 @@ def replace_all(self, event=None):
130
179
if new :
131
180
text .insert (first , new )
132
181
col = i + len (new )
133
- ok = 0
182
+ ok = False
134
183
text .undo_block_stop ()
135
184
if first and last :
136
185
self .show_hit (first , last )
137
186
self .close ()
138
187
139
- def do_find (self , ok = 0 ):
188
+ def do_find (self , ok = False ):
189
+ """Search for and highlight next occurrence of pattern in text.
190
+
191
+ No text replacement is done with this option.
192
+ """
140
193
if not self .engine .getprog ():
141
194
return False
142
195
text = self .text
@@ -149,10 +202,11 @@ def do_find(self, ok=0):
149
202
first = "%d.%d" % (line , i )
150
203
last = "%d.%d" % (line , j )
151
204
self .show_hit (first , last )
152
- self .ok = 1
205
+ self .ok = True
153
206
return True
154
207
155
208
def do_replace (self ):
209
+ "Replace search pattern in text with replacement value."
156
210
prog = self .engine .getprog ()
157
211
if not prog :
158
212
return False
@@ -180,12 +234,20 @@ def do_replace(self):
180
234
text .insert (first , new )
181
235
text .undo_block_stop ()
182
236
self .show_hit (first , text .index ("insert" ))
183
- self .ok = 0
237
+ self .ok = False
184
238
return True
185
239
186
240
def show_hit (self , first , last ):
187
- """Highlight text from 'first' to 'last'.
188
- 'first', 'last' - Text indices"""
241
+ """Highlight text between first and last indices.
242
+
243
+ Text is highlighted via the 'hit' tag and the marked
244
+ section is brought into view.
245
+
246
+ The colors from the 'hit' tag aren't currently shown
247
+ when the text is displayed. This is due to the 'sel'
248
+ tag being added first, so the colors in the 'sel'
249
+ config are seen instead of the colors for 'hit'.
250
+ """
189
251
text = self .text
190
252
text .mark_set ("insert" , first )
191
253
text .tag_remove ("sel" , "1.0" , "end" )
@@ -199,6 +261,7 @@ def show_hit(self, first, last):
199
261
text .update_idletasks ()
200
262
201
263
def close (self , event = None ):
264
+ "Close the dialog and remove hit tags."
202
265
SearchDialogBase .close (self , event )
203
266
self .text .tag_remove ("hit" , "1.0" , "end" )
204
267
0 commit comments