Skip to content

Commit 39044fe

Browse files
committed
Allow locate parent module action in cargo toml
1 parent 60c5449 commit 39044fe

File tree

3 files changed

+83
-8
lines changed

3 files changed

+83
-8
lines changed

crates/rust-analyzer/src/handlers.rs

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ use lsp_types::{
2020
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
2121
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
2222
CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, FoldingRange,
23-
FoldingRangeParams, HoverContents, Location, NumberOrString, Position, PrepareRenameResponse,
24-
Range, RenameParams, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult,
25-
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
26-
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
23+
FoldingRangeParams, HoverContents, Location, LocationLink, NumberOrString, Position,
24+
PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
25+
SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
26+
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
27+
TextDocumentIdentifier, Url, WorkspaceEdit,
2728
};
28-
use project_model::TargetKind;
29+
use project_model::{ProjectWorkspace, TargetKind};
2930
use serde_json::json;
3031
use stdx::{format_to, never};
3132
use syntax::{algo, ast, AstNode, TextRange, TextSize, T};
33+
use vfs::AbsPath;
3234

3335
use crate::{
3436
cargo_target_spec::CargoTargetSpec,
@@ -603,6 +605,74 @@ pub(crate) fn handle_parent_module(
603605
params: lsp_types::TextDocumentPositionParams,
604606
) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
605607
let _p = profile::span("handle_parent_module");
608+
if let Ok(file_path) = &params.text_document.uri.to_file_path() {
609+
if file_path.file_name().unwrap_or_default() == "Cargo.toml" {
610+
// search parent workspace and collect a list of `LocationLink`path,
611+
// since cargo.toml doesn't have file_id
612+
let links: Vec<LocationLink> = snap
613+
.workspaces
614+
.iter()
615+
.filter_map(|ws| match ws {
616+
ProjectWorkspace::Cargo { cargo, .. } => cargo
617+
.packages()
618+
.find(|&pkg| {
619+
cargo[pkg]
620+
.targets
621+
.iter()
622+
.find_map(|&it| {
623+
let pkg_parent_path = cargo[it].root.parent()?;
624+
let file_parent_path = AbsPath::assert(file_path.parent()?);
625+
if pkg_parent_path == file_parent_path {
626+
Some(())
627+
} else {
628+
None
629+
}
630+
})
631+
.is_some()
632+
})
633+
.and_then(|_| Some(cargo)),
634+
_ => None,
635+
})
636+
.map(|ws| {
637+
let target_cargo_toml_path = ws.workspace_root().join("Cargo.toml");
638+
let target_cargo_toml_url =
639+
to_proto::url_from_abs_path(&target_cargo_toml_path);
640+
LocationLink {
641+
origin_selection_range: None,
642+
target_uri: target_cargo_toml_url,
643+
target_range: Range::default(),
644+
target_selection_range: Range::default(),
645+
}
646+
})
647+
.collect::<_>();
648+
return Ok(Some(links.into()));
649+
}
650+
651+
// check if invoked at the crate root
652+
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
653+
let crate_id = match snap.analysis.crate_for(file_id)?.first() {
654+
Some(&crate_id) => crate_id,
655+
None => return Ok(None),
656+
};
657+
let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? {
658+
Some(it) => it,
659+
None => return Ok(None),
660+
};
661+
662+
if snap.analysis.crate_root(crate_id)? == file_id {
663+
let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml);
664+
let res = vec![LocationLink {
665+
origin_selection_range: None,
666+
target_uri: cargo_toml_url,
667+
target_range: Range::default(),
668+
target_selection_range: Range::default(),
669+
}]
670+
.into();
671+
return Ok(Some(res));
672+
}
673+
}
674+
675+
// locate parent module by semantics
606676
let position = from_proto::file_position(&snap, params)?;
607677
let navs = snap.analysis.parent_module(position)?;
608678
let res = to_proto::goto_definition_response(&snap, None, navs)?;

editors/code/src/commands.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets';
88
import { spawnSync } from 'child_process';
99
import { RunnableQuickPick, selectRunnable, createTask, createArgs } from './run';
1010
import { AstInspector } from './ast_inspector';
11-
import { isRustDocument, sleep, isRustEditor } from './util';
11+
import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from './util';
1212
import { startDebugSession, makeDebugConfig } from './debug';
1313
import { LanguageClient } from 'vscode-languageclient/node';
1414

@@ -185,10 +185,10 @@ export function onEnter(ctx: Ctx): Cmd {
185185

186186
export function parentModule(ctx: Ctx): Cmd {
187187
return async () => {
188-
const editor = ctx.activeRustEditor;
188+
const editor = vscode.window.activeTextEditor;
189189
const client = ctx.client;
190190
if (!editor || !client) return;
191-
191+
if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return;
192192
const locations = await client.sendRequest(ra.parentModule, {
193193
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
194194
position: client.code2ProtocolConverter.asPosition(

editors/code/src/util.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ export function isRustDocument(document: vscode.TextDocument): document is RustD
104104
return document.languageId === 'rust' && document.uri.scheme === 'file';
105105
}
106106

107+
export function isCargoTomlDocument(document: vscode.TextDocument): document is RustDocument {
108+
// ideally `document.languageId` should be 'toml' but user maybe not have toml extension installed
109+
return document.uri.scheme === 'file' && document.fileName.endsWith('Cargo.toml');
110+
}
111+
107112
export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
108113
return isRustDocument(editor.document);
109114
}

0 commit comments

Comments
 (0)