mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-16 07:03:57 +00:00
fix: Do completions in path qualifier position
This commit is contained in:
parent
7c59d7c75c
commit
74abd44a26
2 changed files with 119 additions and 86 deletions
|
@ -11,7 +11,14 @@ pub(crate) fn complete_expr_path(
|
|||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
|
||||
&ExprCtx {
|
||||
expr_ctx: &ExprCtx,
|
||||
) {
|
||||
let _p = profile::span("complete_expr_path");
|
||||
if !ctx.qualifier_ctx.none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let &ExprCtx {
|
||||
in_block_expr,
|
||||
in_loop_body,
|
||||
after_if_expr,
|
||||
|
@ -23,12 +30,7 @@ pub(crate) fn complete_expr_path(
|
|||
ref impl_,
|
||||
in_match_guard,
|
||||
..
|
||||
}: &ExprCtx,
|
||||
) {
|
||||
let _p = profile::span("complete_expr_path");
|
||||
if !ctx.qualifier_ctx.none() {
|
||||
return;
|
||||
}
|
||||
} = expr_ctx;
|
||||
|
||||
let wants_mut_token =
|
||||
ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
|
||||
|
|
|
@ -592,7 +592,7 @@ impl<'a> CompletionContext<'a> {
|
|||
has_call_parens: false,
|
||||
has_macro_bang: false,
|
||||
qualified: Qualified::No,
|
||||
parent: path.parent_path(),
|
||||
parent: None,
|
||||
path: path.clone(),
|
||||
kind: PathKind::Item { kind: ItemListKind::SourceFile },
|
||||
has_type_args: false,
|
||||
|
@ -827,92 +827,123 @@ impl<'a> CompletionContext<'a> {
|
|||
PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
|
||||
};
|
||||
|
||||
let mut kind_macro_call = |it: ast::MacroCall| {
|
||||
path_ctx.has_macro_bang = it.excl_token().is_some();
|
||||
let parent = it.syntax().parent()?;
|
||||
// Any path in an item list will be treated as a macro call by the parser
|
||||
let kind = match_ast! {
|
||||
match parent {
|
||||
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
|
||||
ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
|
||||
ast::MacroType(ty) => make_path_kind_type(ty.into()),
|
||||
ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
|
||||
ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
|
||||
Some(it) => match_ast! {
|
||||
match it {
|
||||
ast::Trait(_) => ItemListKind::Trait,
|
||||
ast::Impl(it) => if it.trait_().is_some() {
|
||||
ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
|
||||
} else {
|
||||
ItemListKind::Impl
|
||||
},
|
||||
_ => return None
|
||||
}
|
||||
},
|
||||
None => return None,
|
||||
} },
|
||||
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
|
||||
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
Some(kind)
|
||||
};
|
||||
let make_path_kind_attr = |meta: ast::Meta| {
|
||||
let attr = meta.parent_attr()?;
|
||||
let kind = attr.kind();
|
||||
let attached = attr.syntax().parent()?;
|
||||
let is_trailing_outer_attr = kind != AttrKind::Inner
|
||||
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next)
|
||||
.is_none();
|
||||
let annotated_item_kind =
|
||||
if is_trailing_outer_attr { None } else { Some(attached.kind()) };
|
||||
Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
|
||||
};
|
||||
|
||||
// Infer the path kind
|
||||
let parent = path.syntax().parent()?;
|
||||
let kind = match_ast! {
|
||||
match parent {
|
||||
ast::PathType(it) => make_path_kind_type(it.into()),
|
||||
ast::PathExpr(it) => {
|
||||
if let Some(p) = it.syntax().parent() {
|
||||
if ast::ExprStmt::can_cast(p.kind()) {
|
||||
if let Some(kind) = inbetween_body_and_decl_check(p) {
|
||||
return Some(make_res(NameRefKind::Keyword(kind)));
|
||||
}
|
||||
match parent {
|
||||
ast::PathType(it) => make_path_kind_type(it.into()),
|
||||
ast::PathExpr(it) => {
|
||||
if let Some(p) = it.syntax().parent() {
|
||||
if ast::ExprStmt::can_cast(p.kind()) {
|
||||
if let Some(kind) = inbetween_body_and_decl_check(p) {
|
||||
return Some(make_res(NameRefKind::Keyword(kind)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
|
||||
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
|
||||
|
||||
make_path_kind_expr(it.into())
|
||||
},
|
||||
ast::TupleStructPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::RecordPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::PathPat(it) => {
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
// A macro call in this position is usually a result of parsing recovery, so check that
|
||||
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
|
||||
return Some(make_res(NameRefKind::Keyword(kind)));
|
||||
make_path_kind_expr(it.into())
|
||||
},
|
||||
ast::TupleStructPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
|
||||
},
|
||||
ast::RecordPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
|
||||
},
|
||||
ast::PathPat(it) => {
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
// A macro call in this position is usually a result of parsing recovery, so check that
|
||||
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
|
||||
return Some(make_res(NameRefKind::Keyword(kind)));
|
||||
}
|
||||
|
||||
kind_macro_call(it)?
|
||||
},
|
||||
ast::Meta(meta) => make_path_kind_attr(meta)?,
|
||||
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
|
||||
ast::UseTree(_) => PathKind::Use,
|
||||
// completing inside a qualifier
|
||||
ast::Path(parent) => {
|
||||
path_ctx.parent = Some(parent.clone());
|
||||
let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?;
|
||||
match_ast! {
|
||||
match parent {
|
||||
ast::PathType(it) => make_path_kind_type(it.into()),
|
||||
ast::PathExpr(it) => {
|
||||
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
|
||||
|
||||
make_path_kind_expr(it.into())
|
||||
},
|
||||
ast::TupleStructPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
|
||||
},
|
||||
ast::RecordPat(it) => {
|
||||
path_ctx.has_call_parens = true;
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
|
||||
},
|
||||
ast::PathPat(it) => {
|
||||
PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
kind_macro_call(it)?
|
||||
},
|
||||
ast::Meta(meta) => make_path_kind_attr(meta)?,
|
||||
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
|
||||
ast::UseTree(_) => PathKind::Use,
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
path_ctx.has_macro_bang = it.excl_token().is_some();
|
||||
let parent = it.syntax().parent()?;
|
||||
// Any path in an item list will be treated as a macro call by the parser
|
||||
match_ast! {
|
||||
match parent {
|
||||
ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
|
||||
ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
|
||||
ast::MacroType(ty) => make_path_kind_type(ty.into()),
|
||||
ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
|
||||
ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
|
||||
Some(it) => match_ast! {
|
||||
match it {
|
||||
ast::Trait(_) => ItemListKind::Trait,
|
||||
ast::Impl(it) => if it.trait_().is_some() {
|
||||
ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
|
||||
} else {
|
||||
ItemListKind::Impl
|
||||
},
|
||||
_ => return None
|
||||
}
|
||||
},
|
||||
None => return None,
|
||||
} },
|
||||
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
|
||||
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
ast::Meta(meta) => {
|
||||
let attr = meta.parent_attr()?;
|
||||
let kind = attr.kind();
|
||||
let attached = attr.syntax().parent()?;
|
||||
let is_trailing_outer_attr = kind != AttrKind::Inner
|
||||
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
|
||||
let annotated_item_kind = if is_trailing_outer_attr {
|
||||
None
|
||||
} else {
|
||||
Some(attached.kind())
|
||||
};
|
||||
PathKind::Attr {
|
||||
attr_ctx: AttrCtx {
|
||||
kind,
|
||||
annotated_item_kind,
|
||||
}
|
||||
}
|
||||
},
|
||||
ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
|
||||
ast::UseTree(_) => PathKind::Use,
|
||||
_ => return None,
|
||||
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue