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