[Python-checkins] python/dist/src/Lib/idlelib CallTipWindow.py, 1.7, 1.7.14.1 CallTips.py, 1.12, 1.12.6.1 EditorWindow.py, 1.69, 1.69.2.1 ParenMatch.py, 1.8, 1.8.6.1 PyParse.py, 1.5, 1.5.14.1 PyShell.py, 1.99, 1.99.2.1 config-extensions.def, 1.15, 1.15.6.1 configDialog.py, 1.62, 1.62.2.1 run.py, 1.32, 1.32.2.1

kbk@users.sourceforge.net kbk at users.sourceforge.net
Mon Oct 10 02:05:36 CEST 2005


Update of /cvsroot/python/python/dist/src/Lib/idlelib
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22453

Modified Files:
      Tag: IDLE-syntax-branch
	CallTipWindow.py CallTips.py EditorWindow.py ParenMatch.py 
	PyParse.py PyShell.py config-extensions.def configDialog.py 
	run.py 
Log Message:
Noam Raphael 'syntax' patch 10Jul05


Index: CallTipWindow.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/CallTipWindow.py,v
retrieving revision 1.7
retrieving revision 1.7.14.1
diff -u -d -r1.7 -r1.7.14.1
--- CallTipWindow.py	31 Dec 2002 15:59:14 -0000	1.7
+++ CallTipWindow.py	10 Oct 2005 00:05:30 -0000	1.7.14.1
@@ -6,33 +6,65 @@
 """
 from Tkinter import *
 
+HIDE_VIRTUAL_EVENT_NAME = "<<caltipwindow-hide>>"
+HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")
+CHECKHIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-checkhide>>"
+CHECKHIDE_SEQUENCES = ("<KeyRelease>", "<ButtonRelease>")
+CHECKHIDE_TIME = 100 # miliseconds
+
+MARK_RIGHT = "calltipwindowregion_right"
+
 class CallTip:
 
     def __init__(self, widget):
         self.widget = widget
-        self.tipwindow = None
-        self.id = None
-        self.x = self.y = 0
+        self.tipwindow = self.label = None
+        self.parenline = self.parencol = None
+        self.lastline = None
+        self.hideid = self.checkhideid = None
 
-    def showtip(self, text):
-        " Display text in calltip window"
+    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.
+        """
         # truncate overly long calltip
         if len(text) >= 79:
             text = text[:75] + ' ...'
         self.text = text
         if self.tipwindow or not self.text:
             return
-        self.widget.see("insert")
-        x, y, cx, cy = self.widget.bbox("insert")
-        x = x + self.widget.winfo_rootx() + 2
-        y = y + cy + self.widget.winfo_rooty()
+        
+        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()
         # XXX 12 Dec 2002 KBK The following command has two effects: It removes
         #     the calltip window border (good) but also causes (at least on
         #     Linux) the calltip to show as a top level window, burning through
         #     any other window dragged over it.  Also, shows on all viewports!
         tw.wm_overrideredirect(1)
-        tw.wm_geometry("+%d+%d" % (x, y))
         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
@@ -41,16 +73,66 @@
                        "help", "noActivates")
         except TclError:
             pass
-        label = Label(tw, text=self.text, justify=LEFT,
-                      background="#ffffe0", relief=SOLID, borderwidth=1,
-                      font = self.widget['font'])
-        label.pack()
+        self.label = Label(tw, text=self.text, justify=LEFT,
+                           background="#ffffe0", relief=SOLID, borderwidth=1,
+                           font = self.widget['font'])
+        self.label.pack()
+
+        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()
+            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):
-        tw = self.tipwindow
+        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
-        if tw:
-            tw.destroy()
+
+        self.widget.mark_unset(MARK_RIGHT)
+        self.parenline = self.parencol = self.lastline = None
+
+    def is_active(self):
+        return bool(self.tipwindow)
+
 
 
 ###############################

