17db96d56Sopenharmony_ci"""Editor window that can serve as an output file. 27db96d56Sopenharmony_ci""" 37db96d56Sopenharmony_ci 47db96d56Sopenharmony_ciimport re 57db96d56Sopenharmony_ci 67db96d56Sopenharmony_cifrom tkinter import messagebox 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_cifrom idlelib.editor import EditorWindow 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_cifile_line_pats = [ 127db96d56Sopenharmony_ci # order of patterns matters 137db96d56Sopenharmony_ci r'file "([^"]*)", line (\d+)', 147db96d56Sopenharmony_ci r'([^\s]+)\((\d+)\)', 157db96d56Sopenharmony_ci r'^(\s*\S.*?):\s*(\d+):', # Win filename, maybe starting with spaces 167db96d56Sopenharmony_ci r'([^\s]+):\s*(\d+):', # filename or path, ltrim 177db96d56Sopenharmony_ci r'^\s*(\S.*?):\s*(\d+):', # Win abs path with embedded spaces, ltrim 187db96d56Sopenharmony_ci] 197db96d56Sopenharmony_ci 207db96d56Sopenharmony_cifile_line_progs = None 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_cidef compile_progs(): 247db96d56Sopenharmony_ci "Compile the patterns for matching to file name and line number." 257db96d56Sopenharmony_ci global file_line_progs 267db96d56Sopenharmony_ci file_line_progs = [re.compile(pat, re.IGNORECASE) 277db96d56Sopenharmony_ci for pat in file_line_pats] 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_cidef file_line_helper(line): 317db96d56Sopenharmony_ci """Extract file name and line number from line of text. 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_ci Check if line of text contains one of the file/line patterns. 347db96d56Sopenharmony_ci If it does and if the file and line are valid, return 357db96d56Sopenharmony_ci a tuple of the file name and line number. If it doesn't match 367db96d56Sopenharmony_ci or if the file or line is invalid, return None. 377db96d56Sopenharmony_ci """ 387db96d56Sopenharmony_ci if not file_line_progs: 397db96d56Sopenharmony_ci compile_progs() 407db96d56Sopenharmony_ci for prog in file_line_progs: 417db96d56Sopenharmony_ci match = prog.search(line) 427db96d56Sopenharmony_ci if match: 437db96d56Sopenharmony_ci filename, lineno = match.group(1, 2) 447db96d56Sopenharmony_ci try: 457db96d56Sopenharmony_ci f = open(filename) 467db96d56Sopenharmony_ci f.close() 477db96d56Sopenharmony_ci break 487db96d56Sopenharmony_ci except OSError: 497db96d56Sopenharmony_ci continue 507db96d56Sopenharmony_ci else: 517db96d56Sopenharmony_ci return None 527db96d56Sopenharmony_ci try: 537db96d56Sopenharmony_ci return filename, int(lineno) 547db96d56Sopenharmony_ci except TypeError: 557db96d56Sopenharmony_ci return None 567db96d56Sopenharmony_ci 577db96d56Sopenharmony_ci 587db96d56Sopenharmony_ciclass OutputWindow(EditorWindow): 597db96d56Sopenharmony_ci """An editor window that can serve as an output file. 607db96d56Sopenharmony_ci 617db96d56Sopenharmony_ci Also the future base class for the Python shell window. 627db96d56Sopenharmony_ci This class has no input facilities. 637db96d56Sopenharmony_ci 647db96d56Sopenharmony_ci Adds binding to open a file at a line to the text widget. 657db96d56Sopenharmony_ci """ 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ci # Our own right-button menu 687db96d56Sopenharmony_ci rmenu_specs = [ 697db96d56Sopenharmony_ci ("Cut", "<<cut>>", "rmenu_check_cut"), 707db96d56Sopenharmony_ci ("Copy", "<<copy>>", "rmenu_check_copy"), 717db96d56Sopenharmony_ci ("Paste", "<<paste>>", "rmenu_check_paste"), 727db96d56Sopenharmony_ci (None, None, None), 737db96d56Sopenharmony_ci ("Go to file/line", "<<goto-file-line>>", None), 747db96d56Sopenharmony_ci ] 757db96d56Sopenharmony_ci 767db96d56Sopenharmony_ci allow_code_context = False 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci def __init__(self, *args): 797db96d56Sopenharmony_ci EditorWindow.__init__(self, *args) 807db96d56Sopenharmony_ci self.text.bind("<<goto-file-line>>", self.goto_file_line) 817db96d56Sopenharmony_ci 827db96d56Sopenharmony_ci # Customize EditorWindow 837db96d56Sopenharmony_ci def ispythonsource(self, filename): 847db96d56Sopenharmony_ci "Python source is only part of output: do not colorize." 857db96d56Sopenharmony_ci return False 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci def short_title(self): 887db96d56Sopenharmony_ci "Customize EditorWindow title." 897db96d56Sopenharmony_ci return "Output" 907db96d56Sopenharmony_ci 917db96d56Sopenharmony_ci def maybesave(self): 927db96d56Sopenharmony_ci "Customize EditorWindow to not display save file messagebox." 937db96d56Sopenharmony_ci return 'yes' if self.get_saved() else 'no' 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci # Act as output file 967db96d56Sopenharmony_ci def write(self, s, tags=(), mark="insert"): 977db96d56Sopenharmony_ci """Write text to text widget. 987db96d56Sopenharmony_ci 997db96d56Sopenharmony_ci The text is inserted at the given index with the provided 1007db96d56Sopenharmony_ci tags. The text widget is then scrolled to make it visible 1017db96d56Sopenharmony_ci and updated to display it, giving the effect of seeing each 1027db96d56Sopenharmony_ci line as it is added. 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci Args: 1057db96d56Sopenharmony_ci s: Text to insert into text widget. 1067db96d56Sopenharmony_ci tags: Tuple of tag strings to apply on the insert. 1077db96d56Sopenharmony_ci mark: Index for the insert. 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci Return: 1107db96d56Sopenharmony_ci Length of text inserted. 1117db96d56Sopenharmony_ci """ 1127db96d56Sopenharmony_ci assert isinstance(s, str) 1137db96d56Sopenharmony_ci self.text.insert(mark, s, tags) 1147db96d56Sopenharmony_ci self.text.see(mark) 1157db96d56Sopenharmony_ci self.text.update_idletasks() 1167db96d56Sopenharmony_ci return len(s) 1177db96d56Sopenharmony_ci 1187db96d56Sopenharmony_ci def writelines(self, lines): 1197db96d56Sopenharmony_ci "Write each item in lines iterable." 1207db96d56Sopenharmony_ci for line in lines: 1217db96d56Sopenharmony_ci self.write(line) 1227db96d56Sopenharmony_ci 1237db96d56Sopenharmony_ci def flush(self): 1247db96d56Sopenharmony_ci "No flushing needed as write() directly writes to widget." 1257db96d56Sopenharmony_ci pass 1267db96d56Sopenharmony_ci 1277db96d56Sopenharmony_ci def showerror(self, *args, **kwargs): 1287db96d56Sopenharmony_ci messagebox.showerror(*args, **kwargs) 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_ci def goto_file_line(self, event=None): 1317db96d56Sopenharmony_ci """Handle request to open file/line. 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci If the selected or previous line in the output window 1347db96d56Sopenharmony_ci contains a file name and line number, then open that file 1357db96d56Sopenharmony_ci name in a new window and position on the line number. 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci Otherwise, display an error messagebox. 1387db96d56Sopenharmony_ci """ 1397db96d56Sopenharmony_ci line = self.text.get("insert linestart", "insert lineend") 1407db96d56Sopenharmony_ci result = file_line_helper(line) 1417db96d56Sopenharmony_ci if not result: 1427db96d56Sopenharmony_ci # Try the previous line. This is handy e.g. in tracebacks, 1437db96d56Sopenharmony_ci # where you tend to right-click on the displayed source line 1447db96d56Sopenharmony_ci line = self.text.get("insert -1line linestart", 1457db96d56Sopenharmony_ci "insert -1line lineend") 1467db96d56Sopenharmony_ci result = file_line_helper(line) 1477db96d56Sopenharmony_ci if not result: 1487db96d56Sopenharmony_ci self.showerror( 1497db96d56Sopenharmony_ci "No special line", 1507db96d56Sopenharmony_ci "The line you point at doesn't look like " 1517db96d56Sopenharmony_ci "a valid file name followed by a line number.", 1527db96d56Sopenharmony_ci parent=self.text) 1537db96d56Sopenharmony_ci return 1547db96d56Sopenharmony_ci filename, lineno = result 1557db96d56Sopenharmony_ci self.flist.gotofileline(filename, lineno) 1567db96d56Sopenharmony_ci 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_ci# These classes are currently not used but might come in handy 1597db96d56Sopenharmony_ciclass OnDemandOutputWindow: 1607db96d56Sopenharmony_ci 1617db96d56Sopenharmony_ci tagdefs = { 1627db96d56Sopenharmony_ci # XXX Should use IdlePrefs.ColorPrefs 1637db96d56Sopenharmony_ci "stdout": {"foreground": "blue"}, 1647db96d56Sopenharmony_ci "stderr": {"foreground": "#007700"}, 1657db96d56Sopenharmony_ci } 1667db96d56Sopenharmony_ci 1677db96d56Sopenharmony_ci def __init__(self, flist): 1687db96d56Sopenharmony_ci self.flist = flist 1697db96d56Sopenharmony_ci self.owin = None 1707db96d56Sopenharmony_ci 1717db96d56Sopenharmony_ci def write(self, s, tags, mark): 1727db96d56Sopenharmony_ci if not self.owin: 1737db96d56Sopenharmony_ci self.setup() 1747db96d56Sopenharmony_ci self.owin.write(s, tags, mark) 1757db96d56Sopenharmony_ci 1767db96d56Sopenharmony_ci def setup(self): 1777db96d56Sopenharmony_ci self.owin = owin = OutputWindow(self.flist) 1787db96d56Sopenharmony_ci text = owin.text 1797db96d56Sopenharmony_ci for tag, cnf in self.tagdefs.items(): 1807db96d56Sopenharmony_ci if cnf: 1817db96d56Sopenharmony_ci text.tag_configure(tag, **cnf) 1827db96d56Sopenharmony_ci text.tag_raise('sel') 1837db96d56Sopenharmony_ci self.write = self.owin.write 1847db96d56Sopenharmony_ci 1857db96d56Sopenharmony_ciif __name__ == '__main__': 1867db96d56Sopenharmony_ci from unittest import main 1877db96d56Sopenharmony_ci main('idlelib.idle_test.test_outwin', verbosity=2, exit=False) 188