From fe694a7d1242b899a66aa925f47732a926bbab77 2009-03-30 01:31:18
From: Gael Varoquaux <gael.varoquaux@normalesup.org>
Date: 2009-03-30 01:31:18
Subject: [PATCH] ENH: Enter adds lines at the right position
---

diff --git a/IPython/frontend/linefrontendbase.py b/IPython/frontend/linefrontendbase.py
index 84d45f0..b33ea50 100644
--- a/IPython/frontend/linefrontendbase.py
+++ b/IPython/frontend/linefrontendbase.py
@@ -188,16 +188,6 @@ class LineFrontEndBase(FrontEndBase):
         # Create a false result, in case there is an exception
         self.last_result = dict(number=self.prompt_number)
 
-        ## try:
-        ##     self.history.input_cache[-1] = raw_string.rstrip()
-        ##     result = self.shell.execute(python_string)
-        ##     self.last_result = result
-        ##     self.render_result(result)
-        ## except:
-        ##     self.show_traceback()
-        ## finally:
-        ##     self.after_execute()
-
         try:
             try:
                 self.history.input_cache[-1] = raw_string.rstrip()
@@ -318,9 +308,20 @@ class LineFrontEndBase(FrontEndBase):
     # Private API
     #--------------------------------------------------------------------------
  
-    def _on_enter(self):
+    def _on_enter(self, new_line_pos=0):
         """ Called when the return key is pressed in a line editing
             buffer.
+
+            Parameters
+            ----------
+            new_line_pos : integer, optional
+                Position of the new line to add, starting from the
+                end (0 adds a new line after the last line, -1 before
+                the last line...)
+
+            Returns
+            -------
+            True if execution is triggered
         """
         current_buffer = self.input_buffer
         # XXX: This string replace is ugly, but there should be no way it
@@ -331,14 +332,25 @@ class LineFrontEndBase(FrontEndBase):
         cleaned_buffer = self.prefilter_input(prompt_less_buffer)
         if self.is_complete(cleaned_buffer):
             self.execute(cleaned_buffer, raw_string=current_buffer)
+            return True
         else:
-            self.input_buffer += self.continuation_prompt() + \
-                  self._get_indent_string(prompt_less_buffer[:-1])
-            if len(current_buffer.split('\n')) == 2:
-                self.input_buffer += '\t'
+            new_line_pos = -new_line_pos
+            lines = current_buffer.split('\n')[:-1]
+            prompt_less_lines = prompt_less_buffer.split('\n')
+            new_line = self.continuation_prompt() + \
+                  self._get_indent_string('\n'.join(
+                                    prompt_less_lines[:new_line_pos-1]))
+            if len(lines) == 2:
+                new_line += '\t'
             if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
-                self.input_buffer += '\t'
+                new_line += '\t'
 
+            if new_line_pos == 0:
+                lines.append(new_line)
+            else:
+                lines.insert(new_line_pos, new_line)
+            self.input_buffer = '\n'.join(lines)
+            
 
     def _get_indent_string(self, string):
         """ Return the string of whitespace that prefixes a line. Used to
diff --git a/IPython/frontend/wx/console_widget.py b/IPython/frontend/wx/console_widget.py
index 35c2d9c..56761e8 100644
--- a/IPython/frontend/wx/console_widget.py
+++ b/IPython/frontend/wx/console_widget.py
@@ -472,6 +472,11 @@ class ConsoleWidget(editwindow.EditWindow):
             Return True if event as been catched.
         """
         catched = True
+        # XXX: Would the right way to do this be to have a
+        #  dictionary at the instance level associating keys with
+        #  callbacks? How would we deal with inheritance? And Do the
+        #  different callbacks share local variables?
+
         # Intercept some specific keys.
         if event.KeyCode == ord('L') and event.ControlDown() :
             self.scroll_to_bottom()
@@ -489,6 +494,10 @@ class ConsoleWidget(editwindow.EditWindow):
             self.ScrollPages(-1)
         elif event.KeyCode == wx.WXK_PAGEDOWN:
             self.ScrollPages(1)
+        elif event.KeyCode == wx.WXK_HOME:
+            self.GotoPos(self.GetLength())
+        elif event.KeyCode == wx.WXK_END:
+            self.GotoPos(self.GetLength())
         elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
             self.ScrollLines(-1)
         elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
@@ -500,17 +509,19 @@ class ConsoleWidget(editwindow.EditWindow):
             event.Skip()
         else:
             if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
-                        event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
+                                event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
+                                                    wx.MOD_SHIFT):
                 catched = True
                 if not self.enter_catched:
                     self.CallTipCancel()
