feat: Add break and return postfix keyword completions
This commit is contained in:
parent
6655960186
commit
15bffe25bd
@ -4,7 +4,10 @@ use ide_db::FxHashSet;
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
context::{CompletionContext, DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, Qualified},
|
||||
context::{
|
||||
CompletionContext, DotAccess, DotAccessExprCtx, DotAccessKind, PathCompletionCtx,
|
||||
PathExprCtx, Qualified,
|
||||
},
|
||||
CompletionItem, CompletionItemKind, Completions,
|
||||
};
|
||||
|
||||
@ -51,7 +54,7 @@ pub(crate) fn complete_undotted_self(
|
||||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
path_ctx: &PathCompletionCtx,
|
||||
expr_ctx: &ExprCtx,
|
||||
expr_ctx: &PathExprCtx,
|
||||
) {
|
||||
if !ctx.config.enable_self_on_the_fly {
|
||||
return;
|
||||
@ -66,7 +69,7 @@ pub(crate) fn complete_undotted_self(
|
||||
return;
|
||||
}
|
||||
let self_param = match expr_ctx {
|
||||
ExprCtx { self_param: Some(self_param), .. } => self_param,
|
||||
PathExprCtx { self_param: Some(self_param), .. } => self_param,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
@ -82,6 +85,10 @@ pub(crate) fn complete_undotted_self(
|
||||
receiver: None,
|
||||
receiver_ty: None,
|
||||
kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal: false },
|
||||
ctx: DotAccessExprCtx {
|
||||
in_block_expr: expr_ctx.in_block_expr,
|
||||
in_breakable: expr_ctx.in_breakable,
|
||||
},
|
||||
},
|
||||
Some(hir::known::SELF_PARAM),
|
||||
field,
|
||||
@ -99,6 +106,10 @@ pub(crate) fn complete_undotted_self(
|
||||
receiver: None,
|
||||
receiver_ty: None,
|
||||
kind: DotAccessKind::Method { has_parens: false },
|
||||
ctx: DotAccessExprCtx {
|
||||
in_block_expr: expr_ctx.in_block_expr,
|
||||
in_breakable: expr_ctx.in_breakable,
|
||||
},
|
||||
},
|
||||
func,
|
||||
Some(hir::known::SELF_PARAM),
|
||||
|
@ -5,7 +5,7 @@ use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
completions::record::add_default_update,
|
||||
context::{ExprCtx, PathCompletionCtx, Qualified},
|
||||
context::{BreakableKind, PathCompletionCtx, PathExprCtx, Qualified},
|
||||
CompletionContext, Completions,
|
||||
};
|
||||
|
||||
@ -13,16 +13,16 @@ pub(crate) fn complete_expr_path(
|
||||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
|
||||
expr_ctx: &ExprCtx,
|
||||
expr_ctx: &PathExprCtx,
|
||||
) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "complete_expr_path").entered();
|
||||
if !ctx.qualifier_ctx.none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let &ExprCtx {
|
||||
let &PathExprCtx {
|
||||
in_block_expr,
|
||||
in_loop_body,
|
||||
in_breakable,
|
||||
after_if_expr,
|
||||
in_condition,
|
||||
incomplete_let,
|
||||
@ -290,7 +290,7 @@ pub(crate) fn complete_expr_path(
|
||||
add_keyword("mut", "mut ");
|
||||
}
|
||||
|
||||
if in_loop_body {
|
||||
if in_breakable != BreakableKind::None {
|
||||
if in_block_expr {
|
||||
add_keyword("continue", "continue;");
|
||||
add_keyword("break", "break;");
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Completion of paths and keywords at item list position.
|
||||
|
||||
use crate::{
|
||||
context::{ExprCtx, ItemListKind, PathCompletionCtx, Qualified},
|
||||
context::{ItemListKind, PathCompletionCtx, PathExprCtx, Qualified},
|
||||
CompletionContext, Completions,
|
||||
};
|
||||
|
||||
@ -11,7 +11,7 @@ pub(crate) fn complete_item_list_in_expr(
|
||||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
path_ctx: &PathCompletionCtx,
|
||||
expr_ctx: &ExprCtx,
|
||||
expr_ctx: &PathExprCtx,
|
||||
) {
|
||||
if !expr_ctx.in_block_expr {
|
||||
return;
|
||||
|
@ -81,11 +81,13 @@ fn foo(a: A) { a.$0 }
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
@ -106,11 +108,13 @@ fn foo() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
@ -133,11 +137,13 @@ fn foo(a: A) { a.$0 }
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
mod format_like;
|
||||
|
||||
use hir::ItemInNs;
|
||||
use ide_db::{
|
||||
documentation::{Documentation, HasDocs},
|
||||
imports::insert_use::ImportScope,
|
||||
@ -17,7 +18,7 @@ use text_edit::TextEdit;
|
||||
|
||||
use crate::{
|
||||
completions::postfix::format_like::add_format_like_completions,
|
||||
context::{CompletionContext, DotAccess, DotAccessKind},
|
||||
context::{BreakableKind, CompletionContext, DotAccess, DotAccessKind},
|
||||
item::{Builder, CompletionRelevancePostfixMatch},
|
||||
CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,
|
||||
};
|
||||
@ -44,6 +45,7 @@ pub(crate) fn complete_postfix(
|
||||
),
|
||||
_ => return,
|
||||
};
|
||||
let expr_ctx = &dot_access.ctx;
|
||||
|
||||
let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
|
||||
|
||||
@ -59,16 +61,22 @@ pub(crate) fn complete_postfix(
|
||||
|
||||
if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
|
||||
if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) {
|
||||
if let &[hir::AssocItem::Function(drop_fn)] = &*drop_trait.items(ctx.db) {
|
||||
cov_mark::hit!(postfix_drop_completion);
|
||||
// FIXME: check that `drop` is in scope, use fully qualified path if it isn't/if shadowed
|
||||
let mut item = postfix_snippet(
|
||||
"drop",
|
||||
"fn drop(&mut self)",
|
||||
&format!("drop($0{receiver_text})"),
|
||||
);
|
||||
item.set_documentation(drop_fn.docs(ctx.db));
|
||||
item.add_to(acc, ctx.db);
|
||||
if let Some(drop_fn) = ctx.famous_defs().core_mem_drop() {
|
||||
if let Some(path) = ctx.module.find_use_path(
|
||||
ctx.db,
|
||||
ItemInNs::Values(drop_fn.into()),
|
||||
ctx.config.prefer_no_std,
|
||||
ctx.config.prefer_prelude,
|
||||
) {
|
||||
cov_mark::hit!(postfix_drop_completion);
|
||||
let mut item = postfix_snippet(
|
||||
"drop",
|
||||
"fn drop(&mut self)",
|
||||
&format!("{path}($0{receiver_text})", path = path.display(ctx.db)),
|
||||
);
|
||||
item.set_documentation(drop_fn.docs(ctx.db));
|
||||
item.add_to(acc, ctx.db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,6 +148,7 @@ pub(crate) fn complete_postfix(
|
||||
|
||||
postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db);
|
||||
postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db);
|
||||
postfix_snippet("deref", "*expr", &format!("*{receiver_text}")).add_to(acc, ctx.db);
|
||||
|
||||
let mut unsafe_should_be_wrapped = true;
|
||||
if dot_receiver.syntax().kind() == BLOCK_EXPR {
|
||||
@ -224,6 +233,28 @@ pub(crate) fn complete_postfix(
|
||||
add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text);
|
||||
}
|
||||
}
|
||||
|
||||
postfix_snippet(
|
||||
"return",
|
||||
"return expr",
|
||||
&format!(
|
||||
"return {receiver_text}{semi}",
|
||||
semi = if expr_ctx.in_block_expr { ";" } else { "" }
|
||||
),
|
||||
)
|
||||
.add_to(acc, ctx.db);
|
||||
|
||||
if let BreakableKind::Block | BreakableKind::Loop = expr_ctx.in_breakable {
|
||||
postfix_snippet(
|
||||
"break",
|
||||
"break expr",
|
||||
&format!(
|
||||
"break {receiver_text}{semi}",
|
||||
semi = if expr_ctx.in_block_expr { ";" } else { "" }
|
||||
),
|
||||
)
|
||||
.add_to(acc, ctx.db);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
|
||||
@ -368,6 +399,7 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn if if expr {}
|
||||
sn let let
|
||||
sn letm let mut
|
||||
@ -375,6 +407,7 @@ fn main() {
|
||||
sn not !expr
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
sn while while expr {}
|
||||
"#]],
|
||||
@ -399,11 +432,13 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn if if expr {}
|
||||
sn match match expr {}
|
||||
sn not !expr
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
sn while while expr {}
|
||||
"#]],
|
||||
@ -424,11 +459,13 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
)
|
||||
@ -448,6 +485,7 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn if if expr {}
|
||||
sn let let
|
||||
sn letm let mut
|
||||
@ -455,6 +493,7 @@ fn main() {
|
||||
sn not !expr
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
sn while while expr {}
|
||||
"#]],
|
||||
|
@ -6,7 +6,7 @@ use syntax::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
context::{DotAccess, DotAccessKind, PatternContext},
|
||||
context::{DotAccess, DotAccessExprCtx, DotAccessKind, PatternContext},
|
||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
|
||||
CompletionRelevancePostfixMatch, Completions,
|
||||
};
|
||||
@ -118,12 +118,17 @@ fn complete_fields(
|
||||
missing_fields: Vec<(hir::Field, hir::Type)>,
|
||||
) {
|
||||
for (field, ty) in missing_fields {
|
||||
// This should call something else, we shouldn't be synthesizing a DotAccess here
|
||||
acc.add_field(
|
||||
ctx,
|
||||
&DotAccess {
|
||||
receiver: None,
|
||||
receiver_ty: None,
|
||||
kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal: false },
|
||||
ctx: DotAccessExprCtx {
|
||||
in_block_expr: false,
|
||||
in_breakable: crate::context::BreakableKind::None,
|
||||
},
|
||||
},
|
||||
None,
|
||||
field,
|
||||
|
@ -3,7 +3,7 @@
|
||||
use ide_db::{documentation::Documentation, imports::insert_use::ImportScope, SnippetCap};
|
||||
|
||||
use crate::{
|
||||
context::{ExprCtx, ItemListKind, PathCompletionCtx, Qualified},
|
||||
context::{ItemListKind, PathCompletionCtx, PathExprCtx, Qualified},
|
||||
item::Builder,
|
||||
CompletionContext, CompletionItem, CompletionItemKind, Completions, SnippetScope,
|
||||
};
|
||||
@ -12,7 +12,7 @@ pub(crate) fn complete_expr_snippet(
|
||||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
path_ctx: &PathCompletionCtx,
|
||||
&ExprCtx { in_block_expr, .. }: &ExprCtx,
|
||||
&PathExprCtx { in_block_expr, .. }: &PathExprCtx,
|
||||
) {
|
||||
if !matches!(path_ctx.qualified, Qualified::No) {
|
||||
return;
|
||||
|
@ -45,13 +45,13 @@ pub(crate) enum Visible {
|
||||
|
||||
/// Existing qualifiers for the thing we are currently completing.
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct QualifierCtx {
|
||||
pub(super) unsafe_tok: Option<SyntaxToken>,
|
||||
pub(super) vis_node: Option<ast::Visibility>,
|
||||
pub(crate) struct QualifierCtx {
|
||||
pub(crate) unsafe_tok: Option<SyntaxToken>,
|
||||
pub(crate) vis_node: Option<ast::Visibility>,
|
||||
}
|
||||
|
||||
impl QualifierCtx {
|
||||
pub(super) fn none(&self) -> bool {
|
||||
pub(crate) fn none(&self) -> bool {
|
||||
self.unsafe_tok.is_none() && self.vis_node.is_none()
|
||||
}
|
||||
}
|
||||
@ -60,27 +60,27 @@ impl QualifierCtx {
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PathCompletionCtx {
|
||||
/// If this is a call with () already there (or {} in case of record patterns)
|
||||
pub(super) has_call_parens: bool,
|
||||
pub(crate) has_call_parens: bool,
|
||||
/// If this has a macro call bang !
|
||||
pub(super) has_macro_bang: bool,
|
||||
pub(crate) has_macro_bang: bool,
|
||||
/// The qualifier of the current path.
|
||||
pub(super) qualified: Qualified,
|
||||
pub(crate) qualified: Qualified,
|
||||
/// The parent of the path we are completing.
|
||||
pub(super) parent: Option<ast::Path>,
|
||||
pub(crate) parent: Option<ast::Path>,
|
||||
#[allow(dead_code)]
|
||||
/// The path of which we are completing the segment
|
||||
pub(super) path: ast::Path,
|
||||
pub(crate) path: ast::Path,
|
||||
/// The path of which we are completing the segment in the original file
|
||||
pub(crate) original_path: Option<ast::Path>,
|
||||
pub(super) kind: PathKind,
|
||||
pub(crate) kind: PathKind,
|
||||
/// Whether the path segment has type args or not.
|
||||
pub(super) has_type_args: bool,
|
||||
pub(crate) has_type_args: bool,
|
||||
/// Whether the qualifier comes from a use tree parent or not
|
||||
pub(crate) use_tree_parent: bool,
|
||||
}
|
||||
|
||||
impl PathCompletionCtx {
|
||||
pub(super) fn is_trivial_path(&self) -> bool {
|
||||
pub(crate) fn is_trivial_path(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
PathCompletionCtx {
|
||||
@ -97,9 +97,9 @@ impl PathCompletionCtx {
|
||||
|
||||
/// The kind of path we are completing right now.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) enum PathKind {
|
||||
pub(crate) enum PathKind {
|
||||
Expr {
|
||||
expr_ctx: ExprCtx,
|
||||
expr_ctx: PathExprCtx,
|
||||
},
|
||||
Type {
|
||||
location: TypeLocation,
|
||||
@ -132,9 +132,9 @@ pub(crate) struct AttrCtx {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) struct ExprCtx {
|
||||
pub(crate) struct PathExprCtx {
|
||||
pub(crate) in_block_expr: bool,
|
||||
pub(crate) in_loop_body: bool,
|
||||
pub(crate) in_breakable: BreakableKind,
|
||||
pub(crate) after_if_expr: bool,
|
||||
/// Whether this expression is the direct condition of an if or while expression
|
||||
pub(crate) in_condition: bool,
|
||||
@ -221,7 +221,7 @@ pub(crate) enum TypeAscriptionTarget {
|
||||
|
||||
/// The kind of item list a [`PathKind::Item`] belongs to.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) enum ItemListKind {
|
||||
pub(crate) enum ItemListKind {
|
||||
SourceFile,
|
||||
Module,
|
||||
Impl,
|
||||
@ -231,7 +231,7 @@ pub(super) enum ItemListKind {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum Qualified {
|
||||
pub(crate) enum Qualified {
|
||||
No,
|
||||
With {
|
||||
path: ast::Path,
|
||||
@ -259,37 +259,37 @@ pub(super) enum Qualified {
|
||||
|
||||
/// The state of the pattern we are completing.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(super) struct PatternContext {
|
||||
pub(super) refutability: PatternRefutability,
|
||||
pub(super) param_ctx: Option<ParamContext>,
|
||||
pub(super) has_type_ascription: bool,
|
||||
pub(super) parent_pat: Option<ast::Pat>,
|
||||
pub(super) ref_token: Option<SyntaxToken>,
|
||||
pub(super) mut_token: Option<SyntaxToken>,
|
||||
pub(crate) struct PatternContext {
|
||||
pub(crate) refutability: PatternRefutability,
|
||||
pub(crate) param_ctx: Option<ParamContext>,
|
||||
pub(crate) has_type_ascription: bool,
|
||||
pub(crate) parent_pat: Option<ast::Pat>,
|
||||
pub(crate) ref_token: Option<SyntaxToken>,
|
||||
pub(crate) mut_token: Option<SyntaxToken>,
|
||||
/// The record pattern this name or ref is a field of
|
||||
pub(super) record_pat: Option<ast::RecordPat>,
|
||||
pub(super) impl_: Option<ast::Impl>,
|
||||
pub(crate) record_pat: Option<ast::RecordPat>,
|
||||
pub(crate) impl_: Option<ast::Impl>,
|
||||
/// List of missing variants in a match expr
|
||||
pub(super) missing_variants: Vec<hir::Variant>,
|
||||
pub(crate) missing_variants: Vec<hir::Variant>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(super) struct ParamContext {
|
||||
pub(super) param_list: ast::ParamList,
|
||||
pub(super) param: ast::Param,
|
||||
pub(super) kind: ParamKind,
|
||||
pub(crate) struct ParamContext {
|
||||
pub(crate) param_list: ast::ParamList,
|
||||
pub(crate) param: ast::Param,
|
||||
pub(crate) kind: ParamKind,
|
||||
}
|
||||
|
||||
/// The state of the lifetime we are completing.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct LifetimeContext {
|
||||
pub(super) lifetime: Option<ast::Lifetime>,
|
||||
pub(super) kind: LifetimeKind,
|
||||
pub(crate) struct LifetimeContext {
|
||||
pub(crate) lifetime: Option<ast::Lifetime>,
|
||||
pub(crate) kind: LifetimeKind,
|
||||
}
|
||||
|
||||
/// The kind of lifetime we are completing.
|
||||
#[derive(Debug)]
|
||||
pub(super) enum LifetimeKind {
|
||||
pub(crate) enum LifetimeKind {
|
||||
LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
|
||||
Lifetime,
|
||||
LabelRef,
|
||||
@ -298,16 +298,16 @@ pub(super) enum LifetimeKind {
|
||||
|
||||
/// The state of the name we are completing.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NameContext {
|
||||
pub(crate) struct NameContext {
|
||||
#[allow(dead_code)]
|
||||
pub(super) name: Option<ast::Name>,
|
||||
pub(super) kind: NameKind,
|
||||
pub(crate) name: Option<ast::Name>,
|
||||
pub(crate) kind: NameKind,
|
||||
}
|
||||
|
||||
/// The kind of the name we are completing.
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(super) enum NameKind {
|
||||
pub(crate) enum NameKind {
|
||||
Const,
|
||||
ConstParam,
|
||||
Enum,
|
||||
@ -331,15 +331,15 @@ pub(super) enum NameKind {
|
||||
|
||||
/// The state of the NameRef we are completing.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NameRefContext {
|
||||
pub(crate) struct NameRefContext {
|
||||
/// NameRef syntax in the original file
|
||||
pub(super) nameref: Option<ast::NameRef>,
|
||||
pub(super) kind: NameRefKind,
|
||||
pub(crate) nameref: Option<ast::NameRef>,
|
||||
pub(crate) kind: NameRefKind,
|
||||
}
|
||||
|
||||
/// The kind of the NameRef we are completing.
|
||||
#[derive(Debug)]
|
||||
pub(super) enum NameRefKind {
|
||||
pub(crate) enum NameRefKind {
|
||||
Path(PathCompletionCtx),
|
||||
DotAccess(DotAccess),
|
||||
/// Position where we are only interested in keyword completions
|
||||
@ -355,7 +355,7 @@ pub(super) enum NameRefKind {
|
||||
|
||||
/// The identifier we are currently completing.
|
||||
#[derive(Debug)]
|
||||
pub(super) enum CompletionAnalysis {
|
||||
pub(crate) enum CompletionAnalysis {
|
||||
Name(NameContext),
|
||||
NameRef(NameRefContext),
|
||||
Lifetime(LifetimeContext),
|
||||
@ -376,14 +376,15 @@ pub(super) enum CompletionAnalysis {
|
||||
|
||||
/// Information about the field or method access we are completing.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct DotAccess {
|
||||
pub(super) receiver: Option<ast::Expr>,
|
||||
pub(super) receiver_ty: Option<TypeInfo>,
|
||||
pub(super) kind: DotAccessKind,
|
||||
pub(crate) struct DotAccess {
|
||||
pub(crate) receiver: Option<ast::Expr>,
|
||||
pub(crate) receiver_ty: Option<TypeInfo>,
|
||||
pub(crate) kind: DotAccessKind,
|
||||
pub(crate) ctx: DotAccessExprCtx,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum DotAccessKind {
|
||||
pub(crate) enum DotAccessKind {
|
||||
Field {
|
||||
/// True if the receiver is an integer and there is no ident in the original file after it yet
|
||||
/// like `0.$0`
|
||||
@ -394,6 +395,21 @@ pub(super) enum DotAccessKind {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) struct DotAccessExprCtx {
|
||||
pub(crate) in_block_expr: bool,
|
||||
pub(crate) in_breakable: BreakableKind,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum BreakableKind {
|
||||
None,
|
||||
Loop,
|
||||
For,
|
||||
While,
|
||||
Block,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ParamKind {
|
||||
Function(ast::Fn),
|
||||
@ -404,39 +420,39 @@ pub(crate) enum ParamKind {
|
||||
/// exactly is the cursor, syntax-wise.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CompletionContext<'a> {
|
||||
pub(super) sema: Semantics<'a, RootDatabase>,
|
||||
pub(super) scope: SemanticsScope<'a>,
|
||||
pub(super) db: &'a RootDatabase,
|
||||
pub(super) config: &'a CompletionConfig,
|
||||
pub(super) position: FilePosition,
|
||||
pub(crate) sema: Semantics<'a, RootDatabase>,
|
||||
pub(crate) scope: SemanticsScope<'a>,
|
||||
pub(crate) db: &'a RootDatabase,
|
||||
pub(crate) config: &'a CompletionConfig,
|
||||
pub(crate) position: FilePosition,
|
||||
|
||||
/// The token before the cursor, in the original file.
|
||||
pub(super) original_token: SyntaxToken,
|
||||
pub(crate) original_token: SyntaxToken,
|
||||
/// The token before the cursor, in the macro-expanded file.
|
||||
pub(super) token: SyntaxToken,
|
||||
pub(crate) token: SyntaxToken,
|
||||
/// The crate of the current file.
|
||||
pub(super) krate: hir::Crate,
|
||||
pub(crate) krate: hir::Crate,
|
||||
/// The module of the `scope`.
|
||||
pub(super) module: hir::Module,
|
||||
pub(crate) module: hir::Module,
|
||||
/// Whether nightly toolchain is used. Cached since this is looked up a lot.
|
||||
is_nightly: bool,
|
||||
|
||||
/// The expected name of what we are completing.
|
||||
/// This is usually the parameter name of the function argument we are completing.
|
||||
pub(super) expected_name: Option<NameOrNameRef>,
|
||||
pub(crate) expected_name: Option<NameOrNameRef>,
|
||||
/// The expected type of what we are completing.
|
||||
pub(super) expected_type: Option<Type>,
|
||||
pub(crate) expected_type: Option<Type>,
|
||||
|
||||
pub(super) qualifier_ctx: QualifierCtx,
|
||||
pub(crate) qualifier_ctx: QualifierCtx,
|
||||
|
||||
pub(super) locals: FxHashMap<Name, Local>,
|
||||
pub(crate) locals: FxHashMap<Name, Local>,
|
||||
|
||||
/// The module depth of the current module of the cursor position.
|
||||
/// - crate-root
|
||||
/// - mod foo
|
||||
/// - mod bar
|
||||
/// Here depth will be 2
|
||||
pub(super) depth_from_crate_root: usize,
|
||||
pub(crate) depth_from_crate_root: usize,
|
||||
}
|
||||
|
||||
impl CompletionContext<'_> {
|
||||
@ -634,7 +650,7 @@ impl CompletionContext<'_> {
|
||||
|
||||
// CompletionContext construction
|
||||
impl<'a> CompletionContext<'a> {
|
||||
pub(super) fn new(
|
||||
pub(crate) fn new(
|
||||
db: &'a RootDatabase,
|
||||
position @ FilePosition { file_id, offset }: FilePosition,
|
||||
config: &'a CompletionConfig,
|
||||
|
@ -11,10 +11,11 @@ use syntax::{
|
||||
};
|
||||
|
||||
use crate::context::{
|
||||
AttrCtx, CompletionAnalysis, DotAccess, DotAccessKind, ExprCtx, ItemListKind, LifetimeContext,
|
||||
LifetimeKind, NameContext, NameKind, NameRefContext, NameRefKind, ParamContext, ParamKind,
|
||||
PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx,
|
||||
TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER,
|
||||
AttrCtx, BreakableKind, CompletionAnalysis, DotAccess, DotAccessExprCtx, DotAccessKind,
|
||||
ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext,
|
||||
NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, PathKind, PatternContext,
|
||||
PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation,
|
||||
COMPLETION_MARKER,
|
||||
};
|
||||
|
||||
struct ExpansionResult {
|
||||
@ -623,7 +624,8 @@ fn classify_name_ref(
|
||||
let kind = NameRefKind::DotAccess(DotAccess {
|
||||
receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
|
||||
kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
|
||||
receiver
|
||||
receiver,
|
||||
ctx: DotAccessExprCtx { in_block_expr: is_in_block(field.syntax()), in_breakable: is_in_breakable(field.syntax()) }
|
||||
});
|
||||
return Some(make_res(kind));
|
||||
},
|
||||
@ -636,7 +638,8 @@ fn classify_name_ref(
|
||||
let kind = NameRefKind::DotAccess(DotAccess {
|
||||
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()) },
|
||||
receiver
|
||||
receiver,
|
||||
ctx: DotAccessExprCtx { in_block_expr: is_in_block(method.syntax()), in_breakable: is_in_breakable(method.syntax()) }
|
||||
});
|
||||
return Some(make_res(kind));
|
||||
},
|
||||
@ -659,13 +662,6 @@ fn classify_name_ref(
|
||||
use_tree_parent: false,
|
||||
};
|
||||
|
||||
let is_in_block = |it: &SyntaxNode| {
|
||||
it.parent()
|
||||
.map(|node| {
|
||||
ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())
|
||||
})
|
||||
.unwrap_or(false)
|
||||
};
|
||||
let func_update_record = |syn: &SyntaxNode| {
|
||||
if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
|
||||
find_node_in_file_compensated(sema, original_file, &record_expr)
|
||||
@ -932,7 +928,7 @@ fn classify_name_ref(
|
||||
let make_path_kind_expr = |expr: ast::Expr| {
|
||||
let it = expr.syntax();
|
||||
let in_block_expr = is_in_block(it);
|
||||
let in_loop_body = is_in_loop_body(it);
|
||||
let in_loop_body = is_in_breakable(it);
|
||||
let after_if_expr = after_if_expr(it.clone());
|
||||
let ref_expr_parent =
|
||||
path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
|
||||
@ -998,9 +994,9 @@ fn classify_name_ref(
|
||||
};
|
||||
|
||||
PathKind::Expr {
|
||||
expr_ctx: ExprCtx {
|
||||
expr_ctx: PathExprCtx {
|
||||
in_block_expr,
|
||||
in_loop_body,
|
||||
in_breakable: in_loop_body,
|
||||
after_if_expr,
|
||||
in_condition,
|
||||
ref_expr_parent,
|
||||
@ -1202,7 +1198,7 @@ fn classify_name_ref(
|
||||
if path_ctx.is_trivial_path() {
|
||||
// fetch the full expression that may have qualifiers attached to it
|
||||
let top_node = match path_ctx.kind {
|
||||
PathKind::Expr { expr_ctx: ExprCtx { in_block_expr: true, .. } } => {
|
||||
PathKind::Expr { expr_ctx: PathExprCtx { in_block_expr: true, .. } } => {
|
||||
parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| {
|
||||
let parent = p.parent()?;
|
||||
if ast::StmtList::can_cast(parent.kind()) {
|
||||
@ -1467,21 +1463,30 @@ fn is_in_token_of_for_loop(path: &ast::Path) -> bool {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn is_in_loop_body(node: &SyntaxNode) -> bool {
|
||||
fn is_in_breakable(node: &SyntaxNode) -> BreakableKind {
|
||||
node.ancestors()
|
||||
.take_while(|it| it.kind() != SyntaxKind::FN && it.kind() != SyntaxKind::CLOSURE_EXPR)
|
||||
.find_map(|it| {
|
||||
let loop_body = match_ast! {
|
||||
let (breakable, loop_body) = match_ast! {
|
||||
match it {
|
||||
ast::ForExpr(it) => it.loop_body(),
|
||||
ast::WhileExpr(it) => it.loop_body(),
|
||||
ast::LoopExpr(it) => it.loop_body(),
|
||||
_ => None,
|
||||
ast::ForExpr(it) => (BreakableKind::For, it.loop_body()),
|
||||
ast::WhileExpr(it) => (BreakableKind::While, it.loop_body()),
|
||||
ast::LoopExpr(it) => (BreakableKind::Loop, it.loop_body()),
|
||||
ast::BlockExpr(it) => return it.label().map(|_| BreakableKind::Block),
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
loop_body.filter(|it| it.syntax().text_range().contains_range(node.text_range()))
|
||||
loop_body
|
||||
.filter(|it| it.syntax().text_range().contains_range(node.text_range()))
|
||||
.map(|_| breakable)
|
||||
})
|
||||
.is_some()
|
||||
.unwrap_or(BreakableKind::None)
|
||||
}
|
||||
|
||||
fn is_in_block(node: &SyntaxNode) -> bool {
|
||||
node.parent()
|
||||
.map(|node| ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind()))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn previous_non_trivia_token(e: impl Into<SyntaxElement>) -> Option<SyntaxToken> {
|
||||
|
@ -2199,12 +2199,14 @@ fn main() {
|
||||
sn while []
|
||||
sn ref []
|
||||
sn refm []
|
||||
sn deref []
|
||||
sn unsafe []
|
||||
sn match []
|
||||
sn box []
|
||||
sn dbg []
|
||||
sn dbgr []
|
||||
sn call []
|
||||
sn return []
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
@ -2227,6 +2229,7 @@ fn main() {
|
||||
me f() []
|
||||
sn ref []
|
||||
sn refm []
|
||||
sn deref []
|
||||
sn unsafe []
|
||||
sn match []
|
||||
sn box []
|
||||
@ -2235,6 +2238,7 @@ fn main() {
|
||||
sn call []
|
||||
sn let []
|
||||
sn letm []
|
||||
sn return []
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -362,6 +362,27 @@ fn completes_in_loop_ctx() {
|
||||
sn ppd
|
||||
"#]],
|
||||
);
|
||||
check_empty(
|
||||
r"fn my() { loop { foo.$0 } }",
|
||||
expect![[r#"
|
||||
sn box Box::new(expr)
|
||||
sn break break expr
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn if if expr {}
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn not !expr
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
sn while while expr {}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1115,9 +1136,11 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
@ -1139,9 +1162,11 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
@ -1167,9 +1192,11 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
@ -1191,9 +1218,11 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
@ -1215,9 +1244,11 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
@ -1238,11 +1269,13 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn if if expr {}
|
||||
sn match match expr {}
|
||||
sn not !expr
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
sn while while expr {}
|
||||
"#]],
|
||||
|
@ -29,11 +29,13 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
)
|
||||
@ -60,11 +62,13 @@ fn main() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
)
|
||||
@ -93,11 +97,13 @@ fn main() {}
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
)
|
||||
@ -126,11 +132,13 @@ fn main() {}
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
)
|
||||
|
@ -1157,11 +1157,13 @@ fn here_we_go() {
|
||||
sn call function(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn deref *expr
|
||||
sn let let
|
||||
sn letm let mut
|
||||
sn match match expr {}
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn return return expr
|
||||
sn unsafe unsafe {}
|
||||
"#]],
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! See [`FamousDefs`].
|
||||
|
||||
use base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase};
|
||||
use hir::{Crate, Enum, Macro, Module, ScopeDef, Semantics, Trait};
|
||||
use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Trait};
|
||||
|
||||
use crate::RootDatabase;
|
||||
|
||||
@ -110,6 +110,10 @@ impl FamousDefs<'_, '_> {
|
||||
self.find_macro("core:macros:builtin:derive")
|
||||
}
|
||||
|
||||
pub fn core_mem_drop(&self) -> Option<Function> {
|
||||
self.find_function("core:mem:drop")
|
||||
}
|
||||
|
||||
pub fn builtin_crates(&self) -> impl Iterator<Item = Crate> {
|
||||
IntoIterator::into_iter([
|
||||
self.std(),
|
||||
@ -149,6 +153,13 @@ impl FamousDefs<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_function(&self, path: &str) -> Option<Function> {
|
||||
match self.find_def(path)? {
|
||||
hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_lang_crate(&self, origin: LangCrateOrigin) -> Option<Crate> {
|
||||
let krate = self.1;
|
||||
let db = self.0.db;
|
||||
|
@ -328,7 +328,6 @@ pub mod convert {
|
||||
}
|
||||
|
||||
pub mod mem {
|
||||
// region:drop
|
||||
// region:manually_drop
|
||||
#[lang = "manually_drop"]
|
||||
#[repr(transparent)]
|
||||
@ -353,6 +352,7 @@ pub mod mem {
|
||||
|
||||
// endregion:manually_drop
|
||||
|
||||
// region:drop
|
||||
pub fn drop<T>(_x: T) {}
|
||||
pub const fn replace<T>(dest: &mut T, src: T) -> T {
|
||||
unsafe {
|
||||
|
Loading…
x
Reference in New Issue
Block a user