Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide_completion/src/render/compound.rs')
| -rw-r--r-- | crates/ide_completion/src/render/compound.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/crates/ide_completion/src/render/compound.rs b/crates/ide_completion/src/render/compound.rs new file mode 100644 index 0000000000..586bb92a8e --- /dev/null +++ b/crates/ide_completion/src/render/compound.rs @@ -0,0 +1,93 @@ +//! Code common to structs, unions, and enum variants. + +use crate::render::RenderContext; +use hir::{db::HirDatabase, HasAttrs, HasVisibility, HirDisplay}; +use ide_db::SnippetCap; +use itertools::Itertools; + +/// A rendered struct, union, or enum variant, split into fields for actual +/// auto-completion (`literal`, using `field: ()`) and display in the +/// completions menu (`detail`, using `field: type`). +pub(crate) struct RenderedCompound { + pub literal: String, + pub detail: String, +} + +/// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for +/// the `name` argument for an anonymous type. +pub(crate) fn render_record( + db: &dyn HirDatabase, + snippet_cap: Option<SnippetCap>, + fields: &[hir::Field], + name: Option<&str>, +) -> RenderedCompound { + let fields = fields.iter(); + + let (completions, types): (Vec<_>, Vec<_>) = fields + .enumerate() + .map(|(idx, field)| { + ( + if snippet_cap.is_some() { + format!("{}: ${{{}:()}}", field.name(db), idx + 1) + } else { + format!("{}: ()", field.name(db)) + }, + format!("{}: {}", field.name(db), field.ty(db).display(db)), + ) + }) + .unzip(); + RenderedCompound { + literal: format!("{} {{ {} }}", name.unwrap_or(""), completions.iter().format(", ")), + detail: format!("{} {{ {} }}", name.unwrap_or(""), types.iter().format(", ")), + } +} + +/// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for +/// the `name` argument for an anonymous type. +pub(crate) fn render_tuple( + db: &dyn HirDatabase, + snippet_cap: Option<SnippetCap>, + fields: &[hir::Field], + name: Option<&str>, +) -> RenderedCompound { + let fields = fields.iter(); + + let (completions, types): (Vec<_>, Vec<_>) = fields + .enumerate() + .map(|(idx, field)| { + ( + if snippet_cap.is_some() { + format!("${{{}:()}}", (idx + 1).to_string()) + } else { + "()".to_string() + }, + field.ty(db).display(db).to_string(), + ) + }) + .unzip(); + RenderedCompound { + literal: format!("{}({})", name.unwrap_or(""), completions.iter().format(", ")), + detail: format!("{}({})", name.unwrap_or(""), types.iter().format(", ")), + } +} + +/// Find all the visible fields in a `HasAttrs`. Returns the list of visible +/// fields, plus a boolean for whether the list is comprehensive (contains no +/// private fields and is not marked `#[non_exhaustive]`). +pub(crate) fn visible_fields( + ctx: &RenderContext<'_>, + fields: &[hir::Field], + item: impl HasAttrs, +) -> Option<(Vec<hir::Field>, bool)> { + let module = ctx.completion.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)) +} |