reenable multiline history for terminals #838

Merged
merged 3 commits into from Oct 18, 2011
View
13 IPython/core/interactiveshell.py
@@ -307,6 +307,9 @@ def _exiter_default(self):
Automatically call the pdb debugger after every exception.
"""
)
+ multiline_history = CBool(False, config=True,
+ help="Store multiple line spanning cells as a single entry in history."
@takluyver
IPython member
takluyver added a line comment Oct 15, 2011

This description could probably be clearer. We always store multiline cells in our own history; this affects what happens in readline history - the history you get by pressing up & down arrows, or using Ctrl-R.

Also, I think the config option should be on the TerminalInteractiveShell subclass (IPython.frontends.terminal.interactiveshell), since it doesn't affect other frontends.

@juliantaylor
juliantaylor added a line comment Oct 16, 2011

it needs to be in core as there the history is recalled from disk and one must decide to do it line by line or per cell

@takluyver
IPython member
takluyver added a line comment Oct 16, 2011

The code that's initialising readline should now only be run by the terminal frontend (as self.readline_use is now False for the ZMQShell).

In time, we should move the init_readline code to TerminalInteractiveShell as well. In the meantime, just to be on the safe side, we can use getattr(self, 'multiline_history', True), which will behave even if the code does somehow get run by the other frontends.

@minrk
IPython member
minrk added a line comment Oct 16, 2011

Instead of using getattr, let's make multiline_history a non-configurable trait on core.InteractiveShell, with a note that it should be removed when we finish getting the readline code out of the core.

@takluyver
IPython member
takluyver added a line comment Oct 16, 2011

OK, that makes sense. Presumably then we override it with a configurable trait in TerminalInteractiveShell.

@fperez
IPython member
fperez added a line comment Oct 17, 2011

@juliantaylor, does that make sense? It seems that this is almost ready to go, so if you implement @mink's suggestion and make the default true (as there seems to be interest in that), this should be in good shape.

I would, however, like to see a test added. A simple test that pushes two lines of history and gets them back intact should suffice. It must be protected with a decorator so it only runs when readline is present, though.

That will ensure we don't regress on this feature again in the future...

With those changes and a test, I don't see what else would be needed and we can proceed to merge. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ )
prompt_in1 = Unicode('In [\\#]: ', config=True)
prompt_in2 = Unicode(' .\\D.: ', config=True)
@@ -1721,9 +1724,13 @@ def refill_readline_hist(self):
for _, _, cell in self.history_manager.get_tail(1000,
include_latest=True):
if cell.strip(): # Ignore blank lines
- for line in cell.splitlines():
- self.readline.add_history(py3compat.unicode_to_str(line,
- stdin_encoding))
+ if self.multiline_history:
+ self.readline.add_history(py3compat.unicode_to_str(cell.rstrip(),
+ stdin_encoding))
+ else:
+ for line in cell.splitlines():
+ self.readline.add_history(py3compat.unicode_to_str(line,
+ stdin_encoding))
def set_next_input(self, s):
""" Sets the 'default' input string for the next command line.
View
15 IPython/frontend/terminal/interactiveshell.py
@@ -229,6 +229,14 @@ def mainloop(self, display_banner=None):
# handling seems rather unpredictable...
self.write("\nKeyboardInterrupt in interact()\n")
+ def _replace_rlhist_multiline(self, source_raw, hlen_before_cell):
+ """Store multiple lines as a single entry in history"""
+ if self.multiline_history and self.has_readline:
+ hlen = self.readline.get_current_history_length()
+ for i in range(hlen - hlen_before_cell):
+ self.readline.remove_history_item(hlen - i - 1)
+ self.readline.add_history(source_raw.rstrip())
+
def interact(self, display_banner=None):
"""Closely emulate the interactive Python console."""
@@ -245,6 +253,7 @@ def interact(self, display_banner=None):
self.show_banner()
more = False
+ hlen_before_cell = self.readline.get_current_history_length()
# Mark activity in the builtins
__builtin__.__dict__['__IPYTHON__active'] += 1
@@ -281,7 +290,9 @@ def interact(self, display_banner=None):
#double-guard against keyboardinterrupts during kbdint handling
try:
self.write('\nKeyboardInterrupt\n')
- self.input_splitter.reset()
+ source_raw = self.input_splitter.source_raw_reset()[1]
+ self._replace_rlhist_multiline(source_raw, hlen_before_cell)
+ hlen_before_cell = self.readline.get_current_history_length()
more = False
except KeyboardInterrupt:
pass
@@ -309,6 +320,8 @@ def interact(self, display_banner=None):
self.edit_syntax_error()
if not more:
source_raw = self.input_splitter.source_raw_reset()[1]
+ self._replace_rlhist_multiline(source_raw, hlen_before_cell)
+ hlen_before_cell = self.readline.get_current_history_length()
self.run_cell(source_raw, store_history=True)
# We are off again...