use hir::{db::HirDatabase, StructKind};
use ide_db::{
documentation::{Documentation, HasDocs},
SymbolKind,
};
use crate::{
context::{CompletionContext, PathCompletionCtx, PathKind},
item::{Builder, CompletionItem},
render::{
compute_type_match,
variant::{
format_literal_label, format_literal_lookup, render_record_lit, render_tuple_lit,
visible_fields, RenderedLiteral,
},
RenderContext,
},
CompletionItemKind, CompletionRelevance,
};
pub(crate) fn render_variant_lit(
ctx: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
local_name: Option<hir::Name>,
variant: hir::Variant,
path: Option<hir::ModPath>,
) -> Option<Builder> {
let _p = tracing::info_span!("render_variant_lit").entered();
let db = ctx.db();
let name = local_name.unwrap_or_else(|| variant.name(db));
render(ctx, path_ctx, Variant::EnumVariant(variant), name, path)
}
pub(crate) fn render_struct_literal(
ctx: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
strukt: hir::Struct,
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) -> Option<Builder> {
let _p = tracing::info_span!("render_struct_literal").entered();
let db = ctx.db();
let name = local_name.unwrap_or_else(|| strukt.name(db));
render(ctx, path_ctx, Variant::Struct(strukt), name, path)
}
fn render(
ctx @ RenderContext { completion, .. }: RenderContext<'_>,
path_ctx: &PathCompletionCtx,
thing: Variant,
name: hir::Name,
path: Option<hir::ModPath>,
) -> Option<Builder> {
let db = completion.db;
let mut kind = thing.kind(db);
let should_add_parens = !matches!(
path_ctx,
PathCompletionCtx { has_call_parens: true, .. }
| PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }
);
let fields = thing.fields(completion)?;
let (qualified_name, short_qualified_name, qualified) = match path {
Some(path) => {
let short = hir::ModPath::from_segments(
hir::PathKind::Plain,
path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
);
(path, short, true)
}
None => (name.clone().into(), name.into(), false),
};
let (qualified_name, escaped_qualified_name) = (
qualified_name.unescaped().display(ctx.db()).to_string(),
qualified_name.display(ctx.db(), completion.edition).to_string(),
);
let snippet_cap = ctx.snippet_cap();
let mut rendered = match kind {
StructKind::Tuple if should_add_parens => {
render_tuple_lit(db, snippet_cap, &fields, &escaped_qualified_name, completion.edition)
}
StructKind::Record if should_add_parens => {
render_record_lit(db, snippet_cap, &fields, &escaped_qualified_name, completion.edition)
}
_ => RenderedLiteral {
literal: escaped_qualified_name.clone(),
detail: escaped_qualified_name,
},
};
if snippet_cap.is_some() {
rendered.literal.push_str("$0");
}
if !should_add_parens {
kind = StructKind::Unit;
}
let label = format_literal_label(&qualified_name, kind, snippet_cap);
let lookup = if qualified {
format_literal_lookup(
&short_qualified_name.display(ctx.db(), completion.edition).to_string(),
kind,
)
} else {
format_literal_lookup(&qualified_name, kind)
};
let mut item = CompletionItem::new(
CompletionItemKind::SymbolKind(thing.symbol_kind()),
ctx.source_range(),
label,
completion.edition,
);
item.lookup_by(lookup);
item.detail(rendered.detail);
match snippet_cap {
Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal).trigger_call_info(),
None => item.insert_text(rendered.literal),
};
item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx));
let ty = thing.ty(db);
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(ctx.completion, &ty),
..ctx.completion_relevance()
});
super::path_ref_match(completion, path_ctx, &ty, &mut item);
if let Some(import_to_add) = ctx.import_to_add {
item.add_import(import_to_add);
}
Some(item)
}
#[derive(Clone, Copy)]
enum Variant {
Struct(hir::Struct),
EnumVariant(hir::Variant),
}
impl Variant {
fn fields(self, ctx: &CompletionContext<'_>) -> Option<Vec<hir::Field>> {
let fields = match self {
Variant::Struct(it) => it.fields(ctx.db),
Variant::EnumVariant(it) => it.fields(ctx.db),
};
let (visible_fields, fields_omitted) = match self {
Variant::Struct(it) => visible_fields(ctx, &fields, it)?,
Variant::EnumVariant(it) => visible_fields(ctx, &fields, it)?,
};
if !fields_omitted {
Some(visible_fields)
} else {
None
}
}
fn kind(self, db: &dyn HirDatabase) -> StructKind {
match self {
Variant::Struct(it) => it.kind(db),
Variant::EnumVariant(it) => it.kind(db),
}
}
fn symbol_kind(self) -> SymbolKind {
match self {
Variant::Struct(_) => SymbolKind::Struct,
Variant::EnumVariant(_) => SymbolKind::Variant,
}
}
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
match self {
Variant::Struct(it) => it.docs(db),
Variant::EnumVariant(it) => it.docs(db),
}
}
fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool {
match self {
Variant::Struct(it) => ctx.is_deprecated(it),
Variant::EnumVariant(it) => ctx.is_deprecated(it),
}
}
fn ty(self, db: &dyn HirDatabase) -> hir::Type {
match self {
Variant::Struct(it) => it.ty(db),
Variant::EnumVariant(it) => it.parent_enum(db).ty(db),
}
}
}