Index: CallTips.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/CallTips.py,v
retrieving revision 1.12
retrieving revision 1.12.6.1
diff -u -d -r1.12 -r1.12.6.1
--- CallTips.py	4 May 2004 08:34:56 -0000	1.12
+++ CallTips.py	10 Oct 2005 00:05:30 -0000	1.12.6.1
@@ -3,21 +3,21 @@
 Call Tips are floating windows which display function, class, and method
 parameter and docstring information when you type an opening parenthesis, and
 which disappear when you type a closing parenthesis.
-
-Future plans include extending the functionality to include class attributes.
-
 """
 import sys
-import string
 import types
 
 import CallTipWindow
+from HyperParser import HyperParser
 
 import __main__
 
 class CallTips:
 
     menudefs = [
+        ('edit', [
+            ("Show call tip", "<<force-open-calltip>>"),
+        ])
     ]
 
     def __init__(self, editwin=None):
@@ -36,51 +36,47 @@
         # See __init__ for usage
         return CallTipWindow.CallTip(self.text)
 
-    def _remove_calltip_window(self):
+    def _remove_calltip_window(self, event=None):
         if self.calltip:
             self.calltip.hidetip()
             self.calltip = None
 
-    def paren_open_event(self, event):
-        self._remove_calltip_window()
-        name = self.get_name_at_cursor()
-        arg_text = self.fetch_tip(name)
-        if arg_text:
-            self.calltip_start = self.text.index("insert")
-            self.calltip = self._make_calltip_window()
-            self.calltip.showtip(arg_text)
-        return "" #so the event is handled normally.
+    def force_open_calltip_event(self, event):
+        """Happens when the user really wants to open a CallTip, even if a
+        function call is needed.
+        """
+        self.open_calltip(True)
 
-    def paren_close_event(self, event):
-        # Now just hides, but later we should check if other
-        # paren'd expressions remain open.
-        self._remove_calltip_window()
-        return "" #so the event is handled normally.
+    def try_open_calltip_event(self, event):
+        """Happens when it would be nice to open a CallTip, but not really
+        neccesary, for example after an opening bracket, so function calls
+        won't be made.
+        """
+        self.open_calltip(False)
 
-    def check_calltip_cancel_event(self, event):
-        if self.calltip:
-            # If we have moved before the start of the calltip,
-            # or off the calltip line, then cancel the tip.
-            # (Later need to be smarter about multi-line, etc)
-            if self.text.compare("insert", "<=", self.calltip_start) or \
-               self.text.compare("insert", ">", self.calltip_start
-                                 + " lineend"):
-                self._remove_calltip_window()
-        return "" #so the event is handled normally.
+    def refresh_calltip_event(self, event):
+        """If there is already a calltip window, check if it is still needed,
+        and if so, reload it.
+        """
+        if self.calltip and self.calltip.is_active():
+            self.open_calltip(False)
 
-    def calltip_cancel_event(self, event):
+    def open_calltip(self, evalfuncs):
         self._remove_calltip_window()
-        return "" #so the event is handled normally.
-
-    __IDCHARS = "._" + string.ascii_letters + string.digits
 
-    def get_name_at_cursor(self):
-        idchars = self.__IDCHARS
-        str = self.text.get("insert linestart", "insert")
-        i = len(str)
-        while i and str[i-1] in idchars:
-            i -= 1
-        return str[i:]
+        hp = HyperParser(self.editwin, "insert")
+        sur_paren = hp.get_surrounding_brackets('(')
+        if not sur_paren:
+            return
+        hp.set_index(sur_paren[0])
+        name = hp.get_expression()
+        if not name or (not evalfuncs and name.find('(') != -1):
+            return
+        arg_text = self.fetch_tip(name)
+        if not arg_text:
+            return
+        self.calltip = self._make_calltip_window()
+        self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1])
 
     def fetch_tip(self, name):
         """Return the argument list and docstring of a function or class
@@ -127,7 +123,7 @@
     return None
 
 def get_arg_text(ob):
