mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			187 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""Editor window that can serve as an output file.
 | 
						|
"""
 | 
						|
 | 
						|
import re
 | 
						|
 | 
						|
from tkinter import messagebox
 | 
						|
 | 
						|
from idlelib.editor import EditorWindow
 | 
						|
 | 
						|
 | 
						|
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]
 | 
						|
 | 
						|
 | 
						|
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:
 | 
						|
                f = open(filename, "r")
 | 
						|
                f.close()
 | 
						|
                break
 | 
						|
            except OSError:
 | 
						|
                continue
 | 
						|
    else:
 | 
						|
        return None
 | 
						|
    try:
 | 
						|
        return filename, int(lineno)
 | 
						|
    except TypeError:
 | 
						|
        return None
 | 
						|
 | 
						|
 | 
						|
class OutputWindow(EditorWindow):
 | 
						|
    """An editor window that can serve as an output file.
 | 
						|
 | 
						|
    Also the future base class for the Python shell window.
 | 
						|
    This class has no input facilities.
 | 
						|
 | 
						|
    Adds binding to open a file at a line to the text widget.
 | 
						|
    """
 | 
						|
 | 
						|
    # 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),
 | 
						|
    ]
 | 
						|
 | 
						|
    allow_code_context = False
 | 
						|
 | 
						|
    def __init__(self, *args):
 | 
						|
        EditorWindow.__init__(self, *args)
 | 
						|
        self.text.bind("<<goto-file-line>>", self.goto_file_line)
 | 
						|
 | 
						|
    # Customize EditorWindow
 | 
						|
    def ispythonsource(self, filename):
 | 
						|
        "Python source is only part of output: do not colorize."
 | 
						|
        return False
 | 
						|
 | 
						|
    def short_title(self):
 | 
						|
        "Customize EditorWindow title."
 | 
						|
        return "Output"
 | 
						|
 | 
						|
    def maybesave(self):
 | 
						|
        "Customize EditorWindow to not display save file messagebox."
 | 
						|
        return 'yes' if self.get_saved() else 'no'
 | 
						|
 | 
						|
    # Act as output file
 | 
						|
    def write(self, s, tags=(), mark="insert"):
 | 
						|
        """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.
 | 
						|
        """
 | 
						|
        assert isinstance(s, str)
 | 
						|
        self.text.insert(mark, s, tags)
 | 
						|
        self.text.see(mark)
 | 
						|
        self.text.update()
 | 
						|
        return len(s)
 | 
						|
 | 
						|
    def writelines(self, lines):
 | 
						|
        "Write each item in lines iterable."
 | 
						|
        for line in lines:
 | 
						|
            self.write(line)
 | 
						|
 | 
						|
    def flush(self):
 | 
						|
        "No flushing needed as write() directly writes to widget."
 | 
						|
        pass
 | 
						|
 | 
						|
    def showerror(self, *args, **kwargs):
 | 
						|
        messagebox.showerror(*args, **kwargs)
 | 
						|
 | 
						|
    def goto_file_line(self, event=None):
 | 
						|
        """Handle request to open file/line.
 | 
						|
 | 
						|
        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.
 | 
						|
 | 
						|
        Otherwise, display an error messagebox.
 | 
						|
        """
 | 
						|
        line = self.text.get("insert linestart", "insert lineend")
 | 
						|
        result = file_line_helper(line)
 | 
						|
        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")
 | 
						|
            result = file_line_helper(line)
 | 
						|
            if not result:
 | 
						|
                self.showerror(
 | 
						|
                    "No special line",
 | 
						|
                    "The line you point at doesn't look like "
 | 
						|
                    "a valid file name followed by a line number.",
 | 
						|
                    parent=self.text)
 | 
						|
                return
 | 
						|
        filename, lineno = result
 | 
						|
        self.flist.gotofileline(filename, lineno)
 | 
						|
 | 
						|
 | 
						|
# These classes are currently not used but might come in handy
 | 
						|
class OnDemandOutputWindow:
 | 
						|
 | 
						|
    tagdefs = {
 | 
						|
        # XXX Should use IdlePrefs.ColorPrefs
 | 
						|
        "stdout":  {"foreground": "blue"},
 | 
						|
        "stderr":  {"foreground": "#007700"},
 | 
						|
    }
 | 
						|
 | 
						|
    def __init__(self, flist):
 | 
						|
        self.flist = flist
 | 
						|
        self.owin = None
 | 
						|
 | 
						|
    def write(self, s, tags, mark):
 | 
						|
        if not self.owin:
 | 
						|
            self.setup()
 | 
						|
        self.owin.write(s, tags, mark)
 | 
						|
 | 
						|
    def setup(self):
 | 
						|
        self.owin = owin = OutputWindow(self.flist)
 | 
						|
        text = owin.text
 | 
						|
        for tag, cnf in self.tagdefs.items():
 | 
						|
            if cnf:
 | 
						|
                text.tag_configure(tag, **cnf)
 | 
						|
        text.tag_raise('sel')
 | 
						|
        self.write = self.owin.write
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    from unittest import main
 | 
						|
    main('idlelib.idle_test.test_outwin', verbosity=2, exit=False)
 |