From 0ebb25b29b0988be89f42091fd373ea58d7ff9fb Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 25 May 2020 15:55:25 +0200 Subject: [PATCH] Document `parentModule` experimental LSP request --- crates/rust-analyzer/src/caps.rs | 1 + crates/rust-analyzer/src/lsp_ext.rs | 6 +-- .../rust-analyzer/src/main_loop/handlers.rs | 32 +++++---------- crates/rust-analyzer/src/to_proto.rs | 15 +++++-- docs/dev/lsp-extensions.md | 40 ++++++++++++++++--- editors/code/src/commands.ts | 6 +-- editors/code/src/lsp_ext.ts | 2 +- 7 files changed, 64 insertions(+), 38 deletions(-) diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index d55cbb15fe..345693524e 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -86,6 +86,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti "joinLines": true, "ssr": true, "onEnter": true, + "parentModule": true, })), } } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index c571c62aed..acb1dacb6b 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, path::PathBuf}; use lsp_types::request::Request; -use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; +use lsp_types::{Position, Range, TextDocumentIdentifier}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; @@ -79,8 +79,8 @@ pub enum ParentModule {} impl Request for ParentModule { type Params = lsp_types::TextDocumentPositionParams; - type Result = Vec; - const METHOD: &'static str = "rust-analyzer/parentModule"; + type Result = Option; + const METHOD: &'static str = "experimental/parentModule"; } pub enum JoinLines {} diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 3ccc95c23e..1f910ff82b 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -344,11 +344,8 @@ pub fn handle_goto_definition( None => return Ok(None), Some(it) => it, }; - let res = to_proto::goto_definition_response( - &world, - FileRange { file_id: position.file_id, range: nav_info.range }, - nav_info.info, - )?; + let src = FileRange { file_id: position.file_id, range: nav_info.range }; + let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; Ok(Some(res)) } @@ -362,11 +359,8 @@ pub fn handle_goto_implementation( None => return Ok(None), Some(it) => it, }; - let res = to_proto::goto_definition_response( - &world, - FileRange { file_id: position.file_id, range: nav_info.range }, - nav_info.info, - )?; + let src = FileRange { file_id: position.file_id, range: nav_info.range }; + let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; Ok(Some(res)) } @@ -380,26 +374,20 @@ pub fn handle_goto_type_definition( None => return Ok(None), Some(it) => it, }; - let res = to_proto::goto_definition_response( - &world, - FileRange { file_id: position.file_id, range: nav_info.range }, - nav_info.info, - )?; + let src = FileRange { file_id: position.file_id, range: nav_info.range }; + let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; Ok(Some(res)) } pub fn handle_parent_module( world: WorldSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> Result> { let _p = profile("handle_parent_module"); let position = from_proto::file_position(&world, params)?; - world - .analysis() - .parent_module(position)? - .into_iter() - .map(|it| to_proto::location(&world, it.file_range())) - .collect::>>() + let navs = world.analysis().parent_module(position)?; + let res = to_proto::goto_definition_response(&world, None, navs)?; + Ok(Some(res)) } pub fn handle_runnables( diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 39d58f1e01..bb7594dbf3 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -403,13 +403,20 @@ pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result, target: NavigationTarget, ) -> Result { - let src_location = location(world, src)?; + let origin_selection_range = match src { + Some(src) => { + let line_index = world.analysis().file_line_index(src.file_id)?; + let range = range(&line_index, src.range); + Some(range) + } + None => None, + }; let (target_uri, target_range, target_selection_range) = location_info(world, target)?; let res = lsp_types::LocationLink { - origin_selection_range: Some(src_location.range), + origin_selection_range, target_uri, target_range, target_selection_range, @@ -432,7 +439,7 @@ fn location_info( pub(crate) fn goto_definition_response( world: &WorldSnapshot, - src: FileRange, + src: Option, targets: Vec, ) -> Result { if world.config.client_caps.location_link { diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 48147b1739..209f470eba 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -87,6 +87,40 @@ Invoking code action at this position will yield two code actions for importing * 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 + +**Server Capability:** `{ "parentModule": boolean }` + +This request is send 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. + This is the approach IntelliJ Rust is takeing. + However, experience shows that super module (which generally has a feeling of navigation between files) should be separate. + If you want super module, but the cursor happens to be inside an overriden function, the behavior with single "gotoSuper" request is surprising. + ## Join Lines **Issue:** https://github.com/microsoft/language-server-protocol/issues/992 @@ -108,11 +142,7 @@ interface JoinLinesParams { } ``` -**Response:** - -```typescript -TextEdit[] -``` +**Response:** `TextEdit[]` ### Example diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 49e3845d5b..86302db37c 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -138,10 +138,10 @@ export function parentModule(ctx: Ctx): Cmd { ), }); const loc = response[0]; - if (loc == null) return; + if (!loc) return; - const uri = client.protocol2CodeConverter.asUri(loc.uri); - const range = client.protocol2CodeConverter.asRange(loc.range); + const uri = client.protocol2CodeConverter.asUri(loc.targetUri); + const range = client.protocol2CodeConverter.asRange(loc.targetRange); const doc = await vscode.workspace.openTextDocument(uri); const e = await vscode.window.showTextDocument(doc); diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 2a06632619..4da12eb309 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -31,7 +31,7 @@ export interface MatchingBraceParams { } export const matchingBrace = new lc.RequestType("experimental/matchingBrace"); -export const parentModule = new lc.RequestType("rust-analyzer/parentModule"); +export const parentModule = new lc.RequestType("experimental/parentModule"); export interface JoinLinesParams { textDocument: lc.TextDocumentIdentifier;