Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide_completion/src/context.rs')
| -rw-r--r-- | crates/ide_completion/src/context.rs | 160 |
1 files changed, 86 insertions, 74 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index b3ce1f8e9c..e8566b80b6 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -35,16 +35,23 @@ pub(super) enum PathKind { Expr, Type, Attr, + Mac, + Pat, + Vis { has_in_token: bool }, + Use, } #[derive(Debug)] pub(crate) struct PathCompletionContext { /// If this is a call with () already there - call_kind: Option<CallKind>, + has_call_parens: bool, /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. pub(super) is_trivial_path: bool, /// If not a trivial path, the prefix (qualifier). pub(super) qualifier: Option<ast::Path>, + #[allow(dead_code)] + /// If not a trivial path, the suffix (parent). + pub(super) parent: Option<ast::Path>, /// Whether the qualifier comes from a use tree parent or not pub(super) use_tree_parent: bool, pub(super) kind: Option<PathKind>, @@ -71,13 +78,6 @@ pub(super) enum LifetimeContext { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum CallKind { - Pat, - Mac, - Expr, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum ParamKind { Function, Closure, @@ -206,13 +206,6 @@ impl<'a> CompletionContext<'a> { ) } - pub(crate) fn in_use_tree(&self) -> bool { - matches!( - self.completion_location, - Some(ImmediateLocation::Use | ImmediateLocation::UseTree) - ) - } - pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { matches!( self.prev_sibling, @@ -257,8 +250,8 @@ impl<'a> CompletionContext<'a> { matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. })) } - pub(crate) fn path_call_kind(&self) -> Option<CallKind> { - self.path_context.as_ref().and_then(|it| it.call_kind) + pub(crate) fn path_is_call(&self) -> bool { + self.path_context.as_ref().map_or(false, |it| it.has_call_parens) } pub(crate) fn is_trivial_path(&self) -> bool { @@ -673,7 +666,12 @@ impl<'a> CompletionContext<'a> { Self::classify_lifetime(&self.sema, original_file, lifetime, offset); } ast::NameLike::NameRef(name_ref) => { - self.path_context = Self::classify_name_ref(&self.sema, original_file, name_ref); + if let Some((path_ctx, pat_ctx)) = + Self::classify_name_ref(&self.sema, original_file, name_ref) + { + self.path_context = Some(path_ctx); + self.pattern_ctx = pat_ctx; + } } ast::NameLike::Name(name) => { self.pattern_ctx = Self::classify_name(&self.sema, name); @@ -716,83 +714,61 @@ impl<'a> CompletionContext<'a> { if !bind_pat.is_simple_ident() { return None; } - let mut is_param = None; - let (refutability, has_type_ascription) = bind_pat - .syntax() - .ancestors() - .skip_while(|it| ast::Pat::can_cast(it.kind())) - .next() - .map_or((PatternRefutability::Irrefutable, false), |node| { - let refutability = match_ast! { - match node { - ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()), - ast::Param(param) => { - let is_closure_param = param - .syntax() - .ancestors() - .nth(2) - .and_then(ast::ClosureExpr::cast) - .is_some(); - is_param = Some(if is_closure_param { - ParamKind::Closure - } else { - ParamKind::Function - }); - return (PatternRefutability::Irrefutable, param.ty().is_some()) - }, - ast::MatchArm(__) => PatternRefutability::Refutable, - ast::Condition(__) => PatternRefutability::Refutable, - ast::ForExpr(__) => PatternRefutability::Irrefutable, - _ => PatternRefutability::Irrefutable, - } - }; - (refutability, false) - }); - Some(PatternContext { refutability, is_param, has_type_ascription }) + Some(pattern_context_for(bind_pat.into())) } fn classify_name_ref( _sema: &Semantics<RootDatabase>, original_file: &SyntaxNode, name_ref: ast::NameRef, - ) -> Option<PathCompletionContext> { + ) -> Option<(PathCompletionContext, Option<PatternContext>)> { let parent = name_ref.syntax().parent()?; let segment = ast::PathSegment::cast(parent)?; + let path = segment.parent_path(); let mut path_ctx = PathCompletionContext { - call_kind: None, + has_call_parens: false, is_trivial_path: false, qualifier: None, + parent: None, has_type_args: false, can_be_stmt: false, in_loop_body: false, use_tree_parent: false, kind: None, }; + let mut pat_ctx = None; path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); - let path = segment.parent_path(); - - if let Some(p) = path.syntax().parent() { - path_ctx.call_kind = match_ast! { - match p { - ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr), - ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)), - ast::TupleStructPat(_it) => Some(CallKind::Pat), - _ => None - } - }; - } - if let Some(parent) = path.syntax().parent() { - path_ctx.kind = match_ast! { - match parent { + path_ctx.kind = path.syntax().ancestors().find_map(|it| { + match_ast! { + match it { ast::PathType(_it) => Some(PathKind::Type), - ast::PathExpr(_it) => Some(PathKind::Expr), + ast::PathExpr(it) => { + path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + Some(PathKind::Expr) + }, + ast::TupleStructPat(it) => { + path_ctx.has_call_parens = true; + pat_ctx = Some(pattern_context_for(it.into())); + Some(PathKind::Pat) + }, + ast::RecordPat(it) => { + pat_ctx = Some(pattern_context_for(it.into())); + Some(PathKind::Pat) + }, + ast::PathPat(it) => { + pat_ctx = Some(pattern_context_for(it.into())); + Some(PathKind::Pat) + }, + ast::MacroCall(it) => it.excl_token().and(Some(PathKind::Mac)), ast::Meta(_it) => Some(PathKind::Attr), + ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }), + ast::UseTree(_it) => Some(PathKind::Use), _ => None, } - }; - } + } + }); path_ctx.has_type_args = segment.generic_arg_list().is_some(); if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) { @@ -806,12 +782,12 @@ impl<'a> CompletionContext<'a> { ) }) .map(|it| it.parent_path()); - return Some(path_ctx); + return Some((path_ctx, pat_ctx)); } if let Some(segment) = path.segment() { if segment.coloncolon_token().is_some() { - return Some(path_ctx); + return Some((path_ctx, pat_ctx)); } } @@ -835,10 +811,46 @@ impl<'a> CompletionContext<'a> { None }) .unwrap_or(false); - Some(path_ctx) + Some((path_ctx, pat_ctx)) } } +fn pattern_context_for(pat: ast::Pat) -> PatternContext { + let mut is_param = None; + let (refutability, has_type_ascription) = + pat + .syntax() + .ancestors() + .skip_while(|it| ast::Pat::can_cast(it.kind())) + .next() + .map_or((PatternRefutability::Irrefutable, false), |node| { + let refutability = match_ast! { + match node { + ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()), + ast::Param(param) => { + let is_closure_param = param + .syntax() + .ancestors() + .nth(2) + .and_then(ast::ClosureExpr::cast) + .is_some(); + is_param = Some(if is_closure_param { + ParamKind::Closure + } else { + ParamKind::Function + }); + return (PatternRefutability::Irrefutable, param.ty().is_some()) + }, + ast::MatchArm(__) => PatternRefutability::Refutable, + ast::Condition(__) => PatternRefutability::Refutable, + ast::ForExpr(__) => PatternRefutability::Irrefutable, + _ => PatternRefutability::Irrefutable, + } + }; + (refutability, false) + }); + PatternContext { refutability, is_param, has_type_ascription } +} fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> { syntax.covering_element(range).ancestors().find_map(N::cast) } |