bpo-23216: IDLE: Add docstrings to search modules (GH-12141)


diff --git a/Lib/idlelib/replace.py b/Lib/idlelib/replace.py
index 4a834eb..6be034a 100644
--- a/Lib/idlelib/replace.py
+++ b/Lib/idlelib/replace.py
@@ -1,7 +1,7 @@
 """Replace dialog for IDLE. Inherits SearchDialogBase for GUI.
-Uses idlelib.SearchEngine for search capability.
+Uses idlelib.searchengine.SearchEngine for search capability.
 Defines various replace related functions like replace, replace all,
-replace+find.
+and replace+find.
 """
 import re
 
@@ -10,9 +10,16 @@
 from idlelib.searchbase import SearchDialogBase
 from idlelib import searchengine
 
+
 def replace(text):
-    """Returns a singleton ReplaceDialog instance.The single dialog
-     saves user entries and preferences across instances."""
+    """Create or reuse a singleton ReplaceDialog instance.
+
+    The singleton dialog saves user entries and preferences
+    across instances.
+
+    Args:
+        text: Text widget containing the text to be searched.
+    """
     root = text._root()
     engine = searchengine.get(root)
     if not hasattr(engine, "_replacedialog"):
@@ -22,16 +29,36 @@
 
 
 class ReplaceDialog(SearchDialogBase):
+    "Dialog for finding and replacing a pattern in text."
 
     title = "Replace Dialog"
     icon = "Replace"
 
     def __init__(self, root, engine):
-        SearchDialogBase.__init__(self, root, engine)
+        """Create search dialog for finding and replacing text.
+
+        Uses SearchDialogBase as the basis for the GUI and a
+        searchengine instance to prepare the search.
+
+        Attributes:
+            replvar: StringVar containing 'Replace with:' value.
+            replent: Entry widget for replvar.  Created in
+                create_entries().
+            ok: Boolean used in searchengine.search_text to indicate
+                whether the search includes the selection.
+        """
+        super().__init__(root, engine)
         self.replvar = StringVar(root)
 
     def open(self, text):
-        """Display the replace dialog"""
+        """Make dialog visible on top of others and ready to use.
+
+        Also, highlight the currently selected text and set the
+        search to include the current selection (self.ok).
+
+        Args:
+            text: Text widget being searched.
+        """
         SearchDialogBase.open(self, text)
         try:
             first = text.index("sel.first")
@@ -44,37 +71,50 @@
         first = first or text.index("insert")
         last = last or first
         self.show_hit(first, last)
-        self.ok = 1
+        self.ok = True
 
     def create_entries(self):
-        """Create label and text entry widgets"""
+        "Create base and additional label and text entry widgets."
         SearchDialogBase.create_entries(self)
         self.replent = self.make_entry("Replace with:", self.replvar)[0]
 
     def create_command_buttons(self):
+        """Create base and additional command buttons.
+
+        The additional buttons are for Find, Replace,
+        Replace+Find, and Replace All.
+        """
         SearchDialogBase.create_command_buttons(self)
         self.make_button("Find", self.find_it)
         self.make_button("Replace", self.replace_it)
-        self.make_button("Replace+Find", self.default_command, 1)
+        self.make_button("Replace+Find", self.default_command, isdef=True)
         self.make_button("Replace All", self.replace_all)
 
     def find_it(self, event=None):
-        self.do_find(0)
+        "Handle the Find button."
+        self.do_find(False)
 
     def replace_it(self, event=None):
+        """Handle the Replace button.
+
+        If the find is successful, then perform replace.
+        """
         if self.do_find(self.ok):
             self.do_replace()
 
     def default_command(self, event=None):
-        "Replace and find next."
+        """Handle the Replace+Find button as the default command.
+
+        First performs a replace and then, if the replace was
+        successful, a find next.
+        """
         if self.do_find(self.ok):
             if self.do_replace():  # Only find next match if replace succeeded.
                                    # A bad re can cause it to fail.
-                self.do_find(0)
+                self.do_find(False)
 
     def _replace_expand(self, m, repl):
-        """ Helper function for expanding a regular expression
-            in the replace field, if needed. """
+        "Expand replacement text if regular expression."
         if self.engine.isre():
             try:
                 new = m.expand(repl)
@@ -87,7 +127,15 @@
         return new
 
     def replace_all(self, event=None):
-        """Replace all instances of patvar with replvar in text"""
+        """Handle the Replace All button.
+
+        Search text for occurrences of the Find value and replace
+        each of them.  The 'wrap around' value controls the start
+        point for searching.  If wrap isn't set, then the searching
+        starts at the first occurrence after the current selection;
+        if wrap is set, the replacement starts at the first line.
+        The replacement is always done top-to-bottom in the text.
+        """
         prog = self.engine.getprog()
         if not prog:
             return
@@ -104,12 +152,13 @@
         if self.engine.iswrap():
             line = 1
             col = 0
-        ok = 1
+        ok = True
         first = last = None
         # XXX ought to replace circular instead of top-to-bottom when wrapping
         text.undo_block_start()
-        while 1:
-            res = self.engine.search_forward(text, prog, line, col, 0, ok)
+        while True:
+            res = self.engine.search_forward(text, prog, line, col,
+                                             wrap=False, ok=ok)
             if not res:
                 break
             line, m = res
@@ -130,13 +179,17 @@
                 if new:
                     text.insert(first, new)
             col = i + len(new)
-            ok = 0
+            ok = False
         text.undo_block_stop()
         if first and last:
             self.show_hit(first, last)
         self.close()
 
-    def do_find(self, ok=0):
+    def do_find(self, ok=False):
+        """Search for and highlight next occurrence of pattern in text.
+
+        No text replacement is done with this option.
+        """
         if not self.engine.getprog():
             return False
         text = self.text
@@ -149,10 +202,11 @@
         first = "%d.%d" % (line, i)
         last = "%d.%d" % (line, j)
         self.show_hit(first, last)
-        self.ok = 1
+        self.ok = True
         return True
 
     def do_replace(self):
+        "Replace search pattern in text with replacement value."
         prog = self.engine.getprog()
         if not prog:
             return False
@@ -180,12 +234,20 @@
             text.insert(first, new)
         text.undo_block_stop()
         self.show_hit(first, text.index("insert"))
-        self.ok = 0
+        self.ok = False
         return True
 
     def show_hit(self, first, last):
-        """Highlight text from 'first' to 'last'.
-        'first', 'last' - Text indices"""
+        """Highlight text between first and last indices.
+
+        Text is highlighted via the 'hit' tag and the marked
+        section is brought into view.
+
+        The colors from the 'hit' tag aren't currently shown
+        when the text is displayed.  This is due to the 'sel'
+        tag being added first, so the colors in the 'sel'
+        config are seen instead of the colors for 'hit'.
+        """
         text = self.text
         text.mark_set("insert", first)
         text.tag_remove("sel", "1.0", "end")
@@ -199,6 +261,7 @@
         text.update_idletasks()
 
     def close(self, event=None):
+        "Close the dialog and remove hit tags."
         SearchDialogBase.close(self, event)
         self.text.tag_remove("hit", "1.0", "end")