diff options
author | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
---|---|---|
committer | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
commit | 178c3e135a2a249692f7784712492e7884ae0c00 (patch) | |
tree | ac6b769dbf7162150caa0c1624786a4dd79ff3be /crates/ide/src/goto_type_definition.rs | |
parent | 06ff8e6c760ff05f10e868b5d1f9d79e42fbb49c (diff) | |
parent | c2594daf2974dbd4ce3d9b7ec72481764abaceb5 (diff) |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'crates/ide/src/goto_type_definition.rs')
-rw-r--r-- | crates/ide/src/goto_type_definition.rs | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs new file mode 100644 index 000000000..4a151b150 --- /dev/null +++ b/crates/ide/src/goto_type_definition.rs | |||
@@ -0,0 +1,151 @@ | |||
1 | use ide_db::RootDatabase; | ||
2 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | ||
3 | |||
4 | use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; | ||
5 | |||
6 | // Feature: Go to Type Definition | ||
7 | // | ||
8 | // Navigates to the type of an identifier. | ||
9 | // | ||
10 | // |=== | ||
11 | // | Editor | Action Name | ||
12 | // | ||
13 | // | VS Code | **Go to Type Definition* | ||
14 | // |=== | ||
15 | pub(crate) fn goto_type_definition( | ||
16 | db: &RootDatabase, | ||
17 | position: FilePosition, | ||
18 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | ||
19 | let sema = hir::Semantics::new(db); | ||
20 | |||
21 | let file: ast::SourceFile = sema.parse(position.file_id); | ||
22 | let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?; | ||
23 | let token: SyntaxToken = sema.descend_into_macros(token); | ||
24 | |||
25 | let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| { | ||
26 | let ty = match_ast! { | ||
27 | match node { | ||
28 | ast::Expr(it) => sema.type_of_expr(&it)?, | ||
29 | ast::Pat(it) => sema.type_of_pat(&it)?, | ||
30 | ast::SelfParam(it) => sema.type_of_self(&it)?, | ||
31 | _ => return None, | ||
32 | } | ||
33 | }; | ||
34 | |||
35 | Some((ty, node)) | ||
36 | })?; | ||
37 | |||
38 | let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?; | ||
39 | |||
40 | let nav = adt_def.to_nav(db); | ||
41 | Some(RangeInfo::new(node.text_range(), vec![nav])) | ||
42 | } | ||
43 | |||
44 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | ||
45 | return tokens.max_by_key(priority); | ||
46 | fn priority(n: &SyntaxToken) -> usize { | ||
47 | match n.kind() { | ||
48 | IDENT | INT_NUMBER | T![self] => 2, | ||
49 | kind if kind.is_trivia() => 0, | ||
50 | _ => 1, | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | #[cfg(test)] | ||
56 | mod tests { | ||
57 | use base_db::FileRange; | ||
58 | |||
59 | use crate::mock_analysis::MockAnalysis; | ||
60 | |||
61 | fn check(ra_fixture: &str) { | ||
62 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | ||
63 | let (expected, data) = mock.annotation(); | ||
64 | assert!(data.is_empty()); | ||
65 | let analysis = mock.analysis(); | ||
66 | |||
67 | let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info; | ||
68 | assert_eq!(navs.len(), 1); | ||
69 | let nav = navs.pop().unwrap(); | ||
70 | assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); | ||
71 | } | ||
72 | |||
73 | #[test] | ||
74 | fn goto_type_definition_works_simple() { | ||
75 | check( | ||
76 | r#" | ||
77 | struct Foo; | ||
78 | //^^^ | ||
79 | fn foo() { | ||
80 | let f: Foo; f<|> | ||
81 | } | ||
82 | "#, | ||
83 | ); | ||
84 | } | ||
85 | |||
86 | #[test] | ||
87 | fn goto_type_definition_works_simple_ref() { | ||
88 | check( | ||
89 | r#" | ||
90 | struct Foo; | ||
91 | //^^^ | ||
92 | fn foo() { | ||
93 | let f: &Foo; f<|> | ||
94 | } | ||
95 | "#, | ||
96 | ); | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn goto_type_definition_works_through_macro() { | ||
101 | check( | ||
102 | r#" | ||
103 | macro_rules! id { ($($tt:tt)*) => { $($tt)* } } | ||
104 | struct Foo {} | ||
105 | //^^^ | ||
106 | id! { | ||
107 | fn bar() { let f<|> = Foo {}; } | ||
108 | } | ||
109 | "#, | ||
110 | ); | ||
111 | } | ||
112 | |||
113 | #[test] | ||
114 | fn goto_type_definition_for_param() { | ||
115 | check( | ||
116 | r#" | ||
117 | struct Foo; | ||
118 | //^^^ | ||
119 | fn foo(<|>f: Foo) {} | ||
120 | "#, | ||
121 | ); | ||
122 | } | ||
123 | |||
124 | #[test] | ||
125 | fn goto_type_definition_for_tuple_field() { | ||
126 | check( | ||
127 | r#" | ||
128 | struct Foo; | ||
129 | //^^^ | ||
130 | struct Bar(Foo); | ||
131 | fn foo() { | ||
132 | let bar = Bar(Foo); | ||
133 | bar.<|>0; | ||
134 | } | ||
135 | "#, | ||
136 | ); | ||
137 | } | ||
138 | |||
139 | #[test] | ||
140 | fn goto_def_for_self_param() { | ||
141 | check( | ||
142 | r#" | ||
143 | struct Foo; | ||
144 | //^^^ | ||
145 | impl Foo { | ||
146 | fn f(&self<|>) {} | ||
147 | } | ||
148 | "#, | ||
149 | ) | ||
150 | } | ||
151 | } | ||