mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-02 19:53:46 +00:00
Remove ImmediateLocation in favor of PathKind::Type
This commit is contained in:
parent
6b246292ca
commit
f201a40492
@ -3,7 +3,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
context::{
|
context::{
|
||||||
IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx,
|
IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx,
|
||||||
PathKind,
|
PathKind, TypeLocation,
|
||||||
},
|
},
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
@ -18,7 +18,7 @@ pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext
|
|||||||
is_absolute_path: false,
|
is_absolute_path: false,
|
||||||
qualifier: None,
|
qualifier: None,
|
||||||
parent: None,
|
parent: None,
|
||||||
kind: PathKind::Type { in_tuple_struct: true, ascription: None },
|
kind: PathKind::Type { location: TypeLocation::TupleField },
|
||||||
has_type_args: false,
|
has_type_args: false,
|
||||||
..
|
..
|
||||||
})),
|
})),
|
||||||
|
@ -9,9 +9,9 @@ use syntax::{AstNode, SyntaxNode, T};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{
|
context::{
|
||||||
CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PatternContext,
|
CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind,
|
||||||
|
PatternContext, TypeLocation,
|
||||||
},
|
},
|
||||||
patterns::ImmediateLocation,
|
|
||||||
render::{render_resolution_with_import, RenderContext},
|
render::{render_resolution_with_import, RenderContext},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
if !ctx.config.enable_imports_on_the_fly {
|
if !ctx.config.enable_imports_on_the_fly {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let path_kind = match dbg!(ctx.nameref_ctx()) {
|
let path_kind = match ctx.nameref_ctx() {
|
||||||
Some(NameRefContext {
|
Some(NameRefContext {
|
||||||
kind:
|
kind:
|
||||||
Some(NameRefKind::Path(PathCompletionCtx {
|
Some(NameRefKind::Path(PathCompletionCtx {
|
||||||
@ -176,8 +176,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
(PathKind::Pat, ItemInNs::Types(_)) => true,
|
(PathKind::Pat, ItemInNs::Types(_)) => true,
|
||||||
(PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
|
(PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
|
||||||
|
|
||||||
(PathKind::Type { .. }, ItemInNs::Types(ty)) => {
|
(PathKind::Type { location }, ItemInNs::Types(ty)) => {
|
||||||
if matches!(ctx.completion_location, Some(ImmediateLocation::TypeBound)) {
|
if matches!(location, TypeLocation::TypeBound) {
|
||||||
matches!(ty, ModuleDef::Trait(_))
|
matches!(ty, ModuleDef::Trait(_))
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -5,8 +5,7 @@ use ide_db::FxHashSet;
|
|||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{PathCompletionCtx, PathKind, PathQualifierCtx, TypeAscriptionTarget},
|
context::{PathCompletionCtx, PathKind, PathQualifierCtx, TypeAscriptionTarget, TypeLocation},
|
||||||
patterns::ImmediateLocation,
|
|
||||||
render::render_type_inference,
|
render::render_type_inference,
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
@ -14,13 +13,13 @@ use crate::{
|
|||||||
pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let _p = profile::span("complete_type_path");
|
let _p = profile::span("complete_type_path");
|
||||||
|
|
||||||
let (&is_absolute_path, qualifier) = match ctx.path_context() {
|
let (&is_absolute_path, location, qualifier) = match ctx.path_context() {
|
||||||
Some(PathCompletionCtx {
|
Some(PathCompletionCtx {
|
||||||
kind: PathKind::Type { .. },
|
kind: PathKind::Type { location },
|
||||||
is_absolute_path,
|
is_absolute_path,
|
||||||
qualifier,
|
qualifier,
|
||||||
..
|
..
|
||||||
}) => (is_absolute_path, qualifier),
|
}) => (is_absolute_path, location, qualifier),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
|
ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
|
||||||
// unless its a constant in a generic arg list position
|
// unless its a constant in a generic arg list position
|
||||||
ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
|
ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
|
||||||
ctx.expects_generic_arg()
|
matches!(location, TypeLocation::GenericArgList(_))
|
||||||
}
|
}
|
||||||
ScopeDef::ImplSelfType(_) => {
|
ScopeDef::ImplSelfType(_) => {
|
||||||
!ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
|
!ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
|
||||||
@ -47,6 +46,14 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let add_assoc_item = |acc: &mut Completions, item| match item {
|
||||||
|
hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => {
|
||||||
|
acc.add_const(ctx, ct)
|
||||||
|
}
|
||||||
|
hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
|
||||||
|
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
||||||
|
};
|
||||||
|
|
||||||
match qualifier {
|
match qualifier {
|
||||||
Some(PathQualifierCtx { is_infer_qualifier, resolution, .. }) => {
|
Some(PathQualifierCtx { is_infer_qualifier, resolution, .. }) => {
|
||||||
if *is_infer_qualifier {
|
if *is_infer_qualifier {
|
||||||
@ -54,7 +61,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
.0
|
.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|it| hir::Trait::from(it).items(ctx.sema.db))
|
.flat_map(|it| hir::Trait::from(it).items(ctx.sema.db))
|
||||||
.for_each(|item| add_assoc_item(acc, ctx, item));
|
.for_each(|item| add_assoc_item(acc, item));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let resolution = match resolution {
|
let resolution = match resolution {
|
||||||
@ -98,7 +105,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
Some(ctx.module),
|
Some(ctx.module),
|
||||||
None,
|
None,
|
||||||
|item| {
|
|item| {
|
||||||
add_assoc_item(acc, ctx, item);
|
add_assoc_item(acc, item);
|
||||||
None::<()>
|
None::<()>
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -114,7 +121,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
|
hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
|
||||||
// Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
|
// Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
|
||||||
for item in t.items(ctx.db) {
|
for item in t.items(ctx.db) {
|
||||||
add_assoc_item(acc, ctx, item);
|
add_assoc_item(acc, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
|
hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
|
||||||
@ -135,7 +142,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
// We might iterate candidates of a trait multiple times here, so deduplicate
|
// We might iterate candidates of a trait multiple times here, so deduplicate
|
||||||
// them.
|
// them.
|
||||||
if seen.insert(item) {
|
if seen.insert(item) {
|
||||||
add_assoc_item(acc, ctx, item);
|
add_assoc_item(acc, item);
|
||||||
}
|
}
|
||||||
None::<()>
|
None::<()>
|
||||||
},
|
},
|
||||||
@ -147,7 +154,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
None if is_absolute_path => acc.add_crate_roots(ctx),
|
None if is_absolute_path => acc.add_crate_roots(ctx),
|
||||||
None => {
|
None => {
|
||||||
acc.add_nameref_keywords_with_colon(ctx);
|
acc.add_nameref_keywords_with_colon(ctx);
|
||||||
if let Some(ImmediateLocation::TypeBound) = &ctx.completion_location {
|
if let TypeLocation::TypeBound = location {
|
||||||
ctx.process_all_names(&mut |name, res| {
|
ctx.process_all_names(&mut |name, res| {
|
||||||
let add_resolution = match res {
|
let add_resolution = match res {
|
||||||
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
|
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
|
||||||
@ -162,7 +169,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
|
if let TypeLocation::GenericArgList(Some(arg_list)) = location {
|
||||||
if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast)
|
if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast)
|
||||||
{
|
{
|
||||||
if path_seg.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() {
|
if path_seg.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() {
|
||||||
@ -189,10 +196,10 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_inferred_type(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
pub(crate) fn complete_inferred_type(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
||||||
let pat = match dbg!(ctx.path_context()) {
|
let pat = match ctx.path_context() {
|
||||||
Some(
|
Some(
|
||||||
ctx @ PathCompletionCtx {
|
ctx @ PathCompletionCtx {
|
||||||
kind: PathKind::Type { ascription: Some(ascription), .. },
|
kind: PathKind::Type { location: TypeLocation::TypeAscription(ascription), .. },
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
) if ctx.is_trivial_path() => ascription,
|
) if ctx.is_trivial_path() => ascription,
|
||||||
@ -211,11 +218,3 @@ pub(crate) fn complete_inferred_type(acc: &mut Completions, ctx: &CompletionCont
|
|||||||
acc.add(render_type_inference(ty_string, ctx));
|
acc.add(render_type_inference(ty_string, ctx));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) {
|
|
||||||
match item {
|
|
||||||
hir::AssocItem::Const(ct) if ctx.expects_generic_arg() => acc.add_const(ctx, ct),
|
|
||||||
hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
|
|
||||||
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -22,10 +22,7 @@ use syntax::{
|
|||||||
use text_edit::Indel;
|
use text_edit::Indel;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
patterns::{
|
patterns::{is_in_loop_body, is_in_token_of_for_loop, previous_token},
|
||||||
determine_location, is_in_loop_body, is_in_token_of_for_loop, previous_token,
|
|
||||||
ImmediateLocation,
|
|
||||||
},
|
|
||||||
CompletionConfig,
|
CompletionConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,10 +99,7 @@ pub(super) enum PathKind {
|
|||||||
is_func_update: Option<ast::RecordExpr>,
|
is_func_update: Option<ast::RecordExpr>,
|
||||||
},
|
},
|
||||||
Type {
|
Type {
|
||||||
in_tuple_struct: bool,
|
location: TypeLocation,
|
||||||
/// Whether this type path is a type ascription or not
|
|
||||||
/// Original file ast node
|
|
||||||
ascription: Option<TypeAscriptionTarget>,
|
|
||||||
},
|
},
|
||||||
Attr {
|
Attr {
|
||||||
kind: AttrKind,
|
kind: AttrKind,
|
||||||
@ -123,6 +117,16 @@ pub(super) enum PathKind {
|
|||||||
Use,
|
Use,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Original file ast nodes
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) enum TypeLocation {
|
||||||
|
TupleField,
|
||||||
|
TypeAscription(TypeAscriptionTarget),
|
||||||
|
GenericArgList(Option<ast::GenericArgList>),
|
||||||
|
TypeBound,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum TypeAscriptionTarget {
|
pub(crate) enum TypeAscriptionTarget {
|
||||||
Let(Option<ast::Pat>),
|
Let(Option<ast::Pat>),
|
||||||
@ -222,6 +226,7 @@ pub(super) enum NameKind {
|
|||||||
pub(super) struct NameRefContext {
|
pub(super) struct NameRefContext {
|
||||||
/// NameRef syntax in the original file
|
/// NameRef syntax in the original file
|
||||||
pub(super) nameref: Option<ast::NameRef>,
|
pub(super) nameref: Option<ast::NameRef>,
|
||||||
|
// FIXME: This shouldn't be an Option
|
||||||
pub(super) kind: Option<NameRefKind>,
|
pub(super) kind: Option<NameRefKind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,9 +316,9 @@ pub(crate) struct CompletionContext<'a> {
|
|||||||
/// The parent impl of the cursor position if it exists.
|
/// The parent impl of the cursor position if it exists.
|
||||||
pub(super) impl_def: Option<ast::Impl>,
|
pub(super) impl_def: Option<ast::Impl>,
|
||||||
/// Are we completing inside a let statement with a missing semicolon?
|
/// Are we completing inside a let statement with a missing semicolon?
|
||||||
|
// FIXME: This should be part of PathKind::Expr
|
||||||
pub(super) incomplete_let: bool,
|
pub(super) incomplete_let: bool,
|
||||||
|
|
||||||
pub(super) completion_location: Option<ImmediateLocation>,
|
|
||||||
pub(super) previous_token: Option<SyntaxToken>,
|
pub(super) previous_token: Option<SyntaxToken>,
|
||||||
|
|
||||||
pub(super) ident_ctx: IdentContext,
|
pub(super) ident_ctx: IdentContext,
|
||||||
@ -386,11 +391,6 @@ impl<'a> CompletionContext<'a> {
|
|||||||
self.dot_receiver().is_some()
|
self.dot_receiver().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This shouldn't exist
|
|
||||||
pub(crate) fn expects_generic_arg(&self) -> bool {
|
|
||||||
matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
|
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
|
||||||
self.nameref_ctx().and_then(|ctx| match &ctx.kind {
|
self.nameref_ctx().and_then(|ctx| match &ctx.kind {
|
||||||
Some(NameRefKind::Path(path)) => Some(path),
|
Some(NameRefKind::Path(path)) => Some(path),
|
||||||
@ -542,7 +542,6 @@ impl<'a> CompletionContext<'a> {
|
|||||||
function_def: None,
|
function_def: None,
|
||||||
impl_def: None,
|
impl_def: None,
|
||||||
incomplete_let: false,
|
incomplete_let: false,
|
||||||
completion_location: None,
|
|
||||||
previous_token: None,
|
previous_token: None,
|
||||||
// dummy value, will be overwritten
|
// dummy value, will be overwritten
|
||||||
ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
|
ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
|
||||||
@ -935,8 +934,6 @@ impl<'a> CompletionContext<'a> {
|
|||||||
return Some(());
|
return Some(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.completion_location =
|
|
||||||
determine_location(&self.sema, original_file, offset, &name_like);
|
|
||||||
self.impl_def = self
|
self.impl_def = self
|
||||||
.sema
|
.sema
|
||||||
.token_ancestors_with_macros(self.token.clone())
|
.token_ancestors_with_macros(self.token.clone())
|
||||||
@ -1094,7 +1091,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
match parent {
|
match parent {
|
||||||
ast::PathSegment(segment) => segment,
|
ast::PathSegment(segment) => segment,
|
||||||
ast::FieldExpr(field) => {
|
ast::FieldExpr(field) => {
|
||||||
let receiver = find_in_original_file(field.expr(), original_file);
|
let receiver = find_opt_node_in_file(original_file, field.expr());
|
||||||
let receiver_is_ambiguous_float_literal = match &receiver {
|
let receiver_is_ambiguous_float_literal = match &receiver {
|
||||||
Some(ast::Expr::Literal(l)) => matches! {
|
Some(ast::Expr::Literal(l)) => matches! {
|
||||||
l.kind(),
|
l.kind(),
|
||||||
@ -1110,7 +1107,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
ast::MethodCallExpr(method) => {
|
ast::MethodCallExpr(method) => {
|
||||||
let receiver = find_in_original_file(method.receiver(), original_file);
|
let receiver = find_opt_node_in_file(original_file, method.receiver());
|
||||||
nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess {
|
nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess {
|
||||||
receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
|
receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
|
||||||
kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
|
kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
|
||||||
@ -1189,14 +1186,14 @@ impl<'a> CompletionContext<'a> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let fetch_ascription = |it: Option<SyntaxNode>| {
|
let type_location = |it: Option<SyntaxNode>| {
|
||||||
let parent = it?;
|
let parent = it?;
|
||||||
match_ast! {
|
let res = match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::Const(it) => {
|
ast::Const(it) => {
|
||||||
let name = find_in_original_file(it.name(), original_file)?;
|
let name = find_opt_node_in_file(original_file, it.name())?;
|
||||||
let original = ast::Const::cast(name.syntax().parent()?)?;
|
let original = ast::Const::cast(name.syntax().parent()?)?;
|
||||||
Some(TypeAscriptionTarget::Const(original.body()))
|
TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
|
||||||
},
|
},
|
||||||
ast::RetType(it) => {
|
ast::RetType(it) => {
|
||||||
if it.thin_arrow_token().is_none() {
|
if it.thin_arrow_token().is_none() {
|
||||||
@ -1207,8 +1204,8 @@ impl<'a> CompletionContext<'a> {
|
|||||||
None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(),
|
None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let parent = find_in_original_file(parent, original_file)?.syntax().parent()?;
|
let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?;
|
||||||
Some(TypeAscriptionTarget::RetType(match_ast! {
|
TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::ClosureExpr(it) => {
|
ast::ClosureExpr(it) => {
|
||||||
it.body()
|
it.body()
|
||||||
@ -1224,17 +1221,25 @@ impl<'a> CompletionContext<'a> {
|
|||||||
if it.colon_token().is_none() {
|
if it.colon_token().is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(TypeAscriptionTarget::FnParam(find_in_original_file(it.pat(), original_file)))
|
TypeLocation::TypeAscription(TypeAscriptionTarget::FnParam(find_opt_node_in_file(original_file, it.pat())))
|
||||||
},
|
},
|
||||||
ast::LetStmt(it) => {
|
ast::LetStmt(it) => {
|
||||||
if it.colon_token().is_none() {
|
if it.colon_token().is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(TypeAscriptionTarget::Let(find_in_original_file(it.pat(), original_file)))
|
TypeLocation::TypeAscription(TypeAscriptionTarget::Let(find_opt_node_in_file(original_file, it.pat())))
|
||||||
},
|
},
|
||||||
_ => None,
|
ast::TypeBound(_) => TypeLocation::TypeBound,
|
||||||
|
// is this case needed?
|
||||||
|
ast::TypeBoundList(_) => TypeLocation::TypeBound,
|
||||||
|
ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
|
||||||
|
// is this case needed?
|
||||||
|
ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(original_file, Some(it))),
|
||||||
|
ast::TupleField(_) => TypeLocation::TupleField,
|
||||||
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
Some(res)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Infer the path kind
|
// Infer the path kind
|
||||||
@ -1242,10 +1247,9 @@ impl<'a> CompletionContext<'a> {
|
|||||||
match_ast! {
|
match_ast! {
|
||||||
match it {
|
match it {
|
||||||
ast::PathType(it) => {
|
ast::PathType(it) => {
|
||||||
let ascription = fetch_ascription(it.syntax().parent());
|
let location = type_location(it.syntax().parent());
|
||||||
Some(PathKind::Type {
|
Some(PathKind::Type {
|
||||||
in_tuple_struct: it.syntax().parent().map_or(false, |it| ast::TupleField::can_cast(it.kind())),
|
location: location.unwrap_or(TypeLocation::Other),
|
||||||
ascription,
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
ast::PathExpr(it) => {
|
ast::PathExpr(it) => {
|
||||||
@ -1292,7 +1296,12 @@ impl<'a> CompletionContext<'a> {
|
|||||||
let parent = it.syntax().parent();
|
let parent = it.syntax().parent();
|
||||||
match parent.as_ref().map(|it| it.kind()) {
|
match parent.as_ref().map(|it| it.kind()) {
|
||||||
Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
|
Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
|
||||||
Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type { in_tuple_struct: false, ascription: None }),
|
Some(SyntaxKind::MACRO_TYPE) => {
|
||||||
|
let location = type_location(parent.unwrap().parent());
|
||||||
|
Some(PathKind::Type {
|
||||||
|
location: location.unwrap_or(TypeLocation::Other),
|
||||||
|
})
|
||||||
|
},
|
||||||
Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
|
Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
|
||||||
Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
|
Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
|
||||||
Some(it) => match_ast! {
|
Some(it) => match_ast! {
|
||||||
@ -1501,15 +1510,14 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_in_original_file<N: AstNode>(x: Option<N>, original_file: &SyntaxNode) -> Option<N> {
|
/// Attempts to find `node` inside `syntax` via `node`'s text range.
|
||||||
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
|
/// If the fake identifier has been inserted after this node or inside of this node use the `_compensated` version instead.
|
||||||
let range = syntax.text_range().intersect(range)?;
|
fn find_opt_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: Option<N>) -> Option<N> {
|
||||||
syntax.covering_element(range).ancestors().find_map(N::cast)
|
find_node_in_file(syntax, &node?)
|
||||||
}
|
|
||||||
x.map(|e| e.syntax().text_range()).and_then(|r| find_node_with_range(original_file, r))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to find `node` inside `syntax` via `node`'s text range.
|
/// Attempts to find `node` inside `syntax` via `node`'s text range.
|
||||||
|
/// If the fake identifier has been inserted after this node or inside of this node use the `_compensated` version instead.
|
||||||
fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
|
fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
|
||||||
let syntax_range = syntax.text_range();
|
let syntax_range = syntax.text_range();
|
||||||
let range = node.syntax().text_range();
|
let range = node.syntax().text_range();
|
||||||
@ -1528,11 +1536,21 @@ fn find_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> O
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let range = TextRange::new(range.start(), end);
|
let range = TextRange::new(range.start(), end);
|
||||||
// our inserted ident could cause `range` to be go outside of the original syntax, so cap it
|
// our inserted ident could cause `range` to go outside of the original syntax, so cap it
|
||||||
let intersection = range.intersect(syntax_range)?;
|
let intersection = range.intersect(syntax_range)?;
|
||||||
syntax.covering_element(intersection).ancestors().find_map(N::cast)
|
syntax.covering_element(intersection).ancestors().find_map(N::cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
|
||||||
|
/// for the offset introduced by the fake ident..
|
||||||
|
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
|
||||||
|
fn find_opt_node_in_file_compensated<N: AstNode>(
|
||||||
|
syntax: &SyntaxNode,
|
||||||
|
node: Option<N>,
|
||||||
|
) -> Option<N> {
|
||||||
|
find_node_in_file_compensated(syntax, &node?)
|
||||||
|
}
|
||||||
|
|
||||||
fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
|
fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
|
||||||
if let Some(qual) = path.qualifier() {
|
if let Some(qual) = path.qualifier() {
|
||||||
return Some((qual, false));
|
return Some((qual, false));
|
||||||
|
@ -4,103 +4,16 @@
|
|||||||
//! This means we for example expand a NameRef token to its outermost Path node, as semantically these act in the same location
|
//! This means we for example expand a NameRef token to its outermost Path node, as semantically these act in the same location
|
||||||
//! and the completions usually query for path specific things on the Path context instead. This simplifies some location handling.
|
//! and the completions usually query for path specific things on the Path context instead. This simplifies some location handling.
|
||||||
|
|
||||||
use hir::Semantics;
|
|
||||||
use ide_db::RootDatabase;
|
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasLoopBody},
|
ast::{self, HasLoopBody},
|
||||||
match_ast, AstNode, SyntaxElement,
|
match_ast, AstNode, SyntaxElement,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxNode, SyntaxToken, TextSize,
|
SyntaxNode, SyntaxToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::tests::check_pattern_is_applicable;
|
use crate::tests::check_pattern_is_applicable;
|
||||||
|
|
||||||
/// Direct parent "thing" of what we are currently completing.
|
|
||||||
///
|
|
||||||
/// This may contain nodes of the fake file as well as the original, comments on the variants specify
|
|
||||||
/// from which file the nodes are.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub(crate) enum ImmediateLocation {
|
|
||||||
TypeBound,
|
|
||||||
// Only set from a type arg
|
|
||||||
/// Original file ast node
|
|
||||||
GenericArgList(ast::GenericArgList),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn determine_location(
|
|
||||||
sema: &Semantics<RootDatabase>,
|
|
||||||
original_file: &SyntaxNode,
|
|
||||||
offset: TextSize,
|
|
||||||
name_like: &ast::NameLike,
|
|
||||||
) -> Option<ImmediateLocation> {
|
|
||||||
let node = match name_like {
|
|
||||||
ast::NameLike::NameRef(name_ref) => maximize_name_ref(name_ref),
|
|
||||||
ast::NameLike::Name(name) => name.syntax().clone(),
|
|
||||||
ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match_ast! {
|
|
||||||
match node {
|
|
||||||
ast::TypeBoundList(_it) => return Some(ImmediateLocation::TypeBound),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let parent = match node.parent() {
|
|
||||||
Some(parent) => match ast::MacroCall::cast(parent.clone()) {
|
|
||||||
// When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
|
|
||||||
// This is usually fine as the node expansion code above already accounts for that with
|
|
||||||
// the ancestors call, but there is one exception to this which is that when an attribute
|
|
||||||
// precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
|
|
||||||
// FIXME path expr and statement have a similar problem
|
|
||||||
Some(call)
|
|
||||||
if call.excl_token().is_none()
|
|
||||||
&& call.token_tree().is_none()
|
|
||||||
&& call.semicolon_token().is_none() =>
|
|
||||||
{
|
|
||||||
call.syntax().parent()?
|
|
||||||
}
|
|
||||||
_ => parent,
|
|
||||||
},
|
|
||||||
// SourceFile
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = match_ast! {
|
|
||||||
match parent {
|
|
||||||
ast::TypeBound(_) => ImmediateLocation::TypeBound,
|
|
||||||
ast::TypeBoundList(_) => ImmediateLocation::TypeBound,
|
|
||||||
ast::GenericArgList(_) => sema
|
|
||||||
.find_node_at_offset_with_macros(original_file, offset)
|
|
||||||
.map(ImmediateLocation::GenericArgList)?,
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maximize a nameref to its enclosing path if its the last segment of said path.
|
|
||||||
/// That is, when completing a [`NameRef`] we actually handle it as the path it is part of when determining
|
|
||||||
/// its location.
|
|
||||||
fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
|
|
||||||
if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
|
|
||||||
let p = segment.parent_path();
|
|
||||||
if p.parent_path().is_none() {
|
|
||||||
// Get rid of PathExpr, PathType, etc...
|
|
||||||
let path = p
|
|
||||||
.syntax()
|
|
||||||
.ancestors()
|
|
||||||
.take_while(|it| it.text_range() == p.syntax().text_range())
|
|
||||||
.last();
|
|
||||||
if let Some(it) = path {
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
name_ref.syntax().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
|
pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
|
||||||
element.into_token().and_then(previous_non_trivia_token)
|
element.into_token().and_then(previous_non_trivia_token)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user