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