##// END OF EJS Templates
Fix switching prompts in doctest mode
Thomas Kluyver -
Show More
@@ -1,571 +1,572 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 import os
4 import os
5 import sys
5 import sys
6 import signal
6 import signal
7 from warnings import warn
7 from warnings import warn
8
8
9 from IPython.core.error import TryNext
9 from IPython.core.error import TryNext
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 from IPython.utils.process import abbrev_cwd
13 from IPython.utils.process import abbrev_cwd
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
15
15
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
17 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
17 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
18 ViInsertMode, EmacsInsertMode, IsDone, HasCompletions)
18 ViInsertMode, EmacsInsertMode, IsDone, HasCompletions)
19 from prompt_toolkit.filters.cli import ViMode
19 from prompt_toolkit.filters.cli import ViMode
20 from prompt_toolkit.history import InMemoryHistory
20 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
21 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
22 from prompt_toolkit.interface import CommandLineInterface
22 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.key_binding.manager import KeyBindingManager
23 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.keys import Keys
24 from prompt_toolkit.keys import Keys
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
27 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
27 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
28
28
29 from pygments.styles import get_style_by_name, get_all_styles
29 from pygments.styles import get_style_by_name, get_all_styles
30 from pygments.token import Token
30 from pygments.token import Token
31
31
32 from .debugger import TerminalPdb, Pdb
32 from .debugger import TerminalPdb, Pdb
33 from .magics import TerminalMagics
33 from .magics import TerminalMagics
34 from .pt_inputhooks import get_inputhook_func
34 from .pt_inputhooks import get_inputhook_func
35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37
37
38 DISPLAY_BANNER_DEPRECATED = object()
38 DISPLAY_BANNER_DEPRECATED = object()
39
39
40
40
41 from pygments.style import Style
41 from pygments.style import Style
42
42
43 class _NoStyle(Style): pass
43 class _NoStyle(Style): pass
44
44
45
45
46
46
47 _style_overrides_light_bg = {
47 _style_overrides_light_bg = {
48 Token.Prompt: '#0000ff',
48 Token.Prompt: '#0000ff',
49 Token.PromptNum: '#0000ee bold',
49 Token.PromptNum: '#0000ee bold',
50 Token.OutPrompt: '#cc0000',
50 Token.OutPrompt: '#cc0000',
51 Token.OutPromptNum: '#bb0000 bold',
51 Token.OutPromptNum: '#bb0000 bold',
52 }
52 }
53
53
54 _style_overrides_linux = {
54 _style_overrides_linux = {
55 Token.Prompt: '#00cc00',
55 Token.Prompt: '#00cc00',
56 Token.PromptNum: '#00bb00 bold',
56 Token.PromptNum: '#00bb00 bold',
57 Token.OutPrompt: '#cc0000',
57 Token.OutPrompt: '#cc0000',
58 Token.OutPromptNum: '#bb0000 bold',
58 Token.OutPromptNum: '#bb0000 bold',
59 }
59 }
60
60
61
61
62
62
63 def get_default_editor():
63 def get_default_editor():
64 try:
64 try:
65 ed = os.environ['EDITOR']
65 ed = os.environ['EDITOR']
66 if not PY3:
66 if not PY3:
67 ed = ed.decode()
67 ed = ed.decode()
68 return ed
68 return ed
69 except KeyError:
69 except KeyError:
70 pass
70 pass
71 except UnicodeError:
71 except UnicodeError:
72 warn("$EDITOR environment variable is not pure ASCII. Using platform "
72 warn("$EDITOR environment variable is not pure ASCII. Using platform "
73 "default editor.")
73 "default editor.")
74
74
75 if os.name == 'posix':
75 if os.name == 'posix':
76 return 'vi' # the only one guaranteed to be there!
76 return 'vi' # the only one guaranteed to be there!
77 else:
77 else:
78 return 'notepad' # same in Windows!
78 return 'notepad' # same in Windows!
79
79
80
80
81 if sys.stdin and sys.stdout and sys.stderr:
81 if sys.stdin and sys.stdout and sys.stderr:
82 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
82 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
83 else:
83 else:
84 _is_tty = False
84 _is_tty = False
85
85
86
86
87 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
87 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
88
88
89 class TerminalInteractiveShell(InteractiveShell):
89 class TerminalInteractiveShell(InteractiveShell):
90 colors_force = True
90 colors_force = True
91
91
92 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
92 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
93 'to reserve for the completion menu'
93 'to reserve for the completion menu'
94 ).tag(config=True)
94 ).tag(config=True)
95
95
96 def _space_for_menu_changed(self, old, new):
96 def _space_for_menu_changed(self, old, new):
97 self._update_layout()
97 self._update_layout()
98
98
99 pt_cli = None
99 pt_cli = None
100 debugger_history = None
100 debugger_history = None
101 _pt_app = None
101
102
102 simple_prompt = Bool(_use_simple_prompt,
103 simple_prompt = Bool(_use_simple_prompt,
103 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
104 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
104
105
105 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
106 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
106 IPython own testing machinery, and emacs inferior-shell integration through elpy.
107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
107
108
108 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
109 environment variable is set, or the current terminal is not a tty.
110 environment variable is set, or the current terminal is not a tty.
110
111
111 """
112 """
112 ).tag(config=True)
113 ).tag(config=True)
113
114
114 @property
115 @property
115 def debugger_cls(self):
116 def debugger_cls(self):
116 return Pdb if self.simple_prompt else TerminalPdb
117 return Pdb if self.simple_prompt else TerminalPdb
117
118
118 confirm_exit = Bool(True,
119 confirm_exit = Bool(True,
119 help="""
120 help="""
120 Set to confirm when you try to exit IPython with an EOF (Control-D
121 Set to confirm when you try to exit IPython with an EOF (Control-D
121 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
122 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
122 you can force a direct exit without any confirmation.""",
123 you can force a direct exit without any confirmation.""",
123 ).tag(config=True)
124 ).tag(config=True)
124
125
125 editing_mode = Unicode('emacs',
126 editing_mode = Unicode('emacs',
126 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
127 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
127 ).tag(config=True)
128 ).tag(config=True)
128
129
129 mouse_support = Bool(False,
130 mouse_support = Bool(False,
130 help="Enable mouse support in the prompt"
131 help="Enable mouse support in the prompt"
131 ).tag(config=True)
132 ).tag(config=True)
132
133
133 highlighting_style = Unicode('legacy',
134 highlighting_style = Unicode('legacy',
134 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
135 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
135 ).tag(config=True)
136 ).tag(config=True)
136
137
137
138
138 @observe('highlighting_style')
139 @observe('highlighting_style')
139 @observe('colors')
140 @observe('colors')
140 def _highlighting_style_changed(self, change):
141 def _highlighting_style_changed(self, change):
141 self.refresh_style()
142 self.refresh_style()
142
143
143 def refresh_style(self):
144 def refresh_style(self):
144 self._style = self._make_style_from_name(self.highlighting_style)
145 self._style = self._make_style_from_name(self.highlighting_style)
145
146
146
147
147 highlighting_style_overrides = Dict(
148 highlighting_style_overrides = Dict(
148 help="Override highlighting format for specific tokens"
149 help="Override highlighting format for specific tokens"
149 ).tag(config=True)
150 ).tag(config=True)
150
151
151 editor = Unicode(get_default_editor(),
152 editor = Unicode(get_default_editor(),
152 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
153 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
153 ).tag(config=True)
154 ).tag(config=True)
154
155
155 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
156 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
156
157
157 prompts = Instance(Prompts)
158 prompts = Instance(Prompts)
158
159
159 @default('prompts')
160 @default('prompts')
160 def _prompts_default(self):
161 def _prompts_default(self):
161 return self.prompts_class(self)
162 return self.prompts_class(self)
162
163
163 @observe('prompts')
164 @observe('prompts')
164 def _(self, change):
165 def _(self, change):
165 self._update_layout()
166 self._update_layout()
166
167
167 @default('displayhook_class')
168 @default('displayhook_class')
168 def _displayhook_class_default(self):
169 def _displayhook_class_default(self):
169 return RichPromptDisplayHook
170 return RichPromptDisplayHook
170
171
171 term_title = Bool(True,
172 term_title = Bool(True,
172 help="Automatically set the terminal title"
173 help="Automatically set the terminal title"
173 ).tag(config=True)
174 ).tag(config=True)
174
175
175 # Leaving that for beta/rc tester, shoudl remove for 5.0.0 final.
176 # Leaving that for beta/rc tester, shoudl remove for 5.0.0 final.
176 display_completions_in_columns = Bool(None,
177 display_completions_in_columns = Bool(None,
177 help="DEPRECATED", allow_none=True
178 help="DEPRECATED", allow_none=True
178 ).tag(config=True)
179 ).tag(config=True)
179
180
180 @observe('display_completions_in_columns')
181 @observe('display_completions_in_columns')
181 def _display_completions_in_columns_changed(self, new):
182 def _display_completions_in_columns_changed(self, new):
182 raise DeprecationWarning("The `display_completions_in_columns` Boolean has been replaced by the enum `display_completions`"
183 raise DeprecationWarning("The `display_completions_in_columns` Boolean has been replaced by the enum `display_completions`"
183 "with the following acceptable value: 'column', 'multicolumn','readlinelike'. ")
184 "with the following acceptable value: 'column', 'multicolumn','readlinelike'. ")
184
185
185 display_completions = Enum(('column', 'multicolumn','readlinelike'), default_value='multicolumn').tag(config=True)
186 display_completions = Enum(('column', 'multicolumn','readlinelike'), default_value='multicolumn').tag(config=True)
186
187
187 highlight_matching_brackets = Bool(True,
188 highlight_matching_brackets = Bool(True,
188 help="Highlight matching brackets .",
189 help="Highlight matching brackets .",
189 ).tag(config=True)
190 ).tag(config=True)
190
191
191 @observe('term_title')
192 @observe('term_title')
192 def init_term_title(self, change=None):
193 def init_term_title(self, change=None):
193 # Enable or disable the terminal title.
194 # Enable or disable the terminal title.
194 if self.term_title:
195 if self.term_title:
195 toggle_set_term_title(True)
196 toggle_set_term_title(True)
196 set_term_title('IPython: ' + abbrev_cwd())
197 set_term_title('IPython: ' + abbrev_cwd())
197 else:
198 else:
198 toggle_set_term_title(False)
199 toggle_set_term_title(False)
199
200
200 def init_display_formatter(self):
201 def init_display_formatter(self):
201 super(TerminalInteractiveShell, self).init_display_formatter()
202 super(TerminalInteractiveShell, self).init_display_formatter()
202 # terminal only supports plain text
203 # terminal only supports plain text
203 self.display_formatter.active_types = ['text/plain']
204 self.display_formatter.active_types = ['text/plain']
204
205
205 def init_prompt_toolkit_cli(self):
206 def init_prompt_toolkit_cli(self):
206 self._app = None
207 if self.simple_prompt:
207 if self.simple_prompt:
208 # Fall back to plain non-interactive output for tests.
208 # Fall back to plain non-interactive output for tests.
209 # This is very limited, and only accepts a single line.
209 # This is very limited, and only accepts a single line.
210 def prompt():
210 def prompt():
211 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
211 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
212 self.prompt_for_code = prompt
212 self.prompt_for_code = prompt
213 return
213 return
214
214
215 kbmanager = KeyBindingManager.for_prompt()
215 kbmanager = KeyBindingManager.for_prompt()
216 insert_mode = ViInsertMode() | EmacsInsertMode()
216 insert_mode = ViInsertMode() | EmacsInsertMode()
217 # Ctrl+J == Enter, seemingly
217 # Ctrl+J == Enter, seemingly
218 @kbmanager.registry.add_binding(Keys.ControlJ,
218 @kbmanager.registry.add_binding(Keys.ControlJ,
219 filter=(HasFocus(DEFAULT_BUFFER)
219 filter=(HasFocus(DEFAULT_BUFFER)
220 & ~HasSelection()
220 & ~HasSelection()
221 & insert_mode
221 & insert_mode
222 ))
222 ))
223 def _(event):
223 def _(event):
224 b = event.current_buffer
224 b = event.current_buffer
225 d = b.document
225 d = b.document
226
226
227 if b.complete_state:
227 if b.complete_state:
228 cc = b.complete_state.current_completion
228 cc = b.complete_state.current_completion
229 if cc:
229 if cc:
230 b.apply_completion(cc)
230 b.apply_completion(cc)
231 else:
231 else:
232 b.cancel_completion()
232 b.cancel_completion()
233 return
233 return
234
234
235 if not (d.on_last_line or d.cursor_position_row >= d.line_count
235 if not (d.on_last_line or d.cursor_position_row >= d.line_count
236 - d.empty_line_count_at_the_end()):
236 - d.empty_line_count_at_the_end()):
237 b.newline()
237 b.newline()
238 return
238 return
239
239
240 status, indent = self.input_splitter.check_complete(d.text + '\n')
240 status, indent = self.input_splitter.check_complete(d.text + '\n')
241
241
242 if (status != 'incomplete') and b.accept_action.is_returnable:
242 if (status != 'incomplete') and b.accept_action.is_returnable:
243 b.accept_action.validate_and_handle(event.cli, b)
243 b.accept_action.validate_and_handle(event.cli, b)
244 else:
244 else:
245 b.insert_text('\n' + (' ' * (indent or 0)))
245 b.insert_text('\n' + (' ' * (indent or 0)))
246
246
247 @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
247 @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
248 def _previous_history_or_previous_completion(event):
248 def _previous_history_or_previous_completion(event):
249 """
249 """
250 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
250 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
251
251
252 If completer is open this still select previous completion.
252 If completer is open this still select previous completion.
253 """
253 """
254 event.current_buffer.auto_up()
254 event.current_buffer.auto_up()
255
255
256 @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
256 @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
257 def _next_history_or_next_completion(event):
257 def _next_history_or_next_completion(event):
258 """
258 """
259 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
259 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
260
260
261 If completer is open this still select next completion.
261 If completer is open this still select next completion.
262 """
262 """
263 event.current_buffer.auto_down()
263 event.current_buffer.auto_down()
264
264
265 @kbmanager.registry.add_binding(Keys.ControlG, filter=(
265 @kbmanager.registry.add_binding(Keys.ControlG, filter=(
266 HasFocus(DEFAULT_BUFFER) & HasCompletions()
266 HasFocus(DEFAULT_BUFFER) & HasCompletions()
267 ))
267 ))
268 def _dismiss_completion(event):
268 def _dismiss_completion(event):
269 b = event.current_buffer
269 b = event.current_buffer
270 if b.complete_state:
270 if b.complete_state:
271 b.cancel_completion()
271 b.cancel_completion()
272
272
273 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
273 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
274 def _reset_buffer(event):
274 def _reset_buffer(event):
275 b = event.current_buffer
275 b = event.current_buffer
276 if b.complete_state:
276 if b.complete_state:
277 b.cancel_completion()
277 b.cancel_completion()
278 else:
278 else:
279 b.reset()
279 b.reset()
280
280
281 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
281 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
282 def _reset_search_buffer(event):
282 def _reset_search_buffer(event):
283 if event.current_buffer.document.text:
283 if event.current_buffer.document.text:
284 event.current_buffer.reset()
284 event.current_buffer.reset()
285 else:
285 else:
286 event.cli.push_focus(DEFAULT_BUFFER)
286 event.cli.push_focus(DEFAULT_BUFFER)
287
287
288 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
288 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
289
289
290 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
290 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
291 def _suspend_to_bg(event):
291 def _suspend_to_bg(event):
292 event.cli.suspend_to_background()
292 event.cli.suspend_to_background()
293
293
294 @Condition
294 @Condition
295 def cursor_in_leading_ws(cli):
295 def cursor_in_leading_ws(cli):
296 before = cli.application.buffer.document.current_line_before_cursor
296 before = cli.application.buffer.document.current_line_before_cursor
297 return (not before) or before.isspace()
297 return (not before) or before.isspace()
298
298
299 # Ctrl+I == Tab
299 # Ctrl+I == Tab
300 @kbmanager.registry.add_binding(Keys.ControlI,
300 @kbmanager.registry.add_binding(Keys.ControlI,
301 filter=(HasFocus(DEFAULT_BUFFER)
301 filter=(HasFocus(DEFAULT_BUFFER)
302 & ~HasSelection()
302 & ~HasSelection()
303 & insert_mode
303 & insert_mode
304 & cursor_in_leading_ws
304 & cursor_in_leading_ws
305 ))
305 ))
306 def _indent_buffer(event):
306 def _indent_buffer(event):
307 event.current_buffer.insert_text(' ' * 4)
307 event.current_buffer.insert_text(' ' * 4)
308
308
309
309
310 if self.display_completions == 'readlinelike':
310 if self.display_completions == 'readlinelike':
311 @kbmanager.registry.add_binding(Keys.ControlI,
311 @kbmanager.registry.add_binding(Keys.ControlI,
312 filter=(HasFocus(DEFAULT_BUFFER)
312 filter=(HasFocus(DEFAULT_BUFFER)
313 & ~HasSelection()
313 & ~HasSelection()
314 & insert_mode
314 & insert_mode
315 & ~cursor_in_leading_ws
315 & ~cursor_in_leading_ws
316 ))
316 ))
317 def _disaply_compl(ev):
317 def _disaply_compl(ev):
318 display_completions_like_readline(ev)
318 display_completions_like_readline(ev)
319
319
320
320
321 if sys.platform == 'win32':
321 if sys.platform == 'win32':
322 from IPython.lib.clipboard import (ClipboardEmpty,
322 from IPython.lib.clipboard import (ClipboardEmpty,
323 win32_clipboard_get, tkinter_clipboard_get)
323 win32_clipboard_get, tkinter_clipboard_get)
324 @kbmanager.registry.add_binding(Keys.ControlV,
324 @kbmanager.registry.add_binding(Keys.ControlV,
325 filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode()))
325 filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode()))
326 def _paste(event):
326 def _paste(event):
327 try:
327 try:
328 text = win32_clipboard_get()
328 text = win32_clipboard_get()
329 except TryNext:
329 except TryNext:
330 try:
330 try:
331 text = tkinter_clipboard_get()
331 text = tkinter_clipboard_get()
332 except (TryNext, ClipboardEmpty):
332 except (TryNext, ClipboardEmpty):
333 return
333 return
334 except ClipboardEmpty:
334 except ClipboardEmpty:
335 return
335 return
336 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
336 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
337
337
338 # Pre-populate history from IPython's history database
338 # Pre-populate history from IPython's history database
339 history = InMemoryHistory()
339 history = InMemoryHistory()
340 last_cell = u""
340 last_cell = u""
341 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
341 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
342 include_latest=True):
342 include_latest=True):
343 # Ignore blank lines and consecutive duplicates
343 # Ignore blank lines and consecutive duplicates
344 cell = cell.rstrip()
344 cell = cell.rstrip()
345 if cell and (cell != last_cell):
345 if cell and (cell != last_cell):
346 history.append(cell)
346 history.append(cell)
347
347
348 self._style = self._make_style_from_name(self.highlighting_style)
348 self._style = self._make_style_from_name(self.highlighting_style)
349 style = DynamicStyle(lambda: self._style)
349 style = DynamicStyle(lambda: self._style)
350
350
351 editing_mode = getattr(EditingMode, self.editing_mode.upper())
351 editing_mode = getattr(EditingMode, self.editing_mode.upper())
352
352
353 self._app = create_prompt_application(
353 self._pt_app = create_prompt_application(
354 editing_mode=editing_mode,
354 editing_mode=editing_mode,
355 key_bindings_registry=kbmanager.registry,
355 key_bindings_registry=kbmanager.registry,
356 history=history,
356 history=history,
357 completer=IPythonPTCompleter(self.Completer),
357 completer=IPythonPTCompleter(self.Completer),
358 enable_history_search=True,
358 enable_history_search=True,
359 style=style,
359 style=style,
360 mouse_support=self.mouse_support,
360 mouse_support=self.mouse_support,
361 **self._layout_options()
361 **self._layout_options()
362 )
362 )
363 self._eventloop = create_eventloop(self.inputhook)
363 self._eventloop = create_eventloop(self.inputhook)
364 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
364 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self._eventloop)
365
365
366 def _make_style_from_name(self, name):
366 def _make_style_from_name(self, name):
367 """
367 """
368 Small wrapper that make an IPython compatible style from a style name
368 Small wrapper that make an IPython compatible style from a style name
369
369
370 We need that to add style for prompt ... etc.
370 We need that to add style for prompt ... etc.
371 """
371 """
372 if name == 'legacy':
372 if name == 'legacy':
373 legacy = self.colors.lower()
373 legacy = self.colors.lower()
374 if legacy == 'linux':
374 if legacy == 'linux':
375 style_cls = get_style_by_name('monokai')
375 style_cls = get_style_by_name('monokai')
376 style_overrides = _style_overrides_linux
376 style_overrides = _style_overrides_linux
377 elif legacy == 'lightbg':
377 elif legacy == 'lightbg':
378 style_overrides = _style_overrides_light_bg
378 style_overrides = _style_overrides_light_bg
379 style_cls = get_style_by_name('default')
379 style_cls = get_style_by_name('default')
380 # The default theme needs to be visible on both a dark background
380 # The default theme needs to be visible on both a dark background
381 # and a light background, because we can't tell what the terminal
381 # and a light background, because we can't tell what the terminal
382 # looks like. These tweaks to the default theme help with that.
382 # looks like. These tweaks to the default theme help with that.
383 style_overrides.update({
383 style_overrides.update({
384 Token.Number: '#007700',
384 Token.Number: '#007700',
385 Token.Operator: 'noinherit',
385 Token.Operator: 'noinherit',
386 Token.String: '#BB6622',
386 Token.String: '#BB6622',
387 Token.Name.Function: '#2080D0',
387 Token.Name.Function: '#2080D0',
388 Token.Name.Class: 'bold #2080D0',
388 Token.Name.Class: 'bold #2080D0',
389 Token.Name.Namespace: 'bold #2080D0',
389 Token.Name.Namespace: 'bold #2080D0',
390 })
390 })
391 elif legacy =='nocolor':
391 elif legacy =='nocolor':
392 style_cls=_NoStyle
392 style_cls=_NoStyle
393 style_overrides = {}
393 style_overrides = {}
394 else :
394 else :
395 raise ValueError('Got unknown colors: ', legacy)
395 raise ValueError('Got unknown colors: ', legacy)
396 else :
396 else :
397 style_cls = get_style_by_name(name)
397 style_cls = get_style_by_name(name)
398 style_overrides = {
398 style_overrides = {
399 Token.Prompt: '#009900',
399 Token.Prompt: '#009900',
400 Token.PromptNum: '#00ff00 bold',
400 Token.PromptNum: '#00ff00 bold',
401 Token.OutPrompt: '#990000',
401 Token.OutPrompt: '#990000',
402 Token.OutPromptNum: '#ff0000 bold',
402 Token.OutPromptNum: '#ff0000 bold',
403 }
403 }
404 style_overrides.update(self.highlighting_style_overrides)
404 style_overrides.update(self.highlighting_style_overrides)
405 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
405 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
406 style_dict=style_overrides)
406 style_dict=style_overrides)
407
407
408 return style
408 return style
409
409
410 def _layout_options(self):
410 def _layout_options(self):
411 """
411 """
412 Return the current layout option for the current Terminal InteractiveShell
412 Return the current layout option for the current Terminal InteractiveShell
413 """
413 """
414 return {
414 return {
415 'lexer':IPythonPTLexer(),
415 'lexer':IPythonPTLexer(),
416 'reserve_space_for_menu':self.space_for_menu,
416 'reserve_space_for_menu':self.space_for_menu,
417 'get_prompt_tokens':self.prompts.in_prompt_tokens,
417 'get_prompt_tokens':self.prompts.in_prompt_tokens,
418 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
418 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
419 'multiline':True,
419 'multiline':True,
420 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
420 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
421
421
422 # Highlight matching brackets, but only when this setting is
422 # Highlight matching brackets, but only when this setting is
423 # enabled, and only when the DEFAULT_BUFFER has the focus.
423 # enabled, and only when the DEFAULT_BUFFER has the focus.
424 'extra_input_processors': [ConditionalProcessor(
424 'extra_input_processors': [ConditionalProcessor(
425 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
425 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
426 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
426 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
427 Condition(lambda cli: self.highlight_matching_brackets))],
427 Condition(lambda cli: self.highlight_matching_brackets))],
428 }
428 }
429
429
430 def _update_layout(self):
430 def _update_layout(self):
431 """
431 """
432 Ask for a re computation of the application layout, if for example ,
432 Ask for a re computation of the application layout, if for example ,
433 some configuration options have changed.
433 some configuration options have changed.
434 """
434 """
435 if getattr(self, '._app', None):
435 if self._pt_app:
436 self._app.layout = create_prompt_layout(**self._layout_options())
436 self._pt_app.layout = create_prompt_layout(**self._layout_options())
437
437
438 def prompt_for_code(self):
438 def prompt_for_code(self):
439 document = self.pt_cli.run(
439 document = self.pt_cli.run(
440 pre_run=self.pre_prompt, reset_current_buffer=True)
440 pre_run=self.pre_prompt, reset_current_buffer=True)
441 return document.text
441 return document.text
442
442
443 def init_io(self):
443 def init_io(self):
444 if sys.platform not in {'win32', 'cli'}:
444 if sys.platform not in {'win32', 'cli'}:
445 return
445 return
446
446
447 import win_unicode_console
447 import win_unicode_console
448 import colorama
448 import colorama
449
449
450 win_unicode_console.enable()
450 win_unicode_console.enable()
451 colorama.init()
451 colorama.init()
452
452
453 # For some reason we make these wrappers around stdout/stderr.
453 # For some reason we make these wrappers around stdout/stderr.
454 # For now, we need to reset them so all output gets coloured.
454 # For now, we need to reset them so all output gets coloured.
455 # https://github.com/ipython/ipython/issues/8669
455 # https://github.com/ipython/ipython/issues/8669
456 from IPython.utils import io
456 from IPython.utils import io
457 io.stdout = io.IOStream(sys.stdout)
457 io.stdout = io.IOStream(sys.stdout)
458 io.stderr = io.IOStream(sys.stderr)
458 io.stderr = io.IOStream(sys.stderr)
459
459
460 def init_magics(self):
460 def init_magics(self):
461 super(TerminalInteractiveShell, self).init_magics()
461 super(TerminalInteractiveShell, self).init_magics()
462 self.register_magics(TerminalMagics)
462 self.register_magics(TerminalMagics)
463
463
464 def init_alias(self):
464 def init_alias(self):
465 # The parent class defines aliases that can be safely used with any
465 # The parent class defines aliases that can be safely used with any
466 # frontend.
466 # frontend.
467 super(TerminalInteractiveShell, self).init_alias()
467 super(TerminalInteractiveShell, self).init_alias()
468
468
469 # Now define aliases that only make sense on the terminal, because they
469 # Now define aliases that only make sense on the terminal, because they
470 # need direct access to the console in a way that we can't emulate in
470 # need direct access to the console in a way that we can't emulate in
471 # GUI or web frontend
471 # GUI or web frontend
472 if os.name == 'posix':
472 if os.name == 'posix':
473 for cmd in ['clear', 'more', 'less', 'man']:
473 for cmd in ['clear', 'more', 'less', 'man']:
474 self.alias_manager.soft_define_alias(cmd, cmd)
474 self.alias_manager.soft_define_alias(cmd, cmd)
475
475
476
476
477 def __init__(self, *args, **kwargs):
477 def __init__(self, *args, **kwargs):
478 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
478 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
479 self.init_prompt_toolkit_cli()
479 self.init_prompt_toolkit_cli()
480 self.init_term_title()
480 self.init_term_title()
481 self.keep_running = True
481 self.keep_running = True
482
482
483 self.debugger_history = InMemoryHistory()
483 self.debugger_history = InMemoryHistory()
484
484
485 def ask_exit(self):
485 def ask_exit(self):
486 self.keep_running = False
486 self.keep_running = False
487
487
488 rl_next_input = None
488 rl_next_input = None
489
489
490 def pre_prompt(self):
490 def pre_prompt(self):
491 if self.rl_next_input:
491 if self.rl_next_input:
492 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
492 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
493 self.rl_next_input = None
493 self.rl_next_input = None
494
494
495 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
495 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
496
496
497 if display_banner is not DISPLAY_BANNER_DEPRECATED:
497 if display_banner is not DISPLAY_BANNER_DEPRECATED:
498 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
498 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
499
499
500 while self.keep_running:
500 while self.keep_running:
501 print(self.separate_in, end='')
501 print(self.separate_in, end='')
502
502
503 try:
503 try:
504 code = self.prompt_for_code()
504 code = self.prompt_for_code()
505 except EOFError:
505 except EOFError:
506 if (not self.confirm_exit) \
506 if (not self.confirm_exit) \
507 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
507 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
508 self.ask_exit()
508 self.ask_exit()
509
509
510 else:
510 else:
511 if code:
511 if code:
512 self.run_cell(code, store_history=True)
512 self.run_cell(code, store_history=True)
513
513
514 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
514 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
515 # An extra layer of protection in case someone mashing Ctrl-C breaks
515 # An extra layer of protection in case someone mashing Ctrl-C breaks
516 # out of our internal code.
516 # out of our internal code.
517 if display_banner is not DISPLAY_BANNER_DEPRECATED:
517 if display_banner is not DISPLAY_BANNER_DEPRECATED:
518 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
518 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
519 while True:
519 while True:
520 try:
520 try:
521 self.interact()
521 self.interact()
522 break
522 break
523 except KeyboardInterrupt:
523 except KeyboardInterrupt:
524 print("\nKeyboardInterrupt escaped interact()\n")
524 print("\nKeyboardInterrupt escaped interact()\n")
525
525
526 if hasattr(self, '_eventloop'):
526 if hasattr(self, '_eventloop'):
527 self._eventloop.close()
527 self._eventloop.close()
528
528
529 _inputhook = None
529 _inputhook = None
530 def inputhook(self, context):
530 def inputhook(self, context):
531 if self._inputhook is not None:
531 if self._inputhook is not None:
532 self._inputhook(context)
532 self._inputhook(context)
533
533
534 def enable_gui(self, gui=None):
534 def enable_gui(self, gui=None):
535 if gui:
535 if gui:
536 self._inputhook = get_inputhook_func(gui)
536 self._inputhook = get_inputhook_func(gui)
537 else:
537 else:
538 self._inputhook = None
538 self._inputhook = None
539
539
540 # Run !system commands directly, not through pipes, so terminal programs
540 # Run !system commands directly, not through pipes, so terminal programs
541 # work correctly.
541 # work correctly.
542 system = InteractiveShell.system_raw
542 system = InteractiveShell.system_raw
543
543
544 def auto_rewrite_input(self, cmd):
544 def auto_rewrite_input(self, cmd):
545 """Overridden from the parent class to use fancy rewriting prompt"""
545 """Overridden from the parent class to use fancy rewriting prompt"""
546 if not self.show_rewritten_input:
546 if not self.show_rewritten_input:
547 return
547 return
548
548
549 tokens = self.prompts.rewrite_prompt_tokens()
549 tokens = self.prompts.rewrite_prompt_tokens()
550 if self.pt_cli:
550 if self.pt_cli:
551 self.pt_cli.print_tokens(tokens)
551 self.pt_cli.print_tokens(tokens)
552 print(cmd)
552 print(cmd)
553 else:
553 else:
554 prompt = ''.join(s for t, s in tokens)
554 prompt = ''.join(s for t, s in tokens)
555 print(prompt, cmd, sep='')
555 print(prompt, cmd, sep='')
556
556
557 _prompts_before = None
557 _prompts_before = None
558 def switch_doctest_mode(self, mode):
558 def switch_doctest_mode(self, mode):
559 """Switch prompts to classic for %doctest_mode"""
559 """Switch prompts to classic for %doctest_mode"""
560 if mode:
560 if mode:
561 self._prompts_before = self.prompts
561 self._prompts_before = self.prompts
562 self.prompts = ClassicPrompts(self)
562 self.prompts = ClassicPrompts(self)
563 elif self._prompts_before:
563 elif self._prompts_before:
564 self.prompts = self._prompts_before
564 self.prompts = self._prompts_before
565 self._prompts_before = None
565 self._prompts_before = None
566 self._update_layout()
566
567
567
568
568 InteractiveShellABC.register(TerminalInteractiveShell)
569 InteractiveShellABC.register(TerminalInteractiveShell)
569
570
570 if __name__ == '__main__':
571 if __name__ == '__main__':
571 TerminalInteractiveShell.instance().interact()
572 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now