diff options
author | Mikhail Modin <[email protected]> | 2020-01-18 12:17:34 +0000 |
---|---|---|
committer | Mikhail Modin <[email protected]> | 2020-01-27 21:34:19 +0000 |
commit | fbc3ffcee6eec3d89e27417b3d3543327d810299 (patch) | |
tree | 87cc91dda122f4417ff08e893931c8eb4234da1e /crates/ra_ide | |
parent | 1916a78c6a31219a8aaa73f2b443282441671539 (diff) |
Improves reference search by StructLiteral
Diffstat (limited to 'crates/ra_ide')
-rw-r--r-- | crates/ra_ide/src/references.rs | 138 |
1 files changed, 111 insertions, 27 deletions
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 5e2fe1905..ebded715d 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -112,25 +112,20 @@ impl IntoIterator for ReferenceSearchResult { | |||
112 | 112 | ||
113 | pub(crate) fn find_all_refs( | 113 | pub(crate) fn find_all_refs( |
114 | db: &RootDatabase, | 114 | db: &RootDatabase, |
115 | mut position: FilePosition, | 115 | position: FilePosition, |
116 | search_scope: Option<SearchScope>, | 116 | search_scope: Option<SearchScope>, |
117 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 117 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
118 | let parse = db.parse(position.file_id); | 118 | let parse = db.parse(position.file_id); |
119 | let syntax = parse.tree().syntax().clone(); | 119 | let syntax = parse.tree().syntax().clone(); |
120 | 120 | ||
121 | let token = syntax.token_at_offset(position.offset); | 121 | let (opt_name, search_kind) = |
122 | let mut search_kind = ReferenceKind::Other; | 122 | if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) { |
123 | (Some(name), ReferenceKind::StructLiteral) | ||
124 | } else { | ||
125 | (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other) | ||
126 | }; | ||
123 | 127 | ||
124 | if let TokenAtOffset::Between(ref left, ref right) = token { | 128 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?; |
125 | if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN) | ||
126 | && left.kind() != SyntaxKind::IDENT | ||
127 | { | ||
128 | position = FilePosition { offset: left.text_range().start(), ..position }; | ||
129 | search_kind = ReferenceKind::StructLiteral; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; | ||
134 | 129 | ||
135 | let declaration = match def.kind { | 130 | let declaration = match def.kind { |
136 | NameKind::Macro(mac) => mac.to_nav(db), | 131 | NameKind::Macro(mac) => mac.to_nav(db), |
@@ -170,9 +165,10 @@ fn find_name( | |||
170 | db: &RootDatabase, | 165 | db: &RootDatabase, |
171 | syntax: &SyntaxNode, | 166 | syntax: &SyntaxNode, |
172 | position: FilePosition, | 167 | position: FilePosition, |
168 | opt_name: Option<ast::Name>, | ||
173 | ) -> Option<RangeInfo<(String, NameDefinition)>> { | 169 | ) -> Option<RangeInfo<(String, NameDefinition)>> { |
174 | let mut sb = SourceBinder::new(db); | 170 | let mut sb = SourceBinder::new(db); |
175 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { | 171 | if let Some(name) = opt_name { |
176 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; | 172 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; |
177 | let range = name.syntax().text_range(); | 173 | let range = name.syntax().text_range(); |
178 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); | 174 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
@@ -218,15 +214,8 @@ fn process_definition( | |||
218 | if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) | 214 | if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) |
219 | { | 215 | { |
220 | if d == def { | 216 | if d == def { |
221 | let kind = if name_ref | 217 | let kind = if is_record_lit_name_ref(&name_ref) |
222 | .syntax() | 218 | || is_call_expr_name_ref(&name_ref) |
223 | .ancestors() | ||
224 | .find_map(ast::RecordLit::cast) | ||
225 | .and_then(|l| l.path()) | ||
226 | .and_then(|p| p.segment()) | ||
227 | .and_then(|p| p.name_ref()) | ||
228 | .map(|n| n == name_ref) | ||
229 | .unwrap_or(false) | ||
230 | { | 219 | { |
231 | ReferenceKind::StructLiteral | 220 | ReferenceKind::StructLiteral |
232 | } else { | 221 | } else { |
@@ -301,6 +290,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc | |||
301 | mode.or(Some(ReferenceAccess::Read)) | 290 | mode.or(Some(ReferenceAccess::Read)) |
302 | } | 291 | } |
303 | 292 | ||
293 | fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
294 | name_ref | ||
295 | .syntax() | ||
296 | .ancestors() | ||
297 | .find_map(ast::RecordLit::cast) | ||
298 | .and_then(|l| l.path()) | ||
299 | .and_then(|p| p.segment()) | ||
300 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
301 | .unwrap_or(false) | ||
302 | } | ||
303 | |||
304 | fn get_struct_def_name_for_struc_litetal_search( | ||
305 | syntax: &SyntaxNode, | ||
306 | position: FilePosition, | ||
307 | ) -> Option<ast::Name> { | ||
308 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { | ||
309 | if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { | ||
310 | return None; | ||
311 | } | ||
312 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, left.text_range().start()) { | ||
313 | return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); | ||
314 | } | ||
315 | if find_node_at_offset::<ast::TypeParamList>(&syntax, left.text_range().start()).is_some() { | ||
316 | return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); | ||
317 | } | ||
318 | } | ||
319 | None | ||
320 | } | ||
321 | |||
322 | fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | ||
323 | name_ref | ||
324 | .syntax() | ||
325 | .ancestors() | ||
326 | .find_map(ast::CallExpr::cast) | ||
327 | .and_then(|c| match c.expr()? { | ||
328 | ast::Expr::PathExpr(p) => { | ||
329 | Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) | ||
330 | } | ||
331 | _ => None, | ||
332 | }) | ||
333 | .unwrap_or(false) | ||
334 | } | ||
335 | |||
304 | #[cfg(test)] | 336 | #[cfg(test)] |
305 | mod tests { | 337 | mod tests { |
306 | use crate::{ | 338 | use crate::{ |
@@ -309,7 +341,7 @@ mod tests { | |||
309 | }; | 341 | }; |
310 | 342 | ||
311 | #[test] | 343 | #[test] |
312 | fn test_struct_literal() { | 344 | fn test_struct_literal_after_space() { |
313 | let code = r#" | 345 | let code = r#" |
314 | struct Foo <|>{ | 346 | struct Foo <|>{ |
315 | a: i32, | 347 | a: i32, |
@@ -331,6 +363,58 @@ mod tests { | |||
331 | } | 363 | } |
332 | 364 | ||
333 | #[test] | 365 | #[test] |
366 | fn test_struct_literal_befor_space() { | ||
367 | let code = r#" | ||
368 | struct Foo<|> {} | ||
369 | fn main() { | ||
370 | let f: Foo; | ||
371 | f = Foo {}; | ||
372 | }"#; | ||
373 | |||
374 | let refs = get_all_refs(code); | ||
375 | check_result( | ||
376 | refs, | ||
377 | "Foo STRUCT_DEF FileId(1) [5; 18) [12; 15) Other", | ||
378 | &["FileId(1) [54; 57) Other", "FileId(1) [71; 74) StructLiteral"], | ||
379 | ); | ||
380 | } | ||
381 | |||
382 | #[test] | ||
383 | fn test_struct_literal_with_generic_type() { | ||
384 | let code = r#" | ||
385 | struct Foo<T> <|>{} | ||
386 | fn main() { | ||
387 | let f: Foo::<i32>; | ||
388 | f = Foo {}; | ||
389 | }"#; | ||
390 | |||
391 | let refs = get_all_refs(code); | ||
392 | check_result( | ||
393 | refs, | ||
394 | "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other", | ||
395 | &["FileId(1) [81; 84) StructLiteral"], | ||
396 | ); | ||
397 | } | ||
398 | |||
399 | #[test] | ||
400 | fn test_struct_literal_for_tuple() { | ||
401 | let code = r#" | ||
402 | struct Foo<|>(i32); | ||
403 | |||
404 | fn main() { | ||
405 | let f: Foo; | ||
406 | f = Foo(1); | ||
407 | }"#; | ||
408 | |||
409 | let refs = get_all_refs(code); | ||
410 | check_result( | ||
411 | refs, | ||
412 | "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other", | ||
413 | &["FileId(1) [71; 74) StructLiteral"], | ||
414 | ); | ||
415 | } | ||
416 | |||
417 | #[test] | ||
334 | fn test_find_all_refs_for_local() { | 418 | fn test_find_all_refs_for_local() { |
335 | let code = r#" | 419 | let code = r#" |
336 | fn main() { | 420 | fn main() { |
@@ -564,7 +648,7 @@ mod tests { | |||
564 | check_result( | 648 | check_result( |
565 | refs, | 649 | refs, |
566 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", | 650 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", |
567 | &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], | 651 | &["FileId(2) [16; 20) StructLiteral", "FileId(3) [16; 20) StructLiteral"], |
568 | ); | 652 | ); |
569 | 653 | ||
570 | let refs = | 654 | let refs = |
@@ -572,7 +656,7 @@ mod tests { | |||
572 | check_result( | 656 | check_result( |
573 | refs, | 657 | refs, |
574 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", | 658 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", |
575 | &["FileId(3) [16; 20) Other"], | 659 | &["FileId(3) [16; 20) StructLiteral"], |
576 | ); | 660 | ); |
577 | } | 661 | } |
578 | 662 | ||
@@ -591,7 +675,7 @@ mod tests { | |||
591 | check_result( | 675 | check_result( |
592 | refs, | 676 | refs, |
593 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", | 677 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", |
594 | &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], | 678 | &["FileId(1) [96; 98) StructLiteral", "FileId(1) [114; 116) StructLiteral"], |
595 | ); | 679 | ); |
596 | } | 680 | } |
597 | 681 | ||