##// END OF EJS Templates
Support multiline input in --simple-prompt (fixes: #9816)
Ximin Luo -
Show More
@@ -1,530 +1,537 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import os
3 import os
4 import sys
4 import sys
5 import warnings
5 import warnings
6 from warnings import warn
6 from warnings import warn
7
7
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.utils import io
9 from IPython.utils import io
10 from IPython.utils.py3compat import input
10 from IPython.utils.py3compat import input
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.process import abbrev_cwd
12 from IPython.utils.process import abbrev_cwd
13 from traitlets import (
13 from traitlets import (
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Any,
15 Any,
16 )
16 )
17
17
18 from prompt_toolkit.document import Document
18 from prompt_toolkit.document import Document
19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
21 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
23 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.interface import CommandLineInterface
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
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
27
28 from pygments.styles import get_style_by_name, get_all_styles
28 from pygments.styles import get_style_by_name, get_all_styles
29 from pygments.style import Style
29 from pygments.style import Style
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_name_and_func
34 from .pt_inputhooks import get_inputhook_name_and_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 from .shortcuts import register_ipython_shortcuts
37 from .shortcuts import register_ipython_shortcuts
38
38
39 DISPLAY_BANNER_DEPRECATED = object()
39 DISPLAY_BANNER_DEPRECATED = object()
40
40
41
41
42 class _NoStyle(Style): pass
42 class _NoStyle(Style): pass
43
43
44
44
45
45
46 _style_overrides_light_bg = {
46 _style_overrides_light_bg = {
47 Token.Prompt: '#0000ff',
47 Token.Prompt: '#0000ff',
48 Token.PromptNum: '#0000ee bold',
48 Token.PromptNum: '#0000ee bold',
49 Token.OutPrompt: '#cc0000',
49 Token.OutPrompt: '#cc0000',
50 Token.OutPromptNum: '#bb0000 bold',
50 Token.OutPromptNum: '#bb0000 bold',
51 }
51 }
52
52
53 _style_overrides_linux = {
53 _style_overrides_linux = {
54 Token.Prompt: '#00cc00',
54 Token.Prompt: '#00cc00',
55 Token.PromptNum: '#00bb00 bold',
55 Token.PromptNum: '#00bb00 bold',
56 Token.OutPrompt: '#cc0000',
56 Token.OutPrompt: '#cc0000',
57 Token.OutPromptNum: '#bb0000 bold',
57 Token.OutPromptNum: '#bb0000 bold',
58 }
58 }
59
59
60
60
61
61
62 def get_default_editor():
62 def get_default_editor():
63 try:
63 try:
64 return os.environ['EDITOR']
64 return os.environ['EDITOR']
65 except KeyError:
65 except KeyError:
66 pass
66 pass
67 except UnicodeError:
67 except UnicodeError:
68 warn("$EDITOR environment variable is not pure ASCII. Using platform "
68 warn("$EDITOR environment variable is not pure ASCII. Using platform "
69 "default editor.")
69 "default editor.")
70
70
71 if os.name == 'posix':
71 if os.name == 'posix':
72 return 'vi' # the only one guaranteed to be there!
72 return 'vi' # the only one guaranteed to be there!
73 else:
73 else:
74 return 'notepad' # same in Windows!
74 return 'notepad' # same in Windows!
75
75
76 # conservatively check for tty
76 # conservatively check for tty
77 # overridden streams can result in things like:
77 # overridden streams can result in things like:
78 # - sys.stdin = None
78 # - sys.stdin = None
79 # - no isatty method
79 # - no isatty method
80 for _name in ('stdin', 'stdout', 'stderr'):
80 for _name in ('stdin', 'stdout', 'stderr'):
81 _stream = getattr(sys, _name)
81 _stream = getattr(sys, _name)
82 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
82 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
83 _is_tty = False
83 _is_tty = False
84 break
84 break
85 else:
85 else:
86 _is_tty = True
86 _is_tty = True
87
87
88
88
89 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
89 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
90
90
91 class TerminalInteractiveShell(InteractiveShell):
91 class TerminalInteractiveShell(InteractiveShell):
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 _pt_app = None
102
102
103 simple_prompt = Bool(_use_simple_prompt,
103 simple_prompt = Bool(_use_simple_prompt,
104 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
104 help="""Use `raw_input` for the REPL, without completion and prompt colors.
105
105
106 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:
107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
108
108
109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
110 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."""
111 ).tag(config=True)
111 ).tag(config=True)
112
112
113 @property
113 @property
114 def debugger_cls(self):
114 def debugger_cls(self):
115 return Pdb if self.simple_prompt else TerminalPdb
115 return Pdb if self.simple_prompt else TerminalPdb
116
116
117 confirm_exit = Bool(True,
117 confirm_exit = Bool(True,
118 help="""
118 help="""
119 Set to confirm when you try to exit IPython with an EOF (Control-D
119 Set to confirm when you try to exit IPython with an EOF (Control-D
120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
121 you can force a direct exit without any confirmation.""",
121 you can force a direct exit without any confirmation.""",
122 ).tag(config=True)
122 ).tag(config=True)
123
123
124 editing_mode = Unicode('emacs',
124 editing_mode = Unicode('emacs',
125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
126 ).tag(config=True)
126 ).tag(config=True)
127
127
128 mouse_support = Bool(False,
128 mouse_support = Bool(False,
129 help="Enable mouse support in the prompt"
129 help="Enable mouse support in the prompt"
130 ).tag(config=True)
130 ).tag(config=True)
131
131
132 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
132 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
133 help="""The name or class of a Pygments style to use for syntax
133 help="""The name or class of a Pygments style to use for syntax
134 highlighting: \n %s""" % ', '.join(get_all_styles())
134 highlighting: \n %s""" % ', '.join(get_all_styles())
135 ).tag(config=True)
135 ).tag(config=True)
136
136
137
137
138 @observe('highlighting_style')
138 @observe('highlighting_style')
139 @observe('colors')
139 @observe('colors')
140 def _highlighting_style_changed(self, change):
140 def _highlighting_style_changed(self, change):
141 self.refresh_style()
141 self.refresh_style()
142
142
143 def refresh_style(self):
143 def refresh_style(self):
144 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
144 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
145
145
146
146
147 highlighting_style_overrides = Dict(
147 highlighting_style_overrides = Dict(
148 help="Override highlighting format for specific tokens"
148 help="Override highlighting format for specific tokens"
149 ).tag(config=True)
149 ).tag(config=True)
150
150
151 true_color = Bool(False,
151 true_color = Bool(False,
152 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
152 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
153 "If your terminal supports true color, the following command "
153 "If your terminal supports true color, the following command "
154 "should print 'TRUECOLOR' in orange: "
154 "should print 'TRUECOLOR' in orange: "
155 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
155 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
156 ).tag(config=True)
156 ).tag(config=True)
157
157
158 editor = Unicode(get_default_editor(),
158 editor = Unicode(get_default_editor(),
159 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
159 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
160 ).tag(config=True)
160 ).tag(config=True)
161
161
162 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
162 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
163
163
164 prompts = Instance(Prompts)
164 prompts = Instance(Prompts)
165
165
166 @default('prompts')
166 @default('prompts')
167 def _prompts_default(self):
167 def _prompts_default(self):
168 return self.prompts_class(self)
168 return self.prompts_class(self)
169
169
170 @observe('prompts')
170 @observe('prompts')
171 def _(self, change):
171 def _(self, change):
172 self._update_layout()
172 self._update_layout()
173
173
174 @default('displayhook_class')
174 @default('displayhook_class')
175 def _displayhook_class_default(self):
175 def _displayhook_class_default(self):
176 return RichPromptDisplayHook
176 return RichPromptDisplayHook
177
177
178 term_title = Bool(True,
178 term_title = Bool(True,
179 help="Automatically set the terminal title"
179 help="Automatically set the terminal title"
180 ).tag(config=True)
180 ).tag(config=True)
181
181
182 term_title_format = Unicode("IPython: {cwd}",
182 term_title_format = Unicode("IPython: {cwd}",
183 help="Customize the terminal title format. This is a python format string. " +
183 help="Customize the terminal title format. This is a python format string. " +
184 "Available substitutions are: {cwd}."
184 "Available substitutions are: {cwd}."
185 ).tag(config=True)
185 ).tag(config=True)
186
186
187 display_completions = Enum(('column', 'multicolumn','readlinelike'),
187 display_completions = Enum(('column', 'multicolumn','readlinelike'),
188 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
188 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
189 "'readlinelike'. These options are for `prompt_toolkit`, see "
189 "'readlinelike'. These options are for `prompt_toolkit`, see "
190 "`prompt_toolkit` documentation for more information."
190 "`prompt_toolkit` documentation for more information."
191 ),
191 ),
192 default_value='multicolumn').tag(config=True)
192 default_value='multicolumn').tag(config=True)
193
193
194 highlight_matching_brackets = Bool(True,
194 highlight_matching_brackets = Bool(True,
195 help="Highlight matching brackets.",
195 help="Highlight matching brackets.",
196 ).tag(config=True)
196 ).tag(config=True)
197
197
198 extra_open_editor_shortcuts = Bool(False,
198 extra_open_editor_shortcuts = Bool(False,
199 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
199 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
200 "This is in addition to the F2 binding, which is always enabled."
200 "This is in addition to the F2 binding, which is always enabled."
201 ).tag(config=True)
201 ).tag(config=True)
202
202
203 handle_return = Any(None,
203 handle_return = Any(None,
204 help="Provide an alternative handler to be called when the user presses "
204 help="Provide an alternative handler to be called when the user presses "
205 "Return. This is an advanced option intended for debugging, which "
205 "Return. This is an advanced option intended for debugging, which "
206 "may be changed or removed in later releases."
206 "may be changed or removed in later releases."
207 ).tag(config=True)
207 ).tag(config=True)
208
208
209 @observe('term_title')
209 @observe('term_title')
210 def init_term_title(self, change=None):
210 def init_term_title(self, change=None):
211 # Enable or disable the terminal title.
211 # Enable or disable the terminal title.
212 if self.term_title:
212 if self.term_title:
213 toggle_set_term_title(True)
213 toggle_set_term_title(True)
214 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
214 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
215 else:
215 else:
216 toggle_set_term_title(False)
216 toggle_set_term_title(False)
217
217
218 def init_display_formatter(self):
218 def init_display_formatter(self):
219 super(TerminalInteractiveShell, self).init_display_formatter()
219 super(TerminalInteractiveShell, self).init_display_formatter()
220 # terminal only supports plain text
220 # terminal only supports plain text
221 self.display_formatter.active_types = ['text/plain']
221 self.display_formatter.active_types = ['text/plain']
222 # disable `_ipython_display_`
222 # disable `_ipython_display_`
223 self.display_formatter.ipython_display_formatter.enabled = False
223 self.display_formatter.ipython_display_formatter.enabled = False
224
224
225 def init_prompt_toolkit_cli(self):
225 def init_prompt_toolkit_cli(self):
226 if self.simple_prompt:
226 if self.simple_prompt:
227 # Fall back to plain non-interactive output for tests.
227 # Fall back to plain non-interactive output for tests.
228 # This is very limited, and only accepts a single line.
228 # This is very limited, and only accepts a single line.
229 def prompt():
229 def prompt():
230 return input('In [%d]: ' % self.execution_count)
230 isp = self.input_splitter
231 prompt_text = 'In [%d]: ' % self.execution_count
232 prompt_continuation = ("%%%ds" % len(prompt_text)) % '...: '
233 while isp.push_accepts_more():
234 line = cast_unicode_py2(input(prompt_text))
235 isp.push(line)
236 prompt_text = prompt_continuation
237 return isp.source_reset()
231 self.prompt_for_code = prompt
238 self.prompt_for_code = prompt
232 return
239 return
233
240
234 # Set up keyboard shortcuts
241 # Set up keyboard shortcuts
235 kbmanager = KeyBindingManager.for_prompt(
242 kbmanager = KeyBindingManager.for_prompt(
236 enable_open_in_editor=self.extra_open_editor_shortcuts,
243 enable_open_in_editor=self.extra_open_editor_shortcuts,
237 )
244 )
238 register_ipython_shortcuts(kbmanager.registry, self)
245 register_ipython_shortcuts(kbmanager.registry, self)
239
246
240 # Pre-populate history from IPython's history database
247 # Pre-populate history from IPython's history database
241 history = InMemoryHistory()
248 history = InMemoryHistory()
242 last_cell = u""
249 last_cell = u""
243 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
250 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
244 include_latest=True):
251 include_latest=True):
245 # Ignore blank lines and consecutive duplicates
252 # Ignore blank lines and consecutive duplicates
246 cell = cell.rstrip()
253 cell = cell.rstrip()
247 if cell and (cell != last_cell):
254 if cell and (cell != last_cell):
248 history.append(cell)
255 history.append(cell)
249 last_cell = cell
256 last_cell = cell
250
257
251 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
258 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
252 self.style = DynamicStyle(lambda: self._style)
259 self.style = DynamicStyle(lambda: self._style)
253
260
254 editing_mode = getattr(EditingMode, self.editing_mode.upper())
261 editing_mode = getattr(EditingMode, self.editing_mode.upper())
255
262
256 def patch_stdout(**kwargs):
263 def patch_stdout(**kwargs):
257 return self.pt_cli.patch_stdout_context(**kwargs)
264 return self.pt_cli.patch_stdout_context(**kwargs)
258
265
259 self._pt_app = create_prompt_application(
266 self._pt_app = create_prompt_application(
260 editing_mode=editing_mode,
267 editing_mode=editing_mode,
261 key_bindings_registry=kbmanager.registry,
268 key_bindings_registry=kbmanager.registry,
262 history=history,
269 history=history,
263 completer=IPythonPTCompleter(shell=self,
270 completer=IPythonPTCompleter(shell=self,
264 patch_stdout=patch_stdout),
271 patch_stdout=patch_stdout),
265 enable_history_search=True,
272 enable_history_search=True,
266 style=self.style,
273 style=self.style,
267 mouse_support=self.mouse_support,
274 mouse_support=self.mouse_support,
268 **self._layout_options()
275 **self._layout_options()
269 )
276 )
270 self._eventloop = create_eventloop(self.inputhook)
277 self._eventloop = create_eventloop(self.inputhook)
271 self.pt_cli = CommandLineInterface(
278 self.pt_cli = CommandLineInterface(
272 self._pt_app, eventloop=self._eventloop,
279 self._pt_app, eventloop=self._eventloop,
273 output=create_output(true_color=self.true_color))
280 output=create_output(true_color=self.true_color))
274
281
275 def _make_style_from_name_or_cls(self, name_or_cls):
282 def _make_style_from_name_or_cls(self, name_or_cls):
276 """
283 """
277 Small wrapper that make an IPython compatible style from a style name
284 Small wrapper that make an IPython compatible style from a style name
278
285
279 We need that to add style for prompt ... etc.
286 We need that to add style for prompt ... etc.
280 """
287 """
281 style_overrides = {}
288 style_overrides = {}
282 if name_or_cls == 'legacy':
289 if name_or_cls == 'legacy':
283 legacy = self.colors.lower()
290 legacy = self.colors.lower()
284 if legacy == 'linux':
291 if legacy == 'linux':
285 style_cls = get_style_by_name('monokai')
292 style_cls = get_style_by_name('monokai')
286 style_overrides = _style_overrides_linux
293 style_overrides = _style_overrides_linux
287 elif legacy == 'lightbg':
294 elif legacy == 'lightbg':
288 style_overrides = _style_overrides_light_bg
295 style_overrides = _style_overrides_light_bg
289 style_cls = get_style_by_name('pastie')
296 style_cls = get_style_by_name('pastie')
290 elif legacy == 'neutral':
297 elif legacy == 'neutral':
291 # The default theme needs to be visible on both a dark background
298 # The default theme needs to be visible on both a dark background
292 # and a light background, because we can't tell what the terminal
299 # and a light background, because we can't tell what the terminal
293 # looks like. These tweaks to the default theme help with that.
300 # looks like. These tweaks to the default theme help with that.
294 style_cls = get_style_by_name('default')
301 style_cls = get_style_by_name('default')
295 style_overrides.update({
302 style_overrides.update({
296 Token.Number: '#007700',
303 Token.Number: '#007700',
297 Token.Operator: 'noinherit',
304 Token.Operator: 'noinherit',
298 Token.String: '#BB6622',
305 Token.String: '#BB6622',
299 Token.Name.Function: '#2080D0',
306 Token.Name.Function: '#2080D0',
300 Token.Name.Class: 'bold #2080D0',
307 Token.Name.Class: 'bold #2080D0',
301 Token.Name.Namespace: 'bold #2080D0',
308 Token.Name.Namespace: 'bold #2080D0',
302 Token.Prompt: '#009900',
309 Token.Prompt: '#009900',
303 Token.PromptNum: '#00ff00 bold',
310 Token.PromptNum: '#00ff00 bold',
304 Token.OutPrompt: '#990000',
311 Token.OutPrompt: '#990000',
305 Token.OutPromptNum: '#ff0000 bold',
312 Token.OutPromptNum: '#ff0000 bold',
306 })
313 })
307
314
308 # Hack: Due to limited color support on the Windows console
315 # Hack: Due to limited color support on the Windows console
309 # the prompt colors will be wrong without this
316 # the prompt colors will be wrong without this
310 if os.name == 'nt':
317 if os.name == 'nt':
311 style_overrides.update({
318 style_overrides.update({
312 Token.Prompt: '#ansidarkgreen',
319 Token.Prompt: '#ansidarkgreen',
313 Token.PromptNum: '#ansigreen bold',
320 Token.PromptNum: '#ansigreen bold',
314 Token.OutPrompt: '#ansidarkred',
321 Token.OutPrompt: '#ansidarkred',
315 Token.OutPromptNum: '#ansired bold',
322 Token.OutPromptNum: '#ansired bold',
316 })
323 })
317 elif legacy =='nocolor':
324 elif legacy =='nocolor':
318 style_cls=_NoStyle
325 style_cls=_NoStyle
319 style_overrides = {}
326 style_overrides = {}
320 else :
327 else :
321 raise ValueError('Got unknown colors: ', legacy)
328 raise ValueError('Got unknown colors: ', legacy)
322 else :
329 else :
323 if isinstance(name_or_cls, str):
330 if isinstance(name_or_cls, str):
324 style_cls = get_style_by_name(name_or_cls)
331 style_cls = get_style_by_name(name_or_cls)
325 else:
332 else:
326 style_cls = name_or_cls
333 style_cls = name_or_cls
327 style_overrides = {
334 style_overrides = {
328 Token.Prompt: '#009900',
335 Token.Prompt: '#009900',
329 Token.PromptNum: '#00ff00 bold',
336 Token.PromptNum: '#00ff00 bold',
330 Token.OutPrompt: '#990000',
337 Token.OutPrompt: '#990000',
331 Token.OutPromptNum: '#ff0000 bold',
338 Token.OutPromptNum: '#ff0000 bold',
332 }
339 }
333 style_overrides.update(self.highlighting_style_overrides)
340 style_overrides.update(self.highlighting_style_overrides)
334 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
341 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
335 style_dict=style_overrides)
342 style_dict=style_overrides)
336
343
337 return style
344 return style
338
345
339 def _layout_options(self):
346 def _layout_options(self):
340 """
347 """
341 Return the current layout option for the current Terminal InteractiveShell
348 Return the current layout option for the current Terminal InteractiveShell
342 """
349 """
343 return {
350 return {
344 'lexer':IPythonPTLexer(),
351 'lexer':IPythonPTLexer(),
345 'reserve_space_for_menu':self.space_for_menu,
352 'reserve_space_for_menu':self.space_for_menu,
346 'get_prompt_tokens':self.prompts.in_prompt_tokens,
353 'get_prompt_tokens':self.prompts.in_prompt_tokens,
347 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
354 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
348 'multiline':True,
355 'multiline':True,
349 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
356 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
350
357
351 # Highlight matching brackets, but only when this setting is
358 # Highlight matching brackets, but only when this setting is
352 # enabled, and only when the DEFAULT_BUFFER has the focus.
359 # enabled, and only when the DEFAULT_BUFFER has the focus.
353 'extra_input_processors': [ConditionalProcessor(
360 'extra_input_processors': [ConditionalProcessor(
354 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
361 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
355 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
362 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
356 Condition(lambda cli: self.highlight_matching_brackets))],
363 Condition(lambda cli: self.highlight_matching_brackets))],
357 }
364 }
358
365
359 def _update_layout(self):
366 def _update_layout(self):
360 """
367 """
361 Ask for a re computation of the application layout, if for example ,
368 Ask for a re computation of the application layout, if for example ,
362 some configuration options have changed.
369 some configuration options have changed.
363 """
370 """
364 if self._pt_app:
371 if self._pt_app:
365 self._pt_app.layout = create_prompt_layout(**self._layout_options())
372 self._pt_app.layout = create_prompt_layout(**self._layout_options())
366
373
367 def prompt_for_code(self):
374 def prompt_for_code(self):
368 document = self.pt_cli.run(
375 document = self.pt_cli.run(
369 pre_run=self.pre_prompt, reset_current_buffer=True)
376 pre_run=self.pre_prompt, reset_current_buffer=True)
370 return document.text
377 return document.text
371
378
372 def enable_win_unicode_console(self):
379 def enable_win_unicode_console(self):
373 if sys.version_info >= (3, 6):
380 if sys.version_info >= (3, 6):
374 # Since PEP 528, Python uses the unicode APIs for the Windows
381 # Since PEP 528, Python uses the unicode APIs for the Windows
375 # console by default, so WUC shouldn't be needed.
382 # console by default, so WUC shouldn't be needed.
376 return
383 return
377
384
378 import win_unicode_console
385 import win_unicode_console
379 win_unicode_console.enable()
386 win_unicode_console.enable()
380
387
381 def init_io(self):
388 def init_io(self):
382 if sys.platform not in {'win32', 'cli'}:
389 if sys.platform not in {'win32', 'cli'}:
383 return
390 return
384
391
385 self.enable_win_unicode_console()
392 self.enable_win_unicode_console()
386
393
387 import colorama
394 import colorama
388 colorama.init()
395 colorama.init()
389
396
390 # For some reason we make these wrappers around stdout/stderr.
397 # For some reason we make these wrappers around stdout/stderr.
391 # For now, we need to reset them so all output gets coloured.
398 # For now, we need to reset them so all output gets coloured.
392 # https://github.com/ipython/ipython/issues/8669
399 # https://github.com/ipython/ipython/issues/8669
393 # io.std* are deprecated, but don't show our own deprecation warnings
400 # io.std* are deprecated, but don't show our own deprecation warnings
394 # during initialization of the deprecated API.
401 # during initialization of the deprecated API.
395 with warnings.catch_warnings():
402 with warnings.catch_warnings():
396 warnings.simplefilter('ignore', DeprecationWarning)
403 warnings.simplefilter('ignore', DeprecationWarning)
397 io.stdout = io.IOStream(sys.stdout)
404 io.stdout = io.IOStream(sys.stdout)
398 io.stderr = io.IOStream(sys.stderr)
405 io.stderr = io.IOStream(sys.stderr)
399
406
400 def init_magics(self):
407 def init_magics(self):
401 super(TerminalInteractiveShell, self).init_magics()
408 super(TerminalInteractiveShell, self).init_magics()
402 self.register_magics(TerminalMagics)
409 self.register_magics(TerminalMagics)
403
410
404 def init_alias(self):
411 def init_alias(self):
405 # The parent class defines aliases that can be safely used with any
412 # The parent class defines aliases that can be safely used with any
406 # frontend.
413 # frontend.
407 super(TerminalInteractiveShell, self).init_alias()
414 super(TerminalInteractiveShell, self).init_alias()
408
415
409 # Now define aliases that only make sense on the terminal, because they
416 # Now define aliases that only make sense on the terminal, because they
410 # need direct access to the console in a way that we can't emulate in
417 # need direct access to the console in a way that we can't emulate in
411 # GUI or web frontend
418 # GUI or web frontend
412 if os.name == 'posix':
419 if os.name == 'posix':
413 for cmd in ['clear', 'more', 'less', 'man']:
420 for cmd in ['clear', 'more', 'less', 'man']:
414 self.alias_manager.soft_define_alias(cmd, cmd)
421 self.alias_manager.soft_define_alias(cmd, cmd)
415
422
416
423
417 def __init__(self, *args, **kwargs):
424 def __init__(self, *args, **kwargs):
418 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
425 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
419 self.init_prompt_toolkit_cli()
426 self.init_prompt_toolkit_cli()
420 self.init_term_title()
427 self.init_term_title()
421 self.keep_running = True
428 self.keep_running = True
422
429
423 self.debugger_history = InMemoryHistory()
430 self.debugger_history = InMemoryHistory()
424
431
425 def ask_exit(self):
432 def ask_exit(self):
426 self.keep_running = False
433 self.keep_running = False
427
434
428 rl_next_input = None
435 rl_next_input = None
429
436
430 def pre_prompt(self):
437 def pre_prompt(self):
431 if self.rl_next_input:
438 if self.rl_next_input:
432 # We can't set the buffer here, because it will be reset just after
439 # We can't set the buffer here, because it will be reset just after
433 # this. Adding a callable to pre_run_callables does what we need
440 # this. Adding a callable to pre_run_callables does what we need
434 # after the buffer is reset.
441 # after the buffer is reset.
435 s = self.rl_next_input
442 s = self.rl_next_input
436 def set_doc():
443 def set_doc():
437 self.pt_cli.application.buffer.document = Document(s)
444 self.pt_cli.application.buffer.document = Document(s)
438 if hasattr(self.pt_cli, 'pre_run_callables'):
445 if hasattr(self.pt_cli, 'pre_run_callables'):
439 self.pt_cli.pre_run_callables.append(set_doc)
446 self.pt_cli.pre_run_callables.append(set_doc)
440 else:
447 else:
441 # Older version of prompt_toolkit; it's OK to set the document
448 # Older version of prompt_toolkit; it's OK to set the document
442 # directly here.
449 # directly here.
443 set_doc()
450 set_doc()
444 self.rl_next_input = None
451 self.rl_next_input = None
445
452
446 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
453 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
447
454
448 if display_banner is not DISPLAY_BANNER_DEPRECATED:
455 if display_banner is not DISPLAY_BANNER_DEPRECATED:
449 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
456 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
450
457
451 self.keep_running = True
458 self.keep_running = True
452 while self.keep_running:
459 while self.keep_running:
453 print(self.separate_in, end='')
460 print(self.separate_in, end='')
454
461
455 try:
462 try:
456 code = self.prompt_for_code()
463 code = self.prompt_for_code()
457 except EOFError:
464 except EOFError:
458 if (not self.confirm_exit) \
465 if (not self.confirm_exit) \
459 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'):
460 self.ask_exit()
467 self.ask_exit()
461
468
462 else:
469 else:
463 if code:
470 if code:
464 self.run_cell(code, store_history=True)
471 self.run_cell(code, store_history=True)
465
472
466 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
473 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
467 # An extra layer of protection in case someone mashing Ctrl-C breaks
474 # An extra layer of protection in case someone mashing Ctrl-C breaks
468 # out of our internal code.
475 # out of our internal code.
469 if display_banner is not DISPLAY_BANNER_DEPRECATED:
476 if display_banner is not DISPLAY_BANNER_DEPRECATED:
470 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
477 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
471 while True:
478 while True:
472 try:
479 try:
473 self.interact()
480 self.interact()
474 break
481 break
475 except KeyboardInterrupt as e:
482 except KeyboardInterrupt as e:
476 print("\n%s escaped interact()\n" % type(e).__name__)
483 print("\n%s escaped interact()\n" % type(e).__name__)
477 finally:
484 finally:
478 # An interrupt during the eventloop will mess up the
485 # An interrupt during the eventloop will mess up the
479 # internal state of the prompt_toolkit library.
486 # internal state of the prompt_toolkit library.
480 # Stopping the eventloop fixes this, see
487 # Stopping the eventloop fixes this, see
481 # https://github.com/ipython/ipython/pull/9867
488 # https://github.com/ipython/ipython/pull/9867
482 if hasattr(self, '_eventloop'):
489 if hasattr(self, '_eventloop'):
483 self._eventloop.stop()
490 self._eventloop.stop()
484
491
485 _inputhook = None
492 _inputhook = None
486 def inputhook(self, context):
493 def inputhook(self, context):
487 if self._inputhook is not None:
494 if self._inputhook is not None:
488 self._inputhook(context)
495 self._inputhook(context)
489
496
490 active_eventloop = None
497 active_eventloop = None
491 def enable_gui(self, gui=None):
498 def enable_gui(self, gui=None):
492 if gui:
499 if gui:
493 self.active_eventloop, self._inputhook =\
500 self.active_eventloop, self._inputhook =\
494 get_inputhook_name_and_func(gui)
501 get_inputhook_name_and_func(gui)
495 else:
502 else:
496 self.active_eventloop = self._inputhook = None
503 self.active_eventloop = self._inputhook = None
497
504
498 # Run !system commands directly, not through pipes, so terminal programs
505 # Run !system commands directly, not through pipes, so terminal programs
499 # work correctly.
506 # work correctly.
500 system = InteractiveShell.system_raw
507 system = InteractiveShell.system_raw
501
508
502 def auto_rewrite_input(self, cmd):
509 def auto_rewrite_input(self, cmd):
503 """Overridden from the parent class to use fancy rewriting prompt"""
510 """Overridden from the parent class to use fancy rewriting prompt"""
504 if not self.show_rewritten_input:
511 if not self.show_rewritten_input:
505 return
512 return
506
513
507 tokens = self.prompts.rewrite_prompt_tokens()
514 tokens = self.prompts.rewrite_prompt_tokens()
508 if self.pt_cli:
515 if self.pt_cli:
509 self.pt_cli.print_tokens(tokens)
516 self.pt_cli.print_tokens(tokens)
510 print(cmd)
517 print(cmd)
511 else:
518 else:
512 prompt = ''.join(s for t, s in tokens)
519 prompt = ''.join(s for t, s in tokens)
513 print(prompt, cmd, sep='')
520 print(prompt, cmd, sep='')
514
521
515 _prompts_before = None
522 _prompts_before = None
516 def switch_doctest_mode(self, mode):
523 def switch_doctest_mode(self, mode):
517 """Switch prompts to classic for %doctest_mode"""
524 """Switch prompts to classic for %doctest_mode"""
518 if mode:
525 if mode:
519 self._prompts_before = self.prompts
526 self._prompts_before = self.prompts
520 self.prompts = ClassicPrompts(self)
527 self.prompts = ClassicPrompts(self)
521 elif self._prompts_before:
528 elif self._prompts_before:
522 self.prompts = self._prompts_before
529 self.prompts = self._prompts_before
523 self._prompts_before = None
530 self._prompts_before = None
524 self._update_layout()
531 self._update_layout()
525
532
526
533
527 InteractiveShellABC.register(TerminalInteractiveShell)
534 InteractiveShellABC.register(TerminalInteractiveShell)
528
535
529 if __name__ == '__main__':
536 if __name__ == '__main__':
530 TerminalInteractiveShell.instance().interact()
537 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now