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