from idlelib import searchengine
# Importing OutputWindow here fails due to import loop
-# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow
+# EditorWindow -> GrepDialog -> OutputWindow -> EditorWindow
def grep(text, io=None, flist=None):
- """Create or find singleton GrepDialog instance.
+ """Open the Find in Files dialog.
+
+ Module-level function to access the singleton GrepDialog
+ instance and open the dialog. If text is selected, it is
+ used as the search phrase; otherwise, the previous entry
+ is used.
Args:
text: Text widget that contains the selected text for
io: iomenu.IOBinding instance with default path to search.
flist: filelist.FileList instance for OutputWindow parent.
"""
-
root = text._root()
engine = searchengine.get(root)
if not hasattr(engine, "_grepdialog"):
searchengine instance to prepare the search.
Attributes:
- globvar: Value of Text Entry widget for path to search.
- recvar: Boolean value of Checkbutton widget
- for traversing through subdirectories.
+ flist: filelist.Filelist instance for OutputWindow parent.
+ globvar: String value of Entry widget for path to search.
+ globent: Entry widget for globvar. Created in
+ create_entries().
+ recvar: Boolean value of Checkbutton widget for
+ traversing through subdirectories.
"""
- SearchDialogBase.__init__(self, root, engine)
+ super().__init__(root, engine)
self.flist = flist
self.globvar = StringVar(root)
self.recvar = BooleanVar(root)
def open(self, text, searchphrase, io=None):
- "Make dialog visible on top of others and ready to use."
+ """Make dialog visible on top of others and ready to use.
+
+ Extend the SearchDialogBase open() to set the initial value
+ for globvar.
+
+ Args:
+ text: Multicall object containing the text information.
+ searchphrase: String phrase to search.
+ io: iomenu.IOBinding instance containing file path.
+ """
SearchDialogBase.open(self, text, searchphrase)
if io:
path = io.filename or ""
btn.pack(side="top", fill="both")
def create_command_buttons(self):
- "Create base command buttons and add button for search."
+ "Create base command buttons and add button for Search Files."
SearchDialogBase.create_command_buttons(self)
- self.make_button("Search Files", self.default_command, 1)
+ self.make_button("Search Files", self.default_command, isdef=True)
def default_command(self, event=None):
"""Grep for search pattern in file path. The default command is bound
search each line for the matching pattern. If the pattern is
found, write the file and line information to stdout (which
is an OutputWindow).
+
+ Args:
+ prog: The compiled, cooked search pattern.
+ path: String containing the search path.
"""
dir, base = os.path.split(path)
list = self.findfiles(dir, base, self.recvar.get())
def findfiles(self, dir, base, rec):
"""Return list of files in the dir that match the base pattern.
+ Use the current directory if dir has no value.
If rec is True, recursively iterate through subdirectories.
+
+ Args:
+ dir: Directory path to search.
+ base: File search pattern.
+ rec: Boolean for recursive search through subdirectories.
"""
try:
names = os.listdir(dir or os.curdir)
"""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
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"):
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")
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)
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
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
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
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
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")
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")
+"""Search dialog for Find, Find Again, and Find Selection
+ functionality.
+
+ Inherits from SearchDialogBase for GUI and uses searchengine
+ to prepare search pattern.
+"""
from tkinter import TclError
from idlelib import searchengine
from idlelib.searchbase import SearchDialogBase
def _setup(text):
- "Create or find the singleton SearchDialog instance."
+ """Return the new or existing singleton SearchDialog 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, "_searchdialog"):
return engine._searchdialog
def find(text):
- "Handle the editor edit menu item and corresponding event."
+ """Open the search dialog.
+
+ Module-level function to access the singleton SearchDialog
+ instance and open the dialog. If text is selected, it is
+ used as the search phrase; otherwise, the previous entry
+ is used. No search is done with this command.
+ """
pat = text.get("sel.first", "sel.last")
return _setup(text).open(text, pat) # Open is inherited from SDBase.
def find_again(text):
- "Handle the editor edit menu item and corresponding event."
+ """Repeat the search for the last pattern and preferences.
+
+ Module-level function to access the singleton SearchDialog
+ instance to search again using the user entries and preferences
+ from the last dialog. If there was no prior search, open the
+ search dialog; otherwise, perform the search without showing the
+ dialog.
+ """
return _setup(text).find_again(text)
def find_selection(text):
- "Handle the editor edit menu item and corresponding event."
+ """Search for the selected pattern in the text.
+
+ Module-level function to access the singleton SearchDialog
+ instance to search using the selected text. With a text
+ selection, perform the search without displaying the dialog.
+ Without a selection, use the prior entry as the search phrase
+ and don't display the dialog. If there has been no prior
+ search, open the search dialog.
+ """
return _setup(text).find_selection(text)
class SearchDialog(SearchDialogBase):
+ "Dialog for finding a pattern in text."
def create_widgets(self):
+ "Create the base search dialog and add a button for Find Next."
SearchDialogBase.create_widgets(self)
- self.make_button("Find Next", self.default_command, 1)
+ # TODO - why is this here and not in a create_command_buttons?
+ self.make_button("Find Next", self.default_command, isdef=True)
def default_command(self, event=None):
+ "Handle the Find Next button as the default command."
if not self.engine.getprog():
return
self.find_again(self.text)
def find_again(self, text):
+ """Repeat the last search.
+
+ If no search was previously run, open a new search dialog. In
+ this case, no search is done.
+
+ If a seach was previously run, the search dialog won't be
+ shown and the options from the previous search (including the
+ search pattern) will be used to find the next occurrence
+ of the pattern. Next is relative based on direction.
+
+ Position the window to display the located occurrence in the
+ text.
+
+ Return True if the search was successful and False otherwise.
+ """
if not self.engine.getpat():
self.open(text)
return False
return False
def find_selection(self, text):
+ """Search for selected text with previous dialog preferences.
+
+ Instead of using the same pattern for searching (as Find
+ Again does), this first resets the pattern to the currently
+ selected text. If the selected text isn't changed, then use
+ the prior search phrase.
+ """
pat = text.get("sel.first", "sel.last")
if pat:
self.engine.setcookedpat(pat)
--- /dev/null
+Add docstrings to IDLE search modules.