Refine self, super and crate completion in use paths

This commit is contained in:
Lukas Wirth 2021-06-17 13:56:55 +02:00
parent e14f5cfff0
commit 2225db2eb4
9 changed files with 47 additions and 54 deletions

View file

@ -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()

View file

@ -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

View file

@ -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() {

View file

@ -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| {

View file

@ -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<ast::Path>,
/// Whether the qualifier comes from a use tree parent or not
pub(super) use_tree_parent: bool,
pub(super) kind: Option<PathKind>,
/// 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<ast::Impl>,
pub(super) name_ref_syntax: Option<ast::NameRef>,
pub(super) use_item_syntax: Option<ast::Use>,
// potentially set if we are completing a lifetime
pub(super) lifetime_syntax: Option<ast::Lifetime>,
@ -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<N: AstNode>(node: &SyntaxNode) -> bool {
}
}
fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> {
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)]

View file

@ -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]

View file

@ -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;
}

View file

@ -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))
}

View file

@ -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
"#]],