]> git.lizzy.rs Git - rust.git/blob - docs/dev/lsp-extensions.md
fix: add capability for "open cargo.toml" lsp extension
[rust.git] / docs / dev / lsp-extensions.md
1 <!---
2 lsp_ext.rs hash: 3b2931972b33198b
3
4 If you need to change the above hash to make the test pass, please check if you
5 need to adjust this doc as well and ping this issue:
6
7   https://github.com/rust-analyzer/rust-analyzer/issues/4604
8
9 --->
10
11 # LSP Extensions
12
13 This document describes LSP extensions used by rust-analyzer.
14 It's a best effort document, when in doubt, consult the source (and send a PR with clarification ;-) ).
15 We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority.
16 All capabilities are enabled via `experimental` field of `ClientCapabilities` or `ServerCapabilities`.
17 Requests which we hope to upstream live under `experimental/` namespace.
18 Requests, which are likely to always remain specific to `rust-analyzer` are under `rust-analyzer/` namespace.
19
20 If you want to be notified about the changes to this document, subscribe to [#4604](https://github.com/rust-analyzer/rust-analyzer/issues/4604).
21
22 ## UTF-8 offsets
23
24 rust-analyzer supports clangd's extension for opting into UTF-8 as the coordinate space for offsets (by default, LSP uses UTF-16 offsets).
25
26 https://clangd.llvm.org/extensions.html#utf-8-offsets
27
28 ## Configuration in `initializationOptions`
29
30 **Issue:** https://github.com/microsoft/language-server-protocol/issues/567
31
32 The `initializationOptions` filed of the `InitializeParams` of the initialization request should contain `"rust-analyzer"` section of the configuration.
33
34 `rust-analyzer` normally sends a `"workspace/configuration"` request with `{ "items": ["rust-analyzer"] }` payload.
35 However, the server can't do this during initialization.
36 At the same time some essential configuration parameters are needed early on, before servicing requests.
37 For this reason, we ask that `initializationOptions` contains the configuration, as if the server did make a `"workspace/configuration"` request.
38
39 If a language client does not know about `rust-analyzer`'s configuration options it can get sensible defaults by doing any of the following:
40  * Not sending `initializationOptions`
41  * Sending `"initializationOptions": null`
42  * Sending `"initializationOptions": {}`
43
44 ## Snippet `TextEdit`
45
46 **Issue:** https://github.com/microsoft/language-server-protocol/issues/724
47
48 **Experimental Client Capability:** `{ "snippetTextEdit": boolean }`
49
50 If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s:
51
52 ```typescript
53 interface SnippetTextEdit extends TextEdit {
54     insertTextFormat?: InsertTextFormat;
55     annotationId?: ChangeAnnotationIdentifier;
56 }
57 ```
58
59 ```typescript
60 export interface TextDocumentEdit {
61     textDocument: OptionalVersionedTextDocumentIdentifier;
62     edits: (TextEdit | SnippetTextEdit)[];
63 }
64 ```
65
66 When applying such code action, the editor should insert snippet, with tab stops and placeholder.
67 At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`.
68
69 ### Example
70
71 "Add `derive`" code action transforms `struct S;` into `#[derive($0)] struct S;`
72
73 ### Unresolved Questions
74
75 * Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)?
76 * Can snippets span multiple files (so far, no)?
77
78 ## `CodeAction` Groups
79
80 **Issue:** https://github.com/microsoft/language-server-protocol/issues/994
81
82 **Experimental Client Capability:** `{ "codeActionGroup": boolean }`
83
84 If this capability is set, `CodeAction` returned from the server contain an additional field, `group`:
85
86 ```typescript
87 interface CodeAction {
88     title: string;
89     group?: string;
90     ...
91 }
92 ```
93
94 All code-actions with the same `group` should be grouped under single (extendable) entry in lightbulb menu.
95 The set of actions `[ { title: "foo" }, { group: "frobnicate", title: "bar" }, { group: "frobnicate", title: "baz" }]` should be rendered as
96
97 ```
98 💡
99   +-------------+
100   | foo         |
101   +-------------+-----+
102   | frobnicate >| bar |
103   +-------------+-----+
104                 | baz |
105                 +-----+
106 ```
107
108 Alternatively, selecting `frobnicate` could present a user with an additional menu to choose between `bar` and `baz`.
109
110 ### Example
111
112 ```rust
113 fn main() {
114     let x: Entry/*cursor here*/ = todo!();
115 }
116 ```
117
118 Invoking code action at this position will yield two code actions for importing `Entry` from either `collections::HashMap` or `collection::BTreeMap`, grouped under a single "import" group.
119
120 ### Unresolved Questions
121
122 * Is a fixed two-level structure enough?
123 * Should we devise a general way to encode custom interaction protocols for GUI refactorings?
124
125 ## Parent Module
126
127 **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002
128
129 **Experimental Server Capability:** `{ "parentModule": boolean }`
130
131 This request is sent from client to server to handle "Goto Parent Module" editor action.
132
133 **Method:** `experimental/parentModule`
134
135 **Request:** `TextDocumentPositionParams`
136
137 **Response:** `Location | Location[] | LocationLink[] | null`
138
139
140 ### Example
141
142 ```rust
143 // src/main.rs
144 mod foo;
145 // src/foo.rs
146
147 /* cursor here*/
148 ```
149
150 `experimental/parentModule` returns a single `Link` to the `mod foo;` declaration.
151
152 ### Unresolved Question
153
154 * An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
155   This is the approach IntelliJ Rust is taking.
156   However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
157   If you want super module, but the cursor happens to be inside an overridden function, the behavior with single "gotoSuper" request is surprising.
158
159 ## Join Lines
160
161 **Issue:** https://github.com/microsoft/language-server-protocol/issues/992
162
163 **Experimental Server Capability:** `{ "joinLines": boolean }`
164
165 This request is sent from client to server to handle "Join Lines" editor action.
166
167 **Method:** `experimental/joinLines`
168
169 **Request:**
170
171 ```typescript
172 interface JoinLinesParams {
173     textDocument: TextDocumentIdentifier,
174     /// Currently active selections/cursor offsets.
175     /// This is an array to support multiple cursors.
176     ranges: Range[],
177 }
178 ```
179
180 **Response:** `TextEdit[]`
181
182 ### Example
183
184 ```rust
185 fn main() {
186     /*cursor here*/let x = {
187         92
188     };
189 }
190 ```
191
192 `experimental/joinLines` yields (curly braces are automagically removed)
193
194 ```rust
195 fn main() {
196     let x = 92;
197 }
198 ```
199
200 ### Unresolved Question
201
202 * What is the position of the cursor after `joinLines`?
203   Currently, this is left to editor's discretion, but it might be useful to specify on the server via snippets.
204   However, it then becomes unclear how it works with multi cursor.
205
206 ## On Enter
207
208 **Issue:** https://github.com/microsoft/language-server-protocol/issues/1001
209
210 **Experimental Server Capability:** `{ "onEnter": boolean }`
211
212 This request is sent from client to server to handle <kbd>Enter</kbd> keypress.
213
214 **Method:** `experimental/onEnter`
215
216 **Request:**: `TextDocumentPositionParams`
217
218 **Response:**
219
220 ```typescript
221 SnippetTextEdit[]
222 ```
223
224 ### Example
225
226 ```rust
227 fn main() {
228     // Some /*cursor here*/ docs
229     let x = 92;
230 }
231 ```
232
233 `experimental/onEnter` returns the following snippet
234
235 ```rust
236 fn main() {
237     // Some
238     // $0 docs
239     let x = 92;
240 }
241 ```
242
243 The primary goal of `onEnter` is to handle automatic indentation when opening a new line.
244 This is not yet implemented.
245 The secondary goal is to handle fixing up syntax, like continuing doc strings and comments, and escaping `\n` in string literals.
246
247 As proper cursor positioning is raison-d'etat for `onEnter`, it uses `SnippetTextEdit`.
248
249 ### Unresolved Question
250
251 * How to deal with synchronicity of the request?
252   One option is to require the client to block until the server returns the response.
253   Another option is to do a OT-style merging of edits from client and server.
254   A third option is to do a record-replay: client applies heuristic on enter immediately, then applies all user's keypresses.
255   When the server is ready with the response, the client rollbacks all the changes and applies the recorded actions on top of the correct response.
256 * How to deal with multiple carets?
257 * Should we extend this to arbitrary typed events and not just `onEnter`?
258
259 ## Structural Search Replace (SSR)
260
261 **Experimental Server Capability:** `{ "ssr": boolean }`
262
263 This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source.
264
265 **Method:** `experimental/ssr`
266
267 **Request:**
268
269 ```typescript
270 interface SsrParams {
271     /// Search query.
272     /// The specific syntax is specified outside of the protocol.
273     query: string,
274     /// If true, only check the syntax of the query and don't compute the actual edit.
275     parseOnly: bool,
276     /// The current text document. This and `position` will be used to determine in what scope
277     /// paths in `query` should be resolved.
278     textDocument: lc.TextDocumentIdentifier;
279     /// Position where SSR was invoked.
280     position: lc.Position;
281 }
282 ```
283
284 **Response:**
285
286 ```typescript
287 WorkspaceEdit
288 ```
289
290 ### Example
291
292 SSR with query `foo($a, $b) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z)` into `(y + 5).foo(z)`.
293
294 ### Unresolved Question
295
296 * Probably needs search without replace mode
297 * Needs a way to limit the scope to certain files.
298
299 ## Matching Brace
300
301 **Issue:** https://github.com/microsoft/language-server-protocol/issues/999
302
303 **Experimental Server Capability:** `{ "matchingBrace": boolean }`
304
305 This request is sent from client to server to handle "Matching Brace" editor action.
306
307 **Method:** `experimental/matchingBrace`
308
309 **Request:**
310
311 ```typescript
312 interface MatchingBraceParams {
313     textDocument: TextDocumentIdentifier,
314     /// Position for each cursor
315     positions: Position[],
316 }
317 ```
318
319 **Response:**
320
321 ```typescript
322 Position[]
323 ```
324
325 ### Example
326
327 ```rust
328 fn main() {
329     let x: Vec<()>/*cursor here*/ = vec![]
330 }
331 ```
332
333 `experimental/matchingBrace` yields the position of `<`.
334 In many cases, matching braces can be handled by the editor.
335 However, some cases (like disambiguating between generics and comparison operations) need a real parser.
336 Moreover, it would be cool if editors didn't need to implement even basic language parsing
337
338 ### Unresolved Question
339
340 * Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
341   This is how `SelectionRange` request works.
342 * Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
343
344 ## Runnables
345
346 **Issue:** https://github.com/microsoft/language-server-protocol/issues/944
347
348 **Experimental Server Capability:** `{ "runnables": { "kinds": string[] } }`
349
350 This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
351
352 **Method:** `experimental/runnables`
353
354 **Request:**
355
356 ```typescript
357 interface RunnablesParams {
358     textDocument: TextDocumentIdentifier;
359     /// If null, compute runnables for the whole file.
360     position?: Position;
361 }
362 ```
363
364 **Response:** `Runnable[]`
365
366 ```typescript
367 interface Runnable {
368     label: string;
369     /// If this Runnable is associated with a specific function/module, etc, the location of this item
370     location?: LocationLink;
371     /// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities,
372     // the type of `args` is specific to `kind`. The actual running is handled by the client.
373     kind: string;
374     args: any;
375 }
376 ```
377
378 rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look like this:
379
380 ```typescript
381 {
382     workspaceRoot?: string;
383     cargoArgs: string[];
384     cargoExtraArgs: string[];
385     executableArgs: string[];
386     expectTest?: boolean;
387     overrideCargo?: string;
388 }
389 ```
390
391 ## Open External Documentation
392
393 This request is sent from client to server to get a URL to documentation for the symbol under the cursor, if available.
394
395 **Method** `experimental/externalDocs`
396
397 **Request:**: `TextDocumentPositionParams`
398
399 **Response** `string | null`
400
401
402 ## Analyzer Status
403
404 **Method:** `rust-analyzer/analyzerStatus`
405
406 **Request:**
407
408 ```typescript
409 interface AnalyzerStatusParams {
410     /// If specified, show dependencies of the current file.
411     textDocument?: TextDocumentIdentifier;
412 }
413 ```
414
415 **Response:** `string`
416
417 Returns internal status message, mostly for debugging purposes.
418
419 ## Reload Workspace
420
421 **Method:** `rust-analyzer/reloadWorkspace`
422
423 **Request:** `null`
424
425 **Response:** `null`
426
427 Reloads project information (that is, re-executes `cargo metadata`).
428
429 ## Server Status
430
431 **Experimental Client Capability:** `{ "serverStatusNotification": boolean }`
432
433 **Method:** `experimental/serverStatus`
434
435 **Notification:**
436
437 ```typescript
438 interface ServerStatusParams {
439     /// `ok` means that the server is completely functional.
440     ///
441     /// `warning` means that the server is partially functional.
442     /// It can answer correctly to most requests, but some results
443     /// might be wrong due to, for example, some missing dependencies.
444     ///
445     /// `error` means that the server is not functional. For example,
446     /// there's a fatal build configuration problem. The server might
447     /// still give correct answers to simple requests, but most results
448     /// will be incomplete or wrong.
449     health: "ok" | "warning" | "error",
450     /// Is there any pending background work which might change the status?
451     /// For example, are dependencies being downloaded?
452     quiescent: bool,
453     /// Explanatory message to show on hover.
454     message?: string,
455 }
456 ```
457
458 This notification is sent from server to client.
459 The client can use it to display *persistent* status to the user (in modline).
460 It is similar to the `showMessage`, but is intended for stares rather than point-in-time events.
461
462 Note that this functionality is intended primarily to inform the end user about the state of the server.
463 In particular, it's valid for the client to completely ignore this extension.
464 Clients are discouraged from but are allowed to use the `health` status to decide if it's worth sending a request to the server.
465
466 ## Syntax Tree
467
468 **Method:** `rust-analyzer/syntaxTree`
469
470 **Request:**
471
472 ```typescript
473 interface SyntaxTreeParams {
474     textDocument: TextDocumentIdentifier,
475     range?: Range,
476 }
477 ```
478
479 **Response:** `string`
480
481 Returns textual representation of a parse tree for the file/selected region.
482 Primarily for debugging, but very useful for all people working on rust-analyzer itself.
483
484 ## View Hir
485
486 **Method:** `rust-analyzer/viewHir`
487
488 **Request:** `TextDocumentPositionParams`
489
490 **Response:** `string`
491
492 Returns a textual representation of the HIR of the function containing the cursor.
493 For debugging or when working on rust-analyzer itself.
494
495 ## View ItemTree
496
497 **Method:** `rust-analyzer/viewItemTree`
498
499 **Request:**
500
501 ```typescript
502 interface ViewItemTreeParams {
503     textDocument: TextDocumentIdentifier,
504 }
505 ```
506
507 **Response:** `string`
508
509 Returns a textual representation of the `ItemTree` of the currently open file, for debugging.
510
511 ## View Crate Graph
512
513 **Method:** `rust-analyzer/viewCrateGraph`
514
515 **Request:**
516
517 ```typescript
518 interface ViewCrateGraphParams {
519     full: boolean,
520 }
521 ```
522
523 **Response:** `string`
524
525 Renders rust-analyzer's crate graph as an SVG image.
526
527 If `full` is `true`, the graph includes non-workspace crates (crates.io dependencies as well as sysroot crates).
528
529 ## Expand Macro
530
531 **Method:** `rust-analyzer/expandMacro`
532
533 **Request:**
534
535 ```typescript
536 interface ExpandMacroParams {
537     textDocument: TextDocumentIdentifier,
538     position: Position,
539 }
540 ```
541
542 **Response:**
543
544 ```typescript
545 interface ExpandedMacro {
546     name: string,
547     expansion: string,
548 }
549 ```
550
551 Expands macro call at a given position.
552
553 ## Inlay Hints
554
555 **Method:** `rust-analyzer/inlayHints`
556
557 This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
558 Generally, the client should re-query inlay hints after every modification.
559 Note that we plan to move this request to `experimental/inlayHints`, as it is not really Rust-specific, but the current API is not necessary the right one.
560 Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
561
562 **Request:**
563
564 ```typescript
565 interface InlayHintsParams {
566     textDocument: TextDocumentIdentifier,
567 }
568 ```
569
570 **Response:** `InlayHint[]`
571
572 ```typescript
573 interface InlayHint {
574     kind: "TypeHint" | "ParameterHint" | "ChainingHint",
575     range: Range,
576     label: string,
577 }
578 ```
579
580 ## Hover Actions
581
582 **Experimental Client Capability:** `{ "hoverActions": boolean }`
583
584 If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`:
585
586 ```typescript
587 interface Hover {
588     ...
589     actions?: CommandLinkGroup[];
590 }
591
592 interface CommandLink extends Command {
593     /**
594      * A tooltip for the command, when represented in the UI.
595      */
596     tooltip?: string;
597 }
598
599 interface CommandLinkGroup {
600     title?: string;
601     commands: CommandLink[];
602 }
603 ```
604
605 Such actions on the client side are appended to a hover bottom as command links:
606 ```
607   +-----------------------------+
608   | Hover content               |
609   |                             |
610   +-----------------------------+
611   | _Action1_ | _Action2_       |  <- first group, no TITLE
612   +-----------------------------+
613   | TITLE _Action1_ | _Action2_ |  <- second group
614   +-----------------------------+
615   ...
616 ```
617
618 ## Open Cargo.toml
619
620 **Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6462
621
622 **Experimental Server Capability:** `{ "openCargoToml": boolean }`
623
624 This request is sent from client to server to open the current project's Cargo.toml
625
626 **Method:** `experimental/openCargoToml`
627
628 **Request:** `OpenCargoTomlParams`
629
630 **Response:** `Location | null`
631
632
633 ### Example
634
635 ```rust
636 // Cargo.toml
637 [package]
638 // src/main.rs
639
640 /* cursor here*/
641 ```
642
643 `experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword.
644
645 ## Related tests
646
647 This request is sent from client to server to get the list of tests for the specified position.
648
649 **Method:** `rust-analyzer/relatedTests`
650
651 **Request:** `TextDocumentPositionParams`
652
653 **Response:** `TestInfo[]`
654
655 ```typescript
656 interface TestInfo {
657     runnable: Runnable;
658 }
659 ```
660
661 ## Hover Actions
662
663 **Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6823
664
665 This request is sent from client to server to move item under cursor or selection in some direction.
666
667 **Method:** `experimental/moveItem`
668
669 **Request:** `MoveItemParams`
670
671 **Response:** `SnippetTextEdit[]`
672
673 ```typescript
674 export interface MoveItemParams {
675     textDocument: lc.TextDocumentIdentifier,
676     range: lc.Range,
677     direction: Direction
678 }
679
680 export const enum Direction {
681     Up = "Up",
682     Down = "Down"
683 }
684 ```
685
686 ## Workspace Symbols Filtering
687
688 **Issue:** https://github.com/rust-analyzer/rust-analyzer/pull/7698
689
690 **Experimental Server Capability:** `{ "workspaceSymbolScopeKindFiltering": boolean }`
691
692 Extends the existing `workspace/symbol` request with ability to filter symbols by broad scope and kind of symbol.
693 If this capability is set, `workspace/symbol` parameter gains two new optional fields:
694
695
696 ```typescript
697 interface WorkspaceSymbolParams {
698     /**
699      * Return only the symbols defined in the specified scope.
700      */
701     searchScope?: WorkspaceSymbolSearchScope;
702     /**
703      * Return only the symbols of specified kinds.
704      */
705     searchKind?: WorkspaceSymbolSearchKind;
706     ...
707 }
708
709 const enum WorkspaceSymbolSearchScope {
710     Workspace = "workspace",
711     WorkspaceAndDependencies = "workspaceAndDependencies"
712 }
713
714 const enum WorkspaceSymbolSearchKind {
715     OnlyTypes = "onlyTypes",
716     AllSymbols = "allSymbols"
717 }
718 ```