Skip to content

Commit f4747f2

Browse files
authored
Merge pull request #19255 from geetanshjuneja/master
Add children modules feature
2 parents dc363f7 + 8f6d32c commit f4747f2

File tree

11 files changed

+204
-1
lines changed

11 files changed

+204
-1
lines changed

crates/ide/src/children_modules.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use hir::Semantics;
2+
use ide_db::{FilePosition, RootDatabase};
3+
use syntax::{
4+
algo::find_node_at_offset,
5+
ast::{self, AstNode},
6+
};
7+
8+
use crate::NavigationTarget;
9+
10+
// Feature: Children Modules
11+
//
12+
// Navigates to the children modules of the current module.
13+
//
14+
// | Editor | Action Name |
15+
// |---------|-------------|
16+
// | VS Code | **rust-analyzer: Locate children modules** |
17+
18+
/// This returns `Vec` because a module may be included from several places.
19+
pub(crate) fn children_modules(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
20+
let sema = Semantics::new(db);
21+
let source_file = sema.parse_guess_edition(position.file_id);
22+
// First go to the parent module which contains the cursor
23+
let module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset);
24+
25+
match module {
26+
Some(module) => {
27+
// Return all the children module inside the ItemList of the parent module
28+
sema.to_def(&module)
29+
.into_iter()
30+
.flat_map(|module| module.children(db))
31+
.map(|module| NavigationTarget::from_module_to_decl(db, module).call_site())
32+
.collect()
33+
}
34+
None => {
35+
// Return all the children module inside the source file
36+
sema.file_to_module_defs(position.file_id)
37+
.flat_map(|module| module.children(db))
38+
.map(|module| NavigationTarget::from_module_to_decl(db, module).call_site())
39+
.collect()
40+
}
41+
}
42+
}
43+
44+
#[cfg(test)]
45+
mod tests {
46+
use ide_db::FileRange;
47+
48+
use crate::fixture;
49+
50+
fn check_children_module(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
51+
let (analysis, position, expected) = fixture::annotations(ra_fixture);
52+
let navs = analysis.children_modules(position).unwrap();
53+
let navs = navs
54+
.iter()
55+
.map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
56+
.collect::<Vec<_>>();
57+
assert_eq!(expected.into_iter().map(|(fr, _)| fr).collect::<Vec<_>>(), navs);
58+
}
59+
60+
#[test]
61+
fn test_resolve_children_module() {
62+
check_children_module(
63+
r#"
64+
//- /lib.rs
65+
$0
66+
mod foo;
67+
//^^^
68+
69+
//- /foo.rs
70+
// empty
71+
"#,
72+
);
73+
}
74+
75+
#[test]
76+
fn test_resolve_children_module_on_module_decl() {
77+
check_children_module(
78+
r#"
79+
//- /lib.rs
80+
mod $0foo;
81+
//- /foo.rs
82+
mod bar;
83+
//^^^
84+
85+
//- /foo/bar.rs
86+
// empty
87+
"#,
88+
);
89+
}
90+
91+
#[test]
92+
fn test_resolve_children_module_for_inline() {
93+
check_children_module(
94+
r#"
95+
//- /lib.rs
96+
mod foo {
97+
mod $0bar {
98+
mod baz {}
99+
} //^^^
100+
}
101+
"#,
102+
);
103+
}
104+
105+
#[test]
106+
fn test_resolve_multi_child_module() {
107+
check_children_module(
108+
r#"
109+
//- /main.rs
110+
$0
111+
mod foo;
112+
//^^^
113+
mod bar;
114+
//^^^
115+
//- /foo.rs
116+
// empty
117+
118+
//- /bar.rs
119+
// empty
120+
"#,
121+
);
122+
}
123+
}

crates/ide/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod navigation_target;
2020

2121
mod annotations;
2222
mod call_hierarchy;
23+
mod children_modules;
2324
mod doc_links;
2425
mod expand_macro;
2526
mod extend_selection;
@@ -605,6 +606,11 @@ impl Analysis {
605606
self.with_db(|db| parent_module::parent_module(db, position))
606607
}
607608

609+
/// Returns vec of `mod name;` declaration which are created by the current module.
610+
pub fn children_modules(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
611+
self.with_db(|db| children_modules::children_modules(db, position))
612+
}
613+
608614
/// Returns crates that this file belongs to.
609615
pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
610616
self.with_db(|db| parent_module::crates_for(db, file_id))

crates/rust-analyzer/src/handlers/request.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,18 @@ pub(crate) fn handle_parent_module(
943943
Ok(Some(res))
944944
}
945945

