From 4fd1ad0ee73ac23d14c0a6208e50628b6669307f 2008-07-17 04:18:47
From: Gael Varoquaux <gael.varoquaux@normalesup.org>
Date: 2008-07-17 04:18:47
Subject: [PATCH] Improve tab-completion.

---

diff --git a/IPython/frontend/linefrontendbase.py b/IPython/frontend/linefrontendbase.py
index c82e645..2cc46a6 100644
--- a/IPython/frontend/linefrontendbase.py
+++ b/IPython/frontend/linefrontendbase.py
@@ -32,6 +32,8 @@ class LineFrontEndBase(FrontEndBase):
     # it when there is an exception.
     prompt_number = 1
 
+    # To bootstrap
+    last_result = dict(number=0)
 
     #--------------------------------------------------------------------------
     # Public API
@@ -87,11 +89,15 @@ class LineFrontEndBase(FrontEndBase):
 
 
     def is_complete(self, string):
-        if ( len(self.get_current_edit_buffer().split('\n'))>2 
+        if string in ('', '\n'):
+            return True
+        elif ( len(self.get_current_edit_buffer().split('\n'))>2 
                         and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
             return False
         else:
-            return FrontEndBase.is_complete(self, string)
+            # Add line returns here, to make sure that the statement is
+            # complete.
+            return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
     
 
     def execute(self, python_string, raw_string=None):
diff --git a/IPython/frontend/wx/console_widget.py b/IPython/frontend/wx/console_widget.py
index 24765af..9ff4bc7 100644
--- a/IPython/frontend/wx/console_widget.py
+++ b/IPython/frontend/wx/console_widget.py
@@ -63,6 +63,9 @@ _STDERR_STYLE = 16
 _TRACE_STYLE  = 17
 
 
+# system colors
+SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
+
 #-------------------------------------------------------------------------------
 # The console widget class
 #-------------------------------------------------------------------------------
@@ -99,10 +102,10 @@ class ConsoleWidget(editwindow.EditWindow):
     
     def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, 
                         size=wx.DefaultSize, style=0, 
-                        autocomplete_mode='STC'):
-        """ Autocomplete_mode: Can be 'IPYTHON' or 'STC'
-            'IPYTHON' show autocompletion the ipython way
-            'STC" show it scintilla text control way
+                        autocomplete_mode='popup'):
+        """ Autocomplete_mode: Can be 'popup' or 'text'
+            'text' show autocompletion in the text buffer
+            'popup' show it with a dropdown popup
         """
         editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
         self.configure_scintilla()
@@ -265,7 +268,7 @@ class ConsoleWidget(editwindow.EditWindow):
         """
         self.SetCaretForeground(self.carret_color)
 
-        self.StyleClearAll()
+        #self.StyleClearAll()
         self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,  
                           "fore:#FF0000,back:#0000FF,bold")
         self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
@@ -275,32 +278,18 @@ class ConsoleWidget(editwindow.EditWindow):
             self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
 
 
-    def write_completion(self, possibilities):
-        if self.autocomplete_mode == 'IPYTHON':
+    def write_completion(self, possibilities, mode=None):
+        if mode=='text' or self.autocomplete_mode == 'text':
             max_len = len(max(possibilities, key=len))
-            max_symbol = ' '*max_len
-            
-            #now we check how much symbol we can put on a line...
-            test_buffer = max_symbol + ' '*4
-            
-            allowed_symbols = 80/len(test_buffer)
-            if allowed_symbols == 0:
-                allowed_symbols = 1
-            
-            pos = 1
-            buf = ''
+            current_buffer = self.get_current_edit_buffer()
+           
+            self.write('\n')
             for symbol in possibilities:
-                #buf += symbol+'\n'#*spaces)
-                if pos < allowed_symbols:
-                    spaces = max_len - len(symbol) + 4
-                    buf += symbol+' '*spaces
-                    pos += 1
-                else:
-                    buf += symbol+'\n'
-                    pos = 1
-            self.write(buf)
+                self.write(symbol.ljust(max_len))
+            self.new_prompt(self.prompt % (self.last_result['number'] + 1))
+            self.replace_current_edit_buffer(current_buffer)
         else:
-            possibilities.sort()  # Python sorts are case sensitive
+            #possibilities.sort()  # Python sorts are case sensitive
             self.AutoCompSetIgnoreCase(False)
             self.AutoCompSetAutoHide(False)
             #let compute the length ot text)last word
diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py
index 0580b9a..c5632a6 100644
--- a/IPython/frontend/wx/wx_frontend.py
+++ b/IPython/frontend/wx/wx_frontend.py
@@ -51,10 +51,24 @@ class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
         self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
 
 
-    def do_completion(self):
+    def do_completion(self, mode=None):
+        """ Do code completion. 
+            mode can be 'text', 'popup' or 'none' to use default.
+        """
         line = self.get_current_edit_buffer()
         completions = self.complete(line)
-        self.write_completion(completions)
+        self.write_completion(completions, mode=mode)
+
+
+    def update_completion(self):
+        line = self.get_current_edit_buffer()
+        if self.AutoCompActive() and not line[-1] == '.':
+            line = line[:-1]
+            completions = self.complete(line)
+            choose_single = self.AutoCompGetChooseSingle()
+            self.AutoCompSetChooseSingle(False)
+            self.write_completion(completions, mode='popup')
+            self.AutoCompSetChooseSingle(choose_single)
 
 
     def execute(self, *args, **kwargs):
@@ -64,7 +78,8 @@ class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
     
     def after_execute(self):
         PrefilterFrontEnd.after_execute(self)
-        del self._cursor
+        if hasattr(self, '_cursor'):
+            del self._cursor
 
     #--------------------------------------------------------------------------
     # Private API
@@ -78,6 +93,11 @@ class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
         current_line_number = self.GetCurrentLine()
         if self.AutoCompActive():
             event.Skip()
+            if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE): 
+                wx.CallAfter(self.do_completion)
+            elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
+                            wx.WXK_RIGHT):
+                wx.CallAfter(self.update_completion)
         else:
             # Up history
             if event.KeyCode == wx.WXK_UP and (
@@ -102,14 +122,21 @@ class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
             elif event.KeyCode == ord('\t'):
                 last_line = self.get_current_edit_buffer().split('\n')[-1]
                 if not re.match(r'^\s*$', last_line):
-                    self.do_completion()
+                    self.do_completion(mode='text')
                 else:
                     event.Skip()
             else:
                 ConsoleWidget._on_key_down(self, event, skip=skip)
 
 
-       
+    def _on_key_up(self, event, skip=True):
+        if event.KeyCode == 59:
+            # Intercepting '.'
+            event.Skip()
+            self.do_completion(mode='popup')
+        else:
+            ConsoleWidget._on_key_up(self, event, skip=skip)
+
 
 if __name__ == '__main__':
     class MainWindow(wx.Frame):