-    "Get a string describing the arguments for the given object"
+    """Get a string describing the arguments for the given object"""
     argText = ""
     if ob is not None:
         argOffset = 0
@@ -150,7 +146,7 @@
             try:
                 realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
                 defaults = fob.func_defaults or []
-                defaults = list(map(lambda name: "=%s" % name, defaults))
+                defaults = list(map(lambda name: "=%s" % repr(name), defaults))
                 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
                 items = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
                 if fob.func_code.co_flags & 0x4:

Index: EditorWindow.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/EditorWindow.py,v
retrieving revision 1.69
retrieving revision 1.69.2.1
diff -u -d -r1.69 -r1.69.2.1
--- EditorWindow.py	12 Jun 2005 05:19:23 -0000	1.69
+++ EditorWindow.py	10 Oct 2005 00:05:30 -0000	1.69.2.1
@@ -6,6 +6,7 @@
 from Tkinter import *
 import tkSimpleDialog
 import tkMessageBox
+from MultiCall import MultiCallCreator
 
 import webbrowser
 import idlever
@@ -89,7 +90,8 @@
         self.vbar = vbar = Scrollbar(top, name='vbar')
         self.text_frame = text_frame = Frame(top)
         self.width = idleConf.GetOption('main','EditorWindow','width')
-        self.text = text = Text(text_frame, name='text', padx=5, wrap='none',
+        self.text = text = MultiCallCreator(Text)(
+                text_frame, name='text', padx=5, wrap='none',
                 foreground=idleConf.GetHighlight(currentTheme,
                         'normal',fgBg='fg'),
                 background=idleConf.GetHighlight(currentTheme,
@@ -264,8 +266,9 @@
         self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
         self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
         self.status_bar.pack(side=BOTTOM, fill=X)
-        self.text.bind('<KeyRelease>', self.set_line_and_column)
-        self.text.bind('<ButtonRelease>', self.set_line_and_column)
+        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):
@@ -355,6 +358,9 @@
         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"
 
@@ -557,14 +563,28 @@
                 idleConf.GetOption('main','EditorWindow','font-size'),
                 fontWeight))
 
-    def ResetKeybindings(self):
-        "Update the keybindings if they are changed"
+    def RemoveKeybindings(self):
+        "Remove the keybindings before they are changed."
         # Called from configDialog.py
         self.Bindings.default_keydefs=idleConf.GetCurrentKeySet()
         keydefs = self.Bindings.default_keydefs
         for event, keylist in keydefs.items():
-            self.text.event_delete(event)
+            self.text.event_delete(event, *keylist)
+        for extensionName in self.get_standard_extension_names():
+            keydefs = idleConf.GetExtensionBindings(extensionName)
+            if keydefs:
+                for event, keylist in keydefs.items():
+                    self.text.event_delete(event, *keylist)
+
+    def ApplyKeybindings(self):
+        "Update the keybindings after they are changed"
+        # Called from configDialog.py
+        self.Bindings.default_keydefs=idleConf.GetCurrentKeySet()
         self.apply_bindings()
+        for extensionName in self.get_standard_extension_names():
+            keydefs = idleConf.GetExtensionBindings(extensionName)
+            if keydefs:
+                self.apply_bindings(keydefs)
         #update menu accelerators
         menuEventDict={}
         for menu in self.Bindings.menudefs:
@@ -1064,17 +1084,28 @@
             # open/close first need to find the last stmt
             lno = index2line(text.index('insert'))
             y = PyParse.Parser(self.indentwidth, self.tabwidth)
-            for context in self.num_context_lines:
-                startat = max(lno - context, 1)
-                startatindex = repr(startat) + ".0"
+            if not self.context_use_ps1:
+                for context in self.num_context_lines:
+                    startat = max(lno - context, 1)
+                    startatindex = `startat` + ".0"
+                    rawtext = text.get(startatindex, "insert")
+                    y.set_str(rawtext)
+                    bod = y.find_good_parse_start(
+                              self.context_use_ps1,
+                              self._build_char_in_string_func(startatindex))
+                    if bod is not None or startat == 1:
+                        break
+                y.set_lo(bod or 0)
+            else:
+                r = text.tag_prevrange("console", "insert")
+                if r:
+                    startatindex = r[1]
+                else:
+                    startatindex = "1.0"
                 rawtext = text.get(startatindex, "insert")
                 y.set_str(rawtext)
