Add completion for struct literal in which all fields are visible.
Fix ide_completion tests. Move 'complete_record_literal' call to the main completion function. Fix a rendering bug when snippet not available. Checks if an expression is expected before adding completion for struct literal. Move 'completion struct literal with private field' test to 'expressions.rs' test file. Update 'expect' tests with new check in 'complete record literal'.
This commit is contained in:
parent
8a8431133e
commit
9beefef8f9
6 changed files with 259 additions and 0 deletions
121
crates/ide_completion/src/render/struct_literal.rs
Normal file
121
crates/ide_completion/src/render/struct_literal.rs
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
//! Renderer for `struct` literal.
|
||||
|
||||
use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
|
||||
use ide_db::helpers::SnippetCap;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind};
|
||||
|
||||
pub(crate) fn render_struct_literal(
|
||||
ctx: RenderContext<'_>,
|
||||
strukt: hir::Struct,
|
||||
local_name: Option<Name>,
|
||||
) -> Option<CompletionItem> {
|
||||
let _p = profile::span("render_struct_literal");
|
||||
|
||||
let fields = strukt.fields(ctx.db());
|
||||
let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
|
||||
|
||||
if fields_omitted {
|
||||
// If some fields are private you can't make `struct` literal.
|
||||
return None;
|
||||
}
|
||||
|
||||
let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string();
|
||||
let literal = render_literal(&ctx, &name, strukt.kind(ctx.db()), &visible_fields)?;
|
||||
|
||||
Some(build_completion(ctx, name, literal, strukt))
|
||||
}
|
||||
|
||||
fn build_completion(
|
||||
ctx: RenderContext<'_>,
|
||||
name: String,
|
||||
literal: String,
|
||||
def: impl HasAttrs + Copy,
|
||||
) -> CompletionItem {
|
||||
let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name + " {…}");
|
||||
item.kind(CompletionItemKind::Snippet)
|
||||
.set_documentation(ctx.docs(def))
|
||||
.set_deprecated(ctx.is_deprecated(def))
|
||||
.detail(&literal);
|
||||
if let Some(snippet_cap) = ctx.snippet_cap() {
|
||||
item.insert_snippet(snippet_cap, literal);
|
||||
} else {
|
||||
item.insert_text(literal);
|
||||
};
|
||||
item.build()
|
||||
}
|
||||
|
||||
fn render_literal(
|
||||
ctx: &RenderContext<'_>,
|
||||
name: &str,
|
||||
kind: StructKind,
|
||||
fields: &[hir::Field],
|
||||
) -> Option<String> {
|
||||
let mut literal = match kind {
|
||||
StructKind::Tuple if ctx.snippet_cap().is_some() => render_tuple_as_literal(fields, name),
|
||||
StructKind::Record => render_record_as_literal(ctx.db(), ctx.snippet_cap(), fields, name),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if ctx.completion.is_param {
|
||||
literal.push(':');
|
||||
literal.push(' ');
|
||||
literal.push_str(name);
|
||||
}
|
||||
if ctx.snippet_cap().is_some() {
|
||||
literal.push_str("$0");
|
||||
}
|
||||
Some(literal)
|
||||
}
|
||||
|
||||
fn render_record_as_literal(
|
||||
db: &dyn HirDatabase,
|
||||
snippet_cap: Option<SnippetCap>,
|
||||
fields: &[hir::Field],
|
||||
name: &str,
|
||||
) -> String {
|
||||
let fields = fields.iter();
|
||||
if snippet_cap.is_some() {
|
||||
format!(
|
||||
"{name} {{ {} }}",
|
||||
fields
|
||||
.enumerate()
|
||||
.map(|(idx, field)| format!("{}: ${{{}:()}}", field.name(db), idx + 1))
|
||||
.format(", "),
|
||||
name = name
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{name} {{ {} }}",
|
||||
fields.map(|field| format!("{}: ()", field.name(db))).format(", "),
|
||||
name = name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_tuple_as_literal(fields: &[hir::Field], name: &str) -> String {
|
||||
format!(
|
||||
"{name}({})",
|
||||
fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
|
||||
name = name
|
||||
)
|
||||
}
|
||||
|
||||
fn visible_fields(
|
||||
ctx: &RenderContext<'_>,
|
||||
fields: &[hir::Field],
|
||||
item: impl HasAttrs,
|
||||
) -> Option<(Vec<hir::Field>, bool)> {
|
||||
let module = ctx.completion.scope.module()?;
|
||||
let n_fields = fields.len();
|
||||
let fields = fields
|
||||
.iter()
|
||||
.filter(|field| field.is_visible_from(ctx.db(), module))
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let fields_omitted =
|
||||
n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
|
||||
Some((fields, fields_omitted))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue