From 0bb28acca98e7c348178c864877947c47884887e Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Tue, 22 Jul 2025 23:53:19 -0300 Subject: [PATCH 1/4] fix: cancel inline completion UI on non mapped commands On some situations, keybindings such as M-x are not caught by the default handler (lsp-inline-completion-cancel-with-input, via overriding-terminal-local-map). This results in the tooltip not being hidden when it should. The consequence is that the user has to explicitly call lsp-inline-completion-cancel to hide the completion overlay and continue working in the buffer This change fixes that by placing a symbol property on the commands that should not cancel the UI (accept, next, prev, recenter-top-botton and ignore). On pre-command hook, we check for that property and cancel the completion if needed. --- lsp-inline-completion.el | 58 +++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 553610cc6b..e9fbe71d3d 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -62,28 +62,53 @@ InlineCompletionItem objects" ;;;;;; Default UI -- overlay +(defun lsp-inline-completion--define-key (keymap key def &optional remove keep) + "Defines key on map. With non-nil keep, will mark it as a command that +should not cancel the inline completion UI" + (define-key keymap key def remove) + (when keep + (pcase def + ;; Command is OK + ((pred symbolp) + (put def 'lsp-inline-completion-keep t)) + ;; ("str" . command) is OK + (`(,_str . ,defn) + (if (symbolp defn) + (put defn 'lsp-inline-completion-keep t) + (error "Unexpected type (str . defn) key definition %S" (type-of defn)))) + ;; Keymaps and macros are unsupported + (t (error "Unsupported definition with keep -- %S" def))))) + (defvar lsp-inline-completion-active-map (let ((map (make-sparse-keymap))) ;; accept - (define-key map (kbd "C-") #'lsp-inline-completion-accept) - (define-key map [mouse-1] #'lsp-inline-completion-accept-on-click) + (lsp-inline-completion--define-key map (kbd "C-") #'lsp-inline-completion-accept nil 'keep) + (lsp-inline-completion--define-key map [mouse-1] #'lsp-inline-completion-accept-on-click nil 'keep) ;; navigate - (define-key map (kbd "C-n") #'lsp-inline-completion-next) - (define-key map (kbd "C-p") #'lsp-inline-completion-prev) + (lsp-inline-completion--define-key map (kbd "C-n") #'lsp-inline-completion-next nil 'keep) + (lsp-inline-completion--define-key map (kbd "C-p") #'lsp-inline-completion-prev nil 'keep) ;; cancel - (define-key map (kbd "C-g") #'lsp-inline-completion-cancel) - (define-key map (kbd "") #'lsp-inline-completion-cancel) - (define-key map (kbd "C-c C-k") #'lsp-inline-completion-cancel) + (lsp-inline-completion--define-key map (kbd "C-g") #'lsp-inline-completion-cancel) + (lsp-inline-completion--define-key map (kbd "") #'lsp-inline-completion-cancel) + (lsp-inline-completion--define-key map (kbd "C-c C-k") #'lsp-inline-completion-cancel) ;; useful -- recenter without loosing the completion - (define-key map (kbd "C-l") #'recenter-top-bottom) + (lsp-inline-completion--define-key map (kbd "C-l") #'recenter-top-bottom nil 'keep) ;; ignore - (define-key map [down-mouse-1] #'ignore) - (define-key map [up-mouse-1] #'ignore) - (define-key map [mouse-movement] #'ignore) + (lsp-inline-completion--define-key map [down-mouse-1] #'ignore nil 'keep) + (lsp-inline-completion--define-key map [up-mouse-1] #'ignore nil 'keep) + (lsp-inline-completion--define-key map [mouse-movement] #'ignore nil 'keep) ;; Any event outside of the map, cancel and use it (define-key map [t] #'lsp-inline-completion-cancel-with-input) map) - "Keymap active when showing inline code suggestions.") + "Keymap active when showing inline code suggestions. + +When adding new bindings to this map, prefer using +lsp-inline-completion--define-key. Use a non-nil keep unless the command +should cancel the completion UI") + +(defsubst lsp-inline-completion--keep (cmd) + "Returns t if the command should not cancel the current completion" + (and (symbolp cmd) (get cmd 'lsp-inline-completion-keep))) (defface lsp-inline-completion-overlay-face '((t :inherit shadow)) @@ -383,6 +408,12 @@ The functions receive the inserted text and the range that was updated by the co (lsp--spinner-stop))) (t (lsp--error "Could not fetch completions: %s" err)))) +(defun lsp-inline-completion--pre-command-hook () + "Cancels the inline completio before a non-keep command" + (when (and (lsp-inline-completion--active-p) + (not (lsp-inline-completion--keep this-command))) + (lsp-inline-completion-cancel))) + ;; Inline Completion Mode ;;;###autoload @@ -413,13 +444,16 @@ lsp-inline-completion-mode is active." :lighter nil (cond ((and lsp-inline-completion-mode lsp--buffer-workspaces) + (add-hook 'pre-command-hook #'lsp-inline-completion--pre-command-hook nil t) (add-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change nil t)) + (t (when lsp-inline-completion--idle-timer (cancel-timer lsp-inline-completion--idle-timer)) (lsp-inline-completion-cancel) + (remove-hook 'pre-command-hook #'lsp-inline-completion--pre-command-hook t) (remove-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) (defun lsp-inline-completion--maybe-display (original-buffer original-point) From 4029ad0ca07cfd8d864ea50bb85474c1231ab2da Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 23 Jul 2025 00:09:56 -0300 Subject: [PATCH 2/4] fix: use emacs-28-compatible define-key --- lsp-inline-completion.el | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index e9fbe71d3d..2a4aabec7e 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -62,10 +62,10 @@ InlineCompletionItem objects" ;;;;;; Default UI -- overlay -(defun lsp-inline-completion--define-key (keymap key def &optional remove keep) +(defun lsp-inline-completion--define-key (keymap key def &optional keep) "Defines key on map. With non-nil keep, will mark it as a command that should not cancel the inline completion UI" - (define-key keymap key def remove) + (define-key keymap key def) (when keep (pcase def ;; Command is OK @@ -82,21 +82,21 @@ should not cancel the inline completion UI" (defvar lsp-inline-completion-active-map (let ((map (make-sparse-keymap))) ;; accept - (lsp-inline-completion--define-key map (kbd "C-") #'lsp-inline-completion-accept nil 'keep) - (lsp-inline-completion--define-key map [mouse-1] #'lsp-inline-completion-accept-on-click nil 'keep) + (lsp-inline-completion--define-key map (kbd "C-") #'lsp-inline-completion-accept 'keep) + (lsp-inline-completion--define-key map [mouse-1] #'lsp-inline-completion-accept-on-click 'keep) ;; navigate - (lsp-inline-completion--define-key map (kbd "C-n") #'lsp-inline-completion-next nil 'keep) - (lsp-inline-completion--define-key map (kbd "C-p") #'lsp-inline-completion-prev nil 'keep) + (lsp-inline-completion--define-key map (kbd "C-n") #'lsp-inline-completion-next 'keep) + (lsp-inline-completion--define-key map (kbd "C-p") #'lsp-inline-completion-prev 'keep) ;; cancel (lsp-inline-completion--define-key map (kbd "C-g") #'lsp-inline-completion-cancel) (lsp-inline-completion--define-key map (kbd "") #'lsp-inline-completion-cancel) (lsp-inline-completion--define-key map (kbd "C-c C-k") #'lsp-inline-completion-cancel) ;; useful -- recenter without loosing the completion - (lsp-inline-completion--define-key map (kbd "C-l") #'recenter-top-bottom nil 'keep) + (lsp-inline-completion--define-key map (kbd "C-l") #'recenter-top-bottom 'keep) ;; ignore - (lsp-inline-completion--define-key map [down-mouse-1] #'ignore nil 'keep) - (lsp-inline-completion--define-key map [up-mouse-1] #'ignore nil 'keep) - (lsp-inline-completion--define-key map [mouse-movement] #'ignore nil 'keep) + (lsp-inline-completion--define-key map [down-mouse-1] #'ignore 'keep) + (lsp-inline-completion--define-key map [up-mouse-1] #'ignore 'keep) + (lsp-inline-completion--define-key map [mouse-movement] #'ignore 'keep) ;; Any event outside of the map, cancel and use it (define-key map [t] #'lsp-inline-completion-cancel-with-input) map) From 5dc99563eba9439e40a17eb40e6598940f4990cc Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 23 Jul 2025 00:36:10 -0300 Subject: [PATCH 3/4] chore: cancel-with-input is no longer needed With the lsp-inline-completion--keep method, we no longer need the cancel with input method -- simply let the binding on some other keymap be found and cancel the completion in the pre-command-hook --- lsp-inline-completion.el | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 2a4aabec7e..f6499fff3b 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -97,8 +97,6 @@ should not cancel the inline completion UI" (lsp-inline-completion--define-key map [down-mouse-1] #'ignore 'keep) (lsp-inline-completion--define-key map [up-mouse-1] #'ignore 'keep) (lsp-inline-completion--define-key map [mouse-movement] #'ignore 'keep) - ;; Any event outside of the map, cancel and use it - (define-key map [t] #'lsp-inline-completion-cancel-with-input) map) "Keymap active when showing inline code suggestions. @@ -345,15 +343,6 @@ The functions receive the inserted text and the range that was updated by the co (goto-char lsp-inline-completion--start-point) (run-hooks 'lsp-inline-completion-cancelled-hook)))) - -(defun lsp-inline-completion-cancel-with-input (event) - "Cancel the inline completion and executes whatever event was received." - (interactive (list last-input-event)) - - (lsp-inline-completion-cancel) - - (setq unread-command-events (nconc unread-command-events (list event)))) - (defun lsp-inline-completion-next () "Display the next inline completion." (interactive) From da26904d31847cdef4867223bb50d3ec1e4eaad2 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 23 Jul 2025 00:38:21 -0300 Subject: [PATCH 4/4] fix: set up the pre-command-hook when starting the completion Users may use the completion without activating the minor mode (which was originally intended only for idle-completion). --- lsp-inline-completion.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index f6499fff3b..e56b02f8a3 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -171,7 +171,8 @@ The functions receive the inserted text and the range that was updated by the co (when (overlayp lsp-inline-completion--overlay) (delete-overlay lsp-inline-completion--overlay)) (setq lsp-inline-completion--overlay nil) - (internal-pop-keymap lsp-inline-completion-active-map 'overriding-terminal-local-map)) + (internal-pop-keymap lsp-inline-completion-active-map 'overriding-terminal-local-map) + (remove-hook 'pre-command-hook #'lsp-inline-completion--pre-command-hook t)) (defun lsp-inline-completion--show-keys () @@ -206,6 +207,7 @@ The functions receive the inserted text and the range that was updated by the co "Build the suggestions overlay." (lsp-inline-completion--clear-overlay) + (add-hook 'pre-command-hook #'lsp-inline-completion--pre-command-hook nil t) (setq lsp-inline-completion--overlay (make-overlay beg end nil nil t)) (overlay-put lsp-inline-completion--overlay 'priority lsp-inline-completion-overlay-priority) (internal-push-keymap lsp-inline-completion-active-map 'overriding-terminal-local-map) @@ -433,7 +435,6 @@ lsp-inline-completion-mode is active." :lighter nil (cond ((and lsp-inline-completion-mode lsp--buffer-workspaces) - (add-hook 'pre-command-hook #'lsp-inline-completion--pre-command-hook nil t) (add-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change nil t)) (t @@ -442,7 +443,6 @@ lsp-inline-completion-mode is active." (lsp-inline-completion-cancel) - (remove-hook 'pre-command-hook #'lsp-inline-completion--pre-command-hook t) (remove-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) (defun lsp-inline-completion--maybe-display (original-buffer original-point)