diff options
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/impls.rs | 120 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide_api/src/navigation_target.rs | 10 |
3 files changed, 138 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs new file mode 100644 index 000000000..469d56d63 --- /dev/null +++ b/crates/ra_ide_api/src/impls.rs | |||
@@ -0,0 +1,120 @@ | |||
1 | use ra_db::{SourceDatabase}; | ||
2 | use ra_syntax::{ | ||
3 | AstNode, ast, | ||
4 | algo::find_node_at_offset, | ||
5 | }; | ||
6 | use hir::{db::HirDatabase, source_binder}; | ||
7 | |||
8 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; | ||
9 | |||
10 | pub(crate) fn goto_implementation( | ||
11 | db: &RootDatabase, | ||
12 | position: FilePosition, | ||
13 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | ||
14 | let file = db.parse(position.file_id); | ||
15 | let syntax = file.syntax(); | ||
16 | |||
17 | let module = source_binder::module_from_position(db, position)?; | ||
18 | let krate = module.krate(db)?; | ||
19 | |||
20 | let node = find_node_at_offset::<ast::NominalDef>(syntax, position.offset)?; | ||
21 | let ty = match node.kind() { | ||
22 | ast::NominalDefKind::StructDef(def) => { | ||
23 | source_binder::struct_from_module(db, module, &def).ty(db) | ||
24 | } | ||
25 | ast::NominalDefKind::EnumDef(def) => { | ||
26 | source_binder::enum_from_module(db, module, &def).ty(db) | ||
27 | } | ||
28 | }; | ||
29 | |||
30 | let impls = db.impls_in_crate(krate); | ||
31 | |||
32 | let navs = impls | ||
33 | .lookup_impl_blocks(db, &ty) | ||
34 | .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp)); | ||
35 | |||
36 | Some(RangeInfo::new(node.syntax().range(), navs.collect())) | ||
37 | } | ||
38 | |||
39 | #[cfg(test)] | ||
40 | mod tests { | ||
41 | use crate::mock_analysis::analysis_and_position; | ||
42 | |||
43 | fn check_goto(fixuture: &str, expected: &[&str]) { | ||
44 | let (analysis, pos) = analysis_and_position(fixuture); | ||
45 | |||
46 | let navs = analysis.goto_implementation(pos).unwrap().unwrap().info; | ||
47 | assert_eq!(navs.len(), expected.len()); | ||
48 | navs.into_iter() | ||
49 | .enumerate() | ||
50 | .for_each(|(i, nav)| nav.assert_match(expected[i])); | ||
51 | } | ||
52 | |||
53 | #[test] | ||
54 | fn goto_implementation_works() { | ||
55 | check_goto( | ||
56 | " | ||
57 | //- /lib.rs | ||
58 | struct Foo<|>; | ||
59 | impl Foo {} | ||
60 | ", | ||
61 | &["impl IMPL_BLOCK FileId(1) [12; 23)"], | ||
62 | ); | ||
63 | } | ||
64 | |||
65 | #[test] | ||
66 | fn goto_implementation_works_multiple_blocks() { | ||
67 | check_goto( | ||
68 | " | ||
69 | //- /lib.rs | ||
70 | struct Foo<|>; | ||
71 | impl Foo {} | ||
72 | impl Foo {} | ||
73 | ", | ||
74 | &[ | ||
75 | "impl IMPL_BLOCK FileId(1) [12; 23)", | ||
76 | "impl IMPL_BLOCK FileId(1) [24; 35)", | ||
77 | ], | ||
78 | ); | ||
79 | } | ||
80 | |||
81 | #[test] | ||
82 | fn goto_implementation_works_multiple_mods() { | ||
83 | check_goto( | ||
84 | " | ||
85 | //- /lib.rs | ||
86 | struct Foo<|>; | ||
87 | mod a { | ||
88 | impl super::Foo {} | ||
89 | } | ||
90 | mod b { | ||
91 | impl super::Foo {} | ||
92 | } | ||
93 | ", | ||
94 | &[ | ||
95 | "impl IMPL_BLOCK FileId(1) [24; 42)", | ||
96 | "impl IMPL_BLOCK FileId(1) [57; 75)", | ||
97 | ], | ||
98 | ); | ||
99 | } | ||
100 | |||
101 | #[test] | ||
102 | fn goto_implementation_works_multiple_files() { | ||
103 | check_goto( | ||
104 | " | ||
105 | //- /lib.rs | ||
106 | struct Foo<|>; | ||
107 | mod a; | ||
108 | mod b; | ||
109 | //- /a.rs | ||
110 | impl crate::Foo {} | ||
111 | //- /b.rs | ||
112 | impl crate::Foo {} | ||
113 | ", | ||
114 | &[ | ||
115 | "impl IMPL_BLOCK FileId(2) [0; 18)", | ||
116 | "impl IMPL_BLOCK FileId(3) [0; 18)", | ||
117 | ], | ||
118 | ); | ||
119 | } | ||
120 | } | ||
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 09cf0216d..5d8acf9df 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -25,6 +25,7 @@ mod call_info; | |||
25 | mod syntax_highlighting; | 25 | mod syntax_highlighting; |
26 | mod parent_module; | 26 | mod parent_module; |
27 | mod rename; | 27 | mod rename; |
28 | mod impls; | ||
28 | 29 | ||
29 | #[cfg(test)] | 30 | #[cfg(test)] |
30 | mod marks; | 31 | mod marks; |
@@ -416,6 +417,13 @@ impl Analysis { | |||
416 | self.with_db(|db| goto_definition::goto_definition(db, position)) | 417 | self.with_db(|db| goto_definition::goto_definition(db, position)) |
417 | } | 418 | } |
418 | 419 | ||
420 | pub fn goto_implementation( | ||
421 | &self, | ||
422 | position: FilePosition, | ||
423 | ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { | ||
424 | self.with_db(|db| impls::goto_implementation(db, position)) | ||
425 | } | ||
426 | |||
419 | /// Finds all usages of the reference at point. | 427 | /// Finds all usages of the reference at point. |
420 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { | 428 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { |
421 | self.with_db(|db| db.find_all_refs(position)) | 429 | self.with_db(|db| db.find_all_refs(position)) |
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index d73d4afa7..5ccb5cc2e 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs | |||
@@ -147,6 +147,16 @@ impl NavigationTarget { | |||
147 | } | 147 | } |
148 | } | 148 | } |
149 | 149 | ||
150 | pub(crate) fn from_impl_block( | ||
151 | db: &RootDatabase, | ||
152 | module: hir::Module, | ||
153 | impl_block: &hir::ImplBlock, | ||
154 | ) -> NavigationTarget { | ||
155 | let (file_id, _) = module.definition_source(db); | ||
156 | let node = module.impl_source(db, impl_block.id()); | ||
157 | NavigationTarget::from_syntax(file_id, "impl".into(), None, node.syntax()) | ||
158 | } | ||
159 | |||
150 | #[cfg(test)] | 160 | #[cfg(test)] |
151 | pub(crate) fn assert_match(&self, expected: &str) { | 161 | pub(crate) fn assert_match(&self, expected: &str) { |
152 | let actual = self.debug_render(); | 162 | let actual = self.debug_render(); |