From 3395782ba6c7bcad5ee945a92fe91fde62df77fb 2010-09-08 15:56:58 From: epatters Date: 2010-09-08 15:56:58 Subject: [PATCH] First cut at support readline-esque history filtering in ConsoleWidget. --- diff --git a/IPython/frontend/qt/console/history_console_widget.py b/IPython/frontend/qt/console/history_console_widget.py index 8ff5916..09be4c0 100644 --- a/IPython/frontend/qt/console/history_console_widget.py +++ b/IPython/frontend/qt/console/history_console_widget.py @@ -7,7 +7,7 @@ from console_widget import ConsoleWidget class HistoryConsoleWidget(ConsoleWidget): """ A ConsoleWidget that keeps a history of the commands that have been - executed. + executed and provides a readline-esque interface to this history. """ #--------------------------------------------------------------------------- @@ -16,8 +16,11 @@ class HistoryConsoleWidget(ConsoleWidget): def __init__(self, *args, **kw): super(HistoryConsoleWidget, self).__init__(*args, **kw) + + # HistoryConsoleWidget protected variables. self._history = [] self._history_index = 0 + self._history_prefix = '' #--------------------------------------------------------------------------- # 'ConsoleWidget' public interface @@ -54,14 +57,31 @@ class HistoryConsoleWidget(ConsoleWidget): """ prompt_cursor = self._get_prompt_cursor() if self._get_cursor().blockNumber() == prompt_cursor.blockNumber(): - self.history_previous() - # Go to the first line of prompt for seemless history scrolling. + # Set a search prefix based on the cursor position. + col = self._get_input_buffer_cursor_column() + input_buffer = self.input_buffer + if self._history_index == len(self._history) or \ + (self._history_prefix and col != len(self._history_prefix)): + self._history_index = len(self._history) + self._history_prefix = input_buffer[:col] + + # Perform the search. + self.history_previous(self._history_prefix) + + # Go to the first line of the prompt for seemless history scrolling. + # Emulate readline: keep the cursor position fixed for a prefix + # search. cursor = self._get_prompt_cursor() - cursor.movePosition(QtGui.QTextCursor.EndOfLine) + if self._history_prefix: + cursor.movePosition(QtGui.QTextCursor.Right, + n=len(self._history_prefix)) + else: + cursor.movePosition(QtGui.QTextCursor.EndOfLine) self._set_cursor(cursor) return False + return True def _down_pressed(self): @@ -70,38 +90,69 @@ class HistoryConsoleWidget(ConsoleWidget): """ end_cursor = self._get_end_cursor() if self._get_cursor().blockNumber() == end_cursor.blockNumber(): - self.history_next() + + # Perform the search. + self.history_next(self._history_prefix) + + # Emulate readline: keep the cursor position fixed for a prefix + # search. (We don't need to move the cursor to the end of the buffer + # in the other case because this happens automatically when the + # input buffer is set.) + if self._history_prefix: + cursor = self._get_prompt_cursor() + cursor.movePosition(QtGui.QTextCursor.Right, + n=len(self._history_prefix)) + self._set_cursor(cursor) + return False + return True #--------------------------------------------------------------------------- # 'HistoryConsoleWidget' public interface #--------------------------------------------------------------------------- - def history_previous(self): - """ If possible, set the input buffer to the previous item in the - history. - """ - if self._history_index > 0: - self._history_index -= 1 - self.input_buffer = self._history[self._history_index] + def history_previous(self, prefix=''): + """ If possible, set the input buffer to a previous item in the history. - def history_next(self): - """ Set the input buffer to the next item in the history, or a blank - line if there is no subsequent item. + Parameters: + ----------- + prefix : str, optional + If specified, search for an item with this prefix. """ - if self._history_index < len(self._history): + index = self._history_index + while index > 0: + index -= 1 + history = self._history[index] + if history.startswith(prefix): + break + else: + history = None + + if history is not None: + self._history_index = index + self.input_buffer = history + + def history_next(self, prefix=''): + """ Set the input buffer to a subsequent item in the history, or to the + original search prefix if there is no such item. + + Parameters: + ----------- + prefix : str, optional + If specified, search for an item with this prefix. + """ + while self._history_index < len(self._history) - 1: self._history_index += 1 - if self._history_index < len(self._history): - self.input_buffer = self._history[self._history_index] - else: - self.input_buffer = '' - - #--------------------------------------------------------------------------- - # 'HistoryConsoleWidget' protected interface - #--------------------------------------------------------------------------- + history = self._history[self._history_index] + if history.startswith(prefix): + break + else: + self._history_index = len(self._history) + history = prefix + self.input_buffer = history - def _set_history(self, history): + def set_history(self, history): """ Replace the current history with a sequence of history items. """ self._history = list(history)