Simplify function rendering, remove constructor structs

This commit is contained in:
Lukas Wirth 2021-12-21 14:51:06 +01:00
parent 22b2c2fdf7
commit a720fc8e3a
3 changed files with 130 additions and 140 deletions

View File

@ -115,7 +115,7 @@ impl Completions {
if !ctx.is_visible(&func) {
return;
}
self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func));
self.add(render_fn(RenderContext::new(ctx), None, local_name, func));
}
pub(crate) fn add_method(
@ -128,7 +128,7 @@ impl Completions {
if !ctx.is_visible(&func) {
return;
}
self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
self.add(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext, konst: hir::Const) {

View File

@ -156,17 +156,16 @@ fn render_resolution_(
let _p = profile::span("render_resolution");
use hir::ModuleDef::*;
let db = ctx.db();
let kind = match resolution {
ScopeDef::ModuleDef(Function(func)) => {
return render_fn(ctx, import_to_add, Some(local_name), func)
return Some(render_fn(ctx, import_to_add, Some(local_name), func));
}
ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => {
return Some(render_variant(ctx, import_to_add, Some(local_name), var, None))
}
ScopeDef::MacroDef(mac) => {
let item = render_macro(ctx, import_to_add, local_name, mac);
return item;
return Some(render_variant(ctx, import_to_add, Some(local_name), var, None));
}
ScopeDef::MacroDef(mac) => return render_macro(ctx, import_to_add, local_name, mac),
ScopeDef::Unknown => {
let mut item = CompletionItem::new(
CompletionItemKind::UnresolvedReference,
@ -206,9 +205,9 @@ fn render_resolution_(
let local_name = local_name.to_smol_str();
let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone());
if let ScopeDef::Local(local) = resolution {
let ty = local.ty(ctx.db());
let ty = local.ty(db);
if !ty.is_unknown() {
item.detail(ty.display(ctx.db()).to_string());
item.detail(ty.display(db).to_string());
}
item.set_relevance(CompletionRelevance {
@ -224,15 +223,15 @@ fn render_resolution_(
};
// Add `<>` for generic types
if matches!(
let type_path_no_ty_args = matches!(
ctx.completion.path_context,
Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. })
) && ctx.completion.config.add_call_parenthesis
{
) && ctx.completion.config.add_call_parenthesis;
if type_path_no_ty_args {
if let Some(cap) = ctx.snippet_cap() {
let has_non_default_type_params = match resolution {
ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db()),
ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db()),
ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(db),
ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(db),
_ => false,
};
if has_non_default_type_params {
@ -243,7 +242,7 @@ fn render_resolution_(
}
}
}
item.set_documentation(scope_def_docs(ctx.db(), resolution))
item.set_documentation(scope_def_docs(db, resolution))
.set_deprecated(scope_def_is_deprecated(&ctx, resolution));
if let Some(import_to_add) = import_to_add {

View File

@ -1,11 +1,12 @@
//! Renderer for function calls.
use hir::{AsAssocItem, HirDisplay};
use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
use ide_db::SymbolKind;
use itertools::Itertools;
use stdx::format_to;
use crate::{
context::CompletionContext,
item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit},
render::{
builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match,
@ -13,14 +14,19 @@ use crate::{
},
};
enum FuncType {
Function,
Method(Option<hir::Name>),
}
pub(crate) fn render_fn(
ctx: RenderContext<'_>,
import_to_add: Option<ImportEdit>,
local_name: Option<hir::Name>,
fn_: hir::Function,
) -> Option<CompletionItem> {
func: hir::Function,
) -> CompletionItem {
let _p = profile::span("render_fn");
Some(FunctionRender::new(ctx, None, local_name, fn_, false)?.render(import_to_add))
render(ctx, local_name, func, FuncType::Function, import_to_add)
}
pub(crate) fn render_method(
@ -28,135 +34,120 @@ pub(crate) fn render_method(
import_to_add: Option<ImportEdit>,
receiver: Option<hir::Name>,
local_name: Option<hir::Name>,
fn_: hir::Function,
) -> Option<CompletionItem> {
let _p = profile::span("render_method");
Some(FunctionRender::new(ctx, receiver, local_name, fn_, true)?.render(import_to_add))
}
#[derive(Debug)]
struct FunctionRender<'a> {
ctx: RenderContext<'a>,
name: hir::Name,
receiver: Option<hir::Name>,
func: hir::Function,
is_method: bool,
) -> CompletionItem {
let _p = profile::span("render_method");
render(ctx, local_name, func, FuncType::Method(receiver), import_to_add)
}
impl<'a> FunctionRender<'a> {
fn new(
ctx: RenderContext<'a>,
receiver: Option<hir::Name>,
local_name: Option<hir::Name>,
fn_: hir::Function,
is_method: bool,
) -> Option<FunctionRender<'a>> {
let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()));
fn render(
ctx @ RenderContext { completion }: RenderContext<'_>,
local_name: Option<hir::Name>,
func: hir::Function,
func_type: FuncType,
import_to_add: Option<ImportEdit>,
) -> CompletionItem {
let db = completion.db;
Some(FunctionRender { ctx, name, receiver, func: fn_, is_method })
}
let name = local_name.unwrap_or_else(|| func.name(db));
let params = params(completion, func, &func_type);
fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
let params = self.params();
let call = match &self.receiver {
Some(receiver) => format!("{}.{}", receiver, &self.name),
None => self.name.to_string(),
};
let mut item = CompletionItem::new(self.kind(), self.ctx.source_range(), call.clone());
item.set_documentation(self.ctx.docs(self.func))
.set_deprecated(
self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
)
.detail(self.detail())
.add_call_parens(self.ctx.completion, call.clone(), params);
if import_to_add.is_none() {
let db = self.ctx.db();
if let Some(actm) = self.func.as_assoc_item(db) {
if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
item.trait_name(trt.name(db).to_smol_str());
}
}
}
if let Some(import_to_add) = import_to_add {
item.add_import(import_to_add);
}
item.lookup_by(self.name.to_smol_str());
let ret_type = self.func.ret_type(self.ctx.db());
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(self.ctx.completion, &ret_type),
exact_name_match: compute_exact_name_match(self.ctx.completion, &call),
..CompletionRelevance::default()
});
if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) {
// FIXME
// For now we don't properly calculate the edits for ref match
// completions on methods, so we've disabled them. See #8058.
if !self.is_method {
item.ref_match(ref_match);
}
}
item.build()
}
fn detail(&self) -> String {
let ret_ty = self.func.ret_type(self.ctx.db());
let mut detail = format!("fn({})", self.params_display());
if !ret_ty.is_unit() {
format_to!(detail, " -> {}", ret_ty.display(self.ctx.db()));
}
detail
}
fn params_display(&self) -> String {
if let Some(self_param) = self.func.self_param(self.ctx.db()) {
let params = self
.func
.assoc_fn_params(self.ctx.db())
.into_iter()
.skip(1) // skip the self param because we are manually handling that
.map(|p| p.ty().display(self.ctx.db()).to_string());
std::iter::once(self_param.display(self.ctx.db()).to_owned()).chain(params).join(", ")
} else {
let params = self
.func
.assoc_fn_params(self.ctx.db())
.into_iter()
.map(|p| p.ty().display(self.ctx.db()).to_string())
.join(", ");
params
}
}
fn params(&self) -> Params {
let (params, self_param) =
if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() {
(self.func.method_params(self.ctx.db()).unwrap_or_default(), None)
} else {
let self_param = self.func.self_param(self.ctx.db());
let mut assoc_params = self.func.assoc_fn_params(self.ctx.db());
if self_param.is_some() {
assoc_params.remove(0);
}
(assoc_params, self_param)
};
Params::Named(self_param, params)
}
fn kind(&self) -> CompletionItemKind {
if self.func.self_param(self.ctx.db()).is_some() {
// FIXME: SmolStr?
let call = match &func_type {
FuncType::Method(Some(receiver)) => format!("{}.{}", receiver, &name),
_ => name.to_string(),
};
let mut item = CompletionItem::new(
if func.self_param(db).is_some() {
CompletionItemKind::Method
} else {
SymbolKind::Function.into()
CompletionItemKind::SymbolKind(SymbolKind::Function)
},
ctx.source_range(),
call.clone(),
);
item.set_documentation(ctx.docs(func))
.set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
.detail(detail(db, func))
.add_call_parens(completion, call.clone(), params);
if import_to_add.is_none() {
if let Some(actm) = func.as_assoc_item(db) {
if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
item.trait_name(trt.name(db).to_smol_str());
}
}
}
if let Some(import_to_add) = import_to_add {
item.add_import(import_to_add);
}
item.lookup_by(name.to_smol_str());
let ret_type = func.ret_type(db);
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(completion, &ret_type),
exact_name_match: compute_exact_name_match(completion, &call),
..CompletionRelevance::default()
});
if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
// FIXME
// For now we don't properly calculate the edits for ref match
// completions on methods, so we've disabled them. See #8058.
if matches!(func_type, FuncType::Function) {
item.ref_match(ref_match);
}
}
item.build()
}
fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
let ret_ty = func.ret_type(db);
let mut detail = format!("fn({})", params_display(db, func));
if !ret_ty.is_unit() {
format_to!(detail, " -> {}", ret_ty.display(db));
}
detail
}
fn params_display(db: &dyn HirDatabase, func: hir::Function) -> String {
if let Some(self_param) = func.self_param(db) {
let assoc_fn_params = func.assoc_fn_params(db);
let params = assoc_fn_params
.iter()
.skip(1) // skip the self param because we are manually handling that
.map(|p| p.ty().display(db));
format!(
"{}{}",
self_param.display(db),
params.format_with("", |display, f| {
f(&", ")?;
f(&display)
})
)
} else {
let assoc_fn_params = func.assoc_fn_params(db);
assoc_fn_params.iter().map(|p| p.ty().display(db)).join(", ")
}
}
fn params(ctx: &CompletionContext<'_>, func: hir::Function, func_type: &FuncType) -> Params {
let (params, self_param) =
if ctx.has_dot_receiver() || matches!(func_type, FuncType::Method(Some(_))) {
(func.method_params(ctx.db).unwrap_or_default(), None)
} else {
let self_param = func.self_param(ctx.db);
let mut assoc_params = func.assoc_fn_params(ctx.db);
if self_param.is_some() {
assoc_params.remove(0);
}
(assoc_params, self_param)
};
Params::Named(self_param, params)
}
#[cfg(test)]