Hi everyone!
This is Ahmed Elrefaey, I’m excited to share with you an update on my GSoC project, Improve the display of documentation during code completion in SourceKit-LSP, mentored by @hamishknight.
Project Goals
The aim of this project is to enhance how documentation is displayed in SourceKit-LSP during code completion by:
-
Showing the full documentation for a code completion item instead of the first paragraph only which we call “brief documentation”.
-
Implementing Language Server Protocol’s signature help request showing the user which overloads are available along with their corresponding documentation.
Progress
During this summer, we have made great progress on this project that I want to share with you.
We have successfully implemented full documentation comment retrieval for completion items. We went with a lazy approach that retrieves the full documentation comment upon request to avoid having to compute all comments and cache them at once which will require a lot of time and memory. This involved making Swift declarations round-trippable to be able to restore them for cached completion items and retrieve the documentation comment.
Here’s what SourceKit-LSP currently provides in VS Code (brief documentation):
And here’s how it looks with full documentation:
We have also implemented a large portion of signature help support by retrieving the available overloads similar to the existing argument completion logic and refactoring the logic for creating the code completion string to be reusable in signature help. The current implementation returns a list of viable overloads along with their full documentation comment for functions, subscripts, initializers, and enum cases with associated values.
Here’s a quick demo of signature help in VS Code.
What’s Left
Full documentation retrieval is currently in a good state.
For signature help however, there’s still room for improvement:
-
Showing the active parameter’s documentation only instead of the entire signature documentation.
-
Ranking the available signatures to determine the active signature. Currently, the active signature is always the first signature.
Code Changes
Here’s a list of the pull requests for the code changes. All pull requests have been merged upstream.
Full Documentation in Code Completion
-
[IDE] Add full documentation to code completion result (swiftlang/swift#82464):
Makes declarations round-trippable by reconstructing the declaration from its USR. This is done by demangling the USR to get the declaration context and the declaration name, then performing name lookup to find the actual declaration. This allows us to always have (or reconstruct) the declaration associated with aCodeCompletionResult
which we then use to find the full documentation comment and we expose an API to allow retrieval through the Swift SourceKit plugin. -
Fetch full documentation in code completion (swiftlang/sourcekit-lsp#2207):
Integrates full documentation support provided by sourcekitd in the Swift SourceKit plugin and in the code completion request in SourceKit-LSP.
Signature Help Support
-
[IDE] [Signature Help] Extract primitive CodeCompletionString creation into CodeCompletionStringBuilder (swiftlang/swift#83646):
Extracts a reusable utility for constructingCodeCompletionString
s which we use in signature help to find the signature label and parameter label offsets. -
[IDE] [Signature Help] Add basic signature help request to SourceKit (swiftlang/swift#83378):
Adds the basic implementation of the signature help request which returns the signature label, parameter label offsets, and the signature documentation (which includes parameter documentation for now). -
[IDE] Erase archetypes without declaration generic signature (swiftlang/swift#83652):
Generalizes a utility used when creating theCodeCompletionString
(which we use for the signature label) for replacing generic parameters with their upper bound type based on the type checking result to work for types without an explicit generic signature coming from a declaration. This is useful when we only have is a function type but not a function declaration (e.g. calling a closure returned from a function). -
Add signature help LSP request support (swiftlang/sourcekit-lsp#2250):
Adds the signature help LSP request to SourceKit-LSP by utilizing the signature help request added to sourcekitd in the previous PRs. -
Trigger parameter hints on accepting a function-like completion item (swiftlang/vscode-swift#1802):
Enhances the signature help experience in VS Code by automatically triggering signature help after accepting a function-like completion.
Challenges
One of the interesting challenges we faced was reconstructing a declaration from its USR in order to retrieve full documentation comments for cached completion items. The challenging part is that there are many types of declarations (e.g. functions, properties, classes, etc), the most important distinction is Swift declarations vs. Clang-imported declarations. There are also different types of declaration contexts (e.g. a declaration might be declared as a top-level module item, or as a member of a nominal type declaration like a struct). And modules can be synthesized by the compiler or they can be regular modules. We needed to account for all such cases when implementing USR to declaration reconstruction.
Another interesting challenge we faced was an issue where a Clang-imported declaration and a compatibility type-alias for it produce the same USR. This breaks the assumption that a USR is unique to a certain declaration and would result in declaration reconstruction tests failing due to non-matching declarations (e.g. the original was for the actual declaration but the one returned was for a type-alias). We fixed this issue by changing the mangling for compatibility type-aliases to be mangled as a ClangImporter-synthesized declaration with the USR containing the its Swift-exposed name disambiguating the USR for the actual declaration vs. for a type-alias.
Closing Thoughts
I'm incredibly grateful for this opportunity to contribute to the Swift project and I really learned a lot from this experience. I'd like to thank my mentor @hamishknight for his unwavering support and guidance throughout this summer. I’d also like to thank @ahoppen, @rintaro, and @bnbarham for their valuable feedback during code review.
Best,
Ahmed Elrefaey.