mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 03:54:42 +00:00 
			
		
		
		
	Merge pull request #19255 from geetanshjuneja/master
Add children modules feature
This commit is contained in:
		
						commit
						f4747f2617
					
				
					 11 changed files with 204 additions and 1 deletions
				
			
		
							
								
								
									
										123
									
								
								crates/ide/src/children_modules.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								crates/ide/src/children_modules.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | ||||||
|  | use hir::Semantics; | ||||||
|  | use ide_db::{FilePosition, RootDatabase}; | ||||||
|  | use syntax::{ | ||||||
|  |     algo::find_node_at_offset, | ||||||
|  |     ast::{self, AstNode}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use crate::NavigationTarget; | ||||||
|  | 
 | ||||||
|  | // Feature: Children Modules
 | ||||||
|  | //
 | ||||||
|  | // Navigates to the children modules of the current module.
 | ||||||
|  | //
 | ||||||
|  | // | Editor  | Action Name |
 | ||||||
|  | // |---------|-------------|
 | ||||||
|  | // | VS Code | **rust-analyzer: Locate children modules** |
 | ||||||
|  | 
 | ||||||
|  | /// This returns `Vec` because a module may be included from several places.
 | ||||||
|  | pub(crate) fn children_modules(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { | ||||||
|  |     let sema = Semantics::new(db); | ||||||
|  |     let source_file = sema.parse_guess_edition(position.file_id); | ||||||
|  |     // First go to the parent module which contains the cursor
 | ||||||
|  |     let module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset); | ||||||
|  | 
 | ||||||
|  |     match module { | ||||||
|  |         Some(module) => { | ||||||
|  |             // Return all the children module inside the ItemList of the parent module
 | ||||||
|  |             sema.to_def(&module) | ||||||
|  |                 .into_iter() | ||||||
|  |                 .flat_map(|module| module.children(db)) | ||||||
|  |                 .map(|module| NavigationTarget::from_module_to_decl(db, module).call_site()) | ||||||
|  |                 .collect() | ||||||
|  |         } | ||||||
|  |         None => { | ||||||
|  |             // Return all the children module inside the source file
 | ||||||
|  |             sema.file_to_module_defs(position.file_id) | ||||||
|  |                 .flat_map(|module| module.children(db)) | ||||||
|  |                 .map(|module| NavigationTarget::from_module_to_decl(db, module).call_site()) | ||||||
|  |                 .collect() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use ide_db::FileRange; | ||||||
|  | 
 | ||||||
|  |     use crate::fixture; | ||||||
|  | 
 | ||||||
|  |     fn check_children_module(#[rust_analyzer::rust_fixture] ra_fixture: &str) { | ||||||
|  |         let (analysis, position, expected) = fixture::annotations(ra_fixture); | ||||||
|  |         let navs = analysis.children_modules(position).unwrap(); | ||||||
|  |         let navs = navs | ||||||
|  |             .iter() | ||||||
|  |             .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }) | ||||||
|  |             .collect::<Vec<_>>(); | ||||||
|  |         assert_eq!(expected.into_iter().map(|(fr, _)| fr).collect::<Vec<_>>(), navs); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn test_resolve_children_module() { | ||||||
|  |         check_children_module( | ||||||
|  |             r#" | ||||||
|  | //- /lib.rs
 | ||||||
|  | $0 | ||||||
|  | mod foo; | ||||||
|  |   //^^^
 | ||||||
|  | 
 | ||||||
|  | //- /foo.rs
 | ||||||
|  | // empty
 | ||||||
|  | "#,
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn test_resolve_children_module_on_module_decl() { | ||||||
|  |         check_children_module( | ||||||
|  |             r#" | ||||||
|  | //- /lib.rs
 | ||||||
|  | mod $0foo; | ||||||
|  | //- /foo.rs
 | ||||||
|  | mod bar; | ||||||
|  |   //^^^
 | ||||||
|  | 
 | ||||||
|  | //- /foo/bar.rs
 | ||||||
|  | // empty
 | ||||||
|  | "#,
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn test_resolve_children_module_for_inline() { | ||||||
|  |         check_children_module( | ||||||
|  |             r#" | ||||||
|  | //- /lib.rs
 | ||||||
|  | mod foo { | ||||||
|  |     mod $0bar { | ||||||
|  |         mod baz {} | ||||||
|  |     }     //^^^
 | ||||||
|  | } | ||||||
|  | "#,
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn test_resolve_multi_child_module() { | ||||||
|  |         check_children_module( | ||||||
|  |             r#" | ||||||
|  | //- /main.rs
 | ||||||
|  | $0 | ||||||
|  | mod foo; | ||||||
|  |   //^^^
 | ||||||
|  | mod bar; | ||||||
|  |   //^^^
 | ||||||
|  | //- /foo.rs
 | ||||||
|  | // empty
 | ||||||
|  | 
 | ||||||
|  | //- /bar.rs
 | ||||||
|  | // empty
 | ||||||
|  | "#,
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -20,6 +20,7 @@ mod navigation_target; | ||||||
| 
 | 
 | ||||||
| mod annotations; | mod annotations; | ||||||
| mod call_hierarchy; | mod call_hierarchy; | ||||||
|  | mod children_modules; | ||||||
| mod doc_links; | mod doc_links; | ||||||
| mod expand_macro; | mod expand_macro; | ||||||
| mod extend_selection; | mod extend_selection; | ||||||
|  | @ -605,6 +606,11 @@ impl Analysis { | ||||||
|         self.with_db(|db| parent_module::parent_module(db, position)) |         self.with_db(|db| parent_module::parent_module(db, position)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Returns vec of `mod name;` declaration which are created by the current module.
 | ||||||
|  |     pub fn children_modules(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> { | ||||||
|  |         self.with_db(|db| children_modules::children_modules(db, position)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Returns crates that this file belongs to.
 |     /// Returns crates that this file belongs to.
 | ||||||
|     pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> { |     pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> { | ||||||
|         self.with_db(|db| parent_module::crates_for(db, file_id)) |         self.with_db(|db| parent_module::crates_for(db, file_id)) | ||||||
|  |  | ||||||
|  | @ -943,6 +943,18 @@ pub(crate) fn handle_parent_module( | ||||||
|     Ok(Some(res)) |     Ok(Some(res)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | pub(crate) fn handle_children_modules( | ||||||
|  |     snap: GlobalStateSnapshot, | ||||||
|  |     params: lsp_types::TextDocumentPositionParams, | ||||||
|  | ) -> anyhow::Result<Option<lsp_types::GotoDefinitionResponse>> { | ||||||
|  |     let _p = tracing::info_span!("handle_children_module").entered(); | ||||||
|  |     // locate children module by semantics
 | ||||||
|  |     let position = try_default!(from_proto::file_position(&snap, params)?); | ||||||
|  |     let navs = snap.analysis.children_modules(position)?; | ||||||
|  |     let res = to_proto::goto_definition_response(&snap, None, navs)?; | ||||||
|  |     Ok(Some(res)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| pub(crate) fn handle_runnables( | pub(crate) fn handle_runnables( | ||||||
|     snap: GlobalStateSnapshot, |     snap: GlobalStateSnapshot, | ||||||
|     params: lsp_ext::RunnablesParams, |     params: lsp_ext::RunnablesParams, | ||||||
|  |  | ||||||
|  | @ -157,6 +157,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities { | ||||||
|             "onEnter": true, |             "onEnter": true, | ||||||
|             "openCargoToml": true, |             "openCargoToml": true, | ||||||
|             "parentModule": true, |             "parentModule": true, | ||||||
|  |             "childrenModules": true, | ||||||
|             "runnables": { |             "runnables": { | ||||||
|                 "kinds": [ "cargo" ], |                 "kinds": [ "cargo" ], | ||||||
|             }, |             }, | ||||||
|  |  | ||||||
|  | @ -399,6 +399,14 @@ impl Request for ParentModule { | ||||||
|     const METHOD: &'static str = "experimental/parentModule"; |     const METHOD: &'static str = "experimental/parentModule"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | pub enum ChildrenModules {} | ||||||
|  | 
 | ||||||
|  | impl Request for ChildrenModules { | ||||||
|  |     type Params = lsp_types::TextDocumentPositionParams; | ||||||
|  |     type Result = Option<lsp_types::GotoDefinitionResponse>; | ||||||
|  |     const METHOD: &'static str = "experimental/childrenModule"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| pub enum JoinLines {} | pub enum JoinLines {} | ||||||
| 
 | 
 | ||||||
| impl Request for JoinLines { | impl Request for JoinLines { | ||||||
|  |  | ||||||
|  | @ -1172,6 +1172,7 @@ impl GlobalState { | ||||||
|             .on::<NO_RETRY, lsp_ext::InterpretFunction>(handlers::handle_interpret_function) |             .on::<NO_RETRY, lsp_ext::InterpretFunction>(handlers::handle_interpret_function) | ||||||
|             .on::<NO_RETRY, lsp_ext::ExpandMacro>(handlers::handle_expand_macro) |             .on::<NO_RETRY, lsp_ext::ExpandMacro>(handlers::handle_expand_macro) | ||||||
|             .on::<NO_RETRY, lsp_ext::ParentModule>(handlers::handle_parent_module) |             .on::<NO_RETRY, lsp_ext::ParentModule>(handlers::handle_parent_module) | ||||||
|  |             .on::<NO_RETRY, lsp_ext::ChildrenModules>(handlers::handle_children_modules) | ||||||
|             .on::<NO_RETRY, lsp_ext::Runnables>(handlers::handle_runnables) |             .on::<NO_RETRY, lsp_ext::Runnables>(handlers::handle_runnables) | ||||||
|             .on::<NO_RETRY, lsp_ext::RelatedTests>(handlers::handle_related_tests) |             .on::<NO_RETRY, lsp_ext::RelatedTests>(handlers::handle_related_tests) | ||||||
|             .on::<NO_RETRY, lsp_ext::CodeActionRequest>(handlers::handle_code_action) |             .on::<NO_RETRY, lsp_ext::CodeActionRequest>(handlers::handle_code_action) | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <!--- | <!--- | ||||||
| lsp/ext.rs hash: 3549077514b37437 | lsp/ext.rs hash: 300b4be5841cee6f | ||||||
| 
 | 
 | ||||||
| If you need to change the above hash to make the test pass, please check if you | If you need to change the above hash to make the test pass, please check if you | ||||||
| need to adjust this doc as well and ping this issue: | need to adjust this doc as well and ping this issue: | ||||||
|  |  | ||||||
|  | @ -170,6 +170,11 @@ | ||||||
|                 "title": "Locate parent module", |                 "title": "Locate parent module", | ||||||
|                 "category": "rust-analyzer" |                 "category": "rust-analyzer" | ||||||
|             }, |             }, | ||||||
|  |             { | ||||||
|  |                 "command": "rust-analyzer.childrenModules", | ||||||
|  |                 "title": "Locate children modules", | ||||||
|  |                 "category": "rust-analyzer" | ||||||
|  |             }, | ||||||
|             { |             { | ||||||
|                 "command": "rust-analyzer.joinLines", |                 "command": "rust-analyzer.joinLines", | ||||||
|                 "title": "Join lines", |                 "title": "Join lines", | ||||||
|  | @ -3373,6 +3378,10 @@ | ||||||
|                     "command": "rust-analyzer.parentModule", |                     "command": "rust-analyzer.parentModule", | ||||||
|                     "when": "inRustProject" |                     "when": "inRustProject" | ||||||
|                 }, |                 }, | ||||||
|  |                 { | ||||||
|  |                     "command": "rust-analyzer.childrenModule", | ||||||
|  |                     "when": "inRustProject" | ||||||
|  |                 }, | ||||||
|                 { |                 { | ||||||
|                     "command": "rust-analyzer.joinLines", |                     "command": "rust-analyzer.joinLines", | ||||||
|                     "when": "inRustProject" |                     "when": "inRustProject" | ||||||
|  |  | ||||||
|  | @ -266,6 +266,43 @@ export function parentModule(ctx: CtxInit): Cmd { | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function childrenModules(ctx: CtxInit): Cmd { | ||||||
|  |     return async () => { | ||||||
|  |         const editor = vscode.window.activeTextEditor; | ||||||
|  |         if (!editor) return; | ||||||
|  |         if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return; | ||||||
|  | 
 | ||||||
|  |         const client = ctx.client; | ||||||
|  | 
 | ||||||
|  |         const locations = await client.sendRequest(ra.childrenModules, { | ||||||
|  |             textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), | ||||||
|  |             position: client.code2ProtocolConverter.asPosition(editor.selection.active), | ||||||
|  |         }); | ||||||
|  |         if (!locations) return; | ||||||
|  | 
 | ||||||
|  |         if (locations.length === 1) { | ||||||
|  |             const loc = unwrapUndefinable(locations[0]); | ||||||
|  | 
 | ||||||
|  |             const uri = client.protocol2CodeConverter.asUri(loc.targetUri); | ||||||
|  |             const range = client.protocol2CodeConverter.asRange(loc.targetRange); | ||||||
|  | 
 | ||||||
|  |             const doc = await vscode.workspace.openTextDocument(uri); | ||||||
|  |             const e = await vscode.window.showTextDocument(doc); | ||||||
|  |             e.selection = new vscode.Selection(range.start, range.start); | ||||||
|  |             e.revealRange(range, vscode.TextEditorRevealType.InCenter); | ||||||
|  |         } else { | ||||||
|  |             const uri = editor.document.uri.toString(); | ||||||
|  |             const position = client.code2ProtocolConverter.asPosition(editor.selection.active); | ||||||
|  |             await showReferencesImpl( | ||||||
|  |                 client, | ||||||
|  |                 uri, | ||||||
|  |                 position, | ||||||
|  |                 locations.map((loc) => lc.Location.create(loc.targetUri, loc.targetRange)), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export function openCargoToml(ctx: CtxInit): Cmd { | export function openCargoToml(ctx: CtxInit): Cmd { | ||||||
|     return async () => { |     return async () => { | ||||||
|         const editor = ctx.activeRustEditor; |         const editor = ctx.activeRustEditor; | ||||||
|  |  | ||||||
|  | @ -194,6 +194,11 @@ export const parentModule = new lc.RequestType< | ||||||
|     lc.LocationLink[] | null, |     lc.LocationLink[] | null, | ||||||
|     void |     void | ||||||
| >("experimental/parentModule"); | >("experimental/parentModule"); | ||||||
|  | export const childrenModules = new lc.RequestType< | ||||||
|  |     lc.TextDocumentPositionParams, | ||||||
|  |     lc.LocationLink[] | null, | ||||||
|  |     void | ||||||
|  | >("experimental/childrenModule"); | ||||||
| export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>( | export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>( | ||||||
|     "experimental/runnables", |     "experimental/runnables", | ||||||
| ); | ); | ||||||
|  |  | ||||||
|  | @ -158,6 +158,7 @@ function createCommands(): Record<string, CommandFactory> { | ||||||
|         matchingBrace: { enabled: commands.matchingBrace }, |         matchingBrace: { enabled: commands.matchingBrace }, | ||||||
|         joinLines: { enabled: commands.joinLines }, |         joinLines: { enabled: commands.joinLines }, | ||||||
|         parentModule: { enabled: commands.parentModule }, |         parentModule: { enabled: commands.parentModule }, | ||||||
|  |         childrenModules: { enabled: commands.childrenModules }, | ||||||
|         viewHir: { enabled: commands.viewHir }, |         viewHir: { enabled: commands.viewHir }, | ||||||
|         viewMir: { enabled: commands.viewMir }, |         viewMir: { enabled: commands.viewMir }, | ||||||
|         interpretFunction: { enabled: commands.interpretFunction }, |         interpretFunction: { enabled: commands.interpretFunction }, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Wirth
						Lukas Wirth