Skip to content

Commit f87debc

Browse files
bors[bot]yue4u
andauthored
Merge #10434
10434: Allow `Locate parent module` command in Cargo.toml r=Veykril a=rainy-me close #10355 Co-authored-by: rainy-me <[email protected]> Co-authored-by: rainy-me <[email protected]>
2 parents 641fa37 + 59c7552 commit f87debc

File tree

5 files changed

+108
-9
lines changed

5 files changed

+108
-9
lines changed

crates/project_model/src/cargo_workspace.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! See [`CargoWorkspace`].
22
3-
use std::convert::TryInto;
3+
use std::convert::{TryFrom, TryInto};
44
use std::iter;
55
use std::path::PathBuf;
66
use std::{ops, process::Command};
@@ -400,6 +400,39 @@ impl CargoWorkspace {
400400
}
401401
}
402402

403+
pub fn parent_manifests(&self, manifest_path: &ManifestPath) -> Option<Vec<ManifestPath>> {
404+
let mut found = false;
405+
let parent_manifests = self
406+
.packages()
407+
.filter_map(|pkg| {
408+
if !found && &self[pkg].manifest == manifest_path {
409+
found = true
410+
}
411+
self[pkg].dependencies.iter().find_map(|dep| {
412+
if &self[dep.pkg].manifest == manifest_path {
413+
return Some(self[pkg].manifest.clone());
414+
}
415+
None
416+
})
417+
})
418+
.collect::<Vec<ManifestPath>>();
419+
420+
// some packages has this pkg as dep. return their manifests
421+
if parent_manifests.len() > 0 {
422+
return Some(parent_manifests);
423+
}
424+
425+
// this pkg is inside this cargo workspace, fallback to workspace root
426+
if found {
427+
return Some(vec![
428+
ManifestPath::try_from(self.workspace_root().join("Cargo.toml")).ok()?
429+
]);
430+
}
431+
432+
// not in this workspace
433+
None
434+
}
435+
403436
fn is_unique(&self, name: &str) -> bool {
404437
self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
405438
}

crates/rust-analyzer/src/handlers.rs

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! `ide` crate.
44
55
use std::{
6+
convert::TryFrom,
67
io::Write as _,
78
process::{self, Stdio},
89
};
@@ -20,15 +21,17 @@ use lsp_types::{
2021
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
2122
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
2223
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,
24+
FoldingRangeParams, HoverContents, Location, LocationLink, NumberOrString, Position,
25+
PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
26+
SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
27+
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
28+
TextDocumentIdentifier, Url, WorkspaceEdit,
2729
};
28-
use project_model::TargetKind;
30+
use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
2931
use serde_json::json;
3032
use stdx::{format_to, never};
3133
use syntax::{algo, ast, AstNode, TextRange, TextSize, T};
34+
use vfs::AbsPathBuf;
3235

3336
use crate::{
3437
cargo_target_spec::CargoTargetSpec,
@@ -601,6 +604,62 @@ pub(crate) fn handle_parent_module(
601604
params: lsp_types::TextDocumentPositionParams,
602605
) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
603606
let _p = profile::span("handle_parent_module");
607+
if let Ok(file_path) = &params.text_document.uri.to_file_path() {
608+
if file_path.file_name().unwrap_or_default() == "Cargo.toml" {
609+
// search workspaces for parent packages or fallback to workspace root
610+
let abs_path_buf = match AbsPathBuf::try_from(file_path.to_path_buf()).ok() {
611+
Some(abs_path_buf) => abs_path_buf,
612+
None => return Ok(None),
613+
};
614+
615+
let manifest_path = match ManifestPath::try_from(abs_path_buf).ok() {
616+
Some(manifest_path) => manifest_path,
617+
None => return Ok(None),
618+
};
619+
620+
let links: Vec<LocationLink> = snap
621+
.workspaces
622+
.iter()
623+
.filter_map(|ws| match ws {
624+
ProjectWorkspace::Cargo { cargo, .. } => cargo.parent_manifests(&manifest_path),
625+
_ => None,
626+
})
627+
.flatten()
628+
.map(|parent_manifest_path| LocationLink {
629+
origin_selection_range: None,
630+
target_uri: to_proto::url_from_abs_path(&parent_manifest_path),
631+
target_range: Range::default(),
632+
target_selection_range: Range::default(),
633+
})
634+
.collect::<_>();
635+
return Ok(Some(links.into()));
636+
}
637+
638+
// check if invoked at the crate root
639+
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
640+
let crate_id = match snap.analysis.crate_for(file_id)?.first() {
641+
Some(&crate_id) => crate_id,
642+
None => return Ok(None),
643+
};
644+
let cargo_spec = match CargoTargetSpec::for_file(&snap, file_id)? {
645+
Some(it) => it,
646+
None => return Ok(None),
647+
};
648+
649+
if snap.analysis.crate_root(crate_id)? == file_id {
650+
let cargo_toml_url = to_proto::url_from_abs_path(&cargo_spec.cargo_toml);
651+
let res = vec![LocationLink {
652+
origin_selection_range: None,
653+
target_uri: cargo_toml_url,
654+
target_range: Range::default(),
655+
target_selection_range: Range::default(),
656+
}]
657+
.into();
658+
return Ok(Some(res));
659+
}
660+
}
661+
662+
// locate parent module by semantics
604663
let position = from_proto::file_position(&snap, params)?;
605664
let navs = snap.analysis.parent_module(position)?;
606665
let res = to_proto::goto_definition_response(&snap, None, navs)?;

editors/code/src/commands.ts

Lines changed: 4 additions & 2 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,16 +185,18 @@ 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+
if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return;
191192

192193
const locations = await client.sendRequest(ra.parentModule, {
193194
textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
194195
position: client.code2ProtocolConverter.asPosition(
195196
editor.selection.active,
196197
),
197198
});
199+
if (!locations) return;
198200

199201
if (locations.length === 1) {
200202
const loc = locations[0];

editors/code/src/lsp_ext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export interface MatchingBraceParams {
6262
}
6363
export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position[], void>("experimental/matchingBrace");
6464

65-
export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule");
65+
export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[] | null, void>("experimental/parentModule");
6666

6767
export interface JoinLinesParams {
6868
textDocument: lc.TextDocumentIdentifier;

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)