-                    self.write('\n', refresh=False)
-                    # Under windows scintilla seems to be doing funny
-                    # stuff to the line returns here, but the getter for
-                    # input_buffer filters this out.
-                    if sys.platform == 'win32':
-                        self.input_buffer = self.input_buffer
-                    self._on_enter()
+                    if event.Modifiers == wx.MOD_SHIFT:
+                        # Try to force execution
+                        self.GotoPos(self.GetLength())
+                        self.write('\n' + self.continuation_prompt(), 
+                                        refresh=False)
+                        self._on_enter()
+                    else:
+                        self._on_enter()
                     self.enter_catched = True
 
             elif event.KeyCode == wx.WXK_HOME:
diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py
index b6c963c..e20e558 100644
--- a/IPython/frontend/wx/wx_frontend.py
+++ b/IPython/frontend/wx/wx_frontend.py
@@ -284,6 +284,8 @@ class WxController(ConsoleWidget, PrefilterFrontEnd):
         """ Execute a command, not only in the model, but also in the
             view.
         """
+        # XXX: This method needs to be integrated in the base fronted
+        # interface
         if hidden:
             return self.shell.execute(command)
         else:
@@ -386,7 +388,7 @@ class WxController(ConsoleWidget, PrefilterFrontEnd):
             widget handle them, and put our logic afterward.
         """
         # FIXME: This method needs to be broken down in smaller ones.
-        current_line_number = self.GetCurrentLine()
+        current_line_num = self.GetCurrentLine()
         if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
             # Capture Control-C
             if self._input_state == 'subprocess':
@@ -432,7 +434,7 @@ class WxController(ConsoleWidget, PrefilterFrontEnd):
         else:
             # Up history
             if event.KeyCode == wx.WXK_UP and (
-                    ( current_line_number == self.current_prompt_line and
+                    ( current_line_num == self.current_prompt_line and
                         event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) ) 
                     or event.ControlDown() ):
                 new_buffer = self.get_history_previous(
@@ -444,7 +446,7 @@ class WxController(ConsoleWidget, PrefilterFrontEnd):
                         self.GotoPos(self.current_prompt_pos)
             # Down history
             elif event.KeyCode == wx.WXK_DOWN and (
-                    ( current_line_number == self.LineCount -1 and
+                    ( current_line_num == self.LineCount -1 and
                         event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) ) 
                     or event.ControlDown() ):
                 new_buffer = self.get_history_next()
@@ -452,7 +454,7 @@ class WxController(ConsoleWidget, PrefilterFrontEnd):
                     self.input_buffer = new_buffer
             # Tab-completion
             elif event.KeyCode == ord('\t'):
-                current_line, current_line_number = self.CurLine
+                current_line, current_line_num = self.CurLine
                 if not re.match(r'^\s*$', current_line):
                     self.complete_current_input()
                     if self.AutoCompActive():
@@ -467,16 +469,16 @@ class WxController(ConsoleWidget, PrefilterFrontEnd):
                 # independant of IPython
                 current_line, _ = self.CurLine
                 current_pos = self.GetCurrentPos()
-                current_line_number = self.LineFromPosition(current_pos)
+                current_line_num = self.LineFromPosition(current_pos)
                 current_col = self.GetColumn(current_pos)
                 len_prompt = len(self.continuation_prompt())
                 if ( current_line.startswith(self.continuation_prompt())
-                        and current_col == len_prompt):
+                                            and current_col == len_prompt):
                     new_lines = []
                     for line_num, line in enumerate(
                                     self.input_buffer.split('\n')):
                         if (line_num + self.current_prompt_line ==
-                                            current_line_number):
+                                            current_line_num):
                             new_lines.append(line[len_prompt:])
                         else:
                             new_lines.append('\n'+line)
@@ -515,9 +517,22 @@ class WxController(ConsoleWidget, PrefilterFrontEnd):
     def _on_enter(self):
         """ Called on return key down, in readline input_state.
         """
+        last_line_num = self.LineFromPosition(self.GetLength())
+        current_line_num = self.LineFromPosition(self.GetCurrentPos())
+        new_line_pos = (last_line_num - current_line_num)
         if self.debug:
             print >>sys.__stdout__, repr(self.input_buffer)
-        PrefilterFrontEnd._on_enter(self)
+        self.write('\n', refresh=False)
+        # Under windows scintilla seems to be doing funny
+        # stuff to the line returns here, but the getter for
+        # input_buffer filters this out.
+        if sys.platform == 'win32':
+            self.input_buffer = self.input_buffer
+        has_executed = PrefilterFrontEnd._on_enter(self, 
+                                            new_line_pos=new_line_pos)
+        if not has_executed:
+            self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
+        return has_executed
 
 
     #--------------------------------------------------------------------------