Skip to content

Commit

Permalink
lsp-completion: support async detail completion resolve and more lazy…
Browse files Browse the repository at this point in the history
… properties (#4610)
  • Loading branch information
kiennq authored Nov 13, 2024
1 parent e178115 commit 5991c60
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 58 deletions.
135 changes: 78 additions & 57 deletions lsp-completion.el
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,76 @@ This will help minimize popup flickering issue in `company-mode'."
'lsp-completion-markers markers
'lsp-completion-prefix prefix)))

(defun lsp-completion--fix-resolve-data (item)
"Patch `CompletionItem' ITEM for rust-analyzer otherwise resolve will fail.
See #2675"
(let ((data (lsp:completion-item-data? item)))
(when (lsp-member? data :import_for_trait_assoc_item)
(unless (lsp-get data :import_for_trait_assoc_item)
(lsp-put data :import_for_trait_assoc_item :json-false)))))

(defun lsp-completion--resolve (item)
"Resolve completion ITEM.
ITEM can be string or a CompletionItem"
(cl-assert item nil "Completion item must not be nil")
(-let (((completion-item . resolved)
(pcase item
((pred stringp) (cons (get-text-property 0 'lsp-completion-item item)
(get-text-property 0 'lsp-completion-resolved item)))
(_ (cons item nil)))))
(if resolved item
(lsp-completion--fix-resolve-data completion-item)
(setq completion-item
(or (ignore-errors
(when (lsp-feature? "completionItem/resolve")
(lsp-request "completionItem/resolve"
(lsp-delete (lsp-copy completion-item) :_emacsStartPoint))))
completion-item))
(pcase item
((pred stringp)
(let ((len (length item)))
(put-text-property 0 len 'lsp-completion-item completion-item item)
(put-text-property 0 len 'lsp-completion-resolved t item)
item))
(_ completion-item)))))

(defun lsp-completion--resolve-async (item callback &optional cleanup-fn)
"Resolve completion ITEM asynchronously with CALLBACK.
The CLEANUP-FN will be called to cleanup."
(cl-assert item nil "Completion item must not be nil")
(-let (((completion-item . resolved)
(pcase item
((pred stringp) (cons (get-text-property 0 'lsp-completion-item item)
(get-text-property 0 'lsp-completion-resolved item)))
(_ (cons item nil)))))
(ignore-errors
(if (and (lsp-feature? "completionItem/resolve") (not resolved))
(progn
(lsp-completion--fix-resolve-data completion-item)
(lsp-request-async "completionItem/resolve"
(lsp-delete (lsp-copy completion-item) :_emacsStartPoint)
(lambda (completion-item)
(when (stringp item)
(let ((len (length item)))
(put-text-property 0 len 'lsp-completion-item completion-item item)
(put-text-property 0 len 'lsp-completion-resolved t item)
item))
(funcall callback completion-item)
(when cleanup-fn (funcall cleanup-fn)))
:error-handler (lambda (err)
(when cleanup-fn (funcall cleanup-fn))
(error (lsp:json-error-message err)))
:cancel-handler cleanup-fn
:mode 'alive))
(funcall callback completion-item)
(when cleanup-fn (funcall cleanup-fn))))))

(defun lsp-completion--annotate (item)
"Annotate ITEM detail."
(-let (((&CompletionItem :detail? :kind? :label-details?) (plist-get (text-properties-at 0 item)
'lsp-completion-item)))
(-let (((completion-item &as &CompletionItem :detail? :kind? :label-details?)
(get-text-property 0 'lsp-completion-item item)))
(lsp-completion--resolve-async item #'ignore)

(concat (when (and lsp-completion-show-detail detail?)
(concat " " (s-replace "\r" "" detail?)))
(when (and lsp-completion-show-label-description label-details?)
Expand Down Expand Up @@ -388,15 +454,8 @@ The MARKERS and PREFIX value will be attached to each candidate."

(defun lsp-completion--get-documentation (item)
"Get doc comment for completion ITEM."
(unless (get-text-property 0 'lsp-completion-resolved item)
(let ((resolved-item
(-some->> item
(get-text-property 0 'lsp-completion-item)
(lsp-completion--resolve)))
(len (length item)))
(put-text-property 0 len 'lsp-completion-item resolved-item item)
(put-text-property 0 len 'lsp-completion-resolved t item)))
(-some->> item
(lsp-completion--resolve)
(get-text-property 0 'lsp-completion-item)
(lsp:completion-item-documentation?)
(lsp--render-element)))
Expand Down Expand Up @@ -556,6 +615,12 @@ Others: CANDIDATES"
'lsp-completion-item)
candidate
(cl-find candidate (funcall candidates) :test #'equal)))
(candidate
;; see #3498 typescript-language-server does not provide the
;; proper insertText without resolving.
(if (lsp-completion--find-workspace 'ts-ls)
(lsp-completion--resolve candidate)
candidate))
((&plist 'lsp-completion-item item
'lsp-completion-start-point start-point
'lsp-completion-markers markers
Expand All @@ -564,12 +629,7 @@ Others: CANDIDATES"
(text-properties-at 0 candidate))
((&CompletionItem? :label :insert-text? :text-edit? :insert-text-format?
:additional-text-edits? :insert-text-mode? :command?)
;; see #3498 typescript-language-server does not provide the
;; proper insertText without resolving.
(if (and (lsp-completion--find-workspace 'ts-ls)
(not resolved))
(lsp-completion--resolve item)
item)))
item))
(cond
(text-edit?
(apply #'delete-region markers)
Expand Down Expand Up @@ -597,7 +657,7 @@ Others: CANDIDATES"
(point)))

(when lsp-completion-enable-additional-text-edit
(if (or (get-text-property 0 'lsp-completion-resolved candidate)
(if (or resolved
(not (seq-empty-p additional-text-edits?)))
(lsp--apply-text-edits additional-text-edits? 'completion)
(-let [(callback cleanup-fn) (lsp--create-apply-text-edits-handlers)]
Expand All @@ -606,8 +666,7 @@ Others: CANDIDATES"
(-compose callback #'lsp:completion-item-additional-text-edits?)
cleanup-fn))))

(if (or (get-text-property 0 'lsp-completion-resolved candidate)
command?)
(if (or resolved command?)
(when command? (lsp--execute-command command?))
(lsp-completion--resolve-async
item
Expand Down Expand Up @@ -705,44 +764,6 @@ The return is nil or in range of (0, inf)."
(unless (zerop len)
(/ score-numerator (1+ score-denominator) 1.0))))

(defun lsp-completion--fix-resolve-data (item)
"Patch `CompletionItem' ITEM for rust-analyzer otherwise resolve will fail.
See #2675"
(let ((data (lsp:completion-item-data? item)))
(when (lsp-member? data :import_for_trait_assoc_item)
(unless (lsp-get data :import_for_trait_assoc_item)
(lsp-put data :import_for_trait_assoc_item :json-false)))))

(defun lsp-completion--resolve (item)
"Resolve completion ITEM."
(cl-assert item nil "Completion item must not be nil")
(lsp-completion--fix-resolve-data item)
(or (ignore-errors
(when (lsp-feature? "completionItem/resolve")
(lsp-request "completionItem/resolve"
(lsp-delete (lsp-copy item) :_emacsStartPoint))))
item))

(defun lsp-completion--resolve-async (item callback &optional cleanup-fn)
"Resolve completion ITEM asynchronously with CALLBACK.
The CLEANUP-FN will be called to cleanup."
(cl-assert item nil "Completion item must not be nil")
(lsp-completion--fix-resolve-data item)
(ignore-errors
(if (lsp-feature? "completionItem/resolve")
(lsp-request-async "completionItem/resolve"
(lsp-delete (lsp-copy item) :_emacsStartPoint)
(lambda (result)
(funcall callback result)
(when cleanup-fn (funcall cleanup-fn)))
:error-handler (lambda (err)
(when cleanup-fn (funcall cleanup-fn))
(error (lsp:json-error-message err)))
:cancel-handler cleanup-fn
:mode 'alive)
(funcall callback item)
(when cleanup-fn (funcall cleanup-fn)))))


;;;###autoload
(defun lsp-completion--enable ()
Expand Down
4 changes: 3 additions & 1 deletion lsp-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -3773,7 +3773,9 @@ disappearing, unset all the variables related to it."
. ((properties . ["documentation"
"detail"
"additionalTextEdits"
"command"])))
"command"
"insertTextFormat"
"insertTextMode"])))
(insertTextModeSupport . ((valueSet . [1 2])))))
(contextSupport . t)
(dynamicRegistration . t)))
Expand Down

0 comments on commit 5991c60

Please sign in to comment.