Show More
@@ -333,6 +333,7 b' def create_ipython_shortcuts(shell, for_all_platforms: bool = False):' | |||||
333 | kb.add("escape", "f", filter=focused_insert_vi & ebivim)( |
|
333 | kb.add("escape", "f", filter=focused_insert_vi & ebivim)( | |
334 | autosuggestions.accept_word |
|
334 | autosuggestions.accept_word | |
335 | ) |
|
335 | ) | |
|
336 | kb.add("c-right", filter=has_focus(DEFAULT_BUFFER))(autosuggestions.accept_token) | |||
336 |
|
337 | |||
337 | # Simple Control keybindings |
|
338 | # Simple Control keybindings | |
338 | key_cmd_dict = { |
|
339 | key_cmd_dict = { |
@@ -1,7 +1,13 b'' | |||||
1 | import re |
|
1 | import re | |
|
2 | import tokenize | |||
|
3 | from io import StringIO | |||
|
4 | from typing import List, Optional | |||
|
5 | ||||
2 | from prompt_toolkit.key_binding import KeyPressEvent |
|
6 | from prompt_toolkit.key_binding import KeyPressEvent | |
3 | from prompt_toolkit.key_binding.bindings import named_commands as nc |
|
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 | # Needed for to accept autosuggestions in vi insert mode |
|
12 | # Needed for to accept autosuggestions in vi insert mode | |
7 | def accept_in_vi_insert_mode(event: KeyPressEvent): |
|
13 | def accept_in_vi_insert_mode(event: KeyPressEvent): | |
@@ -18,7 +24,7 b' def accept_in_vi_insert_mode(event: KeyPressEvent):' | |||||
18 | nc.end_of_line(event) |
|
24 | nc.end_of_line(event) | |
19 |
|
25 | |||
20 |
|
26 | |||
21 | def accept(event): |
|
27 | def accept(event: KeyPressEvent): | |
22 | """Accept suggestion""" |
|
28 | """Accept suggestion""" | |
23 | b = event.current_buffer |
|
29 | b = event.current_buffer | |
24 | suggestion = b.suggestion |
|
30 | suggestion = b.suggestion | |
@@ -28,7 +34,7 b' def accept(event):' | |||||
28 | nc.forward_char(event) |
|
34 | nc.forward_char(event) | |
29 |
|
35 | |||
30 |
|
36 | |||
31 | def accept_word(event): |
|
37 | def accept_word(event: KeyPressEvent): | |
32 | """Fill partial suggestion by word""" |
|
38 | """Fill partial suggestion by word""" | |
33 | b = event.current_buffer |
|
39 | b = event.current_buffer | |
34 | suggestion = b.suggestion |
|
40 | suggestion = b.suggestion | |
@@ -37,3 +43,45 b' def accept_word(event):' | |||||
37 | b.insert_text(next((x for x in t if x), "")) |
|
43 | b.insert_text(next((x for x in t if x), "")) | |
38 | else: |
|
44 | else: | |
39 | nc.forward_word(event) |
|
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 | import pytest |
|
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 | def make_event(text, cursor, suggestion): |
|
10 | def make_event(text, cursor, suggestion): | |
8 | event = Mock() |
|
11 | event = Mock() | |
9 | event.current_buffer = Mock() |
|
12 | event.current_buffer = Mock() | |
10 | event.current_buffer.suggestion = Mock() |
|
13 | event.current_buffer.suggestion = Mock() | |
|
14 | event.current_buffer.text = text | |||
11 | event.current_buffer.cursor_position = cursor |
|
15 | event.current_buffer.cursor_position = cursor | |
12 | event.current_buffer.suggestion.text = suggestion |
|
16 | event.current_buffer.suggestion.text = suggestion | |
13 | event.current_buffer.document = Mock() |
|
17 | event.current_buffer.document = Mock() | |
@@ -32,9 +36,59 b' def test_autosuggest_at_EOL(text, cursor, suggestion, called):' | |||||
32 |
|
36 | |||
33 | event = make_event(text, cursor, suggestion) |
|
37 | event = make_event(text, cursor, suggestion) | |
34 | event.current_buffer.insert_text = Mock() |
|
38 | event.current_buffer.insert_text = Mock() | |
35 | _apply_autosuggest(event) |
|
39 | accept_in_vi_insert_mode(event) | |
36 | if called: |
|
40 | if called: | |
37 | event.current_buffer.insert_text.assert_called() |
|
41 | event.current_buffer.insert_text.assert_called() | |
38 | else: |
|
42 | else: | |
39 | event.current_buffer.insert_text.assert_not_called() |
|
43 | event.current_buffer.insert_text.assert_not_called() | |
40 | # event.current_buffer.document.get_end_of_line_position.assert_called() |
|
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