From f8261d611a60e99bc188022773f84912972208d2 Mon Sep 17 00:00:00 2001 From: Marcus Klaas de Vries Date: Tue, 8 Jan 2019 23:38:51 +0100 Subject: Fix typo defenition -> definition --- crates/ra_ide_api/src/goto_definition.rs | 139 +++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 crates/ra_ide_api/src/goto_definition.rs (limited to 'crates/ra_ide_api/src/goto_definition.rs') diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs new file mode 100644 index 000000000..0d524b6f1 --- /dev/null +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -0,0 +1,139 @@ +use ra_db::{FileId, Cancelable, SyntaxDatabase}; +use ra_syntax::{ + TextRange, AstNode, ast, SyntaxKind::{NAME, MODULE}, + algo::find_node_at_offset, +}; + +use crate::{FilePosition, NavigationTarget, db::RootDatabase}; + +pub(crate) fn goto_definition( + db: &RootDatabase, + position: FilePosition, +) -> Cancelable>> { + let file = db.source_file(position.file_id); + let syntax = file.syntax(); + if let Some(name_ref) = find_node_at_offset::(syntax, position.offset) { + return Ok(Some(reference_definition(db, position.file_id, name_ref)?)); + } + if let Some(name) = find_node_at_offset::(syntax, position.offset) { + return name_definition(db, position.file_id, name); + } + Ok(None) +} + +pub(crate) fn reference_definition( + db: &RootDatabase, + file_id: FileId, + name_ref: &ast::NameRef, +) -> Cancelable> { + if let Some(fn_descr) = + hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())? + { + let scope = fn_descr.scopes(db)?; + // First try to resolve the symbol locally + if let Some(entry) = scope.resolve_local_name(name_ref) { + let nav = NavigationTarget { + file_id, + name: entry.name().to_string().into(), + range: entry.ptr().range(), + kind: NAME, + ptr: None, + }; + return Ok(vec![nav]); + }; + } + // If that fails try the index based approach. + let navs = db + .index_resolve(name_ref)? + .into_iter() + .map(NavigationTarget::from_symbol) + .collect(); + Ok(navs) +} + +fn name_definition( + db: &RootDatabase, + file_id: FileId, + name: &ast::Name, +) -> Cancelable>> { + if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { + if module.has_semi() { + if let Some(child_module) = + hir::source_binder::module_from_declaration(db, file_id, module)? + { + let (file_id, _) = child_module.definition_source(db)?; + let name = match child_module.name(db)? { + Some(name) => name.to_string().into(), + None => "".into(), + }; + let nav = NavigationTarget { + file_id, + name, + range: TextRange::offset_len(0.into(), 0.into()), + kind: MODULE, + ptr: None, + }; + return Ok(Some(vec![nav])); + } + } + } + Ok(None) +} + +#[cfg(test)] +mod tests { + use test_utils::assert_eq_dbg; + use crate::mock_analysis::analysis_and_position; + + #[test] + fn goto_definition_works_in_items() { + let (analysis, pos) = analysis_and_position( + " + //- /lib.rs + struct Foo; + enum E { X(Foo<|>) } + ", + ); + + let symbols = analysis.goto_definition(pos).unwrap().unwrap(); + assert_eq_dbg( + r#"[NavigationTarget { file_id: FileId(1), name: "Foo", + kind: STRUCT_DEF, range: [0; 11), + ptr: Some(LocalSyntaxPtr { range: [0; 11), kind: STRUCT_DEF }) }]"#, + &symbols, + ); + } + + #[test] + fn goto_definition_works_for_module_declaration() { + let (analysis, pos) = analysis_and_position( + " + //- /lib.rs + mod <|>foo; + //- /foo.rs + // empty + ", + ); + + let symbols = analysis.goto_definition(pos).unwrap().unwrap(); + assert_eq_dbg( + r#"[NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]"#, + &symbols, + ); + + let (analysis, pos) = analysis_and_position( + " + //- /lib.rs + mod <|>foo; + //- /foo/mod.rs + // empty + ", + ); + + let symbols = analysis.goto_definition(pos).unwrap().unwrap(); + assert_eq_dbg( + r#"[NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]"#, + &symbols, + ); + } +} -- cgit v1.2.3