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