diff options
Diffstat (limited to 'crates/ide/src/completion/completion_context.rs')
-rw-r--r-- | crates/ide/src/completion/completion_context.rs | 91 |
1 files changed, 53 insertions, 38 deletions
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 09440334d..3857dce67 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs | |||
@@ -56,7 +56,7 @@ pub(crate) struct CompletionContext<'a> { | |||
56 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 56 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
57 | pub(super) is_trivial_path: bool, | 57 | pub(super) is_trivial_path: bool, |
58 | /// If not a trivial path, the prefix (qualifier). | 58 | /// If not a trivial path, the prefix (qualifier). |
59 | pub(super) path_prefix: Option<hir::Path>, | 59 | pub(super) path_qual: Option<ast::Path>, |
60 | pub(super) after_if: bool, | 60 | pub(super) after_if: bool, |
61 | /// `true` if we are a statement or a last expr in the block. | 61 | /// `true` if we are a statement or a last expr in the block. |
62 | pub(super) can_be_stmt: bool, | 62 | pub(super) can_be_stmt: bool, |
@@ -137,7 +137,7 @@ impl<'a> CompletionContext<'a> { | |||
137 | is_param: false, | 137 | is_param: false, |
138 | is_pat_binding_or_const: false, | 138 | is_pat_binding_or_const: false, |
139 | is_trivial_path: false, | 139 | is_trivial_path: false, |
140 | path_prefix: None, | 140 | path_qual: None, |
141 | after_if: false, | 141 | after_if: false, |
142 | can_be_stmt: false, | 142 | can_be_stmt: false, |
143 | is_expr: false, | 143 | is_expr: false, |
@@ -385,48 +385,54 @@ impl<'a> CompletionContext<'a> { | |||
385 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 385 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); |
386 | self.has_type_args = segment.generic_arg_list().is_some(); | 386 | self.has_type_args = segment.generic_arg_list().is_some(); |
387 | 387 | ||
388 | let hygiene = hir::Hygiene::new(self.db, self.position.file_id.into()); | 388 | if let Some(path) = path_or_use_tree_qualifier(&path) { |
389 | if let Some(path) = hir::Path::from_src(path.clone(), &hygiene) { | 389 | self.path_qual = path |
390 | if let Some(path_prefix) = path.qualifier() { | 390 | .segment() |
391 | self.path_prefix = Some(path_prefix); | 391 | .and_then(|it| { |
392 | find_node_with_range::<ast::PathSegment>( | ||
393 | original_file, | ||
394 | it.syntax().text_range(), | ||
395 | ) | ||
396 | }) | ||
397 | .map(|it| it.parent_path()); | ||
398 | return; | ||
399 | } | ||
400 | |||
401 | if let Some(segment) = path.segment() { | ||
402 | if segment.coloncolon_token().is_some() { | ||
392 | return; | 403 | return; |
393 | } | 404 | } |
394 | } | 405 | } |
395 | 406 | ||
396 | if path.qualifier().is_none() { | 407 | self.is_trivial_path = true; |
397 | self.is_trivial_path = true; | ||
398 | |||
399 | // Find either enclosing expr statement (thing with `;`) or a | ||
400 | // block. If block, check that we are the last expr. | ||
401 | self.can_be_stmt = name_ref | ||
402 | .syntax() | ||
403 | .ancestors() | ||
404 | .find_map(|node| { | ||
405 | if let Some(stmt) = ast::ExprStmt::cast(node.clone()) { | ||
406 | return Some( | ||
407 | stmt.syntax().text_range() == name_ref.syntax().text_range(), | ||
408 | ); | ||
409 | } | ||
410 | if let Some(block) = ast::BlockExpr::cast(node) { | ||
411 | return Some( | ||
412 | block.expr().map(|e| e.syntax().text_range()) | ||
413 | == Some(name_ref.syntax().text_range()), | ||
414 | ); | ||
415 | } | ||
416 | None | ||
417 | }) | ||
418 | .unwrap_or(false); | ||
419 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | ||
420 | 408 | ||
421 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | 409 | // Find either enclosing expr statement (thing with `;`) or a |
422 | if let Some(if_expr) = | 410 | // block. If block, check that we are the last expr. |
423 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) | 411 | self.can_be_stmt = name_ref |
412 | .syntax() | ||
413 | .ancestors() | ||
414 | .find_map(|node| { | ||
415 | if let Some(stmt) = ast::ExprStmt::cast(node.clone()) { | ||
416 | return Some(stmt.syntax().text_range() == name_ref.syntax().text_range()); | ||
417 | } | ||
418 | if let Some(block) = ast::BlockExpr::cast(node) { | ||
419 | return Some( | ||
420 | block.expr().map(|e| e.syntax().text_range()) | ||
421 | == Some(name_ref.syntax().text_range()), | ||
422 | ); | ||
423 | } | ||
424 | None | ||
425 | }) | ||
426 | .unwrap_or(false); | ||
427 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | ||
428 | |||
429 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | ||
430 | if let Some(if_expr) = | ||
431 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) | ||
432 | { | ||
433 | if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start() | ||
424 | { | 434 | { |
425 | if if_expr.syntax().text_range().end() | 435 | self.after_if = true; |
426 | < name_ref.syntax().text_range().start() | ||
427 | { | ||
428 | self.after_if = true; | ||
429 | } | ||
430 | } | 436 | } |
431 | } | 437 | } |
432 | } | 438 | } |
@@ -469,3 +475,12 @@ fn is_node<N: AstNode>(node: &SyntaxNode) -> bool { | |||
469 | Some(n) => n.syntax().text_range() == node.text_range(), | 475 | Some(n) => n.syntax().text_range() == node.text_range(), |
470 | } | 476 | } |
471 | } | 477 | } |
478 | |||
479 | fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> { | ||
480 | if let Some(qual) = path.qualifier() { | ||
481 | return Some(qual); | ||
482 | } | ||
483 | let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; | ||
484 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; | ||
485 | use_tree.path() | ||
486 | } | ||