##// END OF EJS Templates
Add true_color option for prompt_toolkit
Jacob Niehus -
Show More
@@ -1,453 +1,462 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 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.py3compat import PY3, cast_unicode_py2, input
9 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
10 from IPython.utils.terminal import toggle_set_term_title, set_term_title
10 from IPython.utils.terminal import toggle_set_term_title, set_term_title
11 from IPython.utils.process import abbrev_cwd
11 from IPython.utils.process import abbrev_cwd
12 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
12 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
13
13
14 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
14 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
15 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
15 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
16 from prompt_toolkit.history import InMemoryHistory
16 from prompt_toolkit.history import InMemoryHistory
17 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
17 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
18 from prompt_toolkit.interface import CommandLineInterface
18 from prompt_toolkit.interface import CommandLineInterface
19 from prompt_toolkit.key_binding.manager import KeyBindingManager
19 from prompt_toolkit.key_binding.manager import KeyBindingManager
20 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
20 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
21 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
21 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
22
22
23 from pygments.styles import get_style_by_name, get_all_styles
23 from pygments.styles import get_style_by_name, get_all_styles
24 from pygments.token import Token
24 from pygments.token import Token
25
25
26 from .debugger import TerminalPdb, Pdb
26 from .debugger import TerminalPdb, Pdb
27 from .magics import TerminalMagics
27 from .magics import TerminalMagics
28 from .pt_inputhooks import get_inputhook_func
28 from .pt_inputhooks import get_inputhook_func
29 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
29 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
30 from .ptutils import IPythonPTCompleter, IPythonPTLexer
30 from .ptutils import IPythonPTCompleter, IPythonPTLexer
31 from .shortcuts import register_ipython_shortcuts
31 from .shortcuts import register_ipython_shortcuts
32
32
33 DISPLAY_BANNER_DEPRECATED = object()
33 DISPLAY_BANNER_DEPRECATED = object()
34
34
35
35
36 from pygments.style import Style
36 from pygments.style import Style
37
37
38 class _NoStyle(Style): pass
38 class _NoStyle(Style): pass
39
39
40
40
41
41
42 _style_overrides_light_bg = {
42 _style_overrides_light_bg = {
43 Token.Prompt: '#0000ff',
43 Token.Prompt: '#0000ff',
44 Token.PromptNum: '#0000ee bold',
44 Token.PromptNum: '#0000ee bold',
45 Token.OutPrompt: '#cc0000',
45 Token.OutPrompt: '#cc0000',
46 Token.OutPromptNum: '#bb0000 bold',
46 Token.OutPromptNum: '#bb0000 bold',
47 }
47 }
48
48
49 _style_overrides_linux = {
49 _style_overrides_linux = {
50 Token.Prompt: '#00cc00',
50 Token.Prompt: '#00cc00',
51 Token.PromptNum: '#00bb00 bold',
51 Token.PromptNum: '#00bb00 bold',
52 Token.OutPrompt: '#cc0000',
52 Token.OutPrompt: '#cc0000',
53 Token.OutPromptNum: '#bb0000 bold',
53 Token.OutPromptNum: '#bb0000 bold',
54 }
54 }
55
55
56
56
57
57
58 def get_default_editor():
58 def get_default_editor():
59 try:
59 try:
60 ed = os.environ['EDITOR']
60 ed = os.environ['EDITOR']
61 if not PY3:
61 if not PY3:
62 ed = ed.decode()
62 ed = ed.decode()
63 return ed
63 return ed
64 except KeyError:
64 except KeyError:
65 pass
65 pass
66 except UnicodeError:
66 except UnicodeError:
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
68 "default editor.")
68 "default editor.")
69
69
70 if os.name == 'posix':
70 if os.name == 'posix':
71 return 'vi' # the only one guaranteed to be there!
71 return 'vi' # the only one guaranteed to be there!
72 else:
72 else:
73 return 'notepad' # same in Windows!
73 return 'notepad' # same in Windows!
74
74
75
75
76 if sys.stdin and sys.stdout and sys.stderr:
76 if sys.stdin and sys.stdout and sys.stderr:
77 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
77 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
78 else:
78 else:
79 _is_tty = False
79 _is_tty = False
80
80
81
81
82 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
82 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
83
83
84 class TerminalInteractiveShell(InteractiveShell):
84 class TerminalInteractiveShell(InteractiveShell):
85 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
85 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
86 'to reserve for the completion menu'
86 'to reserve for the completion menu'
87 ).tag(config=True)
87 ).tag(config=True)
88
88
89 def _space_for_menu_changed(self, old, new):
89 def _space_for_menu_changed(self, old, new):
90 self._update_layout()
90 self._update_layout()
91
91
92 pt_cli = None
92 pt_cli = None
93 debugger_history = None
93 debugger_history = None
94 _pt_app = None
94 _pt_app = None
95
95
96 simple_prompt = Bool(_use_simple_prompt,
96 simple_prompt = Bool(_use_simple_prompt,
97 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
97 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
98
98
99 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
99 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
100 IPython own testing machinery, and emacs inferior-shell integration through elpy.
100 IPython own testing machinery, and emacs inferior-shell integration through elpy.
101
101
102 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
102 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
103 environment variable is set, or the current terminal is not a tty.
103 environment variable is set, or the current terminal is not a tty.
104
104
105 """
105 """
106 ).tag(config=True)
106 ).tag(config=True)
107
107
108 @property
108 @property
109 def debugger_cls(self):
109 def debugger_cls(self):
110 return Pdb if self.simple_prompt else TerminalPdb
110 return Pdb if self.simple_prompt else TerminalPdb
111
111
112 confirm_exit = Bool(True,
112 confirm_exit = Bool(True,
113 help="""
113 help="""
114 Set to confirm when you try to exit IPython with an EOF (Control-D
114 Set to confirm when you try to exit IPython with an EOF (Control-D
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
116 you can force a direct exit without any confirmation.""",
116 you can force a direct exit without any confirmation.""",
117 ).tag(config=True)
117 ).tag(config=True)
118
118
119 editing_mode = Unicode('emacs',
119 editing_mode = Unicode('emacs',
120 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
120 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
121 ).tag(config=True)
121 ).tag(config=True)
122
122
123 mouse_support = Bool(False,
123 mouse_support = Bool(False,
124 help="Enable mouse support in the prompt"
124 help="Enable mouse support in the prompt"
125 ).tag(config=True)
125 ).tag(config=True)
126
126
127 highlighting_style = Unicode('legacy',
127 highlighting_style = Unicode('legacy',
128 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
128 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
129 ).tag(config=True)
129 ).tag(config=True)
130
130
131
131
132 @observe('highlighting_style')
132 @observe('highlighting_style')
133 @observe('colors')
133 @observe('colors')
134 def _highlighting_style_changed(self, change):
134 def _highlighting_style_changed(self, change):
135 self.refresh_style()
135 self.refresh_style()
136
136
137 def refresh_style(self):
137 def refresh_style(self):
138 self._style = self._make_style_from_name(self.highlighting_style)
138 self._style = self._make_style_from_name(self.highlighting_style)
139
139
140
140
141 highlighting_style_overrides = Dict(
141 highlighting_style_overrides = Dict(
142 help="Override highlighting format for specific tokens"
142 help="Override highlighting format for specific tokens"
143 ).tag(config=True)
143 ).tag(config=True)
144
144
145 true_color = Bool(False,
146 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
147 "If your terminal supports true color, the following command "
148 "should print 'TRUECOLOR' in orange: "
149 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
150 ).tag(config=True)
151
145 editor = Unicode(get_default_editor(),
152 editor = Unicode(get_default_editor(),
146 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)."
147 ).tag(config=True)
154 ).tag(config=True)
148
155
149 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)
150
157
151 prompts = Instance(Prompts)
158 prompts = Instance(Prompts)
152
159
153 @default('prompts')
160 @default('prompts')
154 def _prompts_default(self):
161 def _prompts_default(self):
155 return self.prompts_class(self)
162 return self.prompts_class(self)
156
163
157 @observe('prompts')
164 @observe('prompts')
158 def _(self, change):
165 def _(self, change):
159 self._update_layout()
166 self._update_layout()
160
167
161 @default('displayhook_class')
168 @default('displayhook_class')
162 def _displayhook_class_default(self):
169 def _displayhook_class_default(self):
163 return RichPromptDisplayHook
170 return RichPromptDisplayHook
164
171
165 term_title = Bool(True,
172 term_title = Bool(True,
166 help="Automatically set the terminal title"
173 help="Automatically set the terminal title"
167 ).tag(config=True)
174 ).tag(config=True)
168
175
169 # 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.
170 display_completions_in_columns = Bool(None,
177 display_completions_in_columns = Bool(None,
171 help="DEPRECATED", allow_none=True
178 help="DEPRECATED", allow_none=True
172 ).tag(config=True)
179 ).tag(config=True)
173
180
174 @observe('display_completions_in_columns')
181 @observe('display_completions_in_columns')
175 def _display_completions_in_columns_changed(self, new):
182 def _display_completions_in_columns_changed(self, new):
176 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`"
177 "with the following acceptable value: 'column', 'multicolumn','readlinelike'. ")
184 "with the following acceptable value: 'column', 'multicolumn','readlinelike'. ")
178
185
179 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)
180
187
181 highlight_matching_brackets = Bool(True,
188 highlight_matching_brackets = Bool(True,
182 help="Highlight matching brackets .",
189 help="Highlight matching brackets .",
183 ).tag(config=True)
190 ).tag(config=True)
184
191
185 @observe('term_title')
192 @observe('term_title')
186 def init_term_title(self, change=None):
193 def init_term_title(self, change=None):
187 # Enable or disable the terminal title.
194 # Enable or disable the terminal title.
188 if self.term_title:
195 if self.term_title:
189 toggle_set_term_title(True)
196 toggle_set_term_title(True)
190 set_term_title('IPython: ' + abbrev_cwd())
197 set_term_title('IPython: ' + abbrev_cwd())
191 else:
198 else:
192 toggle_set_term_title(False)
199 toggle_set_term_title(False)
193
200
194 def init_display_formatter(self):
201 def init_display_formatter(self):
195 super(TerminalInteractiveShell, self).init_display_formatter()
202 super(TerminalInteractiveShell, self).init_display_formatter()
196 # terminal only supports plain text
203 # terminal only supports plain text
197 self.display_formatter.active_types = ['text/plain']
204 self.display_formatter.active_types = ['text/plain']
198
205
199 def init_prompt_toolkit_cli(self):
206 def init_prompt_toolkit_cli(self):
200 if self.simple_prompt:
207 if self.simple_prompt:
201 # Fall back to plain non-interactive output for tests.
208 # Fall back to plain non-interactive output for tests.
202 # This is very limited, and only accepts a single line.
209 # This is very limited, and only accepts a single line.
203 def prompt():
210 def prompt():
204 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
211 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
205 self.prompt_for_code = prompt
212 self.prompt_for_code = prompt
206 return
213 return
207
214
208 # Set up keyboard shortcuts
215 # Set up keyboard shortcuts
209 kbmanager = KeyBindingManager.for_prompt()
216 kbmanager = KeyBindingManager.for_prompt()
210 register_ipython_shortcuts(kbmanager.registry, self)
217 register_ipython_shortcuts(kbmanager.registry, self)
211
218
212 # Pre-populate history from IPython's history database
219 # Pre-populate history from IPython's history database
213 history = InMemoryHistory()
220 history = InMemoryHistory()
214 last_cell = u""
221 last_cell = u""
215 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
222 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
216 include_latest=True):
223 include_latest=True):
217 # Ignore blank lines and consecutive duplicates
224 # Ignore blank lines and consecutive duplicates
218 cell = cell.rstrip()
225 cell = cell.rstrip()
219 if cell and (cell != last_cell):
226 if cell and (cell != last_cell):
220 history.append(cell)
227 history.append(cell)
221
228
222 self._style = self._make_style_from_name(self.highlighting_style)
229 self._style = self._make_style_from_name(self.highlighting_style)
223 style = DynamicStyle(lambda: self._style)
230 style = DynamicStyle(lambda: self._style)
224
231
225 editing_mode = getattr(EditingMode, self.editing_mode.upper())
232 editing_mode = getattr(EditingMode, self.editing_mode.upper())
226
233
227 self._pt_app = create_prompt_application(
234 self._pt_app = create_prompt_application(
228 editing_mode=editing_mode,
235 editing_mode=editing_mode,
229 key_bindings_registry=kbmanager.registry,
236 key_bindings_registry=kbmanager.registry,
230 history=history,
237 history=history,
231 completer=IPythonPTCompleter(self.Completer),
238 completer=IPythonPTCompleter(self.Completer),
232 enable_history_search=True,
239 enable_history_search=True,
233 style=style,
240 style=style,
234 mouse_support=self.mouse_support,
241 mouse_support=self.mouse_support,
235 **self._layout_options()
242 **self._layout_options()
236 )
243 )
237 self._eventloop = create_eventloop(self.inputhook)
244 self._eventloop = create_eventloop(self.inputhook)
238 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self._eventloop)
245 self.pt_cli = CommandLineInterface(
246 self._pt_app, eventloop=self._eventloop,
247 output=create_output(true_color=self.true_color))
239
248
240 def _make_style_from_name(self, name):
249 def _make_style_from_name(self, name):
241 """
250 """
242 Small wrapper that make an IPython compatible style from a style name
251 Small wrapper that make an IPython compatible style from a style name
243
252
244 We need that to add style for prompt ... etc.
253 We need that to add style for prompt ... etc.
245 """
254 """
246 style_overrides = {}
255 style_overrides = {}
247 if name == 'legacy':
256 if name == 'legacy':
248 legacy = self.colors.lower()
257 legacy = self.colors.lower()
249 if legacy == 'linux':
258 if legacy == 'linux':
250 style_cls = get_style_by_name('monokai')
259 style_cls = get_style_by_name('monokai')
251 style_overrides = _style_overrides_linux
260 style_overrides = _style_overrides_linux
252 elif legacy == 'lightbg':
261 elif legacy == 'lightbg':
253 style_overrides = _style_overrides_light_bg
262 style_overrides = _style_overrides_light_bg
254 style_cls = get_style_by_name('pastie')
263 style_cls = get_style_by_name('pastie')
255 elif legacy == 'neutral':
264 elif legacy == 'neutral':
256 # The default theme needs to be visible on both a dark background
265 # The default theme needs to be visible on both a dark background
257 # and a light background, because we can't tell what the terminal
266 # and a light background, because we can't tell what the terminal
258 # looks like. These tweaks to the default theme help with that.
267 # looks like. These tweaks to the default theme help with that.
259 style_cls = get_style_by_name('default')
268 style_cls = get_style_by_name('default')
260 style_overrides.update({
269 style_overrides.update({
261 Token.Number: '#007700',
270 Token.Number: '#007700',
262 Token.Operator: 'noinherit',
271 Token.Operator: 'noinherit',
263 Token.String: '#BB6622',
272 Token.String: '#BB6622',
264 Token.Name.Function: '#2080D0',
273 Token.Name.Function: '#2080D0',
265 Token.Name.Class: 'bold #2080D0',
274 Token.Name.Class: 'bold #2080D0',
266 Token.Name.Namespace: 'bold #2080D0',
275 Token.Name.Namespace: 'bold #2080D0',
267 Token.Prompt: '#009900',
276 Token.Prompt: '#009900',
268 Token.PromptNum: '#00ff00 bold',
277 Token.PromptNum: '#00ff00 bold',
269 Token.OutPrompt: '#990000',
278 Token.OutPrompt: '#990000',
270 Token.OutPromptNum: '#ff0000 bold',
279 Token.OutPromptNum: '#ff0000 bold',
271 })
280 })
272 elif legacy =='nocolor':
281 elif legacy =='nocolor':
273 style_cls=_NoStyle
282 style_cls=_NoStyle
274 style_overrides = {}
283 style_overrides = {}
275 else :
284 else :
276 raise ValueError('Got unknown colors: ', legacy)
285 raise ValueError('Got unknown colors: ', legacy)
277 else :
286 else :
278 style_cls = get_style_by_name(name)
287 style_cls = get_style_by_name(name)
279 style_overrides = {
288 style_overrides = {
280 Token.Prompt: '#009900',
289 Token.Prompt: '#009900',
281 Token.PromptNum: '#00ff00 bold',
290 Token.PromptNum: '#00ff00 bold',
282 Token.OutPrompt: '#990000',
291 Token.OutPrompt: '#990000',
283 Token.OutPromptNum: '#ff0000 bold',
292 Token.OutPromptNum: '#ff0000 bold',
284 }
293 }
285 style_overrides.update(self.highlighting_style_overrides)
294 style_overrides.update(self.highlighting_style_overrides)
286 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
295 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
287 style_dict=style_overrides)
296 style_dict=style_overrides)
288
297
289 return style
298 return style
290
299
291 def _layout_options(self):
300 def _layout_options(self):
292 """
301 """
293 Return the current layout option for the current Terminal InteractiveShell
302 Return the current layout option for the current Terminal InteractiveShell
294 """
303 """
295 return {
304 return {
296 'lexer':IPythonPTLexer(),
305 'lexer':IPythonPTLexer(),
297 'reserve_space_for_menu':self.space_for_menu,
306 'reserve_space_for_menu':self.space_for_menu,
298 'get_prompt_tokens':self.prompts.in_prompt_tokens,
307 'get_prompt_tokens':self.prompts.in_prompt_tokens,
299 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
308 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
300 'multiline':True,
309 'multiline':True,
301 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
310 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
302
311
303 # Highlight matching brackets, but only when this setting is
312 # Highlight matching brackets, but only when this setting is
304 # enabled, and only when the DEFAULT_BUFFER has the focus.
313 # enabled, and only when the DEFAULT_BUFFER has the focus.
305 'extra_input_processors': [ConditionalProcessor(
314 'extra_input_processors': [ConditionalProcessor(
306 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
315 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
307 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
316 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
308 Condition(lambda cli: self.highlight_matching_brackets))],
317 Condition(lambda cli: self.highlight_matching_brackets))],
309 }
318 }
310
319
311 def _update_layout(self):
320 def _update_layout(self):
312 """
321 """
313 Ask for a re computation of the application layout, if for example ,
322 Ask for a re computation of the application layout, if for example ,
314 some configuration options have changed.
323 some configuration options have changed.
315 """
324 """
316 if self._pt_app:
325 if self._pt_app:
317 self._pt_app.layout = create_prompt_layout(**self._layout_options())
326 self._pt_app.layout = create_prompt_layout(**self._layout_options())
318
327
319 def prompt_for_code(self):
328 def prompt_for_code(self):
320 document = self.pt_cli.run(
329 document = self.pt_cli.run(
321 pre_run=self.pre_prompt, reset_current_buffer=True)
330 pre_run=self.pre_prompt, reset_current_buffer=True)
322 return document.text
331 return document.text
323
332
324 def init_io(self):
333 def init_io(self):
325 if sys.platform not in {'win32', 'cli'}:
334 if sys.platform not in {'win32', 'cli'}:
326 return
335 return
327
336
328 import win_unicode_console
337 import win_unicode_console
329 import colorama
338 import colorama
330
339
331 win_unicode_console.enable()
340 win_unicode_console.enable()
332 colorama.init()
341 colorama.init()
333
342
334 # For some reason we make these wrappers around stdout/stderr.
343 # For some reason we make these wrappers around stdout/stderr.
335 # For now, we need to reset them so all output gets coloured.
344 # For now, we need to reset them so all output gets coloured.
336 # https://github.com/ipython/ipython/issues/8669
345 # https://github.com/ipython/ipython/issues/8669
337 from IPython.utils import io
346 from IPython.utils import io
338 io.stdout = io.IOStream(sys.stdout)
347 io.stdout = io.IOStream(sys.stdout)
339 io.stderr = io.IOStream(sys.stderr)
348 io.stderr = io.IOStream(sys.stderr)
340
349
341 def init_magics(self):
350 def init_magics(self):
342 super(TerminalInteractiveShell, self).init_magics()
351 super(TerminalInteractiveShell, self).init_magics()
343 self.register_magics(TerminalMagics)
352 self.register_magics(TerminalMagics)
344
353
345 def init_alias(self):
354 def init_alias(self):
346 # The parent class defines aliases that can be safely used with any
355 # The parent class defines aliases that can be safely used with any
347 # frontend.
356 # frontend.
348 super(TerminalInteractiveShell, self).init_alias()
357 super(TerminalInteractiveShell, self).init_alias()
349
358
350 # Now define aliases that only make sense on the terminal, because they
359 # Now define aliases that only make sense on the terminal, because they
351 # need direct access to the console in a way that we can't emulate in
360 # need direct access to the console in a way that we can't emulate in
352 # GUI or web frontend
361 # GUI or web frontend
353 if os.name == 'posix':
362 if os.name == 'posix':
354 for cmd in ['clear', 'more', 'less', 'man']:
363 for cmd in ['clear', 'more', 'less', 'man']:
355 self.alias_manager.soft_define_alias(cmd, cmd)
364 self.alias_manager.soft_define_alias(cmd, cmd)
356
365
357
366
358 def __init__(self, *args, **kwargs):
367 def __init__(self, *args, **kwargs):
359 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
368 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
360 self.init_prompt_toolkit_cli()
369 self.init_prompt_toolkit_cli()
361 self.init_term_title()
370 self.init_term_title()
362 self.keep_running = True
371 self.keep_running = True
363
372
364 self.debugger_history = InMemoryHistory()
373 self.debugger_history = InMemoryHistory()
365
374
366 def ask_exit(self):
375 def ask_exit(self):
367 self.keep_running = False
376 self.keep_running = False
368
377
369 rl_next_input = None
378 rl_next_input = None
370
379
371 def pre_prompt(self):
380 def pre_prompt(self):
372 if self.rl_next_input:
381 if self.rl_next_input:
373 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
382 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
374 self.rl_next_input = None
383 self.rl_next_input = None
375
384
376 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
385 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
377
386
378 if display_banner is not DISPLAY_BANNER_DEPRECATED:
387 if display_banner is not DISPLAY_BANNER_DEPRECATED:
379 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
388 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
380
389
381 while self.keep_running:
390 while self.keep_running:
382 print(self.separate_in, end='')
391 print(self.separate_in, end='')
383
392
384 try:
393 try:
385 code = self.prompt_for_code()
394 code = self.prompt_for_code()
386 except EOFError:
395 except EOFError:
387 if (not self.confirm_exit) \
396 if (not self.confirm_exit) \
388 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
397 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
389 self.ask_exit()
398 self.ask_exit()
390
399
391 else:
400 else:
392 if code:
401 if code:
393 self.run_cell(code, store_history=True)
402 self.run_cell(code, store_history=True)
394
403
395 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
404 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
396 # An extra layer of protection in case someone mashing Ctrl-C breaks
405 # An extra layer of protection in case someone mashing Ctrl-C breaks
397 # out of our internal code.
406 # out of our internal code.
398 if display_banner is not DISPLAY_BANNER_DEPRECATED:
407 if display_banner is not DISPLAY_BANNER_DEPRECATED:
399 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
408 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
400 while True:
409 while True:
401 try:
410 try:
402 self.interact()
411 self.interact()
403 break
412 break
404 except KeyboardInterrupt:
413 except KeyboardInterrupt:
405 print("\nKeyboardInterrupt escaped interact()\n")
414 print("\nKeyboardInterrupt escaped interact()\n")
406
415
407 if hasattr(self, '_eventloop'):
416 if hasattr(self, '_eventloop'):
408 self._eventloop.close()
417 self._eventloop.close()
409
418
410 _inputhook = None
419 _inputhook = None
411 def inputhook(self, context):
420 def inputhook(self, context):
412 if self._inputhook is not None:
421 if self._inputhook is not None:
413 self._inputhook(context)
422 self._inputhook(context)
414
423
415 def enable_gui(self, gui=None):
424 def enable_gui(self, gui=None):
416 if gui:
425 if gui:
417 self._inputhook = get_inputhook_func(gui)
426 self._inputhook = get_inputhook_func(gui)
418 else:
427 else:
419 self._inputhook = None
428 self._inputhook = None
420
429
421 # Run !system commands directly, not through pipes, so terminal programs
430 # Run !system commands directly, not through pipes, so terminal programs
422 # work correctly.
431 # work correctly.
423 system = InteractiveShell.system_raw
432 system = InteractiveShell.system_raw
424
433
425 def auto_rewrite_input(self, cmd):
434 def auto_rewrite_input(self, cmd):
426 """Overridden from the parent class to use fancy rewriting prompt"""
435 """Overridden from the parent class to use fancy rewriting prompt"""
427 if not self.show_rewritten_input:
436 if not self.show_rewritten_input:
428 return
437 return
429
438
430 tokens = self.prompts.rewrite_prompt_tokens()
439 tokens = self.prompts.rewrite_prompt_tokens()
431 if self.pt_cli:
440 if self.pt_cli:
432 self.pt_cli.print_tokens(tokens)
441 self.pt_cli.print_tokens(tokens)
433 print(cmd)
442 print(cmd)
434 else:
443 else:
435 prompt = ''.join(s for t, s in tokens)
444 prompt = ''.join(s for t, s in tokens)
436 print(prompt, cmd, sep='')
445 print(prompt, cmd, sep='')
437
446
438 _prompts_before = None
447 _prompts_before = None
439 def switch_doctest_mode(self, mode):
448 def switch_doctest_mode(self, mode):
440 """Switch prompts to classic for %doctest_mode"""
449 """Switch prompts to classic for %doctest_mode"""
441 if mode:
450 if mode:
442 self._prompts_before = self.prompts
451 self._prompts_before = self.prompts
443 self.prompts = ClassicPrompts(self)
452 self.prompts = ClassicPrompts(self)
444 elif self._prompts_before:
453 elif self._prompts_before:
445 self.prompts = self._prompts_before
454 self.prompts = self._prompts_before
446 self._prompts_before = None
455 self._prompts_before = None
447 self._update_layout()
456 self._update_layout()
448
457
449
458
450 InteractiveShellABC.register(TerminalInteractiveShell)
459 InteractiveShellABC.register(TerminalInteractiveShell)
451
460
452 if __name__ == '__main__':
461 if __name__ == '__main__':
453 TerminalInteractiveShell.instance().interact()
462 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now