From ae0c7268f7c19d83c7be342bf7c3f3d68f2a0599 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 6 Dec 2021 15:51:33 +0100 Subject: [PATCH] internal: completion: split out more PathKinds from ImmediateLocation --- .../src/completions/flyimport.rs | 24 ++- .../ide_completion/src/completions/keyword.rs | 9 +- .../ide_completion/src/completions/pattern.rs | 10 +- .../src/completions/qualified_path.rs | 69 ++++---- .../src/completions/unqualified_path.rs | 46 +++-- crates/ide_completion/src/context.rs | 160 ++++++++++-------- crates/ide_completion/src/patterns.rs | 19 --- .../ide_completion/src/render/builder_ext.rs | 6 +- crates/ide_completion/src/render/macro_.rs | 10 +- crates/ide_completion/src/tests/flyimport.rs | 13 ++ 10 files changed, 206 insertions(+), 160 deletions(-) diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 446a808de84..3a9c1b3beb7 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs @@ -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, } diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 36d25d5d029..1c686dc0a63 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs @@ -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"); } diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index c63c274151c..04765901428 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -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 { diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index 4abf7374b3b..b5c3d83c168 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -7,17 +7,23 @@ 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() { diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 414c1d961bb..ac1ba2da7c4 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -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; - } _ => (), } diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index b3ce1f8e9c4..e8566b80b6f 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, + 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, + #[allow(dead_code)] + /// If not a trivial path, the suffix (parent). + pub(super) parent: Option, /// Whether the qualifier comes from a use tree parent or not pub(super) use_tree_parent: bool, pub(super) kind: Option, @@ -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 @@ pub(crate) fn expect_field(&self) -> bool { ) } - 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 @@ pub(crate) fn expects_type(&self) -> bool { matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. })) } - pub(crate) fn path_call_kind(&self) -> Option { - 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 @@ fn fill( 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 @@ fn classify_name(_sema: &Semantics, name: ast::Name) -> Option 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, original_file: &SyntaxNode, name_ref: ast::NameRef, - ) -> Option { + ) -> Option<(PathCompletionContext, Option)> { 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 @@ fn classify_name_ref( ) }) .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 @@ fn classify_name_ref( 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(syntax: &SyntaxNode, range: TextRange) -> Option { syntax.covering_element(range).ancestors().find_map(N::cast) } diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 13c739325c8..b50f76b9116 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs @@ -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, @@ -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 @@ fn test_impl_loc() { 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); diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs index ea8222f76a7..4d040544454 100644 --- a/crates/ide_completion/src/render/builder_ext.rs +++ b/crates/ide_completion/src/render/builder_ext.rs @@ -4,7 +4,7 @@ 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 @@ fn should_add_parens(&self, ctx: &CompletionContext) -> bool { 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, .. }) diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index 22fb1f48251..6eb982a43ee 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs @@ -9,7 +9,7 @@ }; use crate::{ - context::CallKind, + context::PathKind, item::{CompletionItem, ImportEdit}, render::RenderContext, }; @@ -67,9 +67,8 @@ fn render(self, import_to_add: Option) -> Option { } 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 @@ fn render(self, import_to_add: Option) -> Option { } 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 { diff --git a/crates/ide_completion/src/tests/flyimport.rs b/crates/ide_completion/src/tests/flyimport.rs index ff46dda5e23..23e5da463c7 100644 --- a/crates/ide_completion/src/tests/flyimport.rs +++ b/crates/ide_completion/src/tests/flyimport.rs @@ -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( -- 2.44.0