Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement support for 'cell' mode with Ctrl-Enter.

Now, once c-enter has been used, the widget will continue accepting
more input just as if the input was indented: even complete
expressions won't trigger execution, until an extra blank line (or
Shift-Enter) is used.
  • Loading branch information...
commit 60bf09757856f7769eb79c2581a86656de4d275a 1 parent 9ff92ab
@fperez fperez authored
View
59 IPython/core/inputsplitter.py
@@ -60,6 +60,7 @@
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#-----------------------------------------------------------------------------
+from __future__ import print_function
#-----------------------------------------------------------------------------
# Imports
@@ -71,6 +72,7 @@
# IPython modules
from IPython.utils.text import make_quoted_expr
+
#-----------------------------------------------------------------------------
# Globals
#-----------------------------------------------------------------------------
@@ -81,14 +83,14 @@
# for all intents and purposes they constitute the 'IPython syntax', so they
# should be considered fixed.
-ESC_SHELL = '!'
-ESC_SH_CAP = '!!'
-ESC_HELP = '?'
-ESC_HELP2 = '??'
-ESC_MAGIC = '%'
-ESC_QUOTE = ','
-ESC_QUOTE2 = ';'
-ESC_PAREN = '/'
+ESC_SHELL = '!' # Send line to underlying system shell
+ESC_SH_CAP = '!!' # Send line to system shell and capture output
+ESC_HELP = '?' # Find information about object
+ESC_HELP2 = '??' # Find extra-detailed information about object
+ESC_MAGIC = '%' # Call magic function
+ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
+ESC_QUOTE2 = ';' # Quote all args as a single string, call
+ESC_PAREN = '/' # Call first argument with rest of line as arguments
#-----------------------------------------------------------------------------
# Utilities
@@ -308,7 +310,7 @@ def __init__(self, input_mode=None):
----------
input_mode : str
- One of ['line', 'block']; default is 'line'.
+ One of ['line', 'cell']; default is 'line'.
The input_mode parameter controls how new inputs are used when fed via
the :meth:`push` method:
@@ -316,10 +318,11 @@ def __init__(self, input_mode=None):
- 'line': meant for line-oriented clients, inputs are appended one at a
time to the internal buffer and the whole buffer is compiled.
- - 'block': meant for clients that can edit multi-line blocks of text at
- a time. Each new input new input completely replaces all prior
- inputs. Block mode is thus equivalent to prepending a full reset()
- to every push() call.
+ - 'cell': meant for clients that can edit multi-line 'cells' of text at
+ a time. A cell can contain one or more blocks that can be compile in
+ 'single' mode by Python. In this mode, each new input new input
+ completely replaces all prior inputs. Cell mode is thus equivalent
+ to prepending a full reset() to every push() call.
"""
self._buffer = []
self._compile = codeop.CommandCompiler()
@@ -365,7 +368,7 @@ def push(self, lines):
this value is also stored as a private attribute (_is_complete), so it
can be queried at any time.
"""
- if self.input_mode == 'block':
+ if self.input_mode == 'cell':
self.reset()
# If the source code has leading blanks, add 'if 1:\n' to it
@@ -433,13 +436,31 @@ def push_accepts_more(self):
backend which might convert the invalid syntax into valid Python via
one of the dynamic IPython mechanisms.
"""
-
+
+ # With incomplete input, unconditionally accept more
if not self._is_complete:
return True
+ # If we already have complete input and we're flush left, the answer
+ # depends. In line mode, we're done. But in cell mode, we need to
+ # check how many blocks the input so far compiles into, because if
+ # there's already more than one full independent block of input, then
+ # the client has entered full 'cell' mode and is feeding lines that
+ # each is complete. In this case we should then keep accepting.
+ # The Qt terminal-like console does precisely this, to provide the
+ # convenience of terminal-like input of single expressions, but
+ # allowing the user (with a separate keystroke) to switch to 'cell'
+ # mode and type multiple expressions in one shot.
if self.indent_spaces==0:
- return False
-
+ if self.input_mode=='line':
+ return False
+ else:
+ nblocks = len(split_blocks(''.join(self._buffer)))
+ if nblocks==1:
+ return False
+
+ # When input is complete, then termination is marked by an extra blank
+ # line at the end.
last_line = self.source.splitlines()[-1]
return bool(last_line and not last_line.isspace())
@@ -934,10 +955,10 @@ def push(self, lines):
# line.
changed_input_mode = False
- if len(lines_list)>1 and self.input_mode == 'block':
+ if len(lines_list)>1 and self.input_mode == 'cell':
self.reset()
changed_input_mode = True
- saved_input_mode = 'block'
+ saved_input_mode = 'cell'
self.input_mode = 'line'
try:
View
4 IPython/core/tests/test_inputsplitter.py
@@ -204,7 +204,7 @@ def test_push3(self):
def test_replace_mode(self):
isp = self.isp
- isp.input_mode = 'block'
+ isp.input_mode = 'cell'
isp.push('x=1')
self.assertEqual(isp.source, 'x=1\n')
isp.push('x=2')
@@ -591,7 +591,7 @@ class BlockIPythonInputTestCase(IPythonInputTestCase):
test_push3 = test_split = lambda s: None
def setUp(self):
- self.isp = isp.IPythonInputSplitter(input_mode='block')
+ self.isp = isp.IPythonInputSplitter(input_mode='cell')
def test_syntax_multiline(self):
isp = self.isp
View
5 IPython/frontend/qt/console/console_widget.py
@@ -363,6 +363,11 @@ def execute(self, source=None, hidden=False, interactive=False):
# disable the undo/redo history, but just to be safe:
self._control.setUndoRedoEnabled(False)
+ # Flush all state from the input splitter so the next round of
+ # reading input starts with a clean buffer.
+ self._input_splitter.reset()
+
+ # Call actual execution
self._execute(source, hidden)
else:
View
5 IPython/frontend/qt/console/frontend_widget.py
@@ -1,3 +1,5 @@
+from __future__ import print_function
+
# Standard library imports
from collections import namedtuple
import signal
@@ -114,7 +116,7 @@ def __init__(self, *args, **kw):
self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
self._hidden = False
self._highlighter = FrontendHighlighter(self)
- self._input_splitter = self._input_splitter_class(input_mode='block')
+ self._input_splitter = self._input_splitter_class(input_mode='cell')
self._kernel_manager = None
self._possible_kernel_restart = False
self._request_info = {}
@@ -236,6 +238,7 @@ def _insert_continuation_prompt(self, cursor):
""" Reimplemented for auto-indentation.
"""
super(FrontendWidget, self)._insert_continuation_prompt(cursor)
+ #print('SPACES:', self._input_splitter.indent_spaces) # dbg
spaces = self._input_splitter.indent_spaces
cursor.insertText('\t' * (spaces / self.tab_width))
cursor.insertText(' ' * (spaces % self.tab_width))
Please sign in to comment.
Something went wrong with that request. Please try again.