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