Skip to content
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

Add options to filter candidate returned from server. #86

Merged
merged 1 commit into from
Mar 6, 2019

Conversation

tigersoldier
Copy link
Owner

A new custom option company-lsp-filter-candidates is added to allow
customizing whether or not to filter candidates when
company-lsp-cache-candidates is set to nil. The option can be either set to a
boolean, or an alist for configuring it on a per-server basis.

When company-lsp-cache-candidates is set to non-nil values, this option
doesn't affect the filtering behavior since candidates are always filtered in
this case.

This should fix #79.

A new custom option `company-lsp-filter-candidates` is added to allow
customizing whether or not to filter candidates when
`company-lsp-cache-candidates` is set to nil. The option can be either set to a
boolean, or an alist for configuring it on a per-server basis.

When `company-lsp-cache-candidates` is set to non-nil values, this option
doesn't affect the filtering behavior since candidates are always filtered in
this case.

This should fix #79.
@yyoncho
Copy link
Contributor

yyoncho commented Feb 28, 2019

Afaik company itself could do the filtering if you return 'no-cache nil. It seems to me that In lsp-mode case we need to return nil when the server has returned isIncomplete=false which pretty much means "you could do the rest of the filtering client side" (cc @dgutov)

@dgutov
Copy link

dgutov commented Feb 28, 2019

company itself could do the filtering if you return 'no-cache nil

Only if you do prefix matching. Meaning, fuzzy matching won't work this way.

@tigersoldier
Copy link
Owner Author

We do return 'no-cache nil when company-lsp-cache-candidates is set to t.

It's unclear how to handle incomplete candidates here. We shouldn't let company to cache incomplete candidates here. @dgutov How does company behave if for prefix="foo" no-cache is t and for prefix="foob" no-cache is nil? When user deletes the trailing "b" from the prefix, does company use the cache or not?

@stardiviner
Copy link

I tried out your PR, confirmed it works. I didn't set any option like company-lsp-cache-candidates etc. Thanks for your work!!!

@yyoncho
Copy link
Contributor

yyoncho commented Mar 1, 2019

We do return 'no-cache nil when company-lsp-cache-candidates is set to t.

IMO we should do it only if isIncomplete is nil since the server might have returned partial results.

From what I can see, VScode handles isIncomplete the way I have described it in the previous comment and so it makes sense to have this as a default behaviour for all of the servers(having company-lsp-filter-candidates = t). This approach has also a performance benefit. You may ignore my concern about company-lsp doing the filtering since it seems like it doesn't matter much whether the filtering is performed in company or company-lsp(if it is in company-lsp we may implement flx matching as per your comment).

Example(using vscode and jdtls). The first screenshot is if I have invoked manually the completion, the second one is if I have typed .ode

String s = new String();
s.ode

selection_105
selection_107

@dgutov
Copy link

dgutov commented Mar 1, 2019

How does company behave if for prefix="foo" no-cache is t and for prefix="foob" no-cache is nil? When user deletes the trailing "b" from the prefix, does company use the cache or not?

It does. When the cache is cleared, it uses the previous value of prefix.

But you should probably do just do all cache management in the backend. The logic will be easier to follow, for one thing.

@tigersoldier
Copy link
Owner Author

@yyoncho If you set company-lsp-cache-candidates to 'auto it does mostly what you described. The difference is that company-lsp doesn't set no-cache to nil. Caching and filtering is done on company-lsp side. You can try it out.

Now, let me describe why the other two options, t and nil, exists:

  • t (always use cached candidates regardless the value of inComplete) isn't necessary needed. It exists there as a fallback because currently 'auto has some bug in invalidating caches.
  • nil (always send request to server regardless the value of inComplete) is useful for servers that have more sophisticated filtering and sorting logics. For example CCLS filters candidates by partial matching, while company-lsp only implemented prefix matching. By setting company-lsp-cache-candidates to nil users can disable client-side filtering and enjoy better server-side filtering.

'auto should be the default option for most servers. The current default is nil because 'auto has cache invalidation bug. I'm planning to make company-lsp-cache-candidates a per-server configuration as what I did for company-lsp-filter-candidates in this PR.

@tigersoldier
Copy link
Owner Author

But you should probably do just do all cache management in the backend. The logic will be easier to follow, for one thing.

@dgutov What's the best practice in invalidating caches? I'm listening to company-completion-cancelled-hook and company-completion-finished-hook when populating the cache:

company-lsp/company-lsp.el

Lines 311 to 313 in 4eb6949

(when (null company-lsp--completion-cache)
(add-hook 'company-completion-cancelled-hook #'company-lsp--cleanup-cache nil t)
(add-hook 'company-completion-finished-hook #'company-lsp--cleanup-cache nil t))

And invalidating and removing when the hooks are called:

company-lsp/company-lsp.el

Lines 320 to 324 in 4eb6949

(defun company-lsp--cleanup-cache (_)
"Clean up completion cache and company hooks."
(setq company-lsp--completion-cache nil)
(remove-hook 'company-completion-finished-hook #'company-lsp--cleanup-cache t)
(remove-hook 'company-completion-cancelled-hook #'company-lsp--cleanup-cache t))

However from time to time the caches are not invalidated while the clean up function is removed from the hooks. I can always listen to the hooks and not remove them when invalidating caches. This will cause the function be invoked when other backends are used though. I wonder if there are better way to trigger cleanup function only when the company-lsp backend is used and finished?

@yyoncho
Copy link
Contributor

yyoncho commented Mar 1, 2019

@tigersoldier I am rooting for caching since it looks slicker when e. g. there is no flickering, it is instant. What you are saying make sense though. I guess once you implement flx matching we may rely more on client-side filtering, right? For me this seems to be the best solution, what do you think?

@tigersoldier
Copy link
Owner Author

@yyoncho I do think auto caching provides best out-of-box user experience, even without flx matching (which may not come any time soon). Just need to fix the annoying bug before making it the default option.

Before that, let's have this work around submitted for the current default, no caching.

@dgutov
Copy link

dgutov commented Mar 1, 2019

@tigersoldier

I'm listening to company-completion-cancelled-hook and company-completion-finished-hook

Starting with company-mode 0.9.9, you can replace them with company-after-completion-hook only.

I can always listen to the hooks and not remove them when invalidating caches

That's one option. If the cache is a global lsp-related variable, other backends will probably not mind anyway.

I wonder if there are better way to trigger cleanup function only when the company-lsp backend is used and finished?

Have you figured out why this is not the case? Since you're using the local values of the hooks, and populating them from the backend, it seems like it should work.

@garyo
Copy link

garyo commented Oct 12, 2019

Sorry to revive this old thread. I'm using javascript-typescript-server which returns thousands of completions all the time, completely unfiltered so it seems. To filter those down, this is the only thing I've found that works:

      (use-package company-lsp
        :config
        (push 'company-lsp company-backends)
        ;; Disable client-side cache because the LSP server does a better job.
        (setq company-lsp-async t
              company-lsp-enable-recompletion t
              company-lsp-filter-candidates t
              ;; this is important for typescript language server which returns way too much!
              company-lsp-match-candidate-predicate #'company-lsp-match-candidate-prefix
              company-lsp-cache-candidates 'nil)
        )

In other words set prefix matching and don't cache candidates. Anything else seems to give me way too much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Completions highlighted but not narrowed
5 participants