rust/docs/dev/lsp-extensions.md

637 lines
17 KiB
Markdown
Raw Normal View History

<!---
lsp_ext.rs hash: b19ddc3ab8767af9
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue:
https://github.com/rust-analyzer/rust-analyzer/issues/4604
--->
2020-05-17 14:24:33 -05:00
# LSP Extensions
This document describes LSP extensions used by rust-analyzer.
It's a best effort document, when in doubt, consult the source (and send a PR with clarification ;-) ).
We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority.
2020-05-24 08:49:32 -05:00
All capabilities are enabled via `experimental` field of `ClientCapabilities` or `ServerCapabilities`.
Requests which we hope to upstream live under `experimental/` namespace.
Requests, which are likely to always remain specific to `rust-analyzer` are under `rust-analyzer/` namespace.
2020-05-17 14:24:33 -05:00
2020-06-01 10:51:15 -05:00
If you want to be notified about the changes to this document, subscribe to [#4604](https://github.com/rust-analyzer/rust-analyzer/issues/4604).
2021-02-14 09:23:16 -06:00
## UTF-8 offsets
rust-analyzer supports clangd's extension for opting into UTF-8 as the coordinate space for offsets (by default, LSP uses UTF-16 offsets).
https://clangd.llvm.org/extensions.html#utf-8-offsets
## `initializationOptions`
2020-07-22 09:05:36 -05:00
For `initializationOptions`, `rust-analyzer` expects `"rust-analyzer"` section of the configuration.
That is, `rust-analyzer` usually sends `"workspace/configuration"` request with `{ "items": ["rust-analyzer"] }` payload.
`initializationOptions` should contain the same data that would be in the first item of the result.
2020-07-22 09:05:36 -05:00
If a language client does not know about `rust-analyzer`'s configuration options it can get sensible defaults by doing any of the following:
* Not sending `initializationOptions`
* Send `"initializationOptions": null`
* Send `"initializationOptions": {}`
2020-05-22 10:29:55 -05:00
## Snippet `TextEdit`
2020-05-17 14:24:33 -05:00
2020-05-22 12:14:14 -05:00
**Issue:** https://github.com/microsoft/language-server-protocol/issues/724
2021-03-09 03:02:20 -06:00
**Experimental Client Capability:** `{ "snippetTextEdit": boolean }`
2020-05-17 14:24:33 -05:00
If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s:
```typescript
interface SnippetTextEdit extends TextEdit {
insertTextFormat?: InsertTextFormat;
}
```
```typescript
export interface TextDocumentEdit {
2021-04-06 10:37:41 -05:00
textDocument: OptionalVersionedTextDocumentIdentifier;
edits: (TextEdit | SnippetTextEdit)[];
2020-05-17 14:24:33 -05:00
}
```
When applying such code action, the editor should insert snippet, with tab stops and placeholder.
At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`.
2020-05-21 12:50:23 -05:00
### Example
"Add `derive`" code action transforms `struct S;` into `#[derive($0)] struct S;`
### Unresolved Questions
* Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)?
* Can snippets span multiple files (so far, no)?
2020-05-24 08:49:32 -05:00
## `CodeAction` Groups
**Issue:** https://github.com/microsoft/language-server-protocol/issues/994
2021-03-09 03:02:20 -06:00
**Experimental Client Capability:** `{ "codeActionGroup": boolean }`
2020-05-24 08:49:32 -05:00
If this capability is set, `CodeAction` returned from the server contain an additional field, `group`:
```typescript
interface CodeAction {
title: string;
group?: string;
...
}
```
All code-actions with the same `group` should be grouped under single (extendable) entry in lightbulb menu.
The set of actions `[ { title: "foo" }, { group: "frobnicate", title: "bar" }, { group: "frobnicate", title: "baz" }]` should be rendered as
```
💡
+-------------+
| foo |
+-------------+-----+
| frobnicate >| bar |
+-------------+-----+
| baz |
+-----+
```
Alternatively, selecting `frobnicate` could present a user with an additional menu to choose between `bar` and `baz`.
### Example
```rust
fn main() {
let x: Entry/*cursor here*/ = todo!();
}
```
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.
### Unresolved Questions
* Is a fixed two-level structure enough?
* Should we devise a general way to encode custom interaction protocols for GUI refactorings?
## Parent Module
**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002
2021-03-09 03:02:20 -06:00
**Experimental Server Capability:** `{ "parentModule": boolean }`
2020-09-03 03:13:02 -05:00
This request is sent from client to server to handle "Goto Parent Module" editor action.
**Method:** `experimental/parentModule`
**Request:** `TextDocumentPositionParams`
**Response:** `Location | Location[] | LocationLink[] | null`
### Example
```rust
// src/main.rs
mod foo;
// src/foo.rs
/* cursor here*/
```
`experimental/parentModule` returns a single `Link` to the `mod foo;` declaration.
### Unresolved Question
* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
2021-04-06 10:37:41 -05:00
This is the approach IntelliJ Rust is taking.
However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
2021-04-06 10:37:41 -05:00
If you want super module, but the cursor happens to be inside an overridden function, the behavior with single "gotoSuper" request is surprising.
2020-05-22 10:29:55 -05:00
## Join Lines
2020-05-21 12:50:23 -05:00
2020-05-22 12:14:14 -05:00
**Issue:** https://github.com/microsoft/language-server-protocol/issues/992
2021-03-09 03:02:20 -06:00
**Experimental Server Capability:** `{ "joinLines": boolean }`
2020-05-21 12:50:23 -05:00
2020-09-03 03:13:02 -05:00
This request is sent from client to server to handle "Join Lines" editor action.
2020-05-21 12:50:23 -05:00
2020-05-24 09:18:46 -05:00
**Method:** `experimental/joinLines`
2020-05-21 12:50:23 -05:00
**Request:**
```typescript
interface JoinLinesParams {
textDocument: TextDocumentIdentifier,
/// Currently active selections/cursor offsets.
/// This is an array to support multiple cursors.
ranges: Range[],
}
```
**Response:** `TextEdit[]`
2020-05-21 12:50:23 -05:00
### Example
```rust
fn main() {
/*cursor here*/let x = {
92
};
}
```
`experimental/joinLines` yields (curly braces are automagically removed)
2020-05-21 12:50:23 -05:00
```rust
fn main() {
let x = 92;
}
```
### Unresolved Question
* What is the position of the cursor after `joinLines`?
2021-04-06 10:37:41 -05:00
Currently, this is left to editor's discretion, but it might be useful to specify on the server via snippets.
2020-05-21 12:50:23 -05:00
However, it then becomes unclear how it works with multi cursor.
2020-05-21 17:28:49 -05:00
2020-05-25 07:12:53 -05:00
## On Enter
**Issue:** https://github.com/microsoft/language-server-protocol/issues/1001
2021-03-09 03:02:20 -06:00
**Experimental Server Capability:** `{ "onEnter": boolean }`
2020-05-25 07:12:53 -05:00
2020-09-03 03:13:02 -05:00
This request is sent from client to server to handle <kbd>Enter</kbd> keypress.
2020-05-25 07:12:53 -05:00
**Method:** `experimental/onEnter`
**Request:**: `TextDocumentPositionParams`
**Response:**
```typescript
SnippetTextEdit[]
```
### Example
```rust
fn main() {
// Some /*cursor here*/ docs
let x = 92;
}
```
`experimental/onEnter` returns the following snippet
```rust
fn main() {
// Some
// $0 docs
let x = 92;
}
```
The primary goal of `onEnter` is to handle automatic indentation when opening a new line.
This is not yet implemented.
The secondary goal is to handle fixing up syntax, like continuing doc strings and comments, and escaping `\n` in string literals.
As proper cursor positioning is raison-d'etat for `onEnter`, it uses `SnippetTextEdit`.
### Unresolved Question
* How to deal with synchronicity of the request?
One option is to require the client to block until the server returns the response.
Another option is to do a OT-style merging of edits from client and server.
2021-02-03 06:40:24 -06:00
A third option is to do a record-replay: client applies heuristic on enter immediately, then applies all user's keypresses.
2020-05-25 07:12:53 -05:00
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.
* How to deal with multiple carets?
* Should we extend this to arbitrary typed events and not just `onEnter`?
2020-05-21 17:28:49 -05:00
## Structural Search Replace (SSR)
2021-03-09 03:02:20 -06:00
**Experimental Server Capability:** `{ "ssr": boolean }`
2020-05-21 17:28:49 -05:00
2020-09-03 03:13:02 -05:00
This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source.
2020-05-21 17:28:49 -05:00
**Method:** `experimental/ssr`
**Request:**
```typescript
interface SsrParams {
/// Search query.
/// The specific syntax is specified outside of the protocol.
query: string,
/// If true, only check the syntax of the query and don't compute the actual edit.
parseOnly: bool,
/// The current text document. This and `position` will be used to determine in what scope
/// paths in `query` should be resolved.
textDocument: lc.TextDocumentIdentifier;
/// Position where SSR was invoked.
position: lc.Position;
2020-05-21 17:28:49 -05:00
}
```
**Response:**
```typescript
WorkspaceEdit
```
### Example
SSR with query `foo($a, $b) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z)` into `(y + 5).foo(z)`.
2020-05-21 17:28:49 -05:00
### Unresolved Question
* Probably needs search without replace mode
* Needs a way to limit the scope to certain files.
2020-05-24 09:18:46 -05:00
## Matching Brace
**Issue:** https://github.com/microsoft/language-server-protocol/issues/999
2021-03-09 03:02:20 -06:00
**Experimental Server Capability:** `{ "matchingBrace": boolean }`
2020-05-24 09:18:46 -05:00
2020-09-01 03:36:48 -05:00
This request is sent from client to server to handle "Matching Brace" editor action.
2020-05-24 09:18:46 -05:00
**Method:** `experimental/matchingBrace`
**Request:**
```typescript
interface MatchingBraceParams {
textDocument: TextDocumentIdentifier,
/// Position for each cursor
positions: Position[],
}
```
**Response:**
```typescript
Position[]
```
### Example
```rust
fn main() {
let x: Vec<()>/*cursor here*/ = vec![]
}
```
`experimental/matchingBrace` yields the position of `<`.
In many cases, matching braces can be handled by the editor.
However, some cases (like disambiguating between generics and comparison operations) need a real parser.
Moreover, it would be cool if editors didn't need to implement even basic language parsing
### Unresolved Question
2021-04-06 10:37:41 -05:00
* Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
2020-05-24 09:18:46 -05:00
This is how `SelectionRange` request works.
* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
2020-06-02 10:34:18 -05:00
## Runnables
**Issue:** https://github.com/microsoft/language-server-protocol/issues/944
2021-03-09 03:02:20 -06:00
**Experimental Server Capability:** `{ "runnables": { "kinds": string[] } }`
2020-06-02 10:34:18 -05:00
2020-09-03 03:13:02 -05:00
This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
2020-06-02 10:34:18 -05:00
**Method:** `experimental/runnables`
**Request:**
```typescript
interface RunnablesParams {
textDocument: TextDocumentIdentifier;
/// If null, compute runnables for the whole file.
position?: Position;
}
```
**Response:** `Runnable[]`
```typescript
interface Runnable {
label: string;
/// If this Runnable is associated with a specific function/module, etc, the location of this item
location?: LocationLink;
/// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities,
// the type of `args` is specific to `kind`. The actual running is handled by the client.
kind: string;
args: any;
}
```
rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look like this:
```typescript
{
workspaceRoot?: string;
cargoArgs: string[];
cargoExtraArgs: string[];
2020-06-02 10:34:18 -05:00
executableArgs: string[];
expectTest?: boolean;
overrideCargo?: string;
2020-06-02 10:34:18 -05:00
}
```
2020-09-01 03:36:48 -05:00
## Open External Documentation
2020-09-03 03:13:02 -05:00
This request is sent from client to server to get a URL to documentation for the symbol under the cursor, if available.
2020-09-01 03:36:48 -05:00
**Method** `experimental/externalDocs`
**Request:**: `TextDocumentPositionParams`
**Response** `string | null`
## Analyzer Status
**Method:** `rust-analyzer/analyzerStatus`
**Request:**
```typescript
interface AnalyzerStatusParams {
/// If specified, show dependencies of the current file.
textDocument?: TextDocumentIdentifier;
}
```
**Response:** `string`
Returns internal status message, mostly for debugging purposes.
2020-07-01 07:57:59 -05:00
## Reload Workspace
2020-07-01 07:57:59 -05:00
**Method:** `rust-analyzer/reloadWorkspace`
**Request:** `null`
**Response:** `null`
2020-07-01 07:57:59 -05:00
Reloads project information (that is, re-executes `cargo metadata`).
2021-04-06 06:16:35 -05:00
## Server Status
2020-07-02 05:37:04 -05:00
2021-04-06 06:16:35 -05:00
**Experimental Client Capability:** `{ "serverStatus": boolean }`
2020-07-02 05:37:04 -05:00
2021-04-06 06:16:35 -05:00
**Method:** `experimental/serverStatus`
2020-07-02 05:37:04 -05:00
2020-08-17 06:56:27 -05:00
**Notification:**
```typescript
2021-04-06 06:16:35 -05:00
interface ServerStatusParams {
/// `ok` means that the server is completely functional.
///
/// `warning` means that the server is partially functional.
/// It can server requests, but some results might be wrong due to,
/// for example, some missing dependencies.
///
/// `error` means that the server is not functional. For example,
/// there's a fatal build configuration problem.
health: "ok" | "warning" | "error",
/// Is there any pending background work which might change the status?
/// For example, are dependencies being downloaded?
quiescent: bool,
/// Explanatory message to show on hover.
message?: string,
2020-08-17 06:56:27 -05:00
}
```
2020-07-02 05:37:04 -05:00
This notification is sent from server to client.
2021-04-06 06:16:35 -05:00
The client can use it to display *persistent* status to the user (in modline).
It is similar to the `showMessage`, but is intended for stares rather than point-in-time events.
2020-07-02 05:37:04 -05:00
## Syntax Tree
**Method:** `rust-analyzer/syntaxTree`
**Request:**
```typescript
interface SyntaxTeeParams {
textDocument: TextDocumentIdentifier,
range?: Range,
}
```
**Response:** `string`
Returns textual representation of a parse tree for the file/selected region.
Primarily for debugging, but very useful for all people working on rust-analyzer itself.
## View Hir
**Method:** `rust-analyzer/viewHir`
**Request:** `TextDocumentPositionParams`
**Response:** `string`
Returns a textual representation of the HIR of the function containing the cursor.
For debugging or when working on rust-analyzer itself.
## Expand Macro
**Method:** `rust-analyzer/expandMacro`
**Request:**
```typescript
interface ExpandMacroParams {
textDocument: TextDocumentIdentifier,
position: Position,
}
```
**Response:**
```typescript
interface ExpandedMacro {
name: string,
expansion: string,
}
```
Expands macro call at a given position.
## Inlay Hints
**Method:** `rust-analyzer/inlayHints`
2020-09-03 03:13:02 -05:00
This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
Generally, the client should re-query inlay hints after every modification.
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.
2021-04-06 10:37:41 -05:00
Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
**Request:**
```typescript
interface InlayHintsParams {
textDocument: TextDocumentIdentifier,
}
```
**Response:** `InlayHint[]`
```typescript
interface InlayHint {
2020-05-27 06:04:57 -05:00
kind: "TypeHint" | "ParameterHint" | "ChainingHint",
range: Range,
label: string,
}
```
## Hover Actions
2021-03-09 03:02:20 -06:00
**Experimental Client Capability:** `{ "hoverActions": boolean }`
If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`:
```typescript
interface Hover {
...
actions?: CommandLinkGroup[];
}
interface CommandLink extends Command {
/**
* A tooltip for the command, when represented in the UI.
*/
tooltip?: string;
}
interface CommandLinkGroup {
title?: string;
commands: CommandLink[];
}
```
Such actions on the client side are appended to a hover bottom as command links:
```
+-----------------------------+
| Hover content |
| |
+-----------------------------+
| _Action1_ | _Action2_ | <- first group, no TITLE
+-----------------------------+
| TITLE _Action1_ | _Action2_ | <- second group
+-----------------------------+
...
2020-07-01 07:57:59 -05:00
```
2020-11-12 19:48:07 -06:00
## Open Cargo.toml
**Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6462
This request is sent from client to server to open the current project's Cargo.toml
**Method:** `experimental/openCargoToml`
**Request:** `OpenCargoTomlParams`
**Response:** `Location | null`
### Example
```rust
// Cargo.toml
[package]
// src/main.rs
/* cursor here*/
```
`experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword.
2021-02-27 12:07:58 -06:00
## Related tests
This request is sent from client to server to get the list of tests for the specified position.
**Method:** `rust-analyzer/relatedTests`
2021-03-11 08:59:27 -06:00
**Request:** `TextDocumentPositionParams`
2021-02-27 12:07:58 -06:00
**Response:** `TestInfo[]`
```typescript
interface TestInfo {
runnable: Runnable;
}
```
2021-03-16 07:37:00 -05:00
## Hover Actions
**Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6823
This request is sent from client to server to move item under cursor or selection in some direction.
2021-04-04 13:49:32 -05:00
**Method:** `experimental/moveItem`
2021-03-16 07:37:00 -05:00
**Request:** `MoveItemParams`
**Response:** `SnippetTextEdit[]`
2021-03-16 07:37:00 -05:00
```typescript
export interface MoveItemParams {
textDocument: lc.TextDocumentIdentifier,
range: lc.Range,
direction: Direction
}
export const enum Direction {
Up = "Up",
Down = "Down"
}
```