'Go to Definition' for stdlib

Hey there!

Is it possible to add the ability to 'Go to Definition' for standard library symbols like in Xcode?

The reason I'd like to see that is that the hover info doesn't say which protocol a symbol is coming from, and that the hover info shows associatedtype names instead of the typealiases on the conforming type of the variable that I'm actually using.

Being able to open that in VS Code instead of having to switch to Xcode would be neat.

3 Likes

Yes I’d love to see that as well. I think this is something that would need to be implemented in sourcekit-lsp. I will find out what is required.

2 Likes

Glad to hear that. Thanks for your reply, and thank you and the team for this wonderful extension!

1 Like

When you go to the "definition" of a standard library stuff (or other pre-compiled framework) in Xcode, you’ll be redirected to the "interface view". It is not displaying any singular file on the disk, but the combination of library interface (generated from swiftmodule or parsed from swiftinterface) and document (parsed from swiftdoc). This is the magic power of an IDE, which can handle the parsing, generation and previewing seamlessly.

To support a similar function in VSCode, we need to break this into steps. First, we need to find the related files (swiftmodule, swiftinterface and swiftdoc) somewhere on the computer with SourceKit-LSP. Then we need to combine these materials and generate a temporary read-only file for preview. We also need to teach SourceKit-LSP to read such file, so it can output the right highlighting and context. The last step is to tell VSCode about the new format. SourceKit-LSP needs to be updated heavily to support the feature, but it seems the generation step may use the compiler driver directly.

2 Likes

I’ve had a quick chat with @ahoppen about this and there are a number of issues.

First as @stevapple says above this data is generated. There is no file stored on the file system with this representation. How we display this in vscode without having some file backing it up is awkward. We could store a temporary file I guess.

Secondly it doesn’t look like there is anything in the LSP specification that supports passing back the contents of a generated file. A custom message could be built but is not ideal. Also this would be a different result to the standard response to go to definition requests.

2 Likes

Thanks for taking the time to look into this, and for your explanations!

Aha! I figured it wouldn't be too difficult to make this work because you've already got the documentation popups, and I thought the extension must be getting that info from this interface view, and it would be just a matter of opening that in a new tab. Now I know better...

Just out of curiosity I did a search for these files and found 456 of them under /Library/Developer/CommandLineTools/SDKs. swiftmodule seems to be a container for one or more swiftinterface + swiftdoc pairs.

Isn't the extension already having to find these files in order to show the documentation hover popups? Or is it embedded in the extension?

Didn't think about that, but yeah, the interface syntax is like a mix between protocols and regular Swift, so it needs its own grammar rules and all that.

To me, as a user of the extension, this would make sense. But perhaps that's just due to my previous experience with Xcode? Don't know how it is with other languages.

If I understand correctly, you're saying it would be somewhat like this:

  1. User clicks 'go to definition'.
  2. LSP figures out this comes from a pre-compiled framework and finds the relevant swiftmodule, swiftinterface, swiftdoc files.
  3. LSP generates the interface view.
  4. LSP finds the definition in the interface view.
  5. LSP responds saying "the definition is on line 400, column 20, in file xyz, but xyz doesn't yet exist, here I'll give it to you: «interface view»" or instead gives the path to a temporary file.
  6. VS Code opens the new file and goes to line 400, column 20.

But the way @stevapple described it, it sounds like it could also be like this:

  1. User clicks 'go to definition'.
  2. LSP figures out this comes from a pre-compiled framework and tells the extension which one it is.
  3. The extension finds the relevant files and generates the interface view.
  4. The extension opens the interface view in a new tab, as a temporary file.
  5. The extension then asks LSP for the definition again, but passes the interface view as new context.
  6. LSP finds the definition in the interface view and responds with the line number and column.

Either way seems like a lot of plumbing, and I have to admit, the payoff is not so big. Arguably my single motivating example could be solved some other way. Perhaps just adding the protocol name to the hover popup is feasible and would be, I imagine, a much simpler solution?

I'm not sure what you mean by this, but assuming you mean the 'opening a temporary file' aspect, then again, as a (Xcode) user, this technical inconsistency wouldn't be an issue to me.

When you select Go to definition. VSCode sends a textDocument\definition request to the LSP server. This request includes the file and position in the file to locate symbol I want to go to definition of. The LSP server replies with a textDocument\definition response which includes the file and location range of the definition and VSCode then jumps to that location. This is all internal to VSCode. There isn't an easy way to replace the textDocument\definition response with something custom.

1 Like

Interestingly if you ask for a symbol in the Standard Library LSP does return the swift module the symbol comes from so maybe we can do something with that.

1 Like

OIC, thanks for the detailed explanation!

What do you think about my other suggestion of just adding the protocol name to the hover popup?

I also just remembered that in Xcode, once you're in the interface view, you cannot 'go to definition' on further symbols therein, which would be really nice if you could [edit: nevermind, looks like that's been fixed and is working in Xcode 13.4]. But as it is, sometimes I want to click 'go to definition' on a function because I want to get info on the type of one of the parameters, but instead I have to do something like write let x: TypeName or func f<T : ProtocolName>(t: T) {} to be able to actually click on that and 'go to definition'. So if even Xcode doesn't fully handle interface views as one would expect, perhaps it's not worth pursuing currently? Would it make sense to wait for LSP and VS Code to come up with a standard mechanism for this? Is that something that's on their radar?

This text comes from SourceKit-LSP as well. You would be best adding an issue to that repo to see if it can be done GitHub - apple/sourcekit-lsp: Language Server Protocol implementation for Swift and C-based languages.

I'll continue discussions with the SourceKit-lsp team to see if we can come up with a solution.

It is now

2 Likes

Great! I'm gonna ponder my idea a bit further and try to come up with additional motivating examples. Thanks again.