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