From 59c755227d3f6903095cf03bce5e76b7747138ec Mon Sep 17 00:00:00 2001 From: rainy-me Date: Thu, 14 Oct 2021 07:16:42 +0900 Subject: [PATCH] Provide navigations to parent modules --- crates/project_model/src/cargo_workspace.rs | 35 ++++++++++++++++++- crates/rust-analyzer/src/handlers.rs | 38 ++++++++++++--------- editors/code/src/commands.ts | 2 ++ editors/code/src/lsp_ext.ts | 2 +- 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index 1417ac2fa1d..a55901a8f75 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs @@ -1,6 +1,6 @@ //! See [`CargoWorkspace`]. -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::iter; use std::path::PathBuf; use std::{ops, process::Command}; @@ -400,6 +400,39 @@ impl CargoWorkspace { } } + pub fn parent_manifests(&self, manifest_path: &ManifestPath) -> Option> { + let mut found = false; + let parent_manifests = self + .packages() + .filter_map(|pkg| { + if !found && &self[pkg].manifest == manifest_path { + found = true + } + self[pkg].dependencies.iter().find_map(|dep| { + if &self[dep.pkg].manifest == manifest_path { + return Some(self[pkg].manifest.clone()); + } + None + }) + }) + .collect::>(); + + // some packages has this pkg as dep. return their manifests + if parent_manifests.len() > 0 { + return Some(parent_manifests); + } + + // this pkg is inside this cargo workspace, fallback to workspace root + if found { + return Some(vec![ + ManifestPath::try_from(self.workspace_root().join("Cargo.toml")).ok()? + ]); + } + + // not in this workspace + None + } + fn is_unique(&self, name: &str) -> bool { self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 306de042517..0105496ba2a 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -3,6 +3,7 @@ //! `ide` crate. use std::{ + convert::TryFrom, io::Write as _, process::{self, Stdio}, }; @@ -26,10 +27,11 @@ use lsp_types::{ SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, }; -use project_model::{ProjectWorkspace, TargetKind}; +use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize, T}; +use vfs::AbsPathBuf; use crate::{ cargo_target_spec::CargoTargetSpec, @@ -606,28 +608,30 @@ pub(crate) fn handle_parent_module( let _p = profile::span("handle_parent_module"); if let Ok(file_path) = ¶ms.text_document.uri.to_file_path() { if file_path.file_name().unwrap_or_default() == "Cargo.toml" { - // search parent workspace and collect a list of `LocationLink`path, - // since cargo.toml doesn't have file_id + // search workspaces for parent packages or fallback to workspace root + let abs_path_buf = match AbsPathBuf::try_from(file_path.to_path_buf()).ok() { + Some(abs_path_buf) => abs_path_buf, + None => return Ok(None), + }; + + let manifest_path = match ManifestPath::try_from(abs_path_buf).ok() { + Some(manifest_path) => manifest_path, + None => return Ok(None), + }; + let links: Vec = snap .workspaces .iter() .filter_map(|ws| match ws { - ProjectWorkspace::Cargo { cargo, .. } => cargo - .packages() - .find(|&pkg| cargo[pkg].manifest.as_ref() == file_path) - .and_then(|_| Some(cargo)), + ProjectWorkspace::Cargo { cargo, .. } => cargo.parent_manifests(&manifest_path), _ => None, }) - .map(|ws| { - let target_cargo_toml_path = ws.workspace_root().join("Cargo.toml"); - let target_cargo_toml_url = - to_proto::url_from_abs_path(&target_cargo_toml_path); - LocationLink { - origin_selection_range: None, - target_uri: target_cargo_toml_url, - target_range: Range::default(), - target_selection_range: Range::default(), - } + .flatten() + .map(|parent_manifest_path| LocationLink { + origin_selection_range: None, + target_uri: to_proto::url_from_abs_path(&parent_manifest_path), + target_range: Range::default(), + target_selection_range: Range::default(), }) .collect::<_>(); return Ok(Some(links.into())); diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 623e33c7efb..c9385361f88 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -189,12 +189,14 @@ export function parentModule(ctx: Ctx): Cmd { const client = ctx.client; if (!editor || !client) return; if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return; + const locations = await client.sendRequest(ra.parentModule, { textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), position: client.code2ProtocolConverter.asPosition( editor.selection.active, ), }); + if (!locations) return; if (locations.length === 1) { const loc = locations[0]; diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index ac632a01567..90796e611e6 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -62,7 +62,7 @@ export interface MatchingBraceParams { } export const matchingBrace = new lc.RequestType("experimental/matchingBrace"); -export const parentModule = new lc.RequestType("experimental/parentModule"); +export const parentModule = new lc.RequestType("experimental/parentModule"); export interface JoinLinesParams { textDocument: lc.TextDocumentIdentifier;