aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r--crates/ide_completion/src/context.rs72
1 files changed, 53 insertions, 19 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 6d57da06a..4c2b31084 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -4,8 +4,11 @@ use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
4use ide_db::base_db::{FilePosition, SourceDatabase}; 4use ide_db::base_db::{FilePosition, SourceDatabase};
5use ide_db::{call_info::ActiveParameter, RootDatabase}; 5use ide_db::{call_info::ActiveParameter, RootDatabase};
6use syntax::{ 6use syntax::{
7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, 7 algo::find_node_at_offset,
8 SyntaxToken, TextRange, TextSize, 8 ast::{self, NameOrNameRef, NameOwner},
9 match_ast, AstNode, NodeOrToken,
10 SyntaxKind::*,
11 SyntaxNode, SyntaxToken, TextRange, TextSize,
9}; 12};
10 13
11use text_edit::Indel; 14use text_edit::Indel;
@@ -35,15 +38,18 @@ pub(crate) struct CompletionContext<'a> {
35 /// The token before the cursor, in the macro-expanded file. 38 /// The token before the cursor, in the macro-expanded file.
36 pub(super) token: SyntaxToken, 39 pub(super) token: SyntaxToken,
37 pub(super) krate: Option<hir::Crate>, 40 pub(super) krate: Option<hir::Crate>,
38 pub(super) expected_name: Option<String>, 41 pub(super) expected_name: Option<NameOrNameRef>,
39 pub(super) expected_type: Option<Type>, 42 pub(super) expected_type: Option<Type>,
40 pub(super) name_ref_syntax: Option<ast::NameRef>, 43 pub(super) name_ref_syntax: Option<ast::NameRef>,
44 pub(super) lifetime_syntax: Option<ast::Lifetime>,
45 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
41 pub(super) function_syntax: Option<ast::Fn>, 46 pub(super) function_syntax: Option<ast::Fn>,
42 pub(super) use_item_syntax: Option<ast::Use>, 47 pub(super) use_item_syntax: Option<ast::Use>,
43 pub(super) record_lit_syntax: Option<ast::RecordExpr>, 48 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
44 pub(super) record_pat_syntax: Option<ast::RecordPat>, 49 pub(super) record_pat_syntax: Option<ast::RecordPat>,
45 pub(super) record_field_syntax: Option<ast::RecordExprField>, 50 pub(super) record_field_syntax: Option<ast::RecordExprField>,
46 pub(super) impl_def: Option<ast::Impl>, 51 pub(super) impl_def: Option<ast::Impl>,
52 pub(super) lifetime_allowed: bool,
47 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
48 pub(super) active_parameter: Option<ActiveParameter>, 54 pub(super) active_parameter: Option<ActiveParameter>,
49 pub(super) is_param: bool, 55 pub(super) is_param: bool,
@@ -136,9 +142,12 @@ impl<'a> CompletionContext<'a> {
136 original_token, 142 original_token,
137 token, 143 token,
138 krate, 144 krate,
145 lifetime_allowed: false,
139 expected_name: None, 146 expected_name: None,
140 expected_type: None, 147 expected_type: None,
141 name_ref_syntax: None, 148 name_ref_syntax: None,
149 lifetime_syntax: None,
150 lifetime_param_syntax: None,
142 function_syntax: None, 151 function_syntax: None,
143 use_item_syntax: None, 152 use_item_syntax: None,
144 record_lit_syntax: None, 153 record_lit_syntax: None,
@@ -241,7 +250,7 @@ impl<'a> CompletionContext<'a> {
241 pub(crate) fn source_range(&self) -> TextRange { 250 pub(crate) fn source_range(&self) -> TextRange {
242 // check kind of macro-expanded token, but use range of original token 251 // check kind of macro-expanded token, but use range of original token
243 let kind = self.token.kind(); 252 let kind = self.token.kind();
244 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { 253 if kind == IDENT || kind == LIFETIME_IDENT || kind == UNDERSCORE || kind.is_keyword() {
245 cov_mark::hit!(completes_if_prefix_is_keyword); 254 cov_mark::hit!(completes_if_prefix_is_keyword);
246 self.original_token.text_range() 255 self.original_token.text_range()
247 } else { 256 } else {
@@ -292,13 +301,13 @@ impl<'a> CompletionContext<'a> {
292 file_with_fake_ident: SyntaxNode, 301 file_with_fake_ident: SyntaxNode,
293 offset: TextSize, 302 offset: TextSize,
294 ) { 303 ) {
295 let expected = { 304 let (expected_type, expected_name) = {
296 let mut node = match self.token.parent() { 305 let mut node = match self.token.parent() {
297 Some(it) => it, 306 Some(it) => it,
298 None => return, 307 None => return,
299 }; 308 };
300 loop { 309 loop {
301 let ret = match_ast! { 310 break match_ast! {
302 match node { 311 match node {
303 ast::LetStmt(it) => { 312 ast::LetStmt(it) => {
304 cov_mark::hit!(expected_type_let_with_leading_char); 313 cov_mark::hit!(expected_type_let_with_leading_char);
@@ -306,7 +315,7 @@ impl<'a> CompletionContext<'a> {
306 let ty = it.pat() 315 let ty = it.pat()
307 .and_then(|pat| self.sema.type_of_pat(&pat)); 316 .and_then(|pat| self.sema.type_of_pat(&pat));
308 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { 317 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
309 Some(ident.syntax().text().to_string()) 318 ident.name().map(NameOrNameRef::Name)
310 } else { 319 } else {
311 None 320 None
312 }; 321 };
@@ -319,7 +328,10 @@ impl<'a> CompletionContext<'a> {
319 ActiveParameter::at_token( 328 ActiveParameter::at_token(
320 &self.sema, 329 &self.sema,
321 self.token.clone(), 330 self.token.clone(),
322 ).map(|ap| (Some(ap.ty), Some(ap.name))) 331 ).map(|ap| {
332 let name = ap.ident().map(NameOrNameRef::Name);
333 (Some(ap.ty), name)
334 })
323 .unwrap_or((None, None)) 335 .unwrap_or((None, None))
324 }, 336 },
325 ast::RecordExprFieldList(_it) => { 337 ast::RecordExprFieldList(_it) => {
@@ -327,10 +339,10 @@ impl<'a> CompletionContext<'a> {
327 self.token.prev_sibling_or_token() 339 self.token.prev_sibling_or_token()
328 .and_then(|se| se.into_node()) 340 .and_then(|se| se.into_node())
329 .and_then(|node| ast::RecordExprField::cast(node)) 341 .and_then(|node| ast::RecordExprField::cast(node))
330 .and_then(|rf| self.sema.resolve_record_field(&rf)) 342 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
331 .map(|f|( 343 .map(|(f, rf)|(
332 Some(f.0.signature_ty(self.db)), 344 Some(f.0.signature_ty(self.db)),
333 Some(f.0.name(self.db).to_string()), 345 rf.field_name().map(NameOrNameRef::NameRef),
334 )) 346 ))
335 .unwrap_or((None, None)) 347 .unwrap_or((None, None))
336 }, 348 },
@@ -340,7 +352,7 @@ impl<'a> CompletionContext<'a> {
340 .resolve_record_field(&it) 352 .resolve_record_field(&it)
341 .map(|f|( 353 .map(|f|(
342 Some(f.0.signature_ty(self.db)), 354 Some(f.0.signature_ty(self.db)),
343 Some(f.0.name(self.db).to_string()), 355 it.field_name().map(NameOrNameRef::NameRef),
344 )) 356 ))
345 .unwrap_or((None, None)) 357 .unwrap_or((None, None))
346 }, 358 },
@@ -378,14 +390,17 @@ impl<'a> CompletionContext<'a> {
378 }, 390 },
379 } 391 }
380 }; 392 };
381
382 break ret;
383 } 393 }
384 }; 394 };
385 self.expected_type = expected.0; 395 self.expected_type = expected_type;
386 self.expected_name = expected.1; 396 self.expected_name = expected_name;
387 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 397 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
388 398
399 if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset)
400 {
401 self.classify_lifetime(original_file, lifetime, offset);
402 }
403
389 // First, let's try to complete a reference to some declaration. 404 // First, let's try to complete a reference to some declaration.
390 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 405 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
391 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. 406 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
@@ -445,6 +460,23 @@ impl<'a> CompletionContext<'a> {
445 } 460 }
446 } 461 }
447 462
463 fn classify_lifetime(
464 &mut self,
465 original_file: &SyntaxNode,
466 lifetime: ast::Lifetime,
467 offset: TextSize,
468 ) {
469 self.lifetime_syntax =
470 find_node_at_offset(original_file, lifetime.syntax().text_range().start());
471 if lifetime.syntax().parent().map_or(false, |p| p.kind() != syntax::SyntaxKind::ERROR) {
472 self.lifetime_allowed = true;
473 }
474 if let Some(_) = lifetime.syntax().parent().and_then(ast::LifetimeParam::cast) {
475 self.lifetime_param_syntax =
476 self.sema.find_node_at_offset_with_macros(original_file, offset);
477 }
478 }
479
448 fn classify_name_ref( 480 fn classify_name_ref(
449 &mut self, 481 &mut self,
450 original_file: &SyntaxNode, 482 original_file: &SyntaxNode,
@@ -452,11 +484,11 @@ impl<'a> CompletionContext<'a> {
452 offset: TextSize, 484 offset: TextSize,
453 ) { 485 ) {
454 self.name_ref_syntax = 486 self.name_ref_syntax =
455 find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); 487 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
456 let name_range = name_ref.syntax().text_range(); 488 let name_range = name_ref.syntax().text_range();
457 if ast::RecordExprField::for_field_name(&name_ref).is_some() { 489 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
458 self.record_lit_syntax = 490 self.record_lit_syntax =
459 self.sema.find_node_at_offset_with_macros(&original_file, offset); 491 self.sema.find_node_at_offset_with_macros(original_file, offset);
460 } 492 }
461 493
462 self.fill_impl_def(); 494 self.fill_impl_def();
@@ -631,7 +663,9 @@ mod tests {
631 .map(|t| t.display_test(&db).to_string()) 663 .map(|t| t.display_test(&db).to_string())
632 .unwrap_or("?".to_owned()); 664 .unwrap_or("?".to_owned());
633 665
634 let name = completion_context.expected_name.unwrap_or("?".to_owned()); 666 let name = completion_context
667 .expected_name
668 .map_or_else(|| "?".to_owned(), |name| name.to_string());
635 669
636 expect.assert_eq(&format!("ty: {}, name: {}", ty, name)); 670 expect.assert_eq(&format!("ty: {}, name: {}", ty, name));
637 } 671 }