From 39044fe39c0557747ee12c5dc6d163dfdf65d88c Mon Sep 17 00:00:00 2001 From: rainy-me Date: Sun, 3 Oct 2021 11:58:10 +0900 Subject: [PATCH] Allow locate parent module action in cargo toml --- crates/rust-analyzer/src/handlers.rs | 80 ++++++++++++++++++++++++++-- editors/code/src/commands.ts | 6 +-- editors/code/src/util.ts | 5 ++ 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index e62bb9499f..a7ca5a7a34 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -20,15 +20,17 @@ use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, FoldingRange, - FoldingRangeParams, HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, - Range, RenameParams, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, - SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, - SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, + FoldingRangeParams, HoverContents, Location, LocationLink, NumberOrString, Position, + PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams, + SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams, + SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag, + TextDocumentIdentifier, Url, WorkspaceEdit, }; -use project_model::TargetKind; +use project_model::{ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize, T}; +use vfs::AbsPath; use crate::{ cargo_target_spec::CargoTargetSpec, @@ -603,6 +605,74 @@ pub(crate) fn handle_parent_module( params: lsp_types::TextDocumentPositionParams, ) -> Result> { 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 + let links: Vec = snap + .workspaces + .iter() + .filter_map(|ws| match ws { + ProjectWorkspace::Cargo { cargo, .. } => cargo + .packages() + .find(|&pkg| { + cargo[pkg] + .targets + .iter() + .find_map(|&it| { + let pkg_parent_path = cargo[it].root.parent()?; + let file_parent_path = AbsPath::assert(file_path.parent()?); + if pkg_parent_path == file_parent_path { + Some(()) + } else { + None + } + }) + .is_some() + }) + .and_then(|_| Some(cargo)), + _ => 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(), + } + }) + .collect::<_>(); + return Ok(Some(links.into())); + } + + // check if invoked at the crate root + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let crate_id = match snap.analysis.crate_for(file_id)?.first() { + Some(&crate_id) => crate_id, + None => return Ok(None), + }; + let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? { + Some(it) => it, + None => return Ok(None), + }; + + if snap.analysis.crate_root(crate_id)? == file_id { + let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml); + let res = vec![LocationLink { + origin_selection_range: None, + target_uri: cargo_toml_url, + target_range: Range::default(), + target_selection_range: Range::default(), + }] + .into(); + return Ok(Some(res)); + } + } + + // locate parent module by semantics let position = from_proto::file_position(&snap, params)?; let navs = snap.analysis.parent_module(position)?; let res = to_proto::goto_definition_response(&snap, None, navs)?; diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 0e08b60a93..623e33c7ef 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -8,7 +8,7 @@ import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets'; import { spawnSync } from 'child_process'; import { RunnableQuickPick, selectRunnable, createTask, createArgs } from './run'; import { AstInspector } from './ast_inspector'; -import { isRustDocument, sleep, isRustEditor } from './util'; +import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from './util'; import { startDebugSession, makeDebugConfig } from './debug'; import { LanguageClient } from 'vscode-languageclient/node'; @@ -185,10 +185,10 @@ export function onEnter(ctx: Ctx): Cmd { export function parentModule(ctx: Ctx): Cmd { return async () => { - const editor = ctx.activeRustEditor; + const editor = vscode.window.activeTextEditor; 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( diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index aa57081a5f..057a3d2e19 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -104,6 +104,11 @@ export function isRustDocument(document: vscode.TextDocument): document is RustD return document.languageId === 'rust' && document.uri.scheme === 'file'; } +export function isCargoTomlDocument(document: vscode.TextDocument): document is RustDocument { + // ideally `document.languageId` should be 'toml' but user maybe not have toml extension installed + return document.uri.scheme === 'file' && document.fileName.endsWith('Cargo.toml'); +} + export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor { return isRustDocument(editor.document); }