-
-
Notifications
You must be signed in to change notification settings - Fork 904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement simplified capf mode #3825
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -37,6 +37,7 @@ | |||||
"The completion backend provider." | ||||||
:type '(choice | ||||||
(const :tag "Use company-capf" :capf) | ||||||
(const :tag "Use simplified capf" :corfu-capf) | ||||||
(const :tag "None" :none)) | ||||||
:group 'lsp-completion | ||||||
:package-version '(lsp-mode . "7.0.1")) | ||||||
|
@@ -159,10 +160,18 @@ This will help minimize popup flickering issue in `company-mode'." | |||||
(cl-defun lsp-completion--make-item (item &key markers prefix) | ||||||
"Make completion item from lsp ITEM and with MARKERS and PREFIX." | ||||||
(-let (((&CompletionItem :label | ||||||
:filter-text? | ||||||
:sort-text? | ||||||
:_emacsStartPoint start-point) | ||||||
item)) | ||||||
(propertize label | ||||||
;; In :corfu-capf mode, the filtering is done by completion styles | ||||||
;; which work only with the candidate text. As some LSP servers | ||||||
;; return candidates with simplified labels ("var" for "$var", | ||||||
;; "Schema" for "\mysql_xdevapi\Schema"), prefer filter text as | ||||||
;; label. As completion is filtering, this does makes sense. | ||||||
(propertize (or (unless (or (equal lsp-completion-provider :corfu-capf) | ||||||
(lsp-falsy? filter-text?)) filter-text?) | ||||||
label) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could also use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I took another look at the lsp capf. We should better change this, such that filter-text is used for filtering. Instead of calling Lines 513 to 514 in e5a6274
We should handle the actions separately ( Iirc I've suggested this a while ago, but then we went with the simpler solution of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, but if we use the Eglot approach we will still get the wrong results with completion styles like flex/substring/orderless due to completion-regexp-list which is respected by all-completions. This means there is also a bug in Eglot here. The correct approach is this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The The reason why the completion candidates are filtered before is because the prefix of each candidate can be different and can be completely different from what Emacs expect. An example is Java decoration operator or JavaScript's member - see the attached example. The Now if we blindly apply the candidate based on Emacs's symbol expectation, that will lead to some bugs (we have bug reports for them already). I would say, this is a quirk of integrating LSP completion with Emacs completion. To resolve that problem, we need to do the filtering with the prefix that the language server expected, which means doing it before passing back to Emacs filtering, via Now if you want to support all completion styles, what you can do is to override As for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'll just grab this first. How does one set this up? I've tried
Which as far as I can see wraps (after the initial popup, no further filtering is done). Typing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @xendk yes. IOW there is no guarantee that the server uses one single point for starting the completion. Note that there might be more than one servers running in the buffer too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
FTR initially we were performing the filtering in emacs and we did no filtering on lsp-mode side. Later the bug reports came and we had to move the filtering on lsp-mode side - we decided to work correctly instead of being a good citizen and returning the wrong set of results. If we do second filtering it might cut off the items with miscalculated prefixes. IMHO in order for the whole thing to work the filtering strategy should be passed to the provider instead (not sure that such a thing can be achieved). But that I guess is not in the scope.
Not sure if we will do better than what we currently have because we already have a cache locally in lsp-mode. In general, I am fine with putting a less precise implementation of the completion for the sake of being integrated better with emacs tools but the limitations should be well documented. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yyoncho Good to know the history of the developments. Likely, I would have made the same design choices as you did.
In fact, we can. If you use Corfu this is already the case. Note that the caching happens on the side of Corfu.
Okay, I will prepare a PR when I find time. To be 100% clear and to avoid any misunderstandings: The patch I am going to create will preserve the existing filtering if lsp-passthrough is configured. There won't be a regression, no loss in precision. Everything will just behave exactly as before in the default lsp-completion configuration. The difference will only be observable if you configure a different completion style and if the server attaches filter-text to the candidates. Also the patch shouldn't add significant complexity to the existing completion function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Any progress? I don't understand enough of this to take a stab at it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @minad Could you spare some time for this one? This PR might halfway work, but your idea seemed better. |
||||||
'lsp-completion-item item | ||||||
'lsp-sort-text sort-text? | ||||||
'lsp-completion-start-point start-point | ||||||
|
@@ -757,40 +766,43 @@ TABLE PRED" | |||||
;; Ensure that `lsp-completion-at-point' the first CAPF to be tried, | ||||||
;; unless user has put it elsewhere in the list by their own | ||||||
(add-to-list 'completion-at-point-functions #'lsp-completion-at-point) | ||||||
(make-local-variable 'completion-category-defaults) | ||||||
(setf (alist-get 'lsp-capf completion-category-defaults) '((styles . (lsp-passthrough)))) | ||||||
(make-local-variable 'completion-styles-alist) | ||||||
(setf (alist-get 'lsp-passthrough completion-styles-alist) | ||||||
'(completion-basic-try-completion | ||||||
lsp-completion-passthrough-all-completions | ||||||
"Passthrough completion.")) | ||||||
|
||||||
(unless (equal lsp-completion-provider :corfu-capf) | ||||||
(make-local-variable 'completion-category-defaults) | ||||||
(setf (alist-get 'lsp-capf completion-category-defaults) '((styles . (lsp-passthrough)))) | ||||||
(make-local-variable 'completion-styles-alist) | ||||||
(setf (alist-get 'lsp-passthrough completion-styles-alist) | ||||||
'(completion-basic-try-completion | ||||||
lsp-completion-passthrough-all-completions | ||||||
"Passthrough completion."))) | ||||||
|
||||||
(cond | ||||||
((equal lsp-completion-provider :none)) | ||||||
((and (not (equal lsp-completion-provider :none)) | ||||||
((or (equal lsp-completion-provider :none) | ||||||
(equal lsp-completion-provider :corfu-capf))) | ||||||
((and (equal lsp-completion-provider :capf) | ||||||
(fboundp 'company-mode)) | ||||||
(setq-local company-abort-on-unique-match nil) | ||||||
(company-mode 1) | ||||||
(setq-local company-backends (cl-adjoin 'company-capf company-backends :test #'equal))) | ||||||
(setq-local company-backends (cl-adjoin 'company-capf company-backends :test #'equal)) | ||||||
(when (bound-and-true-p company-mode) | ||||||
(add-hook 'company-completion-started-hook | ||||||
completion-started-fn | ||||||
nil | ||||||
t) | ||||||
(add-hook 'company-after-completion-hook | ||||||
after-completion-fn | ||||||
nil | ||||||
t))) | ||||||
(t | ||||||
(lsp--warn "Unable to autoconfigure company-mode."))) | ||||||
|
||||||
(when (bound-and-true-p company-mode) | ||||||
(add-hook 'company-completion-started-hook | ||||||
completion-started-fn | ||||||
nil | ||||||
t) | ||||||
(add-hook 'company-after-completion-hook | ||||||
after-completion-fn | ||||||
nil | ||||||
t)) | ||||||
(add-hook 'lsp-unconfigure-hook #'lsp-completion--disable nil t)) | ||||||
(t | ||||||
(remove-hook 'completion-at-point-functions #'lsp-completion-at-point t) | ||||||
(setq-local completion-category-defaults | ||||||
(cl-remove 'lsp-capf completion-category-defaults :key #'cl-first)) | ||||||
(setq-local completion-styles-alist | ||||||
(cl-remove 'lsp-passthrough completion-styles-alist :key #'cl-first)) | ||||||
(unless (equal lsp-completion-provider :corfu-capf) | ||||||
(setq-local completion-category-defaults | ||||||
(cl-remove 'lsp-capf completion-category-defaults :key #'cl-first)) | ||||||
(setq-local completion-styles-alist | ||||||
(cl-remove 'lsp-passthrough completion-styles-alist :key #'cl-first))) | ||||||
(remove-hook 'lsp-unconfigure-hook #'lsp-completion--disable t) | ||||||
(when (featurep 'company) | ||||||
(remove-hook 'company-completion-started-hook | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this option should be replaced with a new option
lsp-completion-frontend
which can take these values:default-passthrough
: Setup passthrough styledefault-filtered
: Setup flex style (or orderless if available)company
: Auto configure Company, likedefault-passthrough
corfu
: Auto configure Corfu, likedefault-filtered
Note that
default-filtered
is equivalent to the behavior of Eglot.