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