bpo-23216: IDLE: Add docstrings to search modules (GH-12141) · python/cpython@b34f1aa
11"""Replace dialog for IDLE. Inherits SearchDialogBase for GUI.
2-Uses idlelib.SearchEngine for search capability.
2+Uses idlelib.searchengine.SearchEngine for search capability.
33Defines various replace related functions like replace, replace all,
4-replace+find.
4+and replace+find.
55"""
66import re
77@@ -10,9 +10,16 @@
1010from idlelib.searchbase import SearchDialogBase
1111from idlelib import searchengine
121213+1314def 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+ """
1623root = text._root()
1724engine = searchengine.get(root)
1825if not hasattr(engine, "_replacedialog"):
@@ -22,16 +29,36 @@ def replace(text):
222923302431class ReplaceDialog(SearchDialogBase):
32+"Dialog for finding and replacing a pattern in text."
25332634title = "Replace Dialog"
2735icon = "Replace"
28362937def __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)
3151self.replvar = StringVar(root)
32523353def 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+ """
3562SearchDialogBase.open(self, text)
3663try:
3764first = text.index("sel.first")
@@ -44,37 +71,50 @@ def open(self, text):
4471first = first or text.index("insert")
4572last = last or first
4673self.show_hit(first, last)
47-self.ok = 1
74+self.ok = True
48754976def create_entries(self):
50-"""Create label and text entry widgets"""
77+"Create base and additional label and text entry widgets."
5178SearchDialogBase.create_entries(self)
5279self.replent = self.make_entry("Replace with:", self.replvar)[0]
53805481def 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+ """
5587SearchDialogBase.create_command_buttons(self)
5688self.make_button("Find", self.find_it)
5789self.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)
5991self.make_button("Replace All", self.replace_all)
60926193def find_it(self, event=None):
62-self.do_find(0)
94+"Handle the Find button."
95+self.do_find(False)
63966497def replace_it(self, event=None):
98+"""Handle the Replace button.
99+100+ If the find is successful, then perform replace.
101+ """
65102if self.do_find(self.ok):
66103self.do_replace()
6710468105def 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+ """
70111if self.do_find(self.ok):
71112if self.do_replace(): # Only find next match if replace succeeded.
72113# A bad re can cause it to fail.
73-self.do_find(0)
114+self.do_find(False)
7411575116def _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."
78118if self.engine.isre():
79119try:
80120new = m.expand(repl)
@@ -87,7 +127,15 @@ def _replace_expand(self, m, repl):
87127return new
8812889129def 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+ """
91139prog = self.engine.getprog()
92140if not prog:
93141return
@@ -104,12 +152,13 @@ def replace_all(self, event=None):
104152if self.engine.iswrap():
105153line = 1
106154col = 0
107-ok = 1
155+ok = True
108156first = last = None
109157# XXX ought to replace circular instead of top-to-bottom when wrapping
110158text.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)
113162if not res:
114163break
115164line, m = res
@@ -130,13 +179,17 @@ def replace_all(self, event=None):
130179if new:
131180text.insert(first, new)
132181col = i + len(new)
133-ok = 0
182+ok = False
134183text.undo_block_stop()
135184if first and last:
136185self.show_hit(first, last)
137186self.close()
138187139-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+ """
140193if not self.engine.getprog():
141194return False
142195text = self.text
@@ -149,10 +202,11 @@ def do_find(self, ok=0):
149202first = "%d.%d" % (line, i)
150203last = "%d.%d" % (line, j)
151204self.show_hit(first, last)
152-self.ok = 1
205+self.ok = True
153206return True
154207155208def do_replace(self):
209+"Replace search pattern in text with replacement value."
156210prog = self.engine.getprog()
157211if not prog:
158212return False
@@ -180,12 +234,20 @@ def do_replace(self):
180234text.insert(first, new)
181235text.undo_block_stop()
182236self.show_hit(first, text.index("insert"))
183-self.ok = 0
237+self.ok = False
184238return True
185239186240def 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+ """
189251text = self.text
190252text.mark_set("insert", first)
191253text.tag_remove("sel", "1.0", "end")
@@ -199,6 +261,7 @@ def show_hit(self, first, last):
199261text.update_idletasks()
200262201263def close(self, event=None):
264+"Close the dialog and remove hit tags."
202265SearchDialogBase.close(self, event)
203266self.text.tag_remove("hit", "1.0", "end")
204267