diff --git a/IPython/frontend/linefrontendbase.py b/IPython/frontend/linefrontendbase.py index 9dd3010..5aedb72 100644 --- a/IPython/frontend/linefrontendbase.py +++ b/IPython/frontend/linefrontendbase.py @@ -295,6 +295,17 @@ class LineFrontEndBase(FrontEndBase): self.write(prompt) + def continuation_prompt(self): + """Returns the current continuation prompt. + Overridden to generate a continuation prompt matching the length of the + current prompt.""" + + # FIXME: This is a bad hack.. I need to find a way to use the 'Prompt2' + # class in IPython/kernel/prompts.py. Basically, I am trying to get the + # length of the current prompt ("In ['number']"). + return ("."*(5+len(str(self.last_result['number']))) + ':') + + def execute_command(self, command, hidden=False): """ Execute a command, not only in the model, but also in the view, if any. @@ -310,14 +321,19 @@ class LineFrontEndBase(FrontEndBase): buffer. """ current_buffer = self.input_buffer - cleaned_buffer = self.prefilter_input(current_buffer) + # XXX: This string replace is ugly, but there should be no way it + # fails. + prompt_less_buffer = re.sub('^' + self.continuation_prompt(), + '', current_buffer).replace('\n' + self.continuation_prompt(), + '\n') + cleaned_buffer = self.prefilter_input(prompt_less_buffer) if self.is_complete(cleaned_buffer): self.execute(cleaned_buffer, raw_string=current_buffer) else: - self.input_buffer += self._get_indent_string( - current_buffer[:-1]) + 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\t' + self.input_buffer += '\t' if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'): self.input_buffer += '\t' diff --git a/IPython/frontend/prefilterfrontend.py b/IPython/frontend/prefilterfrontend.py index fbb7b6b..7b44ead 100644 --- a/IPython/frontend/prefilterfrontend.py +++ b/IPython/frontend/prefilterfrontend.py @@ -24,6 +24,7 @@ __docformat__ = "restructuredtext en" import sys import pydoc import os +import re import __builtin__ from IPython.ipmaker import make_IPython @@ -113,9 +114,9 @@ class PrefilterFrontEnd(LineFrontEndBase): if not 'banner' in kwargs and self.banner is None: - self.banner = self.ipython0.BANNER + """ -This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code.""" + self.banner = self.ipython0.BANNER + # FIXME: __init__ and start should be two different steps self.start() #-------------------------------------------------------------------------- @@ -182,7 +183,9 @@ This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code.""" def complete(self, line): # FIXME: This should be factored out in the linefrontendbase # method. + word = self._get_completion_text(line) word = line.split('\n')[-1].split(' ')[-1] + print 'Completion', word completions = self.ipython0.complete(word) # FIXME: The proper sort should be done in the complete method. key = lambda x: x.replace('_', '') @@ -255,3 +258,18 @@ This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code.""" """ self.ipython0.atexit_operations() + + def _get_completion_text(self, line): + """ Returns the text to be completed by breaking the line at specified + delimiters. + """ + # Break at: spaces, '=', all parentheses (except if balanced). + # FIXME2: In the future, we need to make the implementation similar to + # that in the 'pyreadline' module (modes/basemode.py) where we break at + # each delimiter and try to complete the residual line, until we get a + # successful list of completions. + expression = '\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})' + complete_sep = re.compile(expression) + text = complete_sep.split(line)[-1] + return text + diff --git a/IPython/frontend/wx/console_widget.py b/IPython/frontend/wx/console_widget.py index 30ec0b8..176bf2b 100644 --- a/IPython/frontend/wx/console_widget.py +++ b/IPython/frontend/wx/console_widget.py @@ -216,6 +216,16 @@ class ConsoleWidget(editwindow.EditWindow): """ return self.GetSize()[0]/self.GetCharWidth() + + def clear_screen(self): + """ Empty completely the widget. + """ + self.ClearAll() + self.new_prompt(self.input_prompt_template.substitute( + number=(self.last_result['number'] + 1))) + + + #-------------------------------------------------------------------------- # EditWindow API #-------------------------------------------------------------------------- @@ -391,15 +401,16 @@ class ConsoleWidget(editwindow.EditWindow): catched = True elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK): - if self.GetCurrentPos() > self.current_prompt_pos: + if not self._keep_cursor_in_buffer(): event.Skip() catched = True if skip and not catched: # Put the cursor back in the edit region - if self.GetCurrentPos() < self.current_prompt_pos: - self.GotoPos(self.current_prompt_pos) - else: + if not self._keep_cursor_in_buffer(): + if (self.GetCurrentPos() == self.GetLength() + and event.KeyCode == wx.WXK_DELETE): + pass event.Skip() return catched @@ -408,9 +419,44 @@ class ConsoleWidget(editwindow.EditWindow): def _on_key_up(self, event, skip=True): """ If cursor is outside the editing region, put it back. """ - event.Skip() - if self.GetCurrentPos() < self.current_prompt_pos: + if skip: + event.Skip() + self._keep_cursor_in_buffer() + + + def _keep_cursor_in_buffer(self): + """ Checks if the cursor is where it is allowed to be. If not, + put it back. + + Returns + ------- + cursor_moved: Boolean + whether or not the cursor was moved by this routine. + + Notes + ------ + WARNING: This does proper checks only for horizontal + movements. + """ + current_pos = self.GetCurrentPos() + if current_pos < self.current_prompt_pos: self.GotoPos(self.current_prompt_pos) + return True + line, line_pos = self.GetCurLine() + # Jump the continuation prompt + continuation_prompt = self.continuation_prompt() + if ( line.startswith(continuation_prompt) + and line_pos < len(continuation_prompt)+1): + if line_pos < 2: + # We are at the beginning of the line, trying to move + # forward: jump forward. + self.GotoPos(current_pos + 1 + + len(continuation_prompt) - line_pos) + else: + # Jump back up + self.GotoPos(self.GetLineEndPosition(self.GetCurrentLine()-1)) + return True + return False diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py index d3ae507..dc10e23 100644 --- a/IPython/frontend/wx/wx_frontend.py +++ b/IPython/frontend/wx/wx_frontend.py @@ -485,6 +485,12 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): wx.CallAfter(self._popup_completion, create=True) else: ConsoleWidget._on_key_up(self, event, skip=skip) + if (self.input_buffer.split('\n')[-1] == self.continuation_prompt() + and self._input_state == 'readline'): + # Make sure the continuation_prompt is followed by a whitespace + position = self.GetCurrentPos() + self.input_buffer += ' ' + self.GotoPos(position) def _on_enter(self):