aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/goto_defenition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/goto_defenition.rs')
-rw-r--r--crates/ra_analysis/src/goto_defenition.rs177
1 files changed, 121 insertions, 56 deletions
diff --git a/crates/ra_analysis/src/goto_defenition.rs b/crates/ra_analysis/src/goto_defenition.rs
index 607a25115..e37421f8d 100644
--- a/crates/ra_analysis/src/goto_defenition.rs
+++ b/crates/ra_analysis/src/goto_defenition.rs
@@ -1,73 +1,138 @@
1use ra_db::FileId; 1use ra_db::{FileId, Cancelable, SyntaxDatabase};
2use ra_syntax::ast; 2use ra_syntax::{TextRange, AstNode, ast, SyntaxKind::{NAME, MODULE}};
3 3
4use crate::db::RootDatabase; 4use ra_editor::find_node_at_offset;
5 5
6pub fn goto_defenition(db: &RootDatabase, position: FilePosition, 6use crate::{FilePosition, NavigationTarget, db::RootDatabase};
7
8pub(crate) fn goto_defenition(
9 db: &RootDatabase,
10 position: FilePosition,
7) -> Cancelable<Option<Vec<NavigationTarget>>> { 11) -> Cancelable<Option<Vec<NavigationTarget>>> {
8 let file = db.source_file(position.file_id); 12 let file = db.source_file(position.file_id);
9 let syntax = file.syntax(); 13 let syntax = file.syntax();
10 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 14 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
11 return Ok(Some(reference_defenition(db, position.file_id, name_ref))); 15 return Ok(Some(reference_defenition(db, position.file_id, name_ref)?));
12 } 16 }
13 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { 17 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
14 return Ok(Some(name_defenition(db, position.file_idname))); 18 return name_defenition(db, position.file_id, name);
15 } 19 }
16 Ok(None) 20 Ok(None)
17} 21}
18 22
19fn reference_defenition(db: &RootDatabase, file_id: FileId, name_ref: ast::NameRef) -> Cancelable<Vec<Nav>> { 23pub(crate) fn reference_defenition(
20 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 24 db: &RootDatabase,
21 let mut rr = ReferenceResolution::new(name_ref.syntax().range()); 25 file_id: FileId,
22 if let Some(fn_descr) = 26 name_ref: ast::NameRef,
23 source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())? 27) -> Cancelable<Vec<NavigationTarget>> {
24 { 28 if let Some(fn_descr) =
25 let scope = fn_descr.scopes(self); 29 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())?
26 // First try to resolve the symbol locally 30 {
27 if let Some(entry) = scope.resolve_local_name(name_ref) { 31 let scope = fn_descr.scopes(db);
28 rr.resolves_to.push(NavigationTarget { 32 // First try to resolve the symbol locally
29 file_id: position.file_id, 33 if let Some(entry) = scope.resolve_local_name(name_ref) {
30 name: entry.name().to_string().into(), 34 let nav = NavigationTarget {
31 range: entry.ptr().range(), 35 file_id,
32 kind: NAME, 36 name: entry.name().to_string().into(),
33 ptr: None, 37 range: entry.ptr().range(),
34 }); 38 kind: NAME,
35 return Ok(Some(rr)); 39 ptr: None,
36 }; 40 };
37 } 41 return Ok(vec![nav]);
38 // If that fails try the index based approach. 42 };
39 rr.resolves_to.extend(
40 self.index_resolve(name_ref)?
41 .into_iter()
42 .map(NavigationTarget::from_symbol),
43 );
44 return Ok(Some(rr));
45 } 43 }
46 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { 44 // If that fails try the index based approach.
47 let mut rr = ReferenceResolution::new(name.syntax().range()); 45 let navs = db
48 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 46 .index_resolve(name_ref)?
49 if module.has_semi() { 47 .into_iter()
50 if let Some(child_module) = 48 .map(NavigationTarget::from_symbol)
51 source_binder::module_from_declaration(self, position.file_id, module)? 49 .collect();
52 { 50 Ok(navs)
53 let file_id = child_module.file_id(); 51}
54 let name = match child_module.name() { 52
55 Some(name) => name.to_string().into(), 53fn name_defenition(
56 None => "".into(), 54 db: &RootDatabase,
57 }; 55 file_id: FileId,
58 let symbol = NavigationTarget { 56 name: ast::Name,
59 file_id, 57) -> Cancelable<Option<Vec<NavigationTarget>>> {
60 name, 58 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
61 range: TextRange::offset_len(0.into(), 0.into()), 59 if module.has_semi() {
62 kind: MODULE, 60 if let Some(child_module) =
63 ptr: None, 61 hir::source_binder::module_from_declaration(db, file_id, module)?
64 }; 62 {
65 rr.resolves_to.push(symbol); 63 let file_id = child_module.file_id();
66 return Ok(Some(rr)); 64 let name = match child_module.name() {
67 } 65 Some(name) => name.to_string().into(),
68 } 66 None => "".into(),
67 };
68 let nav = NavigationTarget {
69 file_id,
70 name,
71 range: TextRange::offset_len(0.into(), 0.into()),
72 kind: MODULE,
73 ptr: None,
74 };
75 return Ok(Some(vec![nav]));
69 } 76 }
70 } 77 }
71 Ok(None) 78 }
79 Ok(None)
80}
81
82#[cfg(test)]
83mod tests {
84 use test_utils::assert_eq_dbg;
85 use crate::mock_analysis::analysis_and_position;
72 86
87 #[test]
88 fn goto_defenition_works_in_items() {
89 let (analysis, pos) = analysis_and_position(
90 "
91 //- /lib.rs
92 struct Foo;
93 enum E { X(Foo<|>) }
94 ",
95 );
96
97 let symbols = analysis.goto_defenition(pos).unwrap().unwrap();
98 assert_eq_dbg(
99 r#"[NavigationTarget { file_id: FileId(1), name: "Foo",
100 kind: STRUCT_DEF, range: [0; 11),
101 ptr: Some(LocalSyntaxPtr { range: [0; 11), kind: STRUCT_DEF }) }]"#,
102 &symbols,
103 );
104 }
105
106 #[test]
107 fn goto_defenition_works_for_module_declaration() {
108 let (analysis, pos) = analysis_and_position(
109 "
110 //- /lib.rs
111 mod <|>foo;
112 //- /foo.rs
113 // empty
114 ",
115 );
116
117 let symbols = analysis.goto_defenition(pos).unwrap().unwrap();
118 assert_eq_dbg(
119 r#"[NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]"#,
120 &symbols,
121 );
122
123 let (analysis, pos) = analysis_and_position(
124 "
125 //- /lib.rs
126 mod <|>foo;
127 //- /foo/mod.rs
128 // empty
129 ",
130 );
131
132 let symbols = analysis.goto_defenition(pos).unwrap().unwrap();
133 assert_eq_dbg(
134 r#"[NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]"#,
135 &symbols,
136 );
137 }
73} 138}