]> git.lizzy.rs Git - rust.git/blob - docs/dev/lsp-extensions.md
Merge #11433
[rust.git] / docs / dev / lsp-extensions.md
1 <!---
2 lsp_ext.rs hash: 5b53b92c9f9d6650
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:** `rust-analyzer/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 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.
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     kind: "TypeHint" | "ParameterHint" | "ChainingHint",
585     range: Range,
586     label: string,
587 }
588 ```
589
590 ## Hover Actions
591
592 **Experimental Client Capability:** `{ "hoverActions": boolean }`
593
594 If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`:
595
596 ```typescript
597 interface Hover {
598     ...
599     actions?: CommandLinkGroup[];
600 }
601
602 interface CommandLink extends Command {
603     /**
604      * A tooltip for the command, when represented in the UI.
605      */
606     tooltip?: string;
607 }
608
609 interface CommandLinkGroup {
610     title?: string;
611     commands: CommandLink[];
612 }
613 ```
614
615 Such actions on the client side are appended to a hover bottom as command links:
616 ```
617   +-----------------------------+
618   | Hover content               |
619   |                             |
620   +-----------------------------+
621   | _Action1_ | _Action2_       |  <- first group, no TITLE
622   +-----------------------------+
623   | TITLE _Action1_ | _Action2_ |  <- second group
624   +-----------------------------+
625   ...
626 ```
627
628 ## Open Cargo.toml
629
630 **Upstream Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6462
631
632 **Experimental Server Capability:** `{ "openCargoToml": boolean }`
633
634 This request is sent from client to server to open the current project's Cargo.toml
635
636 **Method:** `experimental/openCargoToml`
637
638 **Request:** `OpenCargoTomlParams`
639
640 **Response:** `Location | null`
641
642
643 ### Example
644
645 ```rust
646 // Cargo.toml
647 [package]
648 // src/main.rs
649
650 /* cursor here*/
651 ```
652
653 `experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword.
654
655 ## Related tests
656
657 This request is sent from client to server to get the list of tests for the specified position.
658
659 **Method:** `rust-analyzer/relatedTests`
660
661 **Request:** `TextDocumentPositionParams`
662
663 **Response:** `TestInfo[]`
664
665 ```typescript
666 interface TestInfo {
667     runnable: Runnable;
668 }
669 ```
670
671 ## Hover Range
672
673 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/377
674
675 **Experimental Server Capability:** { "hoverRange": boolean }
676
677 This extension allows passing a `Range` as a `position` field of `HoverParams`.
678 The primary use-case is to use the hover request to show the type of the expression currently selected.
679
680 ```typescript
681 interface HoverParams extends WorkDoneProgressParams {
682     textDocument: TextDocumentIdentifier;
683     position: Range | Position;
684 }
685 ```
686 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.
687
688 ### Example
689
690 ```rust
691 fn main() {
692     let expression = $01 + 2 * 3$0;
693 }
694 ```
695
696 Triggering a hover inside the selection above will show a result of `i32`.
697
698 ## Move Item
699
700 **Upstream Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6823
701
702 This request is sent from client to server to move item under cursor or selection in some direction.
703
704 **Method:** `experimental/moveItem`
705
706 **Request:** `MoveItemParams`
707
708 **Response:** `SnippetTextEdit[]`
709
710 ```typescript
711 export interface MoveItemParams {
712     textDocument: TextDocumentIdentifier,
713     range: Range,
714     direction: Direction
715 }
716
717 export const enum Direction {
718     Up = "Up",
719     Down = "Down"
720 }
721 ```
722
723 ## Workspace Symbols Filtering
724
725 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/941
726
727 **Experimental Server Capability:** `{ "workspaceSymbolScopeKindFiltering": boolean }`
728
729 Extends the existing `workspace/symbol` request with ability to filter symbols by broad scope and kind of symbol.
730 If this capability is set, `workspace/symbol` parameter gains two new optional fields:
731
732
733 ```typescript
734 interface WorkspaceSymbolParams {
735     /**
736      * Return only the symbols defined in the specified scope.
737      */
738     searchScope?: WorkspaceSymbolSearchScope;
739     /**
740      * Return only the symbols of specified kinds.
741      */
742     searchKind?: WorkspaceSymbolSearchKind;
743     ...
744 }
745
746 const enum WorkspaceSymbolSearchScope {
747     Workspace = "workspace",
748     WorkspaceAndDependencies = "workspaceAndDependencies"
749 }
750
751 const enum WorkspaceSymbolSearchKind {
752     OnlyTypes = "onlyTypes",
753     AllSymbols = "allSymbols"
754 }
755 ```
756
757 ## Client Commands
758
759 **Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/642
760
761 **Experimental Client Capability:** `{ "commands?": ClientCommandOptions }`
762
763 Certain LSP types originating on the server, notably code lenses, embed commands.
764 Commands can be serviced either by the server or by the client.
765 However, the server doesn't know which commands are available on the client.
766
767 This extensions allows the client to communicate this info.
768
769
770 ```typescript
771 export interface ClientCommandOptions {
772     /**
773      * The commands to be executed on the client
774      */
775     commands: string[];
776 }
777 ```