| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  | """Grep dialog for Find in Files functionality.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    Inherits from SearchDialogBase for GUI and uses searchengine | 
					
						
							|  |  |  |    to prepare search pattern. | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | import fnmatch | 
					
						
							| 
									
										
										
										
											2016-08-31 00:50:55 -04:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2016-08-31 00:50:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-10 20:21:31 -04:00
										 |  |  | from tkinter import StringVar, BooleanVar | 
					
						
							| 
									
										
										
										
											2019-01-02 22:04:06 -05:00
										 |  |  | from tkinter.ttk import Checkbutton  # Frame imported in ...Base | 
					
						
							| 
									
										
										
										
											2016-08-31 00:50:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-28 13:22:31 -04:00
										 |  |  | from idlelib.searchbase import SearchDialogBase | 
					
						
							| 
									
										
										
										
											2016-08-31 00:50:55 -04:00
										 |  |  | from idlelib import searchengine | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Importing OutputWindow here fails due to import loop | 
					
						
							| 
									
										
										
										
											2019-03-16 19:29:33 -04:00
										 |  |  | # EditorWindow -> GrepDialog -> OutputWindow -> EditorWindow | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | def grep(text, io=None, flist=None): | 
					
						
							| 
									
										
										
										
											2019-03-16 19:29:33 -04:00
										 |  |  |     """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. | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |         text: Text widget that contains the selected text for | 
					
						
							|  |  |  |               default search phrase. | 
					
						
							|  |  |  |         io: iomenu.IOBinding instance with default path to search. | 
					
						
							|  |  |  |         flist: filelist.FileList instance for OutputWindow parent. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |     root = text._root() | 
					
						
							| 
									
										
										
										
											2016-05-28 13:22:31 -04:00
										 |  |  |     engine = searchengine.get(root) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |     if not hasattr(engine, "_grepdialog"): | 
					
						
							|  |  |  |         engine._grepdialog = GrepDialog(root, engine, flist) | 
					
						
							|  |  |  |     dialog = engine._grepdialog | 
					
						
							| 
									
										
										
										
											2003-06-07 03:21:17 +00:00
										 |  |  |     searchphrase = text.get("sel.first", "sel.last") | 
					
						
							|  |  |  |     dialog.open(text, searchphrase, io) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-23 07:33:42 -04:00
										 |  |  | def walk_error(msg): | 
					
						
							|  |  |  |     "Handle os.walk error." | 
					
						
							|  |  |  |     print(msg) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def findfiles(folder, pattern, recursive): | 
					
						
							|  |  |  |     """Generate file names in dir that match pattern.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Args: | 
					
						
							|  |  |  |         folder: Root directory to search. | 
					
						
							|  |  |  |         pattern: File pattern to match. | 
					
						
							|  |  |  |         recursive: True to include subdirectories. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     for dirpath, _, filenames in os.walk(folder, onerror=walk_error): | 
					
						
							|  |  |  |         yield from (os.path.join(dirpath, name) | 
					
						
							|  |  |  |                     for name in filenames | 
					
						
							|  |  |  |                     if fnmatch.fnmatch(name, pattern)) | 
					
						
							|  |  |  |         if not recursive: | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | class GrepDialog(SearchDialogBase): | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |     "Dialog for searching multiple files." | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     title = "Find in Files Dialog" | 
					
						
							|  |  |  |     icon = "Grep" | 
					
						
							|  |  |  |     needwrapbutton = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, root, engine, flist): | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |         """Create search dialog for searching for a phrase in the file system.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Uses SearchDialogBase as the basis for the GUI and a | 
					
						
							|  |  |  |         searchengine instance to prepare the search. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Attributes: | 
					
						
							| 
									
										
										
										
											2019-03-16 19:29:33 -04:00
										 |  |  |             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. | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2019-03-16 19:29:33 -04:00
										 |  |  |         super().__init__(root, engine) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         self.flist = flist | 
					
						
							|  |  |  |         self.globvar = StringVar(root) | 
					
						
							|  |  |  |         self.recvar = BooleanVar(root) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-06-07 03:21:17 +00:00
										 |  |  |     def open(self, text, searchphrase, io=None): | 
					
						
							| 
									
										
										
										
											2019-03-16 19:29:33 -04:00
										 |  |  |         """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. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2003-06-07 03:21:17 +00:00
										 |  |  |         SearchDialogBase.open(self, text, searchphrase) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         if io: | 
					
						
							|  |  |  |             path = io.filename or "" | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             path = "" | 
					
						
							|  |  |  |         dir, base = os.path.split(path) | 
					
						
							|  |  |  |         head, tail = os.path.splitext(base) | 
					
						
							|  |  |  |         if not tail: | 
					
						
							|  |  |  |             tail = ".py" | 
					
						
							|  |  |  |         self.globvar.set(os.path.join(dir, "*" + tail)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_entries(self): | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |         "Create base entry widgets and add widget for search path." | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         SearchDialogBase.create_entries(self) | 
					
						
							| 
									
										
										
										
											2014-07-13 17:27:26 -04:00
										 |  |  |         self.globent = self.make_entry("In files:", self.globvar)[0] | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def create_other_buttons(self): | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |         "Add check button to recurse down subdirectories." | 
					
						
							| 
									
										
										
										
											2016-07-10 20:21:31 -04:00
										 |  |  |         btn = Checkbutton( | 
					
						
							|  |  |  |                 self.make_frame()[0], variable=self.recvar, | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |                 text="Recurse down subdirectories") | 
					
						
							|  |  |  |         btn.pack(side="top", fill="both") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_command_buttons(self): | 
					
						
							| 
									
										
										
										
											2019-03-16 19:29:33 -04:00
										 |  |  |         "Create base command buttons and add button for Search Files." | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         SearchDialogBase.create_command_buttons(self) | 
					
						
							| 
									
										
										
										
											2019-03-16 19:29:33 -04:00
										 |  |  |         self.make_button("Search Files", self.default_command, isdef=True) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def default_command(self, event=None): | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |         """Grep for search pattern in file path. The default command is bound
 | 
					
						
							|  |  |  |         to <Return>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If entry values are populated, set OutputWindow as stdout | 
					
						
							|  |  |  |         and perform search.  The search dialog is closed automatically | 
					
						
							|  |  |  |         when the search begins. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         prog = self.engine.getprog() | 
					
						
							|  |  |  |         if not prog: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         path = self.globvar.get() | 
					
						
							|  |  |  |         if not path: | 
					
						
							|  |  |  |             self.top.bell() | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2016-05-28 13:22:31 -04:00
										 |  |  |         from idlelib.outwin import OutputWindow  # leave here! | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         save = sys.stdout | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             sys.stdout = OutputWindow(self.flist) | 
					
						
							|  |  |  |             self.grep_it(prog, path) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             sys.stdout = save | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def grep_it(self, prog, path): | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |         """Search for prog within the lines of the files in path.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         For the each file in the path directory, open the file and | 
					
						
							|  |  |  |         search each line for the matching pattern.  If the pattern is | 
					
						
							|  |  |  |         found,  write the file and line information to stdout (which | 
					
						
							|  |  |  |         is an OutputWindow). | 
					
						
							| 
									
										
										
										
											2019-03-16 19:29:33 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							|  |  |  |             prog: The compiled, cooked search pattern. | 
					
						
							|  |  |  |             path: String containing the search path. | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2019-03-23 07:33:42 -04:00
										 |  |  |         folder, filepat = os.path.split(path) | 
					
						
							|  |  |  |         if not folder: | 
					
						
							|  |  |  |             folder = os.curdir | 
					
						
							|  |  |  |         filelist = sorted(findfiles(folder, filepat, self.recvar.get())) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         self.close() | 
					
						
							|  |  |  |         pat = self.engine.getpat() | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |         print(f"Searching {pat!r} in {path} ...") | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         hits = 0 | 
					
						
							| 
									
										
										
										
											2014-06-10 02:49:35 -04:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2019-03-23 07:33:42 -04:00
										 |  |  |             for fn in filelist: | 
					
						
							| 
									
										
										
										
											2014-06-10 02:49:35 -04:00
										 |  |  |                 try: | 
					
						
							|  |  |  |                     with open(fn, errors='replace') as f: | 
					
						
							|  |  |  |                         for lineno, line in enumerate(f, 1): | 
					
						
							|  |  |  |                             if line[-1:] == '\n': | 
					
						
							|  |  |  |                                 line = line[:-1] | 
					
						
							|  |  |  |                             if prog.search(line): | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |                                 sys.stdout.write(f"{fn}: {lineno}: {line}\n") | 
					
						
							| 
									
										
										
										
											2014-06-10 02:49:35 -04:00
										 |  |  |                                 hits += 1 | 
					
						
							|  |  |  |                 except OSError as msg: | 
					
						
							|  |  |  |                     print(msg) | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |             print(f"Hits found: {hits}\n(Hint: right-click to open locations.)" | 
					
						
							|  |  |  |                   if hits else "No hits.") | 
					
						
							| 
									
										
										
										
											2014-06-10 02:49:35 -04:00
										 |  |  |         except AttributeError: | 
					
						
							|  |  |  |             # Tk window has been closed, OutputWindow.text = None, | 
					
						
							|  |  |  |             # so in OW.write, OW.text.insert fails. | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-10 02:49:35 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-17 01:31:35 -04:00
										 |  |  | def _grep_dialog(parent):  # htest # | 
					
						
							| 
									
										
										
										
											2024-06-12 20:56:42 +02:00
										 |  |  |     from tkinter import Toplevel, Text, SEL | 
					
						
							| 
									
										
										
										
											2019-01-02 22:04:06 -05:00
										 |  |  |     from tkinter.ttk import Frame, Button | 
					
						
							| 
									
										
										
										
											2016-08-31 00:50:55 -04:00
										 |  |  |     from idlelib.pyshell import PyShellFileList | 
					
						
							| 
									
										
										
										
											2019-01-02 22:04:06 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-21 18:41:38 -04:00
										 |  |  |     top = Toplevel(parent) | 
					
						
							|  |  |  |     top.title("Test GrepDialog") | 
					
						
							| 
									
										
										
										
											2016-07-10 17:28:10 -04:00
										 |  |  |     x, y = map(int, parent.geometry().split('+')[1:]) | 
					
						
							| 
									
										
										
										
											2017-06-27 02:41:08 -04:00
										 |  |  |     top.geometry(f"+{x}+{y + 175}") | 
					
						
							| 
									
										
										
										
											2014-05-29 01:46:26 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-21 18:41:38 -04:00
										 |  |  |     flist = PyShellFileList(top) | 
					
						
							| 
									
										
										
										
											2019-01-02 22:04:06 -05:00
										 |  |  |     frame = Frame(top) | 
					
						
							|  |  |  |     frame.pack() | 
					
						
							|  |  |  |     text = Text(frame, height=5) | 
					
						
							| 
									
										
										
										
											2014-05-29 01:46:26 -04:00
										 |  |  |     text.pack() | 
					
						
							| 
									
										
										
										
											2023-12-04 01:36:40 -05:00
										 |  |  |     text.insert('1.0', 'import grep') | 
					
						
							| 
									
										
										
										
											2014-05-29 01:46:26 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def show_grep_dialog(): | 
					
						
							| 
									
										
										
										
											2023-12-04 01:36:40 -05:00
										 |  |  |         text.tag_add(SEL, "1.0", '1.end') | 
					
						
							| 
									
										
										
										
											2014-05-29 01:46:26 -04:00
										 |  |  |         grep(text, flist=flist) | 
					
						
							| 
									
										
										
										
											2023-12-04 01:36:40 -05:00
										 |  |  |         text.tag_remove(SEL, "1.0", '1.end') | 
					
						
							| 
									
										
										
										
											2014-05-29 01:46:26 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-02 22:04:06 -05:00
										 |  |  |     button = Button(frame, text="Show GrepDialog", command=show_grep_dialog) | 
					
						
							| 
									
										
										
										
											2014-05-29 01:46:26 -04:00
										 |  |  |     button.pack() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-04 01:36:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-22 18:26:51 -04:00
										 |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2018-06-15 18:20:55 -04:00
										 |  |  |     from unittest import main | 
					
						
							|  |  |  |     main('idlelib.idle_test.test_grep', verbosity=2, exit=False) | 
					
						
							| 
									
										
										
										
											2014-05-29 01:46:26 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     from idlelib.idle_test.htest import run | 
					
						
							|  |  |  |     run(_grep_dialog) |