diff options
Diffstat (limited to 'crates/ra_analysis/src/goto_defenition.rs')
-rw-r--r-- | crates/ra_analysis/src/goto_defenition.rs | 177 |
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 @@ | |||
1 | use ra_db::FileId; | 1 | use ra_db::{FileId, Cancelable, SyntaxDatabase}; |
2 | use ra_syntax::ast; | 2 | use ra_syntax::{TextRange, AstNode, ast, SyntaxKind::{NAME, MODULE}}; |
3 | 3 | ||
4 | use crate::db::RootDatabase; | 4 | use ra_editor::find_node_at_offset; |
5 | 5 | ||
6 | pub fn goto_defenition(db: &RootDatabase, position: FilePosition, | 6 | use crate::{FilePosition, NavigationTarget, db::RootDatabase}; |
7 | |||
8 | pub(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 | ||
19 | fn reference_defenition(db: &RootDatabase, file_id: FileId, name_ref: ast::NameRef) -> Cancelable<Vec<Nav>> { | 23 | pub(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(), | 53 | fn 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)] | ||
83 | mod 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 | } |