192 lines
5.8 KiB
Rust
192 lines
5.8 KiB
Rust
//! Renderer for `enum` variants.
|
|
|
|
use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
|
|
use ide_db::SymbolKind;
|
|
use syntax::AstNode;
|
|
|
|
use crate::{
|
|
context::{CompletionContext, PathCompletionCtx, PathKind},
|
|
item::{Builder, CompletionItem},
|
|
render::{
|
|
compute_ref_match, compute_type_match,
|
|
variant::{
|
|
format_literal_label, 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 = profile::span("render_enum_variant");
|
|
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 = profile::span("render_struct_literal");
|
|
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 = match &path_ctx {
|
|
PathCompletionCtx { has_call_parens: true, .. } => false,
|
|
PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. } => false,
|
|
_ => true,
|
|
};
|
|
|
|
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.to_string(), qualified_name.escaped().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)
|
|
}
|
|
StructKind::Record if should_add_parens => {
|
|
render_record_lit(db, snippet_cap, &fields, &escaped_qualified_name)
|
|
}
|
|
_ => RenderedLiteral {
|
|
literal: escaped_qualified_name.clone(),
|
|
detail: escaped_qualified_name.clone(),
|
|
},
|
|
};
|
|
|
|
if snippet_cap.is_some() {
|
|
rendered.literal.push_str("$0");
|
|
}
|
|
|
|
// only show name in label if not adding parens
|
|
if !should_add_parens {
|
|
kind = StructKind::Unit;
|
|
}
|
|
|
|
let mut item = CompletionItem::new(
|
|
CompletionItemKind::SymbolKind(thing.symbol_kind()),
|
|
ctx.source_range(),
|
|
format_literal_label(&qualified_name, kind),
|
|
);
|
|
|
|
item.detail(rendered.detail);
|
|
|
|
match snippet_cap {
|
|
Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
|
|
None => item.insert_text(rendered.literal),
|
|
};
|
|
|
|
if qualified {
|
|
item.lookup_by(format_literal_label(&short_qualified_name.to_string(), kind));
|
|
}
|
|
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()
|
|
});
|
|
if let Some(ref_match) = compute_ref_match(completion, &ty) {
|
|
item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
|
|
}
|
|
|
|
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),
|
|
}
|
|
}
|
|
}
|