mod builder;
mod render;
pub use builder::{hardline, hardspace, line, line_prime, newline};
#[cfg(any(test, feature = "debug-dump"))]
pub use render::fixup;
pub use render::{RenderConfig, render_with_config};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Spacing {
Softbreak,
Break,
Hardspace,
Softspace,
Space,
Hardline,
Emptyline,
Newlines(usize),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GroupAnn {
RegularG,
Priority,
Transparent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextAnn {
RegularT,
Comment,
TrailingComment,
Trailing,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DocE {
Text(usize, usize, TextAnn, String),
Spacing(Spacing),
Group(GroupAnn, Doc),
Nest(isize, isize),
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Doc(pub(crate) Vec<DocE>);
impl Doc {
pub const fn new() -> Self {
Self(Vec::new())
}
pub fn push_raw(&mut self, e: DocE) -> &mut Self {
self.0.push(e);
self
}
}
impl std::ops::Deref for Doc {
type Target = [DocE];
fn deref(&self) -> &[DocE] {
&self.0
}
}
impl IntoIterator for Doc {
type Item = DocE;
type IntoIter = std::vec::IntoIter<DocE>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Extend<DocE> for Doc {
fn extend<I: IntoIterator<Item = DocE>>(&mut self, iter: I) {
self.0.extend(iter);
}
}
impl From<Vec<DocE>> for Doc {
fn from(v: Vec<DocE>) -> Self {
Self(v)
}
}
#[cfg(any(test, feature = "debug-dump"))]
#[derive(Debug)]
pub struct IR(pub(crate) Doc);
pub trait Pretty {
fn pretty(&self, doc: &mut Doc);
}
impl Pretty for Doc {
fn pretty(&self, doc: &mut Doc) {
doc.0.extend_from_slice(&self.0);
}
}
impl<T: Pretty> Pretty for Option<T> {
fn pretty(&self, doc: &mut Doc) {
if let Some(x) = self {
x.pretty(doc);
}
}
}
pub fn text_width(s: &str) -> usize {
s.chars().count()
}
pub fn unexpand_spacing_prime(mut limit: Option<i32>, doc: &[DocE]) -> Option<Doc> {
let mut result = Vec::new();
let mut stack: Vec<std::slice::Iter<'_, DocE>> = vec![doc.iter()];
while let Some(iter) = stack.last_mut() {
let Some(elem) = iter.next() else {
stack.pop();
continue;
};
match elem {
DocE::Text(_, _, _, t) => {
if let Some(n) = limit.as_mut() {
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
{
*n -= text_width(t) as i32;
}
}
result.push(elem.clone());
}
DocE::Spacing(Spacing::Hardspace | Spacing::Space | Spacing::Softspace) => {
if let Some(n) = limit.as_mut() {
*n -= 1;
}
result.push(DocE::Spacing(Spacing::Hardspace));
}
DocE::Spacing(Spacing::Break | Spacing::Softbreak) => {}
DocE::Spacing(_) => return None,
DocE::Nest(..) => result.push(elem.clone()),
DocE::Group(_, inner) => stack.push(inner.iter()),
}
if matches!(limit, Some(n) if n < 0) {
return None;
}
}
Some(Doc(result))
}