mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-10 06:47:34 +00:00
internal: completion: split out more PathKinds from ImmediateLocation
This commit is contained in:
parent
49b0970970
commit
ae0c7268f7
@ -110,7 +110,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||
if !ctx.config.enable_imports_on_the_fly {
|
||||
return None;
|
||||
}
|
||||
if ctx.in_use_tree()
|
||||
if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use))
|
||||
|| ctx.is_path_disallowed()
|
||||
|| ctx.expects_item()
|
||||
|| ctx.expects_assoc_item()
|
||||
@ -118,6 +118,11 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
// FIXME: This should be encoded in a different way
|
||||
if ctx.pattern_ctx.is_none() && ctx.path_context.is_none() && !ctx.has_dot_receiver() {
|
||||
// completion inside `ast::Name` of a item declaration
|
||||
return None;
|
||||
}
|
||||
let potential_import_name = {
|
||||
let token_kind = ctx.token.kind();
|
||||
if matches!(token_kind, T![.] | T![::]) {
|
||||
@ -147,14 +152,25 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||
}
|
||||
};
|
||||
match (kind, import.original_item) {
|
||||
// Aren't handled in flyimport
|
||||
(PathKind::Vis { .. } | PathKind::Use, _) => false,
|
||||
// modules are always fair game
|
||||
(_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
|
||||
// and so are macros(except for attributes)
|
||||
(
|
||||
PathKind::Expr | PathKind::Type | PathKind::Mac | PathKind::Pat,
|
||||
ItemInNs::Macros(mac),
|
||||
) => mac.is_fn_like(),
|
||||
(PathKind::Mac, _) => true,
|
||||
|
||||
(PathKind::Expr, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
|
||||
|
||||
(PathKind::Pat, ItemInNs::Types(_)) => true,
|
||||
(PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
|
||||
|
||||
(PathKind::Type, ItemInNs::Types(_)) => true,
|
||||
(PathKind::Type, ItemInNs::Values(_)) => false,
|
||||
|
||||
(PathKind::Expr | PathKind::Type, ItemInNs::Macros(mac)) => mac.is_fn_like(),
|
||||
|
||||
(PathKind::Attr, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
|
||||
(PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
|
||||
(PathKind::Attr, _) => false,
|
||||
}
|
||||
|
@ -5,8 +5,9 @@
|
||||
use syntax::{SyntaxKind, T};
|
||||
|
||||
use crate::{
|
||||
context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
|
||||
CompletionItemKind, Completions,
|
||||
context::{PathCompletionContext, PathKind},
|
||||
patterns::ImmediateLocation,
|
||||
CompletionContext, CompletionItem, CompletionItemKind, Completions,
|
||||
};
|
||||
|
||||
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
@ -33,8 +34,8 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
||||
let has_block_expr_parent = ctx.has_block_expr_parent();
|
||||
let expects_item = ctx.expects_item();
|
||||
|
||||
if let Some(ImmediateLocation::Visibility(vis)) = &ctx.completion_location {
|
||||
if vis.in_token().is_none() {
|
||||
if let Some(PathKind::Vis { has_in_token }) = ctx.path_kind() {
|
||||
if !has_in_token {
|
||||
cov_mark::hit!(kw_completion_in);
|
||||
add_keyword("in", "in");
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
//! Completes constants and paths in patterns.
|
||||
//! Completes constants and paths in unqualified patterns.
|
||||
|
||||
use crate::{
|
||||
context::{PatternContext, PatternRefutability},
|
||||
CompletionContext, Completions,
|
||||
};
|
||||
|
||||
/// Completes constants and paths in patterns.
|
||||
/// Completes constants and paths in unqualified patterns.
|
||||
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
let refutable = match ctx.pattern_ctx {
|
||||
Some(PatternContext { refutability, .. }) => refutability == PatternRefutability::Refutable,
|
||||
None => return,
|
||||
Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => {
|
||||
refutability == PatternRefutability::Refutable
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if refutable {
|
||||
|
@ -7,17 +7,23 @@ use rustc_hash::FxHashSet;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
use crate::{
|
||||
context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, Completions,
|
||||
context::{PathCompletionContext, PathKind},
|
||||
patterns::ImmediateLocation,
|
||||
CompletionContext, Completions,
|
||||
};
|
||||
|
||||
pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
|
||||
return;
|
||||
}
|
||||
let (path, use_tree_parent) = match &ctx.path_context {
|
||||
Some(PathCompletionContext { qualifier: Some(qualifier), use_tree_parent, .. }) => {
|
||||
(qualifier, *use_tree_parent)
|
||||
}
|
||||
let (path, use_tree_parent, kind) = match ctx.path_context {
|
||||
// let ... else, syntax would come in really handy here right now
|
||||
Some(PathCompletionContext {
|
||||
qualifier: Some(ref qualifier),
|
||||
use_tree_parent,
|
||||
kind,
|
||||
..
|
||||
}) => (qualifier, use_tree_parent, kind),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
@ -44,7 +50,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||
}
|
||||
return;
|
||||
}
|
||||
Some(ImmediateLocation::Visibility(_)) => {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match kind {
|
||||
Some(PathKind::Vis { .. }) => {
|
||||
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
|
||||
if let Some(current_module) = ctx.scope.module() {
|
||||
if let Some(next) = current_module
|
||||
@ -61,7 +71,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||
}
|
||||
return;
|
||||
}
|
||||
Some(ImmediateLocation::Attribute(_)) => {
|
||||
Some(PathKind::Attr) => {
|
||||
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
|
||||
for (name, def) in module.scope(ctx.db, context_module) {
|
||||
let add_resolution = match def {
|
||||
@ -76,37 +86,38 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||
}
|
||||
return;
|
||||
}
|
||||
Some(PathKind::Use) => {
|
||||
if iter::successors(Some(path.clone()), |p| p.qualifier())
|
||||
.all(|p| p.segment().and_then(|s| s.super_token()).is_some())
|
||||
{
|
||||
acc.add_keyword(ctx, "super::");
|
||||
}
|
||||
// only show `self` in a new use-tree when the qualifier doesn't end in self
|
||||
if use_tree_parent
|
||||
&& !matches!(
|
||||
path.segment().and_then(|it| it.kind()),
|
||||
Some(ast::PathSegmentKind::SelfKw)
|
||||
)
|
||||
{
|
||||
acc.add_keyword(ctx, "self");
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if ctx.in_use_tree() {
|
||||
if iter::successors(Some(path.clone()), |p| p.qualifier())
|
||||
.all(|p| p.segment().and_then(|s| s.super_token()).is_some())
|
||||
{
|
||||
acc.add_keyword(ctx, "super::");
|
||||
}
|
||||
// only show `self` in a new use-tree when the qualifier doesn't end in self
|
||||
if use_tree_parent
|
||||
&& !matches!(
|
||||
path.segment().and_then(|it| it.kind()),
|
||||
Some(ast::PathSegmentKind::SelfKw)
|
||||
)
|
||||
{
|
||||
acc.add_keyword(ctx, "self");
|
||||
}
|
||||
if !matches!(kind, Some(PathKind::Pat)) {
|
||||
// Add associated types on type parameters and `Self`.
|
||||
resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
|
||||
acc.add_type_alias(ctx, alias);
|
||||
None::<()>
|
||||
});
|
||||
}
|
||||
|
||||
// Add associated types on type parameters and `Self`.
|
||||
resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
|
||||
acc.add_type_alias(ctx, alias);
|
||||
None::<()>
|
||||
});
|
||||
|
||||
match resolution {
|
||||
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
|
||||
let module_scope = module.scope(ctx.db, context_module);
|
||||
for (name, def) in module_scope {
|
||||
if ctx.in_use_tree() {
|
||||
if let Some(PathKind::Use) = kind {
|
||||
if let ScopeDef::Unknown = def {
|
||||
if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
|
||||
if name_ref.syntax().text() == name.to_smol_str().as_str() {
|
||||
|
@ -3,15 +3,23 @@
|
||||
use hir::ScopeDef;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
use crate::{patterns::ImmediateLocation, CompletionContext, Completions};
|
||||
use crate::{
|
||||
context::{PathCompletionContext, PathKind},
|
||||
patterns::ImmediateLocation,
|
||||
CompletionContext, Completions,
|
||||
};
|
||||
|
||||
pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
let _p = profile::span("complete_unqualified_path");
|
||||
if ctx.is_path_disallowed() || !ctx.is_trivial_path() || ctx.has_impl_or_trait_prev_sibling() {
|
||||
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
|
||||
return;
|
||||
}
|
||||
let kind = match ctx.path_context {
|
||||
Some(PathCompletionContext { is_trivial_path: true, kind, .. }) => kind,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if ctx.in_use_tree() {
|
||||
if let Some(PathKind::Use) = kind {
|
||||
// only show modules in a fresh UseTree
|
||||
cov_mark::hit!(unqualified_path_only_modules_in_import);
|
||||
ctx.process_all_names(&mut |name, res| {
|
||||
@ -25,8 +33,25 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
||||
}
|
||||
["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
|
||||
|
||||
match kind {
|
||||
Some(PathKind::Vis { .. }) => return,
|
||||
Some(PathKind::Attr) => {
|
||||
ctx.process_all_names(&mut |name, res| {
|
||||
let add_resolution = match res {
|
||||
ScopeDef::MacroDef(mac) => mac.is_attr(),
|
||||
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
|
||||
_ => false,
|
||||
};
|
||||
if add_resolution {
|
||||
acc.add_resolution(ctx, name, &res);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match &ctx.completion_location {
|
||||
Some(ImmediateLocation::Visibility(_)) => return,
|
||||
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
|
||||
// only show macros in {Assoc}ItemList
|
||||
ctx.process_all_names(&mut |name, res| {
|
||||
@ -56,19 +81,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
||||
});
|
||||
return;
|
||||
}
|
||||
Some(ImmediateLocation::Attribute(_)) => {
|
||||
ctx.process_all_names(&mut |name, res| {
|
||||
let add_resolution = match res {
|
||||
ScopeDef::MacroDef(mac) => mac.is_attr(),
|
||||
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
|
||||
_ => false,
|
||||
};
|
||||
if add_resolution {
|
||||
acc.add_resolution(ctx, name, &res);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
@ -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>,
|
||||
@ -70,13 +77,6 @@ pub(super) enum LifetimeContext {
|
||||
LabelDef,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum CallKind {
|
||||
Pat,
|
||||
Mac,
|
||||
Expr,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ParamKind {
|
||||
Function,
|
||||
@ -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)
|
||||
}
|
||||
|
@ -33,8 +33,6 @@ pub(crate) enum ImmediatePrevSibling {
|
||||
/// from which file the nodes are.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ImmediateLocation {
|
||||
Use,
|
||||
UseTree,
|
||||
Rename,
|
||||
Impl,
|
||||
Trait,
|
||||
@ -47,10 +45,7 @@ pub(crate) enum ImmediateLocation {
|
||||
TypeBound,
|
||||
Variant,
|
||||
/// Fake file ast node
|
||||
Attribute(ast::Attr),
|
||||
/// Fake file ast node
|
||||
ModDeclaration(ast::Module),
|
||||
Visibility(ast::Visibility),
|
||||
/// Original file ast node
|
||||
MethodCall {
|
||||
receiver: Option<ast::Expr>,
|
||||
@ -206,9 +201,6 @@ pub(crate) fn determine_location(
|
||||
let res = match_ast! {
|
||||
match parent {
|
||||
ast::IdentPat(_it) => ImmediateLocation::IdentPat,
|
||||
ast::Use(_it) => ImmediateLocation::Use,
|
||||
ast::UseTree(_it) => ImmediateLocation::UseTree,
|
||||
ast::UseTreeList(_it) => ImmediateLocation::UseTree,
|
||||
ast::Rename(_it) => ImmediateLocation::Rename,
|
||||
ast::StmtList(_it) => ImmediateLocation::StmtList,
|
||||
ast::SourceFile(_it) => ImmediateLocation::ItemList,
|
||||
@ -242,7 +234,6 @@ pub(crate) fn determine_location(
|
||||
return None;
|
||||
}
|
||||
},
|
||||
ast::Attr(it) => ImmediateLocation::Attribute(it),
|
||||
ast::FieldExpr(it) => {
|
||||
let receiver = it
|
||||
.expr()
|
||||
@ -268,8 +259,6 @@ pub(crate) fn determine_location(
|
||||
.and_then(|r| find_node_with_range(original_file, r)),
|
||||
has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
|
||||
},
|
||||
ast::Visibility(it) => it.pub_token()
|
||||
.and_then(|t| (t.text_range().end() < offset).then(|| ImmediateLocation::Visibility(it)))?,
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
@ -417,14 +406,6 @@ mod tests {
|
||||
check_location(r"impl A { fn f$0 }", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_use_loc() {
|
||||
check_location(r"use f$0", ImmediateLocation::Use);
|
||||
check_location(r"use f$0;", ImmediateLocation::Use);
|
||||
check_location(r"use f::{f$0}", ImmediateLocation::UseTree);
|
||||
check_location(r"use {f$0}", ImmediateLocation::UseTree);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_record_field_loc() {
|
||||
check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
|
||||
|
@ -4,7 +4,7 @@ use either::Either;
|
||||
use itertools::Itertools;
|
||||
use syntax::ast::{self, HasName};
|
||||
|
||||
use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
|
||||
use crate::{context::PathKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum Params {
|
||||
@ -30,11 +30,11 @@ impl Builder {
|
||||
if !ctx.config.add_call_parenthesis {
|
||||
return false;
|
||||
}
|
||||
if ctx.in_use_tree() {
|
||||
if let Some(PathKind::Use) = ctx.path_kind() {
|
||||
cov_mark::hit!(no_parens_in_use_item);
|
||||
return false;
|
||||
}
|
||||
if matches!(ctx.path_call_kind(), Some(CallKind::Expr | CallKind::Pat))
|
||||
if matches!(ctx.path_kind(), Some(PathKind::Expr | PathKind::Pat) if ctx.path_is_call())
|
||||
| matches!(
|
||||
ctx.completion_location,
|
||||
Some(ImmediateLocation::MethodCall { has_parens: true, .. })
|
||||
|
@ -9,7 +9,7 @@ use syntax::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
context::CallKind,
|
||||
context::PathKind,
|
||||
item::{CompletionItem, ImportEdit},
|
||||
render::RenderContext,
|
||||
};
|
||||
@ -67,9 +67,8 @@ impl<'a> MacroRender<'a> {
|
||||
}
|
||||
|
||||
let needs_bang = self.macro_.is_fn_like()
|
||||
&& !(self.ctx.completion.in_use_tree()
|
||||
|| matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)));
|
||||
let has_parens = self.ctx.completion.path_call_kind().is_some();
|
||||
&& !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use));
|
||||
let has_parens = self.ctx.completion.path_is_call();
|
||||
|
||||
match self.ctx.snippet_cap() {
|
||||
Some(cap) if needs_bang && !has_parens => {
|
||||
@ -92,8 +91,7 @@ impl<'a> MacroRender<'a> {
|
||||
}
|
||||
|
||||
fn needs_bang(&self) -> bool {
|
||||
!self.ctx.completion.in_use_tree()
|
||||
&& !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac))
|
||||
!matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use))
|
||||
}
|
||||
|
||||
fn label(&self) -> SmolStr {
|
||||
|
@ -1000,6 +1000,19 @@ fn function() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flyimport_item_name() {
|
||||
check(
|
||||
r#"
|
||||
mod module {
|
||||
pub struct Struct;
|
||||
}
|
||||
struct Str$0
|
||||
"#,
|
||||
expect![[r#""#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flyimport_rename() {
|
||||
check(
|
||||
|
Loading…
Reference in New Issue
Block a user