]> git.lizzy.rs Git - rust.git/blob - docs/dev/lsp-extensions.md
f0f981802ef241fa577cfbb2c89901b1241129f4
[rust.git] / docs / dev / lsp-extensions.md
1 <!---
2 lsp_ext.rs hash: 28a9d5a24b7ca396
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 ## `initializationOptions`
29
30 For `initializationOptions`, `rust-analyzer` expects `"rust-analyzer"` section of the configuration.
31 That is, `rust-analyzer` usually sends `"workspace/configuration"` request with `{ "items": ["rust-analyzer"] }` payload.
32 `initializationOptions` should contain the same data that would be in the first item of the result.
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  * Send `"initializationOptions": null`
36  * Send `"initializationOptions": {}`
37
38 ## Snippet `TextEdit`
39
40 **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 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, 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 **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` 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 **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 **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 **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 <kbd>Enter</kbd> keypress.
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: bool,
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: lc.TextDocumentIdentifier;
273     /// Position where SSR was invoked.
274     position: lc.Position;
275 }
276 ```
277
278 **Response:**
279
280 ```typescript
281 WorkspaceEdit
282 ```
283
284 ### Example
285
286 SSR with query `foo($a, $b) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z)` into `(y + 5).foo(z)`.
287
288 ### Unresolved Question
289
290 * Probably needs search without replace mode
291 * Needs a way to limit the scope to certain files.
292
293 ## Matching Brace
294
295 **Issue:** https://github.com/microsoft/language-server-protocol/issues/999
296
297 **Experimental Server Capability:** `{ "matchingBrace": boolean }`
298
299 This request is sent from client to server to handle "Matching Brace" editor action.
300
301 **Method:** `experimental/matchingBrace`
302
303 **Request:**
304
305 ```typescript
306 interface MatchingBraceParams {
307     textDocument: TextDocumentIdentifier,
308     /// Position for each cursor
309     positions: Position[],
310 }
311 ```
312
313 **Response:**
314
315 ```typescript
316 Position[]
317 ```
318
319 ### Example
320
321 ```rust
322 fn main() {
323     let x: Vec<()>/*cursor here*/ = vec![]
324 }
325 ```
326
327 `experimental/matchingBrace` yields the position of `<`.
328 In many cases, matching braces can be handled by the editor.
329 However, some cases (like disambiguating between generics and comparison operations) need a real parser.
330 Moreover, it would be cool if editors didn't need to implement even basic language parsing
331
332 ### Unresolved Question
333
334 * Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
335   This is how `SelectionRange` request works.
336 * Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
337
338 ## Runnables
339
340 **Issue:** https://github.com/microsoft/language-server-protocol/issues/944
341
342 **Experimental Server Capability:** `{ "runnables": { "kinds": string[] } }`
343
344 This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
345
346 **Method:** `experimental/runnables`
347
348 **Request:**
349
350 ```typescript
351 interface RunnablesParams {
352     textDocument: TextDocumentIdentifier;
353     /// If null, compute runnables for the whole file.
354     position?: Position;
355 }
356 ```
357
358 **Response:** `Runnable[]`
359
360 ```typescript
361 interface Runnable {
362     label: string;
363     /// If this Runnable is associated with a specific function/module, etc, the location of this item
364     location?: LocationLink;
365     /// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities,
366     // the type of `args` is specific to `kind`. The actual running is handled by the client.
367     kind: string;
368     args: any;
369 }
370 ```
371
372 rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look like this:
373
374 ```typescript
375 {
376     workspaceRoot?: string;
377     cargoArgs: string[];
378     cargoExtraArgs: string[];
379     executableArgs: string[];
380     expectTest?: boolean;
381     overrideCargo?: string;
382 }
383 ```
384
385 ## Open External Documentation
386
387 This request is sent from client to server to get a URL to documentation for the symbol under the cursor, if available.
388
389 **Method** `experimental/externalDocs`
390
391 **Request:**: `TextDocumentPositionParams`
392
393 **Response** `string | null`
394
395
396 ## Analyzer Status
397
398 **Method:** `rust-analyzer/analyzerStatus`
399
400 **Request:**
401
402 ```typescript
403 interface AnalyzerStatusParams {
404     /// If specified, show dependencies of the current file.
405     textDocument?: TextDocumentIdentifier;
406 }
407 ```
408
409 **Response:** `string`
410
411 Returns internal status message, mostly for debugging purposes.
412
413 ## Reload Workspace
414
415 **Method:** `rust-analyzer/reloadWorkspace`
416
417 **Request:** `null`
418
419 **Response:** `null`
420
421 Reloads project information (that is, re-executes `cargo metadata`).
422
423 ## Server Status
424
425 **Experimental Client Capability:** `{ "serverStatusNotification": boolean }`
426
427 **Method:** `experimental/serverStatus`
428
429 **Notification:**
430
431 ```typescript
432 interface ServerStatusParams {
433     /// `ok` means that the server is completely functional.
434     ///
435     /// `warning` means that the server is partially functional.
436     /// It can answer correctly to most requests, but some results
437     /// might be wrong due to, for example, some missing dependencies.
438     ///
439     /// `error` means that the server is not functional. For example,
440     /// there's a fatal build configuration problem. The server might
441     /// still give correct answers to simple requests, but most results
442     /// will be incomplete or wrong.
443     health: "ok" | "warning" | "error",
444     /// Is there any pending background work which might change the status?
445     /// For example, are dependencies being downloaded?
446     quiescent: bool,
447     /// Explanatory message to show on hover.
448     message?: string,
449 }
450 ```
451
452 This notification is sent from server to client.
453 The client can use it to display *persistent* status to the user (in modline).
454 It is similar to the `showMessage`, but is intended for stares rather than point-in-time events.
455
456 Note that this functionality is intended primarily to inform the end user about the state of the server.
457 In particular, it's valid for the client to completely ignore this extension.
458 Clients are discouraged from but are allowed to use the `health` status to decide if it's worth sending a request to the server.
459
460 ## Syntax Tree
461
462 **Method:** `rust-analyzer/syntaxTree`
463
464 **Request:**
465
466 ```typescript
467 interface SyntaxTeeParams {
468     textDocument: TextDocumentIdentifier,
469     range?: Range,
470 }
471 ```
472
473 **Response:** `string`
474
475 Returns textual representation of a parse tree for the file/selected region.
476 Primarily for debugging, but very useful for all people working on rust-analyzer itself.
477
478 ## View Hir
479
480 **Method:** `rust-analyzer/viewHir`
481
482 **Request:** `TextDocumentPositionParams`
483
484 **Response:** `string`
485
486 Returns a textual representation of the HIR of the function containing the cursor.
487 For debugging or when working on rust-analyzer itself.
488
489 ## Expand Macro
490
491 **Method:** `rust-analyzer/expandMacro`
492
493 **Request:**
494
495 ```typescript
496 interface ExpandMacroParams {
497     textDocument: TextDocumentIdentifier,
498     position: Position,
499 }
500 ```
501
502 **Response:**
503
504 ```typescript
505 interface ExpandedMacro {
506     name: string,
507     expansion: string,
508 }
509 ```
510
511 Expands macro call at a given position.
512
513 ## Inlay Hints
514
515 **Method:** `rust-analyzer/inlayHints`
516
517 This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
518 Generally, the client should re-query inlay hints after every modification.
519 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.
520 Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
521
522 **Request:**
523
524 ```typescript
525 interface InlayHintsParams {
526     textDocument: TextDocumentIdentifier,
527 }
528 ```
529
530 **Response:** `InlayHint[]`
531
532 ```typescript
533 interface InlayHint {
534     kind: "TypeHint" | "ParameterHint" | "ChainingHint",
535     range: Range,
536     label: string,
537 }
538 ```
539
540 ## Hover Actions
541
542 **Experimental Client Capability:** `{ "hoverActions": boolean }`
543
544 If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`:
545
546 ```typescript
547 interface Hover {
548     ...
549     actions?: CommandLinkGroup[];
550 }
551
552 interface CommandLink extends Command {
553     /**
554      * A tooltip for the command, when represented in the UI.
555      */
556     tooltip?: string;
557 }
558
559 interface CommandLinkGroup {
560     title?: string;
561     commands: CommandLink[];
562 }
563 ```
564
565 Such actions on the client side are appended to a hover bottom as command links:
566 ```
567   +-----------------------------+
568   | Hover content               |
569   |                             |
570   +-----------------------------+
571   | _Action1_ | _Action2_       |  <- first group, no TITLE
572   +-----------------------------+
573   | TITLE _Action1_ | _Action2_ |  <- second group
574   +-----------------------------+
575   ...
576 ```
577
578 ## Open Cargo.toml
579
580 **Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6462
581
582 This request is sent from client to server to open the current project's Cargo.toml
583
584 **Method:** `experimental/openCargoToml`
585
586 **Request:** `OpenCargoTomlParams`
587
588 **Response:** `Location | null`
589
590
591 ### Example
592
593 ```rust
594 // Cargo.toml
595 [package]
596 // src/main.rs
597
598 /* cursor here*/
599 ```
600
601 `experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword.
602
603 ## Related tests
604
605 This request is sent from client to server to get the list of tests for the specified position.
606
607 **Method:** `rust-analyzer/relatedTests`
608
609 **Request:** `TextDocumentPositionParams`
610
611 **Response:** `TestInfo[]`
612
613 ```typescript
614 interface TestInfo {
615     runnable: Runnable;
616 }
617 ```
618
619 ## Hover Actions
620
621 **Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6823
622
623 This request is sent from client to server to move item under cursor or selection in some direction.
624
625 **Method:** `experimental/moveItem`
626
627 **Request:** `MoveItemParams`
628
629 **Response:** `SnippetTextEdit[]`
630
631 ```typescript
632 export interface MoveItemParams {
633     textDocument: lc.TextDocumentIdentifier,
634     range: lc.Range,
635     direction: Direction
636 }
637
638 export const enum Direction {
639     Up = "Up",
640     Down = "Down"
641 }
642 ```