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