-                bod = y.find_good_parse_start(
-                          self.context_use_ps1,
-                          self._build_char_in_string_func(startatindex))
-                if bod is not None or startat == 1:
-                    break
-            y.set_lo(bod or 0)
+                y.set_lo(0)
+                
             c = y.get_continuation_type()
             if c != PyParse.C_NONE:
                 # The current stmt hasn't ended yet.

Index: ParenMatch.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/ParenMatch.py,v
retrieving revision 1.8
retrieving revision 1.8.6.1
diff -u -d -r1.8 -r1.8.6.1
--- ParenMatch.py	12 Feb 2004 17:35:09 -0000	1.8
+++ ParenMatch.py	10 Oct 2005 00:05:30 -0000	1.8.6.1
@@ -3,17 +3,14 @@
 When you hit a right paren, the cursor should move briefly to the left
 paren.  Paren here is used generically; the matching applies to
 parentheses, square brackets, and curly braces.
-
-WARNING: This extension will fight with the CallTips extension,
-because they both are interested in the KeyRelease-parenright event.
-We'll have to fix IDLE to do something reasonable when two or more
-extensions what to capture the same event.
 """
 
-import PyParse
-from EditorWindow import EditorWindow, index2line
+from HyperParser import HyperParser
 from configHandler import idleConf
 
+keysym_opener = {"parenright":'(', "bracketright":'[', "braceright":'{'}
+CHECK_DELAY = 100 # miliseconds
+
 class ParenMatch:
     """Highlight matching parentheses
 
@@ -31,7 +28,6 @@
         expression from the left paren to the right paren.
 
     TODO:
-        - fix interaction with CallTips
         - extend IDLE with configuration dialog to change options
         - implement rest of Emacs highlight styles (see below)
         - print mismatch warning in IDLE status window
