mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-15 22:54:00 +00:00
Auto merge of #12021 - Veykril:completion-ctx, r=Veykril
internal: Add a `NameContext` to `CompletionContext`, move out some ImmediateLocation variants Continues the completion rewrite I started some time ago (will merge tomorrow after stable since our completion tests still let a lot through)
This commit is contained in:
commit
e0d41bc2a1
10 changed files with 96 additions and 69 deletions
|
@ -103,10 +103,14 @@ impl Completions {
|
|||
item.add_to(self);
|
||||
}
|
||||
|
||||
pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext) {
|
||||
pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext) {
|
||||
["self::", "super::", "crate::"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
|
||||
}
|
||||
|
||||
pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext) {
|
||||
["self", "super", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
|
||||
}
|
||||
|
||||
pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
|
||||
ctx.process_all_names(&mut |name, res| match res {
|
||||
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
|
||||
|
|
|
@ -107,7 +107,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
|
|||
acc.add_resolution(ctx, name, def);
|
||||
}
|
||||
});
|
||||
acc.add_nameref_keywords(ctx);
|
||||
acc.add_nameref_keywords_with_colon(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) {
|
|||
None => acc.add_resolution(ctx, name, def),
|
||||
}
|
||||
});
|
||||
acc.add_nameref_keywords(ctx);
|
||||
acc.add_nameref_keywords_with_colon(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,16 @@ use ide_db::{
|
|||
};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{patterns::ImmediateLocation, CompletionItem};
|
||||
use crate::{context::NameContext, CompletionItem};
|
||||
|
||||
use crate::{context::CompletionContext, Completions};
|
||||
|
||||
/// Complete mod declaration, i.e. `mod $0;`
|
||||
pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
||||
let mod_under_caret = match &ctx.completion_location {
|
||||
Some(ImmediateLocation::ModDeclaration(mod_under_caret)) => mod_under_caret,
|
||||
let mod_under_caret = match &ctx.name_ctx {
|
||||
Some(NameContext::Module(mod_under_caret)) if mod_under_caret.item_list().is_none() => {
|
||||
mod_under_caret
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
|
|
@ -202,7 +202,7 @@ fn pattern_path_completion(
|
|||
acc.add_resolution(ctx, name, res);
|
||||
}
|
||||
});
|
||||
acc.add_nameref_keywords(ctx);
|
||||
acc.add_nameref_keywords_with_colon(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,21 +17,15 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
|||
}
|
||||
match ctx.path_context {
|
||||
Some(PathCompletionCtx {
|
||||
kind:
|
||||
Some(
|
||||
PathKind::Attr { .. }
|
||||
| PathKind::Derive
|
||||
| PathKind::Pat
|
||||
| PathKind::Use { .. }
|
||||
| PathKind::Vis { .. },
|
||||
),
|
||||
is_absolute_path: false,
|
||||
qualifier: None,
|
||||
kind: None | Some(PathKind::Expr | PathKind::Type | PathKind::Mac),
|
||||
..
|
||||
}) => return,
|
||||
Some(PathCompletionCtx { is_absolute_path: false, qualifier: None, .. }) => (),
|
||||
}) => (),
|
||||
_ => return,
|
||||
}
|
||||
|
||||
["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
|
||||
acc.add_nameref_keywords(ctx);
|
||||
|
||||
match &ctx.completion_location {
|
||||
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
|
||||
|
|
|
@ -113,7 +113,7 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext)
|
|||
acc.add_resolution(ctx, name, res);
|
||||
}
|
||||
});
|
||||
acc.add_nameref_keywords(ctx);
|
||||
acc.add_nameref_keywords_with_colon(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ pub(crate) fn complete_vis(acc: &mut Completions, ctx: &CompletionContext) {
|
|||
cov_mark::hit!(kw_completion_in);
|
||||
acc.add_keyword(ctx, "in");
|
||||
}
|
||||
["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
|
||||
acc.add_nameref_keywords(ctx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -100,6 +100,30 @@ pub(super) enum LifetimeContext {
|
|||
LabelDef,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(super) enum NameContext {
|
||||
Const,
|
||||
ConstParam,
|
||||
Enum,
|
||||
Function,
|
||||
IdentPat,
|
||||
MacroDef,
|
||||
MacroRules,
|
||||
/// Fake node
|
||||
Module(ast::Module),
|
||||
RecordField,
|
||||
Rename,
|
||||
SelfParam,
|
||||
Static,
|
||||
Struct,
|
||||
Trait,
|
||||
TypeAlias,
|
||||
TypeParam,
|
||||
Union,
|
||||
Variant,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ParamKind {
|
||||
Function(ast::Fn),
|
||||
|
@ -140,6 +164,7 @@ pub(crate) struct CompletionContext<'a> {
|
|||
pub(super) fake_attribute_under_caret: Option<ast::Attr>,
|
||||
pub(super) previous_token: Option<SyntaxToken>,
|
||||
|
||||
pub(super) name_ctx: Option<NameContext>,
|
||||
pub(super) lifetime_ctx: Option<LifetimeContext>,
|
||||
pub(super) pattern_ctx: Option<PatternContext>,
|
||||
pub(super) path_context: Option<PathCompletionCtx>,
|
||||
|
@ -197,7 +222,7 @@ impl<'a> CompletionContext<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn expects_variant(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::Variant))
|
||||
matches!(self.name_ctx, Some(NameContext::Variant))
|
||||
}
|
||||
|
||||
pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
|
||||
|
@ -221,10 +246,8 @@ impl<'a> CompletionContext<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn expect_field(&self) -> bool {
|
||||
matches!(
|
||||
self.completion_location,
|
||||
Some(ImmediateLocation::RecordField | ImmediateLocation::TupleField)
|
||||
)
|
||||
matches!(self.completion_location, Some(ImmediateLocation::TupleField))
|
||||
|| matches!(self.name_ctx, Some(NameContext::RecordField))
|
||||
}
|
||||
|
||||
pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
|
||||
|
@ -254,13 +277,9 @@ impl<'a> CompletionContext<'a> {
|
|||
)
|
||||
|| matches!(
|
||||
self.completion_location,
|
||||
Some(
|
||||
ImmediateLocation::ModDeclaration(_)
|
||||
| ImmediateLocation::RecordPat(_)
|
||||
| ImmediateLocation::RecordExpr(_)
|
||||
| ImmediateLocation::Rename
|
||||
)
|
||||
Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_))
|
||||
)
|
||||
|| matches!(self.name_ctx, Some(NameContext::Module(_) | NameContext::Rename))
|
||||
}
|
||||
|
||||
pub(crate) fn expects_expression(&self) -> bool {
|
||||
|
@ -429,6 +448,7 @@ impl<'a> CompletionContext<'a> {
|
|||
name_syntax: None,
|
||||
lifetime_ctx: None,
|
||||
pattern_ctx: None,
|
||||
name_ctx: None,
|
||||
completion_location: None,
|
||||
prev_sibling: None,
|
||||
fake_attribute_under_caret: None,
|
||||
|
@ -800,7 +820,12 @@ impl<'a> CompletionContext<'a> {
|
|||
}
|
||||
}
|
||||
ast::NameLike::Name(name) => {
|
||||
self.pattern_ctx = Self::classify_name(&self.sema, original_file, name);
|
||||
if let Some((name_ctx, pat_ctx)) =
|
||||
Self::classify_name(&self.sema, original_file, name)
|
||||
{
|
||||
self.pattern_ctx = pat_ctx;
|
||||
self.name_ctx = Some(name_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -833,17 +858,44 @@ impl<'a> CompletionContext<'a> {
|
|||
_sema: &Semantics<RootDatabase>,
|
||||
original_file: &SyntaxNode,
|
||||
name: ast::Name,
|
||||
) -> Option<PatternContext> {
|
||||
let bind_pat = name.syntax().parent().and_then(ast::IdentPat::cast)?;
|
||||
let is_name_in_field_pat = bind_pat
|
||||
.syntax()
|
||||
.parent()
|
||||
.and_then(ast::RecordPatField::cast)
|
||||
.map_or(false, |pat_field| pat_field.name_ref().is_none());
|
||||
if is_name_in_field_pat {
|
||||
return None;
|
||||
}
|
||||
Some(pattern_context_for(original_file, bind_pat.into()))
|
||||
) -> Option<(NameContext, Option<PatternContext>)> {
|
||||
let parent = name.syntax().parent()?;
|
||||
let mut pat_ctx = None;
|
||||
let name_ctx = match_ast! {
|
||||
match parent {
|
||||
ast::Const(_) => NameContext::Const,
|
||||
ast::ConstParam(_) => NameContext::ConstParam,
|
||||
ast::Enum(_) => NameContext::Enum,
|
||||
ast::Fn(_) => NameContext::Function,
|
||||
ast::IdentPat(bind_pat) => {
|
||||
let is_name_in_field_pat = bind_pat
|
||||
.syntax()
|
||||
.parent()
|
||||
.and_then(ast::RecordPatField::cast)
|
||||
.map_or(false, |pat_field| pat_field.name_ref().is_none());
|
||||
if !is_name_in_field_pat {
|
||||
pat_ctx = Some(pattern_context_for(original_file, bind_pat.into()));
|
||||
}
|
||||
|
||||
NameContext::IdentPat
|
||||
},
|
||||
ast::MacroDef(_) => NameContext::MacroDef,
|
||||
ast::MacroRules(_) => NameContext::MacroRules,
|
||||
ast::Module(module) => NameContext::Module(module),
|
||||
ast::RecordField(_) => NameContext::RecordField,
|
||||
ast::Rename(_) => NameContext::Rename,
|
||||
ast::SelfParam(_) => NameContext::SelfParam,
|
||||
ast::Static(_) => NameContext::Static,
|
||||
ast::Struct(_) => NameContext::Struct,
|
||||
ast::Trait(_) => NameContext::Trait,
|
||||
ast::TypeAlias(_) => NameContext::TypeAlias,
|
||||
ast::TypeParam(_) => NameContext::TypeParam,
|
||||
ast::Union(_) => NameContext::Union,
|
||||
ast::Variant(_) => NameContext::Variant,
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
Some((name_ctx, pat_ctx))
|
||||
}
|
||||
|
||||
fn classify_name_ref(
|
||||
|
|
|
@ -41,21 +41,16 @@ pub(crate) enum TypeAnnotation {
|
|||
/// from which file the nodes are.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ImmediateLocation {
|
||||
Rename,
|
||||
Impl,
|
||||
Trait,
|
||||
RecordField,
|
||||
TupleField,
|
||||
RefExpr,
|
||||
IdentPat,
|
||||
StmtList,
|
||||
ItemList,
|
||||
TypeBound,
|
||||
Variant,
|
||||
/// Original file ast node
|
||||
TypeAnnotation(TypeAnnotation),
|
||||
/// Fake file ast node
|
||||
ModDeclaration(ast::Module),
|
||||
/// Original file ast node
|
||||
MethodCall {
|
||||
receiver: Option<ast::Expr>,
|
||||
|
@ -80,6 +75,7 @@ pub(crate) enum ImmediateLocation {
|
|||
/// The record pat of the field name we are completing
|
||||
///
|
||||
/// Original file ast node
|
||||
// FIXME: This should be moved to pattern_ctx
|
||||
RecordPat(ast::RecordPat),
|
||||
}
|
||||
|
||||
|
@ -211,17 +207,10 @@ pub(crate) fn determine_location(
|
|||
let res = match_ast! {
|
||||
match parent {
|
||||
ast::IdentPat(_) => ImmediateLocation::IdentPat,
|
||||
ast::Rename(_) => ImmediateLocation::Rename,
|
||||
ast::StmtList(_) => ImmediateLocation::StmtList,
|
||||
ast::SourceFile(_) => ImmediateLocation::ItemList,
|
||||
ast::ItemList(_) => ImmediateLocation::ItemList,
|
||||
ast::RefExpr(_) => ImmediateLocation::RefExpr,
|
||||
ast::Variant(_) => ImmediateLocation::Variant,
|
||||
ast::RecordField(it) => if it.ty().map_or(false, |it| it.syntax().text_range().contains(offset)) {
|
||||
return None;
|
||||
} else {
|
||||
ImmediateLocation::RecordField
|
||||
},
|
||||
ast::RecordExprFieldList(_) => sema
|
||||
.find_node_at_offset_with_macros(original_file, offset)
|
||||
.map(ImmediateLocation::RecordExprUpdate)?,
|
||||
|
@ -237,13 +226,6 @@ pub(crate) fn determine_location(
|
|||
ast::GenericArgList(_) => sema
|
||||
.find_node_at_offset_with_macros(original_file, offset)
|
||||
.map(ImmediateLocation::GenericArgList)?,
|
||||
ast::Module(it) => {
|
||||
if it.item_list().is_none() {
|
||||
ImmediateLocation::ModDeclaration(it)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
ast::FieldExpr(it) => {
|
||||
let receiver = find_in_original_file(it.expr(), original_file);
|
||||
let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver {
|
||||
|
@ -475,13 +457,6 @@ mod tests {
|
|||
check_location(r"impl A { fn f$0 }", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_record_field_loc() {
|
||||
check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
|
||||
check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField);
|
||||
check_location(r"struct Foo { pub f: i32, f$0 }", ImmediateLocation::RecordField);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_expr_loc() {
|
||||
check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::StmtList);
|
||||
|
|
Loading…
Reference in a new issue