diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-12-14 17:20:18 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-12-14 17:20:18 +0000 |
commit | 202ad1e2d9376565cb273cf085be600ed10e5a93 (patch) | |
tree | 21ef288f183f39468147972aef88617411a3fdec | |
parent | 77db6177658b32f69ad7ebfdef96c1b3b2893fdd (diff) | |
parent | c82529a97f10b1302d2944f1946bcb3479f64e2d (diff) |
Merge #2552
2552: fix goto definition when inbetween tokens r=matklad a=succcubbus
fixes both goto_definition and goto_type_definition.
before, when running goto between some non-trivia token and an
identifier, goto would be attempted for the non-trivia token.
but this does not make sense for e.g. L_PAREN or COLONCOLON only for
IDENTs.
this resulted in goto actions not working when running them on the first
character of some identifier e.g. for `module::<|>method()` or
`method(<|>parameter)`.
now only IDENTs will be searched for in goto actions, though i'm not sure
if this is correct or if goto should also work for some other token types.
Co-authored-by: succcubbus <[email protected]>
-rw-r--r-- | crates/ra_ide/src/goto_definition.rs | 46 | ||||
-rw-r--r-- | crates/ra_ide/src/goto_type_definition.rs | 43 | ||||
-rw-r--r-- | crates/ra_ide/src/hover.rs | 23 |
3 files changed, 106 insertions, 6 deletions
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index cfe62037f..27052d72b 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -3,7 +3,9 @@ | |||
3 | use hir::{db::AstDatabase, InFile}; | 3 | use hir::{db::AstDatabase, InFile}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{self, DocCommentsOwner}, | 5 | ast::{self, DocCommentsOwner}, |
6 | match_ast, AstNode, SyntaxNode, | 6 | match_ast, AstNode, |
7 | SyntaxKind::*, | ||
8 | SyntaxNode, SyntaxToken, TokenAtOffset, | ||
7 | }; | 9 | }; |
8 | 10 | ||
9 | use crate::{ | 11 | use crate::{ |
@@ -19,8 +21,7 @@ pub(crate) fn goto_definition( | |||
19 | position: FilePosition, | 21 | position: FilePosition, |
20 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 22 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
21 | let file = db.parse_or_expand(position.file_id.into())?; | 23 | let file = db.parse_or_expand(position.file_id.into())?; |
22 | let original_token = | 24 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
23 | file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; | ||
24 | let token = descend_into_macros(db, position.file_id, original_token.clone()); | 25 | let token = descend_into_macros(db, position.file_id, original_token.clone()); |
25 | 26 | ||
26 | let nav_targets = match_ast! { | 27 | let nav_targets = match_ast! { |
@@ -38,6 +39,17 @@ pub(crate) fn goto_definition( | |||
38 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) | 39 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) |
39 | } | 40 | } |
40 | 41 | ||
42 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | ||
43 | return tokens.max_by_key(priority); | ||
44 | fn priority(n: &SyntaxToken) -> usize { | ||
45 | match n.kind() { | ||
46 | IDENT | INT_NUMBER => 2, | ||
47 | kind if kind.is_trivia() => 0, | ||
48 | _ => 1, | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
41 | #[derive(Debug)] | 53 | #[derive(Debug)] |
42 | pub(crate) enum ReferenceResult { | 54 | pub(crate) enum ReferenceResult { |
43 | Exact(NavigationTarget), | 55 | Exact(NavigationTarget), |
@@ -235,6 +247,18 @@ mod tests { | |||
235 | } | 247 | } |
236 | 248 | ||
237 | #[test] | 249 | #[test] |
250 | fn goto_definition_works_at_start_of_item() { | ||
251 | check_goto( | ||
252 | " | ||
253 | //- /lib.rs | ||
254 | struct Foo; | ||
255 | enum E { X(<|>Foo) } | ||
256 | ", | ||
257 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", | ||
258 | ); | ||
259 | } | ||
260 | |||
261 | #[test] | ||
238 | fn goto_definition_resolves_correct_name() { | 262 | fn goto_definition_resolves_correct_name() { |
239 | check_goto( | 263 | check_goto( |
240 | " | 264 | " |
@@ -435,6 +459,22 @@ mod tests { | |||
435 | } | 459 | } |
436 | 460 | ||
437 | #[test] | 461 | #[test] |
462 | fn goto_for_tuple_fields() { | ||
463 | check_goto( | ||
464 | " | ||
465 | //- /lib.rs | ||
466 | struct Foo(u32); | ||
467 | |||
468 | fn bar() { | ||
469 | let foo = Foo(0); | ||
470 | foo.<|>0; | ||
471 | } | ||
472 | ", | ||
473 | "TUPLE_FIELD_DEF FileId(1) [11; 14)", | ||
474 | ); | ||
475 | } | ||
476 | |||
477 | #[test] | ||
438 | fn goto_definition_works_for_ufcs_inherent_methods() { | 478 | fn goto_definition_works_for_ufcs_inherent_methods() { |
439 | check_goto( | 479 | check_goto( |
440 | " | 480 | " |
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index 992a08809..ce8b6c72a 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::db::AstDatabase; | 3 | use hir::db::AstDatabase; |
4 | use ra_syntax::{ast, AstNode}; | 4 | use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, | 7 | db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, |
@@ -13,7 +13,7 @@ pub(crate) fn goto_type_definition( | |||
13 | position: FilePosition, | 13 | position: FilePosition, |
14 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 14 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
15 | let file = db.parse_or_expand(position.file_id.into())?; | 15 | let file = db.parse_or_expand(position.file_id.into())?; |
16 | let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; | 16 | let token = pick_best(file.token_at_offset(position.offset))?; |
17 | let token = descend_into_macros(db, position.file_id, token); | 17 | let token = descend_into_macros(db, position.file_id, token); |
18 | 18 | ||
19 | let node = token.value.ancestors().find_map(|token| { | 19 | let node = token.value.ancestors().find_map(|token| { |
@@ -41,6 +41,17 @@ pub(crate) fn goto_type_definition( | |||
41 | Some(RangeInfo::new(node.text_range(), vec![nav])) | 41 | Some(RangeInfo::new(node.text_range(), vec![nav])) |
42 | } | 42 | } |
43 | 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 => 2, | ||
49 | kind if kind.is_trivia() => 0, | ||
50 | _ => 1, | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
44 | #[cfg(test)] | 55 | #[cfg(test)] |
45 | mod tests { | 56 | mod tests { |
46 | use crate::mock_analysis::analysis_and_position; | 57 | use crate::mock_analysis::analysis_and_position; |
@@ -102,4 +113,32 @@ mod tests { | |||
102 | "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)", | 113 | "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)", |
103 | ); | 114 | ); |
104 | } | 115 | } |
116 | |||
117 | #[test] | ||
118 | fn goto_type_definition_for_param() { | ||
119 | check_goto( | ||
120 | " | ||
121 | //- /lib.rs | ||
122 | struct Foo; | ||
123 | fn foo(<|>f: Foo) {} | ||
124 | ", | ||
125 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", | ||
126 | ); | ||
127 | } | ||
128 | |||
129 | #[test] | ||
130 | fn goto_type_definition_for_tuple_field() { | ||
131 | check_goto( | ||
132 | " | ||
133 | //- /lib.rs | ||
134 | struct Foo; | ||
135 | struct Bar(Foo); | ||
136 | fn foo() { | ||
137 | let bar = Bar(Foo); | ||
138 | bar.<|>0; | ||
139 | } | ||
140 | ", | ||
141 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", | ||
142 | ); | ||
143 | } | ||
105 | } | 144 | } |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index d372ca758..51e320128 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -6,6 +6,8 @@ use ra_syntax::{ | |||
6 | algo::find_covering_element, | 6 | algo::find_covering_element, |
7 | ast::{self, DocCommentsOwner}, | 7 | ast::{self, DocCommentsOwner}, |
8 | match_ast, AstNode, | 8 | match_ast, AstNode, |
9 | SyntaxKind::*, | ||
10 | SyntaxToken, TokenAtOffset, | ||
9 | }; | 11 | }; |
10 | 12 | ||
11 | use crate::{ | 13 | use crate::{ |
@@ -156,7 +158,7 @@ fn hover_text_from_name_kind( | |||
156 | 158 | ||
157 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | 159 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { |
158 | let file = db.parse_or_expand(position.file_id.into())?; | 160 | let file = db.parse_or_expand(position.file_id.into())?; |
159 | let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; | 161 | let token = pick_best(file.token_at_offset(position.offset))?; |
160 | let token = descend_into_macros(db, position.file_id, token); | 162 | let token = descend_into_macros(db, position.file_id, token); |
161 | 163 | ||
162 | let mut res = HoverResult::new(); | 164 | let mut res = HoverResult::new(); |
@@ -218,6 +220,18 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
218 | Some(RangeInfo::new(range, res)) | 220 | Some(RangeInfo::new(range, res)) |
219 | } | 221 | } |
220 | 222 | ||
223 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | ||
224 | return tokens.max_by_key(priority); | ||
225 | fn priority(n: &SyntaxToken) -> usize { | ||
226 | match n.kind() { | ||
227 | IDENT | INT_NUMBER => 3, | ||
228 | L_PAREN | R_PAREN => 2, | ||
229 | kind if kind.is_trivia() => 0, | ||
230 | _ => 1, | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | |||
221 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | 235 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { |
222 | let parse = db.parse(frange.file_id); | 236 | let parse = db.parse(frange.file_id); |
223 | let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); | 237 | let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); |
@@ -505,6 +519,13 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
505 | } | 519 | } |
506 | 520 | ||
507 | #[test] | 521 | #[test] |
522 | fn hover_for_param_edge() { | ||
523 | let (analysis, position) = single_file_with_position("fn func(<|>foo: i32) {}"); | ||
524 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
525 | assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); | ||
526 | } | ||
527 | |||
528 | #[test] | ||
508 | fn test_type_of_for_function() { | 529 | fn test_type_of_for_function() { |
509 | let (analysis, range) = single_file_with_range( | 530 | let (analysis, range) = single_file_with_range( |
510 | " | 531 | " |