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

[completion] Filter completions on the server side #298

Merged

Conversation

benlangmuir
Copy link
Contributor

Implement server-side code-completion filtering and limit the number of completion items to 200 results by default. This radically improves the performance of code-completion when there are a large number of results by avoiding the cost of serialization of lower priority results. We take advantage of LSP's isIncomplete flag to inform the editor that as the user types more characters it should re-query SourceKit-LSP for completions in case there are results that were previously filtered out that are now relevant to the new filter text.

I verified that server-side filtering is working in VSCode, Sublime Text, and vim (with coc.nvim). If any editors do not support isIncomplete yet, it is possible to disable server-side filtering.

Configuration

  • By default, server-side filtering is enabled, and the maximum number of results is 200.
  • You can override this using command-line options to sourcekit-lsp
sourcekit-lsp -completion-server-side-filtering=true -completion-max-results=200
  • Alternatively, you can override this by passing initializationOptions to SourceKit-LSP during the initialize request:
"initializationOptions": {
    "completion": {
        "serverSideFiltering": true,
        "maxResults": 200
    }
}
  • As an LSP extension, we also support passing completion options directly in the textDocument/completion request using the new parameter sourcekitlspOptions. This is intended mostly as a tool for experimentation and debugging.

Note: In the initial implementation, setting maxResults to unlimited (0 or nil) will have worse performance than if you disabled server-side filtering. We should be able to close that gap, but for now I don't recommend using large values for maxResults.

Implementation

When using server-side filtering, we keep track of a code-completion "session" for a given source file and completion location.

foo.barB|
    ^   ^
    |    ` cursor location
     ` start of identifier

In the above example, the completion session will be for the location after "." at the start of the identifier, and the completion location (usually the cursor location) determines what the "filter text" for the completion will be, in this case "barB".

Once we open a session, any subsequent completion requests that have the same location and use triggerKind == triggerFromIncompleteCompletions will efficiently re-filter the existing completions. If the new completion location is not compatible with the session, we return an error rather than recompute completions so that editors can rely on triggerFromIncompleteCompletions always being really fast. Currently, any other trigger kind (manual or trigger character) will open a new completion session, although we could improve that in the future to reuse the session if no other changes have been made to the document.

The underlying filtering uses sourcekitd's complete.open, complete.update and complete.close requests.

* The interesting keys to split out are "code-completion options", not
  any key that is used by completion.
* Sort the main key list
* Fix the name of the doc_brief key
Similar to cursor info, there is enough code here it makes sense to
separate it out.
Work in progress: add a basic implementation for keeping track of the
current completion session, and use the sourcekitd
complete.open/update/close requests.
With this change, tests pass whether we default to server-side or
client-side filtering. Also duplicate a few interesting tests to run
both ways. A future commit will beef up the test coverage for
server-side filtering specifically.
By default, print 1-based line/column numbers using the defacto standard
line:column format. In debug print, continue to use the 0-based values
to match the constructor.
Since it is being used in the LSP protocol layer, give it a name that
clarifies that it is SourceKit-specific.
@benlangmuir
Copy link
Contributor Author

@swift-ci please test

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.

1 participant