use rlsp_fmt::{Doc, concat, flat_alt, group, hard_line, indent, join, line, text};
use rlsp_yaml_parser::CollectionStyle;
use rlsp_yaml_parser::Span;
use rlsp_yaml_parser::node::Node;
use super::options::YamlFormatOptions;
use super::scalar_render::{format_tag, is_core_schema_tag};
pub(super) fn sequence_to_doc(
seq: &[Node<Span>],
style: CollectionStyle,
options: &YamlFormatOptions,
) -> Doc {
if seq.is_empty() {
return text("[]");
}
let effective_style = if options.format_enforce_block_style {
CollectionStyle::Block
} else {
style
};
match effective_style {
CollectionStyle::Flow => flow_sequence_to_doc(seq, options),
CollectionStyle::Block => {
let items: Vec<Doc> = seq
.iter()
.map(|item| sequence_item_to_doc(item, options))
.collect();
join(&hard_line(), items)
}
}
}
pub(super) fn flow_sequence_to_doc(seq: &[Node<Span>], options: &YamlFormatOptions) -> Doc {
let items: Vec<Doc> = seq
.iter()
.map(|item| super::node_to_doc::flow_item_to_doc(item, options, false))
.collect();
let sep = concat(vec![text(","), line()]);
let inner = join(&sep, items);
group(concat(vec![
text("["),
indent(concat(vec![flat_alt(text(""), line()), inner])),
flat_alt(text(""), line()),
text("]"),
]))
}
pub(super) fn sequence_item_to_doc(item: &Node<Span>, options: &YamlFormatOptions) -> Doc {
let effective_style = |style: CollectionStyle| {
if options.format_enforce_block_style {
CollectionStyle::Block
} else {
style
}
};
let item_doc = match item {
Node::Mapping {
entries,
style,
tag,
..
} if !entries.is_empty() && effective_style(*style) == CollectionStyle::Block => {
let pairs: Vec<Doc> = entries
.iter()
.map(|(k, v)| super::mapping_render::key_value_to_doc(k, v, options))
.collect();
let inner = join(&hard_line(), pairs);
let user_tag = tag.as_ref().filter(|t| !is_core_schema_tag(t));
let prefix = match (item.anchor(), user_tag) {
(Some(name), Some(t)) => format!("&{name} {}", format_tag(t)),
(Some(name), None) => format!("&{name}"),
(None, Some(t)) => format_tag(t),
(None, None) => String::new(),
};
if prefix.is_empty() {
concat(vec![text("- "), indent(inner)])
} else {
concat(vec![
text("- "),
text(prefix),
indent(concat(vec![hard_line(), inner])),
])
}
}
Node::Sequence {
items, style, tag, ..
} if !items.is_empty() && effective_style(*style) == CollectionStyle::Block => {
let user_tag = tag.as_ref().filter(|t| !is_core_schema_tag(t));
let prefix_doc = match (item.anchor(), user_tag) {
(Some(name), Some(t)) => text(format!("&{name} {}", format_tag(t))),
(Some(name), None) => text(format!("&{name}")),
(None, Some(t)) => text(format_tag(t)),
(None, None) => text(String::new()),
};
concat(vec![
text("- "),
prefix_doc,
indent(concat(vec![
hard_line(),
sequence_to_doc(items, *style, options),
])),
])
}
Node::Scalar { .. } | Node::Mapping { .. } | Node::Sequence { .. } | Node::Alias { .. } => {
concat(vec![
text("- "),
super::node_to_doc::node_to_doc(item, options, false),
])
}
};
let item_doc = if let Some(tc) = item.trailing_comment() {
concat(vec![item_doc, text(format!(" {tc}"))])
} else {
item_doc
};
let leading = item.leading_comments();
if leading.is_empty() {
item_doc
} else {
let mut parts: Vec<Doc> = Vec::new();
for lc in leading {
parts.push(text(lc.clone()));
parts.push(hard_line());
}
parts.push(item_doc);
concat(parts)
}
}