##// END OF EJS Templates
Add missing line_below/line_above conditions
krassowski -
Show More
@@ -1,668 +1,671
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 os
10 10 import re
11 11 import signal
12 12 import sys
13 13 import warnings
14 14 from typing import Callable, Dict, Union
15 15
16 16 from prompt_toolkit.application.current import get_app
17 17 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
18 18 from prompt_toolkit.filters import Condition, emacs_insert_mode, has_completions
19 19 from prompt_toolkit.filters import has_focus as has_focus_impl
20 20 from prompt_toolkit.filters import (
21 21 has_selection,
22 22 has_suggestion,
23 23 vi_insert_mode,
24 24 vi_mode,
25 25 )
26 26 from prompt_toolkit.key_binding import KeyBindings
27 27 from prompt_toolkit.key_binding.bindings import named_commands as nc
28 28 from prompt_toolkit.key_binding.bindings.completion import (
29 29 display_completions_like_readline,
30 30 )
31 31 from prompt_toolkit.key_binding.vi_state import InputMode, ViState
32 32 from prompt_toolkit.layout.layout import FocusableElement
33 33
34 34 from IPython.terminal.shortcuts import auto_match as match
35 35 from IPython.terminal.shortcuts import auto_suggest
36 36 from IPython.utils.decorators import undoc
37 37
38 38 __all__ = ["create_ipython_shortcuts"]
39 39
40 40
41 41 @undoc
42 42 @Condition
43 43 def cursor_in_leading_ws():
44 44 before = get_app().current_buffer.document.current_line_before_cursor
45 45 return (not before) or before.isspace()
46 46
47 47
48 48 def has_focus(value: FocusableElement):
49 49 """Wrapper around has_focus adding a nice `__name__` to tester function"""
50 50 tester = has_focus_impl(value).func
51 51 tester.__name__ = f"is_focused({value})"
52 52 return Condition(tester)
53 53
54 54
55 @undoc
55 56 @Condition
56 57 def has_line_below() -> bool:
57 58 document = get_app().current_buffer.document
58 59 return document.cursor_position_row < len(document.lines) - 1
59 60
60 61
62 @undoc
61 63 @Condition
62 64 def has_line_above() -> bool:
63 65 document = get_app().current_buffer.document
64 66 return document.cursor_position_row != 0
65 67
66 68
67 69 def create_ipython_shortcuts(shell, for_all_platforms: bool = False) -> KeyBindings:
68 70 """Set up the prompt_toolkit keyboard shortcuts for IPython.
69 71
70 72 Parameters
71 73 ----------
72 74 shell: InteractiveShell
73 75 The current IPython shell Instance
74 76 for_all_platforms: bool (default false)
75 77 This parameter is mostly used in generating the documentation
76 78 to create the shortcut binding for all the platforms, and export
77 79 them.
78 80
79 81 Returns
80 82 -------
81 83 KeyBindings
82 84 the keybinding instance for prompt toolkit.
83 85
84 86 """
85 87 # Warning: if possible, do NOT define handler functions in the locals
86 88 # scope of this function, instead define functions in the global
87 89 # scope, or a separate module, and include a user-friendly docstring
88 90 # describing the action.
89 91
90 92 kb = KeyBindings()
91 93 insert_mode = vi_insert_mode | emacs_insert_mode
92 94
93 95 if getattr(shell, "handle_return", None):
94 96 return_handler = shell.handle_return(shell)
95 97 else:
96 98 return_handler = newline_or_execute_outer(shell)
97 99
98 100 kb.add("enter", filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode))(
99 101 return_handler
100 102 )
101 103
102 104 @Condition
103 105 def ebivim():
104 106 return shell.emacs_bindings_in_vi_insert_mode
105 107
106 108 @kb.add(
107 109 "escape",
108 110 "enter",
109 111 filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode & ebivim),
110 112 )
111 113 def reformat_and_execute(event):
112 114 """Reformat code and execute it"""
113 115 reformat_text_before_cursor(
114 116 event.current_buffer, event.current_buffer.document, shell
115 117 )
116 118 event.current_buffer.validate_and_handle()
117 119
118 120 kb.add("c-\\")(quit)
119 121
120 122 kb.add("c-p", filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)))(
121 123 previous_history_or_previous_completion
122 124 )
123 125
124 126 kb.add("c-n", filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)))(
125 127 next_history_or_next_completion
126 128 )
127 129
128 130 kb.add("c-g", filter=(has_focus(DEFAULT_BUFFER) & has_completions))(
129 131 dismiss_completion
130 132 )
131 133
132 134 kb.add("c-c", filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
133 135
134 136 kb.add("c-c", filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
135 137
136 138 supports_suspend = Condition(lambda: hasattr(signal, "SIGTSTP"))
137 139 kb.add("c-z", filter=supports_suspend)(suspend_to_bg)
138 140
139 141 # Ctrl+I == Tab
140 142 kb.add(
141 143 "tab",
142 144 filter=(
143 145 has_focus(DEFAULT_BUFFER)
144 146 & ~has_selection
145 147 & insert_mode
146 148 & cursor_in_leading_ws
147 149 ),
148 150 )(indent_buffer)
149 151 kb.add("c-o", filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode))(
150 152 newline_autoindent_outer(shell.input_transformer_manager)
151 153 )
152 154
153 155 kb.add("f2", filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
154 156
155 157 @Condition
156 158 def auto_match():
157 159 return shell.auto_match
158 160
159 161 def all_quotes_paired(quote, buf):
160 162 paired = True
161 163 i = 0
162 164 while i < len(buf):
163 165 c = buf[i]
164 166 if c == quote:
165 167 paired = not paired
166 168 elif c == "\\":
167 169 i += 1
168 170 i += 1
169 171 return paired
170 172
171 173 focused_insert = (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER)
172 174 _preceding_text_cache: Dict[Union[str, Callable], Condition] = {}
173 175 _following_text_cache: Dict[Union[str, Callable], Condition] = {}
174 176
175 177 def preceding_text(pattern: Union[str, Callable]):
176 178 if pattern in _preceding_text_cache:
177 179 return _preceding_text_cache[pattern]
178 180
179 181 if callable(pattern):
180 182
181 183 def _preceding_text():
182 184 app = get_app()
183 185 before_cursor = app.current_buffer.document.current_line_before_cursor
184 186 # mypy can't infer if(callable): https://github.com/python/mypy/issues/3603
185 187 return bool(pattern(before_cursor)) # type: ignore[operator]
186 188
187 189 else:
188 190 m = re.compile(pattern)
189 191
190 192 def _preceding_text():
191 193 app = get_app()
192 194 before_cursor = app.current_buffer.document.current_line_before_cursor
193 195 return bool(m.match(before_cursor))
194 196
195 197 _preceding_text.__name__ = f"preceding_text({pattern!r})"
196 198
197 199 condition = Condition(_preceding_text)
198 200 _preceding_text_cache[pattern] = condition
199 201 return condition
200 202
201 203 def following_text(pattern):
202 204 try:
203 205 return _following_text_cache[pattern]
204 206 except KeyError:
205 207 pass
206 208 m = re.compile(pattern)
207 209
208 210 def _following_text():
209 211 app = get_app()
210 212 return bool(m.match(app.current_buffer.document.current_line_after_cursor))
211 213
212 214 _following_text.__name__ = f"following_text({pattern!r})"
213 215
214 216 condition = Condition(_following_text)
215 217 _following_text_cache[pattern] = condition
216 218 return condition
217 219
218 220 @Condition
219 221 def not_inside_unclosed_string():
220 222 app = get_app()
221 223 s = app.current_buffer.document.text_before_cursor
222 224 # remove escaped quotes
223 225 s = s.replace('\\"', "").replace("\\'", "")
224 226 # remove triple-quoted string literals
225 227 s = re.sub(r"(?:\"\"\"[\s\S]*\"\"\"|'''[\s\S]*''')", "", s)
226 228 # remove single-quoted string literals
227 229 s = re.sub(r"""(?:"[^"]*["\n]|'[^']*['\n])""", "", s)
228 230 return not ('"' in s or "'" in s)
229 231
230 232 # auto match
231 233 for key, cmd in match.auto_match_parens.items():
232 234 kb.add(key, filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))(
233 235 cmd
234 236 )
235 237
236 238 # raw string
237 239 for key, cmd in match.auto_match_parens_raw_string.items():
238 240 kb.add(
239 241 key,
240 242 filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$"),
241 243 )(cmd)
242 244
243 245 kb.add(
244 246 '"',
245 247 filter=focused_insert
246 248 & auto_match
247 249 & not_inside_unclosed_string
248 250 & preceding_text(lambda line: all_quotes_paired('"', line))
249 251 & following_text(r"[,)}\]]|$"),
250 252 )(match.double_quote)
251 253
252 254 kb.add(
253 255 "'",
254 256 filter=focused_insert
255 257 & auto_match
256 258 & not_inside_unclosed_string
257 259 & preceding_text(lambda line: all_quotes_paired("'", line))
258 260 & following_text(r"[,)}\]]|$"),
259 261 )(match.single_quote)
260 262
261 263 kb.add(
262 264 '"',
263 265 filter=focused_insert
264 266 & auto_match
265 267 & not_inside_unclosed_string
266 268 & preceding_text(r'^.*""$'),
267 269 )(match.docstring_double_quotes)
268 270
269 271 kb.add(
270 272 "'",
271 273 filter=focused_insert
272 274 & auto_match
273 275 & not_inside_unclosed_string
274 276 & preceding_text(r"^.*''$"),
275 277 )(match.docstring_single_quotes)
276 278
277 279 # just move cursor
278 280 kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)"))(
279 281 match.skip_over
280 282 )
281 283 kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]"))(
282 284 match.skip_over
283 285 )
284 286 kb.add("}", filter=focused_insert & auto_match & following_text(r"^\}"))(
285 287 match.skip_over
286 288 )
287 289 kb.add('"', filter=focused_insert & auto_match & following_text('^"'))(
288 290 match.skip_over
289 291 )
290 292 kb.add("'", filter=focused_insert & auto_match & following_text("^'"))(
291 293 match.skip_over
292 294 )
293 295
294 296 kb.add(
295 297 "backspace",
296 298 filter=focused_insert
297 299 & preceding_text(r".*\($")
298 300 & auto_match
299 301 & following_text(r"^\)"),
300 302 )(match.delete_pair)
301 303 kb.add(
302 304 "backspace",
303 305 filter=focused_insert
304 306 & preceding_text(r".*\[$")
305 307 & auto_match
306 308 & following_text(r"^\]"),
307 309 )(match.delete_pair)
308 310 kb.add(
309 311 "backspace",
310 312 filter=focused_insert
311 313 & preceding_text(r".*\{$")
312 314 & auto_match
313 315 & following_text(r"^\}"),
314 316 )(match.delete_pair)
315 317 kb.add(
316 318 "backspace",
317 319 filter=focused_insert
318 320 & preceding_text('.*"$')
319 321 & auto_match
320 322 & following_text('^"'),
321 323 )(match.delete_pair)
322 324 kb.add(
323 325 "backspace",
324 326 filter=focused_insert
325 327 & preceding_text(r".*'$")
326 328 & auto_match
327 329 & following_text(r"^'"),
328 330 )(match.delete_pair)
329 331
330 332 if shell.display_completions == "readlinelike":
331 333 kb.add(
332 334 "c-i",
333 335 filter=(
334 336 has_focus(DEFAULT_BUFFER)
335 337 & ~has_selection
336 338 & insert_mode
337 339 & ~cursor_in_leading_ws
338 340 ),
339 341 )(display_completions_like_readline)
340 342
341 343 if sys.platform == "win32" or for_all_platforms:
342 344 kb.add("c-v", filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
343 345
344 346 focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode
345 347
346 348 # autosuggestions
347 349 @Condition
348 350 def navigable_suggestions():
349 351 return isinstance(
350 352 shell.auto_suggest, auto_suggest.NavigableAutoSuggestFromHistory
351 353 )
352 354
353 355 kb.add("end", filter=has_focus(DEFAULT_BUFFER) & (ebivim | ~vi_insert_mode))(
354 356 auto_suggest.accept_in_vi_insert_mode
355 357 )
356 358 kb.add("c-e", filter=focused_insert_vi & ebivim)(
357 359 auto_suggest.accept_in_vi_insert_mode
358 360 )
359 361 kb.add("c-f", filter=focused_insert_vi)(auto_suggest.accept)
360 362 kb.add("escape", "f", filter=focused_insert_vi & ebivim)(auto_suggest.accept_word)
361 363 kb.add("c-right", filter=has_suggestion & has_focus(DEFAULT_BUFFER))(
362 364 auto_suggest.accept_token
363 365 )
364 366 kb.add("escape", filter=has_suggestion & has_focus(DEFAULT_BUFFER))(
365 367 auto_suggest.discard
366 368 )
367 369 kb.add(
368 370 "up",
369 371 filter=navigable_suggestions
370 372 & ~has_line_above
371 373 & has_suggestion
372 374 & has_focus(DEFAULT_BUFFER),
373 375 )(auto_suggest.swap_autosuggestion_up(shell.auto_suggest))
374 376 kb.add(
375 377 "down",
376 378 filter=navigable_suggestions
377 379 & ~has_line_below
378 380 & has_suggestion
379 381 & has_focus(DEFAULT_BUFFER),
380 382 )(auto_suggest.swap_autosuggestion_down(shell.auto_suggest))
381 kb.add("up", filter=navigable_suggestions & has_focus(DEFAULT_BUFFER))(
382 auto_suggest.up_and_update_hint
383 )
384 kb.add("down", filter=navigable_suggestions & has_focus(DEFAULT_BUFFER))(
385 auto_suggest.down_and_update_hint
386 )
383 kb.add(
384 "up", filter=has_line_above & navigable_suggestions & has_focus(DEFAULT_BUFFER)
385 )(auto_suggest.up_and_update_hint)
386 kb.add(
387 "down",
388 filter=has_line_below & navigable_suggestions & has_focus(DEFAULT_BUFFER),
389 )(auto_suggest.down_and_update_hint)
387 390 kb.add("right", filter=has_suggestion & has_focus(DEFAULT_BUFFER))(
388 391 auto_suggest.accept_character
389 392 )
390 393 kb.add("c-left", filter=has_suggestion & has_focus(DEFAULT_BUFFER))(
391 394 auto_suggest.accept_and_move_cursor_left
392 395 )
393 396 kb.add("c-down", filter=has_suggestion & has_focus(DEFAULT_BUFFER))(
394 397 auto_suggest.accept_and_keep_cursor
395 398 )
396 399 kb.add("backspace", filter=has_suggestion & has_focus(DEFAULT_BUFFER))(
397 400 auto_suggest.backspace_and_resume_hint
398 401 )
399 402
400 403 # Simple Control keybindings
401 404 key_cmd_dict = {
402 405 "c-a": nc.beginning_of_line,
403 406 "c-b": nc.backward_char,
404 407 "c-k": nc.kill_line,
405 408 "c-w": nc.backward_kill_word,
406 409 "c-y": nc.yank,
407 410 "c-_": nc.undo,
408 411 }
409 412
410 413 for key, cmd in key_cmd_dict.items():
411 414 kb.add(key, filter=focused_insert_vi & ebivim)(cmd)
412 415
413 416 # Alt and Combo Control keybindings
414 417 keys_cmd_dict = {
415 418 # Control Combos
416 419 ("c-x", "c-e"): nc.edit_and_execute,
417 420 ("c-x", "e"): nc.edit_and_execute,
418 421 # Alt
419 422 ("escape", "b"): nc.backward_word,
420 423 ("escape", "c"): nc.capitalize_word,
421 424 ("escape", "d"): nc.kill_word,
422 425 ("escape", "h"): nc.backward_kill_word,
423 426 ("escape", "l"): nc.downcase_word,
424 427 ("escape", "u"): nc.uppercase_word,
425 428 ("escape", "y"): nc.yank_pop,
426 429 ("escape", "."): nc.yank_last_arg,
427 430 }
428 431
429 432 for keys, cmd in keys_cmd_dict.items():
430 433 kb.add(*keys, filter=focused_insert_vi & ebivim)(cmd)
431 434
432 435 def get_input_mode(self):
433 436 app = get_app()
434 437 app.ttimeoutlen = shell.ttimeoutlen
435 438 app.timeoutlen = shell.timeoutlen
436 439
437 440 return self._input_mode
438 441
439 442 def set_input_mode(self, mode):
440 443 shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
441 444 cursor = "\x1b[{} q".format(shape)
442 445
443 446 sys.stdout.write(cursor)
444 447 sys.stdout.flush()
445 448
446 449 self._input_mode = mode
447 450
448 451 if shell.editing_mode == "vi" and shell.modal_cursor:
449 452 ViState._input_mode = InputMode.INSERT # type: ignore
450 453 ViState.input_mode = property(get_input_mode, set_input_mode) # type: ignore
451 454
452 455 return kb
453 456
454 457
455 458 def reformat_text_before_cursor(buffer, document, shell):
456 459 text = buffer.delete_before_cursor(len(document.text[: document.cursor_position]))
457 460 try:
458 461 formatted_text = shell.reformat_handler(text)
459 462 buffer.insert_text(formatted_text)
460 463 except Exception as e:
461 464 buffer.insert_text(text)
462 465
463 466
464 467 def newline_or_execute_outer(shell):
465 468 def newline_or_execute(event):
466 469 """When the user presses return, insert a newline or execute the code."""
467 470 b = event.current_buffer
468 471 d = b.document
469 472
470 473 if b.complete_state:
471 474 cc = b.complete_state.current_completion
472 475 if cc:
473 476 b.apply_completion(cc)
474 477 else:
475 478 b.cancel_completion()
476 479 return
477 480
478 481 # If there's only one line, treat it as if the cursor is at the end.
479 482 # See https://github.com/ipython/ipython/issues/10425
480 483 if d.line_count == 1:
481 484 check_text = d.text
482 485 else:
483 486 check_text = d.text[: d.cursor_position]
484 487 status, indent = shell.check_complete(check_text)
485 488
486 489 # if all we have after the cursor is whitespace: reformat current text
487 490 # before cursor
488 491 after_cursor = d.text[d.cursor_position :]
489 492 reformatted = False
490 493 if not after_cursor.strip():
491 494 reformat_text_before_cursor(b, d, shell)
492 495 reformatted = True
493 496 if not (
494 497 d.on_last_line
495 498 or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
496 499 ):
497 500 if shell.autoindent:
498 501 b.insert_text("\n" + indent)
499 502 else:
500 503 b.insert_text("\n")
501 504 return
502 505
503 506 if (status != "incomplete") and b.accept_handler:
504 507 if not reformatted:
505 508 reformat_text_before_cursor(b, d, shell)
506 509 b.validate_and_handle()
507 510 else:
508 511 if shell.autoindent:
509 512 b.insert_text("\n" + indent)
510 513 else:
511 514 b.insert_text("\n")
512 515
513 516 newline_or_execute.__qualname__ = "newline_or_execute"
514 517
515 518 return newline_or_execute
516 519
517 520
518 521 def previous_history_or_previous_completion(event):
519 522 """
520 523 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
521 524
522 525 If completer is open this still select previous completion.
523 526 """
524 527 event.current_buffer.auto_up()
525 528
526 529
527 530 def next_history_or_next_completion(event):
528 531 """
529 532 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
530 533
531 534 If completer is open this still select next completion.
532 535 """
533 536 event.current_buffer.auto_down()
534 537
535 538
536 539 def dismiss_completion(event):
537 540 """Dismiss completion"""
538 541 b = event.current_buffer
539 542 if b.complete_state:
540 543 b.cancel_completion()
541 544
542 545
543 546 def reset_buffer(event):
544 547 """Reset buffer"""
545 548 b = event.current_buffer
546 549 if b.complete_state:
547 550 b.cancel_completion()
548 551 else:
549 552 b.reset()
550 553
551 554
552 555 def reset_search_buffer(event):
553 556 """Reset search buffer"""
554 557 if event.current_buffer.document.text:
555 558 event.current_buffer.reset()
556 559 else:
557 560 event.app.layout.focus(DEFAULT_BUFFER)
558 561
559 562
560 563 def suspend_to_bg(event):
561 564 """Suspend to background"""
562 565 event.app.suspend_to_background()
563 566
564 567
565 568 def quit(event):
566 569 """
567 570 Quit application with ``SIGQUIT`` if supported or ``sys.exit`` otherwise.
568 571
569 572 On platforms that support SIGQUIT, send SIGQUIT to the current process.
570 573 On other platforms, just exit the process with a message.
571 574 """
572 575 sigquit = getattr(signal, "SIGQUIT", None)
573 576 if sigquit is not None:
574 577 os.kill(0, signal.SIGQUIT)
575 578 else:
576 579 sys.exit("Quit")
577 580
578 581
579 582 def indent_buffer(event):
580 583 """Indent buffer"""
581 584 event.current_buffer.insert_text(" " * 4)
582 585
583 586
584 587 @undoc
585 588 def newline_with_copy_margin(event):
586 589 """
587 590 DEPRECATED since IPython 6.0
588 591
589 592 See :any:`newline_autoindent_outer` for a replacement.
590 593
591 594 Preserve margin and cursor position when using
592 595 Control-O to insert a newline in EMACS mode
593 596 """
594 597 warnings.warn(
595 598 "`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
596 599 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
597 600 DeprecationWarning,
598 601 stacklevel=2,
599 602 )
600 603
601 604 b = event.current_buffer
602 605 cursor_start_pos = b.document.cursor_position_col
603 606 b.newline(copy_margin=True)
604 607 b.cursor_up(count=1)
605 608 cursor_end_pos = b.document.cursor_position_col
606 609 if cursor_start_pos != cursor_end_pos:
607 610 pos_diff = cursor_start_pos - cursor_end_pos
608 611 b.cursor_right(count=pos_diff)
609 612
610 613
611 614 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
612 615 """
613 616 Return a function suitable for inserting a indented newline after the cursor.
614 617
615 618 Fancier version of deprecated ``newline_with_copy_margin`` which should
616 619 compute the correct indentation of the inserted line. That is to say, indent
617 620 by 4 extra space after a function definition, class definition, context
618 621 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
619 622 """
620 623
621 624 def newline_autoindent(event):
622 625 """Insert a newline after the cursor indented appropriately."""
623 626 b = event.current_buffer
624 627 d = b.document
625 628
626 629 if b.complete_state:
627 630 b.cancel_completion()
628 631 text = d.text[: d.cursor_position] + "\n"
629 632 _, indent = inputsplitter.check_complete(text)
630 633 b.insert_text("\n" + (" " * (indent or 0)), move_cursor=False)
631 634
632 635 newline_autoindent.__qualname__ = "newline_autoindent"
633 636
634 637 return newline_autoindent
635 638
636 639
637 640 def open_input_in_editor(event):
638 641 """Open code from input in external editor"""
639 642 event.app.current_buffer.open_in_editor()
640 643
641 644
642 645 if sys.platform == "win32":
643 646 from IPython.core.error import TryNext
644 647 from IPython.lib.clipboard import (
645 648 ClipboardEmpty,
646 649 tkinter_clipboard_get,
647 650 win32_clipboard_get,
648 651 )
649 652
650 653 @undoc
651 654 def win_paste(event):
652 655 try:
653 656 text = win32_clipboard_get()
654 657 except TryNext:
655 658 try:
656 659 text = tkinter_clipboard_get()
657 660 except (TryNext, ClipboardEmpty):
658 661 return
659 662 except ClipboardEmpty:
660 663 return
661 664 event.current_buffer.insert_text(text.replace("\t", " " * 4))
662 665
663 666 else:
664 667
665 668 @undoc
666 669 def win_paste(event):
667 670 """Stub used when auto-generating shortcuts for documentation"""
668 671 pass
General Comments 0
You need to be logged in to leave comments. Login now