aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-03-15 17:50:20 +0000
committerGitHub <[email protected]>2021-03-15 17:50:20 +0000
commitd38fd77845c40c6f07507c5c436af903a452efbd (patch)
tree7921f1e3b8811ae9af2bde20fd73c6ee69f23d2e
parent1f28345b37130659438a8d2427f8879a19a14ae9 (diff)
parentf05fef70638f4f66be6681a87be5a8d24b29b0cf (diff)
Merge #8028
8028: Return multiple modules in `parent_module` feature r=matklad a=Veykril Co-authored-by: Lukas Wirth <[email protected]>
-rw-r--r--Cargo.lock1
-rw-r--r--crates/hir/Cargo.toml1
-rw-r--r--crates/hir/src/semantics.rs8
-rw-r--r--crates/hir/src/semantics/source_to_def.rs22
-rw-r--r--crates/ide/src/parent_module.rs79
-rw-r--r--editors/code/src/commands.ts24
6 files changed, 91 insertions, 44 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b1fef2e80..b2d009e38 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -486,6 +486,7 @@ dependencies = [
486 "log", 486 "log",
487 "profile", 487 "profile",
488 "rustc-hash", 488 "rustc-hash",
489 "smallvec",
489 "stdx", 490 "stdx",
490 "syntax", 491 "syntax",
491 "tt", 492 "tt",
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index d4ea7327e..55e9c3f0c 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -15,6 +15,7 @@ rustc-hash = "1.1.0"
15either = "1.5.3" 15either = "1.5.3"
16arrayvec = "0.5.1" 16arrayvec = "0.5.1"
17itertools = "0.10.0" 17itertools = "0.10.0"
18smallvec = "1.4.0"
18 19
19stdx = { path = "../stdx", version = "0.0.0" } 20stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" } 21syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 945638cc5..519339c0c 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -259,6 +259,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
259 } 259 }
260 260
261 pub fn to_module_def(&self, file: FileId) -> Option<Module> { 261 pub fn to_module_def(&self, file: FileId) -> Option<Module> {
262 self.imp.to_module_def(file).next()
263 }
264
265 pub fn to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> {
262 self.imp.to_module_def(file) 266 self.imp.to_module_def(file)
263 } 267 }
264 268
@@ -537,8 +541,8 @@ impl<'db> SemanticsImpl<'db> {
537 f(&mut ctx) 541 f(&mut ctx)
538 } 542 }
539 543
540 fn to_module_def(&self, file: FileId) -> Option<Module> { 544 fn to_module_def(&self, file: FileId) -> impl Iterator<Item = Module> {
541 self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from) 545 self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from)
542 } 546 }
543 547
544 fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> { 548 fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 6c612ee86..e9d820140 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -12,6 +12,7 @@ use hir_def::{
12}; 12};
13use hir_expand::{name::AsName, AstId, MacroDefKind}; 13use hir_expand::{name::AsName, AstId, MacroDefKind};
14use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
15use smallvec::SmallVec;
15use stdx::impl_from; 16use stdx::impl_from;
16use syntax::{ 17use syntax::{
17 ast::{self, NameOwner}, 18 ast::{self, NameOwner},
@@ -28,14 +29,19 @@ pub(super) struct SourceToDefCtx<'a, 'b> {
28} 29}
29 30
30impl SourceToDefCtx<'_, '_> { 31impl SourceToDefCtx<'_, '_> {
31 pub(super) fn file_to_def(&mut self, file: FileId) -> Option<ModuleId> { 32 pub(super) fn file_to_def(&mut self, file: FileId) -> SmallVec<[ModuleId; 1]> {
32 let _p = profile::span("SourceBinder::to_module_def"); 33 let _p = profile::span("SourceBinder::to_module_def");
33 self.db.relevant_crates(file).iter().find_map(|&crate_id| { 34 let mut mods = SmallVec::new();
35 for &crate_id in self.db.relevant_crates(file).iter() {
34 // FIXME: inner items 36 // FIXME: inner items
35 let crate_def_map = self.db.crate_def_map(crate_id); 37 let crate_def_map = self.db.crate_def_map(crate_id);
36 let local_id = crate_def_map.modules_for_file(file).next()?; 38 mods.extend(
37 Some(crate_def_map.module_id(local_id)) 39 crate_def_map
38 }) 40 .modules_for_file(file)
41 .map(|local_id| crate_def_map.module_id(local_id)),
42 )
43 }
44 mods
39 } 45 }
40 46
41 pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> { 47 pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
@@ -55,7 +61,7 @@ impl SourceToDefCtx<'_, '_> {
55 Some(parent_declaration) => self.module_to_def(parent_declaration), 61 Some(parent_declaration) => self.module_to_def(parent_declaration),
56 None => { 62 None => {
57 let file_id = src.file_id.original_file(self.db.upcast()); 63 let file_id = src.file_id.original_file(self.db.upcast());
58 self.file_to_def(file_id) 64 self.file_to_def(file_id).get(0).copied()
59 } 65 }
60 }?; 66 }?;
61 67
@@ -185,7 +191,7 @@ impl SourceToDefCtx<'_, '_> {
185 ) -> Option<MacroDefId> { 191 ) -> Option<MacroDefId> {
186 let kind = MacroDefKind::Declarative; 192 let kind = MacroDefKind::Declarative;
187 let file_id = src.file_id.original_file(self.db.upcast()); 193 let file_id = src.file_id.original_file(self.db.upcast());
188 let krate = self.file_to_def(file_id)?.krate(); 194 let krate = self.file_to_def(file_id).get(0).copied()?.krate();
189 let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); 195 let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
190 let ast_id = Some(AstId::new(src.file_id, file_ast_id.upcast())); 196 let ast_id = Some(AstId::new(src.file_id, file_ast_id.upcast()));
191 Some(MacroDefId { krate, ast_id, kind, local_inner: false }) 197 Some(MacroDefId { krate, ast_id, kind, local_inner: false })
@@ -245,7 +251,7 @@ impl SourceToDefCtx<'_, '_> {
245 return Some(res); 251 return Some(res);
246 } 252 }
247 253
248 let def = self.file_to_def(src.file_id.original_file(self.db.upcast()))?; 254 let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).get(0).copied()?;
249 Some(def.into()) 255 Some(def.into())
250 } 256 }
251 257
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index 03d71b380..22b0d6ecb 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -1,6 +1,7 @@
1use hir::Semantics; 1use hir::Semantics;
2use ide_db::base_db::{CrateId, FileId, FilePosition}; 2use ide_db::base_db::{CrateId, FileId, FilePosition};
3use ide_db::RootDatabase; 3use ide_db::RootDatabase;
4use itertools::Itertools;
4use syntax::{ 5use syntax::{
5 algo::find_node_at_offset, 6 algo::find_node_at_offset,
6 ast::{self, AstNode}, 7 ast::{self, AstNode},
@@ -18,8 +19,7 @@ use crate::NavigationTarget;
18// | VS Code | **Rust Analyzer: Locate parent module** 19// | VS Code | **Rust Analyzer: Locate parent module**
19// |=== 20// |===
20 21
21/// This returns `Vec` because a module may be included from several places. We 22/// This returns `Vec` because a module may be included from several places.
22/// don't handle this case yet though, so the Vec has length at most one.
23pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { 23pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
24 let sema = Semantics::new(db); 24 let sema = Semantics::new(db);
25 let source_file = sema.parse(position.file_id); 25 let source_file = sema.parse(position.file_id);
@@ -37,27 +37,23 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
37 } 37 }
38 } 38 }
39 39
40 let module = match module { 40 match module {
41 Some(module) => sema.to_def(&module), 41 Some(module) => sema
42 None => sema.to_module_def(position.file_id), 42 .to_def(&module)
43 }; 43 .into_iter()
44 let module = match module { 44 .map(|module| NavigationTarget::from_module_to_decl(db, module))
45 None => return Vec::new(), 45 .collect(),
46 Some(it) => it, 46 None => sema
47 }; 47 .to_module_defs(position.file_id)
48 let nav = NavigationTarget::from_module_to_decl(db, module); 48 .map(|module| NavigationTarget::from_module_to_decl(db, module))
49 vec![nav] 49 .collect(),
50 }
50} 51}
51 52
52/// Returns `Vec` for the same reason as `parent_module` 53/// Returns `Vec` for the same reason as `parent_module`
53pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { 54pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
54 let sema = Semantics::new(db); 55 let sema = Semantics::new(db);
55 let module = match sema.to_module_def(file_id) { 56 sema.to_module_defs(file_id).map(|module| module.krate().into()).unique().collect()
56 Some(it) => it,
57 None => return Vec::new(),
58 };
59 let krate = module.krate();
60 vec![krate.into()]
61} 57}
62 58
63#[cfg(test)] 59#[cfg(test)]
@@ -67,11 +63,13 @@ mod tests {
67 use crate::fixture; 63 use crate::fixture;
68 64
69 fn check(ra_fixture: &str) { 65 fn check(ra_fixture: &str) {
70 let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture); 66 let (analysis, position, expected) = fixture::annotations(ra_fixture);
71 let mut navs = analysis.parent_module(position).unwrap(); 67 let navs = analysis.parent_module(position).unwrap();
72 assert_eq!(navs.len(), 1); 68 let navs = navs
73 let nav = navs.pop().unwrap(); 69 .iter()
74 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); 70 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
71 .collect::<Vec<_>>();
72 assert_eq!(expected.into_iter().map(|(fr, _)| fr).collect::<Vec<_>>(), navs);
75 } 73 }
76 74
77 #[test] 75 #[test]
@@ -120,15 +118,46 @@ mod foo {
120 } 118 }
121 119
122 #[test] 120 #[test]
123 fn test_resolve_crate_root() { 121 fn test_resolve_multi_parent_module() {
124 let (analysis, file_id) = fixture::file( 122 check(
125 r#" 123 r#"
126//- /main.rs 124//- /main.rs
127mod foo; 125mod foo;
126 //^^^
127#[path = "foo.rs"]
128mod bar;
129 //^^^
130//- /foo.rs
131$0
132"#,
133 );
134 }
135
136 #[test]
137 fn test_resolve_crate_root() {
138 let (analysis, file_id) = fixture::file(
139 r#"
128//- /foo.rs 140//- /foo.rs
129$0 141$0
142//- /main.rs
143mod foo;
130"#, 144"#,
131 ); 145 );
132 assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1); 146 assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1);
133 } 147 }
148
149 #[test]
150 fn test_resolve_multi_parent_crate() {
151 let (analysis, file_id) = fixture::file(
152 r#"
153//- /baz.rs
154$0
155//- /foo.rs crate:foo
156mod baz;
157//- /bar.rs crate:bar
158mod baz;
159"#,
160 );
161 assert_eq!(analysis.crate_for(file_id).unwrap().len(), 2);
162 }
134} 163}
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 694f445bc..bed1f0116 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -170,22 +170,28 @@ export function parentModule(ctx: Ctx): Cmd {
170 const client = ctx.client; 170 const client = ctx.client;
171 if (!editor || !client) return; 171 if (!editor || !client) return;
172 172
173 const response = await client.sendRequest(ra.parentModule, { 173 const locations = await client.sendRequest(ra.parentModule, {
174 textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), 174 textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
175 position: client.code2ProtocolConverter.asPosition( 175 position: client.code2ProtocolConverter.asPosition(
176 editor.selection.active, 176 editor.selection.active,
177 ), 177 ),
178 }); 178 });
179 const loc = response[0];
180 if (!loc) return;
181 179
182 const uri = client.protocol2CodeConverter.asUri(loc.targetUri); 180 if (locations.length === 1) {
183 const range = client.protocol2CodeConverter.asRange(loc.targetRange); 181 const loc = locations[0];
184 182
185 const doc = await vscode.workspace.openTextDocument(uri); 183 const uri = client.protocol2CodeConverter.asUri(loc.targetUri);
186 const e = await vscode.window.showTextDocument(doc); 184 const range = client.protocol2CodeConverter.asRange(loc.targetRange);
187 e.selection = new vscode.Selection(range.start, range.start); 185
188 e.revealRange(range, vscode.TextEditorRevealType.InCenter); 186 const doc = await vscode.workspace.openTextDocument(uri);
187 const e = await vscode.window.showTextDocument(doc);
188 e.selection = new vscode.Selection(range.start, range.start);
189 e.revealRange(range, vscode.TextEditorRevealType.InCenter);
190 } else {
191 const uri = editor.document.uri.toString();
192 const position = client.code2ProtocolConverter.asPosition(editor.selection.active);
193 await showReferencesImpl(client, uri, position, locations.map(loc => lc.Location.create(loc.targetUri, loc.targetRange)));
194 }
189 }; 195 };
190} 196}
191 197