1511 lines · 56.1 KB · Python
RawDownload
import sysimport osimport reimport impfrom itertools import countfrom Tkinter import *import tkSimpleDialogimport tkMessageBoxfrom MultiCall import MultiCallCreator import webbrowserimport idleverimport WindowListimport SearchDialogimport GrepDialogimport ReplaceDialogimport PyParsefrom configHandler import idleConfimport aboutDialog, textView, configDialogimport macosxSupport # The default tab setting for a Text widget, in average-width characters.TK_TABWIDTH_DEFAULT = 8 def _find_module(fullname, path=None):    """Version of imp.find_module() that handles hierarchical module names"""     file = None    for tgt in fullname.split('.'):        if file is not None:            file.close()            # close intermediate files        (file, filename, descr) = imp.find_module(tgt, path)        if descr[2] == imp.PY_SOURCE:            break                   # find but not load the source file        module = imp.load_module(tgt, file, filename, descr)        try:            path = module.__path__        except AttributeError:            raise ImportError, 'No source for module ' + module.__name__    return file, filename, descr class EditorWindow(object):    from Percolator import Percolator    from ColorDelegator import ColorDelegator    from UndoDelegator import UndoDelegator    from IOBinding import IOBinding, filesystemencoding, encoding    import Bindings    from Tkinter import Toplevel    from MultiStatusBar import MultiStatusBar     help_url = None     def __init__(self, flist=None, filename=None, key=None, root=None):        if EditorWindow.help_url is None:            dochome =  os.path.join(sys.prefix, 'Doc', 'index.html')            if sys.platform.count('linux'):                # look for html docs in a couple of standard places                pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]                if os.path.isdir('/var/www/html/python/'):  # "python2" rpm                    dochome = '/var/www/html/python/index.html'                else:                    basepath = '/usr/share/doc/'  # standard location                    dochome = os.path.join(basepath, pyver,                                           'Doc', 'index.html')            elif sys.platform[:3] == 'win':                chmfile = os.path.join(sys.prefix, 'Doc',                                       'Python%d%d.chm' % sys.version_info[:2])                if os.path.isfile(chmfile):                    dochome = chmfile             elif macosxSupport.runningAsOSXApp():                # documentation is stored inside the python framework                dochome = os.path.join(sys.prefix,                        'Resources/English.lproj/Documentation/index.html')             dochome = os.path.normpath(dochome)            if os.path.isfile(dochome):                EditorWindow.help_url = dochome                if sys.platform == 'darwin':                    # Safari requires real file:-URLs                    EditorWindow.help_url = 'file://' + EditorWindow.help_url            else:                EditorWindow.help_url = "http://www.python.org/doc/current"        currentTheme=idleConf.CurrentTheme()        self.flist = flist        root = root or flist.root        self.root = root        try:            sys.ps1        except AttributeError:            sys.ps1 = '>>> '        self.menubar = Menu(root)        self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)        if flist:            self.tkinter_vars = flist.vars            #self.top.instance_dict makes flist.inversedict avalable to            #configDialog.py so it can access all EditorWindow instaces            self.top.instance_dict = flist.inversedict        else:            self.tkinter_vars = {}  # keys: Tkinter event names                                    # values: Tkinter variable instances            self.top.instance_dict = {}        self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),                'recent-files.lst')        self.vbar = vbar = Scrollbar(top, name='vbar')        self.text_frame = text_frame = Frame(top)        self.width = idleConf.GetOption('main','EditorWindow','width')        self.text = text = MultiCallCreator(Text)(                text_frame, name='text', padx=5, wrap='none',                foreground=idleConf.GetHighlight(currentTheme,                        'normal',fgBg='fg'),                background=idleConf.GetHighlight(currentTheme,                        'normal',fgBg='bg'),                highlightcolor=idleConf.GetHighlight(currentTheme,                        'hilite',fgBg='fg'),                highlightbackground=idleConf.GetHighlight(currentTheme,                        'hilite',fgBg='bg'),                insertbackground=idleConf.GetHighlight(currentTheme,                        'cursor',fgBg='fg'),                width=self.width,                height=idleConf.GetOption('main','EditorWindow','height') )        self.top.focused_widget = self.text         self.createmenubar()        self.apply_bindings()         self.top.protocol("WM_DELETE_WINDOW", self.close)        self.top.bind("<<close-window>>", self.close_event)        if macosxSupport.runningAsOSXApp():            # Command-W on editorwindows doesn't work without this.            text.bind('<<close-window>>', self.close_event)        text.bind("<<cut>>", self.cut)        text.bind("<<copy>>", self.copy)        text.bind("<<paste>>", self.paste)        text.bind("<<center-insert>>", self.center_insert_event)        text.bind("<<help>>", self.help_dialog)        text.bind("<<python-docs>>", self.python_docs)        text.bind("<<about-idle>>", self.about_dialog)        text.bind("<<open-config-dialog>>", self.config_dialog)        text.bind("<<open-module>>", self.open_module)        text.bind("<<do-nothing>>", lambda event: "break")        text.bind("<<select-all>>", self.select_all)        text.bind("<<remove-selection>>", self.remove_selection)        text.bind("<<find>>", self.find_event)        text.bind("<<find-again>>", self.find_again_event)        text.bind("<<find-in-files>>", self.find_in_files_event)        text.bind("<<find-selection>>", self.find_selection_event)        text.bind("<<replace>>", self.replace_event)        text.bind("<<goto-line>>", self.goto_line_event)        text.bind("<3>", self.right_menu_event)        text.bind("<<smart-backspace>>",self.smart_backspace_event)        text.bind("<<newline-and-indent>>",self.newline_and_indent_event)        text.bind("<<smart-indent>>",self.smart_indent_event)        text.bind("<<indent-region>>",self.indent_region_event)        text.bind("<<dedent-region>>",self.dedent_region_event)        text.bind("<<comment-region>>",self.comment_region_event)        text.bind("<<uncomment-region>>",self.uncomment_region_event)        text.bind("<<tabify-region>>",self.tabify_region_event)        text.bind("<<untabify-region>>",self.untabify_region_event)        text.bind("<<toggle-tabs>>",self.toggle_tabs_event)        text.bind("<<change-indentwidth>>",self.change_indentwidth_event)        text.bind("<Left>", self.move_at_edge_if_selection(0))        text.bind("<Right>", self.move_at_edge_if_selection(1))        text.bind("<<del-word-left>>", self.del_word_left)        text.bind("<<del-word-right>>", self.del_word_right)         if flist:            flist.inversedict[self] = key            if key:                flist.dict[key] = self            text.bind("<<open-new-window>>", self.new_callback)            text.bind("<<close-all-windows>>", self.flist.close_all_callback)            text.bind("<<open-class-browser>>", self.open_class_browser)            text.bind("<<open-path-browser>>", self.open_path_browser)         self.set_status_bar()        vbar['command'] = text.yview        vbar.pack(side=RIGHT, fill=Y)        text['yscrollcommand'] = vbar.set        fontWeight = 'normal'        if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):            fontWeight='bold'        text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'),                          idleConf.GetOption('main', 'EditorWindow', 'font-size'),                          fontWeight))        text_frame.pack(side=LEFT, fill=BOTH, expand=1)        text.pack(side=TOP, fill=BOTH, expand=1)        text.focus_set()         # usetabs true  -> literal tab characters are used by indent and        #                  dedent cmds, possibly mixed with spaces if        #                  indentwidth is not a multiple of tabwidth,        #                  which will cause Tabnanny to nag!        #         false -> tab characters are converted to spaces by indent        #                  and dedent cmds, and ditto TAB keystrokes        # Although use-spaces=0 can be configured manually in config-main.def,        # configuration of tabs v. spaces is not supported in the configuration        # dialog.  IDLE promotes the preferred Python indentation: use spaces!        usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool')        self.usetabs = not usespaces         # tabwidth is the display width of a literal tab character.        # CAUTION:  telling Tk to use anything other than its default        # tab setting causes it to use an entirely different tabbing algorithm,        # treating tab stops as fixed distances from the left margin.        # Nobody expects this, so for now tabwidth should never be changed.        self.tabwidth = 8    # must remain 8 until Tk is fixed.         # indentwidth is the number of screen characters per indent level.        # The recommended Python indentation is four spaces.        self.indentwidth = self.tabwidth        self.set_notabs_indentwidth()         # If context_use_ps1 is true, parsing searches back for a ps1 line;        # else searches for a popular (if, def, ...) Python stmt.        self.context_use_ps1 = False         # When searching backwards for a reliable place to begin parsing,        # first start num_context_lines[0] lines back, then        # num_context_lines[1] lines back if that didn't work, and so on.        # The last value should be huge (larger than the # of lines in a        # conceivable file).        # Making the initial values larger slows things down more often.        self.num_context_lines = 50, 500, 5000000         self.per = per = self.Percolator(text)        if self.ispythonsource(filename):            self.color = color = self.ColorDelegator()            per.insertfilter(color)        else:            self.color = None         self.undo = undo = self.UndoDelegator()        per.insertfilter(undo)        text.undo_block_start = undo.undo_block_start        text.undo_block_stop = undo.undo_block_stop        undo.set_saved_change_hook(self.saved_change_hook)         # IOBinding implements file I/O and printing functionality        self.io = io = self.IOBinding(self)        io.set_filename_change_hook(self.filename_change_hook)         # Create the recent files submenu        self.recent_files_menu = Menu(self.menubar)        self.menudict['file'].insert_cascade(3, label='Recent Files',                                             underline=0,                                             menu=self.recent_files_menu)        self.update_recent_files_list()         if filename:            if os.path.exists(filename) and not os.path.isdir(filename):                io.loadfile(filename)            else:                io.set_filename(filename)        self.saved_change_hook()         self.set_indentation_params(self.ispythonsource(filename))         self.load_extensions()         menu = self.menudict.get('windows')        if menu:            end = menu.index("end")            if end is None:                end = -1            if end >= 0:                menu.add_separator()                end = end + 1            self.wmenu_end = end            WindowList.register_callback(self.postwindowsmenu)         # Some abstractions so IDLE extensions are cross-IDE        self.askyesno = tkMessageBox.askyesno        self.askinteger = tkSimpleDialog.askinteger        self.showerror = tkMessageBox.showerror     def _filename_to_unicode(self, filename):        """convert filename to unicode in order to display it in Tk"""        if isinstance(filename, unicode) or not filename:            return filename        else:            try:                return filename.decode(self.filesystemencoding)            except UnicodeDecodeError:                # XXX                try:                    return filename.decode(self.encoding)                except UnicodeDecodeError:                    # byte-to-byte conversion                    return filename.decode('iso8859-1')     def new_callback(self, event):        dirname, basename = self.io.defaultfilename()        self.flist.new(dirname)        return "break"     def set_status_bar(self):        self.status_bar = self.MultiStatusBar(self.top)        if macosxSupport.runningAsOSXApp():            # Insert some padding to avoid obscuring some of the statusbar            # by the resize widget.            self.status_bar.set_label('_padding1', '    ', side=RIGHT)        self.status_bar.set_label('column', 'Col: ?', side=RIGHT)        self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)        self.status_bar.pack(side=BOTTOM, fill=X)        self.text.bind("<<set-line-and-column>>", self.set_line_and_column)        self.text.event_add("<<set-line-and-column>>",                            "<KeyRelease>", "<ButtonRelease>")        self.text.after_idle(self.set_line_and_column)     def set_line_and_column(self, event=None):        line, column = self.text.index(INSERT).split('.')        self.status_bar.set_label('column', 'Col: %s' % column)        self.status_bar.set_label('line', 'Ln: %s' % line)     menu_specs = [        ("file", "_File"),        ("edit", "_Edit"),        ("format", "F_ormat"),        ("run", "_Run"),        ("options", "_Options"),        ("windows", "_Windows"),        ("help", "_Help"),    ]     if macosxSupport.runningAsOSXApp():        del menu_specs[-3]        menu_specs[-2] = ("windows", "_Window")      def createmenubar(self):        mbar = self.menubar        self.menudict = menudict = {}        for name, label in self.menu_specs:            underline, label = prepstr(label)            menudict[name] = menu = Menu(mbar, name=name)            mbar.add_cascade(label=label, menu=menu, underline=underline)         if sys.platform == 'darwin' and '.framework' in sys.executable:            # Insert the application menu            menudict['application'] = menu = Menu(mbar, name='apple')            mbar.add_cascade(label='IDLE', menu=menu)         self.fill_menus()        self.base_helpmenu_length = self.menudict['help'].index(END)        self.reset_help_menu_entries()     def postwindowsmenu(self):        # Only called when Windows menu exists        menu = self.menudict['windows']        end = menu.index("end")        if end is None:            end = -1        if end > self.wmenu_end:            menu.delete(self.wmenu_end+1, end)        WindowList.add_windows_to_menu(menu)     rmenu = None     def right_menu_event(self, event):        self.text.tag_remove("sel", "1.0", "end")        self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))        if not self.rmenu:            self.make_rmenu()        rmenu = self.rmenu        self.event = event        iswin = sys.platform[:3] == 'win'        if iswin:            self.text.config(cursor="arrow")        rmenu.tk_popup(event.x_root, event.y_root)        if iswin:            self.text.config(cursor="ibeam")     rmenu_specs = [        # ("Label", "<<virtual-event>>"), ...        ("Close", "<<close-window>>"), # Example    ]     def make_rmenu(self):        rmenu = Menu(self.text, tearoff=0)        for label, eventname in self.rmenu_specs:            def command(text=self.text, eventname=eventname):                text.event_generate(eventname)            rmenu.add_command(label=label, command=command)        self.rmenu = rmenu     def about_dialog(self, event=None):        aboutDialog.AboutDialog(self.top,'About IDLE')     def config_dialog(self, event=None):        configDialog.ConfigDialog(self.top,'Settings')     def help_dialog(self, event=None):        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')        textView.TextViewer(self.top,'Help',fn)     def python_docs(self, event=None):        if sys.platform[:3] == 'win':            os.startfile(self.help_url)        else:            webbrowser.open(self.help_url)        return "break"     def cut(self,event):        self.text.event_generate("<<Cut>>")        return "break"     def copy(self,event):        if not self.text.tag_ranges("sel"):            # There is no selection, so do nothing and maybe interrupt.            return        self.text.event_generate("<<Copy>>")        return "break"     def paste(self,event):        self.text.event_generate("<<Paste>>")        return "break"     def select_all(self, event=None):        self.text.tag_add("sel", "1.0", "end-1c")        self.text.mark_set("insert", "1.0")        self.text.see("insert")        return "break"     def remove_selection(self, event=None):        self.text.tag_remove("sel", "1.0", "end")        self.text.see("insert")     def move_at_edge_if_selection(self, edge_index):        """Cursor move begins at start or end of selection         When a left/right cursor key is pressed create and return to Tkinter a        function which causes a cursor move from the associated edge of the        selection.         """        self_text_index = self.text.index        self_text_mark_set = self.text.mark_set        edges_table = ("sel.first+1c", "sel.last-1c")        def move_at_edge(event):            if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed                try:                    self_text_index("sel.first")                    self_text_mark_set("insert", edges_table[edge_index])                except TclError:                    pass        return move_at_edge     def del_word_left(self, event):        self.text.event_generate('<Meta-Delete>')        return "break"     def del_word_right(self, event):        self.text.event_generate('<Meta-d>')        return "break"     def find_event(self, event):        SearchDialog.find(self.text)        return "break"     def find_again_event(self, event):        SearchDialog.find_again(self.text)        return "break"     def find_selection_event(self, event):        SearchDialog.find_selection(self.text)        return "break"     def find_in_files_event(self, event):        GrepDialog.grep(self.text, self.io, self.flist)        return "break"     def replace_event(self, event):        ReplaceDialog.replace(self.text)        return "break"     def goto_line_event(self, event):        text = self.text        lineno = tkSimpleDialog.askinteger("Goto",                "Go to line number:",parent=text)        if lineno is None:            return "break"        if lineno <= 0:            text.bell()            return "break"        text.mark_set("insert", "%d.0" % lineno)        text.see("insert")     def open_module(self, event=None):        # XXX Shouldn't this be in IOBinding or in FileList?        try:            name = self.text.get("sel.first", "sel.last")        except TclError:            name = ""        else:            name = name.strip()        name = tkSimpleDialog.askstring("Module",                 "Enter the name of a Python module\n"                 "to search on sys.path and open:",                 parent=self.text, initialvalue=name)

Lines 1–500 of 1511.