Skip to content

Commit 5cd0787

Browse files
committed
Add Open Quickly (workspace/allSymbolNames, workspace/symbolInfo)
Adds three LSP extensions for fast symbol navigation across the workspace, including SDK symbols in .swiftinterface/.swiftmodule: - workspace/allSymbolNames: returns every indexed symbol name so clients can drive a local fuzzy-search UI. - workspace/symbolInfo: takes exact names and returns location info. Source-file symbols get SymbolInformation with a file:// URI; SDK/stdlib symbols (when the client supports workspace.symbol.resolveSupport) get a WorkspaceSymbol with a deferred file://<module-file>?module=<name> URI and the USR in data["usr"]. - workspaceSymbol/resolve: resolves the deferred URI into a sourcekit-lsp://generated-swift-interface/ URI with the symbol's exact position, via openGeneratedInterface.
1 parent d000937 commit 5cd0787

10 files changed

Lines changed: 882 additions & 30 deletions

File tree

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Jump to Definition
2+
3+
Jump to definition for SDK/stdlib symbols works by generating a
4+
textual Swift interface on demand and returning a `sourcekit-lsp://`
5+
URI that the client can fetch via `workspace/getReferenceDocument`.
6+
7+
## Requests Involved
8+
9+
| Request | Direction | Purpose |
10+
|---|---|---|
11+
| `textDocument/definition` | Client → Server | Resolve the symbol under the cursor to a location |
12+
| `workspace/getReferenceDocument` | Client → Server | Fetch the content of a `sourcekit-lsp://` URI |
13+
14+
`workspace/getReferenceDocument` is a SourceKit-LSP extension. The
15+
client must advertise support in `ClientCapabilities.experimental`:
16+
17+
```json
18+
{ "workspace/getReferenceDocument": { "supported": true } }
19+
```
20+
21+
Without this capability the server writes the interface to a temporary
22+
file and returns a `file://` URI instead.
23+
24+
## Workflow
25+
26+
```
27+
Client Server
28+
│ │
29+
│── textDocument/definition ──────────────▶│
30+
│◀─ Location { │
31+
│ uri: "sourcekit-lsp://...", │
32+
│ range: { line: 42, character: 14 } │
33+
│ } │
34+
│ │
35+
│── workspace/getReferenceDocument ───────▶│
36+
│◀─ { content: "..." } ────────────────────│
37+
│ │
38+
│ [open tab, scroll to range] │
39+
```
40+
41+
1. **Definition** — the client requests the definition of the symbol
42+
at the cursor. For source-defined symbols the server returns a
43+
`file://` URI with the exact source location. For SDK/stdlib
44+
symbols it returns a `sourcekit-lsp://` URI and sets `range` to
45+
the symbol's position within the generated interface (computed
46+
server-side via `editor.find_usr`).
47+
2. **Content retrieval** — the client fetches the generated interface
48+
via `workspace/getReferenceDocument` to display its content. The
49+
client scrolls to `range` from the definition response — `symbolPosition`
50+
is not used here since the position is already known from step 1.
51+
52+
## Server-Side Flow
53+
54+
### 1. `textDocument/definition` handling
55+
56+
The server first attempts an index-based lookup
57+
(`indexBasedDefinition`). For system/SDK symbols the index record
58+
points to a `.swiftinterface` or `.swiftmodule` file, so the handler
59+
calls:
60+
61+
```
62+
definitionInInterface(
63+
moduleName: <from SymbolDetails.systemModule>,
64+
groupName: <from SymbolDetails.systemModule>,
65+
symbolUSR: <symbol.usr>,
66+
originatorUri: <the file the cursor is in>
67+
)
68+
```
69+
70+
### 2. `openGeneratedInterface`
71+
72+
`definitionInInterface` delegates to
73+
`SwiftLanguageService.openGeneratedInterface`, which:
74+
75+
1. Constructs a fully-resolved `GeneratedInterfaceDocumentURLData`
76+
using `init(moduleName:groupName:primaryFile:)`:
77+
- `sourcekitdDocumentName` is synthesised as
78+
`<moduleName>.<groupName>.<hash>` where `hash` is
79+
`abs(buildSettingsFile.stringValue.hashValue)`.
80+
- `buildSettingsFrom` is set to `originatorUri.buildSettingsFile`
81+
— the build settings file of the **requesting source file**, not
82+
the module file. This ensures sourcekitd uses the same compiler
83+
arguments as the file that triggered the request.
84+
2. Calls `generatedInterfaceManager.position(ofUsr:in:)` to find the
85+
symbol's position within the generated interface (see below).
86+
3. Returns `GeneratedInterfaceDetails(uri: sourcekit-lsp://...,
87+
position: <symbol position>)`.
88+
89+
The URI has no USR fragment. The position is returned separately and
90+
used as `Location.range` in the definition response.
91+
92+
### 3. Interface generation and caching
93+
94+
`GeneratedInterfaceManager` opens the interface in sourcekitd via
95+
`editor.open.interface`:
96+
97+
```
98+
keys.name: "<moduleName>.<groupName>.<hash>"
99+
keys.moduleName: "<moduleName>"
100+
keys.groupName: "<groupName>" // if present
101+
keys.synthesizedExtension: 1
102+
keys.compilerArgs: [... compiler arguments from build settings ...]
103+
```
104+
105+
The resulting `sourceText` is cached in memory keyed by
106+
`sourcekitdDocumentName`. Subsequent requests for the same module +
107+
build context reuse the cached snapshot.
108+
109+
### 4. Symbol position within the interface
110+
111+
`GeneratedInterfaceManager.position(ofUsr:in:)` sends
112+
`editor.find_usr` to sourcekitd:
113+
114+
```
115+
keys.sourceFile: "<sourcekitdDocumentName>"
116+
keys.usr: "<symbolUSR>"
117+
```
118+
119+
sourcekitd returns a byte offset, which is converted to a 0-based
120+
`Position` via `DocumentSnapshot.positionOf(utf8Offset:)`.
121+
122+
### 5. URI returned to the client
123+
124+
The `sourcekit-lsp://` URI is fully resolved — `sourcekitdDocument`
125+
is always present, and there is no USR fragment:
126+
127+
```
128+
sourcekit-lsp://generated-swift-interface/Swift.String.swiftinterface
129+
?moduleName=Swift
130+
&groupName=String
131+
&sourcekitdDocument=Swift.String.12345678
132+
&buildSettingsFrom=file:///path/to/main.swift
133+
```
134+
135+
The `range` in the returned `Location` carries the symbol's position
136+
in the interface, so the client knows where to scroll without calling
137+
`workspace/getReferenceDocument` first.
138+
139+
### 6. `workspace/getReferenceDocument` handling
140+
141+
Because the URI is fully resolved (`sourcekitdDocumentName != nil`)
142+
the server skips the stub-resolution path and goes straight to the
143+
language service:
144+
145+
```swift
146+
primaryLanguageService(for: buildSettingsUri, ...).getReferenceDocument(req)
147+
```
148+
149+
`SwiftLanguageService.getReferenceDocument` retrieves the cached
150+
interface snapshot. Since the URI carries no USR fragment,
151+
`symbolPosition` in the response is `nil` — the client uses
152+
`Location.range` from the definition response instead.
153+
154+
## Comparison with Open Quickly
155+
156+
| Aspect | Jump to Definition | Open Quickly |
157+
|---|---|---|
158+
| URI type | Fully resolved (`sourcekitdDocumentName` set) | Stub (`sourcekitdDocumentName` nil) |
159+
| USR in URI | No fragment | Fragment (`#<USR>`) |
160+
| `buildSettingsFrom` | Build settings file of the **requesting source file** | Path of the `.swiftinterface`/`.swiftmodule` from the index record |
161+
| Symbol position delivery | `Location.range` in the definition response | `symbolPosition` in the `getReferenceDocument` response |
162+
| Stub resolution in `getReferenceDocument` | Skipped | `resolvedReferenceDocumentRequest` looks up `mainFiles(containing:)` |
163+
| Fallback without capability | Interface written to a temp `file://` | Raw `file://` path from index record |

0 commit comments

Comments
 (0)