From 2225db2eb48bd8c8fdf399c50652d3f95c851ace Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 17 Jun 2021 13:56:55 +0200 Subject: Refine `self`, `super` and `crate` completion in use paths --- crates/ide_completion/src/completions/flyimport.rs | 2 +- crates/ide_completion/src/completions/keyword.rs | 29 ++++++++++++++-------- .../src/completions/qualified_path.rs | 2 +- .../src/completions/unqualified_path.rs | 2 +- crates/ide_completion/src/context.rs | 26 +++++++++++-------- crates/ide_completion/src/patterns.rs | 7 ++++-- crates/ide_completion/src/render/builder_ext.rs | 2 +- crates/ide_completion/src/render/macro_.rs | 2 +- crates/ide_completion/src/tests/use_tree.rs | 29 +++------------------- 9 files changed, 47 insertions(+), 54 deletions(-) (limited to 'crates/ide_completion') diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 4604feb5d..814c15653 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs @@ -109,7 +109,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.use_item_syntax.is_some() + if ctx.in_use_tree() || ctx.is_path_disallowed() || ctx.expects_item() || ctx.expects_assoc_item() diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index b6f06a44f..9754122a0 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs @@ -18,17 +18,24 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC item }; - if ctx.use_item_syntax.is_some() { - let qual = ctx.path_qual(); - if qual.is_none() { - kw_completion("crate::").add_to(acc); - } - kw_completion("self").add_to(acc); - if iter::successors(qual.cloned(), |p| p.qualifier()) - .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) - { - kw_completion("super::").add_to(acc); - } + if ctx.in_use_tree() { + match &ctx.path_context { + Some(PathCompletionContext { qualifier: Some(qual), use_tree_parent, .. }) => { + if iter::successors(Some(qual.clone()), |p| p.qualifier()) + .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) + { + kw_completion("super::").add_to(acc); + } + if *use_tree_parent { + kw_completion("self").add_to(acc); + } + } + _ => { + kw_completion("crate::").add_to(acc); + kw_completion("self::").add_to(acc); + kw_completion("super::").add_to(acc); + } + }; } // Suggest .await syntax for types that implement Future trait diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index 5b49868e4..0597879ac 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -49,7 +49,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { let module_scope = module.scope(ctx.db, context_module); for (name, def) in module_scope { - if ctx.use_item_syntax.is_some() { + if ctx.in_use_tree() { if let hir::ScopeDef::Unknown = def { if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { if name_ref.syntax().text() == name.to_string().as_str() { diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 83b9148b3..4bafc1bf8 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -25,7 +25,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC return; } - if ctx.expects_use_tree() { + if ctx.expects_new_use_tree() { // only show modules in a fresh UseTree cov_mark::hit!(only_completes_modules_in_import); ctx.scope.process_all_names(&mut |name, res| { diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 121909857..c3076f608 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -43,6 +43,8 @@ pub(crate) struct PathCompletionContext { pub(super) is_trivial_path: bool, /// If not a trivial path, the prefix (qualifier). pub(super) qualifier: Option, + /// Whether the qualifier comes from a use tree parent or not + pub(super) use_tree_parent: bool, pub(super) kind: Option, /// Whether the path segment has type args or not. pub(super) has_type_args: bool, @@ -79,7 +81,6 @@ pub(crate) struct CompletionContext<'a> { /// The parent impl of the cursor position if it exists. pub(super) impl_def: Option, pub(super) name_ref_syntax: Option, - pub(super) use_item_syntax: Option, // potentially set if we are completing a lifetime pub(super) lifetime_syntax: Option, @@ -151,7 +152,6 @@ impl<'a> CompletionContext<'a> { function_def: None, impl_def: None, name_ref_syntax: None, - use_item_syntax: None, lifetime_syntax: None, lifetime_param_syntax: None, lifetime_allowed: false, @@ -264,7 +264,7 @@ impl<'a> CompletionContext<'a> { } } - pub(crate) fn expects_use_tree(&self) -> bool { + pub(crate) fn expects_new_use_tree(&self) -> bool { matches!(self.completion_location, Some(ImmediateLocation::Use)) } @@ -295,6 +295,13 @@ impl<'a> CompletionContext<'a> { matches!(self.completion_location, Some(ImmediateLocation::RecordField)) } + pub(crate) fn in_use_tree(&self) -> bool { + matches!( + self.completion_location, + Some(ImmediateLocation::Use) | Some(ImmediateLocation::UseTree) + ) + } + pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { matches!( self.prev_sibling, @@ -578,9 +585,6 @@ impl<'a> CompletionContext<'a> { self.name_ref_syntax = find_node_at_offset(original_file, name_ref.syntax().text_range().start()); - self.use_item_syntax = - self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); - self.function_def = self .sema .token_ancestors_with_macros(self.token.clone()) @@ -600,6 +604,7 @@ impl<'a> CompletionContext<'a> { has_type_args: false, can_be_stmt: false, in_loop_body: false, + use_tree_parent: false, kind: None, }); path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); @@ -627,7 +632,8 @@ impl<'a> CompletionContext<'a> { } path_ctx.has_type_args = segment.generic_arg_list().is_some(); - if let Some(path) = path_or_use_tree_qualifier(&path) { + if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) { + path_ctx.use_tree_parent = use_tree_parent; path_ctx.qualifier = path .segment() .and_then(|it| { @@ -681,13 +687,13 @@ fn is_node(node: &SyntaxNode) -> bool { } } -fn path_or_use_tree_qualifier(path: &ast::Path) -> Option { +fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> { if let Some(qual) = path.qualifier() { - return Some(qual); + return Some((qual, false)); } let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; - use_tree.path() + use_tree.path().zip(Some(true)) } #[cfg(test)] diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 62e4334de..271409c38 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs @@ -27,6 +27,7 @@ pub(crate) enum ImmediatePrevSibling { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum ImmediateLocation { Use, + UseTree, Impl, Trait, RecordField, @@ -180,6 +181,8 @@ pub(crate) fn determine_location( match parent { ast::IdentPat(_it) => ImmediateLocation::IdentPat, ast::Use(_it) => ImmediateLocation::Use, + ast::UseTree(_it) => ImmediateLocation::UseTree, + ast::UseTreeList(_it) => ImmediateLocation::UseTree, ast::BlockExpr(_it) => ImmediateLocation::BlockExpr, ast::SourceFile(_it) => ImmediateLocation::ItemList, ast::ItemList(_it) => ImmediateLocation::ItemList, @@ -373,8 +376,8 @@ mod tests { 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}", None); - check_location(r"use {f$0}", None); + check_location(r"use f::{f$0}", ImmediateLocation::UseTree); + check_location(r"use {f$0}", ImmediateLocation::UseTree); } #[test] diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs index c54752d30..749dfc665 100644 --- a/crates/ide_completion/src/render/builder_ext.rs +++ b/crates/ide_completion/src/render/builder_ext.rs @@ -28,7 +28,7 @@ impl Builder { if !ctx.config.add_call_parenthesis { return false; } - if ctx.use_item_syntax.is_some() { + if ctx.in_use_tree() { cov_mark::hit!(no_parens_in_use_item); return false; } diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index d5a1f45d3..4d5179c4f 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs @@ -69,7 +69,7 @@ impl<'a> MacroRender<'a> { } fn needs_bang(&self) -> bool { - self.ctx.completion.use_item_syntax.is_none() + !self.ctx.completion.in_use_tree() && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)) } diff --git a/crates/ide_completion/src/tests/use_tree.rs b/crates/ide_completion/src/tests/use_tree.rs index 156ca244d..878bc42bf 100644 --- a/crates/ide_completion/src/tests/use_tree.rs +++ b/crates/ide_completion/src/tests/use_tree.rs @@ -20,10 +20,9 @@ mod foo {} //- /other_crate/lib.rs crate:other_crate // nothing here "#, - // FIXME: self in this case should also get the colons expect![[r#" kw crate:: - kw self + kw self:: kw super:: md foo md other_crate @@ -34,13 +33,7 @@ mod foo {} #[test] fn dont_complete_current_use() { cov_mark::check!(dont_complete_current_use); - // FIXME: self shouldn't be here - check( - r#"use self::foo$0;"#, - expect![[r#" - kw self - "#]], - ); + check(r#"use self::foo$0;"#, expect![[r#""#]]); check( r#" mod foo { pub struct S; } @@ -56,7 +49,6 @@ use self::{foo::*, bar$0}; #[test] fn nested_use_tree() { - // FIXME: self shouldn't be here check( r#" mod foo { @@ -67,7 +59,6 @@ mod foo { use foo::{bar::$0} "#, expect![[r#" - kw self st FooBar "#]], ); @@ -89,7 +80,6 @@ use foo::{$0} #[test] fn deeply_nested_use_tree() { - // FIXME: self shouldn't be here check( r#" mod foo { @@ -102,7 +92,6 @@ mod foo { use foo::{bar::{baz::$0}} "#, expect![[r#" - kw self st FooBarBaz "#]], ); @@ -126,7 +115,6 @@ use foo::{bar::{$0}} #[test] fn plain_qualified_use_tree() { - // FIXME: self shouldn't be here check( r#" use foo::$0 @@ -138,7 +126,6 @@ mod foo { struct Bar; "#, expect![[r#" - kw self st Foo "#]], ); @@ -146,7 +133,6 @@ struct Bar; #[test] fn self_qualified_use_tree() { - // FIXME: self shouldn't be here check( r#" use self::$0 @@ -155,7 +141,6 @@ mod foo {} struct Bar; "#, expect![[r#" - kw self md foo st Bar "#]], @@ -164,7 +149,6 @@ struct Bar; #[test] fn super_qualified_use_tree() { - // FIXME: self shouldn't be here check( r#" mod bar { @@ -175,7 +159,6 @@ mod foo {} struct Bar; "#, expect![[r#" - kw self kw super:: st Bar md bar @@ -186,7 +169,6 @@ struct Bar; #[test] fn super_super_qualified_use_tree() { - // FIXME: self shouldn't be here check( r#" mod a { @@ -198,7 +180,6 @@ mod a { } "#, expect![[r#" - kw self kw super:: md b ct A @@ -208,7 +189,6 @@ mod a { #[test] fn crate_qualified_use_tree() { - // FIXME: self shouldn't be here check( r#" use crate::$0 @@ -217,7 +197,6 @@ mod foo {} struct Bar; "#, expect![[r#" - kw self md foo st Bar "#]], @@ -226,7 +205,6 @@ struct Bar; #[test] fn extern_crate_qualified_use_tree() { - // FIXME: self shouldn't be here check( r#" //- /lib.rs crate:main deps:other_crate @@ -236,7 +214,6 @@ pub struct Foo; pub mod foo {} "#, expect![[r#" - kw self st Foo md foo "#]], @@ -253,7 +230,7 @@ pub use $0; "#, expect![[r#" kw crate:: - kw self + kw self:: kw super:: md bar "#]], -- cgit v1.2.3