Author: Matti Picus <matti.pi...@gmail.com>
Branch: release-pypy3.6-7.x
Changeset: r95832:dab365a46514
Date: 2019-02-05 14:46 +0100
http://bitbucket.org/pypy/pypy/changeset/dab365a46514/

Log:    merge 3.6 into release

diff too long, truncating to 2000 out of 22472 lines

diff --git a/lib-python/3/collections/__main__.py 
b/lib-python/3/collections/__main__.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/collections/__main__.py
@@ -0,0 +1,38 @@
+################################################################################
+### Simple tests
+################################################################################
+
+# verify that instances can be pickled
+from collections import namedtuple
+from pickle import loads, dumps
+Point = namedtuple('Point', 'x, y', True)
+p = Point(x=10, y=20)
+assert p == loads(dumps(p))
+
+# test and demonstrate ability to override methods
+class Point(namedtuple('Point', 'x y')):
+    __slots__ = ()
+    @property
+    def hypot(self):
+        return (self.x ** 2 + self.y ** 2) ** 0.5
+    def __str__(self):
+        return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, 
self.hypot)
+
+for p in Point(3, 4), Point(14, 5/7.):
+    print (p)
+
+class Point(namedtuple('Point', 'x y')):
+    'Point class with optimized _make() and _replace() without error-checking'
+    __slots__ = ()
+    _make = classmethod(tuple.__new__)
+    def _replace(self, _map=map, **kwds):
+        return self._make(_map(kwds.get, ('x', 'y'), self))
+
+print(Point(11, 22)._replace(x=100))
+
+Point3D = namedtuple('Point3D', Point._fields + ('z',))
+print(Point3D.__doc__)
+
+import doctest, collections
+TestResults = namedtuple('TestResults', 'failed attempted')
+print(TestResults(*doctest.testmod(collections)))
diff --git a/lib-python/3/idlelib/AutoCompleteWindow.py 
b/lib-python/3/idlelib/AutoCompleteWindow.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/idlelib/AutoCompleteWindow.py
@@ -0,0 +1,416 @@
+"""
+An auto-completion window for IDLE, used by the AutoComplete extension
+"""
+from tkinter import *
+from idlelib.MultiCall import MC_SHIFT
+from idlelib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES
+
+HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>"
+HIDE_SEQUENCES = ("<FocusOut>", "<ButtonPress>")
+KEYPRESS_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keypress>>"
+# We need to bind event beyond <Key> so that the function will be called
+# before the default specific IDLE function
+KEYPRESS_SEQUENCES = ("<Key>", "<Key-BackSpace>", "<Key-Return>", "<Key-Tab>",
+                      "<Key-Up>", "<Key-Down>", "<Key-Home>", "<Key-End>",
+                      "<Key-Prior>", "<Key-Next>")
+KEYRELEASE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keyrelease>>"
+KEYRELEASE_SEQUENCE = "<KeyRelease>"
+LISTUPDATE_SEQUENCE = "<B1-ButtonRelease>"
+WINCONFIG_SEQUENCE = "<Configure>"
+DOUBLECLICK_SEQUENCE = "<B1-Double-ButtonRelease>"
+
+class AutoCompleteWindow:
+
+    def __init__(self, widget):
+        # The widget (Text) on which we place the AutoCompleteWindow
+        self.widget = widget
+        # The widgets we create
+        self.autocompletewindow = self.listbox = self.scrollbar = None
+        # The default foreground and background of a selection. Saved because
+        # they are changed to the regular colors of list items when the
+        # completion start is not a prefix of the selected completion
+        self.origselforeground = self.origselbackground = None
+        # The list of completions
+        self.completions = None
+        # A list with more completions, or None
+        self.morecompletions = None
+        # The completion mode. Either AutoComplete.COMPLETE_ATTRIBUTES or
+        # AutoComplete.COMPLETE_FILES
+        self.mode = None
+        # The current completion start, on the text box (a string)
+        self.start = None
+        # The index of the start of the completion
+        self.startindex = None
+        # The last typed start, used so that when the selection changes,
+        # the new start will be as close as possible to the last typed one.
+        self.lasttypedstart = None
+        # Do we have an indication that the user wants the completion window
+        # (for example, he clicked the list)
+        self.userwantswindow = None
+        # event ids
+        self.hideid = self.keypressid = self.listupdateid = self.winconfigid \
+        = self.keyreleaseid = self.doubleclickid                         = None
+        # Flag set if last keypress was a tab
+        self.lastkey_was_tab = False
+
+    def _change_start(self, newstart):
+        min_len = min(len(self.start), len(newstart))
+        i = 0
+        while i < min_len and self.start[i] == newstart[i]:
+            i += 1
+        if i < len(self.start):
+            self.widget.delete("%s+%dc" % (self.startindex, i),
+                               "%s+%dc" % (self.startindex, len(self.start)))
+        if i < len(newstart):
+            self.widget.insert("%s+%dc" % (self.startindex, i),
+                               newstart[i:])
+        self.start = newstart
+
+    def _binary_search(self, s):
+        """Find the first index in self.completions where completions[i] is
+        greater or equal to s, or the last index if there is no such
+        one."""
+        i = 0; j = len(self.completions)
+        while j > i:
+            m = (i + j) // 2
+            if self.completions[m] >= s:
+                j = m
+            else:
+                i = m + 1
+        return min(i, len(self.completions)-1)
+
+    def _complete_string(self, s):
+        """Assuming that s is the prefix of a string in self.completions,
+        return the longest string which is a prefix of all the strings which
+        s is a prefix of them. If s is not a prefix of a string, return s."""
+        first = self._binary_search(s)
+        if self.completions[first][:len(s)] != s:
+            # There is not even one completion which s is a prefix of.
+            return s
+        # Find the end of the range of completions where s is a prefix of.
+        i = first + 1
+        j = len(self.completions)
+        while j > i:
+            m = (i + j) // 2
+            if self.completions[m][:len(s)] != s:
+                j = m
+            else:
+                i = m + 1
+        last = i-1
+
+        if first == last: # only one possible completion
+            return self.completions[first]
+
+        # We should return the maximum prefix of first and last
+        first_comp = self.completions[first]
+        last_comp = self.completions[last]
+        min_len = min(len(first_comp), len(last_comp))
+        i = len(s)
+        while i < min_len and first_comp[i] == last_comp[i]:
+            i += 1
+        return first_comp[:i]
+
+    def _selection_changed(self):
+        """Should be called when the selection of the Listbox has changed.
+        Updates the Listbox display and calls _change_start."""
+        cursel = int(self.listbox.curselection()[0])
+
+        self.listbox.see(cursel)
+
+        lts = self.lasttypedstart
+        selstart = self.completions[cursel]
+        if self._binary_search(lts) == cursel:
+            newstart = lts
+        else:
+            min_len = min(len(lts), len(selstart))
+            i = 0
+            while i < min_len and lts[i] == selstart[i]:
+                i += 1
+            newstart = selstart[:i]
+        self._change_start(newstart)
+
+        if self.completions[cursel][:len(self.start)] == self.start:
+            # start is a prefix of the selected completion
+            self.listbox.configure(selectbackground=self.origselbackground,
+                                   selectforeground=self.origselforeground)
+        else:
+            self.listbox.configure(selectbackground=self.listbox.cget("bg"),
+                                   selectforeground=self.listbox.cget("fg"))
+            # If there are more completions, show them, and call me again.
+            if self.morecompletions:
+                self.completions = self.morecompletions
+                self.morecompletions = None
+                self.listbox.delete(0, END)
+                for item in self.completions:
+                    self.listbox.insert(END, item)
+                self.listbox.select_set(self._binary_search(self.start))
+                self._selection_changed()
+
+    def show_window(self, comp_lists, index, complete, mode, userWantsWin):
+        """Show the autocomplete list, bind events.
+        If complete is True, complete the text, and if there is exactly one
+        matching completion, don't open a list."""
+        # Handle the start we already have
+        self.completions, self.morecompletions = comp_lists
+        self.mode = mode
+        self.startindex = self.widget.index(index)
+        self.start = self.widget.get(self.startindex, "insert")
+        if complete:
+            completed = self._complete_string(self.start)
+            start = self.start
+            self._change_start(completed)
+            i = self._binary_search(completed)
+            if self.completions[i] == completed and \
+               (i == len(self.completions)-1 or
+                self.completions[i+1][:len(completed)] != completed):
+                # There is exactly one matching completion
+                return completed == start
+        self.userwantswindow = userWantsWin
+        self.lasttypedstart = self.start
+
+        # Put widgets in place
+        self.autocompletewindow = acw = Toplevel(self.widget)
+        # Put it in a position so that it is not seen.
+        acw.wm_geometry("+10000+10000")
+        # Make it float
+        acw.wm_overrideredirect(1)
+        try:
+            # This command is only needed and available on Tk >= 8.4.0 for OSX
+            # Without it, call tips intrude on the typing process by grabbing
+            # the focus.
+            acw.tk.call("::tk::unsupported::MacWindowStyle", "style", acw._w,
+                        "help", "noActivates")
+        except TclError:
+            pass
+        self.scrollbar = scrollbar = Scrollbar(acw, orient=VERTICAL)
+        self.listbox = listbox = Listbox(acw, yscrollcommand=scrollbar.set,
+                                         exportselection=False, bg="white")
+        for item in self.completions:
+            listbox.insert(END, item)
+        self.origselforeground = listbox.cget("selectforeground")
+        self.origselbackground = listbox.cget("selectbackground")
+        scrollbar.config(command=listbox.yview)
+        scrollbar.pack(side=RIGHT, fill=Y)
+        listbox.pack(side=LEFT, fill=BOTH, expand=True)
+        acw.lift()  # work around bug in Tk 8.5.18+ (issue #24570)
+
+        # Initialize the listbox selection
+        self.listbox.select_set(self._binary_search(self.start))
+        self._selection_changed()
+
+        # bind events
+        self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
+                                       self.hide_event)
+        for seq in HIDE_SEQUENCES:
+            self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
+        self.keypressid = self.widget.bind(KEYPRESS_VIRTUAL_EVENT_NAME,
+                                           self.keypress_event)
+        for seq in KEYPRESS_SEQUENCES:
+            self.widget.event_add(KEYPRESS_VIRTUAL_EVENT_NAME, seq)
+        self.keyreleaseid = self.widget.bind(KEYRELEASE_VIRTUAL_EVENT_NAME,
+                                             self.keyrelease_event)
+        
self.widget.event_add(KEYRELEASE_VIRTUAL_EVENT_NAME,KEYRELEASE_SEQUENCE)
+        self.listupdateid = listbox.bind(LISTUPDATE_SEQUENCE,
+                                         self.listselect_event)
+        self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event)
+        self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE,
+                                          self.doubleclick_event)
+
+    def winconfig_event(self, event):
+        if not self.is_active():
+            return
+        # Position the completion list window
+        text = self.widget
+        text.see(self.startindex)
+        x, y, cx, cy = text.bbox(self.startindex)
+        acw = self.autocompletewindow
+        acw_width, acw_height = acw.winfo_width(), acw.winfo_height()
+        text_width, text_height = text.winfo_width(), text.winfo_height()
+        new_x = text.winfo_rootx() + min(x, max(0, text_width - acw_width))
+        new_y = text.winfo_rooty() + y
+        if (text_height - (y + cy) >= acw_height # enough height below
+            or y < acw_height): # not enough height above
+            # place acw below current line
+            new_y += cy
+        else:
+            # place acw above current line
+            new_y -= acw_height
+        acw.wm_geometry("+%d+%d" % (new_x, new_y))
+
+    def hide_event(self, event):
+        if not self.is_active():
+            return
+        self.hide_window()
+
+    def listselect_event(self, event):
+        if not self.is_active():
+            return
+        self.userwantswindow = True
+        cursel = int(self.listbox.curselection()[0])
+        self._change_start(self.completions[cursel])
+
+    def doubleclick_event(self, event):
+        # Put the selected completion in the text, and close the list
+        cursel = int(self.listbox.curselection()[0])
+        self._change_start(self.completions[cursel])
+        self.hide_window()
+
+    def keypress_event(self, event):
+        if not self.is_active():
+            return
+        keysym = event.keysym
+        if hasattr(event, "mc_state"):
+            state = event.mc_state
+        else:
+            state = 0
+        if keysym != "Tab":
+            self.lastkey_was_tab = False
+        if (len(keysym) == 1 or keysym in ("underscore", "BackSpace")
+            or (self.mode == COMPLETE_FILES and keysym in
+                ("period", "minus"))) \
+           and not (state & ~MC_SHIFT):
+            # Normal editing of text
+            if len(keysym) == 1:
+                self._change_start(self.start + keysym)
+            elif keysym == "underscore":
+                self._change_start(self.start + '_')
+            elif keysym == "period":
+                self._change_start(self.start + '.')
+            elif keysym == "minus":
+                self._change_start(self.start + '-')
+            else:
+                # keysym == "BackSpace"
+                if len(self.start) == 0:
+                    self.hide_window()
+                    return
+                self._change_start(self.start[:-1])
+            self.lasttypedstart = self.start
+            self.listbox.select_clear(0, int(self.listbox.curselection()[0]))
+            self.listbox.select_set(self._binary_search(self.start))
+            self._selection_changed()
+            return "break"
+
+        elif keysym == "Return":
+            self.hide_window()
+            return
+
+        elif (self.mode == COMPLETE_ATTRIBUTES and keysym in
+              ("period", "space", "parenleft", "parenright", "bracketleft",
+               "bracketright")) or \
+             (self.mode == COMPLETE_FILES and keysym in
+              ("slash", "backslash", "quotedbl", "apostrophe")) \
+             and not (state & ~MC_SHIFT):
+            # If start is a prefix of the selection, but is not '' when
+            # completing file names, put the whole
+            # selected completion. Anyway, close the list.
+            cursel = int(self.listbox.curselection()[0])
+            if self.completions[cursel][:len(self.start)] == self.start \
+               and (self.mode == COMPLETE_ATTRIBUTES or self.start):
+                self._change_start(self.completions[cursel])
+            self.hide_window()
+            return
+
+        elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \
+             not state:
+            # Move the selection in the listbox
+            self.userwantswindow = True
+            cursel = int(self.listbox.curselection()[0])
+            if keysym == "Home":
+                newsel = 0
+            elif keysym == "End":
+                newsel = len(self.completions)-1
+            elif keysym in ("Prior", "Next"):
+                jump = self.listbox.nearest(self.listbox.winfo_height()) - \
+                       self.listbox.nearest(0)
+                if keysym == "Prior":
+                    newsel = max(0, cursel-jump)
+                else:
+                    assert keysym == "Next"
+                    newsel = min(len(self.completions)-1, cursel+jump)
+            elif keysym == "Up":
+                newsel = max(0, cursel-1)
+            else:
+                assert keysym == "Down"
+                newsel = min(len(self.completions)-1, cursel+1)
+            self.listbox.select_clear(cursel)
+            self.listbox.select_set(newsel)
+            self._selection_changed()
+            self._change_start(self.completions[newsel])
+            return "break"
+
+        elif (keysym == "Tab" and not state):
+            if self.lastkey_was_tab:
+                # two tabs in a row; insert current selection and close acw
+                cursel = int(self.listbox.curselection()[0])
+                self._change_start(self.completions[cursel])
+                self.hide_window()
+                return "break"
+            else:
+                # first tab; let AutoComplete handle the completion
+                self.userwantswindow = True
+                self.lastkey_was_tab = True
+                return
+
+        elif any(s in keysym for s in ("Shift", "Control", "Alt",
+                                       "Meta", "Command", "Option")):
+            # A modifier key, so ignore
+            return
+
+        elif event.char and event.char >= ' ':
+            # Regular character with a non-length-1 keycode
+            self._change_start(self.start + event.char)
+            self.lasttypedstart = self.start
+            self.listbox.select_clear(0, int(self.listbox.curselection()[0]))
+            self.listbox.select_set(self._binary_search(self.start))
+            self._selection_changed()
+            return "break"
+
+        else:
+            # Unknown event, close the window and let it through.
+            self.hide_window()
+            return
+
+    def keyrelease_event(self, event):
+        if not self.is_active():
+            return
+        if self.widget.index("insert") != \
+           self.widget.index("%s+%dc" % (self.startindex, len(self.start))):
+            # If we didn't catch an event which moved the insert, close window
+            self.hide_window()
+
+    def is_active(self):
+        return self.autocompletewindow is not None
+
+    def complete(self):
+        self._change_start(self._complete_string(self.start))
+        # The selection doesn't change.
+
+    def hide_window(self):
+        if not self.is_active():
+            return
+
+        # unbind events
+        for seq in HIDE_SEQUENCES:
+            self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
+        self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
+        self.hideid = None
+        for seq in KEYPRESS_SEQUENCES:
+            self.widget.event_delete(KEYPRESS_VIRTUAL_EVENT_NAME, seq)
+        self.widget.unbind(KEYPRESS_VIRTUAL_EVENT_NAME, self.keypressid)
+        self.keypressid = None
+        self.widget.event_delete(KEYRELEASE_VIRTUAL_EVENT_NAME,
+                                 KEYRELEASE_SEQUENCE)
+        self.widget.unbind(KEYRELEASE_VIRTUAL_EVENT_NAME, self.keyreleaseid)
+        self.keyreleaseid = None
+        self.listbox.unbind(LISTUPDATE_SEQUENCE, self.listupdateid)
+        self.listupdateid = None
+        self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid)
+        self.winconfigid = None
+
+        # destroy widgets
+        self.scrollbar.destroy()
+        self.scrollbar = None
+        self.listbox.destroy()
+        self.listbox = None
+        self.autocompletewindow.destroy()
+        self.autocompletewindow = None
diff --git a/lib-python/3/idlelib/Bindings.py b/lib-python/3/idlelib/Bindings.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/idlelib/Bindings.py
@@ -0,0 +1,96 @@
+"""Define the menu contents, hotkeys, and event bindings.
+
+There is additional configuration information in the EditorWindow class (and
+subclasses): the menus are created there based on the menu_specs (class)
+variable, and menus not created are silently skipped in the code here.  This
+makes it possible, for example, to define a Debug menu which is only present in
+the PythonShell window, and a Format menu which is only present in the Editor
+windows.
+
+"""
+from importlib.util import find_spec
+
+from idlelib.configHandler import idleConf
+
+#   Warning: menudefs is altered in macosxSupport.overrideRootMenu()
+#   after it is determined that an OS X Aqua Tk is in use,
+#   which cannot be done until after Tk() is first called.
+#   Do not alter the 'file', 'options', or 'help' cascades here
+#   without altering overrideRootMenu() as well.
+#       TODO: Make this more robust
+
+menudefs = [
+ # underscore prefixes character to underscore
+ ('file', [
+   ('_New File', '<<open-new-window>>'),
+   ('_Open...', '<<open-window-from-file>>'),
+   ('Open _Module...', '<<open-module>>'),
+   ('Class _Browser', '<<open-class-browser>>'),
+   ('_Path Browser', '<<open-path-browser>>'),
+   None,
+   ('_Save', '<<save-window>>'),
+   ('Save _As...', '<<save-window-as-file>>'),
+   ('Save Cop_y As...', '<<save-copy-of-window-as-file>>'),
+   None,
+   ('Prin_t Window', '<<print-window>>'),
+   None,
+   ('_Close', '<<close-window>>'),
+   ('E_xit', '<<close-all-windows>>'),
+  ]),
+ ('edit', [
+   ('_Undo', '<<undo>>'),
+   ('_Redo', '<<redo>>'),
+   None,
+   ('Cu_t', '<<cut>>'),
+   ('_Copy', '<<copy>>'),
+   ('_Paste', '<<paste>>'),
+   ('Select _All', '<<select-all>>'),
+   None,
+   ('_Find...', '<<find>>'),
+   ('Find A_gain', '<<find-again>>'),
+   ('Find _Selection', '<<find-selection>>'),
+   ('Find in Files...', '<<find-in-files>>'),
+   ('R_eplace...', '<<replace>>'),
+   ('Go to _Line', '<<goto-line>>'),
+  ]),
+('format', [
+   ('_Indent Region', '<<indent-region>>'),
+   ('_Dedent Region', '<<dedent-region>>'),
+   ('Comment _Out Region', '<<comment-region>>'),
+   ('U_ncomment Region', '<<uncomment-region>>'),
+   ('Tabify Region', '<<tabify-region>>'),
+   ('Untabify Region', '<<untabify-region>>'),
+   ('Toggle Tabs', '<<toggle-tabs>>'),
+   ('New Indent Width', '<<change-indentwidth>>'),
+   ]),
+ ('run', [
+   ('Python Shell', '<<open-python-shell>>'),
+   ]),
+ ('shell', [
+   ('_View Last Restart', '<<view-restart>>'),
+   ('_Restart Shell', '<<restart-shell>>'),
+   None,
+   ('_Interrupt Execution', '<<interrupt-execution>>'),
+   ]),
+ ('debug', [
+   ('_Go to File/Line', '<<goto-file-line>>'),
+   ('!_Debugger', '<<toggle-debugger>>'),
+   ('_Stack Viewer', '<<open-stack-viewer>>'),
+   ('!_Auto-open Stack Viewer', '<<toggle-jit-stack-viewer>>'),
+   ]),
+ ('options', [
+   ('Configure _IDLE', '<<open-config-dialog>>'),
+   None,
+   ]),
+ ('help', [
+   ('_About IDLE', '<<about-idle>>'),
+   None,
+   ('_IDLE Help', '<<help>>'),
+   ('Python _Docs', '<<python-docs>>'),
+   ]),
+]
+
+if find_spec('turtledemo'):
+    menudefs[-1][1].append(('Turtle Demo', '<<open-turtle-demo>>'))
+
+default_keydefs = idleConf.GetCurrentKeySet()
diff --git a/lib-python/3/idlelib/CallTipWindow.py 
b/lib-python/3/idlelib/CallTipWindow.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/idlelib/CallTipWindow.py
@@ -0,0 +1,161 @@
+"""A CallTip window class for Tkinter/IDLE.
+
+After ToolTip.py, which uses ideas gleaned from PySol
+Used by the CallTips IDLE extension.
+"""
+from tkinter import Toplevel, Label, LEFT, SOLID, TclError
+
+HIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-hide>>"
+HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")
+CHECKHIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-checkhide>>"
+CHECKHIDE_SEQUENCES = ("<KeyRelease>", "<ButtonRelease>")
+CHECKHIDE_TIME = 100 # milliseconds
+
+MARK_RIGHT = "calltipwindowregion_right"
+
+class CallTip:
+
+    def __init__(self, widget):
+        self.widget = widget
+        self.tipwindow = self.label = None
+        self.parenline = self.parencol = None
+        self.lastline = None
+        self.hideid = self.checkhideid = None
+        self.checkhide_after_id = None
+
+    def position_window(self):
+        """Check if needs to reposition the window, and if so - do it."""
+        curline = int(self.widget.index("insert").split('.')[0])
+        if curline == self.lastline:
+            return
+        self.lastline = curline
+        self.widget.see("insert")
+        if curline == self.parenline:
+            box = self.widget.bbox("%d.%d" % (self.parenline,
+                                              self.parencol))
+        else:
+            box = self.widget.bbox("%d.0" % curline)
+        if not box:
+            box = list(self.widget.bbox("insert"))
+            # align to left of window
+            box[0] = 0
+            box[2] = 0
+        x = box[0] + self.widget.winfo_rootx() + 2
+        y = box[1] + box[3] + self.widget.winfo_rooty()
+        self.tipwindow.wm_geometry("+%d+%d" % (x, y))
+
+    def showtip(self, text, parenleft, parenright):
+        """Show the calltip, bind events which will close it and reposition it.
+        """
+        # Only called in CallTips, where lines are truncated
+        self.text = text
+        if self.tipwindow or not self.text:
+            return
+
+        self.widget.mark_set(MARK_RIGHT, parenright)
+        self.parenline, self.parencol = map(
+            int, self.widget.index(parenleft).split("."))
+
+        self.tipwindow = tw = Toplevel(self.widget)
+        self.position_window()
+        # remove border on calltip window
+        tw.wm_overrideredirect(1)
+        try:
+            # This command is only needed and available on Tk >= 8.4.0 for OSX
+            # Without it, call tips intrude on the typing process by grabbing
+            # the focus.
+            tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,
+                       "help", "noActivates")
+        except TclError:
+            pass
+        self.label = Label(tw, text=self.text, justify=LEFT,
+                           background="#ffffe0", relief=SOLID, borderwidth=1,
+                           font = self.widget['font'])
+        self.label.pack()
+        tw.lift()  # work around bug in Tk 8.5.18+ (issue #24570)
+
+        self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME,
+                                            self.checkhide_event)
+        for seq in CHECKHIDE_SEQUENCES:
+            self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
+        self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
+        self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
+                                       self.hide_event)
+        for seq in HIDE_SEQUENCES:
+            self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
+
+    def checkhide_event(self, event=None):
+        if not self.tipwindow:
+            # If the event was triggered by the same event that unbinded
+            # this function, the function will be called nevertheless,
+            # so do nothing in this case.
+            return
+        curline, curcol = map(int, self.widget.index("insert").split('.'))
+        if curline < self.parenline or \
+           (curline == self.parenline and curcol <= self.parencol) or \
+           self.widget.compare("insert", ">", MARK_RIGHT):
+            self.hidetip()
+        else:
+            self.position_window()
+            if self.checkhide_after_id is not None:
+                self.widget.after_cancel(self.checkhide_after_id)
+            self.checkhide_after_id = \
+                self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
+
+    def hide_event(self, event):
+        if not self.tipwindow:
+            # See the explanation in checkhide_event.
+            return
+        self.hidetip()
+
+    def hidetip(self):
+        if not self.tipwindow:
+            return
+
+        for seq in CHECKHIDE_SEQUENCES:
+            self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
+        self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid)
+        self.checkhideid = None
+        for seq in HIDE_SEQUENCES:
+            self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
+        self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
+        self.hideid = None
+
+        self.label.destroy()
+        self.label = None
+        self.tipwindow.destroy()
+        self.tipwindow = None
+
+        self.widget.mark_unset(MARK_RIGHT)
+        self.parenline = self.parencol = self.lastline = None
+
+    def is_active(self):
+        return bool(self.tipwindow)
+
+
+def _calltip_window(parent):  # htest #
+    from tkinter import Toplevel, Text, LEFT, BOTH
+
+    top = Toplevel(parent)
+    top.title("Test calltips")
+    top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
+                  parent.winfo_rooty() + 150))
+    text = Text(top)
+    text.pack(side=LEFT, fill=BOTH, expand=1)
+    text.insert("insert", "string.split")
+    top.update()
+    calltip = CallTip(text)
+
+    def calltip_show(event):
+        calltip.showtip("(s=Hello world)", "insert", "end")
+    def calltip_hide(event):
+        calltip.hidetip()
+    text.event_add("<<calltip-show>>", "(")
+    text.event_add("<<calltip-hide>>", ")")
+    text.bind("<<calltip-show>>", calltip_show)
+    text.bind("<<calltip-hide>>", calltip_hide)
+    text.focus_set()
+
+if __name__=='__main__':
+    from idlelib.idle_test.htest import run
+    run(_calltip_window)
diff --git a/lib-python/3/idlelib/ClassBrowser.py 
b/lib-python/3/idlelib/ClassBrowser.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/idlelib/ClassBrowser.py
@@ -0,0 +1,236 @@
+"""Class browser.
+
+XXX TO DO:
+
+- reparse when source changed (maybe just a button would be OK?)
+    (or recheck on window popup)
+- add popup menu with more options (e.g. doc strings, base classes, imports)
+- show function argument list? (have to do pattern matching on source)
+- should the classes and methods lists also be in the module's menu bar?
+- add base classes to class browser tree
+"""
+
+import os
+import sys
+import pyclbr
+
+from idlelib import PyShell
+from idlelib.WindowList import ListedToplevel
+from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
+from idlelib.configHandler import idleConf
+
+file_open = None  # Method...Item and Class...Item use this.
+# Normally PyShell.flist.open, but there is no PyShell.flist for htest.
+
+class ClassBrowser:
+
+    def __init__(self, flist, name, path, _htest=False):
+        # XXX This API should change, if the file doesn't end in ".py"
+        # XXX the code here is bogus!
+        """
+        _htest - bool, change box when location running htest.
+        """
+        global file_open
+        if not _htest:
+            file_open = PyShell.flist.open
+        self.name = name
+        self.file = os.path.join(path[0], self.name + ".py")
+        self._htest = _htest
+        self.init(flist)
+
+    def close(self, event=None):
+        self.top.destroy()
+        self.node.destroy()
+
+    def init(self, flist):
+        self.flist = flist
+        # reset pyclbr
+        pyclbr._modules.clear()
+        # create top
+        self.top = top = ListedToplevel(flist.root)
+        top.protocol("WM_DELETE_WINDOW", self.close)
+        top.bind("<Escape>", self.close)
+        if self._htest: # place dialog below parent if running htest
+            top.geometry("+%d+%d" %
+                (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200))
+        self.settitle()
+        top.focus_set()
+        # create scrolled canvas
+        theme = idleConf.CurrentTheme()
+        background = idleConf.GetHighlight(theme, 'normal')['background']
+        sc = ScrolledCanvas(top, bg=background, highlightthickness=0, 
takefocus=1)
+        sc.frame.pack(expand=1, fill="both")
+        item = self.rootnode()
+        self.node = node = TreeNode(sc.canvas, None, item)
+        node.update()
+        node.expand()
+
+    def settitle(self):
+        self.top.wm_title("Class Browser - " + self.name)
+        self.top.wm_iconname("Class Browser")
+
+    def rootnode(self):
+        return ModuleBrowserTreeItem(self.file)
+
+class ModuleBrowserTreeItem(TreeItem):
+
+    def __init__(self, file):
+        self.file = file
+
+    def GetText(self):
+        return os.path.basename(self.file)
+
+    def GetIconName(self):
+        return "python"
+
+    def GetSubList(self):
+        sublist = []
+        for name in self.listclasses():
+            item = ClassBrowserTreeItem(name, self.classes, self.file)
+            sublist.append(item)
+        return sublist
+
+    def OnDoubleClick(self):
+        if os.path.normcase(self.file[-3:]) != ".py":
+            return
+        if not os.path.exists(self.file):
+            return
+        PyShell.flist.open(self.file)
+
+    def IsExpandable(self):
+        return os.path.normcase(self.file[-3:]) == ".py"
+
+    def listclasses(self):
+        dir, file = os.path.split(self.file)
+        name, ext = os.path.splitext(file)
+        if os.path.normcase(ext) != ".py":
+            return []
+        try:
+            dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
+        except ImportError:
+            return []
+        items = []
+        self.classes = {}
+        for key, cl in dict.items():
+            if cl.module == name:
+                s = key
+                if hasattr(cl, 'super') and cl.super:
+                    supers = []
+                    for sup in cl.super:
+                        if type(sup) is type(''):
+                            sname = sup
+                        else:
+                            sname = sup.name
+                            if sup.module != cl.module:
+                                sname = "%s.%s" % (sup.module, sname)
+                        supers.append(sname)
+                    s = s + "(%s)" % ", ".join(supers)
+                items.append((cl.lineno, s))
+                self.classes[s] = cl
+        items.sort()
+        list = []
+        for item, s in items:
+            list.append(s)
+        return list
+
+class ClassBrowserTreeItem(TreeItem):
+
+    def __init__(self, name, classes, file):
+        self.name = name
+        self.classes = classes
+        self.file = file
+        try:
+            self.cl = self.classes[self.name]
+        except (IndexError, KeyError):
+            self.cl = None
+        self.isfunction = isinstance(self.cl, pyclbr.Function)
+
+    def GetText(self):
+        if self.isfunction:
+            return "def " + self.name + "(...)"
+        else:
+            return "class " + self.name
+
+    def GetIconName(self):
+        if self.isfunction:
+            return "python"
+        else:
+            return "folder"
+
+    def IsExpandable(self):
+        if self.cl:
+            try:
+                return not not self.cl.methods
+            except AttributeError:
+                return False
+
+    def GetSubList(self):
+        if not self.cl:
+            return []
+        sublist = []
+        for name in self.listmethods():
+            item = MethodBrowserTreeItem(name, self.cl, self.file)
+            sublist.append(item)
+        return sublist
+
+    def OnDoubleClick(self):
+        if not os.path.exists(self.file):
+            return
+        edit = file_open(self.file)
+        if hasattr(self.cl, 'lineno'):
+            lineno = self.cl.lineno
+            edit.gotoline(lineno)
+
+    def listmethods(self):
+        if not self.cl:
+            return []
+        items = []
+        for name, lineno in self.cl.methods.items():
+            items.append((lineno, name))
+        items.sort()
+        list = []
+        for item, name in items:
+            list.append(name)
+        return list
+
+class MethodBrowserTreeItem(TreeItem):
+
+    def __init__(self, name, cl, file):
+        self.name = name
+        self.cl = cl
+        self.file = file
+
+    def GetText(self):
+        return "def " + self.name + "(...)"
+
+    def GetIconName(self):
+        return "python" # XXX
+
+    def IsExpandable(self):
+        return 0
+
+    def OnDoubleClick(self):
+        if not os.path.exists(self.file):
+            return
+        edit = file_open(self.file)
+        edit.gotoline(self.cl.methods[self.name])
+
+def _class_browser(parent): #Wrapper for htest
+    try:
+        file = __file__
+    except NameError:
+        file = sys.argv[0]
+        if sys.argv[1:]:
+            file = sys.argv[1]
+        else:
+            file = sys.argv[0]
+    dir, file = os.path.split(file)
+    name = os.path.splitext(file)[0]
+    flist = PyShell.PyShellFileList(parent)
+    global file_open
+    file_open = flist.open
+    ClassBrowser(flist, name, [dir], _htest=True)
+
+if __name__ == "__main__":
+    from idlelib.idle_test.htest import run
+    run(_class_browser)
diff --git a/lib-python/3/idlelib/ColorDelegator.py 
b/lib-python/3/idlelib/ColorDelegator.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/idlelib/ColorDelegator.py
@@ -0,0 +1,281 @@
+import time
+import re
+import keyword
+import builtins
+from tkinter import TkVersion
+from idlelib.Delegator import Delegator
+from idlelib.configHandler import idleConf
+
+DEBUG = False
+
+def any(name, alternates):
+    "Return a named group pattern matching list of alternates."
+    return "(?P<%s>" % name + "|".join(alternates) + ")"
+
+def make_pat():
+    kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
+    builtinlist = [str(name) for name in dir(builtins)
+                                        if not name.startswith('_') and \
+                                        name not in keyword.kwlist]
+    # self.file = open("file") :
+    # 1st 'file' colorized normal, 2nd as builtin, 3rd as string
+    builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b"
+    comment = any("COMMENT", [r"#[^\n]*"])
+    stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?"
+    sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?"
+    dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?'
+    sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
+    dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
+    string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
+    return kw + "|" + builtin + "|" + comment + "|" + string +\
+           "|" + any("SYNC", [r"\n"])
+
+prog = re.compile(make_pat(), re.S)
+idprog = re.compile(r"\s+(\w+)", re.S)
+
+def color_config(text):  # Called from htest, Editor, and Turtle Demo.
+    '''Set color opitons of Text widget.
+
+    Should be called whenever ColorDelegator is called.
+    '''
+    # Not automatic because ColorDelegator does not know 'text'.
+    theme = idleConf.CurrentTheme()
+    normal_colors = idleConf.GetHighlight(theme, 'normal')
+    cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
+    select_colors = idleConf.GetHighlight(theme, 'hilite')
+    text.config(
+        foreground=normal_colors['foreground'],
+        background=normal_colors['background'],
+        insertbackground=cursor_color,
+        selectforeground=select_colors['foreground'],
+        selectbackground=select_colors['background'],
+        )
+    if TkVersion >= 8.5:
+        text.config(
+            inactiveselectbackground=select_colors['background'])
+
+
+class ColorDelegator(Delegator):
+
+    def __init__(self):
+        Delegator.__init__(self)
+        self.prog = prog
+        self.idprog = idprog
+        self.LoadTagDefs()
+
+    def setdelegate(self, delegate):
+        if self.delegate is not None:
+            self.unbind("<<toggle-auto-coloring>>")
+        Delegator.setdelegate(self, delegate)
+        if delegate is not None:
+            self.config_colors()
+            self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
+            self.notify_range("1.0", "end")
+        else:
+            # No delegate - stop any colorizing
+            self.stop_colorizing = True
+            self.allow_colorizing = False
+
+    def config_colors(self):
+        for tag, cnf in self.tagdefs.items():
+            if cnf:
+                self.tag_configure(tag, **cnf)
+        self.tag_raise('sel')
+
+    def LoadTagDefs(self):
+        theme = idleConf.CurrentTheme()
+        self.tagdefs = {
+            "COMMENT": idleConf.GetHighlight(theme, "comment"),
+            "KEYWORD": idleConf.GetHighlight(theme, "keyword"),
+            "BUILTIN": idleConf.GetHighlight(theme, "builtin"),
+            "STRING": idleConf.GetHighlight(theme, "string"),
+            "DEFINITION": idleConf.GetHighlight(theme, "definition"),
+            "SYNC": {'background':None,'foreground':None},
+            "TODO": {'background':None,'foreground':None},
+            "ERROR": idleConf.GetHighlight(theme, "error"),
+            # The following is used by ReplaceDialog:
+            "hit": idleConf.GetHighlight(theme, "hit"),
+            }
+
+        if DEBUG: print('tagdefs',self.tagdefs)
+
+    def insert(self, index, chars, tags=None):
+        index = self.index(index)
+        self.delegate.insert(index, chars, tags)
+        self.notify_range(index, index + "+%dc" % len(chars))
+
+    def delete(self, index1, index2=None):
+        index1 = self.index(index1)
+        self.delegate.delete(index1, index2)
+        self.notify_range(index1)
+
+    after_id = None
+    allow_colorizing = True
+    colorizing = False
+
+    def notify_range(self, index1, index2=None):
+        self.tag_add("TODO", index1, index2)
+        if self.after_id:
+            if DEBUG: print("colorizing already scheduled")
+            return
+        if self.colorizing:
+            self.stop_colorizing = True
+            if DEBUG: print("stop colorizing")
+        if self.allow_colorizing:
+            if DEBUG: print("schedule colorizing")
+            self.after_id = self.after(1, self.recolorize)
+
+    close_when_done = None # Window to be closed when done colorizing
+
+    def close(self, close_when_done=None):
+        if self.after_id:
+            after_id = self.after_id
+            self.after_id = None
+            if DEBUG: print("cancel scheduled recolorizer")
+            self.after_cancel(after_id)
+        self.allow_colorizing = False
+        self.stop_colorizing = True
+        if close_when_done:
+            if not self.colorizing:
+                close_when_done.destroy()
+            else:
+                self.close_when_done = close_when_done
+
+    def toggle_colorize_event(self, event):
+        if self.after_id:
+            after_id = self.after_id
+            self.after_id = None
+            if DEBUG: print("cancel scheduled recolorizer")
+            self.after_cancel(after_id)
+        if self.allow_colorizing and self.colorizing:
+            if DEBUG: print("stop colorizing")
+            self.stop_colorizing = True
+        self.allow_colorizing = not self.allow_colorizing
+        if self.allow_colorizing and not self.colorizing:
+            self.after_id = self.after(1, self.recolorize)
+        if DEBUG:
+            print("auto colorizing turned",\
+                  self.allow_colorizing and "on" or "off")
+        return "break"
+
+    def recolorize(self):
+        self.after_id = None
+        if not self.delegate:
+            if DEBUG: print("no delegate")
+            return
+        if not self.allow_colorizing:
+            if DEBUG: print("auto colorizing is off")
+            return
+        if self.colorizing:
+            if DEBUG: print("already colorizing")
+            return
+        try:
+            self.stop_colorizing = False
+            self.colorizing = True
+            if DEBUG: print("colorizing...")
+            t0 = time.perf_counter()
+            self.recolorize_main()
+            t1 = time.perf_counter()
+            if DEBUG: print("%.3f seconds" % (t1-t0))
+        finally:
+            self.colorizing = False
+        if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
+            if DEBUG: print("reschedule colorizing")
+            self.after_id = self.after(1, self.recolorize)
+        if self.close_when_done:
+            top = self.close_when_done
+            self.close_when_done = None
+            top.destroy()
+
+    def recolorize_main(self):
+        next = "1.0"
+        while True:
+            item = self.tag_nextrange("TODO", next)
+            if not item:
+                break
+            head, tail = item
+            self.tag_remove("SYNC", head, tail)
+            item = self.tag_prevrange("SYNC", head)
+            if item:
+                head = item[1]
+            else:
+                head = "1.0"
+
+            chars = ""
+            next = head
+            lines_to_get = 1
+            ok = False
+            while not ok:
+                mark = next
+                next = self.index(mark + "+%d lines linestart" %
+                                         lines_to_get)
+                lines_to_get = min(lines_to_get * 2, 100)
+                ok = "SYNC" in self.tag_names(next + "-1c")
+                line = self.get(mark, next)
+                ##print head, "get", mark, next, "->", repr(line)
+                if not line:
+                    return
+                for tag in self.tagdefs:
+                    self.tag_remove(tag, mark, next)
+                chars = chars + line
+                m = self.prog.search(chars)
+                while m:
+                    for key, value in m.groupdict().items():
+                        if value:
+                            a, b = m.span(key)
+                            self.tag_add(key,
+                                         head + "+%dc" % a,
+                                         head + "+%dc" % b)
+                            if value in ("def", "class"):
+                                m1 = self.idprog.match(chars, b)
+                                if m1:
+                                    a, b = m1.span(1)
+                                    self.tag_add("DEFINITION",
+                                                 head + "+%dc" % a,
+                                                 head + "+%dc" % b)
+                    m = self.prog.search(chars, m.end())
+                if "SYNC" in self.tag_names(next + "-1c"):
+                    head = next
+                    chars = ""
+                else:
+                    ok = False
+                if not ok:
+                    # We're in an inconsistent state, and the call to
+                    # update may tell us to stop.  It may also change
+                    # the correct value for "next" (since this is a
+                    # line.col string, not a true mark).  So leave a
+                    # crumb telling the next invocation to resume here
+                    # in case update tells us to leave.
+                    self.tag_add("TODO", next)
+                self.update()
+                if self.stop_colorizing:
+                    if DEBUG: print("colorizing stopped")
+                    return
+
+    def removecolors(self):
+        for tag in self.tagdefs:
+            self.tag_remove(tag, "1.0", "end")
+
+
+def _color_delegator(parent):  # htest #
+    from tkinter import Toplevel, Text
+    from idlelib.Percolator import Percolator
+
+    top = Toplevel(parent)
+    top.title("Test ColorDelegator")
+    top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
+                  parent.winfo_rooty() + 150))
+    source = "if somename: x = 'abc' # comment\nprint\n"
+    text = Text(top, background="white")
+    text.pack(expand=1, fill="both")
+    text.insert("insert", source)
+    text.focus_set()
+
+    color_config(text)
+    p = Percolator(text)
+    d = ColorDelegator()
+    p.insertfilter(d)
+
+if __name__ == "__main__":
+    from idlelib.idle_test.htest import run
+    run(_color_delegator)
diff --git a/lib-python/3/idlelib/EditorWindow.py 
b/lib-python/3/idlelib/EditorWindow.py
new file mode 100644
--- /dev/null
+++ b/lib-python/3/idlelib/EditorWindow.py
@@ -0,0 +1,1690 @@
+import importlib
+import importlib.abc
+import importlib.util
+import os
+import platform
+import re
+import string
+import sys
+from tkinter import *
+import tkinter.simpledialog as tkSimpleDialog
+import tkinter.messagebox as tkMessageBox
+import traceback
+import webbrowser
+
+from idlelib.MultiCall import MultiCallCreator
+from idlelib import WindowList
+from idlelib import SearchDialog
+from idlelib import GrepDialog
+from idlelib import ReplaceDialog
+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
+    release = '%s%s' % (major, minor)
+    release += '%s' % (micro,)
+    if level == 'candidate':
+        release += 'rc%s' % (serial,)
+    elif level != 'final':
+        release += '%s%s' % (level[0], serial)
+    return release
+
+
+class HelpDialog(object):
+
+    def __init__(self):
+        self.parent = None      # parent of help window
+        self.dlg = None         # the help window iteself
+
+    def display(self, parent, near=None):
+        """ Display the help dialog.
+
+            parent - parent widget for the help window
+
+            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:
+            self.nearwindow(near)
+
+    def show_dialog(self, parent):
+        self.parent = parent
+        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
+        self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False)
+        dlg.bind('<Destroy>', self.destroy, '+')
+
+    def nearwindow(self, near):
+        # Place the help dialog near the window specified by parent.
+        # Note - this may not reposition the window in Metacity
+        #  if "/apps/metacity/general/disable_workarounds" is enabled
+        dlg = self.dlg
+        geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10)
+        dlg.withdraw()
+        dlg.geometry("=+%d+%d" % geom)
+        dlg.deiconify()
+        dlg.lift()
+
+    def destroy(self, ev=None):
+        self.dlg = None
+        self.parent = None
+
+helpDialog = HelpDialog()  # singleton instance, no longer used
+
+
+class EditorWindow(object):
+    from idlelib.Percolator import Percolator
+    from idlelib.ColorDelegator import ColorDelegator, color_config
+    from idlelib.UndoDelegator import UndoDelegator
+    from idlelib.IOBinding import IOBinding, filesystemencoding, encoding
+    from idlelib import Bindings
+    from tkinter import Toplevel
+    from idlelib.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.base_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.base_prefix, 'Doc',
+                                       'Python%s.chm' % _sphinx_version())
+                if os.path.isfile(chmfile):
+                    dochome = chmfile
+            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)
+            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 = "https://docs.python.org/%d.%d/"; % 
sys.version_info[:2]
+        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 available to
+            #configDialog.py so it can access all EditorWindow instances
+            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.text_frame = text_frame = Frame(top)
+        self.vbar = vbar = Scrollbar(text_frame, name='vbar')
+        self.width = idleConf.GetOption('main', 'EditorWindow',
+                                        'width', type='int')
+        text_options = {
+                'name': 'text',
+                'padx': 5,
+                'wrap': 'none',
+                'highlightthickness': 0,
+                'width': self.width,
+                'height': idleConf.GetOption('main', 'EditorWindow',
+                                             'height', type='int')}
+        if TkVersion >= 8.5:
+            # Starting with tk 8.5 we have to set the new tabstyle option
+            # to 'wordprocessor' to achieve the same display of tabs as in
+            # older tk versions.
+            text_options['tabstyle'] = 'wordprocessor'
+        self.text = text = MultiCallCreator(Text)(text_frame, **text_options)
+        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.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 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 popup menus.
+            text.bind("<3>",self.right_menu_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("<<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)
+        text.bind("<<beginning-of-line>>", self.home_callback)
+
+        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)
+            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
+        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()
+
+        # 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)
+        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)
+        self.good_load = False
+        self.set_indentation_params(False)
+        self.color = None # initialized below in self.ResetColorizer
+        if filename:
+            if os.path.exists(filename) and not os.path.isdir(filename):
+                if io.loadfile(filename):
+                    self.good_load = True
+                    is_py_src = self.ispythonsource(filename)
+                    self.set_indentation_params(is_py_src)
+            else:
+                io.set_filename(filename)
+                self.good_load = True
+
+        self.ResetColorizer()
+        self.saved_change_hook()
+        self.update_recent_files_list()
+        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):
+        """Return filename as BMP unicode so diplayable in Tk."""
+        # Decode bytes to unicode.
+        if isinstance(filename, bytes):
+            try:
+                filename = filename.decode(self.filesystemencoding)
+            except UnicodeDecodeError:
+                try:
+                    filename = filename.decode(self.encoding)
+                except UnicodeDecodeError:
+                    # byte-to-byte conversion
+                    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()
+        self.flist.new(dirname)
+        return "break"
+
+    def home_callback(self, event):
+        if (event.state & 4) != 0 and event.keysym == "Home":
+            # state&4==Control. If <Control-Home>, use the Tk binding.
+            return
+        if self.text.index("iomark") and \
+           self.text.compare("iomark", "<=", "insert lineend") and \
+           self.text.compare("insert linestart", "<=", "iomark"):
+            # In Shell on input line, go to just after prompt
+            insertpt = int(self.text.index("iomark").split(".")[1])
+        else:
+            line = self.text.get("insert linestart", "insert lineend")
+            for insertpt in range(len(line)):
+                if line[insertpt] not in (' ','\t'):
+                    break
+            else:
+                insertpt=len(line)
+        lineat = int(self.text.index("insert").split('.')[1])
+        if insertpt == lineat:
+            insertpt = 0
+        dest = "insert linestart+"+str(insertpt)+"c"
+        if (event.state&1) == 0:
+            # shift was not pressed
+            self.text.tag_remove("sel", "1.0", "end")
+        else:
+            if not self.text.index("sel.first"):
+                # there was no previous selection
+                self.text.mark_set("my_anchor", "insert")
+            else:
+                if self.text.compare(self.text.index("sel.first"), "<",
+                                     self.text.index("insert")):
+                    self.text.mark_set("my_anchor", "sel.first") # extend back
+                else:
+                    self.text.mark_set("my_anchor", "sel.last") # extend 
forward
+            first = self.text.index(dest)
+            last = self.text.index("my_anchor")
+            if self.text.compare(first,">",last):
+                first,last = last,first
+            self.text.tag_remove("sel", "1.0", "end")
+            self.text.tag_add("sel", first, last)
+        self.text.mark_set("insert", dest)
+        self.text.see("insert")
+        return "break"
+
+    def set_status_bar(self):
+        self.status_bar = self.MultiStatusBar(self.top)
+        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>")
+        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", "_Window"),
+        ("help", "_Help"),
+    ]
+
+
+    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, tearoff=0)
+            mbar.add_cascade(label=label, menu=menu, underline=underline)
+        if macosxSupport.isCarbonTk():
+            # Insert the application menu
+            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, tearoff=0)
+        self.menudict['file'].insert_cascade(3, label='Recent Files',
+                                             underline=0,
+                                             menu=self.recent_files_menu)
+        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.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")
+
+        for item in self.rmenu_specs:
+            try:
+                label, eventname, verify_state = item
+            except ValueError: # see issue1207589
+                continue
+
+            if verify_state is None:
+                continue
+            state = getattr(self, verify_state)()
+            rmenu.entryconfigure(label, state=state)
+
+
+        rmenu.tk_popup(event.x_root, event.y_root)
+        if iswin:
+            self.text.config(cursor="ibeam")
+
+    rmenu_specs = [
+        # ("Label", "<<virtual-event>>", "statefuncname"), ...
+        ("Close", "<<close-window>>", None), # Example
+    ]
+
+    def make_rmenu(self):
+        rmenu = Menu(self.text, tearoff=0)
+        for item in self.rmenu_specs:
+            label, eventname = item[0], item[1]
+            if label is not None:
+                def command(text=self.text, eventname=eventname):
+                    text.event_generate(eventname)
+                rmenu.add_command(label=label, command=command)
+            else:
+                rmenu.add_separator()
+        self.rmenu = rmenu
+
+    def rmenu_check_cut(self):
+        return self.rmenu_check_copy()
+
+    def rmenu_check_copy(self):
+        try:
+            indx = self.text.index('sel.first')
+        except TclError:
+            return 'disabled'
+        else:
+            return 'normal' if indx else 'disabled'
+
+    def rmenu_check_paste(self):
+        try:
+            self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD')
+        except TclError:
+            return 'disabled'
+        else:
+            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
+        help.show_idlehelp(parent)
+
+    def python_docs(self, event=None):
+        if sys.platform[:3] == 'win':
+            try:
+                os.startfile(self.help_url)
+            except OSError as why:
+                tkMessageBox.showerror(title='Document Start Failure',
+                    message=str(why), parent=self.text)
+        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>>")
+        self.text.see("insert")
+        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?
+        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)
+        if name:
+            name = name.strip()
+        if not name:
+            return
+        # XXX Ought to insert current file's directory in front of path
+        try:
+            spec = importlib.util.find_spec(name)
+        except (ValueError, ImportError) as msg:
+            tkMessageBox.showerror("Import error", str(msg), parent=self.text)
+            return
+        if spec is None:
+            tkMessageBox.showerror("Import error", "module not found",
+                                   parent=self.text)
+            return
+        if not isinstance(spec.loader, importlib.abc.SourceLoader):
+            tkMessageBox.showerror("Import error", "not a source-based module",
+                                   parent=self.text)
+            return
+        try:
+            file_path = spec.loader.get_filename(name)
+        except AttributeError:
+            tkMessageBox.showerror("Import error",
+                                   "loader does not support get_filename",
+                                   parent=self.text)
+            return
+        if self.flist:
+            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 (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
+        ClassBrowser.ClassBrowser(self.flist, base, [head])
+
+    def open_path_browser(self, event=None):
+        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)
+            self.text.tag_remove("sel", "1.0", "end")
+            self.text.tag_add("sel", "insert", "insert +1l")
+            self.center()
+
+    def ispythonsource(self, filename):
+        if not filename or os.path.isdir(filename):
+            return True
+        base, ext = os.path.splitext(os.path.basename(filename))
+        if os.path.normcase(ext) in (".py", ".pyw"):
+            return True
+        line = self.text.get('1.0', '1.0 lineend')
+        return line.startswith('#!') and 'python' in line
+
+    def close_hook(self):
+        if self.flist:
+            self.flist.unregister_maybe_terminate(self)
+            self.flist = None
+
+    def set_close_hook(self, close_hook):
+        self.close_hook = close_hook
+
+    def filename_change_hook(self):
+        if self.flist:
+            self.flist.filename_changed_edit(self)
+        self.saved_change_hook()
+        self.top.update_windowlist_registry(self)
+        self.ResetColorizer()
+
+    def _addcolorizer(self):
+        if self.color:
+            return
+        if self.ispythonsource(self.io.filename):
+            self.color = self.ColorDelegator()
+        # can add more colorizers here...
+        if self.color:
+            self.per.removefilter(self.undo)
+            self.per.insertfilter(self.color)
+            self.per.insertfilter(self.undo)
+
+    def _rmcolorizer(self):
+        if not self.color:
+            return
+        self.color.removecolors()
+        self.per.removefilter(self.color)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to