From 836127f39447fb93ffc9f2af2d0b0abbc2b4f824 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Sat, 20 Jun 2020 21:47:04 -0700 Subject: [PATCH] Ensure completion terminates in correct buffer MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 * 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 | 86 ++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index c0f3143ba14..0b233377b12 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -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))) -- 2.39.2