##// END OF EJS Templates
Fix autosuggestions in multi-line mode, vi command mode delay (#13991)...
Matthias Bussonnier -
r28198:4e7b9408 merge
parent child Browse files
Show More
@@ -181,30 +181,45 b' AUTO_MATCH_BINDINGS = ['
181 ]
181 ]
182
182
183 AUTO_SUGGEST_BINDINGS = [
183 AUTO_SUGGEST_BINDINGS = [
184 # there are two reasons for re-defining bindings defined upstream:
185 # 1) prompt-toolkit does not execute autosuggestion bindings in vi mode,
186 # 2) prompt-toolkit checks if we are at the end of text, not end of line
187 # hence it does not work in multi-line mode of navigable provider
184 Binding(
188 Binding(
185 auto_suggest.accept_in_vi_insert_mode,
189 auto_suggest.accept_or_jump_to_end,
186 ["end"],
190 ["end"],
187 "default_buffer_focused & (ebivim | ~vi_insert_mode)",
191 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
188 ),
192 ),
189 Binding(
193 Binding(
190 auto_suggest.accept_in_vi_insert_mode,
194 auto_suggest.accept_or_jump_to_end,
191 ["c-e"],
195 ["c-e"],
192 "vi_insert_mode & default_buffer_focused & ebivim",
196 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
197 ),
198 Binding(
199 auto_suggest.accept,
200 ["c-f"],
201 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
202 ),
203 Binding(
204 auto_suggest.accept,
205 ["right"],
206 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
193 ),
207 ),
194 Binding(auto_suggest.accept, ["c-f"], "vi_insert_mode & default_buffer_focused"),
195 Binding(
208 Binding(
196 auto_suggest.accept_word,
209 auto_suggest.accept_word,
197 ["escape", "f"],
210 ["escape", "f"],
198 "vi_insert_mode & default_buffer_focused & ebivim",
211 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
199 ),
212 ),
200 Binding(
213 Binding(
201 auto_suggest.accept_token,
214 auto_suggest.accept_token,
202 ["c-right"],
215 ["c-right"],
203 "has_suggestion & default_buffer_focused",
216 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
204 ),
217 ),
205 Binding(
218 Binding(
206 auto_suggest.discard,
219 auto_suggest.discard,
207 ["escape"],
220 ["escape"],
221 # note this one is using `emacs_insert_mode`, not `emacs_like_insert_mode`
222 # as in `vi_insert_mode` we do not want `escape` to be shadowed (ever).
208 "has_suggestion & default_buffer_focused & emacs_insert_mode",
223 "has_suggestion & default_buffer_focused & emacs_insert_mode",
209 ),
224 ),
210 Binding(
225 Binding(
@@ -241,22 +256,23 b' AUTO_SUGGEST_BINDINGS = ['
241 Binding(
256 Binding(
242 auto_suggest.accept_character,
257 auto_suggest.accept_character,
243 ["escape", "right"],
258 ["escape", "right"],
244 "has_suggestion & default_buffer_focused",
259 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
245 ),
260 ),
246 Binding(
261 Binding(
247 auto_suggest.accept_and_move_cursor_left,
262 auto_suggest.accept_and_move_cursor_left,
248 ["c-left"],
263 ["c-left"],
249 "has_suggestion & default_buffer_focused",
264 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
250 ),
265 ),
251 Binding(
266 Binding(
252 auto_suggest.accept_and_keep_cursor,
267 auto_suggest.accept_and_keep_cursor,
253 ["c-down"],
268 ["c-down"],
254 "has_suggestion & default_buffer_focused",
269 "has_suggestion & default_buffer_focused & emacs_like_insert_mode",
255 ),
270 ),
256 Binding(
271 Binding(
257 auto_suggest.backspace_and_resume_hint,
272 auto_suggest.backspace_and_resume_hint,
258 ["backspace"],
273 ["backspace"],
259 "has_suggestion & default_buffer_focused",
274 # no `has_suggestion` here to allow resuming if no suggestion
275 "default_buffer_focused & emacs_like_insert_mode",
260 ),
276 ),
261 ]
277 ]
262
278
@@ -2,6 +2,7 b' import re'
2 import tokenize
2 import tokenize
3 from io import StringIO
3 from io import StringIO
4 from typing import Callable, List, Optional, Union, Generator, Tuple
4 from typing import Callable, List, Optional, Union, Generator, Tuple
5 import warnings
5
6
6 from prompt_toolkit.buffer import Buffer
7 from prompt_toolkit.buffer import Buffer
7 from prompt_toolkit.key_binding import KeyPressEvent
8 from prompt_toolkit.key_binding import KeyPressEvent
@@ -178,9 +179,8 b' class NavigableAutoSuggestFromHistory(AutoSuggestFromHistory):'
178 break
179 break
179
180
180
181
181 # Needed for to accept autosuggestions in vi insert mode
182 def accept_or_jump_to_end(event: KeyPressEvent):
182 def accept_in_vi_insert_mode(event: KeyPressEvent):
183 """Apply autosuggestion or jump to end of line."""
183 """Apply autosuggestion if at end of line."""
184 buffer = event.current_buffer
184 buffer = event.current_buffer
185 d = buffer.document
185 d = buffer.document
186 after_cursor = d.text[d.cursor_position :]
186 after_cursor = d.text[d.cursor_position :]
@@ -193,6 +193,15 b' def accept_in_vi_insert_mode(event: KeyPressEvent):'
193 nc.end_of_line(event)
193 nc.end_of_line(event)
194
194
195
195
196 def _deprected_accept_in_vi_insert_mode(event: KeyPressEvent):
197 """Accept autosuggestion or jump to end of line.
198
199 .. deprecated:: 8.12
200 Use `accept_or_jump_to_end` instead.
201 """
202 return accept_or_jump_to_end(event)
203
204
196 def accept(event: KeyPressEvent):
205 def accept(event: KeyPressEvent):
197 """Accept autosuggestion"""
206 """Accept autosuggestion"""
198 buffer = event.current_buffer
207 buffer = event.current_buffer
@@ -373,3 +382,16 b' def swap_autosuggestion_down(event: KeyPressEvent):'
373 provider=provider,
382 provider=provider,
374 direction_method=provider.down,
383 direction_method=provider.down,
375 )
384 )
385
386
387 def __getattr__(key):
388 if key == "accept_in_vi_insert_mode":
389 warnings.warn(
390 "`accept_in_vi_insert_mode` is deprecated since IPython 8.12 and "
391 "renamed to `accept_or_jump_to_end`. Please update your configuration "
392 "accordingly",
393 DeprecationWarning,
394 stacklevel=2,
395 )
396 return _deprected_accept_in_vi_insert_mode
397 raise AttributeError
@@ -181,10 +181,33 b' KEYBINDING_FILTERS = {'
181 "vi_mode": vi_mode,
181 "vi_mode": vi_mode,
182 "vi_insert_mode": vi_insert_mode,
182 "vi_insert_mode": vi_insert_mode,
183 "emacs_insert_mode": emacs_insert_mode,
183 "emacs_insert_mode": emacs_insert_mode,
184 # https://github.com/ipython/ipython/pull/12603 argued for inclusion of
185 # emacs key bindings with a configurable `emacs_bindings_in_vi_insert_mode`
186 # toggle; when the toggle is on user can access keybindigns like `ctrl + e`
187 # in vi insert mode. Because some of the emacs bindings involve `escape`
188 # followed by another key, e.g. `escape` followed by `f`, prompt-toolkit
189 # needs to wait to see if there will be another character typed in before
190 # executing pure `escape` keybinding; in vi insert mode `escape` switches to
191 # command mode which is common and performance critical action for vi users.
192 # To avoid the delay users employ a workaround:
193 # https://github.com/ipython/ipython/issues/13443#issuecomment-1032753703
194 # which involves switching `emacs_bindings_in_vi_insert_mode` off.
195 #
196 # For the workaround to work:
197 # 1) end users need to toggle `emacs_bindings_in_vi_insert_mode` off
198 # 2) all keybindings which would involve `escape` need to respect that
199 # toggle by including either:
200 # - `vi_insert_mode & ebivim` for actions which have emacs keybindings
201 # predefined upstream in prompt-toolkit, or
202 # - `emacs_like_insert_mode` for actions which do not have existing
203 # emacs keybindings predefined upstream (or need overriding of the
204 # upstream bindings to modify behaviour), defined below.
205 "emacs_like_insert_mode": (vi_insert_mode & ebivim) | emacs_insert_mode,
184 "has_completions": has_completions,
206 "has_completions": has_completions,
185 "insert_mode": vi_insert_mode | emacs_insert_mode,
207 "insert_mode": vi_insert_mode | emacs_insert_mode,
186 "default_buffer_focused": default_buffer_focused,
208 "default_buffer_focused": default_buffer_focused,
187 "search_buffer_focused": has_focus(SEARCH_BUFFER),
209 "search_buffer_focused": has_focus(SEARCH_BUFFER),
210 # `ebivim` stands for emacs bindings in vi insert mode
188 "ebivim": ebivim,
211 "ebivim": ebivim,
189 "supports_suspend": supports_suspend,
212 "supports_suspend": supports_suspend,
190 "is_windows_os": is_windows_os,
213 "is_windows_os": is_windows_os,
@@ -1,7 +1,7 b''
1 import pytest
1 import pytest
2 from IPython.terminal.shortcuts.auto_suggest import (
2 from IPython.terminal.shortcuts.auto_suggest import (
3 accept,
3 accept,
4 accept_in_vi_insert_mode,
4 accept_or_jump_to_end,
5 accept_token,
5 accept_token,
6 accept_character,
6 accept_character,
7 accept_word,
7 accept_word,
@@ -22,6 +22,13 b' from prompt_toolkit.auto_suggest import AutoSuggestFromHistory'
22 from unittest.mock import patch, Mock
22 from unittest.mock import patch, Mock
23
23
24
24
25 def test_deprected():
26 import IPython.terminal.shortcuts.auto_suggest as iptsa
27
28 with pytest.warns(DeprecationWarning, match=r"8\.12.+accept_or_jump_to_end"):
29 iptsa.accept_in_vi_insert_mode
30
31
25 def make_event(text, cursor, suggestion):
32 def make_event(text, cursor, suggestion):
26 event = Mock()
33 event = Mock()
27 event.current_buffer = Mock()
34 event.current_buffer = Mock()
@@ -80,7 +87,7 b' def test_autosuggest_at_EOL(text, cursor, suggestion, called):'
80
87
81 event = make_event(text, cursor, suggestion)
88 event = make_event(text, cursor, suggestion)
82 event.current_buffer.insert_text = Mock()
89 event.current_buffer.insert_text = Mock()
83 accept_in_vi_insert_mode(event)
90 accept_or_jump_to_end(event)
84 if called:
91 if called:
85 event.current_buffer.insert_text.assert_called()
92 event.current_buffer.insert_text.assert_called()
86 else:
93 else:
General Comments 0
You need to be logged in to leave comments. Login now