mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 05:15:04 +00:00
internal: completion: split out more PathKinds from ImmediateLocation
This commit is contained in:
parent
49b0970970
commit
ae0c7268f7
10 changed files with 206 additions and 160 deletions
|
@ -110,7 +110,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;
|
||||||
}
|
}
|
||||||
if ctx.in_use_tree()
|
if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use))
|
||||||
|| ctx.is_path_disallowed()
|
|| ctx.is_path_disallowed()
|
||||||
|| ctx.expects_item()
|
|| ctx.expects_item()
|
||||||
|| ctx.expects_assoc_item()
|
|| ctx.expects_assoc_item()
|
||||||
|
@ -118,6 +118,11 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
// FIXME: This should be encoded in a different way
|
||||||
|
if ctx.pattern_ctx.is_none() && ctx.path_context.is_none() && !ctx.has_dot_receiver() {
|
||||||
|
// completion inside `ast::Name` of a item declaration
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let potential_import_name = {
|
let potential_import_name = {
|
||||||
let token_kind = ctx.token.kind();
|
let token_kind = ctx.token.kind();
|
||||||
if matches!(token_kind, T![.] | T![::]) {
|
if matches!(token_kind, T![.] | T![::]) {
|
||||||
|
@ -147,14 +152,25 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match (kind, import.original_item) {
|
match (kind, import.original_item) {
|
||||||
|
// Aren't handled in flyimport
|
||||||
|
(PathKind::Vis { .. } | PathKind::Use, _) => false,
|
||||||
|
// modules are always fair game
|
||||||
|
(_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
|
||||||
|
// and so are macros(except for attributes)
|
||||||
|
(
|
||||||
|
PathKind::Expr | PathKind::Type | PathKind::Mac | PathKind::Pat,
|
||||||
|
ItemInNs::Macros(mac),
|
||||||
|
) => mac.is_fn_like(),
|
||||||
|
(PathKind::Mac, _) => true,
|
||||||
|
|
||||||
(PathKind::Expr, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
|
(PathKind::Expr, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
|
||||||
|
|
||||||
|
(PathKind::Pat, ItemInNs::Types(_)) => true,
|
||||||
|
(PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
|
||||||
|
|
||||||
(PathKind::Type, ItemInNs::Types(_)) => true,
|
(PathKind::Type, ItemInNs::Types(_)) => true,
|
||||||
(PathKind::Type, ItemInNs::Values(_)) => false,
|
(PathKind::Type, ItemInNs::Values(_)) => false,
|
||||||
|
|
||||||
(PathKind::Expr | PathKind::Type, ItemInNs::Macros(mac)) => mac.is_fn_like(),
|
|
||||||
|
|
||||||
(PathKind::Attr, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
|
|
||||||
(PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
|
(PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
|
||||||
(PathKind::Attr, _) => false,
|
(PathKind::Attr, _) => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
use syntax::{SyntaxKind, T};
|
use syntax::{SyntaxKind, T};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
|
context::{PathCompletionContext, PathKind},
|
||||||
CompletionItemKind, Completions,
|
patterns::ImmediateLocation,
|
||||||
|
CompletionContext, CompletionItem, CompletionItemKind, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
|
@ -33,8 +34,8 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
||||||
let has_block_expr_parent = ctx.has_block_expr_parent();
|
let has_block_expr_parent = ctx.has_block_expr_parent();
|
||||||
let expects_item = ctx.expects_item();
|
let expects_item = ctx.expects_item();
|
||||||
|
|
||||||
if let Some(ImmediateLocation::Visibility(vis)) = &ctx.completion_location {
|
if let Some(PathKind::Vis { has_in_token }) = ctx.path_kind() {
|
||||||
if vis.in_token().is_none() {
|
if !has_in_token {
|
||||||
cov_mark::hit!(kw_completion_in);
|
cov_mark::hit!(kw_completion_in);
|
||||||
add_keyword("in", "in");
|
add_keyword("in", "in");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
//! Completes constants and paths in patterns.
|
//! Completes constants and paths in unqualified patterns.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{PatternContext, PatternRefutability},
|
context::{PatternContext, PatternRefutability},
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Completes constants and paths in patterns.
|
/// Completes constants and paths in unqualified patterns.
|
||||||
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let refutable = match ctx.pattern_ctx {
|
let refutable = match ctx.pattern_ctx {
|
||||||
Some(PatternContext { refutability, .. }) => refutability == PatternRefutability::Refutable,
|
Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => {
|
||||||
None => return,
|
refutability == PatternRefutability::Refutable
|
||||||
|
}
|
||||||
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
if refutable {
|
if refutable {
|
||||||
|
|
|
@ -7,17 +7,23 @@ use rustc_hash::FxHashSet;
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, Completions,
|
context::{PathCompletionContext, PathKind},
|
||||||
|
patterns::ImmediateLocation,
|
||||||
|
CompletionContext, Completions,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
|
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (path, use_tree_parent) = match &ctx.path_context {
|
let (path, use_tree_parent, kind) = match ctx.path_context {
|
||||||
Some(PathCompletionContext { qualifier: Some(qualifier), use_tree_parent, .. }) => {
|
// let ... else, syntax would come in really handy here right now
|
||||||
(qualifier, *use_tree_parent)
|
Some(PathCompletionContext {
|
||||||
}
|
qualifier: Some(ref qualifier),
|
||||||
|
use_tree_parent,
|
||||||
|
kind,
|
||||||
|
..
|
||||||
|
}) => (qualifier, use_tree_parent, kind),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,7 +50,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(ImmediateLocation::Visibility(_)) => {
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
Some(PathKind::Vis { .. }) => {
|
||||||
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
|
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
|
||||||
if let Some(current_module) = ctx.scope.module() {
|
if let Some(current_module) = ctx.scope.module() {
|
||||||
if let Some(next) = current_module
|
if let Some(next) = current_module
|
||||||
|
@ -61,7 +71,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(ImmediateLocation::Attribute(_)) => {
|
Some(PathKind::Attr) => {
|
||||||
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
|
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
|
||||||
for (name, def) in module.scope(ctx.db, context_module) {
|
for (name, def) in module.scope(ctx.db, context_module) {
|
||||||
let add_resolution = match def {
|
let add_resolution = match def {
|
||||||
|
@ -76,10 +86,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => (),
|
Some(PathKind::Use) => {
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.in_use_tree() {
|
|
||||||
if iter::successors(Some(path.clone()), |p| p.qualifier())
|
if iter::successors(Some(path.clone()), |p| p.qualifier())
|
||||||
.all(|p| p.segment().and_then(|s| s.super_token()).is_some())
|
.all(|p| p.segment().and_then(|s| s.super_token()).is_some())
|
||||||
{
|
{
|
||||||
|
@ -95,18 +102,22 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||||
acc.add_keyword(ctx, "self");
|
acc.add_keyword(ctx, "self");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matches!(kind, Some(PathKind::Pat)) {
|
||||||
// Add associated types on type parameters and `Self`.
|
// Add associated types on type parameters and `Self`.
|
||||||
resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
|
resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
|
||||||
acc.add_type_alias(ctx, alias);
|
acc.add_type_alias(ctx, alias);
|
||||||
None::<()>
|
None::<()>
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
match resolution {
|
match resolution {
|
||||||
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
|
hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
|
||||||
let module_scope = module.scope(ctx.db, context_module);
|
let module_scope = module.scope(ctx.db, context_module);
|
||||||
for (name, def) in module_scope {
|
for (name, def) in module_scope {
|
||||||
if ctx.in_use_tree() {
|
if let Some(PathKind::Use) = kind {
|
||||||
if let ScopeDef::Unknown = def {
|
if let ScopeDef::Unknown = def {
|
||||||
if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
|
if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
|
||||||
if name_ref.syntax().text() == name.to_smol_str().as_str() {
|
if name_ref.syntax().text() == name.to_smol_str().as_str() {
|
||||||
|
|
|
@ -3,15 +3,23 @@
|
||||||
use hir::ScopeDef;
|
use hir::ScopeDef;
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
use crate::{patterns::ImmediateLocation, CompletionContext, Completions};
|
use crate::{
|
||||||
|
context::{PathCompletionContext, PathKind},
|
||||||
|
patterns::ImmediateLocation,
|
||||||
|
CompletionContext, Completions,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let _p = profile::span("complete_unqualified_path");
|
let _p = profile::span("complete_unqualified_path");
|
||||||
if ctx.is_path_disallowed() || !ctx.is_trivial_path() || ctx.has_impl_or_trait_prev_sibling() {
|
if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let kind = match ctx.path_context {
|
||||||
|
Some(PathCompletionContext { is_trivial_path: true, kind, .. }) => kind,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
if ctx.in_use_tree() {
|
if let Some(PathKind::Use) = kind {
|
||||||
// only show modules in a fresh UseTree
|
// only show modules in a fresh UseTree
|
||||||
cov_mark::hit!(unqualified_path_only_modules_in_import);
|
cov_mark::hit!(unqualified_path_only_modules_in_import);
|
||||||
ctx.process_all_names(&mut |name, res| {
|
ctx.process_all_names(&mut |name, res| {
|
||||||
|
@ -25,8 +33,25 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
||||||
}
|
}
|
||||||
["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
|
["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
Some(PathKind::Vis { .. }) => return,
|
||||||
|
Some(PathKind::Attr) => {
|
||||||
|
ctx.process_all_names(&mut |name, res| {
|
||||||
|
let add_resolution = match res {
|
||||||
|
ScopeDef::MacroDef(mac) => mac.is_attr(),
|
||||||
|
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if add_resolution {
|
||||||
|
acc.add_resolution(ctx, name, &res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
match &ctx.completion_location {
|
match &ctx.completion_location {
|
||||||
Some(ImmediateLocation::Visibility(_)) => return,
|
|
||||||
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
|
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
|
||||||
// only show macros in {Assoc}ItemList
|
// only show macros in {Assoc}ItemList
|
||||||
ctx.process_all_names(&mut |name, res| {
|
ctx.process_all_names(&mut |name, res| {
|
||||||
|
@ -56,19 +81,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(ImmediateLocation::Attribute(_)) => {
|
|
||||||
ctx.process_all_names(&mut |name, res| {
|
|
||||||
let add_resolution = match res {
|
|
||||||
ScopeDef::MacroDef(mac) => mac.is_attr(),
|
|
||||||
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
if add_resolution {
|
|
||||||
acc.add_resolution(ctx, name, &res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,16 +35,23 @@ pub(super) enum PathKind {
|
||||||
Expr,
|
Expr,
|
||||||
Type,
|
Type,
|
||||||
Attr,
|
Attr,
|
||||||
|
Mac,
|
||||||
|
Pat,
|
||||||
|
Vis { has_in_token: bool },
|
||||||
|
Use,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct PathCompletionContext {
|
pub(crate) struct PathCompletionContext {
|
||||||
/// If this is a call with () already there
|
/// If this is a call with () already there
|
||||||
call_kind: Option<CallKind>,
|
has_call_parens: bool,
|
||||||
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
|
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
|
||||||
pub(super) is_trivial_path: bool,
|
pub(super) is_trivial_path: bool,
|
||||||
/// If not a trivial path, the prefix (qualifier).
|
/// If not a trivial path, the prefix (qualifier).
|
||||||
pub(super) qualifier: Option<ast::Path>,
|
pub(super) qualifier: Option<ast::Path>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
/// If not a trivial path, the suffix (parent).
|
||||||
|
pub(super) parent: Option<ast::Path>,
|
||||||
/// Whether the qualifier comes from a use tree parent or not
|
/// Whether the qualifier comes from a use tree parent or not
|
||||||
pub(super) use_tree_parent: bool,
|
pub(super) use_tree_parent: bool,
|
||||||
pub(super) kind: Option<PathKind>,
|
pub(super) kind: Option<PathKind>,
|
||||||
|
@ -70,13 +77,6 @@ pub(super) enum LifetimeContext {
|
||||||
LabelDef,
|
LabelDef,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub(crate) enum CallKind {
|
|
||||||
Pat,
|
|
||||||
Mac,
|
|
||||||
Expr,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum ParamKind {
|
pub(crate) enum ParamKind {
|
||||||
Function,
|
Function,
|
||||||
|
@ -206,13 +206,6 @@ impl<'a> CompletionContext<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn in_use_tree(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
self.completion_location,
|
|
||||||
Some(ImmediateLocation::Use | ImmediateLocation::UseTree)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
|
pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.prev_sibling,
|
self.prev_sibling,
|
||||||
|
@ -257,8 +250,8 @@ impl<'a> CompletionContext<'a> {
|
||||||
matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. }))
|
matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
|
pub(crate) fn path_is_call(&self) -> bool {
|
||||||
self.path_context.as_ref().and_then(|it| it.call_kind)
|
self.path_context.as_ref().map_or(false, |it| it.has_call_parens)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_trivial_path(&self) -> bool {
|
pub(crate) fn is_trivial_path(&self) -> bool {
|
||||||
|
@ -673,7 +666,12 @@ impl<'a> CompletionContext<'a> {
|
||||||
Self::classify_lifetime(&self.sema, original_file, lifetime, offset);
|
Self::classify_lifetime(&self.sema, original_file, lifetime, offset);
|
||||||
}
|
}
|
||||||
ast::NameLike::NameRef(name_ref) => {
|
ast::NameLike::NameRef(name_ref) => {
|
||||||
self.path_context = Self::classify_name_ref(&self.sema, original_file, name_ref);
|
if let Some((path_ctx, pat_ctx)) =
|
||||||
|
Self::classify_name_ref(&self.sema, original_file, name_ref)
|
||||||
|
{
|
||||||
|
self.path_context = Some(path_ctx);
|
||||||
|
self.pattern_ctx = pat_ctx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast::NameLike::Name(name) => {
|
ast::NameLike::Name(name) => {
|
||||||
self.pattern_ctx = Self::classify_name(&self.sema, name);
|
self.pattern_ctx = Self::classify_name(&self.sema, name);
|
||||||
|
@ -716,8 +714,111 @@ impl<'a> CompletionContext<'a> {
|
||||||
if !bind_pat.is_simple_ident() {
|
if !bind_pat.is_simple_ident() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
Some(pattern_context_for(bind_pat.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classify_name_ref(
|
||||||
|
_sema: &Semantics<RootDatabase>,
|
||||||
|
original_file: &SyntaxNode,
|
||||||
|
name_ref: ast::NameRef,
|
||||||
|
) -> Option<(PathCompletionContext, Option<PatternContext>)> {
|
||||||
|
let parent = name_ref.syntax().parent()?;
|
||||||
|
let segment = ast::PathSegment::cast(parent)?;
|
||||||
|
let path = segment.parent_path();
|
||||||
|
|
||||||
|
let mut path_ctx = PathCompletionContext {
|
||||||
|
has_call_parens: false,
|
||||||
|
is_trivial_path: false,
|
||||||
|
qualifier: None,
|
||||||
|
parent: None,
|
||||||
|
has_type_args: false,
|
||||||
|
can_be_stmt: false,
|
||||||
|
in_loop_body: false,
|
||||||
|
use_tree_parent: false,
|
||||||
|
kind: None,
|
||||||
|
};
|
||||||
|
let mut pat_ctx = None;
|
||||||
|
path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
|
||||||
|
|
||||||
|
path_ctx.kind = path.syntax().ancestors().find_map(|it| {
|
||||||
|
match_ast! {
|
||||||
|
match it {
|
||||||
|
ast::PathType(_it) => Some(PathKind::Type),
|
||||||
|
ast::PathExpr(it) => {
|
||||||
|
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
|
||||||
|
Some(PathKind::Expr)
|
||||||
|
},
|
||||||
|
ast::TupleStructPat(it) => {
|
||||||
|
path_ctx.has_call_parens = true;
|
||||||
|
pat_ctx = Some(pattern_context_for(it.into()));
|
||||||
|
Some(PathKind::Pat)
|
||||||
|
},
|
||||||
|
ast::RecordPat(it) => {
|
||||||
|
pat_ctx = Some(pattern_context_for(it.into()));
|
||||||
|
Some(PathKind::Pat)
|
||||||
|
},
|
||||||
|
ast::PathPat(it) => {
|
||||||
|
pat_ctx = Some(pattern_context_for(it.into()));
|
||||||
|
Some(PathKind::Pat)
|
||||||
|
},
|
||||||
|
ast::MacroCall(it) => it.excl_token().and(Some(PathKind::Mac)),
|
||||||
|
ast::Meta(_it) => Some(PathKind::Attr),
|
||||||
|
ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
|
||||||
|
ast::UseTree(_it) => Some(PathKind::Use),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
path_ctx.has_type_args = segment.generic_arg_list().is_some();
|
||||||
|
|
||||||
|
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| {
|
||||||
|
find_node_with_range::<ast::PathSegment>(
|
||||||
|
original_file,
|
||||||
|
it.syntax().text_range(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|it| it.parent_path());
|
||||||
|
return Some((path_ctx, pat_ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(segment) = path.segment() {
|
||||||
|
if segment.coloncolon_token().is_some() {
|
||||||
|
return Some((path_ctx, pat_ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path_ctx.is_trivial_path = true;
|
||||||
|
|
||||||
|
// Find either enclosing expr statement (thing with `;`) or a
|
||||||
|
// block. If block, check that we are the last expr.
|
||||||
|
path_ctx.can_be_stmt = name_ref
|
||||||
|
.syntax()
|
||||||
|
.ancestors()
|
||||||
|
.find_map(|node| {
|
||||||
|
if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
|
||||||
|
return Some(stmt.syntax().text_range() == name_ref.syntax().text_range());
|
||||||
|
}
|
||||||
|
if let Some(stmt_list) = ast::StmtList::cast(node) {
|
||||||
|
return Some(
|
||||||
|
stmt_list.tail_expr().map(|e| e.syntax().text_range())
|
||||||
|
== Some(name_ref.syntax().text_range()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
Some((path_ctx, pat_ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pattern_context_for(pat: ast::Pat) -> PatternContext {
|
||||||
let mut is_param = None;
|
let mut is_param = None;
|
||||||
let (refutability, has_type_ascription) = bind_pat
|
let (refutability, has_type_ascription) =
|
||||||
|
pat
|
||||||
.syntax()
|
.syntax()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.skip_while(|it| ast::Pat::can_cast(it.kind()))
|
.skip_while(|it| ast::Pat::can_cast(it.kind()))
|
||||||
|
@ -748,97 +849,8 @@ impl<'a> CompletionContext<'a> {
|
||||||
};
|
};
|
||||||
(refutability, false)
|
(refutability, false)
|
||||||
});
|
});
|
||||||
Some(PatternContext { refutability, is_param, has_type_ascription })
|
PatternContext { refutability, is_param, has_type_ascription }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_name_ref(
|
|
||||||
_sema: &Semantics<RootDatabase>,
|
|
||||||
original_file: &SyntaxNode,
|
|
||||||
name_ref: ast::NameRef,
|
|
||||||
) -> Option<PathCompletionContext> {
|
|
||||||
let parent = name_ref.syntax().parent()?;
|
|
||||||
let segment = ast::PathSegment::cast(parent)?;
|
|
||||||
|
|
||||||
let mut path_ctx = PathCompletionContext {
|
|
||||||
call_kind: None,
|
|
||||||
is_trivial_path: false,
|
|
||||||
qualifier: None,
|
|
||||||
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());
|
|
||||||
let path = segment.parent_path();
|
|
||||||
|
|
||||||
if let Some(p) = path.syntax().parent() {
|
|
||||||
path_ctx.call_kind = match_ast! {
|
|
||||||
match p {
|
|
||||||
ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
|
|
||||||
ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)),
|
|
||||||
ast::TupleStructPat(_it) => Some(CallKind::Pat),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(parent) = path.syntax().parent() {
|
|
||||||
path_ctx.kind = match_ast! {
|
|
||||||
match parent {
|
|
||||||
ast::PathType(_it) => Some(PathKind::Type),
|
|
||||||
ast::PathExpr(_it) => Some(PathKind::Expr),
|
|
||||||
ast::Meta(_it) => Some(PathKind::Attr),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
path_ctx.has_type_args = segment.generic_arg_list().is_some();
|
|
||||||
|
|
||||||
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| {
|
|
||||||
find_node_with_range::<ast::PathSegment>(
|
|
||||||
original_file,
|
|
||||||
it.syntax().text_range(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map(|it| it.parent_path());
|
|
||||||
return Some(path_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(segment) = path.segment() {
|
|
||||||
if segment.coloncolon_token().is_some() {
|
|
||||||
return Some(path_ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
path_ctx.is_trivial_path = true;
|
|
||||||
|
|
||||||
// Find either enclosing expr statement (thing with `;`) or a
|
|
||||||
// block. If block, check that we are the last expr.
|
|
||||||
path_ctx.can_be_stmt = name_ref
|
|
||||||
.syntax()
|
|
||||||
.ancestors()
|
|
||||||
.find_map(|node| {
|
|
||||||
if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
|
|
||||||
return Some(stmt.syntax().text_range() == name_ref.syntax().text_range());
|
|
||||||
}
|
|
||||||
if let Some(stmt_list) = ast::StmtList::cast(node) {
|
|
||||||
return Some(
|
|
||||||
stmt_list.tail_expr().map(|e| e.syntax().text_range())
|
|
||||||
== Some(name_ref.syntax().text_range()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.unwrap_or(false);
|
|
||||||
Some(path_ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
|
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
|
||||||
syntax.covering_element(range).ancestors().find_map(N::cast)
|
syntax.covering_element(range).ancestors().find_map(N::cast)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,6 @@ pub(crate) enum ImmediatePrevSibling {
|
||||||
/// from which file the nodes are.
|
/// from which file the nodes are.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum ImmediateLocation {
|
pub(crate) enum ImmediateLocation {
|
||||||
Use,
|
|
||||||
UseTree,
|
|
||||||
Rename,
|
Rename,
|
||||||
Impl,
|
Impl,
|
||||||
Trait,
|
Trait,
|
||||||
|
@ -47,10 +45,7 @@ pub(crate) enum ImmediateLocation {
|
||||||
TypeBound,
|
TypeBound,
|
||||||
Variant,
|
Variant,
|
||||||
/// Fake file ast node
|
/// Fake file ast node
|
||||||
Attribute(ast::Attr),
|
|
||||||
/// Fake file ast node
|
|
||||||
ModDeclaration(ast::Module),
|
ModDeclaration(ast::Module),
|
||||||
Visibility(ast::Visibility),
|
|
||||||
/// Original file ast node
|
/// Original file ast node
|
||||||
MethodCall {
|
MethodCall {
|
||||||
receiver: Option<ast::Expr>,
|
receiver: Option<ast::Expr>,
|
||||||
|
@ -206,9 +201,6 @@ pub(crate) fn determine_location(
|
||||||
let res = match_ast! {
|
let res = match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::IdentPat(_it) => ImmediateLocation::IdentPat,
|
ast::IdentPat(_it) => ImmediateLocation::IdentPat,
|
||||||
ast::Use(_it) => ImmediateLocation::Use,
|
|
||||||
ast::UseTree(_it) => ImmediateLocation::UseTree,
|
|
||||||
ast::UseTreeList(_it) => ImmediateLocation::UseTree,
|
|
||||||
ast::Rename(_it) => ImmediateLocation::Rename,
|
ast::Rename(_it) => ImmediateLocation::Rename,
|
||||||
ast::StmtList(_it) => ImmediateLocation::StmtList,
|
ast::StmtList(_it) => ImmediateLocation::StmtList,
|
||||||
ast::SourceFile(_it) => ImmediateLocation::ItemList,
|
ast::SourceFile(_it) => ImmediateLocation::ItemList,
|
||||||
|
@ -242,7 +234,6 @@ pub(crate) fn determine_location(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ast::Attr(it) => ImmediateLocation::Attribute(it),
|
|
||||||
ast::FieldExpr(it) => {
|
ast::FieldExpr(it) => {
|
||||||
let receiver = it
|
let receiver = it
|
||||||
.expr()
|
.expr()
|
||||||
|
@ -268,8 +259,6 @@ pub(crate) fn determine_location(
|
||||||
.and_then(|r| find_node_with_range(original_file, r)),
|
.and_then(|r| find_node_with_range(original_file, r)),
|
||||||
has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
|
has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
|
||||||
},
|
},
|
||||||
ast::Visibility(it) => it.pub_token()
|
|
||||||
.and_then(|t| (t.text_range().end() < offset).then(|| ImmediateLocation::Visibility(it)))?,
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -417,14 +406,6 @@ mod tests {
|
||||||
check_location(r"impl A { fn f$0 }", None);
|
check_location(r"impl A { fn f$0 }", None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
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}", ImmediateLocation::UseTree);
|
|
||||||
check_location(r"use {f$0}", ImmediateLocation::UseTree);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_record_field_loc() {
|
fn test_record_field_loc() {
|
||||||
check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
|
check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
|
||||||
|
|
|
@ -4,7 +4,7 @@ use either::Either;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::ast::{self, HasName};
|
use syntax::ast::{self, HasName};
|
||||||
|
|
||||||
use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
|
use crate::{context::PathKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum Params {
|
pub(super) enum Params {
|
||||||
|
@ -30,11 +30,11 @@ impl Builder {
|
||||||
if !ctx.config.add_call_parenthesis {
|
if !ctx.config.add_call_parenthesis {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ctx.in_use_tree() {
|
if let Some(PathKind::Use) = ctx.path_kind() {
|
||||||
cov_mark::hit!(no_parens_in_use_item);
|
cov_mark::hit!(no_parens_in_use_item);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if matches!(ctx.path_call_kind(), Some(CallKind::Expr | CallKind::Pat))
|
if matches!(ctx.path_kind(), Some(PathKind::Expr | PathKind::Pat) if ctx.path_is_call())
|
||||||
| matches!(
|
| matches!(
|
||||||
ctx.completion_location,
|
ctx.completion_location,
|
||||||
Some(ImmediateLocation::MethodCall { has_parens: true, .. })
|
Some(ImmediateLocation::MethodCall { has_parens: true, .. })
|
||||||
|
|
|
@ -9,7 +9,7 @@ use syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::CallKind,
|
context::PathKind,
|
||||||
item::{CompletionItem, ImportEdit},
|
item::{CompletionItem, ImportEdit},
|
||||||
render::RenderContext,
|
render::RenderContext,
|
||||||
};
|
};
|
||||||
|
@ -67,9 +67,8 @@ impl<'a> MacroRender<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let needs_bang = self.macro_.is_fn_like()
|
let needs_bang = self.macro_.is_fn_like()
|
||||||
&& !(self.ctx.completion.in_use_tree()
|
&& !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use));
|
||||||
|| matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)));
|
let has_parens = self.ctx.completion.path_is_call();
|
||||||
let has_parens = self.ctx.completion.path_call_kind().is_some();
|
|
||||||
|
|
||||||
match self.ctx.snippet_cap() {
|
match self.ctx.snippet_cap() {
|
||||||
Some(cap) if needs_bang && !has_parens => {
|
Some(cap) if needs_bang && !has_parens => {
|
||||||
|
@ -92,8 +91,7 @@ impl<'a> MacroRender<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn needs_bang(&self) -> bool {
|
fn needs_bang(&self) -> bool {
|
||||||
!self.ctx.completion.in_use_tree()
|
!matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use))
|
||||||
&& !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn label(&self) -> SmolStr {
|
fn label(&self) -> SmolStr {
|
||||||
|
|
|
@ -1000,6 +1000,19 @@ fn function() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flyimport_item_name() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
mod module {
|
||||||
|
pub struct Struct;
|
||||||
|
}
|
||||||
|
struct Str$0
|
||||||
|
"#,
|
||||||
|
expect![[r#""#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn flyimport_rename() {
|
fn flyimport_rename() {
|
||||||
check(
|
check(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue