1 use ra_db::{FileId, Cancelable, SyntaxDatabase};
4 algo::find_node_at_offset,
7 use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
9 pub(crate) fn goto_definition(
11 position: FilePosition,
12 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
13 let file = db.source_file(position.file_id);
14 let syntax = file.syntax();
15 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
16 let navs = reference_definition(db, position.file_id, name_ref)?.to_vec();
17 return Ok(Some(RangeInfo::new(
18 name_ref.syntax().range(),
22 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
23 let navs = ctry!(name_definition(db, position.file_id, name)?);
24 return Ok(Some(RangeInfo::new(name.syntax().range(), navs)));
29 pub(crate) enum ReferenceResult {
30 Exact(NavigationTarget),
31 Approximate(Vec<NavigationTarget>),
34 impl ReferenceResult {
35 fn to_vec(self) -> Vec<NavigationTarget> {
36 use self::ReferenceResult::*;
38 Exact(target) => vec![target],
39 Approximate(vec) => vec,
44 pub(crate) fn reference_definition(
47 name_ref: &ast::NameRef,
48 ) -> Cancelable<ReferenceResult> {
49 use self::ReferenceResult::*;
50 if let Some(fn_descr) =
51 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())?
53 let scope = fn_descr.scopes(db)?;
54 // First try to resolve the symbol locally
55 if let Some(entry) = scope.resolve_local_name(name_ref) {
56 let nav = NavigationTarget::from_scope_entry(file_id, &entry);
57 return Ok(Exact(nav));
60 // Then try module name resolution
62 hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax())?
64 if let Some(path) = name_ref
67 .find_map(ast::Path::cast)
68 .and_then(hir::Path::from_ast)
70 let resolved = module.resolve_path(db, &path)?;
71 if let Some(def_id) = resolved.take_types().or(resolved.take_values()) {
72 if let Some(target) = NavigationTarget::from_def(db, def_id.resolve(db)?)? {
73 return Ok(Exact(target));
78 // If that fails try the index based approach.
80 .index_resolve(name_ref)?
82 .map(NavigationTarget::from_symbol)
91 ) -> Cancelable<Option<Vec<NavigationTarget>>> {
92 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
93 if module.has_semi() {
94 if let Some(child_module) =
95 hir::source_binder::module_from_declaration(db, file_id, module)?
97 let nav = NavigationTarget::from_module(db, child_module)?;
98 return Ok(Some(vec![nav]));
107 use crate::mock_analysis::analysis_and_position;
109 fn check_goto(fixuture: &str, expected: &str) {
110 let (analysis, pos) = analysis_and_position(fixuture);
112 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info;
113 assert_eq!(navs.len(), 1);
114 let nav = navs.pop().unwrap();
115 nav.assert_match(expected);
119 fn goto_definition_works_in_items() {
126 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
131 fn goto_definition_resolves_correct_name() {
144 "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)",
149 fn goto_definition_works_for_module_declaration() {
157 "foo SOURCE_FILE FileId(2) [0; 10)",
167 "foo SOURCE_FILE FileId(2) [0; 10)",