diff options
Diffstat (limited to 'Lib/idlelib/EditorWindow.py')
| -rw-r--r-- | Lib/idlelib/EditorWindow.py | 198 | 
1 files changed, 92 insertions, 106 deletions
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py index 4bf1111..b5868be 100644 --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -1,7 +1,8 @@  import importlib  import importlib.abc +import importlib.util  import os -from platform import python_version +import platform  import re  import string  import sys @@ -12,7 +13,6 @@ import traceback  import webbrowser  from idlelib.MultiCall import MultiCallCreator -from idlelib import idlever  from idlelib import WindowList  from idlelib import SearchDialog  from idlelib import GrepDialog @@ -21,10 +21,13 @@ from idlelib import PyParse  from idlelib.configHandler import idleConf  from idlelib import aboutDialog, textView, configDialog  from idlelib import macosxSupport +from idlelib import help  # The default tab setting for a Text widget, in average-width characters.  TK_TABWIDTH_DEFAULT = 8 +_py_version = ' (%s)' % platform.python_version() +  def _sphinx_version():      "Format sys.version_info to produce the Sphinx version string used to install the chm docs"      major, minor, micro, level, serial = sys.version_info @@ -51,6 +54,11 @@ class HelpDialog(object):              near - a Toplevel widget (e.g. EditorWindow or PyShell)                     to use as a reference for placing the help window          """ +        import warnings as w +        w.warn("EditorWindow.HelpDialog is no longer used by Idle.\n" +               "It will be removed in 3.6 or later.\n" +               "It has been replaced by private help.HelpWindow\n", +               DeprecationWarning, stacklevel=2)          if self.dlg is None:              self.show_dialog(parent)          if near: @@ -77,7 +85,7 @@ class HelpDialog(object):          self.dlg = None          self.parent = None -helpDialog = HelpDialog()  # singleton instance +helpDialog = HelpDialog()  # singleton instance, no longer used  class EditorWindow(object): @@ -108,8 +116,8 @@ class EditorWindow(object):                                         'Python%s.chm' % _sphinx_version())                  if os.path.isfile(chmfile):                      dochome = chmfile -            elif macosxSupport.runningAsOSXApp(): -                # documentation is stored inside the python framework +            elif sys.platform == 'darwin': +                # documentation may be stored inside a python framework                  dochome = os.path.join(sys.base_prefix,                          'Resources/English.lproj/Documentation/index.html')              dochome = os.path.normpath(dochome) @@ -119,8 +127,7 @@ class EditorWindow(object):                      # Safari requires real file:-URLs                      EditorWindow.help_url = 'file://' + EditorWindow.help_url              else: -                EditorWindow.help_url = "http://docs.python.org/%d.%d" % sys.version_info[:2] -        currentTheme=idleConf.CurrentTheme() +                EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2]          self.flist = flist          root = root or flist.root          self.root = root @@ -149,6 +156,7 @@ class EditorWindow(object):                  'name': 'text',                  'padx': 5,                  'wrap': 'none', +                'highlightthickness': 0,                  'width': self.width,                  'height': idleConf.GetOption('main', 'EditorWindow',                                               'height', type='int')} @@ -165,16 +173,16 @@ class EditorWindow(object):          self.top.protocol("WM_DELETE_WINDOW", self.close)          self.top.bind("<<close-window>>", self.close_event) -        if macosxSupport.runningAsOSXApp(): +        if macosxSupport.isAquaTk():              # Command-W on editorwindows doesn't work without this.              text.bind('<<close-window>>', self.close_event) -            # Some OS X systems have only one mouse button, -            # so use control-click for pulldown menus there. -            #  (Note, AquaTk defines <2> as the right button if -            #   present and the Tk Text widget already binds <2>.) +            # Some OS X systems have only one mouse button, so use +            # control-click for popup context menus there. For two +            # buttons, AquaTk defines <2> as the right button, not <3>.              text.bind("<Control-Button-1>",self.right_menu_event) +            text.bind("<2>", self.right_menu_event)          else: -            # Elsewhere, use right-click for pulldown menus. +            # Elsewhere, use right-click for popup menus.              text.bind("<3>",self.right_menu_event)          text.bind("<<cut>>", self.cut)          text.bind("<<copy>>", self.copy) @@ -219,18 +227,13 @@ class EditorWindow(object):              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) +            text.bind("<<open-turtle-demo>>", self.open_turtle_demo)          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', type='int'), -                          fontWeight)) +        text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow')          text_frame.pack(side=LEFT, fill=BOTH, expand=1)          text.pack(side=TOP, fill=BOTH, expand=1)          text.focus_set() @@ -313,50 +316,20 @@ class EditorWindow(object):          self.askinteger = tkSimpleDialog.askinteger          self.showerror = tkMessageBox.showerror -        self._highlight_workaround()  # Fix selection tags on Windows - -    def _highlight_workaround(self): -        # On Windows, Tk removes painting of the selection -        # tags which is different behavior than on Linux and Mac. -        # See issue14146 for more information. -        if not sys.platform.startswith('win'): -            return - -        text = self.text -        text.event_add("<<Highlight-FocusOut>>", "<FocusOut>") -        text.event_add("<<Highlight-FocusIn>>", "<FocusIn>") -        def highlight_fix(focus): -            sel_range = text.tag_ranges("sel") -            if sel_range: -                if focus == 'out': -                    HILITE_CONFIG = idleConf.GetHighlight( -                            idleConf.CurrentTheme(), 'hilite') -                    text.tag_config("sel_fix", HILITE_CONFIG) -                    text.tag_raise("sel_fix") -                    text.tag_add("sel_fix", *sel_range) -                elif focus == 'in': -                    text.tag_remove("sel_fix", "1.0", "end") - -        text.bind("<<Highlight-FocusOut>>", -                lambda ev: highlight_fix("out")) -        text.bind("<<Highlight-FocusIn>>", -                lambda ev: highlight_fix("in")) - -      def _filename_to_unicode(self, filename): -        """convert filename to unicode in order to display it in Tk""" -        if isinstance(filename, str) or not filename: -            return filename -        else: +        """Return filename as BMP unicode so diplayable in Tk.""" +        # Decode bytes to unicode. +        if isinstance(filename, bytes):              try: -                return filename.decode(self.filesystemencoding) +                filename = filename.decode(self.filesystemencoding)              except UnicodeDecodeError: -                # XXX                  try: -                    return filename.decode(self.encoding) +                    filename = filename.decode(self.encoding)                  except UnicodeDecodeError:                      # byte-to-byte conversion -                    return filename.decode('iso8859-1') +                    filename = filename.decode('iso8859-1') +        # Replace non-BMP char with diamond questionmark. +        return re.sub('[\U00010000-\U0010FFFF]', '\ufffd', filename)      def new_callback(self, event):          dirname, basename = self.io.defaultfilename() @@ -408,13 +381,15 @@ class EditorWindow(object):      def set_status_bar(self):          self.status_bar = self.MultiStatusBar(self.top) -        if macosxSupport.runningAsOSXApp(): +        sep = Frame(self.top, height=1, borderwidth=1, background='grey75') +        if sys.platform == "darwin":              # 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) +        sep.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>") @@ -431,27 +406,25 @@ class EditorWindow(object):          ("format", "F_ormat"),          ("run", "_Run"),          ("options", "_Options"), -        ("windows", "_Windows"), +        ("windows", "_Window"),          ("help", "_Help"),      ] -    if macosxSupport.runningAsOSXApp(): -        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) +            menudict[name] = menu = Menu(mbar, name=name, tearoff=0)              mbar.add_cascade(label=label, menu=menu, underline=underline) -        if macosxSupport.isCarbonAquaTk(self.root): +        if macosxSupport.isCarbonTk():              # Insert the application menu -            menudict['application'] = menu = Menu(mbar, name='apple') +            menudict['application'] = menu = Menu(mbar, name='apple', +                                                  tearoff=0)              mbar.add_cascade(label='IDLE', menu=menu)          self.fill_menus() -        self.recent_files_menu = Menu(self.menubar) +        self.recent_files_menu = Menu(self.menubar, tearoff=0)          self.menudict['file'].insert_cascade(3, label='Recent Files',                                               underline=0,                                               menu=self.recent_files_menu) @@ -533,23 +506,29 @@ class EditorWindow(object):              return 'normal'      def about_dialog(self, event=None): +        "Handle Help 'About IDLE' event." +        # Synchronize with macosxSupport.overrideRootMenu.about_dialog.          aboutDialog.AboutDialog(self.top,'About IDLE')      def config_dialog(self, event=None): +        "Handle Options 'Configure IDLE' event." +        # Synchronize with macosxSupport.overrideRootMenu.config_dialog.          configDialog.ConfigDialog(self.top,'Settings')      def help_dialog(self, event=None): +        "Handle Help 'IDLE Help' event." +        # Synchronize with macosxSupport.overrideRootMenu.help_dialog.          if self.root:              parent = self.root          else:              parent = self.top -        helpDialog.display(parent, near=self.top) +        help.show_idlehelp(parent)      def python_docs(self, event=None):          if sys.platform[:3] == 'win':              try:                  os.startfile(self.help_url) -            except WindowsError as why: +            except OSError as why:                  tkMessageBox.showerror(title='Document Start Failure',                      message=str(why), parent=self.text)          else: @@ -660,20 +639,20 @@ class EditorWindow(object):              return          # XXX Ought to insert current file's directory in front of path          try: -            loader = importlib.find_loader(name) +            spec = importlib.util.find_spec(name)          except (ValueError, ImportError) as msg:              tkMessageBox.showerror("Import error", str(msg), parent=self.text)              return -        if loader is None: +        if spec is None:              tkMessageBox.showerror("Import error", "module not found",                                     parent=self.text)              return -        if not isinstance(loader, importlib.abc.SourceLoader): +        if not isinstance(spec.loader, importlib.abc.SourceLoader):              tkMessageBox.showerror("Import error", "not a source-based module",                                     parent=self.text)              return          try: -            file_path = loader.get_filename(name) +            file_path = spec.loader.get_filename(name)          except AttributeError:              tkMessageBox.showerror("Import error",                                     "loader does not support get_filename", @@ -683,16 +662,15 @@ class EditorWindow(object):              self.flist.open(file_path)          else:              self.io.loadfile(file_path) +        return file_path      def open_class_browser(self, event=None):          filename = self.io.filename -        if not filename: -            tkMessageBox.showerror( -                "No filename", -                "This buffer has no associated filename", -                master=self.text) -            self.text.focus_set() -            return None +        if not (self.__class__.__name__ == 'PyShellEditorWindow' +                and filename): +            filename = self.open_module() +            if filename is None: +                return          head, tail = os.path.split(filename)          base, ext = os.path.splitext(tail)          from idlelib import ClassBrowser @@ -702,6 +680,14 @@ class EditorWindow(object):          from idlelib import PathBrowser          PathBrowser.PathBrowser(self.flist) +    def open_turtle_demo(self, event = None): +        import subprocess + +        cmd = [sys.executable, +               '-c', +               'from turtledemo.__main__ import main; main()'] +        subprocess.Popen(cmd, shell=False) +      def gotoline(self, lineno):          if lineno is not None and lineno > 0:              self.text.mark_set("insert", "%d.0" % lineno) @@ -752,11 +738,11 @@ class EditorWindow(object):          self.color = None      def ResetColorizer(self): -        "Update the colour theme" +        "Update the color theme"          # Called from self.filename_change_hook and from configDialog.py          self._rmcolorizer()          self._addcolorizer() -        theme = idleConf.GetOption('main','Theme','name') +        theme = idleConf.CurrentTheme()          normal_colors = idleConf.GetHighlight(theme, 'normal')          cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')          select_colors = idleConf.GetHighlight(theme, 'hilite') @@ -767,6 +753,9 @@ class EditorWindow(object):              selectforeground=select_colors['foreground'],              selectbackground=select_colors['background'],              ) +        if TkVersion >= 8.5: +            self.text.config( +                inactiveselectbackground=select_colors['background'])      IDENTCHARS = string.ascii_letters + string.digits + "_" @@ -784,13 +773,8 @@ class EditorWindow(object):      def ResetFont(self):          "Update the text widgets' font if it is changed"          # Called from configDialog.py -        fontWeight='normal' -        if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'): -            fontWeight='bold' -        self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'), -                idleConf.GetOption('main','EditorWindow','font-size', -                                   type='int'), -                fontWeight)) + +        self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow')      def RemoveKeybindings(self):          "Remove the keybindings before they are changed." @@ -872,7 +856,7 @@ class EditorWindow(object):              if sys.platform[:3] == 'win':                  try:                      os.startfile(helpfile) -                except WindowsError as why: +                except OSError as why:                      tkMessageBox.showerror(title='Document Start Failure',                          message=str(why), parent=self.text)              else: @@ -906,9 +890,11 @@ class EditorWindow(object):          except OSError as err:              if not getattr(self.root, "recentfilelist_error_displayed", False):                  self.root.recentfilelist_error_displayed = True -                tkMessageBox.showerror(title='IDLE Error', -                    message='Unable to update Recent Files list:\n%s' -                        % str(err), +                tkMessageBox.showwarning(title='IDLE Warning', +                    message="Cannot update File menu Recent Files list. " +                            "Your operating system says:\n%s\n" +                            "Select OK and IDLE will continue without updating." +                        % self._filename_to_unicode(str(err)),                      parent=self.text)          # for each edit window instance, construct the recent files menu          for instance in self.top.instance_dict: @@ -932,7 +918,7 @@ class EditorWindow(object):          short = self.short_title()          long = self.long_title()          if short and long: -            title = short + " - " + long +            title = short + " - " + long + _py_version          elif short:              title = short          elif long: @@ -956,14 +942,13 @@ class EditorWindow(object):          self.undo.reset_undo()      def short_title(self): -        pyversion = "Python " + python_version() + ": "          filename = self.io.filename          if filename:              filename = os.path.basename(filename)          else:              filename = "Untitled"          # return unicode string to display non-ASCII chars correctly -        return pyversion + self._filename_to_unicode(filename) +        return self._filename_to_unicode(filename)      def long_title(self):          # return unicode string to display non-ASCII chars correctly @@ -1063,7 +1048,7 @@ class EditorWindow(object):          try:              try:                  mod = importlib.import_module('.' + name, package=__package__) -            except ImportError: +            except (ImportError, TypeError):                  mod = importlib.import_module(name)          except ImportError:              print("\nFailed to import extension: ", name) @@ -1397,7 +1382,7 @@ class EditorWindow(object):              text.see("insert")              text.undo_block_stop() -    # Our editwin provides a is_char_in_string function that works +    # Our editwin provides an is_char_in_string function that works      # with a Tk text index, but PyParse only knows about offsets into      # a string. This builds a function for PyParse that accepts an      # offset. @@ -1672,7 +1657,7 @@ def get_accelerator(keydefs, eventname):      keylist = keydefs.get(eventname)      # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5      # if not keylist: -    if (not keylist) or (macosxSupport.runningAsOSXApp() and eventname in { +    if (not keylist) or (macosxSupport.isCocoaTk() and eventname in {                              "<<open-module>>",                              "<<goto-line>>",                              "<<change-indentwidth>>"}): @@ -1699,19 +1684,20 @@ def fixwordbreaks(root):      tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]') -def test(): -    root = Tk() +def _editor_window(parent):  # htest # +    # error if close master window first - timer event, after script +    root = parent      fixwordbreaks(root) -    root.withdraw()      if sys.argv[1:]:          filename = sys.argv[1]      else:          filename = None +    macosxSupport.setupApp(root, None)      edit = EditorWindow(root=root, filename=filename) -    edit.set_close_hook(root.quit)      edit.text.bind("<<close-all-windows>>", edit.close_event) -    root.mainloop() -    root.destroy() +    # Does not stop error, neither does following +    # edit.text.bind("<<close-window>>", edit.close_event)  if __name__ == '__main__': -    test() +    from idlelib.idle_test.htest import run +    run(_editor_window)  | 
