Show More
@@ -389,7 +389,9 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
389 | 389 | |
|
390 | 390 | def _set_autosuggestions(self, provider): |
|
391 | 391 | # disconnect old handler |
|
392 |
if self.auto_suggest and isinstance( |
|
|
392 | if self.auto_suggest and isinstance( | |
|
393 | self.auto_suggest, NavigableAutoSuggestFromHistory | |
|
394 | ): | |
|
393 | 395 | self.auto_suggest.disconnect() |
|
394 | 396 | if provider is None: |
|
395 | 397 | self.auto_suggest = None |
@@ -660,7 +662,9 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
660 | 662 | |
|
661 | 663 | def __init__(self, *args, **kwargs): |
|
662 | 664 | super(TerminalInteractiveShell, self).__init__(*args, **kwargs) |
|
663 | self.auto_suggest: UnionType[AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None] = None | |
|
665 | self.auto_suggest: UnionType[ | |
|
666 | AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None | |
|
667 | ] = None | |
|
664 | 668 | self._set_autosuggestions(self.autosuggestions_provider) |
|
665 | 669 | self.init_prompt_toolkit_cli() |
|
666 | 670 | self.init_term_title() |
@@ -58,7 +58,6 b' class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):' | |||
|
58 | 58 | self, text: str, skip_lines: float, history: History, previous: bool |
|
59 | 59 | ): |
|
60 | 60 | line_number = -1 |
|
61 | ||
|
62 | 61 | for string in reversed(list(history.get_strings())): |
|
63 | 62 | for line in reversed(string.splitlines()): |
|
64 | 63 | line_number += 1 |
@@ -167,7 +166,7 b' def accept_and_keep_cursor(event: KeyPressEvent):' | |||
|
167 | 166 | |
|
168 | 167 | |
|
169 | 168 | def accept_and_move_cursor_left(event: KeyPressEvent): |
|
170 | """Accept autosuggestion and move cursor left""" | |
|
169 | """Accept autosuggestion and move cursor left in place""" | |
|
171 | 170 | accept_and_keep_cursor(event) |
|
172 | 171 | nc.backward_char(event) |
|
173 | 172 |
@@ -2,8 +2,18 b' import pytest' | |||
|
2 | 2 | from IPython.terminal.shortcuts.auto_suggest import ( |
|
3 | 3 | accept_in_vi_insert_mode, |
|
4 | 4 | accept_token, |
|
5 | accept_character, | |
|
6 | accept_word, | |
|
7 | accept_and_keep_cursor, | |
|
8 | NavigableAutoSuggestFromHistory, | |
|
9 | swap_autosuggestion_up, | |
|
10 | swap_autosuggestion_down, | |
|
5 | 11 | ) |
|
6 | 12 | |
|
13 | from prompt_toolkit.history import InMemoryHistory | |
|
14 | from prompt_toolkit.shortcuts import PromptSession | |
|
15 | from prompt_toolkit.buffer import Buffer | |
|
16 | ||
|
7 | 17 | from unittest.mock import patch, Mock |
|
8 | 18 | |
|
9 | 19 | |
@@ -81,6 +91,59 b' def test_autosuggest_token(text, suggestion, expected):' | |||
|
81 | 91 | assert event.current_buffer.insert_text.call_args[0] == (expected,) |
|
82 | 92 | |
|
83 | 93 | |
|
94 | @pytest.mark.parametrize( | |
|
95 | "text, suggestion, expected", | |
|
96 | [ | |
|
97 | ("", "def out(tag: str, n=50):", "d"), | |
|
98 | ("d", "ef out(tag: str, n=50):", "e"), | |
|
99 | ("de ", "f out(tag: str, n=50):", "f"), | |
|
100 | ("def", " out(tag: str, n=50):", " "), | |
|
101 | ], | |
|
102 | ) | |
|
103 | def test_accept_character(text, suggestion, expected): | |
|
104 | event = make_event(text, len(text), suggestion) | |
|
105 | event.current_buffer.insert_text = Mock() | |
|
106 | accept_character(event) | |
|
107 | assert event.current_buffer.insert_text.called | |
|
108 | assert event.current_buffer.insert_text.call_args[0] == (expected,) | |
|
109 | ||
|
110 | ||
|
111 | @pytest.mark.parametrize( | |
|
112 | "text, suggestion, expected", | |
|
113 | [ | |
|
114 | ("", "def out(tag: str, n=50):", "def "), | |
|
115 | ("d", "ef out(tag: str, n=50):", "ef "), | |
|
116 | ("de", "f out(tag: str, n=50):", "f "), | |
|
117 | ("def", " out(tag: str, n=50):", " "), | |
|
118 | # (this is why we also have accept_token) | |
|
119 | ("def ", "out(tag: str, n=50):", "out(tag: "), | |
|
120 | ], | |
|
121 | ) | |
|
122 | def test_accept_word(text, suggestion, expected): | |
|
123 | event = make_event(text, len(text), suggestion) | |
|
124 | event.current_buffer.insert_text = Mock() | |
|
125 | accept_word(event) | |
|
126 | assert event.current_buffer.insert_text.called | |
|
127 | assert event.current_buffer.insert_text.call_args[0] == (expected,) | |
|
128 | ||
|
129 | ||
|
130 | @pytest.mark.parametrize( | |
|
131 | "text, suggestion, expected, cursor", | |
|
132 | [ | |
|
133 | ("", "def out(tag: str, n=50):", "def out(tag: str, n=50):", 0), | |
|
134 | ("def ", "out(tag: str, n=50):", "out(tag: str, n=50):", 4), | |
|
135 | ], | |
|
136 | ) | |
|
137 | def test_accept_and_keep_cursor(text, suggestion, expected, cursor): | |
|
138 | event = make_event(text, cursor, suggestion) | |
|
139 | buffer = event.current_buffer | |
|
140 | buffer.insert_text = Mock() | |
|
141 | accept_and_keep_cursor(event) | |
|
142 | assert buffer.insert_text.called | |
|
143 | assert buffer.insert_text.call_args[0] == (expected,) | |
|
144 | assert buffer.cursor_position == cursor | |
|
145 | ||
|
146 | ||
|
84 | 147 | def test_autosuggest_token_empty(): |
|
85 | 148 | full = "def out(tag: str, n=50):" |
|
86 | 149 | event = make_event(full, len(full), "") |
@@ -92,3 +155,79 b' def test_autosuggest_token_empty():' | |||
|
92 | 155 | accept_token(event) |
|
93 | 156 | assert not event.current_buffer.insert_text.called |
|
94 | 157 | assert forward_word.called |
|
158 | ||
|
159 | ||
|
160 | async def test_navigable_provider(): | |
|
161 | provider = NavigableAutoSuggestFromHistory() | |
|
162 | history = InMemoryHistory(history_strings=["very_a", "very", "very_b", "very_c"]) | |
|
163 | buffer = Buffer(history=history) | |
|
164 | ||
|
165 | async for _ in history.load(): | |
|
166 | pass | |
|
167 | ||
|
168 | buffer.cursor_position = 5 | |
|
169 | buffer.text = "very" | |
|
170 | ||
|
171 | up = swap_autosuggestion_up(provider) | |
|
172 | down = swap_autosuggestion_down(provider) | |
|
173 | ||
|
174 | event = Mock() | |
|
175 | event.current_buffer = buffer | |
|
176 | ||
|
177 | def get_suggestion(): | |
|
178 | suggestion = provider.get_suggestion(buffer, buffer.document) | |
|
179 | buffer.suggestion = suggestion | |
|
180 | return suggestion | |
|
181 | ||
|
182 | assert get_suggestion().text == "_c" | |
|
183 | ||
|
184 | # should go up | |
|
185 | up(event) | |
|
186 | assert get_suggestion().text == "_b" | |
|
187 | ||
|
188 | # should skip over 'very' which is identical to buffer content | |
|
189 | up(event) | |
|
190 | assert get_suggestion().text == "_a" | |
|
191 | ||
|
192 | # should cycle back to beginning | |
|
193 | up(event) | |
|
194 | assert get_suggestion().text == "_c" | |
|
195 | ||
|
196 | # should cycle back through end boundary | |
|
197 | down(event) | |
|
198 | assert get_suggestion().text == "_a" | |
|
199 | ||
|
200 | down(event) | |
|
201 | assert get_suggestion().text == "_b" | |
|
202 | ||
|
203 | down(event) | |
|
204 | assert get_suggestion().text == "_c" | |
|
205 | ||
|
206 | down(event) | |
|
207 | assert get_suggestion().text == "_a" | |
|
208 | ||
|
209 | ||
|
210 | def test_navigable_provider_connection(): | |
|
211 | provider = NavigableAutoSuggestFromHistory() | |
|
212 | provider.skip_lines = 1 | |
|
213 | ||
|
214 | session_1 = PromptSession() | |
|
215 | provider.connect(session_1) | |
|
216 | ||
|
217 | assert provider.skip_lines == 1 | |
|
218 | session_1.default_buffer.on_text_insert.fire() | |
|
219 | assert provider.skip_lines == 0 | |
|
220 | ||
|
221 | session_2 = PromptSession() | |
|
222 | provider.connect(session_2) | |
|
223 | provider.skip_lines = 2 | |
|
224 | ||
|
225 | assert provider.skip_lines == 2 | |
|
226 | session_2.default_buffer.on_text_insert.fire() | |
|
227 | assert provider.skip_lines == 0 | |
|
228 | ||
|
229 | provider.skip_lines = 3 | |
|
230 | provider.disconnect() | |
|
231 | session_1.default_buffer.on_text_insert.fire() | |
|
232 | session_2.default_buffer.on_text_insert.fire() | |
|
233 | assert provider.skip_lines == 3 |
General Comments 0
You need to be logged in to leave comments.
Login now