diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs new file mode 100644 index 000000000..003a0c11e --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs @@ -0,0 +1,96 @@ +//! Code common to structs, unions, and enum variants. + +use crate::context::CompletionContext; +use hir::{db::HirDatabase, HasAttrs, HasCrate, HasVisibility, HirDisplay, StructKind}; +use ide_db::SnippetCap; +use itertools::Itertools; +use syntax::SmolStr; + +/// 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 RenderedLiteral { + pub(crate) literal: String, + pub(crate) 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_lit( + db: &dyn HirDatabase, + snippet_cap: Option<SnippetCap>, + fields: &[hir::Field], + path: &str, +) -> RenderedLiteral { + let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { + if snippet_cap.is_some() { + f(&format_args!("{}: ${{{}:()}}", field.name(db).escaped(), idx + 1)) + } else { + f(&format_args!("{}: ()", field.name(db).escaped())) + } + }); + + let types = fields.iter().format_with(", ", |field, f| { + f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db))) + }); + + RenderedLiteral { + literal: format!("{} {{ {} }}", path, completions), + detail: format!("{} {{ {} }}", path, types), + } +} + +/// 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_lit( + db: &dyn HirDatabase, + snippet_cap: Option<SnippetCap>, + fields: &[hir::Field], + path: &str, +) -> RenderedLiteral { + let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| { + if snippet_cap.is_some() { + f(&format_args!("${{{}:()}}", idx + 1)) + } else { + f(&format_args!("()")) + } + }); + + let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db))); + + RenderedLiteral { + literal: format!("{}({})", path, completions), + detail: format!("{}({})", path, types), + } +} + +/// Find all the visible fields in a given list. Returns the list of visible +/// fields, plus a boolean for whether the list is comprehensive (contains no +/// private fields and its item is not marked `#[non_exhaustive]`). +pub(crate) fn visible_fields( + ctx: &CompletionContext<'_>, + fields: &[hir::Field], + item: impl HasAttrs + HasCrate + Copy, +) -> Option<(Vec<hir::Field>, bool)> { + let module = ctx.module; + let n_fields = fields.len(); + let fields = fields + .iter() + .filter(|field| field.is_visible_from(ctx.db, module)) + .copied() + .collect::<Vec<_>>(); + let has_invisible_field = n_fields - fields.len() > 0; + let is_foreign_non_exhaustive = item.attrs(ctx.db).by_key("non_exhaustive").exists() + && item.krate(ctx.db) != module.krate(); + let fields_omitted = has_invisible_field || is_foreign_non_exhaustive; + Some((fields, fields_omitted)) +} + +/// Format a struct, etc. literal option for display in the completions menu. +pub(crate) fn format_literal_label(name: &str, kind: StructKind) -> SmolStr { + match kind { + StructKind::Tuple => SmolStr::from_iter([name, "(…)"]), + StructKind::Record => SmolStr::from_iter([name, " {…}"]), + StructKind::Unit => name.into(), + } +} |