mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-29 02:52:11 +00:00
Add smart completions that skip await or iter() and into_iter()
E.g. complete `await.foo()`.
This commit is contained in:
parent
8364ef2997
commit
cec9fa1606
7 changed files with 230 additions and 72 deletions
|
|
@ -58,8 +58,7 @@ use hir_def::{
|
||||||
CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId,
|
CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId,
|
||||||
GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId,
|
GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId,
|
||||||
LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
|
LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
|
||||||
SyntheticSyntax, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId,
|
SyntheticSyntax, TraitAliasId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
|
||||||
UnionId,
|
|
||||||
};
|
};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, RenderedExpandError,
|
attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, RenderedExpandError,
|
||||||
|
|
@ -128,7 +127,7 @@ pub use {
|
||||||
ImportPathConfig,
|
ImportPathConfig,
|
||||||
// FIXME: This is here since some queries take it as input that are used
|
// FIXME: This is here since some queries take it as input that are used
|
||||||
// outside of hir.
|
// outside of hir.
|
||||||
ModuleDefId,
|
{ModuleDefId, TraitId},
|
||||||
},
|
},
|
||||||
hir_expand::{
|
hir_expand::{
|
||||||
attrs::{Attr, AttrId},
|
attrs::{Attr, AttrId},
|
||||||
|
|
@ -4749,6 +4748,14 @@ impl Type {
|
||||||
Some((self.derived(ty.clone()), m))
|
Some((self.derived(ty.clone()), m))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_reference(&self, mutability: Mutability) -> Type {
|
||||||
|
let ty_mutability = match mutability {
|
||||||
|
Mutability::Shared => hir_ty::Mutability::Not,
|
||||||
|
Mutability::Mut => hir_ty::Mutability::Mut,
|
||||||
|
};
|
||||||
|
self.derived(TyKind::Ref(ty_mutability, error_lifetime(), self.ty.clone()).intern(Interner))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_slice(&self) -> bool {
|
pub fn is_slice(&self) -> bool {
|
||||||
matches!(self.ty.kind(Interner), TyKind::Slice(..))
|
matches!(self.ty.kind(Interner), TyKind::Slice(..))
|
||||||
}
|
}
|
||||||
|
|
@ -4804,9 +4811,9 @@ impl Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that particular type `ty` implements `std::future::IntoFuture` or
|
/// Checks that particular type `ty` implements `std::future::IntoFuture` or
|
||||||
/// `std::future::Future`.
|
/// `std::future::Future` and returns the `Output` associated type.
|
||||||
/// This function is used in `.await` syntax completion.
|
/// This function is used in `.await` syntax completion.
|
||||||
pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool {
|
pub fn into_future_output(&self, db: &dyn HirDatabase) -> Option<Type> {
|
||||||
let trait_ = db
|
let trait_ = db
|
||||||
.lang_item(self.env.krate, LangItem::IntoFutureIntoFuture)
|
.lang_item(self.env.krate, LangItem::IntoFutureIntoFuture)
|
||||||
.and_then(|it| {
|
.and_then(|it| {
|
||||||
|
|
@ -4818,16 +4825,18 @@ impl Type {
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let future_trait = db.lang_item(self.env.krate, LangItem::Future)?;
|
let future_trait = db.lang_item(self.env.krate, LangItem::Future)?;
|
||||||
future_trait.as_trait()
|
future_trait.as_trait()
|
||||||
});
|
})?;
|
||||||
|
|
||||||
let trait_ = match trait_ {
|
|
||||||
Some(it) => it,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let canonical_ty =
|
let canonical_ty =
|
||||||
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
||||||
method_resolution::implements_trait(&canonical_ty, db, &self.env, trait_)
|
if !method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, trait_) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let output_assoc_type = db
|
||||||
|
.trait_data(trait_)
|
||||||
|
.associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))?;
|
||||||
|
self.normalize_trait_assoc_type(db, &[], output_assoc_type.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This does **not** resolve `IntoFuture`, only `Future`.
|
/// This does **not** resolve `IntoFuture`, only `Future`.
|
||||||
|
|
@ -4846,6 +4855,26 @@ impl Type {
|
||||||
self.normalize_trait_assoc_type(db, &[], iterator_item.into())
|
self.normalize_trait_assoc_type(db, &[], iterator_item.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
|
||||||
|
let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| {
|
||||||
|
let into_iter_fn = it.as_function()?;
|
||||||
|
let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?;
|
||||||
|
let into_iter_trait = assoc_item.container_or_implemented_trait(db)?;
|
||||||
|
Some(into_iter_trait.id)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let canonical_ty =
|
||||||
|
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
||||||
|
if !method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, trait_) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let into_iter_assoc_type = db
|
||||||
|
.trait_data(trait_)
|
||||||
|
.associated_type_by_name(&Name::new_symbol_root(sym::IntoIter.clone()))?;
|
||||||
|
self.normalize_trait_assoc_type(db, &[], into_iter_assoc_type.into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks that particular type `ty` implements `std::ops::FnOnce`.
|
/// Checks that particular type `ty` implements `std::ops::FnOnce`.
|
||||||
///
|
///
|
||||||
/// This function can be used to check if a particular type is callable, since FnOnce is a
|
/// This function can be used to check if a particular type is callable, since FnOnce is a
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,7 @@ impl Completions {
|
||||||
ctx: &CompletionContext<'_>,
|
ctx: &CompletionContext<'_>,
|
||||||
dot_access: &DotAccess,
|
dot_access: &DotAccess,
|
||||||
func: hir::Function,
|
func: hir::Function,
|
||||||
receiver: Option<hir::Name>,
|
receiver: Option<SmolStr>,
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
) {
|
) {
|
||||||
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
|
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
|
||||||
|
|
@ -475,7 +475,7 @@ impl Completions {
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &CompletionContext<'_>,
|
ctx: &CompletionContext<'_>,
|
||||||
dot_access: &DotAccess,
|
dot_access: &DotAccess,
|
||||||
receiver: Option<hir::Name>,
|
receiver: Option<SmolStr>,
|
||||||
field: hir::Field,
|
field: hir::Field,
|
||||||
ty: &hir::Type,
|
ty: &hir::Type,
|
||||||
) {
|
) {
|
||||||
|
|
@ -533,7 +533,7 @@ impl Completions {
|
||||||
pub(crate) fn add_tuple_field(
|
pub(crate) fn add_tuple_field(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &CompletionContext<'_>,
|
ctx: &CompletionContext<'_>,
|
||||||
receiver: Option<hir::Name>,
|
receiver: Option<SmolStr>,
|
||||||
field: usize,
|
field: usize,
|
||||||
ty: &hir::Type,
|
ty: &hir::Type,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use hir::{sym, HasContainer, ItemContainer, MethodCandidateCallback, Name};
|
use hir::{HasContainer, ItemContainer, MethodCandidateCallback, Name};
|
||||||
use ide_db::FxHashSet;
|
use ide_db::FxHashSet;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
|
|
@ -25,8 +25,13 @@ pub(crate) fn complete_dot(
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
|
||||||
|
let is_method_access_with_parens =
|
||||||
|
matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
|
||||||
|
let traits_in_scope = ctx.traits_in_scope();
|
||||||
|
|
||||||
// Suggest .await syntax for types that implement Future trait
|
// Suggest .await syntax for types that implement Future trait
|
||||||
if receiver_ty.impls_into_future(ctx.db) {
|
if let Some(future_output) = receiver_ty.into_future_output(ctx.db) {
|
||||||
let mut item = CompletionItem::new(
|
let mut item = CompletionItem::new(
|
||||||
CompletionItemKind::Keyword,
|
CompletionItemKind::Keyword,
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
|
|
@ -35,11 +40,37 @@ pub(crate) fn complete_dot(
|
||||||
);
|
);
|
||||||
item.detail("expr.await");
|
item.detail("expr.await");
|
||||||
item.add_to(acc, ctx.db);
|
item.add_to(acc, ctx.db);
|
||||||
}
|
|
||||||
|
|
||||||
let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
|
// Completions that skip `.await`, e.g. `.await.foo()`.
|
||||||
let is_method_access_with_parens =
|
let dot_access_kind = match &dot_access.kind {
|
||||||
matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
|
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
|
||||||
|
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
|
||||||
|
}
|
||||||
|
it @ DotAccessKind::Method { .. } => *it,
|
||||||
|
};
|
||||||
|
let dot_access = DotAccess {
|
||||||
|
receiver: dot_access.receiver.clone(),
|
||||||
|
receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }),
|
||||||
|
kind: dot_access_kind,
|
||||||
|
ctx: dot_access.ctx,
|
||||||
|
};
|
||||||
|
complete_fields(
|
||||||
|
acc,
|
||||||
|
ctx,
|
||||||
|
&future_output,
|
||||||
|
|acc, field, ty| {
|
||||||
|
acc.add_field(ctx, &dot_access, Some(SmolStr::new_static("await")), field, &ty)
|
||||||
|
},
|
||||||
|
|acc, field, ty| {
|
||||||
|
acc.add_tuple_field(ctx, Some(SmolStr::new_static("await")), field, &ty)
|
||||||
|
},
|
||||||
|
is_field_access,
|
||||||
|
is_method_access_with_parens,
|
||||||
|
);
|
||||||
|
complete_methods(ctx, &future_output, &traits_in_scope, |func| {
|
||||||
|
acc.add_method(ctx, &dot_access, func, Some(SmolStr::new_static("await")), None)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
complete_fields(
|
complete_fields(
|
||||||
acc,
|
acc,
|
||||||
|
|
@ -50,8 +81,41 @@ pub(crate) fn complete_dot(
|
||||||
is_field_access,
|
is_field_access,
|
||||||
is_method_access_with_parens,
|
is_method_access_with_parens,
|
||||||
);
|
);
|
||||||
|
complete_methods(ctx, receiver_ty, &traits_in_scope, |func| {
|
||||||
|
acc.add_method(ctx, dot_access, func, None, None)
|
||||||
|
});
|
||||||
|
|
||||||
complete_methods(ctx, receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None));
|
// Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
|
||||||
|
// its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
|
||||||
|
let iter = receiver_ty
|
||||||
|
.strip_references()
|
||||||
|
.add_reference(hir::Mutability::Shared)
|
||||||
|
.into_iterator_iter(ctx.db)
|
||||||
|
.map(|ty| (ty, SmolStr::new_static("iter()")))
|
||||||
|
.or_else(|| {
|
||||||
|
receiver_ty
|
||||||
|
.clone()
|
||||||
|
.into_iterator_iter(ctx.db)
|
||||||
|
.map(|ty| (ty, SmolStr::new_static("into_iter()")))
|
||||||
|
});
|
||||||
|
if let Some((iter, iter_sym)) = iter {
|
||||||
|
// Skip iterators, e.g. complete `.iter().filter_map()`.
|
||||||
|
let dot_access_kind = match &dot_access.kind {
|
||||||
|
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
|
||||||
|
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
|
||||||
|
}
|
||||||
|
it @ DotAccessKind::Method { .. } => *it,
|
||||||
|
};
|
||||||
|
let dot_access = DotAccess {
|
||||||
|
receiver: dot_access.receiver.clone(),
|
||||||
|
receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }),
|
||||||
|
kind: dot_access_kind,
|
||||||
|
ctx: dot_access.ctx,
|
||||||
|
};
|
||||||
|
complete_methods(ctx, &iter, &traits_in_scope, |func| {
|
||||||
|
acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete_undotted_self(
|
pub(crate) fn complete_undotted_self(
|
||||||
|
|
@ -94,18 +158,16 @@ pub(crate) fn complete_undotted_self(
|
||||||
in_breakable: expr_ctx.in_breakable,
|
in_breakable: expr_ctx.in_breakable,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Some(Name::new_symbol_root(sym::self_.clone())),
|
Some(SmolStr::new_static("self")),
|
||||||
field,
|
field,
|
||||||
&ty,
|
&ty,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|acc, field, ty| {
|
|acc, field, ty| acc.add_tuple_field(ctx, Some(SmolStr::new_static("self")), field, &ty),
|
||||||
acc.add_tuple_field(ctx, Some(Name::new_symbol_root(sym::self_.clone())), field, &ty)
|
|
||||||
},
|
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
complete_methods(ctx, &ty, |func| {
|
complete_methods(ctx, &ty, &ctx.traits_in_scope(), |func| {
|
||||||
acc.add_method(
|
acc.add_method(
|
||||||
ctx,
|
ctx,
|
||||||
&DotAccess {
|
&DotAccess {
|
||||||
|
|
@ -118,7 +180,7 @@ pub(crate) fn complete_undotted_self(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
func,
|
func,
|
||||||
Some(Name::new_symbol_root(sym::self_.clone())),
|
Some(SmolStr::new_static("self")),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
@ -160,6 +222,7 @@ fn complete_fields(
|
||||||
fn complete_methods(
|
fn complete_methods(
|
||||||
ctx: &CompletionContext<'_>,
|
ctx: &CompletionContext<'_>,
|
||||||
receiver: &hir::Type,
|
receiver: &hir::Type,
|
||||||
|
traits_in_scope: &FxHashSet<hir::TraitId>,
|
||||||
f: impl FnMut(hir::Function),
|
f: impl FnMut(hir::Function),
|
||||||
) {
|
) {
|
||||||
struct Callback<'a, F> {
|
struct Callback<'a, F> {
|
||||||
|
|
@ -205,7 +268,7 @@ fn complete_methods(
|
||||||
receiver.iterate_method_candidates_split_inherent(
|
receiver.iterate_method_candidates_split_inherent(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
&ctx.scope,
|
&ctx.scope,
|
||||||
&ctx.traits_in_scope(),
|
traits_in_scope,
|
||||||
Some(ctx.module),
|
Some(ctx.module),
|
||||||
None,
|
None,
|
||||||
Callback { ctx, f, seen_methods: FxHashSet::default() },
|
Callback { ctx, f, seen_methods: FxHashSet::default() },
|
||||||
|
|
@ -1306,4 +1369,73 @@ fn baz() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn skip_iter() {
|
||||||
|
check_no_kw(
|
||||||
|
r#"
|
||||||
|
//- minicore: iterator
|
||||||
|
fn foo() {
|
||||||
|
[].$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
me clone() (as Clone) fn(&self) -> Self
|
||||||
|
me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check_no_kw(
|
||||||
|
r#"
|
||||||
|
//- minicore: iterator
|
||||||
|
struct MyIntoIter;
|
||||||
|
impl IntoIterator for MyIntoIter {
|
||||||
|
type Item = ();
|
||||||
|
type IntoIter = MyIterator;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
MyIterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyIterator;
|
||||||
|
impl Iterator for MyIterator {
|
||||||
|
type Item = ();
|
||||||
|
fn next(&mut self) -> Self::Item {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
MyIntoIter.$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
|
||||||
|
me into_iter().by_ref() (as Iterator) fn(&mut self) -> &mut Self
|
||||||
|
me into_iter().into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
|
||||||
|
me into_iter().next() (as Iterator) fn(&mut self) -> Option<<Self as Iterator>::Item>
|
||||||
|
me into_iter().nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn skip_await() {
|
||||||
|
check_no_kw(
|
||||||
|
r#"
|
||||||
|
//- minicore: future
|
||||||
|
struct Foo;
|
||||||
|
impl Foo {
|
||||||
|
fn foo(self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn foo() -> Foo { Foo }
|
||||||
|
|
||||||
|
async fn bar() {
|
||||||
|
foo().$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
me await.foo() fn(self)
|
||||||
|
me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -390,7 +390,7 @@ pub(crate) struct DotAccess {
|
||||||
pub(crate) ctx: DotAccessExprCtx,
|
pub(crate) ctx: DotAccessExprCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) enum DotAccessKind {
|
pub(crate) enum DotAccessKind {
|
||||||
Field {
|
Field {
|
||||||
/// True if the receiver is an integer and there is no ident in the original file after it yet
|
/// True if the receiver is an integer and there is no ident in the original file after it yet
|
||||||
|
|
@ -402,7 +402,7 @@ pub(crate) enum DotAccessKind {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub(crate) struct DotAccessExprCtx {
|
pub(crate) struct DotAccessExprCtx {
|
||||||
pub(crate) in_block_expr: bool,
|
pub(crate) in_block_expr: bool,
|
||||||
pub(crate) in_breakable: BreakableKind,
|
pub(crate) in_breakable: BreakableKind,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ use ide_db::{
|
||||||
imports::import_assets::LocatedImport,
|
imports::import_assets::LocatedImport,
|
||||||
RootDatabase, SnippetCap, SymbolKind,
|
RootDatabase, SnippetCap, SymbolKind,
|
||||||
};
|
};
|
||||||
use syntax::{ast, format_smolstr, AstNode, Edition, SmolStr, SyntaxKind, TextRange, ToSmolStr};
|
use syntax::{ast, format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange, ToSmolStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext},
|
context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext},
|
||||||
|
|
@ -122,7 +122,7 @@ impl<'a> RenderContext<'a> {
|
||||||
pub(crate) fn render_field(
|
pub(crate) fn render_field(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
dot_access: &DotAccess,
|
dot_access: &DotAccess,
|
||||||
receiver: Option<hir::Name>,
|
receiver: Option<SmolStr>,
|
||||||
field: hir::Field,
|
field: hir::Field,
|
||||||
ty: &hir::Type,
|
ty: &hir::Type,
|
||||||
) -> CompletionItem {
|
) -> CompletionItem {
|
||||||
|
|
@ -136,7 +136,7 @@ pub(crate) fn render_field(
|
||||||
let mut item = CompletionItem::new(
|
let mut item = CompletionItem::new(
|
||||||
SymbolKind::Field,
|
SymbolKind::Field,
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
field_with_receiver(db, receiver.as_ref(), &name, ctx.completion.edition),
|
field_with_receiver(receiver.as_deref(), &name),
|
||||||
ctx.completion.edition,
|
ctx.completion.edition,
|
||||||
);
|
);
|
||||||
item.set_relevance(CompletionRelevance {
|
item.set_relevance(CompletionRelevance {
|
||||||
|
|
@ -158,8 +158,7 @@ pub(crate) fn render_field(
|
||||||
|
|
||||||
builder.replace(
|
builder.replace(
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
field_with_receiver(db, receiver.as_ref(), &escaped_name, ctx.completion.edition)
|
field_with_receiver(receiver.as_deref(), &escaped_name).into(),
|
||||||
.into(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected_fn_type =
|
let expected_fn_type =
|
||||||
|
|
@ -183,12 +182,7 @@ pub(crate) fn render_field(
|
||||||
|
|
||||||
item.text_edit(builder.finish());
|
item.text_edit(builder.finish());
|
||||||
} else {
|
} else {
|
||||||
item.insert_text(field_with_receiver(
|
item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name));
|
||||||
db,
|
|
||||||
receiver.as_ref(),
|
|
||||||
&escaped_name,
|
|
||||||
ctx.completion.edition,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if let Some(receiver) = &dot_access.receiver {
|
if let Some(receiver) = &dot_access.receiver {
|
||||||
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
|
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
|
||||||
|
|
@ -201,33 +195,21 @@ pub(crate) fn render_field(
|
||||||
item.build(db)
|
item.build(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field_with_receiver(
|
fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr {
|
||||||
db: &RootDatabase,
|
receiver
|
||||||
receiver: Option<&hir::Name>,
|
.map_or_else(|| field_name.into(), |receiver| format_smolstr!("{}.{field_name}", receiver))
|
||||||
field_name: &str,
|
|
||||||
edition: Edition,
|
|
||||||
) -> SmolStr {
|
|
||||||
receiver.map_or_else(
|
|
||||||
|| field_name.into(),
|
|
||||||
|receiver| format_smolstr!("{}.{field_name}", receiver.display(db, edition)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn render_tuple_field(
|
pub(crate) fn render_tuple_field(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
receiver: Option<hir::Name>,
|
receiver: Option<SmolStr>,
|
||||||
field: usize,
|
field: usize,
|
||||||
ty: &hir::Type,
|
ty: &hir::Type,
|
||||||
) -> CompletionItem {
|
) -> CompletionItem {
|
||||||
let mut item = CompletionItem::new(
|
let mut item = CompletionItem::new(
|
||||||
SymbolKind::Field,
|
SymbolKind::Field,
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
field_with_receiver(
|
field_with_receiver(receiver.as_deref(), &field.to_string()),
|
||||||
ctx.db(),
|
|
||||||
receiver.as_ref(),
|
|
||||||
&field.to_string(),
|
|
||||||
ctx.completion.edition,
|
|
||||||
),
|
|
||||||
ctx.completion.edition,
|
ctx.completion.edition,
|
||||||
);
|
);
|
||||||
item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string())
|
item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string())
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum FuncKind<'ctx> {
|
enum FuncKind<'ctx> {
|
||||||
Function(&'ctx PathCompletionCtx),
|
Function(&'ctx PathCompletionCtx),
|
||||||
Method(&'ctx DotAccess, Option<hir::Name>),
|
Method(&'ctx DotAccess, Option<SmolStr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn render_fn(
|
pub(crate) fn render_fn(
|
||||||
|
|
@ -39,7 +39,7 @@ pub(crate) fn render_fn(
|
||||||
pub(crate) fn render_method(
|
pub(crate) fn render_method(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
dot_access: &DotAccess,
|
dot_access: &DotAccess,
|
||||||
receiver: Option<hir::Name>,
|
receiver: Option<SmolStr>,
|
||||||
local_name: Option<hir::Name>,
|
local_name: Option<hir::Name>,
|
||||||
func: hir::Function,
|
func: hir::Function,
|
||||||
) -> Builder {
|
) -> Builder {
|
||||||
|
|
@ -59,16 +59,8 @@ fn render(
|
||||||
|
|
||||||
let (call, escaped_call) = match &func_kind {
|
let (call, escaped_call) = match &func_kind {
|
||||||
FuncKind::Method(_, Some(receiver)) => (
|
FuncKind::Method(_, Some(receiver)) => (
|
||||||
format_smolstr!(
|
format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())),
|
||||||
"{}.{}",
|
format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)),
|
||||||
receiver.unescaped().display(ctx.db()),
|
|
||||||
name.unescaped().display(ctx.db())
|
|
||||||
),
|
|
||||||
format_smolstr!(
|
|
||||||
"{}.{}",
|
|
||||||
receiver.display(ctx.db(), completion.edition),
|
|
||||||
name.display(ctx.db(), completion.edition)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_ => (
|
_ => (
|
||||||
name.unescaped().display(db).to_smolstr(),
|
name.unescaped().display(db).to_smolstr(),
|
||||||
|
|
|
||||||
|
|
@ -1510,7 +1510,7 @@ pub mod iter {
|
||||||
impl<T, const N: usize> IntoIterator for [T; N] {
|
impl<T, const N: usize> IntoIterator for [T; N] {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
type IntoIter = IntoIter<T, N>;
|
type IntoIter = IntoIter<T, N>;
|
||||||
fn into_iter(self) -> I {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
IntoIter { data: self, range: IndexRange { start: 0, end: loop {} } }
|
IntoIter { data: self, range: IndexRange { start: 0, end: loop {} } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1520,6 +1520,29 @@ pub mod iter {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub struct Iter<'a, T> {
|
||||||
|
slice: &'a [T],
|
||||||
|
}
|
||||||
|
impl<'a, T> IntoIterator for &'a [T; N] {
|
||||||
|
type Item = &'a T;
|
||||||
|
type IntoIter = Iter<'a, T>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, T> IntoIterator for &'a [T] {
|
||||||
|
type Item = &'a T;
|
||||||
|
type IntoIter = Iter<'a, T>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, T> Iterator for Iter<'a, T> {
|
||||||
|
type Item = &'a T;
|
||||||
|
fn next(&mut self) -> Option<T> {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub use self::collect::IntoIterator;
|
pub use self::collect::IntoIterator;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue