From 361255a8780607c026a464280f927bb72b22de25 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 18 Nov 2024 19:01:02 -0300 Subject: [PATCH 01/46] fix: avoid calling line-number-at-pos with a value past the end of the buffer When a overlay is displayed at the end of the buffer, (window-end) may return a value that is past the end of the buffer. Calling line-number-at-pos with a value outside of the buffer bounds raises an error. --- lsp-mode.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lsp-mode.el b/lsp-mode.el index 5f81502ec9b..f93568b6f50 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -6287,7 +6287,10 @@ A reference is highlighted only if it is visible in a window." (let* ((wins-visible-pos (-map (lambda (win) (cons (1- (line-number-at-pos (window-start win) t)) - (1+ (line-number-at-pos (window-end win) t)))) + (1+ (line-number-at-pos (min (window-end win) + (with-current-buffer (window-buffer win) + (buffer-end +1))) + t)))) (get-buffer-window-list nil nil 'visible)))) (setq lsp--have-document-highlights t) (-map From 5caf44c6ceee826573f804d726d57fa9e71204af Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 18 Nov 2024 19:54:21 -0300 Subject: [PATCH 02/46] feat: Inline Completion support Only the parser --- lsp-inline-completion.el | 69 ++++++++++++++++++++++++++++++++++++++++ lsp-mode.el | 1 + lsp-protocol.el | 4 +++ 3 files changed, 74 insertions(+) create mode 100644 lsp-inline-completion.el diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el new file mode 100644 index 00000000000..556fe0eed26 --- /dev/null +++ b/lsp-inline-completion.el @@ -0,0 +1,69 @@ +;;; lsp-inline-completion.el --- LSP mode -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2024 emacs-lsp maintainers + +;; Author: Rodrigo Kassick +;; Keywords: languages +;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (ht "2.3") (spinner "1.7.3")) +;; Version: 9.0.1 + +;; URL: https://github.com/emacs-lsp/lsp-mode +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Inline Completions support +;; Specification here https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlineCompletion + +;;; Code: + +(require 'lsp-mode) +(require 'dash) + +(defun lsp-inline-completion--trigger-kind (implicit) + (plist-put (lsp--text-document-position-params) + :context (ht ("triggerKind" + (if implicit + lsp/inline-completion-trigger-automatic + lsp/inline-completion-trigger-invoked))))) + +;;;###autoload +(defun lsp-inline-completion-get-items (&optional implicit) + "Calls textDocument_inlineCompletion and returns a list of InlineCompletionItem's" + + (lsp--spinner-start) + (unwind-protect + (-some--> + (lsp-request-while-no-input "textDocument/inlineCompletion" + (lsp-inline-completion--trigger-kind implicit)) + ;; Kludge to workaround multiple backends responding -- it may + ;; come as a list (multiple servers) or as a single ht (single + ;; server). Promote it to list and move on + (if (ht-p it) (list it) it) + + ;; Response may or may not come inside an :items. Parse each + ;; response to ensure compatibility. + (map 'list (lambda (elt) + (if (lsp-inline-completion-list? elt) + (lsp:inline-completion-list-items elt) + elt)) + it) + + ;; Join everything into a single list + (apply 'seq-concatenate `(list ,@it))) + + ;; Clean up + (lsp--spinner-stop))) + +(provide 'lsp-inline-completion) diff --git a/lsp-mode.el b/lsp-mode.el index f93568b6f50..e39bc976940 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -1010,6 +1010,7 @@ directory") (with-lsp-workspace wk (lsp:completion-options-resolve-provider? (lsp--capability-for-method "textDocument/completion"))))) + ("textDocument/inlineCompletion" :capability :inlineCompletionProvider) ("textDocument/declaration" :capability :declarationProvider) ("textDocument/definition" :capability :definitionProvider) ("textDocument/documentColor" :capability :colorProvider) diff --git a/lsp-protocol.el b/lsp-protocol.el index f1d8fd90967..15de5c198c2 100644 --- a/lsp-protocol.el +++ b/lsp-protocol.el @@ -500,6 +500,8 @@ See `-let' for a description of the destructuring mechanism." (defconst lsp/completion-trigger-kind-invoked 1) (defconst lsp/completion-trigger-kind-trigger-character 2) (defconst lsp/completion-trigger-kind-trigger-for-incomplete-completions 3) +(defconst lsp/inline-completion-trigger-invoked 1 "Explicit invocation as per https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#inlineCompletionTriggerKind") +(defconst lsp/inline-completion-trigger-automatic 2 "Automatic invocation as per https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#inlineCompletionTriggerKind") (defvar lsp/diagnostic-severity-lookup [nil Error Warning Information Hint Max]) (defconst lsp/diagnostic-severity-error 1) @@ -624,6 +626,8 @@ See `-let' for a description of the destructuring mechanism." (CompletionItemKindCapabilities nil (:valueSet)) (CompletionItemTagSupportCapabilities (:valueSet) nil) (CompletionOptions nil (:resolveProvider :triggerCharacters :allCommitCharacters)) + (InlineCompletionItem (:insertText) (:filterText :range :command)) + (InlineCompletionList (:items) nil) (ConfigurationItem nil (:scopeUri :section)) (CreateFileOptions nil (:ignoreIfExists :overwrite)) (DeclarationCapabilities nil (:dynamicRegistration :linkSupport)) From 7227d67baf1dbcdab11307a2653ddae7b55d905d Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Tue, 19 Nov 2024 11:51:45 -0300 Subject: [PATCH 03/46] feat: Inline Completion default UI --- lsp-inline-completion.el | 235 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 556fe0eed26..ea84578655b 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -66,4 +66,239 @@ ;; Clean up (lsp--spinner-stop))) + +;;;;;; Default UI -- overlay + +(defvar lsp-inline-completion-active-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "") #'lsp-inline-completion-next) + (define-key map (kbd "C-n") #'lsp-inline-completion-next) + (define-key map (kbd "M-n") #'lsp-inline-completion-next) + (define-key map (kbd "C-p") #'lsp-inline-completion-prev) + (define-key map (kbd "M-p") #'lsp-inline-completion-prev) + (define-key map (kbd "") #'lsp-inline-completion-accept) + (define-key map [down-mouse-1] #'ignore) + (define-key map [down-mouse-3] #'ignore) + (define-key map [up-mouse-1] #'ignore) + (define-key map [up-mouse-3] #'ignore) + (define-key map [mouse-1] #'lsp-inline-completion-accept) + (define-key map [mouse-3] #'lsp-inline-completion-accept) + (define-key map (kbd "C-l") #'recenter-top-bottom) + (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) + (define-key map [mouse-movement] #'ignore) + (define-key map [t] #'lsp-inline-completion-cancel) + map) + "Keymap active when showing inline code suggestions") + +;;;###autoload +(defface lsp-inline-completion-overlay-face + '((t :inherit shadow)) + "Face for the inline code suggestions overlay.") + +;; Local Buffer State + +(defvar-local lsp-inline-completion--items nil "The completions provided by the server") +(defvar-local lsp-inline-completion--current nil "The current suggestion to be displayed") +(defvar-local lsp-inline-completion--overlay nil "The overlay displaying code suggestions") +(defvar-local lsp-inline-completion--start-point nil "The point where the completion started") + +(defcustom lsp-before-inline-completion-hook nil + "Hooks run before starting code suggestions" + :type 'hook) + +(defcustom lsp-after-inline-completion-hook nil + "Hooks executed after asking for code suggestions." + :type 'hook) + +;; TODO: Add parameters to the hooks! +(defcustom lsp-inline-completion-accepted-hook nil + "Hooks executed after accepting a code suggestion." + :type 'hook) + +(defcustom lsp-inline-completion-shown-hook nil + "Hooks executed after showing a suggestion." + :type 'hook) + +(defsubst lsp-inline-completion--overlay-visible () + "Return whether the `overlay' is avaiable." + (and (overlayp lsp-inline-completion--overlay) + (overlay-buffer lsp-inline-completion--overlay))) + +(defun lsp-inline-completion--clear-overlay () + "Hide the suggestion overlay" + (when (lsp-inline-completion--overlay-visible) + (delete-overlay lsp-inline-completion--overlay)) + (setq lsp-inline-completion--overlay nil)) + + +(defun lsp-inline-completion--get-overlay (beg end) + "Build the suggestions overlay" + (when (overlayp lsp-inline-completion--overlay) + (lsp-inline-completion--clear-overlay)) + + (setq lsp-inline-completion--overlay (make-overlay beg end nil nil t)) + (overlay-put lsp-inline-completion--overlay 'keymap lsp-inline-completion-active-map) + (overlay-put lsp-inline-completion--overlay 'priority 9000) + + lsp-inline-completion--overlay) + + +;;;###autoload +(defun lsp-inline-completion-show-keys () + "Shows active keymap hints in the minibuffer" + + (unless (and lsp-inline-completion--items + (numberp lsp-inline-completion--current)) + (error "No completions to show 1")) + + (let ((message-log-max nil)) + (message (concat "Completion " + (propertize (format "%d" (1+ lsp-inline-completion--current)) 'face 'bold) + "/" + (propertize (format "%d" (length lsp-inline-completion--items)) 'face 'bold) + + (-when-let (keys (where-is-internal #'lsp-inline-completion-next lsp-inline-completion-active-map)) + (concat ". " + (propertize " Next" 'face 'italic) + (format ": [%s]" + (string-join (--map (propertize (key-description it) 'face 'help-key-binding) + keys) + "/")))) + (-when-let (keys (where-is-internal #'lsp-inline-completion-accept lsp-inline-completion-active-map)) + (concat (propertize " Accept" 'face 'italic) + (format ": [%s]" + (string-join (--map (propertize (key-description it) 'face 'help-key-binding) + keys) + "/")))))))) + +(defun lsp-inline-completion-show-overlay () + "Makes the suggestion overlay visible" + (unless (and lsp-inline-completion--items + (numberp lsp-inline-completion--current)) + (error "No completions to show 2")) + + (lsp-inline-completion--clear-overlay) + + (-let* ((suggestion + (elt lsp-inline-completion--items + lsp-inline-completion--current)) + ((&InlineCompletionItem? :insert-text :filter-text? :range? :command?) suggestion) + ((&RangeToPoint :start :end) range?) + (start-point (or start (point))) + (showing-at-eol (save-excursion + (goto-char start-point) + (eolp))) + (beg (if showing-at-eol (1- start-point) start-point)) + (end-point (or end (1+ beg))) + (text (cond + ((lsp-markup-content? insert-text) (lsp:markup-content-value insert-text)) + (t insert-text))) + (propertizedText (concat + (buffer-substring beg start-point) + (propertize text 'face 'lsp-inline-completion-overlay-face))) + (ov (lsp-inline-completion--get-overlay beg end-point))) + (goto-char beg) + + (put-text-property 0 1 'cursor t propertizedText) + (overlay-put ov 'display (substring propertizedText 0 1)) + (overlay-put ov 'after-string (substring propertizedText 1)) + + (run-hooks 'lsp-inline-completion-shown-hook))) + +(defun lsp-inline-completion-accept () + "Accepts the current suggestion" + (interactive) + (unless (lsp-inline-completion--overlay-visible) + (error "Not showing suggestions")) + + (lsp-inline-completion--clear-overlay) + (-let* ((suggestion (elt lsp-inline-completion--items lsp-inline-completion--current)) + ((&InlineCompletionItem? :insert-text :filter-text? :range? :command?) suggestion) + ((kind . text) (cond + ((lsp-markup-content? insert-text) + (cons 'snippet (lsp:markup-content-value insert-text) )) + (t (cons 'text insert-text)))) + ((start . end) (when range? + (-let (((&RangeToPoint :start :end) range?)) (cons start end)))) + (text-insert-start (or start lsp-inline-completion--start-point)) + text-insert-end) + + (when text-insert-start + (goto-char text-insert-start)) + + ;; When range is provided, must replace the text of the range by the text + ;; to insert + (when (and start end (/= start end)) + (delete-region start end)) + + ;; Insert suggestion, keeping the cursor at the start point + (insert text) + (setq text-insert-end (point)) + + ;; If a template, format it -- keep track of the end position! + (when (eq kind 'snippet) + (let ((end-marker (set-maker (make-maker)))) + (lsp--expand-snippet (buffer-substring text-insert-start text-insert-end) + text-insert-start + text-insert-end) + (setq text-insert-end (marker-position end-marker)) + (set-marker end-marker nil))) + + ;; Post command + (when command? + (lsp--execute-command command?)) + + (goto-char text-insert-start) + + ;; hooks + (run-hook-with-args-until-failure 'lsp-inline-completion-accepted-hook text text-insert-start text-insert-end))) + +(defun lsp-inline-completion-cancel () + "Close the suggestion overlay" + (interactive) + (unless (lsp-inline-completion--overlay-visible) + (error "Not showing suggestions")) + + (lsp-inline-completion--clear-overlay) + + (when lsp-inline-completion--start-point + (goto-char lsp-inline-completion--start-point))) + +(defun lsp-inline-completion-next () + "Display the next inline completion" + (interactive) + (unless (lsp-inline-completion--overlay-visible) + (error "Not showing suggestions")) + (setq lsp-inline-completion--current + (mod (1+ lsp-inline-completion--current) + (length lsp-inline-completion--items))) + + (lsp-inline-completion-show-overlay)) + +(defun lsp-inline-completion-prev () + "Display the previous inline completion" + (interactive) + (unless (lsp-inline-completion--overlay-visible) + (error "Not showing suggestions")) + (setq lsp-inline-completion--current + (mod (1- lsp-inline-completion--current) + (length lsp-inline-completion--items))) + + (lsp-inline-completion-show-overlay)) + +;;;###autoload +(defun lsp-inline-completion-display (&optional implicit) + "Displays the inline completions overlay" + (interactive) + + (if-let* ((items (lsp-inline-completion-get-items implicit))) + (progn + (setq lsp-inline-completion--items items) + (setq lsp-inline-completion--current 0) + (setq lsp-inline-completion--start-point (point)) + (lsp-inline-completion-show-overlay)) + (message "No Suggestions!"))) + (provide 'lsp-inline-completion) From 33025a09bdcaabd0902303bb77e699a8d43b5041 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 10:14:43 -0300 Subject: [PATCH 04/46] feat: do not consume the key used to cancel the current inline completion --- lsp-inline-completion.el | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index ea84578655b..b2e31e9281b 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -88,7 +88,7 @@ (define-key map (kbd "") #'lsp-inline-completion-cancel) (define-key map (kbd "C-c C-k") #'lsp-inline-completion-cancel) (define-key map [mouse-movement] #'ignore) - (define-key map [t] #'lsp-inline-completion-cancel) + (define-key map [t] #'lsp-inline-completion-cancel-with-input) map) "Keymap active when showing inline code suggestions") @@ -258,13 +258,24 @@ (defun lsp-inline-completion-cancel () "Close the suggestion overlay" (interactive) - (unless (lsp-inline-completion--overlay-visible) - (error "Not showing suggestions")) + (when (lsp-inline-completion--overlay-visible) - (lsp-inline-completion--clear-overlay) + (lsp-inline-completion--clear-overlay) + + (when lsp-inline-completion--start-point + (goto-char lsp-inline-completion--start-point)))) + +(defun lsp-inline-completion-cancel-with-input (event &optional arg) + "Cancel the inline completion and executes whatever event was received" + (interactive (list last-input-event current-prefix-arg)) + + (lsp-inline-completion-cancel) + + (let ((command (lookup-key (current-global-map) (vector event))) + (current-prefix-arg current-prefix-arg)) - (when lsp-inline-completion--start-point - (goto-char lsp-inline-completion--start-point))) + (when (commandp command) + (call-interactively command)))) (defun lsp-inline-completion-next () "Display the next inline completion" From 9c5fc21ad9754085a51926cc629708095a10cd96 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 10:16:04 -0300 Subject: [PATCH 05/46] feat: better positioning of the cursor when the inline completion is displayed --- lsp-inline-completion.el | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index b2e31e9281b..c7a699677e7 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -198,12 +198,33 @@ (propertizedText (concat (buffer-substring beg start-point) (propertize text 'face 'lsp-inline-completion-overlay-face))) - (ov (lsp-inline-completion--get-overlay beg end-point))) + (ov (lsp-inline-completion--get-overlay beg end-point)) + (completion-is-substr (string-equal + (buffer-substring beg lsp-inline-completion--start-point) + (substring propertizedText 0 (- lsp-inline-completion--start-point beg)))) + display-str after-str target-position) + (goto-char beg) - (put-text-property 0 1 'cursor t propertizedText) - (overlay-put ov 'display (substring propertizedText 0 1)) - (overlay-put ov 'after-string (substring propertizedText 1)) + (put-text-property 0 (length propertizedText) 'cursor t propertizedText) + + (if completion-is-substr + (progn + ;; Show the prefix as `display' + (setq display-str (substring propertizedText 0 (- lsp-inline-completion--start-point beg))) + (setq after-str (substring propertizedText (- lsp-inline-completion--start-point beg) nil)) + (setq target-position lsp-inline-completion--start-point)) + + + (setq display-str (substring propertizedText 0 1)) + (setq after-str (substring propertizedText 1)) + (setq target-position beg) + ) + + (overlay-put ov 'display display-str) + (overlay-put ov 'after-string after-str) + + (goto-char target-position) (run-hooks 'lsp-inline-completion-shown-hook))) From 2b9d47dd9aa9bdfd0c81c804f320abb485a62818 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 10:16:48 -0300 Subject: [PATCH 06/46] chore: do not warn of no suggestions when implicit --- lsp-inline-completion.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index c7a699677e7..56ba0e5e7e2 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -327,10 +327,12 @@ (if-let* ((items (lsp-inline-completion-get-items implicit))) (progn + (lsp-inline-completion--clear-overlay) (setq lsp-inline-completion--items items) (setq lsp-inline-completion--current 0) (setq lsp-inline-completion--start-point (point)) (lsp-inline-completion-show-overlay)) - (message "No Suggestions!"))) + (unless implicit + (message "No Suggestions!")))) (provide 'lsp-inline-completion) From 5ee78c14e99691fa9d7293ea27de668a9d26c988 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 10:38:07 -0300 Subject: [PATCH 07/46] feat: provide a minor mode for auto activating inline completions --- lsp-mode.el | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/lsp-mode.el b/lsp-mode.el index e39bc976940..f3e47ab87f5 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -4274,6 +4274,10 @@ yet." (lsp-feature? "textDocument/inlayHint")) (lsp-inlay-hints-mode)) + (when (and lsp-inline-completion-enable + (lsp-feature? "textDocument/inlineCompletion")) + (lsp-inline-completion-mode)) + (when (and lsp-enable-dap-auto-configure (functionp 'dap-mode)) (dap-auto-configure-mode 1))) @@ -5430,9 +5434,9 @@ If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." (lsp-help-mode) (with-help-window lsp-help-buf-name (insert - (mapconcat 'string-trim-right - (split-string (lsp--render-on-hover-content contents t) "\n") - "\n")))) + (mapconcat 'string-trim-right + (split-string (lsp--render-on-hover-content contents t) "\n") + "\n")))) (run-mode-hooks))) (lsp--info "No content at point.")))) @@ -9938,6 +9942,46 @@ string." (setf window-scroll-functions (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) + +;; Inline Completions +(defcustom lsp-inline-completion-enable t + "If non-nil it will enable inline completions on idle." + :type 'boolean + :group 'lsp-mode + :package-version '(lsp-mode . "9.0.1")) + +(defcustom lsp-inline-completion-idle-delay 2 + "The number of seconds before trying to fetch inline completions, when +lsp-inline-completion-mode is active" + :type 'number) + +(defvar-local lsp-inline-completion--idle-timer nil + "The idle timer used by lsp-inline-completion-mode") + +(defun lsp-inline-completion--after-change (&rest args) + (when (and lsp-inline-completion-mode lsp--buffer-workspaces) + (when lsp-inline-completion--idle-timer + (cancel-timer lsp-inline-completion--idle-timer)) + (setq lsp-inline-completion--idle-timer + (run-with-timer lsp-inline-completion-idle-delay + nil + #'lsp-inline-completion-display + 'implicit)))) + +(define-minor-mode lsp-inline-completion-mode + "Mode automatically displaying inline completions." + :lighter nil + (cond + ((and lsp-inline-completion-mode lsp--buffer-workspaces) + (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 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) + ;;;###autoload From b4f0c7c0fcac04c4b6c73ace4412f1aa4d7dafa6 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 11:17:41 -0300 Subject: [PATCH 08/46] chore: avoid linter errors --- lsp-inline-completion.el | 23 ++++++++++++-------- lsp-mode.el | 46 ++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 56ba0e5e7e2..96185c2a1cc 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -95,7 +95,8 @@ ;;;###autoload (defface lsp-inline-completion-overlay-face '((t :inherit shadow)) - "Face for the inline code suggestions overlay.") + "Face for the inline code suggestions overlay." + :group 'lsp-mode) ;; Local Buffer State @@ -106,20 +107,24 @@ (defcustom lsp-before-inline-completion-hook nil "Hooks run before starting code suggestions" - :type 'hook) + :type 'hook + :group 'lsp-mode) (defcustom lsp-after-inline-completion-hook nil "Hooks executed after asking for code suggestions." - :type 'hook) + :type 'hook + :group 'lsp-mode) ;; TODO: Add parameters to the hooks! (defcustom lsp-inline-completion-accepted-hook nil "Hooks executed after accepting a code suggestion." - :type 'hook) + :type 'hook + :group 'lsp-mode) (defcustom lsp-inline-completion-shown-hook nil "Hooks executed after showing a suggestion." - :type 'hook) + :type 'hook + :group 'lsp-mode) (defsubst lsp-inline-completion--overlay-visible () "Return whether the `overlay' is avaiable." @@ -184,7 +189,7 @@ (-let* ((suggestion (elt lsp-inline-completion--items lsp-inline-completion--current)) - ((&InlineCompletionItem? :insert-text :filter-text? :range? :command?) suggestion) + ((&InlineCompletionItem? :insert-text :range?) suggestion) ((&RangeToPoint :start :end) range?) (start-point (or start (point))) (showing-at-eol (save-excursion @@ -218,8 +223,7 @@ (setq display-str (substring propertizedText 0 1)) (setq after-str (substring propertizedText 1)) - (setq target-position beg) - ) + (setq target-position beg)) (overlay-put ov 'display display-str) (overlay-put ov 'after-string after-str) @@ -236,7 +240,7 @@ (lsp-inline-completion--clear-overlay) (-let* ((suggestion (elt lsp-inline-completion--items lsp-inline-completion--current)) - ((&InlineCompletionItem? :insert-text :filter-text? :range? :command?) suggestion) + ((&InlineCompletionItem? :insert-text :range? :command?) suggestion) ((kind . text) (cond ((lsp-markup-content? insert-text) (cons 'snippet (lsp:markup-content-value insert-text) )) @@ -276,6 +280,7 @@ ;; hooks (run-hook-with-args-until-failure 'lsp-inline-completion-accepted-hook text text-insert-start text-insert-end))) +;;;###autoload (defun lsp-inline-completion-cancel () "Close the suggestion overlay" (interactive) diff --git a/lsp-mode.el b/lsp-mode.el index f3e47ab87f5..0a284321b20 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -68,6 +68,8 @@ (declare-function yas-expand-snippet "ext:yasnippet") (declare-function dap-mode "ext:dap-mode") (declare-function dap-auto-configure-mode "ext:dap-mode") +(declare-function lsp-inline-completion-display "lsp-inline-completion" (&optional implicit)) +(declare-function lsp-inline-completion-cancel "lsp-inline-completion") (defvar yas-inhibit-overlay-modification-protection) (defvar yas-indent-line) @@ -3693,6 +3695,19 @@ and expand the capabilities section" :group 'lsp-mode :package-version '(lsp-mode . "9.0.0")) +(defcustom lsp-inline-completion-enable t + "If non-nil it will enable inline completions on idle." + :type 'boolean + :group 'lsp-mode + :package-version '(lsp-mode . "9.0.1")) + +(defcustom lsp-inline-completion-idle-delay 2 + "The number of seconds before trying to fetch inline completions, when +lsp-inline-completion-mode is active" + :type 'number + :group 'lsp-mode + :package-version '(lsp-mode . "9.0.1")) + (defun lsp--uninitialize-workspace () "Cleanup buffer state. When a workspace is shut down, by request or from just @@ -9944,30 +9959,9 @@ string." ;; Inline Completions -(defcustom lsp-inline-completion-enable t - "If non-nil it will enable inline completions on idle." - :type 'boolean - :group 'lsp-mode - :package-version '(lsp-mode . "9.0.1")) - -(defcustom lsp-inline-completion-idle-delay 2 - "The number of seconds before trying to fetch inline completions, when -lsp-inline-completion-mode is active" - :type 'number) - (defvar-local lsp-inline-completion--idle-timer nil "The idle timer used by lsp-inline-completion-mode") -(defun lsp-inline-completion--after-change (&rest args) - (when (and lsp-inline-completion-mode lsp--buffer-workspaces) - (when lsp-inline-completion--idle-timer - (cancel-timer lsp-inline-completion--idle-timer)) - (setq lsp-inline-completion--idle-timer - (run-with-timer lsp-inline-completion-idle-delay - nil - #'lsp-inline-completion-display - 'implicit)))) - (define-minor-mode lsp-inline-completion-mode "Mode automatically displaying inline completions." :lighter nil @@ -9982,6 +9976,16 @@ lsp-inline-completion-mode is active" (remove-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) +(defun lsp-inline-completion--after-change (&rest _) + (when (and lsp-inline-completion-mode lsp--buffer-workspaces) + (when lsp-inline-completion--idle-timer + (cancel-timer lsp-inline-completion--idle-timer)) + (setq lsp-inline-completion--idle-timer + (run-with-timer lsp-inline-completion-idle-delay + nil + #'lsp-inline-completion-display + 'implicit)))) + ;;;###autoload From f5b572b9d49bb4c8a988054c6599b3fc5434d222 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 12:45:49 -0300 Subject: [PATCH 09/46] feat: better cursor positioning on accept --- lsp-inline-completion.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 96185c2a1cc..dffac5a997f 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -248,7 +248,10 @@ ((start . end) (when range? (-let (((&RangeToPoint :start :end) range?)) (cons start end)))) (text-insert-start (or start lsp-inline-completion--start-point)) - text-insert-end) + text-insert-end + (completion-is-substr (string-equal + (buffer-substring text-insert-start lsp-inline-completion--start-point) + (substring text 0 (- lsp-inline-completion--start-point text-insert-start))))) (when text-insert-start (goto-char text-insert-start)) @@ -275,7 +278,9 @@ (when command? (lsp--execute-command command?)) - (goto-char text-insert-start) + (if completion-is-substr + (goto-char lsp-inline-completion--start-point) + (goto-char text-insert-start)) ;; hooks (run-hook-with-args-until-failure 'lsp-inline-completion-accepted-hook text text-insert-start text-insert-end))) From f9783fdade8da64f40e307f7adce7d1baffbd08f Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 12:59:13 -0300 Subject: [PATCH 10/46] fix: forward prefix arg --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index dffac5a997f..3864a97546a 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -303,7 +303,7 @@ (lsp-inline-completion-cancel) (let ((command (lookup-key (current-global-map) (vector event))) - (current-prefix-arg current-prefix-arg)) + (current-prefix-arg arg)) (when (commandp command) (call-interactively command)))) From 828875b7aa152785ee93856e5478c2d265fd734b Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 13:03:04 -0300 Subject: [PATCH 11/46] fix: wrong function name --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 3864a97546a..29042209b2c 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -267,7 +267,7 @@ ;; If a template, format it -- keep track of the end position! (when (eq kind 'snippet) - (let ((end-marker (set-maker (make-maker)))) + (let ((end-marker (set-marker (make-marker)))) (lsp--expand-snippet (buffer-substring text-insert-start text-insert-end) text-insert-start text-insert-end) From d3ff223b750c26698a1658261214112b75bcc180 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 13:13:05 -0300 Subject: [PATCH 12/46] fix: wrong number of arguments --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 29042209b2c..629d3bc30c7 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -267,7 +267,7 @@ ;; If a template, format it -- keep track of the end position! (when (eq kind 'snippet) - (let ((end-marker (set-marker (make-marker)))) + (let ((end-marker (set-marker (make-marker) (point)))) (lsp--expand-snippet (buffer-substring text-insert-start text-insert-end) text-insert-start text-insert-end) From 8f13826ffcb3de9e3626a0255fd788dcf8e418d6 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Mon, 25 Nov 2024 13:16:27 -0300 Subject: [PATCH 13/46] chore: use cl-map to avoid linting issues --- lsp-inline-completion.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 629d3bc30c7..704c50b4fec 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -30,6 +30,7 @@ (require 'lsp-mode) (require 'dash) +(require 'cl-lib) (defun lsp-inline-completion--trigger-kind (implicit) (plist-put (lsp--text-document-position-params) @@ -54,7 +55,7 @@ ;; Response may or may not come inside an :items. Parse each ;; response to ensure compatibility. - (map 'list (lambda (elt) + (cl-map 'list (lambda (elt) (if (lsp-inline-completion-list? elt) (lsp:inline-completion-list-items elt) elt)) From 1d4b7c85cb08a2fc338922a216f5f9547f5884e5 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Tue, 26 Nov 2024 12:48:48 -0300 Subject: [PATCH 14/46] fix: avoid weird interactions with company-mode Check if company is active before displaying the overlay -- hide it if so --- lsp-inline-completion.el | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 704c50b4fec..d7fa678339f 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -105,6 +105,7 @@ (defvar-local lsp-inline-completion--current nil "The current suggestion to be displayed") (defvar-local lsp-inline-completion--overlay nil "The overlay displaying code suggestions") (defvar-local lsp-inline-completion--start-point nil "The point where the completion started") +(defvar-local lsp-inline-completion--showing-company nil "If company was active when the tooltip is shown") (defcustom lsp-before-inline-completion-hook nil "Hooks run before starting code suggestions" @@ -187,6 +188,13 @@ (lsp-inline-completion--clear-overlay) + (setq lsp-inline-completion--showing-company + (and (bound-and-true-p company-mode) + (company--active-p))) + + (when lsp-inline-completion--showing-company + (company-cancel)) + (-let* ((suggestion (elt lsp-inline-completion--items lsp-inline-completion--current)) @@ -295,7 +303,10 @@ (lsp-inline-completion--clear-overlay) (when lsp-inline-completion--start-point - (goto-char lsp-inline-completion--start-point)))) + (goto-char lsp-inline-completion--start-point)) + + (when lsp-inline-completion--showing-company + (company-manual-begin)))) (defun lsp-inline-completion-cancel-with-input (event &optional arg) "Cancel the inline completion and executes whatever event was received" From 063fd894fd3be63c6b2e503f552838e56a407b6a Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Tue, 26 Nov 2024 12:54:27 -0300 Subject: [PATCH 15/46] refactor: split parse and request -- may be handy in async --- lsp-inline-completion.el | 65 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index d7fa678339f..fb43d1ad53d 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -40,32 +40,26 @@ lsp/inline-completion-trigger-invoked))))) ;;;###autoload -(defun lsp-inline-completion-get-items (&optional implicit) +(defun lsp-inline-completion-parse-items (response) "Calls textDocument_inlineCompletion and returns a list of InlineCompletionItem's" - (lsp--spinner-start) - (unwind-protect - (-some--> - (lsp-request-while-no-input "textDocument/inlineCompletion" - (lsp-inline-completion--trigger-kind implicit)) - ;; Kludge to workaround multiple backends responding -- it may - ;; come as a list (multiple servers) or as a single ht (single - ;; server). Promote it to list and move on - (if (ht-p it) (list it) it) - - ;; Response may or may not come inside an :items. Parse each - ;; response to ensure compatibility. - (cl-map 'list (lambda (elt) - (if (lsp-inline-completion-list? elt) - (lsp:inline-completion-list-items elt) - elt)) - it) - - ;; Join everything into a single list - (apply 'seq-concatenate `(list ,@it))) + (-some--> + response + ;; Kludge to workaround multiple backends responding -- it may + ;; come as a list (multiple servers) or as a single ht (single + ;; server). Promote it to list and move on + (if (ht-p it) (list it) it) - ;; Clean up - (lsp--spinner-stop))) + ;; Response may or may not come inside an :items. Parse each + ;; response to ensure compatibility. + (cl-map 'list (lambda (elt) + (if (lsp-inline-completion-list? elt) + (lsp:inline-completion-list-items elt) + elt)) + it) + + ;; Join everything into a single list + (apply 'seq-concatenate `(list ,@it)))) ;;;;;; Default UI -- overlay @@ -347,14 +341,21 @@ "Displays the inline completions overlay" (interactive) - (if-let* ((items (lsp-inline-completion-get-items implicit))) - (progn - (lsp-inline-completion--clear-overlay) - (setq lsp-inline-completion--items items) - (setq lsp-inline-completion--current 0) - (setq lsp-inline-completion--start-point (point)) - (lsp-inline-completion-show-overlay)) - (unless implicit - (message "No Suggestions!")))) + (lsp--spinner-start) + (unwind-protect + (if-let* ((resp (lsp-request-while-no-input "textDocument/inlineCompletion" + (lsp-inline-completion--trigger-kind implicit))) + (items (lsp-inline-completion-parse-items resp))) + + (progn + (lsp-inline-completion--clear-overlay) + (setq lsp-inline-completion--items items) + (setq lsp-inline-completion--current 0) + (setq lsp-inline-completion--start-point (point)) + (lsp-inline-completion-show-overlay)) + (unless implicit + (message "No Suggestions!"))) + ;; Clean up + (lsp--spinner-stop))) (provide 'lsp-inline-completion) From 5f67185d691538a679d10f7f15c7b018ff063b30 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Tue, 26 Nov 2024 12:55:13 -0300 Subject: [PATCH 16/46] fix: use current active map instead of global map --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index fb43d1ad53d..b226fd83fc8 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -308,7 +308,7 @@ (lsp-inline-completion-cancel) - (let ((command (lookup-key (current-global-map) (vector event))) + (let ((command (lookup-key (current-active-maps) (vector event))) (current-prefix-arg arg)) (when (commandp command) From 3bab211e6f1d3acd5e144e7ca1b3d9f03f281f3c Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Tue, 26 Nov 2024 13:00:59 -0300 Subject: [PATCH 17/46] fix: add external references --- lsp-inline-completion.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index b226fd83fc8..53931b9703b 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -32,6 +32,10 @@ (require 'dash) (require 'cl-lib) +(declare-function company--active-p "ext:company") +(declare-function company-cancel "ext:company" (&optional result)) +(declare-function company-manual-begin "ext:company") + (defun lsp-inline-completion--trigger-kind (implicit) (plist-put (lsp--text-document-position-params) :context (ht ("triggerKind" From da2d8c285b8baa47e0b987c00d02b3eaf2638d88 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Tue, 26 Nov 2024 15:16:58 -0300 Subject: [PATCH 18/46] feat: provide a method to inhibit inline completion mode Add a list of predicates that can inhibit triggering inline completions -- e.g.: ```elisp (defun lsp-inline-completion-inhibit-if-company-active () (and (bound-and-true-p company-mode) (company--active-p))) (push 'lsp-inline-completion-inhibit-if-company-active lsp-inline-completion-inhibit-predicates) ``` --- lsp-mode.el | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lsp-mode.el b/lsp-mode.el index 0a284321b20..2d3fbd77873 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -9976,6 +9976,15 @@ string." (remove-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) +(defcustom lsp-inline-completion-inhibit-predicates nil + "When a function of this list returns non nil, lsp-inline-completion-mode will not show the completion" + :type '(repeat function) + :group 'lsp-mode) + +(defun lsp-inline-completion--maybe-display () + (unless (--any (funcall it) lsp-inline-completion-inhibit-predicates) + (lsp-inline-completion-display 'implicit))) + (defun lsp-inline-completion--after-change (&rest _) (when (and lsp-inline-completion-mode lsp--buffer-workspaces) (when lsp-inline-completion--idle-timer @@ -9983,8 +9992,7 @@ string." (setq lsp-inline-completion--idle-timer (run-with-timer lsp-inline-completion-idle-delay nil - #'lsp-inline-completion-display - 'implicit)))) + #'lsp-inline-completion--maybe-display)))) From 64e6a774d4b9a9c6122e3c327ed5be58cdb5f173 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Tue, 26 Nov 2024 15:52:36 -0300 Subject: [PATCH 19/46] chore: remove debug --- lsp-inline-completion.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 53931b9703b..0bac0d23812 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -156,7 +156,7 @@ (unless (and lsp-inline-completion--items (numberp lsp-inline-completion--current)) - (error "No completions to show 1")) + (error "No completions to show")) (let ((message-log-max nil)) (message (concat "Completion " @@ -182,7 +182,7 @@ "Makes the suggestion overlay visible" (unless (and lsp-inline-completion--items (numberp lsp-inline-completion--current)) - (error "No completions to show 2")) + (error "No completions to show")) (lsp-inline-completion--clear-overlay) From a1e7d7d87ee5ee51da605e9b4af3fdf8a0e84d44 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 11:13:14 -0300 Subject: [PATCH 20/46] chore: keep interfaces sorted in lsp-protocol.el --- lsp-protocol.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lsp-protocol.el b/lsp-protocol.el index 15de5c198c2..d53bff587c7 100644 --- a/lsp-protocol.el +++ b/lsp-protocol.el @@ -626,8 +626,6 @@ See `-let' for a description of the destructuring mechanism." (CompletionItemKindCapabilities nil (:valueSet)) (CompletionItemTagSupportCapabilities (:valueSet) nil) (CompletionOptions nil (:resolveProvider :triggerCharacters :allCommitCharacters)) - (InlineCompletionItem (:insertText) (:filterText :range :command)) - (InlineCompletionList (:items) nil) (ConfigurationItem nil (:scopeUri :section)) (CreateFileOptions nil (:ignoreIfExists :overwrite)) (DeclarationCapabilities nil (:dynamicRegistration :linkSupport)) @@ -823,7 +821,10 @@ See `-let' for a description of the destructuring mechanism." ;; 3.17 (InlayHint (:label :position) (:kind :paddingLeft :paddingRight)) (InlayHintLabelPart (:value) (:tooltip :location :command)) - (InlayHintsParams (:textDocument) (:range))) + (InlayHintsParams (:textDocument) (:range)) + ;; 3.18 + (InlineCompletionItem (:insertText) (:filterText :range :command)) + (InlineCompletionList (:items) nil)) ;; 3.17 (defconst lsp/inlay-hint-kind-type-hint 1) From 92281ed16bdb23bbca009e2aae6caad2d1ce8dcc Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 11:43:43 -0300 Subject: [PATCH 21/46] refactor: declare and use InlineCompletionParams and InlineCompletionContext --- lsp-inline-completion.el | 17 ++++++++++------- lsp-protocol.el | 2 ++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 0bac0d23812..d119b3c041b 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -36,12 +36,15 @@ (declare-function company-cancel "ext:company" (&optional result)) (declare-function company-manual-begin "ext:company") -(defun lsp-inline-completion--trigger-kind (implicit) - (plist-put (lsp--text-document-position-params) - :context (ht ("triggerKind" - (if implicit - lsp/inline-completion-trigger-automatic - lsp/inline-completion-trigger-invoked))))) +(defun lsp-inline-completion--params (implicit &optional identifier position) + "Returns a InlineCompletionParams instance" + (lsp-make-inline-completion-params + :textDocument (or identifier (lsp--text-document-identifier)) + :position (or position (lsp--cur-position)) + :context (lsp-make-inline-completion-context + :triggerKind (if implicit + lsp/inline-completion-trigger-automatic + lsp/inline-completion-trigger-invoked)))) ;;;###autoload (defun lsp-inline-completion-parse-items (response) @@ -348,7 +351,7 @@ (lsp--spinner-start) (unwind-protect (if-let* ((resp (lsp-request-while-no-input "textDocument/inlineCompletion" - (lsp-inline-completion--trigger-kind implicit))) + (lsp-inline-completion--params implicit))) (items (lsp-inline-completion-parse-items resp))) (progn diff --git a/lsp-protocol.el b/lsp-protocol.el index d53bff587c7..3b39db0e210 100644 --- a/lsp-protocol.el +++ b/lsp-protocol.el @@ -823,6 +823,8 @@ See `-let' for a description of the destructuring mechanism." (InlayHintLabelPart (:value) (:tooltip :location :command)) (InlayHintsParams (:textDocument) (:range)) ;; 3.18 + (InlineCompletionParams (:textDocument :position :context)) + (InlineCompletionContext (:triggerKind)) (InlineCompletionItem (:insertText) (:filterText :range :command)) (InlineCompletionList (:items) nil)) From 55d086a551772b8909602fee19c57035ea7b323f Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 12:32:55 -0300 Subject: [PATCH 22/46] fix: do not assume that the server response is a hash table Also, refactor how we parse the response, using pcase for a more explicit approach --- lsp-inline-completion.el | 43 +++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index d119b3c041b..966aa83f18b 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -46,28 +46,25 @@ lsp/inline-completion-trigger-automatic lsp/inline-completion-trigger-invoked)))) -;;;###autoload -(defun lsp-inline-completion-parse-items (response) - "Calls textDocument_inlineCompletion and returns a list of InlineCompletionItem's" - - (-some--> - response - ;; Kludge to workaround multiple backends responding -- it may - ;; come as a list (multiple servers) or as a single ht (single - ;; server). Promote it to list and move on - (if (ht-p it) (list it) it) - - ;; Response may or may not come inside an :items. Parse each - ;; response to ensure compatibility. - (cl-map 'list (lambda (elt) - (if (lsp-inline-completion-list? elt) - (lsp:inline-completion-list-items elt) - elt)) - it) - - ;; Join everything into a single list - (apply 'seq-concatenate `(list ,@it)))) - +(defun lsp-inline-completion--parse-items (response) + "Parses the reponse from the server and returns a list of +InlineCompletionItem objects" + + (pcase response + ;; Server responded with a completion list + ((lsp-interface InlineCompletionList :items) + (seq-into items 'list)) + + ;; Server responded with a sequence of completion items + ((pred (lambda (i) + (and (sequencep i) + (lsp-inline-completion-item? (elt i 0))))) + (seq-into i 'list)) + + ;; A sequence means multiple server may have responded. Iterate over them and normalize + ((pred sequencep) + (let ((item-seq (map 'list #'lsp-inline-completion--parse-items response))) + (apply 'seq-concatenate `(list ,@item-seq)))))) ;;;;;; Default UI -- overlay @@ -352,7 +349,7 @@ (unwind-protect (if-let* ((resp (lsp-request-while-no-input "textDocument/inlineCompletion" (lsp-inline-completion--params implicit))) - (items (lsp-inline-completion-parse-items resp))) + (items (lsp-inline-completion--parse-items resp))) (progn (lsp-inline-completion--clear-overlay) From 0e81d72a9ed2e6f43cd3de0dd5f7395a86554687 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 17:05:04 -0300 Subject: [PATCH 23/46] refactor: move lsp-inline-completion-mode outside of lsp-mode.el --- lsp-inline-completion.el | 55 +++++++++++++++++++++++++++++++++++++++- lsp-mode.el | 53 +------------------------------------- 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 966aa83f18b..14719361790 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -28,7 +28,7 @@ ;;; Code: -(require 'lsp-mode) +(require 'lsp-protocol) (require 'dash) (require 'cl-lib) @@ -362,4 +362,57 @@ InlineCompletionItem objects" ;; Clean up (lsp--spinner-stop))) + +;; Inline Completion Mode +(defcustom lsp-inline-completion-enable t + "If non-nil it will enable inline completions on idle." + :type 'boolean + :group 'lsp-mode + :package-version '(lsp-mode . "9.0.1")) + +(defcustom lsp-inline-completion-idle-delay 2 + "The number of seconds before trying to fetch inline completions, when +lsp-inline-completion-mode is active" + :type 'number + :group 'lsp-mode + :package-version '(lsp-mode . "9.0.1")) + +(defcustom lsp-inline-completion-inhibit-predicates nil + "When a function of this list returns non nil, lsp-inline-completion-mode will not show the completion" + :type '(repeat function) + :group 'lsp-mode) + + +(defvar-local lsp-inline-completion--idle-timer nil + "The idle timer used by lsp-inline-completion-mode") + +(defun lsp-inline-completion--maybe-display () + (unless (--any (funcall it) lsp-inline-completion-inhibit-predicates) + (setq last-command this-command) + (setq this-command 'lsp-inline-completion-display) + (lsp-inline-completion-display 'implicit))) + +(defun lsp-inline-completion--after-change (&rest _) + (when (and lsp-inline-completion-mode lsp--buffer-workspaces) + (when lsp-inline-completion--idle-timer + (cancel-timer lsp-inline-completion--idle-timer)) + (setq lsp-inline-completion--idle-timer + (run-with-timer lsp-inline-completion-idle-delay + nil + #'lsp-inline-completion--maybe-display)))) + +(define-minor-mode lsp-inline-completion-mode + "Mode automatically displaying inline completions." + :lighter nil + (cond + ((and lsp-inline-completion-mode lsp--buffer-workspaces) + (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 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) + (provide 'lsp-inline-completion) diff --git a/lsp-mode.el b/lsp-mode.el index 2d3fbd77873..f6e3004d8ab 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -57,6 +57,7 @@ (require 'minibuffer) (require 'help-mode) (require 'lsp-protocol) +(require 'lsp-inline-completion) (defgroup lsp-mode nil "Language Server Protocol client." @@ -68,8 +69,6 @@ (declare-function yas-expand-snippet "ext:yasnippet") (declare-function dap-mode "ext:dap-mode") (declare-function dap-auto-configure-mode "ext:dap-mode") -(declare-function lsp-inline-completion-display "lsp-inline-completion" (&optional implicit)) -(declare-function lsp-inline-completion-cancel "lsp-inline-completion") (defvar yas-inhibit-overlay-modification-protection) (defvar yas-indent-line) @@ -3695,19 +3694,6 @@ and expand the capabilities section" :group 'lsp-mode :package-version '(lsp-mode . "9.0.0")) -(defcustom lsp-inline-completion-enable t - "If non-nil it will enable inline completions on idle." - :type 'boolean - :group 'lsp-mode - :package-version '(lsp-mode . "9.0.1")) - -(defcustom lsp-inline-completion-idle-delay 2 - "The number of seconds before trying to fetch inline completions, when -lsp-inline-completion-mode is active" - :type 'number - :group 'lsp-mode - :package-version '(lsp-mode . "9.0.1")) - (defun lsp--uninitialize-workspace () "Cleanup buffer state. When a workspace is shut down, by request or from just @@ -9957,43 +9943,6 @@ string." (setf window-scroll-functions (delete #'lsp--update-inlay-hints-scroll-function window-scroll-functions))))) - -;; Inline Completions -(defvar-local lsp-inline-completion--idle-timer nil - "The idle timer used by lsp-inline-completion-mode") - -(define-minor-mode lsp-inline-completion-mode - "Mode automatically displaying inline completions." - :lighter nil - (cond - ((and lsp-inline-completion-mode lsp--buffer-workspaces) - (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 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) - -(defcustom lsp-inline-completion-inhibit-predicates nil - "When a function of this list returns non nil, lsp-inline-completion-mode will not show the completion" - :type '(repeat function) - :group 'lsp-mode) - -(defun lsp-inline-completion--maybe-display () - (unless (--any (funcall it) lsp-inline-completion-inhibit-predicates) - (lsp-inline-completion-display 'implicit))) - -(defun lsp-inline-completion--after-change (&rest _) - (when (and lsp-inline-completion-mode lsp--buffer-workspaces) - (when lsp-inline-completion--idle-timer - (cancel-timer lsp-inline-completion--idle-timer)) - (setq lsp-inline-completion--idle-timer - (run-with-timer lsp-inline-completion-idle-delay - nil - #'lsp-inline-completion--maybe-display)))) - ;;;###autoload From a74edd4a2cd7e24e86a98c5950ce8eb8c94581c4 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 17:05:23 -0300 Subject: [PATCH 24/46] refactor: extract company integration to a minor mode Users can enable the lsp-inline-completion-company-integration-mode if they see fit. Other completion frontends may be customized via hooks, just as this minor mode does --- lsp-inline-completion.el | 95 ++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 14719361790..a2692516715 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -32,10 +32,6 @@ (require 'dash) (require 'cl-lib) -(declare-function company--active-p "ext:company") -(declare-function company-cancel "ext:company" (&optional result)) -(declare-function company-manual-begin "ext:company") - (defun lsp-inline-completion--params (implicit &optional identifier position) "Returns a InlineCompletionParams instance" (lsp-make-inline-completion-params @@ -103,7 +99,6 @@ InlineCompletionItem objects" (defvar-local lsp-inline-completion--current nil "The current suggestion to be displayed") (defvar-local lsp-inline-completion--overlay nil "The overlay displaying code suggestions") (defvar-local lsp-inline-completion--start-point nil "The point where the completion started") -(defvar-local lsp-inline-completion--showing-company nil "If company was active when the tooltip is shown") (defcustom lsp-before-inline-completion-hook nil "Hooks run before starting code suggestions" @@ -115,9 +110,19 @@ InlineCompletionItem objects" :type 'hook :group 'lsp-mode) -;; TODO: Add parameters to the hooks! (defcustom lsp-inline-completion-accepted-hook nil - "Hooks executed after accepting a code suggestion." + "Hooks executed after accepting a code suggestion. The hooks receive the +text range that was updated by the completion" + :type 'hook + :group 'lsp-mode) + +(defcustom lsp-inline-completion-cancelled-hook nil + "Hooks executed after cancelling the completion UI" + :type 'hook + :group 'lsp-mode) + +(defcustom lsp-inline-completion-before-show-hook nil + "Hooks executed before showing a suggestion." :type 'hook :group 'lsp-mode) @@ -126,6 +131,16 @@ InlineCompletionItem objects" :type 'hook :group 'lsp-mode) +(defcustom lsp-inline-completion-overlay-priority (cons nil 100) + "The priority of the overlay." + :type '(choice (const :tag "No Priority" nil) + (integer :tag "Simple, Overriding Priority") + (cons :tag "Composite" + (choice (integer :tag "Primary") + (const :tag "Primary Unset" nil)) + (integer :tag "Secondary"))) + :group 'lsp-mode) + (defsubst lsp-inline-completion--overlay-visible () "Return whether the `overlay' is avaiable." (and (overlayp lsp-inline-completion--overlay) @@ -145,12 +160,11 @@ InlineCompletionItem objects" (setq lsp-inline-completion--overlay (make-overlay beg end nil nil t)) (overlay-put lsp-inline-completion--overlay 'keymap lsp-inline-completion-active-map) - (overlay-put lsp-inline-completion--overlay 'priority 9000) + (overlay-put lsp-inline-completion--overlay 'priority lsp-inline-completion-overlay-priority) lsp-inline-completion--overlay) -;;;###autoload (defun lsp-inline-completion-show-keys () "Shows active keymap hints in the minibuffer" @@ -186,12 +200,7 @@ InlineCompletionItem objects" (lsp-inline-completion--clear-overlay) - (setq lsp-inline-completion--showing-company - (and (bound-and-true-p company-mode) - (company--active-p))) - - (when lsp-inline-completion--showing-company - (company-cancel)) + (run-hooks 'lsp-inline-completion-before-show-hook) (-let* ((suggestion (elt lsp-inline-completion--items @@ -292,7 +301,6 @@ InlineCompletionItem objects" ;; hooks (run-hook-with-args-until-failure 'lsp-inline-completion-accepted-hook text text-insert-start text-insert-end))) -;;;###autoload (defun lsp-inline-completion-cancel () "Close the suggestion overlay" (interactive) @@ -303,8 +311,8 @@ InlineCompletionItem objects" (when lsp-inline-completion--start-point (goto-char lsp-inline-completion--start-point)) - (when lsp-inline-completion--showing-company - (company-manual-begin)))) + (run-hooks 'lsp-inline-completion-cancelled-hook) + )) (defun lsp-inline-completion-cancel-with-input (event &optional arg) "Cancel the inline completion and executes whatever event was received" @@ -415,4 +423,55 @@ lsp-inline-completion-mode is active" (remove-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) + + +;; Company default integration + +(declare-function company--active-p "ext:company") +(declare-function company-cancel "ext:company" (&optional result)) +(declare-function company-manual-begin "ext:company") + +(defcustom lsp-inline-completion-mode-inhibit-when-company-active t + "If the inline completion mode should avoid calling completions when company is active" + :group 'lsp-mode) + +(defvar-local lsp-inline-completion--showing-company nil "If company was active when the tooltip is shown") + +(defun lsp-inline-completion--company-save-state-and-hide () + (setq lsp-inline-completion--showing-company + (and (bound-and-true-p company-mode) + (company--active-p))) + + (when lsp-inline-completion--showing-company + (company-cancel))) + +(defun lsp-inline-completion--company-restore-state () + (when lsp-inline-completion--showing-company + (company-manual-begin)) + (setq lsp-inline-completion--showing-company nil)) + +(defun lsp-inline-completion--company-active-p () + (and (bound-and-true-p company-mode) (company--active-p))) + +(define-minor-mode lsp-inline-completion-company-integration-mode + "Minor mode to be used when company mode is active with lsp-inline-completion-mode" + :lighter nil + (cond + ((and lsp-inline-completion-company-integration-mode lsp--buffer-workspaces (bound-and-true-p company-mode)) + (add-hook 'lsp-inline-completion-before-show-hook #'lsp-inline-completion--company-save-state-and-hide nil t) + (add-hook 'lsp-inline-completion-cancelled-hook #'lsp-inline-completion--company-restore-state nil t) + (unless (memq #'lsp-inline-completion-display company--begin-inhibit-commands) + (push #'lsp-inline-completion-display company--begin-inhibit-commands)) + (when (and lsp-inline-completion-mode-inhibit-when-company-active + (not (memq #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))) + (push #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))) + + (t + (remove-hook 'lsp-inline-completion-before-show-hook #'lsp-inline-completion--company-save-state-and-hide t) + (remove-hook 'lsp-inline-completion-cancelled-hook #'lsp-inline-completion--company-save-state-and-hide t) + (when (boundp 'company--begin-inhibit-commands) + (setq company--begin-inhibit-commands (delq #'lsp-inline-completion-display company--begin-inhibit-commands))) + (setq lsp-inline-completion-inhibit-predicates + (delq #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))))) + (provide 'lsp-inline-completion) From 80e210a61dc567e79599e926019d32f366f9c8ea Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 17:29:12 -0300 Subject: [PATCH 25/46] refactor: use autoload to configure inline completions mode --- lsp-inline-completion.el | 7 ++++++- lsp-mode.el | 5 ----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index a2692516715..6d9e4e79d25 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -131,7 +131,7 @@ text range that was updated by the completion" :type 'hook :group 'lsp-mode) -(defcustom lsp-inline-completion-overlay-priority (cons nil 100) +(defcustom lsp-inline-completion-overlay-priority 9000 "The priority of the overlay." :type '(choice (const :tag "No Priority" nil) (integer :tag "Simple, Overriding Priority") @@ -424,6 +424,11 @@ lsp-inline-completion-mode is active" (remove-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) +;;;###autoload +(add-hook 'lsp-configure-hook (lambda () + (when (and lsp-inline-completion-enable + (lsp-feature? "textDocument/inlineCompletion")) + (lsp-inline-completion-mode)))) ;; Company default integration diff --git a/lsp-mode.el b/lsp-mode.el index f6e3004d8ab..03bc8272909 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -57,7 +57,6 @@ (require 'minibuffer) (require 'help-mode) (require 'lsp-protocol) -(require 'lsp-inline-completion) (defgroup lsp-mode nil "Language Server Protocol client." @@ -4275,10 +4274,6 @@ yet." (lsp-feature? "textDocument/inlayHint")) (lsp-inlay-hints-mode)) - (when (and lsp-inline-completion-enable - (lsp-feature? "textDocument/inlineCompletion")) - (lsp-inline-completion-mode)) - (when (and lsp-enable-dap-auto-configure (functionp 'dap-mode)) (dap-auto-configure-mode 1))) From 3c43167d76c42e1e635e8c2ad8147fb11be43471 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 17:50:13 -0300 Subject: [PATCH 26/46] fix: ensure the callback is still executing in the context of the original buffer --- lsp-inline-completion.el | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 6d9e4e79d25..08aac642d96 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -394,8 +394,10 @@ lsp-inline-completion-mode is active" (defvar-local lsp-inline-completion--idle-timer nil "The idle timer used by lsp-inline-completion-mode") -(defun lsp-inline-completion--maybe-display () - (unless (--any (funcall it) lsp-inline-completion-inhibit-predicates) +(defun lsp-inline-completion--maybe-display (buffer) + (when (and (buffer-live-p buffer) + (eq (current-buffer) buffer) + (--none? (funcall it) lsp-inline-completion-inhibit-predicates)) (setq last-command this-command) (setq this-command 'lsp-inline-completion-display) (lsp-inline-completion-display 'implicit))) @@ -404,10 +406,12 @@ lsp-inline-completion-mode is active" (when (and lsp-inline-completion-mode lsp--buffer-workspaces) (when lsp-inline-completion--idle-timer (cancel-timer lsp-inline-completion--idle-timer)) - (setq lsp-inline-completion--idle-timer - (run-with-timer lsp-inline-completion-idle-delay - nil - #'lsp-inline-completion--maybe-display)))) + (let ((buffer (current-buffer))) + (setq lsp-inline-completion--idle-timer + (run-with-timer lsp-inline-completion-idle-delay + nil + #'lsp-inline-completion--maybe-display + buffer))))) (define-minor-mode lsp-inline-completion-mode "Mode automatically displaying inline completions." From 612e90e8ce204f0d7d50e16302c66a6061feea04 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 17:50:43 -0300 Subject: [PATCH 27/46] chore: use local variables for the company integration minor mode --- lsp-inline-completion.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 08aac642d96..7151f67db4b 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -470,17 +470,19 @@ lsp-inline-completion-mode is active" (add-hook 'lsp-inline-completion-before-show-hook #'lsp-inline-completion--company-save-state-and-hide nil t) (add-hook 'lsp-inline-completion-cancelled-hook #'lsp-inline-completion--company-restore-state nil t) (unless (memq #'lsp-inline-completion-display company--begin-inhibit-commands) + (make-variable-buffer-local 'company--begin-inhibit-commands) (push #'lsp-inline-completion-display company--begin-inhibit-commands)) (when (and lsp-inline-completion-mode-inhibit-when-company-active (not (memq #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))) + (make-variable-buffer-local 'lsp-inline-completion-inhibit-predicates) (push #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))) (t (remove-hook 'lsp-inline-completion-before-show-hook #'lsp-inline-completion--company-save-state-and-hide t) (remove-hook 'lsp-inline-completion-cancelled-hook #'lsp-inline-completion--company-save-state-and-hide t) (when (boundp 'company--begin-inhibit-commands) - (setq company--begin-inhibit-commands (delq #'lsp-inline-completion-display company--begin-inhibit-commands))) - (setq lsp-inline-completion-inhibit-predicates + (setq-local company--begin-inhibit-commands (delq #'lsp-inline-completion-display company--begin-inhibit-commands))) + (setq-local lsp-inline-completion-inhibit-predicates (delq #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))))) (provide 'lsp-inline-completion) From b19a8f127902655c5a515d18864dfc73ff077a55 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 18:06:50 -0300 Subject: [PATCH 28/46] chore: remove ht as dependency --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 7151f67db4b..724b4639994 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -4,7 +4,7 @@ ;; Author: Rodrigo Kassick ;; Keywords: languages -;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (ht "2.3") (spinner "1.7.3")) +;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (spinner "1.7.3")) ;; Version: 9.0.1 ;; URL: https://github.com/emacs-lsp/lsp-mode From 231b867d5ce7b74b54967e073035aa127ce04d5f Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 18:09:51 -0300 Subject: [PATCH 29/46] fix: variable name --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 724b4639994..6b169607f54 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -55,7 +55,7 @@ InlineCompletionItem objects" ((pred (lambda (i) (and (sequencep i) (lsp-inline-completion-item? (elt i 0))))) - (seq-into i 'list)) + (seq-into response 'list)) ;; A sequence means multiple server may have responded. Iterate over them and normalize ((pred sequencep) From 49895b70be88d6ccc40c7bee6cb5968f040e930c Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 18:13:04 -0300 Subject: [PATCH 30/46] fix: define minor mode before the functions that need to check for it --- lsp-inline-completion.el | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 6b169607f54..6fc5bc6eb7f 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -390,10 +390,23 @@ lsp-inline-completion-mode is active" :type '(repeat function) :group 'lsp-mode) - (defvar-local lsp-inline-completion--idle-timer nil "The idle timer used by lsp-inline-completion-mode") +(define-minor-mode lsp-inline-completion-mode + "Mode automatically displaying inline completions." + :lighter nil + (cond + ((and lsp-inline-completion-mode lsp--buffer-workspaces) + (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 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) + (defun lsp-inline-completion--maybe-display (buffer) (when (and (buffer-live-p buffer) (eq (current-buffer) buffer) @@ -413,21 +426,6 @@ lsp-inline-completion-mode is active" #'lsp-inline-completion--maybe-display buffer))))) -(define-minor-mode lsp-inline-completion-mode - "Mode automatically displaying inline completions." - :lighter nil - (cond - ((and lsp-inline-completion-mode lsp--buffer-workspaces) - (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 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) - - ;;;###autoload (add-hook 'lsp-configure-hook (lambda () (when (and lsp-inline-completion-enable From 312626e3f16541490857d4f8d76ff9ba8d9228d2 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 18:17:39 -0300 Subject: [PATCH 31/46] chore: add missing type to custom variable --- lsp-inline-completion.el | 1 + 1 file changed, 1 insertion(+) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 6fc5bc6eb7f..574355d1e30 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -440,6 +440,7 @@ lsp-inline-completion-mode is active" (defcustom lsp-inline-completion-mode-inhibit-when-company-active t "If the inline completion mode should avoid calling completions when company is active" + :type 'boolean :group 'lsp-mode) (defvar-local lsp-inline-completion--showing-company nil "If company was active when the tooltip is shown") From 79dd07c9797c00fbab0b9ef00e63d9e3e598eff9 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 18:28:30 -0300 Subject: [PATCH 32/46] fix: declare company--begin-inhibit-commands as a variable --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 574355d1e30..55e492ec6f9 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -437,7 +437,7 @@ lsp-inline-completion-mode is active" (declare-function company--active-p "ext:company") (declare-function company-cancel "ext:company" (&optional result)) (declare-function company-manual-begin "ext:company") - +(defvar company--begin-inhibit-commands) (defcustom lsp-inline-completion-mode-inhibit-when-company-active t "If the inline completion mode should avoid calling completions when company is active" :type 'boolean From e20df2734784d2262ce2756f4501f6fc38356e3b Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 18:33:37 -0300 Subject: [PATCH 33/46] chore: do not use make-variable-buffer-local --- lsp-inline-completion.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 55e492ec6f9..36bb3ea5e7d 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -469,12 +469,12 @@ lsp-inline-completion-mode is active" (add-hook 'lsp-inline-completion-before-show-hook #'lsp-inline-completion--company-save-state-and-hide nil t) (add-hook 'lsp-inline-completion-cancelled-hook #'lsp-inline-completion--company-restore-state nil t) (unless (memq #'lsp-inline-completion-display company--begin-inhibit-commands) - (make-variable-buffer-local 'company--begin-inhibit-commands) - (push #'lsp-inline-completion-display company--begin-inhibit-commands)) + (setq-local company--begin-inhibit-commands + (cons #'lsp-inline-completion-display company--begin-inhibit-commands))) (when (and lsp-inline-completion-mode-inhibit-when-company-active (not (memq #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))) - (make-variable-buffer-local 'lsp-inline-completion-inhibit-predicates) - (push #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))) + (setq-local lsp-inline-completion-inhibit-predicates + (cons #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates)))) (t (remove-hook 'lsp-inline-completion-before-show-hook #'lsp-inline-completion--company-save-state-and-hide t) From f2b38e0671c4256b17e70e269ce0e17e1dfb21fd Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Wed, 27 Nov 2024 18:36:37 -0300 Subject: [PATCH 34/46] fix: use cl-map to make the compiler happy --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 36bb3ea5e7d..d633557e82f 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -59,7 +59,7 @@ InlineCompletionItem objects" ;; A sequence means multiple server may have responded. Iterate over them and normalize ((pred sequencep) - (let ((item-seq (map 'list #'lsp-inline-completion--parse-items response))) + (let ((item-seq (cl-map 'list #'lsp-inline-completion--parse-items response))) (apply 'seq-concatenate `(list ,@item-seq)))))) ;;;;;; Default UI -- overlay From 08c4c8dba4b7498057ea12539aaae8cd328f7d0b Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 11:23:11 -0300 Subject: [PATCH 35/46] fix: ensure no state changes before triggering inline completion lsp-inline-completion--after-change, added to lsp on-change hooks, is triggered on a timer by `lsp--after-change`. The inline completion maybe-display function is also triggered on a timer and it must check if the position and current buffer did not change since the change as actually occurred. We can not store this state in lsp-inline-completion--after-change because the user may have clicked somewhere before the timer activated it. This commit introduces a `lsp--after-change-vals` plist to store the context before the timers have been started. lsp-inline-completion--after-change then fetches these values and forward as arguments to the timer invocation of maybe-display, which then ensures that the state has not changed. --- lsp-inline-completion.el | 25 +++++++++++++++++-------- lsp-mode.el | 9 +++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index d633557e82f..30b8b63f6a2 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -407,24 +407,33 @@ lsp-inline-completion-mode is active" (remove-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) -(defun lsp-inline-completion--maybe-display (buffer) - (when (and (buffer-live-p buffer) - (eq (current-buffer) buffer) +(defun lsp-inline-completion--maybe-display (original-buffer original-point) + ;; This is executed on an idle timer -- ensure state did not change before + ;; displaying + (when (and (buffer-live-p original-buffer) + (eq (current-buffer) original-buffer) + (eq (point) original-point) (--none? (funcall it) lsp-inline-completion-inhibit-predicates)) (setq last-command this-command) (setq this-command 'lsp-inline-completion-display) (lsp-inline-completion-display 'implicit))) (defun lsp-inline-completion--after-change (&rest _) + ;; This function is in lsp-on-change-hooks, which is executed on a timer by + ;; lsp-on-change. Do not assume that the buffer/window state has not been + ;; modified in the meantime! Use the values in lsp--after-change-vals to + ;; ensure this. (when (and lsp-inline-completion-mode lsp--buffer-workspaces) (when lsp-inline-completion--idle-timer (cancel-timer lsp-inline-completion--idle-timer)) - (let ((buffer (current-buffer))) + (let ((original-buffer (plist-get lsp--after-change-vals :buffer)) + (original-point (plist-get lsp--after-change-vals :point))) (setq lsp-inline-completion--idle-timer - (run-with-timer lsp-inline-completion-idle-delay - nil - #'lsp-inline-completion--maybe-display - buffer))))) + (run-with-idle-timer lsp-inline-completion-idle-delay + nil + #'lsp-inline-completion--maybe-display + original-buffer + original-point))))) ;;;###autoload (add-hook 'lsp-configure-hook (lambda () diff --git a/lsp-mode.el b/lsp-mode.el index 03bc8272909..f8f305ea22b 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -4950,6 +4950,12 @@ Added to `after-change-functions'." lsp-managed-mode) (run-hooks 'lsp-on-change-hook))) + +(defvar-local lsp--after-change-vals nil + "plist that stores the buffer state when `lsp--after-change' has ben activated. Since the +functions in `lsp-on-change-hook' are called with a timer, mouse +movements may have changed the position") + (defun lsp--after-change (buffer) "Called after most textDocument/didChange events." (setq lsp--signature-last-index nil @@ -4965,6 +4971,9 @@ Added to `after-change-functions'." (lsp--semantic-tokens-refresh-if-enabled buffer)) (when lsp--on-change-timer (cancel-timer lsp--on-change-timer)) + + (setq lsp--after-change-vals (list :point (point) + :buffer (current-buffer))) (setq lsp--on-change-timer (run-with-idle-timer lsp-idle-delay nil From c56027523a499c7cf544e25e29786ca7d34089fe Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 11:28:29 -0300 Subject: [PATCH 36/46] chore: no spinner on implicit inline completions It is a distraction. --- lsp-inline-completion.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 30b8b63f6a2..5810c7d6ef2 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -353,7 +353,9 @@ text range that was updated by the completion" "Displays the inline completions overlay" (interactive) - (lsp--spinner-start) + (unless implicit + (lsp--spinner-start) ) + (unwind-protect (if-let* ((resp (lsp-request-while-no-input "textDocument/inlineCompletion" (lsp-inline-completion--params implicit))) @@ -368,7 +370,8 @@ text range that was updated by the completion" (unless implicit (message "No Suggestions!"))) ;; Clean up - (lsp--spinner-stop))) + (unless implicit + (lsp--spinner-stop)))) ;; Inline Completion Mode From 3eeb087b21d2193a35f09a7e9e6a770805729831 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 11:28:50 -0300 Subject: [PATCH 37/46] chore: always cancel timer --- lsp-inline-completion.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 5810c7d6ef2..c6b7384a23b 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -426,9 +426,11 @@ lsp-inline-completion-mode is active" ;; lsp-on-change. Do not assume that the buffer/window state has not been ;; modified in the meantime! Use the values in lsp--after-change-vals to ;; ensure this. + + (when lsp-inline-completion--idle-timer + (cancel-timer lsp-inline-completion--idle-timer)) + (when (and lsp-inline-completion-mode lsp--buffer-workspaces) - (when lsp-inline-completion--idle-timer - (cancel-timer lsp-inline-completion--idle-timer)) (let ((original-buffer (plist-get lsp--after-change-vals :buffer)) (original-point (plist-get lsp--after-change-vals :point))) (setq lsp-inline-completion--idle-timer From 509f8ee9434507c438450b1f67f8fe5788a74e6d Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 11:32:33 -0300 Subject: [PATCH 38/46] chore: autoload keymap --- lsp-inline-completion.el | 1 + 1 file changed, 1 insertion(+) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index c6b7384a23b..3a1683bb9c2 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -64,6 +64,7 @@ InlineCompletionItem objects" ;;;;;; Default UI -- overlay +;;;###autoload (defvar lsp-inline-completion-active-map (let ((map (make-sparse-keymap))) (define-key map (kbd "") #'lsp-inline-completion-next) From 69e2fa3079bd43eebf86c3607ec2bda1096d1448 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 11:34:43 -0300 Subject: [PATCH 39/46] chore: always show keys/status during active completion --- lsp-inline-completion.el | 1 + 1 file changed, 1 insertion(+) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 3a1683bb9c2..cfdd8b7e6e2 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -247,6 +247,7 @@ text range that was updated by the completion" (goto-char target-position) + (lsp-inline-completion-show-keys) (run-hooks 'lsp-inline-completion-shown-hook))) (defun lsp-inline-completion-accept () From 8a5dc5ce915bbd76942d2aa7167d920fdb6dae51 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 11:35:22 -0300 Subject: [PATCH 40/46] fix: autoload minor modes --- lsp-inline-completion.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index cfdd8b7e6e2..cb1ef098b0d 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -398,6 +398,7 @@ lsp-inline-completion-mode is active" (defvar-local lsp-inline-completion--idle-timer nil "The idle timer used by lsp-inline-completion-mode") +;;;###autoload (define-minor-mode lsp-inline-completion-mode "Mode automatically displaying inline completions." :lighter nil @@ -477,6 +478,7 @@ lsp-inline-completion-mode is active" (defun lsp-inline-completion--company-active-p () (and (bound-and-true-p company-mode) (company--active-p))) +;;;###autoload (define-minor-mode lsp-inline-completion-company-integration-mode "Minor mode to be used when company mode is active with lsp-inline-completion-mode" :lighter nil From 86abb871fdb52ade3316497e261544741ddc692e Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 11:36:14 -0300 Subject: [PATCH 41/46] chore: fix indentation Auto-substitution of tabs to space did not use the correct number of spaces --- lsp-mode.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lsp-mode.el b/lsp-mode.el index f8f305ea22b..3bb4478a90a 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -5439,9 +5439,9 @@ If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." (lsp-help-mode) (with-help-window lsp-help-buf-name (insert - (mapconcat 'string-trim-right - (split-string (lsp--render-on-hover-content contents t) "\n") - "\n")))) + (mapconcat 'string-trim-right + (split-string (lsp--render-on-hover-content contents t) "\n") + "\n")))) (run-mode-hooks))) (lsp--info "No content at point.")))) From a4243c2fc1da768d9e008f67f36b6eed35ee53ed Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 11:41:59 -0300 Subject: [PATCH 42/46] chore: lsp--info when explicit completion request did not return --- lsp-inline-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index cb1ef098b0d..43cdde5f6d8 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -370,7 +370,7 @@ text range that was updated by the completion" (setq lsp-inline-completion--start-point (point)) (lsp-inline-completion-show-overlay)) (unless implicit - (message "No Suggestions!"))) + (lsp--info "No Suggestions!"))) ;; Clean up (unless implicit (lsp--spinner-stop)))) From 2b582bd2c46ba50f181fafdbff724e0563e34e7b Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 18:22:48 -0300 Subject: [PATCH 43/46] chore: make show-keys private --- lsp-inline-completion.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 43cdde5f6d8..75063a8acaf 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -166,7 +166,7 @@ text range that was updated by the completion" lsp-inline-completion--overlay) -(defun lsp-inline-completion-show-keys () +(defun lsp-inline-completion--show-keys () "Shows active keymap hints in the minibuffer" (unless (and lsp-inline-completion--items @@ -247,7 +247,7 @@ text range that was updated by the completion" (goto-char target-position) - (lsp-inline-completion-show-keys) + (lsp-inline-completion--show-keys) (run-hooks 'lsp-inline-completion-shown-hook))) (defun lsp-inline-completion-accept () From 13f340c62d04fb2dec519c803cc6c2a0d7bfa6a2 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 19:18:05 -0300 Subject: [PATCH 44/46] chore: update default keymap --- lsp-inline-completion.el | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index 75063a8acaf..dcd181db812 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -67,23 +67,23 @@ InlineCompletionItem objects" ;;;###autoload (defvar lsp-inline-completion-active-map (let ((map (make-sparse-keymap))) - (define-key map (kbd "") #'lsp-inline-completion-next) + ;; accept + (define-key map (kbd "C-") #'lsp-inline-completion-accept) + (define-key map [mouse-1] #'lsp-inline-completion-accept) + ;; navigate (define-key map (kbd "C-n") #'lsp-inline-completion-next) - (define-key map (kbd "M-n") #'lsp-inline-completion-next) (define-key map (kbd "C-p") #'lsp-inline-completion-prev) - (define-key map (kbd "M-p") #'lsp-inline-completion-prev) - (define-key map (kbd "") #'lsp-inline-completion-accept) - (define-key map [down-mouse-1] #'ignore) - (define-key map [down-mouse-3] #'ignore) - (define-key map [up-mouse-1] #'ignore) - (define-key map [up-mouse-3] #'ignore) - (define-key map [mouse-1] #'lsp-inline-completion-accept) - (define-key map [mouse-3] #'lsp-inline-completion-accept) - (define-key map (kbd "C-l") #'recenter-top-bottom) + ;; 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) + ;; useful -- recenter without loosing the completion + (define-key map (kbd "C-l") #'recenter-top-bottom) + ;; ignore + (define-key map [down-mouse-1] #'ignore) + (define-key map [up-mouse-1] #'ignore) (define-key map [mouse-movement] #'ignore) + ;; 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") @@ -313,8 +313,7 @@ text range that was updated by the completion" (when lsp-inline-completion--start-point (goto-char lsp-inline-completion--start-point)) - (run-hooks 'lsp-inline-completion-cancelled-hook) - )) + (run-hooks 'lsp-inline-completion-cancelled-hook))) (defun lsp-inline-completion-cancel-with-input (event &optional arg) "Cancel the inline completion and executes whatever event was received" From b4cff81acb276ea106415c6ab8eaf9d317a48ff2 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Thu, 28 Nov 2024 20:51:32 -0300 Subject: [PATCH 45/46] feat: on click, place the cursor at the correct point after completion --- lsp-inline-completion.el | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index dcd181db812..d0ff76a57f9 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -31,6 +31,7 @@ (require 'lsp-protocol) (require 'dash) (require 'cl-lib) +(require 'fringe) (defun lsp-inline-completion--params (implicit &optional identifier position) "Returns a InlineCompletionParams instance" @@ -69,7 +70,7 @@ InlineCompletionItem objects" (let ((map (make-sparse-keymap))) ;; accept (define-key map (kbd "C-") #'lsp-inline-completion-accept) - (define-key map [mouse-1] #'lsp-inline-completion-accept) + (define-key map [mouse-1] #'lsp-inline-completion-accept-on-click) ;; navigate (define-key map (kbd "C-n") #'lsp-inline-completion-next) (define-key map (kbd "C-p") #'lsp-inline-completion-prev) @@ -303,6 +304,18 @@ text range that was updated by the completion" ;; hooks (run-hook-with-args-until-failure 'lsp-inline-completion-accepted-hook text text-insert-start text-insert-end))) +(defun lsp-inline-completion-accept-on-click (event) + (interactive "e") + + (lsp-inline-completion-accept) + (-let (((col . row) (posn-actual-col-row (event-end event)))) + (move-to-window-line row) + (beginning-of-line) + (forward-char (- col + (if (bound-and-true-p display-line-numbers-mode) + (+ 2 (line-number-display-width)) + 0))))) + (defun lsp-inline-completion-cancel () "Close the suggestion overlay" (interactive) From a109f34b6bbebf5ff13ededa749119bc30163f00 Mon Sep 17 00:00:00 2001 From: Rodrigo Kassick Date: Fri, 29 Nov 2024 10:39:17 -0300 Subject: [PATCH 46/46] chore: make suggestion insertion undoable --- lsp-inline-completion.el | 75 +++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el index d0ff76a57f9..fffda85bb12 100644 --- a/lsp-inline-completion.el +++ b/lsp-inline-completion.el @@ -33,6 +33,32 @@ (require 'cl-lib) (require 'fringe) +(if (version< emacs-version "29.1") + ;; Undo macro probably introduced in 29.1 + (defmacro lsp-inline-completion--with-undo-amalgamate (&rest body) + "Like `progn' but perform BODY with amalgamated undo barriers. + +This allows multiple operations to be undone in a single step. +When undo is disabled this behaves like `progn'." + (declare (indent 0) (debug t)) + (let ((handle (make-symbol "--change-group-handle--"))) + `(let ((,handle (prepare-change-group)) + ;; Don't truncate any undo data in the middle of this, + ;; otherwise Emacs might truncate part of the resulting + ;; undo step: we want to mimic the behavior we'd get if the + ;; undo-boundaries were never added in the first place. + (undo-outer-limit nil) + (undo-limit most-positive-fixnum) + (undo-strong-limit most-positive-fixnum)) + (unwind-protect + (progn + (activate-change-group ,handle) + ,@body) + (progn + (accept-change-group ,handle) + (undo-amalgamate-change-group ,handle)))))) + (defalias 'lsp-inline-completion--with-undo-amalgamate 'with-undo-amalgamate)) + (defun lsp-inline-completion--params (implicit &optional identifier position) "Returns a InlineCompletionParams instance" (lsp-make-inline-completion-params @@ -251,27 +277,12 @@ text range that was updated by the completion" (lsp-inline-completion--show-keys) (run-hooks 'lsp-inline-completion-shown-hook))) -(defun lsp-inline-completion-accept () - "Accepts the current suggestion" - (interactive) - (unless (lsp-inline-completion--overlay-visible) - (error "Not showing suggestions")) - - (lsp-inline-completion--clear-overlay) - (-let* ((suggestion (elt lsp-inline-completion--items lsp-inline-completion--current)) - ((&InlineCompletionItem? :insert-text :range? :command?) suggestion) - ((kind . text) (cond - ((lsp-markup-content? insert-text) - (cons 'snippet (lsp:markup-content-value insert-text) )) - (t (cons 'text insert-text)))) - ((start . end) (when range? - (-let (((&RangeToPoint :start :end) range?)) (cons start end)))) - (text-insert-start (or start lsp-inline-completion--start-point)) - text-insert-end - (completion-is-substr (string-equal - (buffer-substring text-insert-start lsp-inline-completion--start-point) - (substring text 0 (- lsp-inline-completion--start-point text-insert-start))))) - +(defun lsp-inline-completion--insert-sugestion (text kind start end command?) + (let* ((text-insert-start (or start lsp-inline-completion--start-point)) + text-insert-end + (completion-is-substr (string-equal + (buffer-substring text-insert-start lsp-inline-completion--start-point) + (substring text 0 (- lsp-inline-completion--start-point text-insert-start))))) (when text-insert-start (goto-char text-insert-start)) @@ -282,6 +293,7 @@ text range that was updated by the completion" ;; Insert suggestion, keeping the cursor at the start point (insert text) + (setq text-insert-end (point)) ;; If a template, format it -- keep track of the end position! @@ -304,6 +316,27 @@ text range that was updated by the completion" ;; hooks (run-hook-with-args-until-failure 'lsp-inline-completion-accepted-hook text text-insert-start text-insert-end))) +(defun lsp-inline-completion-accept () + "Accepts the current suggestion" + (interactive) + (unless (lsp-inline-completion--overlay-visible) + (error "Not showing suggestions")) + + (lsp-inline-completion--clear-overlay) + (-let* ((suggestion (elt lsp-inline-completion--items lsp-inline-completion--current)) + ((&InlineCompletionItem? :insert-text :range? :command?) suggestion) + ((kind . text) (cond + ((lsp-markup-content? insert-text) + (cons 'snippet (lsp:markup-content-value insert-text) )) + (t (cons 'text insert-text)))) + ((start . end) (when range? + (-let (((&RangeToPoint :start :end) range?)) (cons start end))))) + + (with-no-warnings + ;; Compiler does not believes this macro is defined + (lsp-inline-completion--with-undo-amalgamate + (lsp-inline-completion--insert-sugestion text kind start end command?))))) + (defun lsp-inline-completion-accept-on-click (event) (interactive "e")