946+
pub(crate) fn handle_children_modules(
947+
snap: GlobalStateSnapshot,
948+
params: lsp_types::TextDocumentPositionParams,
949+
) -> anyhow::Result<Option<lsp_types::GotoDefinitionResponse>> {
950+
let _p = tracing::info_span!("handle_children_module").entered();
951+
// locate children module by semantics
952+
let position = try_default!(from_proto::file_position(&snap, params)?);
953+
let navs = snap.analysis.children_modules(position)?;
954+
let res = to_proto::goto_definition_response(&snap, None, navs)?;
955+
Ok(Some(res))
956+
}
957+
946958
pub(crate) fn handle_runnables(
947959
snap: GlobalStateSnapshot,
948960
params: lsp_ext::RunnablesParams,

crates/rust-analyzer/src/lsp/capabilities.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
157157
"onEnter": true,
158158
"openCargoToml": true,
159159
"parentModule": true,
160+
"childrenModules": true,
160161
"runnables": {
161162
"kinds": [ "cargo" ],
162163
},

crates/rust-analyzer/src/lsp/ext.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,14 @@ impl Request for ParentModule {
399399
const METHOD: &'static str = "experimental/parentModule";
400400
}
401401

402+
pub enum ChildrenModules {}
403+
404+
impl Request for ChildrenModules {
405+
type Params = lsp_types::TextDocumentPositionParams;
406+
type Result = Option<lsp_types::GotoDefinitionResponse>;
407+
const METHOD: &'static str = "experimental/childrenModule";
408+
}
409+
402410
pub enum JoinLines {}
403411

404412
impl Request for JoinLines {

crates/rust-analyzer/src/main_loop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,7 @@ impl GlobalState {
11721172
.on::<NO_RETRY, lsp_ext::InterpretFunction>(handlers::handle_interpret_function)
11731173
.on::<NO_RETRY, lsp_ext::ExpandMacro>(handlers::handle_expand_macro)
11741174
.on::<NO_RETRY, lsp_ext::ParentModule>(handlers::handle_parent_module)
1175+
.on::<NO_RETRY, lsp_ext::ChildrenModules>(handlers::handle_children_modules)
11751176
.on::<NO_RETRY, lsp_ext::Runnables>(handlers::handle_runnables)
11761177
.on::<NO_RETRY, lsp_ext::RelatedTests>(handlers::handle_related_tests)
11771178
.on::<NO_RETRY, lsp_ext::CodeActionRequest>(handlers::handle_code_action)

docs/book/src/contributing/lsp-extensions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!---
2-
lsp/ext.rs hash: 3549077514b37437
2+
lsp/ext.rs hash: 300b4be5841cee6f
33
44
If you need to change the above hash to make the test pass, please check if you
55
need to adjust this doc as well and ping this issue:

editors/code/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@
170170
"title": "Locate parent module",
171171
"category": "rust-analyzer"
172172
},
173+
{
174+
"command": "rust-analyzer.childrenModules",
175+
"title": "Locate children modules",
176+
"category": "rust-analyzer"
177+
},
173178
{
174179
"command": "rust-analyzer.joinLines",
175180
"title": "Join lines",
@@ -3373,6 +3378,10 @@
33733378
"command": "rust-analyzer.parentModule",
33743379
"when": "inRustProject"
33753380
},
3381+
{
3382+
"command": "rust-analyzer.childrenModule",
3383+
"when": "inRustProject"
3384+
},
33763385
{
33773386
"command": "rust-analyzer.joinLines",
33783387
"when": "inRustProject"

editors/code/src/commands.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,43 @@ export function parentModule(ctx: CtxInit): Cmd {
266266
};
267267
}
268268

269+
export function childrenModules(ctx: CtxInit): Cmd {
270+
return async () => {
271+
const editor = vscode.window.activeTextEditor;
272+
if (!editor) return;
273+
if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return;
274+
275+
const client = ctx.client;
276+
277+
const locations = await client.sendRequest(ra.childrenModules, {
278+
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
279+
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
280+
});
281+
if (!locations) return;
282+
283+
if (locations.length === 1) {
284+
const loc = unwrapUndefinable(locations[0]);
285+
286+
const uri = client.protocol2CodeConverter.asUri(loc.targetUri);
287+
const range = client.protocol2CodeConverter.asRange(loc.targetRange);
288+
289+
const doc = await vscode.workspace.openTextDocument(uri);
290+
const e = await vscode.window.showTextDocument(doc);
291+
e.selection = new vscode.Selection(range.start, range.start);
292+
e.revealRange(range, vscode.TextEditorRevealType.InCenter);
293+
} else {
294+
const uri = editor.document.uri.toString();
295+
const position = client.code2ProtocolConverter.asPosition(editor.selection.active);
296+
await showReferencesImpl(
297+
client,
298+
uri,
299+
position,
300+
locations.map((loc) => lc.Location.create(loc.targetUri, loc.targetRange)),
301+
);
302+
}
303+
};
304+
}
305+
269306
export function openCargoToml(ctx: CtxInit): Cmd {
270307
return async () => {
271308
const editor = ctx.activeRustEditor;

editors/code/src/lsp_ext.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ export const parentModule = new lc.RequestType<
194194
lc.LocationLink[] | null,
195195
void
196196
>("experimental/parentModule");
197+
export const childrenModules = new lc.RequestType<
198+
lc.TextDocumentPositionParams,
199+
lc.LocationLink[] | null,
200+
void
201+
>("experimental/childrenModule");
197202
export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>(
198203
"experimental/runnables",
199204
);

editors/code/src/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ function createCommands(): Record<string, CommandFactory> {
158158
matchingBrace: { enabled: commands.matchingBrace },
159159
joinLines: { enabled: commands.joinLines },
160160
parentModule: { enabled: commands.parentModule },
161+
childrenModules: { enabled: commands.childrenModules },
161162
viewHir: { enabled: commands.viewHir },
162163
viewMir: { enabled: commands.viewMir },
163164
interpretFunction: { enabled: commands.interpretFunction },

0 commit comments

Comments
 (0)