rust-analyzer/crates/ide-completion/src/render/literal.rs
Wilfred Hughes c981ff0944 fix: Tracing span names should match function names
When viewing traces, it's slightly confusing when the span name doesn't
match the function name. Ensure the names are consistent.

(It might be worth moving most of these to use #[tracing::instrument]
so the name can never go stale. @davidbarsky suggested that is marginally
slower, so I've just done the simple change here.)
2024-04-30 11:22:47 -07:00

198 lines
5.9 KiB
Rust

//! Renderer for `enum` variants.
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::span!(tracing::Level::INFO, "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::span!(tracing::Level::INFO, "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()).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,
},
};
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 label = format_literal_label(&qualified_name, kind, snippet_cap);
let lookup = if qualified {
format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind)
} else {
format_literal_lookup(&qualified_name, kind)
};
let mut item = CompletionItem::new(
CompletionItemKind::SymbolKind(thing.symbol_kind()),
ctx.source_range(),
label,
);
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),
}
}
}