##// END OF EJS Templates
Implement support for 'cell' mode with Ctrl-Enter....
Fernando Perez -
Show More
@@ -60,6 +60,7 b' Authors'
60 # Distributed under the terms of the BSD License. The full license is in
60 # Distributed under the terms of the BSD License. The full license is in
61 # the file COPYING, distributed as part of this software.
61 # the file COPYING, distributed as part of this software.
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63 from __future__ import print_function
63
64
64 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
65 # Imports
66 # Imports
@@ -71,6 +72,7 b' import sys'
71
72
72 # IPython modules
73 # IPython modules
73 from IPython.utils.text import make_quoted_expr
74 from IPython.utils.text import make_quoted_expr
75
74 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
75 # Globals
77 # Globals
76 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
@@ -81,14 +83,14 b' from IPython.utils.text import make_quoted_expr'
81 # for all intents and purposes they constitute the 'IPython syntax', so they
83 # for all intents and purposes they constitute the 'IPython syntax', so they
82 # should be considered fixed.
84 # should be considered fixed.
83
85
84 ESC_SHELL = '!'
86 ESC_SHELL = '!' # Send line to underlying system shell
85 ESC_SH_CAP = '!!'
87 ESC_SH_CAP = '!!' # Send line to system shell and capture output
86 ESC_HELP = '?'
88 ESC_HELP = '?' # Find information about object
87 ESC_HELP2 = '??'
89 ESC_HELP2 = '??' # Find extra-detailed information about object
88 ESC_MAGIC = '%'
90 ESC_MAGIC = '%' # Call magic function
89 ESC_QUOTE = ','
91 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
90 ESC_QUOTE2 = ';'
92 ESC_QUOTE2 = ';' # Quote all args as a single string, call
91 ESC_PAREN = '/'
93 ESC_PAREN = '/' # Call first argument with rest of line as arguments
92
94
93 #-----------------------------------------------------------------------------
95 #-----------------------------------------------------------------------------
94 # Utilities
96 # Utilities
@@ -308,7 +310,7 b' class InputSplitter(object):'
308 ----------
310 ----------
309 input_mode : str
311 input_mode : str
310
312
311 One of ['line', 'block']; default is 'line'.
313 One of ['line', 'cell']; default is 'line'.
312
314
313 The input_mode parameter controls how new inputs are used when fed via
315 The input_mode parameter controls how new inputs are used when fed via
314 the :meth:`push` method:
316 the :meth:`push` method:
@@ -316,10 +318,11 b' class InputSplitter(object):'
316 - 'line': meant for line-oriented clients, inputs are appended one at a
318 - 'line': meant for line-oriented clients, inputs are appended one at a
317 time to the internal buffer and the whole buffer is compiled.
319 time to the internal buffer and the whole buffer is compiled.
318
320
319 - 'block': meant for clients that can edit multi-line blocks of text at
321 - 'cell': meant for clients that can edit multi-line 'cells' of text at
320 a time. Each new input new input completely replaces all prior
322 a time. A cell can contain one or more blocks that can be compile in
321 inputs. Block mode is thus equivalent to prepending a full reset()
323 'single' mode by Python. In this mode, each new input new input
322 to every push() call.
324 completely replaces all prior inputs. Cell mode is thus equivalent
325 to prepending a full reset() to every push() call.
323 """
326 """
324 self._buffer = []
327 self._buffer = []
325 self._compile = codeop.CommandCompiler()
328 self._compile = codeop.CommandCompiler()
@@ -365,7 +368,7 b' class InputSplitter(object):'
365 this value is also stored as a private attribute (_is_complete), so it
368 this value is also stored as a private attribute (_is_complete), so it
366 can be queried at any time.
369 can be queried at any time.
367 """
370 """
368 if self.input_mode == 'block':
371 if self.input_mode == 'cell':
369 self.reset()
372 self.reset()
370
373
371 # If the source code has leading blanks, add 'if 1:\n' to it
374 # If the source code has leading blanks, add 'if 1:\n' to it
@@ -434,12 +437,30 b' class InputSplitter(object):'
434 one of the dynamic IPython mechanisms.
437 one of the dynamic IPython mechanisms.
435 """
438 """
436
439
440 # With incomplete input, unconditionally accept more
437 if not self._is_complete:
441 if not self._is_complete:
438 return True
442 return True
439
443
444 # If we already have complete input and we're flush left, the answer
445 # depends. In line mode, we're done. But in cell mode, we need to
446 # check how many blocks the input so far compiles into, because if
447 # there's already more than one full independent block of input, then
448 # the client has entered full 'cell' mode and is feeding lines that
449 # each is complete. In this case we should then keep accepting.
450 # The Qt terminal-like console does precisely this, to provide the
451 # convenience of terminal-like input of single expressions, but
452 # allowing the user (with a separate keystroke) to switch to 'cell'
453 # mode and type multiple expressions in one shot.
440 if self.indent_spaces==0:
454 if self.indent_spaces==0:
455 if self.input_mode=='line':
456 return False
457 else:
458 nblocks = len(split_blocks(''.join(self._buffer)))
459 if nblocks==1:
441 return False
460 return False
442
461
462 # When input is complete, then termination is marked by an extra blank
463 # line at the end.
443 last_line = self.source.splitlines()[-1]
464 last_line = self.source.splitlines()[-1]
444 return bool(last_line and not last_line.isspace())
465 return bool(last_line and not last_line.isspace())
445
466
@@ -934,10 +955,10 b' class IPythonInputSplitter(InputSplitter):'
934 # line.
955 # line.
935 changed_input_mode = False
956 changed_input_mode = False
936
957
937 if len(lines_list)>1 and self.input_mode == 'block':
958 if len(lines_list)>1 and self.input_mode == 'cell':
938 self.reset()
959 self.reset()
939 changed_input_mode = True
960 changed_input_mode = True
940 saved_input_mode = 'block'
961 saved_input_mode = 'cell'
941 self.input_mode = 'line'
962 self.input_mode = 'line'
942
963
943 try:
964 try:
@@ -204,7 +204,7 b' class InputSplitterTestCase(unittest.TestCase):'
204
204
205 def test_replace_mode(self):
205 def test_replace_mode(self):
206 isp = self.isp
206 isp = self.isp
207 isp.input_mode = 'block'
207 isp.input_mode = 'cell'
208 isp.push('x=1')
208 isp.push('x=1')
209 self.assertEqual(isp.source, 'x=1\n')
209 self.assertEqual(isp.source, 'x=1\n')
210 isp.push('x=2')
210 isp.push('x=2')
@@ -591,7 +591,7 b' class BlockIPythonInputTestCase(IPythonInputTestCase):'
591 test_push3 = test_split = lambda s: None
591 test_push3 = test_split = lambda s: None
592
592
593 def setUp(self):
593 def setUp(self):
594 self.isp = isp.IPythonInputSplitter(input_mode='block')
594 self.isp = isp.IPythonInputSplitter(input_mode='cell')
595
595
596 def test_syntax_multiline(self):
596 def test_syntax_multiline(self):
597 isp = self.isp
597 isp = self.isp
@@ -363,6 +363,11 b' class ConsoleWidget(Configurable, QtGui.QWidget):'
363 # disable the undo/redo history, but just to be safe:
363 # disable the undo/redo history, but just to be safe:
364 self._control.setUndoRedoEnabled(False)
364 self._control.setUndoRedoEnabled(False)
365
365
366 # Flush all state from the input splitter so the next round of
367 # reading input starts with a clean buffer.
368 self._input_splitter.reset()
369
370 # Call actual execution
366 self._execute(source, hidden)
371 self._execute(source, hidden)
367
372
368 else:
373 else:
@@ -1,3 +1,5 b''
1 from __future__ import print_function
2
1 # Standard library imports
3 # Standard library imports
2 from collections import namedtuple
4 from collections import namedtuple
3 import signal
5 import signal
@@ -114,7 +116,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
114 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
116 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
115 self._hidden = False
117 self._hidden = False
116 self._highlighter = FrontendHighlighter(self)
118 self._highlighter = FrontendHighlighter(self)
117 self._input_splitter = self._input_splitter_class(input_mode='block')
119 self._input_splitter = self._input_splitter_class(input_mode='cell')
118 self._kernel_manager = None
120 self._kernel_manager = None
119 self._possible_kernel_restart = False
121 self._possible_kernel_restart = False
120 self._request_info = {}
122 self._request_info = {}
@@ -236,6 +238,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
236 """ Reimplemented for auto-indentation.
238 """ Reimplemented for auto-indentation.
237 """
239 """
238 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
240 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
241 #print('SPACES:', self._input_splitter.indent_spaces) # dbg
239 spaces = self._input_splitter.indent_spaces
242 spaces = self._input_splitter.indent_spaces
240 cursor.insertText('\t' * (spaces / self.tab_width))
243 cursor.insertText('\t' * (spaces / self.tab_width))
241 cursor.insertText(' ' * (spaces % self.tab_width))
244 cursor.insertText(' ' * (spaces % self.tab_width))
General Comments 0
You need to be logged in to leave comments. Login now