Show More
@@ -333,6 +333,7 b' def create_ipython_shortcuts(shell, for_all_platforms: bool = False):' | |||
|
333 | 333 | kb.add("escape", "f", filter=focused_insert_vi & ebivim)( |
|
334 | 334 | autosuggestions.accept_word |
|
335 | 335 | ) |
|
336 | kb.add("c-right", filter=has_focus(DEFAULT_BUFFER))(autosuggestions.accept_token) | |
|
336 | 337 | |
|
337 | 338 | # Simple Control keybindings |
|
338 | 339 | key_cmd_dict = { |
@@ -1,7 +1,13 b'' | |||
|
1 | 1 | import re |
|
2 | import tokenize | |
|
3 | from io import StringIO | |
|
4 | from typing import List, Optional | |
|
5 | ||
|
2 | 6 | from prompt_toolkit.key_binding import KeyPressEvent |
|
3 | 7 | from prompt_toolkit.key_binding.bindings import named_commands as nc |
|
4 | 8 | |
|
9 | from IPython.utils.tokenutil import generate_tokens | |
|
10 | ||
|
5 | 11 | |
|
6 | 12 | # Needed for to accept autosuggestions in vi insert mode |
|
7 | 13 | def accept_in_vi_insert_mode(event: KeyPressEvent): |
@@ -18,7 +24,7 b' def accept_in_vi_insert_mode(event: KeyPressEvent):' | |||
|
18 | 24 | nc.end_of_line(event) |
|
19 | 25 | |
|
20 | 26 | |
|
21 | def accept(event): | |
|
27 | def accept(event: KeyPressEvent): | |
|
22 | 28 | """Accept suggestion""" |
|
23 | 29 | b = event.current_buffer |
|
24 | 30 | suggestion = b.suggestion |
@@ -28,7 +34,7 b' def accept(event):' | |||
|
28 | 34 | nc.forward_char(event) |
|
29 | 35 | |
|
30 | 36 | |
|
31 | def accept_word(event): | |
|
37 | def accept_word(event: KeyPressEvent): | |
|
32 | 38 | """Fill partial suggestion by word""" |
|
33 | 39 | b = event.current_buffer |
|
34 | 40 | suggestion = b.suggestion |
@@ -37,3 +43,45 b' def accept_word(event):' | |||
|
37 | 43 | b.insert_text(next((x for x in t if x), "")) |
|
38 | 44 | else: |
|
39 | 45 | nc.forward_word(event) |
|
46 | ||
|
47 | ||
|
48 | def accept_token(event: KeyPressEvent): | |
|
49 | """Fill partial suggestion by token""" | |
|
50 | b = event.current_buffer | |
|
51 | suggestion = b.suggestion | |
|
52 | ||
|
53 | if suggestion: | |
|
54 | prefix = b.text | |
|
55 | text = prefix + suggestion.text | |
|
56 | ||
|
57 | tokens: List[Optional[str]] = [None, None, None] | |
|
58 | substings = [""] | |
|
59 | i = 0 | |
|
60 | ||
|
61 | for token in generate_tokens(StringIO(text).readline): | |
|
62 | if token.type == tokenize.NEWLINE: | |
|
63 | index = len(text) | |
|
64 | else: | |
|
65 | index = text.index(token[1], len(substings[-1])) | |
|
66 | substings.append(text[:index]) | |
|
67 | tokenized_so_far = substings[-1] | |
|
68 | if tokenized_so_far.startswith(prefix): | |
|
69 | if i == 0 and len(tokenized_so_far) > len(prefix): | |
|
70 | tokens[0] = tokenized_so_far[len(prefix) :] | |
|
71 | substings.append(tokenized_so_far) | |
|
72 | i += 1 | |
|
73 | tokens[i] = token[1] | |
|
74 | if i == 2: | |
|
75 | break | |
|
76 | i += 1 | |
|
77 | ||
|
78 | if tokens[0]: | |
|
79 | to_insert: str | |
|
80 | insert_text = substings[-2] | |
|
81 | if tokens[1] and len(tokens[1]) == 1: | |
|
82 | insert_text = substings[-1] | |
|
83 | to_insert = insert_text[len(prefix) :] | |
|
84 | b.insert_text(to_insert) | |
|
85 | return | |
|
86 | ||
|
87 | nc.forward_word(event) |
@@ -1,13 +1,17 b'' | |||
|
1 | 1 | import pytest |
|
2 |
from IPython.terminal.shortcuts import |
|
|
2 | from IPython.terminal.shortcuts.autosuggestions import ( | |
|
3 | accept_in_vi_insert_mode, | |
|
4 | accept_token, | |
|
5 | ) | |
|
3 | 6 | |
|
4 | from unittest.mock import Mock | |
|
7 | from unittest.mock import patch, Mock | |
|
5 | 8 | |
|
6 | 9 | |
|
7 | 10 | def make_event(text, cursor, suggestion): |
|
8 | 11 | event = Mock() |
|
9 | 12 | event.current_buffer = Mock() |
|
10 | 13 | event.current_buffer.suggestion = Mock() |
|
14 | event.current_buffer.text = text | |
|
11 | 15 | event.current_buffer.cursor_position = cursor |
|
12 | 16 | event.current_buffer.suggestion.text = suggestion |
|
13 | 17 | event.current_buffer.document = Mock() |
@@ -32,9 +36,59 b' def test_autosuggest_at_EOL(text, cursor, suggestion, called):' | |||
|
32 | 36 | |
|
33 | 37 | event = make_event(text, cursor, suggestion) |
|
34 | 38 | event.current_buffer.insert_text = Mock() |
|
35 | _apply_autosuggest(event) | |
|
39 | accept_in_vi_insert_mode(event) | |
|
36 | 40 | if called: |
|
37 | 41 | event.current_buffer.insert_text.assert_called() |
|
38 | 42 | else: |
|
39 | 43 | event.current_buffer.insert_text.assert_not_called() |
|
40 | 44 | # event.current_buffer.document.get_end_of_line_position.assert_called() |
|
45 | ||
|
46 | ||
|
47 | @pytest.mark.parametrize( | |
|
48 | "text, suggestion, expected", | |
|
49 | [ | |
|
50 | ("", "def out(tag: str, n=50):", "def "), | |
|
51 | ("d", "ef out(tag: str, n=50):", "ef "), | |
|
52 | ("de ", "f out(tag: str, n=50):", "f "), | |
|
53 | ("def", " out(tag: str, n=50):", " "), | |
|
54 | ("def ", "out(tag: str, n=50):", "out("), | |
|
55 | ("def o", "ut(tag: str, n=50):", "ut("), | |
|
56 | ("def ou", "t(tag: str, n=50):", "t("), | |
|
57 | ("def out", "(tag: str, n=50):", "("), | |
|
58 | ("def out(", "tag: str, n=50):", "tag: "), | |
|
59 | ("def out(t", "ag: str, n=50):", "ag: "), | |
|
60 | ("def out(ta", "g: str, n=50):", "g: "), | |
|
61 | ("def out(tag", ": str, n=50):", ": "), | |
|
62 | ("def out(tag:", " str, n=50):", " "), | |
|
63 | ("def out(tag: ", "str, n=50):", "str, "), | |
|
64 | ("def out(tag: s", "tr, n=50):", "tr, "), | |
|
65 | ("def out(tag: st", "r, n=50):", "r, "), | |
|
66 | ("def out(tag: str", ", n=50):", ", n"), | |
|
67 | ("def out(tag: str,", " n=50):", " n"), | |
|
68 | ("def out(tag: str, ", "n=50):", "n="), | |
|
69 | ("def out(tag: str, n", "=50):", "="), | |
|
70 | ("def out(tag: str, n=", "50):", "50)"), | |
|
71 | ("def out(tag: str, n=5", "0):", "0)"), | |
|
72 | ("def out(tag: str, n=50", "):", "):"), | |
|
73 | ("def out(tag: str, n=50)", ":", ":"), | |
|
74 | ], | |
|
75 | ) | |
|
76 | def test_autosuggest_token(text, suggestion, expected): | |
|
77 | event = make_event(text, len(text), suggestion) | |
|
78 | event.current_buffer.insert_text = Mock() | |
|
79 | accept_token(event) | |
|
80 | assert event.current_buffer.insert_text.called | |
|
81 | assert event.current_buffer.insert_text.call_args[0] == (expected,) | |
|
82 | ||
|
83 | ||
|
84 | def test_autosuggest_token_empty(): | |
|
85 | full = "def out(tag: str, n=50):" | |
|
86 | event = make_event(full, len(full), "") | |
|
87 | event.current_buffer.insert_text = Mock() | |
|
88 | ||
|
89 | with patch( | |
|
90 | "prompt_toolkit.key_binding.bindings.named_commands.forward_word" | |
|
91 | ) as forward_word: | |
|
92 | accept_token(event) | |
|
93 | assert not event.current_buffer.insert_text.called | |
|
94 | assert forward_word.called |
General Comments 0
You need to be logged in to leave comments.
Login now