Skip to content

Commit d3110e3

Browse files
authored
fix: use a transient map to avoid overlay keymaps issues (#4676)
* fix: Use overriding-terminal-local-map instead of trusting the overlay keymap Despite having higher priority, the inline completion overlay keymap may not be active when other overlays around point also define a keymap. This can be observed with smartparens -- upon inserting a pair, smartparens creates an overlay to track the inserted pair. This overlay has a keymap binging only `C-g`. Nonetheless, if the user triggers inline completions inside a recently inserted pair such as the example below (cursor at `|') value = [ | ] then the inline completion shown at `|' would now have its keymap active. In that case, pressing `C-g' once would call the smartparens function that removes the current overlay, and afterwards the inline completion overlay would be active. This results in weird behaviours -- e.g. pressing `C-g' once does not cancel the suggestion, but twice does; Pressing `C-<return>' results in a "C-<return> is undefined" message, etc. This change updates the inline completion mechanism to use `overriding-terminal-local-map`. This ensures that the inline completion keymap is active when the overlay is shown (see https://www.gnu.org/software/emacs/manual/html_node/elisp/Searching-Keymaps.html). Since the inline completion keymap binds `[t]' to a "hide and execute whatever command was bound before", we end up with the expected behaviour of the overlay keymap. * feat: use unread-command-events when cancelling inline completion When the inline completion handles `[t]`, we want it to tear down the completion UI and act as if nothing had happened. That works fine for single key events (escape, a-z (self-insert-command), etc.). Trying to use multi-key commands, on the other hand fails: If `C-x w q` is bound (e.g. `quit-restore-window`), starting the key combination when inline completion is active would result in cancel-with-input function being called only with `C-x` as last-key. If we lookup C-x , it's not bound to a command, so we do not execute anything. The user then proceeds to type `w q` and ends up inserting the text "wq" instead of quitting the window. This fixes that by using the unread-command-events -- that basically leaves some input to be handled by the next iteration of the event loop. As a result, we can now successfully call complex key combinations when the inline completion is active.
1 parent c4ec868 commit d3110e3

File tree

1 file changed

+41
-38
lines changed

1 file changed

+41
-38
lines changed

lsp-inline-completion.el

+41-38
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ InlineCompletionItem objects"
7777
;; useful -- recenter without loosing the completion
7878
(define-key map (kbd "C-l") #'recenter-top-bottom)
7979
;; ignore
80-
(define-key map [down-mouse-1] #'ignore)
80+
(define-key map [down-mouse-1] #'ignore)
8181
(define-key map [up-mouse-1] #'ignore)
8282
(define-key map [mouse-movement] #'ignore)
8383
;; Any event outside of the map, cancel and use it
@@ -109,10 +109,11 @@ InlineCompletionItem objects"
109109

110110
(defcustom lsp-inline-completion-accepted-functions nil
111111
"Functions executed after accepting a code suggestion.
112-
The functions receive the text range that was updated by the completion."
112+
The functions receive the inserted text and the range that was updated by the completion."
113113
:type 'hook
114114
:group 'lsp-mode)
115115

116+
116117
(defcustom lsp-inline-completion-cancelled-hook nil
117118
"Hooks executed after cancelling the completion UI."
118119
:type 'hook
@@ -138,28 +139,16 @@ The functions receive the text range that was updated by the completion."
138139
(integer :tag "Secondary")))
139140
:group 'lsp-mode)
140141

141-
(defsubst lsp-inline-completion--overlay-visible ()
142-
"Return whether the `overlay' is avaiable."
143-
(and (overlayp lsp-inline-completion--overlay)
144-
(overlay-buffer lsp-inline-completion--overlay)))
142+
(defsubst lsp-inline-completion--active-p ()
143+
"Returns whether we are in an active completion"
144+
(overlayp lsp-inline-completion--overlay))
145145

146146
(defun lsp-inline-completion--clear-overlay ()
147147
"Hide the suggestion overlay."
148-
(when (lsp-inline-completion--overlay-visible)
149-
(delete-overlay lsp-inline-completion--overlay))
150-
(setq lsp-inline-completion--overlay nil))
151-
152-
153-
(defun lsp-inline-completion--get-overlay (beg end)
154-
"Build the suggestions overlay."
155148
(when (overlayp lsp-inline-completion--overlay)
156-
(lsp-inline-completion--clear-overlay))
157-
158-
(setq lsp-inline-completion--overlay (make-overlay beg end nil nil t))
159-
(overlay-put lsp-inline-completion--overlay 'keymap lsp-inline-completion-active-map)
160-
(overlay-put lsp-inline-completion--overlay 'priority lsp-inline-completion-overlay-priority)
161-
162-
lsp-inline-completion--overlay)
149+
(delete-overlay lsp-inline-completion--overlay))
150+
(setq lsp-inline-completion--overlay nil)
151+
(internal-pop-keymap lsp-inline-completion-active-map 'overriding-terminal-local-map))
163152

164153

165154
(defun lsp-inline-completion--show-keys ()
@@ -189,6 +178,19 @@ The functions receive the text range that was updated by the completion."
189178
keys)
190179
"/"))))))))
191180

181+
182+
(defun lsp-inline-completion--get-overlay (beg end)
183+
"Build the suggestions overlay."
184+
(lsp-inline-completion--clear-overlay)
185+
186+
(setq lsp-inline-completion--overlay (make-overlay beg end nil nil t))
187+
(overlay-put lsp-inline-completion--overlay 'priority lsp-inline-completion-overlay-priority)
188+
(internal-push-keymap lsp-inline-completion-active-map 'overriding-terminal-local-map)
189+
(lsp-inline-completion--show-keys)
190+
191+
lsp-inline-completion--overlay)
192+
193+
192194
(defun lsp-inline-completion-show-overlay ()
193195
"Makes the suggestion overlay visible."
194196
(unless (and lsp-inline-completion--items
@@ -239,7 +241,6 @@ The functions receive the text range that was updated by the completion."
239241

240242
(goto-char target-position)
241243

242-
(lsp-inline-completion--show-keys)
243244
(run-hooks 'lsp-inline-completion-shown-hook)))
244245

245246
(defun lsp-inline-completion--insert-sugestion (text kind start end command?)
@@ -277,10 +278,11 @@ The functions receive the text range that was updated by the completion."
277278
(defun lsp-inline-completion-accept ()
278279
"Accepts the current suggestion."
279280
(interactive)
280-
(unless (lsp-inline-completion--overlay-visible)
281-
(error "Not showing suggestions"))
281+
(unless (lsp-inline-completion--active-p)
282+
(error "Not showing suggestions"))
282283

283284
(lsp-inline-completion--clear-overlay)
285+
284286
(-let* ((suggestion (elt lsp-inline-completion--items lsp-inline-completion--current))
285287
((&InlineCompletionItem? :insert-text :range? :command?) suggestion)
286288
((kind . text) (cond
@@ -310,32 +312,29 @@ The functions receive the text range that was updated by the completion."
310312
(defun lsp-inline-completion-cancel ()
311313
"Close the suggestion overlay."
312314
(interactive)
313-
(when (lsp-inline-completion--overlay-visible)
314-
315+
(message nil) ;; clear echo
316+
(let ((was-active (lsp-inline-completion--active-p)))
315317
(lsp-inline-completion--clear-overlay)
316318

317-
(when lsp-inline-completion--start-point
318-
(goto-char lsp-inline-completion--start-point))
319+
(when was-active
320+
(goto-char lsp-inline-completion--start-point)
321+
(run-hooks 'lsp-inline-completion-cancelled-hook))))
319322

320-
(run-hooks 'lsp-inline-completion-cancelled-hook)))
321323

322-
(defun lsp-inline-completion-cancel-with-input (event &optional arg)
324+
(defun lsp-inline-completion-cancel-with-input (event)
323325
"Cancel the inline completion and executes whatever event was received."
324-
(interactive (list last-input-event current-prefix-arg))
326+
(interactive (list last-input-event))
325327

326328
(lsp-inline-completion-cancel)
327329

328-
(let ((command (lookup-key (current-active-maps) (vector event)))
329-
(current-prefix-arg arg))
330-
331-
(when (commandp command)
332-
(call-interactively command))))
330+
(setq unread-command-events (nconc unread-command-events (list event))))
333331

334332
(defun lsp-inline-completion-next ()
335333
"Display the next inline completion."
336334
(interactive)
337-
(unless (lsp-inline-completion--overlay-visible)
335+
(unless (lsp-inline-completion--active-p)
338336
(error "Not showing suggestions"))
337+
339338
(setq lsp-inline-completion--current
340339
(mod (1+ lsp-inline-completion--current)
341340
(length lsp-inline-completion--items)))
@@ -345,8 +344,10 @@ The functions receive the text range that was updated by the completion."
345344
(defun lsp-inline-completion-prev ()
346345
"Display the previous inline completion."
347346
(interactive)
348-
(unless (lsp-inline-completion--overlay-visible)
347+
348+
(unless (lsp-inline-completion--active-p)
349349
(error "Not showing suggestions"))
350+
350351
(setq lsp-inline-completion--current
351352
(mod (1- lsp-inline-completion--current)
352353
(length lsp-inline-completion--items)))
@@ -359,7 +360,9 @@ The functions receive the text range that was updated by the completion."
359360
(interactive)
360361

361362
(unless implicit
362-
(lsp--spinner-start) )
363+
(lsp--spinner-start))
364+
365+
(run-hooks 'lsp-before-inline-completion-hook)
363366

364367
(condition-case err
365368
(unwind-protect

0 commit comments

Comments
 (0)