@@ -41,7 +37,11 @@
     to the right of a right paren.  I don't know how to do that in Tk,
     so I haven't bothered.
     """
-    menudefs = []
+    menudefs = [
+        ('edit', [
+            ("Show surrounding parens", "<<flash-paren>>"),
+        ])
+    ]
     STYLE = idleConf.GetOption('extensions','ParenMatch','style',
             default='expression')
     FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
@@ -50,14 +50,36 @@
     BELL = idleConf.GetOption('extensions','ParenMatch','bell',
             type='bool',default=1)
 
+    RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>"
+    # We want the restore event be called before the usual return and
+    # backspace events.
+    RESTORE_SEQUENCES = ("<KeyPress>", "<ButtonPress>",
+                         "<Key-Return>", "<Key-BackSpace>")
+
     def __init__(self, editwin):
         self.editwin = editwin
         self.text = editwin.text
-        self.finder = LastOpenBracketFinder(editwin)
+        # Bind the check-restore event to the function restore_event,
+        # so that we can then use activate_restore (which calls event_add)
+        # and deactivate_restore (which calls event_delete).
+        editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
+                          self.restore_event)
         self.counter = 0
-        self._restore = None
+        self.is_restore_active = 0
         self.set_style(self.STYLE)
 
+    def activate_restore(self):
+        if not self.is_restore_active:
+            for seq in self.RESTORE_SEQUENCES:
+                self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
+            self.is_restore_active = True
+        
+    def deactivate_restore(self):
+        if self.is_restore_active:
+            for seq in self.RESTORE_SEQUENCES:
+                self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
+            self.is_restore_active = False
+
     def set_style(self, style):
         self.STYLE = style
         if style == "default":
@@ -67,23 +89,38 @@
             self.create_tag = self.create_tag_expression
             self.set_timeout = self.set_timeout_none
 
-    def flash_open_paren_event(self, event):
-        index = self.finder.find(keysym_type(event.keysym))
-        if index is None:
+    def flash_paren_event(self, event):
+        indices = HyperParser(self.editwin, "insert").get_surrounding_brackets()
+        if indices is None:
             self.warn_mismatched()
             return
-        self._restore = 1
-        self.create_tag(index)
+        self.activate_restore()
+        self.create_tag(indices)
+        self.set_timeout_last()
+
+    def paren_closed_event(self, event):
+        # If it was a shortcut and not really a closing paren, quit.
+        if self.text.get("insert-1c") not in (')',']','}'):
+            return
+        hp = HyperParser(self.editwin, "insert-1c")
+        if not hp.is_in_code():
+            return
+        indices = hp.get_surrounding_brackets(keysym_opener[event.keysym], True)
+        if indices is None:
+            self.warn_mismatched()
+            return
+        self.activate_restore()
+        self.create_tag(indices)
         self.set_timeout()
 
-    def check_restore_event(self, event=None):
-        if self._restore:
-            self.text.tag_delete("paren")
-            self._restore = None
+    def restore_event(self, event=None):
+        self.text.tag_delete("paren")
+        self.deactivate_restore()
+        self.counter += 1   # disable the last timer, if there is one.
 
     def handle_restore_timer(self, timer_count):
-        if timer_count + 1 == self.counter:
-            self.check_restore_event()
+        if timer_count == self.counter:
+            self.restore_event()
 
     def warn_mismatched(self):
         if self.BELL:
@@ -92,87 +129,44 @@
     # any one of the create_tag_XXX methods can be used depending on
     # the style
 
-    def create_tag_default(self, index):
+    def create_tag_default(self, indices):
         """Highlight the single paren that matches"""
-        self.text.tag_add("paren", index)
+        self.text.tag_add("paren", indices[0])
         self.text.tag_config("paren", self.HILITE_CONFIG)
 
-    def create_tag_expression(self, index):
+    def create_tag_expression(self, indices):
         """Highlight the entire expression"""
-        self.text.tag_add("paren", index, "insert")
+        if self.text.get(indices[1]) in (')', ']', '}'):
+            rightindex = indices[1]+"+1c"
+        else:
+            rightindex = indices[1]
+        self.text.tag_add("paren", indices[0], rightindex)
         self.text.tag_config("paren", self.HILITE_CONFIG)
 
     # any one of the set_timeout_XXX methods can be used depending on
     # the style
 
     def set_timeout_none(self):
-        """Highlight will remain until user input turns it off"""
-        pass
+        """Highlight will remain until user input turns it off
+        or the insert has moved"""
+        # After CHECK_DELAY, call a function which disables the "paren" tag
+        # if the event is for the most recent timer and the insert has changed,
+        # or schedules another call for itself.
+        self.counter += 1
+        def callme(callme, self=self, c=self.counter,
+                   index=self.text.index("insert")):
+            if index != self.text.index("insert"):
+                self.handle_restore_timer(c)
+            else:
+                self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
+        self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
 
     def set_timeout_last(self):
         """The last highlight created will be removed after .5 sec"""
         # associate a counter with an event; only disable the "paren"
         # tag if the event is for the most recent timer.
+        self.counter += 1
         self.editwin.text_frame.after(self.FLASH_DELAY,
                                       lambda self=self, c=self.counter: \
                                       self.handle_restore_timer(c))
-        self.counter = self.counter + 1
-
-def keysym_type(ks):
-    # Not all possible chars or keysyms are checked because of the
-    # limited context in which the function is used.
-    if ks == "parenright" or ks == "(":
-        return "paren"
-    if ks == "bracketright" or ks == "[":
-        return "bracket"
-    if ks == "braceright" or ks == "{":
-        return "brace"
-
-class LastOpenBracketFinder:
-    num_context_lines = EditorWindow.num_context_lines
-    indentwidth = EditorWindow.indentwidth
-    tabwidth = EditorWindow.tabwidth
-    context_use_ps1 = EditorWindow.context_use_ps1
 
-    def __init__(self, editwin):
-        self.editwin = editwin
-        self.text = editwin.text
-
-    def _find_offset_in_buf(self, lno):
-        y = PyParse.Parser(self.indentwidth, self.tabwidth)
-        for context in self.num_context_lines:
-            startat = max(lno - context, 1)
-            startatindex = repr(startat) + ".0"
-            # rawtext needs to contain everything up to the last
-            # character, which was the close paren.  the parser also
-            # requires that the last line ends with "\n"
-            rawtext = self.text.get(startatindex, "insert")[:-1] + "\n"
-            y.set_str(rawtext)
-            bod = y.find_good_parse_start(
-                        self.context_use_ps1,
-                        self._build_char_in_string_func(startatindex))
-            if bod is not None or startat == 1:
-                break
-        y.set_lo(bod or 0)
-        i = y.get_last_open_bracket_pos()
-        return i, y.str
-
-    def find(self, right_keysym_type):
-        """Return the location of the last open paren"""
-        lno = index2line(self.text.index("insert"))
-        i, buf = self._find_offset_in_buf(lno)
-        if i is None \
-           or keysym_type(buf[i]) != right_keysym_type:
-            return None
-        lines_back = buf[i:].count("\n") - 1
-        # subtract one for the "\n" added to please the parser
-        upto_open = buf[:i]
-        j = upto_open.rfind("\n") + 1 # offset of column 0 of line
-        offset = i - j
-        return "%d.%d" % (lno - lines_back, offset)
-
-    def _build_char_in_string_func(self, startindex):
-        def inner(offset, startindex=startindex,
-                  icis=self.editwin.is_char_in_string):
-            return icis(startindex + "%dc" % offset)
-        return inner

Index: PyParse.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/PyParse.py,v
retrieving revision 1.5
retrieving revision 1.5.14.1
diff -u -d -r1.5 -r1.5.14.1
--- PyParse.py	17 Sep 2002 03:55:13 -0000	1.5
+++ PyParse.py	10 Oct 2005 00:05:30 -0000	1.5.14.1
@@ -13,9 +13,7 @@
 _synchre = re.compile(r"""
     ^
     [ \t]*
