| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  | """Editor window that can serve as an output file.
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2016-08-31 00:50:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  | from tkinter import messagebox | 
					
						
							| 
									
										
										
										
											2016-08-31 00:50:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | from idlelib.editor import EditorWindow | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-31 00:50:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  | file_line_pats = [ | 
					
						
							|  |  |  |     # order of patterns matters | 
					
						
							|  |  |  |     r'file "([^"]*)", line (\d+)', | 
					
						
							|  |  |  |     r'([^\s]+)\((\d+)\)', | 
					
						
							|  |  |  |     r'^(\s*\S.*?):\s*(\d+):',  # Win filename, maybe starting with spaces | 
					
						
							|  |  |  |     r'([^\s]+):\s*(\d+):',     # filename or path, ltrim | 
					
						
							|  |  |  |     r'^\s*(\S.*?):\s*(\d+):',  # Win abs path with embedded spaces, ltrim | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | file_line_progs = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def compile_progs(): | 
					
						
							|  |  |  |     "Compile the patterns for matching to file name and line number." | 
					
						
							|  |  |  |     global file_line_progs | 
					
						
							|  |  |  |     file_line_progs = [re.compile(pat, re.IGNORECASE) | 
					
						
							|  |  |  |                        for pat in file_line_pats] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  | def file_line_helper(line): | 
					
						
							|  |  |  |     """Extract file name and line number from line of text.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Check if line of text contains one of the file/line patterns. | 
					
						
							|  |  |  |     If it does and if the file and line are valid, return | 
					
						
							|  |  |  |     a tuple of the file name and line number.  If it doesn't match | 
					
						
							|  |  |  |     or if the file or line is invalid, return None. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not file_line_progs: | 
					
						
							|  |  |  |         compile_progs() | 
					
						
							|  |  |  |     for prog in file_line_progs: | 
					
						
							|  |  |  |         match = prog.search(line) | 
					
						
							|  |  |  |         if match: | 
					
						
							|  |  |  |             filename, lineno = match.group(1, 2) | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2023-04-24 02:25:08 +03:00
										 |  |  |                 f = open(filename) | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |                 f.close() | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             except OSError: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         return filename, int(lineno) | 
					
						
							|  |  |  |     except TypeError: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class OutputWindow(EditorWindow): | 
					
						
							| 
									
										
										
										
											2002-06-12 03:28:57 +00:00
										 |  |  |     """An editor window that can serve as an output file.
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-12 03:28:57 +00:00
										 |  |  |     Also the future base class for the Python shell window. | 
					
						
							|  |  |  |     This class has no input facilities. | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Adds binding to open a file at a line to the text widget. | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |     # Our own right-button menu | 
					
						
							|  |  |  |     rmenu_specs = [ | 
					
						
							|  |  |  |         ("Cut", "<<cut>>", "rmenu_check_cut"), | 
					
						
							|  |  |  |         ("Copy", "<<copy>>", "rmenu_check_copy"), | 
					
						
							|  |  |  |         ("Paste", "<<paste>>", "rmenu_check_paste"), | 
					
						
							|  |  |  |         (None, None, None), | 
					
						
							|  |  |  |         ("Go to file/line", "<<goto-file-line>>", None), | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |     allow_code_context = False | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-12 03:28:57 +00:00
										 |  |  |     def __init__(self, *args): | 
					
						
							| 
									
										
										
										
											2003-07-09 18:48:24 +00:00
										 |  |  |         EditorWindow.__init__(self, *args) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         self.text.bind("<<goto-file-line>>", self.goto_file_line) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Customize EditorWindow | 
					
						
							|  |  |  |     def ispythonsource(self, filename): | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         "Python source is only part of output: do not colorize." | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def short_title(self): | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         "Customize EditorWindow title." | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         return "Output" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def maybesave(self): | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         "Customize EditorWindow to not display save file messagebox." | 
					
						
							|  |  |  |         return 'yes' if self.get_saved() else 'no' | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Act as output file | 
					
						
							| 
									
										
										
										
											2002-06-12 03:28:57 +00:00
										 |  |  |     def write(self, s, tags=(), mark="insert"): | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         """Write text to text widget.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The text is inserted at the given index with the provided | 
					
						
							|  |  |  |         tags.  The text widget is then scrolled to make it visible | 
					
						
							|  |  |  |         and updated to display it, giving the effect of seeing each | 
					
						
							|  |  |  |         line as it is added. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Args: | 
					
						
							|  |  |  |             s: Text to insert into text widget. | 
					
						
							|  |  |  |             tags: Tuple of tag strings to apply on the insert. | 
					
						
							|  |  |  |             mark: Index for the insert. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Return: | 
					
						
							|  |  |  |             Length of text inserted. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-06-30 03:18:22 +03:00
										 |  |  |         assert isinstance(s, str) | 
					
						
							| 
									
										
										
										
											2002-09-02 21:29:40 +00:00
										 |  |  |         self.text.insert(mark, s, tags) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         self.text.see(mark) | 
					
						
							| 
									
										
										
										
											2023-04-30 21:36:27 -04:00
										 |  |  |         self.text.update_idletasks() | 
					
						
							| 
									
										
										
										
											2012-07-25 11:32:26 +02:00
										 |  |  |         return len(s) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-09 18:00:23 +00:00
										 |  |  |     def writelines(self, lines): | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         "Write each item in lines iterable." | 
					
						
							| 
									
										
										
										
											2007-08-09 18:00:23 +00:00
										 |  |  |         for line in lines: | 
					
						
							|  |  |  |             self.write(line) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def flush(self): | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         "No flushing needed as write() directly writes to widget." | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |     def showerror(self, *args, **kwargs): | 
					
						
							|  |  |  |         messagebox.showerror(*args, **kwargs) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |     def goto_file_line(self, event=None): | 
					
						
							|  |  |  |         """Handle request to open file/line.
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         If the selected or previous line in the output window | 
					
						
							|  |  |  |         contains a file name and line number, then open that file | 
					
						
							|  |  |  |         name in a new window and position on the line number. | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         Otherwise, display an error messagebox. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         line = self.text.get("insert linestart", "insert lineend") | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         result = file_line_helper(line) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         if not result: | 
					
						
							|  |  |  |             # Try the previous line.  This is handy e.g. in tracebacks, | 
					
						
							|  |  |  |             # where you tend to right-click on the displayed source line | 
					
						
							|  |  |  |             line = self.text.get("insert -1line linestart", | 
					
						
							|  |  |  |                                  "insert -1line lineend") | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |             result = file_line_helper(line) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |             if not result: | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |                 self.showerror( | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |                     "No special line", | 
					
						
							|  |  |  |                     "The line you point at doesn't look like " | 
					
						
							|  |  |  |                     "a valid file name followed by a line number.", | 
					
						
							| 
									
										
										
										
											2015-09-25 22:22:55 -04:00
										 |  |  |                     parent=self.text) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |                 return | 
					
						
							|  |  |  |         filename, lineno = result | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  |         self.flist.gotofileline(filename, lineno) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  | # These classes are currently not used but might come in handy | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  | class OnDemandOutputWindow: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tagdefs = { | 
					
						
							|  |  |  |         # XXX Should use IdlePrefs.ColorPrefs | 
					
						
							|  |  |  |         "stdout":  {"foreground": "blue"}, | 
					
						
							| 
									
										
										
										
											2002-06-12 03:28:57 +00:00
										 |  |  |         "stderr":  {"foreground": "#007700"}, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |     def __init__(self, flist): | 
					
						
							|  |  |  |         self.flist = flist | 
					
						
							|  |  |  |         self.owin = None | 
					
						
							| 
									
										
										
										
											2002-06-12 03:28:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def write(self, s, tags, mark): | 
					
						
							|  |  |  |         if not self.owin: | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |             self.setup() | 
					
						
							|  |  |  |         self.owin.write(s, tags, mark) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setup(self): | 
					
						
							| 
									
										
										
										
											2002-06-12 03:28:57 +00:00
										 |  |  |         self.owin = owin = OutputWindow(self.flist) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         text = owin.text | 
					
						
							|  |  |  |         for tag, cnf in self.tagdefs.items(): | 
					
						
							|  |  |  |             if cnf: | 
					
						
							| 
									
										
										
										
											2003-07-09 18:48:24 +00:00
										 |  |  |                 text.tag_configure(tag, **cnf) | 
					
						
							| 
									
										
										
										
											2000-08-15 01:13:23 +00:00
										 |  |  |         text.tag_raise('sel') | 
					
						
							| 
									
										
										
										
											2002-06-12 03:28:57 +00:00
										 |  |  |         self.write = self.owin.write | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-04 01:36:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 18:06:00 -04:00
										 |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2018-06-19 19:12:52 -04:00
										 |  |  |     from unittest import main | 
					
						
							|  |  |  |     main('idlelib.idle_test.test_outwin', verbosity=2, exit=False) |