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', ' |
|
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 |
- ' |
|
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 == ' |
|
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 | |
@@ -433,13 +436,31 b' class InputSplitter(object):' | |||||
433 | backend which might convert the invalid syntax into valid Python via |
|
436 | backend which might convert the invalid syntax into valid Python via | |
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: | |
441 | return False |
|
455 | if self.input_mode=='line': | |
442 |
|
456 | return False | ||
|
457 | else: | |||
|
458 | nblocks = len(split_blocks(''.join(self._buffer))) | |||
|
459 | if nblocks==1: | |||
|
460 | return False | |||
|
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 == ' |
|
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 = ' |
|
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 = ' |
|
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=' |
|
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=' |
|
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