]> git.eshelyaron.com Git - emacs.git/commitdiff
Ensure completion terminates in correct buffer
authorAndrew Schwartzmeyer <andrew@schwartzmeyer.com>
Sun, 21 Jun 2020 04:47:04 +0000 (21:47 -0700)
committerJoão Távora <joaotavora@gmail.com>
Mon, 27 Jul 2020 09:11:44 +0000 (10:11 +0100)
To design a completion-in-region-function replacement that leverages
the elements in completion-at-point-functions, we must ensure that
their :exit-function parts execute in the correct buffer.  That is the
buffer where the text to be completed lives, not necessarily the
buffer being used for user interaction.

Later on, this guarantee should be provided by Emacs itself, perhaps
by putting the correct with-current-buffer call in completion--done.

Copyright-paperwork-exempt: yes
Co-authored-by: João Távora <joaotavora@gmail.com>
* eglot.el (eglot-completion-at-point): Ensure :exit-function's
buffer is where the source is.

GitHub-reference: close https://github.com/joaotavora/eglot/issues/505

lisp/progmodes/eglot.el

index c0f3143ba143d9b0c6fc4047d724852fd25c313c..0b233377b1224a3e2cc0b105fc60a0ae02cdcd3f 100644 (file)
@@ -2175,45 +2175,53 @@ is not active."
             (line-beginning-position))))
        :exit-function
        (lambda (proxy _status)
-         (eglot--dbind ((CompletionItem) insertTextFormat
-                        insertText textEdit additionalTextEdits label)
-             (funcall
-              resolve-maybe
-              (or (get-text-property 0 'eglot--lsp-item proxy)
-                        ;; When selecting from the *Completions*
-                        ;; buffer, `proxy' won't have any properties.
-                        ;; A lookup should fix that (github#148)
-                        (get-text-property
-                         0 'eglot--lsp-item
-                         (cl-find proxy (funcall proxies) :test #'string=))))
-           (let ((snippet-fn (and (eql insertTextFormat 2)
-                                  (eglot--snippet-expansion-fn))))
-             (cond (textEdit
-                    ;; Undo (yes, undo) the newly inserted completion.
-                    ;; If before completion the buffer was "foo.b" and
-                    ;; now is "foo.bar", `proxy' will be "bar".  We
-                    ;; want to delete only "ar" (`proxy' minus the
-                    ;; symbol whose bounds we've calculated before)
-                    ;; (github#160).
-                    (delete-region (+ (- (point) (length proxy))
-                                      (if bounds (- (cdr bounds) (car bounds)) 0))
-                                   (point))
-                    (eglot--dbind ((TextEdit) range newText) textEdit
-                      (pcase-let ((`(,beg . ,end) (eglot--range-region range)))
-                        (delete-region beg end)
-                        (goto-char beg)
-                        (funcall (or snippet-fn #'insert) newText)))
-                    (when (cl-plusp (length additionalTextEdits))
-                      (eglot--apply-text-edits additionalTextEdits)))
-                   (snippet-fn
-                    ;; A snippet should be inserted, but using plain
-                    ;; `insertText'.  This requires us to delete the
-                    ;; whole completion, since `insertText' is the full
-                    ;; completion's text.
-                    (delete-region (- (point) (length proxy)) (point))
-                    (funcall snippet-fn (or insertText label)))))
-           (eglot--signal-textDocument/didChange)
-           (eldoc)))))))
+         ;; To assist in using this whole `completion-at-point'
+         ;; function inside `completion-in-region', ensure the exit
+         ;; function runs in the buffer where the completion was
+         ;; triggered from.  This should probably be in Emacs itself.
+         ;; (github#505)
+         (with-current-buffer (if (minibufferp)
+                                  (window-buffer (minibuffer-selected-window))
+                                (current-buffer))
+           (eglot--dbind ((CompletionItem) insertTextFormat
+                          insertText textEdit additionalTextEdits label)
+               (funcall
+                resolve-maybe
+                (or (get-text-property 0 'eglot--lsp-item proxy)
+                    ;; When selecting from the *Completions*
+                    ;; buffer, `proxy' won't have any properties.
+                    ;; A lookup should fix that (github#148)
+                    (get-text-property
+                     0 'eglot--lsp-item
+                     (cl-find proxy (funcall proxies) :test #'string=))))
+             (let ((snippet-fn (and (eql insertTextFormat 2)
+                                    (eglot--snippet-expansion-fn))))
+               (cond (textEdit
+                      ;; Undo (yes, undo) the newly inserted completion.
+                      ;; If before completion the buffer was "foo.b" and
+                      ;; now is "foo.bar", `proxy' will be "bar".  We
+                      ;; want to delete only "ar" (`proxy' minus the
+                      ;; symbol whose bounds we've calculated before)
+                      ;; (github#160).
+                      (delete-region (+ (- (point) (length proxy))
+                                        (if bounds (- (cdr bounds) (car bounds)) 0))
+                                     (point))
+                      (eglot--dbind ((TextEdit) range newText) textEdit
+                        (pcase-let ((`(,beg . ,end) (eglot--range-region range)))
+                          (delete-region beg end)
+                          (goto-char beg)
+                          (funcall (or snippet-fn #'insert) newText)))
+                      (when (cl-plusp (length additionalTextEdits))
+                        (eglot--apply-text-edits additionalTextEdits)))
+                     (snippet-fn
+                      ;; A snippet should be inserted, but using plain
+                      ;; `insertText'.  This requires us to delete the
+                      ;; whole completion, since `insertText' is the full
+                      ;; completion's text.
+                      (delete-region (- (point) (length proxy)) (point))
+                      (funcall snippet-fn (or insertText label)))))
+             (eglot--signal-textDocument/didChange)
+             (eldoc))))))))
 
 (defun eglot--hover-info (contents &optional range)
   (let ((heading (and range (pcase-let ((`(,beg . ,end) (eglot--range-region range)))