-    (?: if
-    |   for
-    |   while
+    (?: while
     |   else
     |   def
     |   return
@@ -144,29 +142,11 @@
     # This will be reliable iff given a reliable is_char_in_string
     # function, meaning that when it says "no", it's absolutely
     # guaranteed that the char is not in a string.
-    #
-    # Ack, hack: in the shell window this kills us, because there's
-    # no way to tell the differences between output, >>> etc and
-    # user input.  Indeed, IDLE's first output line makes the rest
-    # look like it's in an unclosed paren!:
-    # Python 1.5.2 (#0, Apr 13 1999, ...
 
-    def find_good_parse_start(self, use_ps1, is_char_in_string=None,
+    def find_good_parse_start(self, is_char_in_string=None,
                               _synchre=_synchre):
         str, pos = self.str, None
-        if use_ps1:
-            # shell window
-            ps1 = '\n' + sys.ps1
-            i = str.rfind(ps1)
-            if i >= 0:
-                pos = i + len(ps1)
-                # make it look like there's a newline instead
-                # of ps1 at the start -- hacking here once avoids
-                # repeated hackery later
-                self.str = str[:pos-1] + '\n' + str[pos:]
-            return pos
 
-        # File window -- real work.
         if not is_char_in_string:
             # no clue -- make the caller pass everything
             return None
@@ -355,6 +335,11 @@
     # Creates:
     #     self.stmt_start, stmt_end
     #         slice indices of last interesting stmt
+    #     self.stmt_bracketing
+    #         the bracketing structure of the last interesting stmt;
+    #         for example, for the statement "say(boo) or die", stmt_bracketing
+    #         will be [(0, 0), (3, 1), (8, 0)]. Strings and comments are
+    #         treated as brackets, for the matter.
     #     self.lastch
     #         last non-whitespace character before optional trailing
     #         comment
@@ -396,6 +381,7 @@
         lastch = ""
         stack = []  # stack of open bracket indices
         push_stack = stack.append
+        bracketing = [(p, 0)]
         while p < q:
             # suck up all except ()[]{}'"#\\
             m = _chew_ordinaryre(str, p, q)
@@ -416,6 +402,7 @@
 
             if ch in "([{":
                 push_stack(p)
+                bracketing.append((p, len(stack)))
                 lastch = ch
                 p = p+1
                 continue
@@ -425,6 +412,7 @@
                     del stack[-1]
                 lastch = ch
                 p = p+1
+                bracketing.append((p, len(stack)))
                 continue
 
             if ch == '"' or ch == "'":
@@ -435,14 +423,18 @@
                 # strings to a couple of characters per line.  study1
                 # also needed to keep track of newlines, and we don't
                 # have to.
+                bracketing.append((p, len(stack)+1))
                 lastch = ch
                 p = _match_stringre(str, p, q).end()
+                bracketing.append((p, len(stack)))
                 continue
 
             if ch == '#':
                 # consume comment and trailing newline
+                bracketing.append((p, len(stack)+1))
                 p = str.find('\n', p, q) + 1
                 assert p > 0
+                bracketing.append((p, len(stack)))
                 continue
 
             assert ch == '\\'
@@ -458,6 +450,7 @@
         self.lastch = lastch
         if stack:
             self.lastopenbracketpos = stack[-1]
+        self.stmt_bracketing = tuple(bracketing)
 
     # Assuming continuation is C_BRACKET, return the number
     # of spaces the next line should be indented.
@@ -582,3 +575,12 @@
     def get_last_open_bracket_pos(self):
         self._study2()
         return self.lastopenbracketpos
+
+    # the structure of the bracketing of the last interesting statement,
+    # in the format defined in _study2, or None if the text didn't contain
+    # anything
+    stmt_bracketing = None
+
+    def get_last_stmt_bracketing(self):
+        self._study2()
+        return self.stmt_bracketing

Index: PyShell.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/PyShell.py,v
retrieving revision 1.99
retrieving revision 1.99.2.1
diff -u -d -r1.99 -r1.99.2.1
--- PyShell.py	1 Sep 2005 00:39:02 -0000	1.99
+++ PyShell.py	10 Oct 2005 00:05:30 -0000	1.99.2.1
@@ -1091,11 +1091,12 @@
                 self.recall(self.text.get(next[0], next[1]), event)
                 return "break"
             # No stdin mark -- just get the current line, less any prompt
-            line = self.text.get("insert linestart", "insert lineend")
-            last_line_of_prompt = sys.ps1.split('\n')[-1]
-            if line.startswith(last_line_of_prompt):
-                line = line[len(last_line_of_prompt):]
-            self.recall(line, event)
+            indices = self.text.tag_nextrange("console", "insert linestart")
+            if indices and \
+               self.text.compare(indices[0], "<=", "insert linestart"):
+                self.recall(self.text.get(indices[1], "insert lineend"), event)
+            else:
+                self.recall(self.text.get("insert linestart", "insert lineend"), event)
             return "break"
         # If we're between the beginning of the line and the iomark, i.e.
         # in the prompt area, move to the end of the prompt

Index: config-extensions.def
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/config-extensions.def,v
retrieving revision 1.15
retrieving revision 1.15.6.1
diff -u -d -r1.15 -r1.15.6.1
--- config-extensions.def	6 Jun 2004 01:29:22 -0000	1.15
+++ config-extensions.def	10 Oct 2005 00:05:30 -0000	1.15.6.1
@@ -52,22 +52,30 @@
 
 [CallTips]
 enable=1
+[CallTips_cfgBindings]
+force-open-calltip=<Control-Key-backslash>
 [CallTips_bindings]
-paren-open=<Key-parenleft>
-paren-close=<Key-parenright>
-check-calltip-cancel=<KeyRelease>
-calltip-cancel=<ButtonPress> <Key-Escape>
+try-open-calltip=<KeyRelease-parenleft>
+refresh-calltip=<KeyRelease-parenright> <KeyRelease-0>
 
 [ParenMatch]
-enable=0
+enable=1
 style= expression
 flash-delay= 500
 bell= 1
-hilite-foreground= black
-hilite-background= #43cd80
+[ParenMatch_cfgBindings]
+flash-paren=<Control-Key-0>
 [ParenMatch_bindings]
-flash-open-paren=<KeyRelease-parenright> <KeyRelease-bracketright> <KeyRelease-braceright>
-check-restore=<KeyPress>
+paren-closed=<KeyRelease-parenright> <KeyRelease-bracketright> <KeyRelease-braceright>
+
+[AutoComplete]
+enable=1
+popupwait=0
+[AutoComplete_cfgBindings]
+force-open-completions=<Control-Key-space>
+[AutoComplete_bindings]
+autocomplete=<Key-Tab>
+try-open-completions=<KeyRelease-period> <KeyRelease-slash> <KeyRelease-backslash>
 
 [CodeContext]
 enable=1

Index: configDialog.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/configDialog.py,v
retrieving revision 1.62
retrieving revision 1.62.2.1
diff -u -d -r1.62 -r1.62.2.1
--- configDialog.py	22 Jul 2005 21:49:29 -0000	1.62
+++ configDialog.py	10 Oct 2005 00:05:30 -0000	1.62.2.1
@@ -1106,6 +1106,13 @@
             idleConf.userCfg[configType].Save()
         self.ResetChangedItems() #clear the changed items dict
 
+    def DeactivateCurrentConfig(self):
+        #Before a config is saved, some cleanup of current
+        #config must be done - remove the previous keybindings
+        winInstances=self.parent.instance_dict.keys()
+        for instance in winInstances:
+            instance.RemoveKeybindings()
+    
     def ActivateConfigChanges(self):
         "Dynamically apply configuration changes"
         winInstances=self.parent.instance_dict.keys()
@@ -1113,7 +1120,7 @@
             instance.ResetColorizer()
             instance.ResetFont()
             instance.set_notabs_indentwidth()
-            instance.ResetKeybindings()
+            instance.ApplyKeybindings()
             instance.reset_help_menu_entries()
 
     def Cancel(self):
@@ -1124,6 +1131,7 @@
         self.destroy()
 
     def Apply(self):
+        self.DeactivateCurrentConfig()
         self.SaveAllChangedConfigs()
         self.ActivateConfigChanges()
 

Index: run.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/idlelib/run.py,v
retrieving revision 1.32
retrieving revision 1.32.2.1
diff -u -d -r1.32 -r1.32.2.1
--- run.py	5 May 2005 23:29:54 -0000	1.32
+++ run.py	10 Oct 2005 00:05:31 -0000	1.32.2.1
@@ -9,6 +9,8 @@
 import Queue
 
 import CallTips
+import AutoComplete
+
 import RemoteDebugger
 import RemoteObjectBrowser
 import StackViewer
@@ -275,6 +277,7 @@
         self.rpchandler = rpchandler
         self.locals = __main__.__dict__
         self.calltip = CallTips.CallTips()
+        self.autocomplete = AutoComplete.AutoComplete()
 
     def runcode(self, code):
         try:
@@ -305,6 +308,9 @@
     def get_the_calltip(self, name):
         return self.calltip.fetch_tip(name)
 
+    def get_the_completion_list(self, what, mode):
+        return self.autocomplete.fetch_completions(what, mode)
+
     def stackviewer(self, flist_oid=None):
         if self.usr_exc_info:
             typ, val, tb = self.usr_exc_info



More information about the Python-checkins mailing list