1<!--- 2lsp_ext.rs hash: 2d60bbffe70ae198 3 4If you need to change the above hash to make the test pass, please check if you 5need 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 13This document describes LSP extensions used by rust-analyzer. 14It's a best effort document, when in doubt, consult the source (and send a PR with clarification ;-) ). 15We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority. 16All capabilities are enabled via the `experimental` field of `ClientCapabilities` or `ServerCapabilities`. 17Requests which we hope to upstream live under `experimental/` namespace. 18Requests, which are likely to always remain specific to `rust-analyzer` are under `rust-analyzer/` namespace. 19 20If 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 26The `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. 29However, the server can't do this during initialization. 30At the same time some essential configuration parameters are needed early on, before servicing requests. 31For this reason, we ask that `initializationOptions` contains the configuration, as if the server did make a `"workspace/configuration"` request. 32 33If 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 44If 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 47interface SnippetTextEdit extends TextEdit { 48 insertTextFormat?: InsertTextFormat; 49 annotationId?: ChangeAnnotationIdentifier; 50} 51``` 52 53```typescript 54export interface TextDocumentEdit { 55 textDocument: OptionalVersionedTextDocumentIdentifier; 56 edits: (TextEdit | SnippetTextEdit)[]; 57} 58``` 59 60When applying such code action or text edit, the editor should insert snippet, with tab stops and placeholder. 61At 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 78If this capability is set, `CodeAction`s returned from the server contain an additional field, `group`: 79 80```typescript 81interface CodeAction { 82 title: string; 83 group?: string; 84 ... 85} 86``` 87 88All code-actions with the same `group` should be grouped under single (extendable) entry in lightbulb menu. 89The 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 102Alternatively, selecting `frobnicate` could present a user with an additional menu to choose between `bar` and `baz`. 103 104### Example 105 106```rust 107fn main() { 108 let x: Entry/*cursor here*/ = todo!(); 109} 110``` 111 112Invoking 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 125This 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 138mod 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 159This request is sent from client to server to handle "Join Lines" editor action. 160 161**Method:** `experimental/joinLines` 162 163**Request:** 164 165```typescript 166interface 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 179fn main() { 180 /*cursor here*/let x = { 181 92 182 }; 183} 184``` 185 186`experimental/joinLines` yields (curly braces are automagically removed) 187 188```rust 189fn 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 206This 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 215SnippetTextEdit[] 216``` 217 218### Example 219 220```rust 221fn main() { 222 // Some /*cursor here*/ docs 223 let x = 92; 224} 225``` 226 227`experimental/onEnter` returns the following snippet 228 229```rust 230fn main() { 231 // Some 232 // $0 docs 233 let x = 92; 234} 235``` 236 237The primary goal of `onEnter` is to handle automatic indentation when opening a new line. 238This is not yet implemented. 239The secondary goal is to handle fixing up syntax, like continuing doc strings and comments, and escaping `\n` in string literals. 240 241As 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 257This 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 264interface 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 283WorkspaceEdit 284``` 285 286### Example 287 288SSR 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 301This request is sent from client to server to handle "Matching Brace" editor action. 302 303**Method:** `experimental/matchingBrace` 304 305**Request:** 306 307```typescript 308interface MatchingBraceParams { 309 textDocument: TextDocumentIdentifier, 310 /// Position for each cursor 311 positions: Position[], 312} 313``` 314 315**Response:** 316 317```typescript 318Position[] 319``` 320 321### Example 322 323```rust 324fn main() { 325 let x: Vec<()>/*cursor here*/ = vec![] 326} 327``` 328 329`experimental/matchingBrace` yields the position of `<`. 330In many cases, matching braces can be handled by the editor. 331However, some cases (like disambiguating between generics and comparison operations) need a real parser. 332Moreover, 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](https://paredit.org/)-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 346This 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 353interface 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 363interface 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 374rust-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 389This request is sent from the client to the server to obtain web and local URL(s) for documentation related to the symbol under the cursor, if available. 390 391**Method:** `experimental/externalDocs` 392 393**Request:** `TextDocumentPositionParams` 394 395**Response:** `string | null` 396 397## Local Documentation 398 399**Experimental Client Capability:** `{ "localDocs": boolean }` 400 401If this capability is set, the `Open External Documentation` request returned from the server will have the following structure: 402 403```typescript 404interface ExternalDocsResponse { 405 web?: string; 406 local?: string; 407} 408``` 409 410## Analyzer Status 411 412**Method:** `rust-analyzer/analyzerStatus` 413 414**Request:** 415 416```typescript 417interface AnalyzerStatusParams { 418 /// If specified, show dependencies of the current file. 419 textDocument?: TextDocumentIdentifier; 420} 421``` 422 423**Response:** `string` 424 425Returns internal status message, mostly for debugging purposes. 426 427## Reload Workspace 428 429**Method:** `rust-analyzer/reloadWorkspace` 430 431**Request:** `null` 432 433**Response:** `null` 434 435Reloads project information (that is, re-executes `cargo metadata`). 436 437## Rebuild proc-macros 438 439**Method:** `rust-analyzer/rebuildProcMacros` 440 441**Request:** `null` 442 443**Response:** `null` 444 445Rebuilds build scripts and proc-macros, and runs the build scripts to reseed the build data. 446 447## Server Status 448 449**Experimental Client Capability:** `{ "serverStatusNotification": boolean }` 450 451**Method:** `experimental/serverStatus` 452 453**Notification:** 454 455```typescript 456interface ServerStatusParams { 457 /// `ok` means that the server is completely functional. 458 /// 459 /// `warning` means that the server is partially functional. 460 /// It can answer correctly to most requests, but some results 461 /// might be wrong due to, for example, some missing dependencies. 462 /// 463 /// `error` means that the server is not functional. For example, 464 /// there's a fatal build configuration problem. The server might 465 /// still give correct answers to simple requests, but most results 466 /// will be incomplete or wrong. 467 health: "ok" | "warning" | "error", 468 /// Is there any pending background work which might change the status? 469 /// For example, are dependencies being downloaded? 470 quiescent: boolean, 471 /// Explanatory message to show on hover. 472 message?: string, 473} 474``` 475 476This notification is sent from server to client. 477The client can use it to display *persistent* status to the user (in modline). 478It is similar to the `showMessage`, but is intended for stares rather than point-in-time events. 479 480Note that this functionality is intended primarily to inform the end user about the state of the server. 481In particular, it's valid for the client to completely ignore this extension. 482Clients are discouraged from but are allowed to use the `health` status to decide if it's worth sending a request to the server. 483 484### Controlling Flycheck 485 486The flycheck/checkOnSave feature can be controlled via notifications sent by the client to the server. 487 488**Method:** `rust-analyzer/runFlycheck` 489 490**Notification:** 491 492```typescript 493interface RunFlycheckParams { 494 /// The text document whose cargo workspace flycheck process should be started. 495 /// If the document is null or does not belong to a cargo workspace all flycheck processes will be started. 496 textDocument: lc.TextDocumentIdentifier | null; 497} 498``` 499 500Triggers the flycheck processes. 501 502 503**Method:** `rust-analyzer/clearFlycheck` 504 505**Notification:** 506 507```typescript 508interface ClearFlycheckParams {} 509``` 510 511Clears the flycheck diagnostics. 512 513**Method:** `rust-analyzer/cancelFlycheck` 514 515**Notification:** 516 517```typescript 518interface CancelFlycheckParams {} 519``` 520 521Cancels all running flycheck processes. 522 523## Syntax Tree 524 525**Method:** `rust-analyzer/syntaxTree` 526 527**Request:** 528 529```typescript 530interface SyntaxTreeParams { 531 textDocument: TextDocumentIdentifier, 532 range?: Range, 533} 534``` 535 536**Response:** `string` 537 538Returns textual representation of a parse tree for the file/selected region. 539Primarily for debugging, but very useful for all people working on rust-analyzer itself. 540 541## View Hir 542 543**Method:** `rust-analyzer/viewHir` 544 545**Request:** `TextDocumentPositionParams` 546 547**Response:** `string` 548 549Returns a textual representation of the HIR of the function containing the cursor. 550For debugging or when working on rust-analyzer itself. 551 552## View Mir 553 554**Method:** `rust-analyzer/viewMir` 555 556**Request:** `TextDocumentPositionParams` 557 558**Response:** `string` 559 560Returns a textual representation of the MIR of the function containing the cursor. 561For debugging or when working on rust-analyzer itself. 562 563## Interpret Function 564 565**Method:** `rust-analyzer/interpretFunction` 566 567**Request:** `TextDocumentPositionParams` 568 569**Response:** `string` 570 571Tries to evaluate the function using internal rust analyzer knowledge, without compiling 572the code. Currently evaluates the function under cursor, but will give a runnable in 573future. Highly experimental. 574 575## View File Text 576 577**Method:** `rust-analyzer/viewFileText` 578 579**Request:** `TextDocumentIdentifier` 580 581**Response:** `string` 582 583Returns the text of a file as seen by the server. 584This is for debugging file sync problems. 585 586## View ItemTree 587 588**Method:** `rust-analyzer/viewItemTree` 589 590**Request:** 591 592```typescript 593interface ViewItemTreeParams { 594 textDocument: TextDocumentIdentifier, 595} 596``` 597 598**Response:** `string` 599 600Returns a textual representation of the `ItemTree` of the currently open file, for debugging. 601 602## View Crate Graph 603 604**Method:** `rust-analyzer/viewCrateGraph` 605 606**Request:** 607 608```typescript 609interface ViewCrateGraphParams { 610 full: boolean, 611} 612``` 613 614**Response:** `string` 615 616Renders rust-analyzer's crate graph as an SVG image. 617 618If `full` is `true`, the graph includes non-workspace crates (crates.io dependencies as well as sysroot crates). 619 620## Shuffle Crate Graph 621 622**Method:** `rust-analyzer/shuffleCrateGraph` 623 624**Request:** `null` 625 626Shuffles the crate IDs in the crate graph, for debugging purposes. 627 628## Expand Macro 629 630**Method:** `rust-analyzer/expandMacro` 631 632**Request:** 633 634```typescript 635interface ExpandMacroParams { 636 textDocument: TextDocumentIdentifier, 637 position: Position, 638} 639``` 640 641**Response:** 642 643```typescript 644interface ExpandedMacro { 645 name: string, 646 expansion: string, 647} 648``` 649 650Expands macro call at a given position. 651 652## Hover Actions 653 654**Experimental Client Capability:** `{ "hoverActions": boolean }` 655 656If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: 657 658```typescript 659interface Hover { 660 ... 661 actions?: CommandLinkGroup[]; 662} 663 664interface CommandLink extends Command { 665 /** 666 * A tooltip for the command, when represented in the UI. 667 */ 668 tooltip?: string; 669} 670 671interface CommandLinkGroup { 672 title?: string; 673 commands: CommandLink[]; 674} 675``` 676 677Such actions on the client side are appended to a hover bottom as command links: 678``` 679 +-----------------------------+ 680 | Hover content | 681 | | 682 +-----------------------------+ 683 | _Action1_ | _Action2_ | <- first group, no TITLE 684 +-----------------------------+ 685 | TITLE _Action1_ | _Action2_ | <- second group 686 +-----------------------------+ 687 ... 688``` 689 690## Open Cargo.toml 691 692**Upstream Issue:** https://github.com/rust-lang/rust-analyzer/issues/6462 693 694**Experimental Server Capability:** `{ "openCargoToml": boolean }` 695 696This request is sent from client to server to open the current project's Cargo.toml 697 698**Method:** `experimental/openCargoToml` 699 700**Request:** `OpenCargoTomlParams` 701 702**Response:** `Location | null` 703 704 705### Example 706 707```rust 708// Cargo.toml 709[package] 710// src/main.rs 711 712/* cursor here*/ 713``` 714 715`experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword. 716 717## Related tests 718 719This request is sent from client to server to get the list of tests for the specified position. 720 721**Method:** `rust-analyzer/relatedTests` 722 723**Request:** `TextDocumentPositionParams` 724 725**Response:** `TestInfo[]` 726 727```typescript 728interface TestInfo { 729 runnable: Runnable; 730} 731``` 732 733## Hover Range 734 735**Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/377 736 737**Experimental Server Capability:** { "hoverRange": boolean } 738 739This extension allows passing a `Range` as a `position` field of `HoverParams`. 740The primary use-case is to use the hover request to show the type of the expression currently selected. 741 742```typescript 743interface HoverParams extends WorkDoneProgressParams { 744 textDocument: TextDocumentIdentifier; 745 position: Range | Position; 746} 747``` 748Whenever 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. 749 750### Example 751 752```rust 753fn main() { 754 let expression = $01 + 2 * 3$0; 755} 756``` 757 758Triggering a hover inside the selection above will show a result of `i32`. 759 760## Move Item 761 762**Upstream Issue:** https://github.com/rust-lang/rust-analyzer/issues/6823 763 764This request is sent from client to server to move item under cursor or selection in some direction. 765 766**Method:** `experimental/moveItem` 767 768**Request:** `MoveItemParams` 769 770**Response:** `SnippetTextEdit[]` 771 772```typescript 773export interface MoveItemParams { 774 textDocument: TextDocumentIdentifier, 775 range: Range, 776 direction: Direction 777} 778 779export const enum Direction { 780 Up = "Up", 781 Down = "Down" 782} 783``` 784 785## Workspace Symbols Filtering 786 787**Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/941 788 789**Experimental Server Capability:** `{ "workspaceSymbolScopeKindFiltering": boolean }` 790 791Extends the existing `workspace/symbol` request with ability to filter symbols by broad scope and kind of symbol. 792If this capability is set, `workspace/symbol` parameter gains two new optional fields: 793 794 795```typescript 796interface WorkspaceSymbolParams { 797 /** 798 * Return only the symbols defined in the specified scope. 799 */ 800 searchScope?: WorkspaceSymbolSearchScope; 801 /** 802 * Return only the symbols of specified kinds. 803 */ 804 searchKind?: WorkspaceSymbolSearchKind; 805 ... 806} 807 808const enum WorkspaceSymbolSearchScope { 809 Workspace = "workspace", 810 WorkspaceAndDependencies = "workspaceAndDependencies" 811} 812 813const enum WorkspaceSymbolSearchKind { 814 OnlyTypes = "onlyTypes", 815 AllSymbols = "allSymbols" 816} 817``` 818 819## Client Commands 820 821**Upstream Issue:** https://github.com/microsoft/language-server-protocol/issues/642 822 823**Experimental Client Capability:** `{ "commands?": ClientCommandOptions }` 824 825Certain LSP types originating on the server, notably code lenses, embed commands. 826Commands can be serviced either by the server or by the client. 827However, the server doesn't know which commands are available on the client. 828 829This extensions allows the client to communicate this info. 830 831 832```typescript 833export interface ClientCommandOptions { 834 /** 835 * The commands to be executed on the client 836 */ 837 commands: string[]; 838} 839``` 840 841## Colored Diagnostic Output 842 843**Experimental Client Capability:** `{ "colorDiagnosticOutput": boolean }` 844 845If this capability is set, the "full compiler diagnostics" provided by `checkOnSave` 846will include ANSI color and style codes to render the diagnostic in a similar manner 847as `cargo`. This is translated into `--message-format=json-diagnostic-rendered-ansi` 848when flycheck is run, instead of the default `--message-format=json`. 849 850The full compiler rendered diagnostics are included in the server response 851regardless of this capability: 852 853```typescript 854// https://microsoft.github.io/language-server-protocol/specifications/specification-current#diagnostic 855export interface Diagnostic { 856 ... 857 data?: { 858 /** 859 * The human-readable compiler output as it would be printed to a terminal. 860 * Includes ANSI color and style codes if the client has set the experimental 861 * `colorDiagnosticOutput` capability. 862 */ 863 rendered?: string; 864 }; 865} 866``` 867 868## Dependency Tree 869 870**Method:** `rust-analyzer/fetchDependencyList` 871 872**Request:** 873 874```typescript 875export interface FetchDependencyListParams {} 876``` 877 878**Response:** 879```typescript 880export interface FetchDependencyListResult { 881 crates: { 882 name: string; 883 version: string; 884 path: string; 885 }[]; 886} 887``` 888Returns all crates from this workspace, so it can be used create a viewTree to help navigate the dependency tree. 889