]> git.lizzy.rs Git - rust.git/blob - docs/dev/lsp-extensions.md
Merge #11690
[rust.git] / docs / dev / lsp-extensions.md
1 <!---
2 lsp_ext.rs hash: 854109e98d02a780
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 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-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 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/567
31
32 The `initializationOptions` field of the `InitializeParams` of the initialization request should contain the `"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 **Upstream 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 **Upstream 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`s 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 **Upstream 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 **Upstream 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 **Upstream 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 the <kbd>Enter</kbd> key press.
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: boolean,
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: TextDocumentIdentifier;
279     /// Position where SSR was invoked.
280     position: Position;
281     /// Current selections. Search/replace will be restricted to these if non-empty.
282     selections: Range[];
283 }
284 ```
285
286 **Response:**
287
288 ```typescript
289 WorkspaceEdit
290 ```
291
292 ### Example
293
294 SSR with query `foo($a, $b) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z)` into `(y + 5).foo(z)`.
295
296 ### Unresolved Question
297
298 * Probably needs search without replace mode
299 * Needs a way to limit the scope to certain files.
300
301 ## Matching Brace
302
303 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/999
304
305 **Experimental Server Capability:** `{ "matchingBrace": boolean }`
306
307 This request is sent from client to server to handle "Matching Brace" editor action.
308
309 **Method:** `experimental/matchingBrace`
310
311 **Request:**
312
313 ```typescript
314 interface MatchingBraceParams {
315     textDocument: TextDocumentIdentifier,
316     /// Position for each cursor
317     positions: Position[],
318 }
319 ```
320
321 **Response:**
322
323 ```typescript
324 Position[]
325 ```
326
327 ### Example
328
329 ```rust
330 fn main() {
331     let x: Vec<()>/*cursor here*/ = vec![]
332 }
333 ```
334
335 `experimental/matchingBrace` yields the position of `<`.
336 In many cases, matching braces can be handled by the editor.
337 However, some cases (like disambiguating between generics and comparison operations) need a real parser.
338 Moreover, it would be cool if editors didn't need to implement even basic language parsing
339
340 ### Unresolved Question
341
342 * Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
343   This is how `SelectionRange` request works.
344 * Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
345
346 ## Runnables
347
348 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/944
349
350 **Experimental Server Capability:** `{ "runnables": { "kinds": string[] } }`
351
352 This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
353
354 **Method:** `experimental/runnables`
355
356 **Request:**
357
358 ```typescript
359 interface RunnablesParams {
360     textDocument: TextDocumentIdentifier;
361     /// If null, compute runnables for the whole file.
362     position?: Position;
363 }
364 ```
365
366 **Response:** `Runnable[]`
367
368 ```typescript
369 interface Runnable {
370     label: string;
371     /// If this Runnable is associated with a specific function/module, etc, the location of this item
372     location?: LocationLink;
373     /// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities,
374     // the type of `args` is specific to `kind`. The actual running is handled by the client.
375     kind: string;
376     args: any;
377 }
378 ```
379
380 rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look like this:
381
382 ```typescript
383 {
384     workspaceRoot?: string;
385     cargoArgs: string[];
386     cargoExtraArgs: string[];
387     executableArgs: string[];
388     expectTest?: boolean;
389     overrideCargo?: string;
390 }
391 ```
392
393 ## Open External Documentation
394
395 This request is sent from client to server to get a URL to documentation for the symbol under the cursor, if available.
396
397 **Method** `experimental/externalDocs`
398
399 **Request:**: `TextDocumentPositionParams`
400
401 **Response** `string | null`
402
403
404 ## Analyzer Status
405
406 **Method:** `rust-analyzer/analyzerStatus`
407
408 **Request:**
409
410 ```typescript
411 interface AnalyzerStatusParams {
412     /// If specified, show dependencies of the current file.
413     textDocument?: TextDocumentIdentifier;
414 }
415 ```
416
417 **Response:** `string`
418
419 Returns internal status message, mostly for debugging purposes.
420
421 ## Reload Workspace
422
423 **Method:** `rust-analyzer/reloadWorkspace`
424
425 **Request:** `null`
426
427 **Response:** `null`
428
429 Reloads project information (that is, re-executes `cargo metadata`).
430
431 ## Server Status
432
433 **Experimental Client Capability:** `{ "serverStatusNotification": boolean }`
434
435 **Method:** `experimental/serverStatus`
436
437 **Notification:**
438
439 ```typescript
440 interface ServerStatusParams {
441     /// `ok` means that the server is completely functional.
442     ///
443     /// `warning` means that the server is partially functional.
444     /// It can answer correctly to most requests, but some results
445     /// might be wrong due to, for example, some missing dependencies.
446     ///
447     /// `error` means that the server is not functional. For example,
448     /// there's a fatal build configuration problem. The server might
449     /// still give correct answers to simple requests, but most results
450     /// will be incomplete or wrong.
451     health: "ok" | "warning" | "error",
452     /// Is there any pending background work which might change the status?
453     /// For example, are dependencies being downloaded?
454     quiescent: boolean,
455     /// Explanatory message to show on hover.
456     message?: string,
457 }
458 ```
459
460 This notification is sent from server to client.
461 The client can use it to display *persistent* status to the user (in modline).
462 It is similar to the `showMessage`, but is intended for stares rather than point-in-time events.
463
464 Note that this functionality is intended primarily to inform the end user about the state of the server.
465 In particular, it's valid for the client to completely ignore this extension.
466 Clients are discouraged from but are allowed to use the `health` status to decide if it's worth sending a request to the server.
467
468 ## Syntax Tree
469
470 **Method:** `rust-analyzer/syntaxTree`
471
472 **Request:**
473
474 ```typescript
475 interface SyntaxTreeParams {
476     textDocument: TextDocumentIdentifier,
477     range?: Range,
478 }
479 ```
480
481 **Response:** `string`
482
483 Returns textual representation of a parse tree for the file/selected region.
484 Primarily for debugging, but very useful for all people working on rust-analyzer itself.
485
486 ## View Hir
487
488 **Method:** `rust-analyzer/viewHir`
489
490 **Request:** `TextDocumentPositionParams`
491
492 **Response:** `string`
493
494 Returns a textual representation of the HIR of the function containing the cursor.
495 For debugging or when working on rust-analyzer itself.
496
497 ## View ItemTree
498
499 **Method:** `rust-analyzer/viewItemTree`
500
501 **Request:**
502
503 ```typescript
504 interface ViewItemTreeParams {
505     textDocument: TextDocumentIdentifier,
506 }
507 ```
508
509 **Response:** `string`
510
511 Returns a textual representation of the `ItemTree` of the currently open file, for debugging.
512
513 ## View Crate Graph
514
515 **Method:** `rust-analyzer/viewCrateGraph`
516
517 **Request:**
518
519 ```typescript
520 interface ViewCrateGraphParams {
521     full: boolean,
522 }
523 ```
524
525 **Response:** `string`
526
527 Renders rust-analyzer's crate graph as an SVG image.
528
529 If `full` is `true`, the graph includes non-workspace crates (crates.io dependencies as well as sysroot crates).
530
531 ## Shuffle Crate Graph
532
533 **Method:** `rust-analyzer/shuffleCrateGraph`
534
535 **Request:** `null`
536
537 Shuffles the crate IDs in the crate graph, for debugging purposes.
538
539 ## Expand Macro
540
541 **Method:** `rust-analyzer/expandMacro`
542
543 **Request:**
544
545 ```typescript
546 interface ExpandMacroParams {
547     textDocument: TextDocumentIdentifier,
548     position: Position,
549 }
550 ```
551
552 **Response:**
553
554 ```typescript
555 interface ExpandedMacro {
556     name: string,
557     expansion: string,
558 }
559 ```
560
561 Expands macro call at a given position.
562
563 ## Inlay Hints
564
565 **Method:** `experimental/inlayHints`
566
567 This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
568 Generally, the client should re-query inlay hints after every modification.
569 Until it gets upstreamed, this follows the VS Code API.
570 Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
571
572 **Request:**
573
574 ```typescript
575 interface InlayHintsParams {
576     textDocument: TextDocumentIdentifier,
577 }
578 ```
579
580 **Response:** `InlayHint[]`
581
582 ```typescript
583 interface InlayHint {
584     position: Position;
585     label: string | InlayHintLabelPart[];
586     tooltip?: string | MarkdownString | undefined;
587     kind?: InlayHintKind;
588     paddingLeft?: boolean;
589     paddingRight?: boolean;
590 }
591 ```
592
593 ## Hover Actions
594
595 **Experimental Client Capability:** `{ "hoverActions": boolean }`
596
597 If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`:
598
599 ```typescript
600 interface Hover {
601     ...
602     actions?: CommandLinkGroup[];
603 }
604
605 interface CommandLink extends Command {
606     /**
607      * A tooltip for the command, when represented in the UI.
608      */
609     tooltip?: string;
610 }
611
612 interface CommandLinkGroup {
613     title?: string;
614     commands: CommandLink[];
615 }
616 ```
617
618 Such actions on the client side are appended to a hover bottom as command links:
619 ```
620   +-----------------------------+
621   | Hover content               |
622   |                             |
623   +-----------------------------+
624   | _Action1_ | _Action2_       |  <- first group, no TITLE
625   +-----------------------------+
626   | TITLE _Action1_ | _Action2_ |  <- second group
627   +-----------------------------+
628   ...
629 ```
630
631 ## Open Cargo.toml
632
633 **Upstream Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6462
634
635 **Experimental Server Capability:** `{ "openCargoToml": boolean }`
636
637 This request is sent from client to server to open the current project's Cargo.toml
638
639 **Method:** `experimental/openCargoToml`
640
641 **Request:** `OpenCargoTomlParams`
642
643 **Response:** `Location | null`
644
645
646 ### Example
647
648 ```rust
649 // Cargo.toml
650 [package]
651 // src/main.rs
652
653 /* cursor here*/
654 ```
655
656 `experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword.
657
658 ## Related tests
659
660 This request is sent from client to server to get the list of tests for the specified position.
661
662 **Method:** `rust-analyzer/relatedTests`
663
664 **Request:** `TextDocumentPositionParams`
665
666 **Response:** `TestInfo[]`
667
668 ```typescript
669 interface TestInfo {
670     runnable: Runnable;
671 }
672 ```
673
674 ## Hover Range
675
676 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/377
677
678 **Experimental Server Capability:** { "hoverRange": boolean }
679
680 This extension allows passing a `Range` as a `position` field of `HoverParams`.
681 The primary use-case is to use the hover request to show the type of the expression currently selected.
682
683 ```typescript
684 interface HoverParams extends WorkDoneProgressParams {
685     textDocument: TextDocumentIdentifier;
686     position: Range | Position;
687 }
688 ```
689 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.
690
691 ### Example
692
693 ```rust
694 fn main() {
695     let expression = $01 + 2 * 3$0;
696 }
697 ```
698
699 Triggering a hover inside the selection above will show a result of `i32`.
700
701 ## Move Item
702
703 **Upstream Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6823
704
705 This request is sent from client to server to move item under cursor or selection in some direction.
706
707 **Method:** `experimental/moveItem`
708
709 **Request:** `MoveItemParams`
710
711 **Response:** `SnippetTextEdit[]`
712
713 ```typescript
714 export interface MoveItemParams {
715     textDocument: TextDocumentIdentifier,
716     range: Range,
717     direction: Direction
718 }
719
720 export const enum Direction {
721     Up = "Up",
722     Down = "Down"
723 }
724 ```
725
726 ## Workspace Symbols Filtering
727
728 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/941
729
730 **Experimental Server Capability:** `{ "workspaceSymbolScopeKindFiltering": boolean }`
731
732 Extends the existing `workspace/symbol` request with ability to filter symbols by broad scope and kind of symbol.
733 If this capability is set, `workspace/symbol` parameter gains two new optional fields:
734
735
736 ```typescript
737 interface WorkspaceSymbolParams {
738     /**
739      * Return only the symbols defined in the specified scope.
740      */
741     searchScope?: WorkspaceSymbolSearchScope;
742     /**
743      * Return only the symbols of specified kinds.
744      */
745     searchKind?: WorkspaceSymbolSearchKind;
746     ...
747 }
748
749 const enum WorkspaceSymbolSearchScope {
750     Workspace = "workspace",
751     WorkspaceAndDependencies = "workspaceAndDependencies"
752 }
753
754 const enum WorkspaceSymbolSearchKind {
755     OnlyTypes = "onlyTypes",
756     AllSymbols = "allSymbols"
757 }
758 ```
759
760 ## Client Commands
761
762 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/642
763
764 **Experimental Client Capability:** `{ "commands?": ClientCommandOptions }`
765
766 Certain LSP types originating on the server, notably code lenses, embed commands.
767 Commands can be serviced either by the server or by the client.
768 However, the server doesn't know which commands are available on the client.
769
770 This extensions allows the client to communicate this info.
771
772
773 ```typescript
774 export interface ClientCommandOptions {
775     /**
776      * The commands to be executed on the client
777      */
778     commands: string[];
779 }
780 ```