use crate::{helpers::PrettySequence, render, FmtWrite, PrettyBuilder, PrettyPrint, PrettyProvider, RenderAnnotated};
use alloc::{borrow::Cow, rc::Rc, string::String};
use color_ansi::AnsiStyle;
use core::{
fmt::{Debug, Formatter},
ops::{Add, AddAssign},
};
use std::io::Write;
use unicode_segmentation::UnicodeSegmentation;
mod display;
mod into;
pub enum PrettyTree {
Nil,
Hardline,
Text(Rc<str>),
StaticText(&'static str),
Annotated {
color: Rc<AnsiStyle>,
doc: Rc<Self>,
},
Append {
lhs: Rc<Self>,
rhs: Rc<Self>,
},
Group {
items: Rc<Self>,
},
MaybeInline {
block: Rc<Self>,
inline: Rc<Self>,
},
Nest {
space: isize,
doc: Rc<Self>,
},
RenderLen {
len: usize,
doc: Rc<Self>,
},
Union {
left: Rc<Self>,
right: Rc<Self>,
},
Column {
function: Rc<dyn Fn(usize) -> Self>,
},
Nesting {
function: Rc<dyn Fn(usize) -> Self>,
},
Fail,
}
#[allow(non_upper_case_globals)]
impl PrettyTree {
pub const Space: Self = PrettyTree::StaticText(" ");
#[inline]
pub fn line() -> Self {
Self::Hardline.flat_alt(Self::Space).into()
}
#[inline]
pub fn line_() -> Self {
Self::Hardline.flat_alt(Self::Nil).into()
}
}
impl PrettyTree {
#[inline]
pub fn text<U: Into<Cow<'static, str>>>(data: U) -> Self {
match data.into() {
Cow::Borrowed(s) => PrettyTree::StaticText(s),
Cow::Owned(s) => PrettyTree::Text(Rc::from(s)),
}
.with_utf8_len()
}
}
impl PrettyTree {
#[inline]
#[cfg(feature = "std")]
pub fn render<W>(&self, width: usize, out: &mut W) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
self.render_raw(width, &mut crate::IoWrite::new(out))
}
#[inline]
pub fn render_fmt<W>(&self, width: usize, out: &mut W) -> core::fmt::Result
where
W: ?Sized + core::fmt::Write,
{
self.render_raw(width, &mut FmtWrite::new(out))
}
#[inline]
pub fn render_raw<W>(&self, width: usize, out: &mut W) -> Result<(), W::Error>
where
W: RenderAnnotated,
W: ?Sized,
{
render::best(Rc::new(self.clone()), width, out)
}
}
impl PrettyTree {
#[inline]
#[cfg(feature = "std")]
pub fn render_colored<W: Write>(&self, width: usize, out: W) -> std::io::Result<()> {
render::best(Rc::new(self.clone()), width, &mut crate::TerminalWriter::new(out))
}
}
impl PrettyBuilder for PrettyTree {
#[inline]
fn flat_alt<E>(self, flat: E) -> Self
where
E: Into<PrettyTree>,
{
Self::MaybeInline { block: Rc::new(self), inline: Rc::new(flat.into()) }
}
#[inline]
fn indent(self, adjust: usize) -> Self {
let spaces = {
use crate::render::SPACES;
let mut doc = PrettyTree::Nil;
let mut remaining = adjust;
while remaining != 0 {
let i = SPACES.len().min(remaining);
remaining -= i;
doc = doc.append(PrettyTree::text(&SPACES[..i]))
}
doc
};
spaces.append(self).hang(adjust.try_into().unwrap())
}
}
impl PrettyPrint for PrettyTree {
fn pretty(&self, _: &PrettyProvider) -> PrettyTree {
self.clone()
}
}
impl PrettyTree {
fn with_utf8_len(self) -> Self {
let s = match &self {
Self::Text(s) => s.as_ref(),
Self::StaticText(s) => s,
_ => return self,
};
if s.is_ascii() {
self
}
else {
let grapheme_len = s.graphemes(true).count();
Self::RenderLen { len: grapheme_len, doc: Rc::new(self) }
}
}
#[inline]
pub fn append<E>(self, follow: E) -> Self
where
E: Into<PrettyTree>,
{
let rhs = follow.into();
match (&self, &rhs) {
(Self::Nil, _) => rhs,
(_, Self::Nil) => self,
_ => Self::Append { lhs: Rc::new(self), rhs: Rc::new(rhs) },
}
}
#[inline]
pub fn join<I, T1, T2>(terms: I, joint: T2) -> PrettyTree
where
I: IntoIterator<Item = T1>,
T1: Into<PrettyTree>,
T2: Into<PrettyTree>,
{
let joint = joint.into();
let mut iter = terms.into_iter().map(|s| s.into());
let mut terms = PrettySequence::new(0);
terms += iter.next().unwrap_or(PrettyTree::Nil);
for term in iter {
terms += joint.clone();
terms += term;
}
terms.into()
}
pub fn concat<I>(docs: I) -> Self
where
I: IntoIterator,
I::Item: Into<PrettyTree>,
{
let mut head = Self::Nil;
for item in docs.into_iter() {
head += item.into();
}
head
}
#[inline]
pub fn group(self) -> Self {
match self {
Self::Group { .. } | Self::Text(_) | Self::StaticText(_) | Self::Nil => self,
_ => Self::Group { items: Rc::new(self) },
}
}
#[inline]
pub fn nest(self, offset: isize) -> Self {
match self {
Self::Nil => {
return self;
}
_ => {}
}
if offset == 0 {
return self;
}
Self::Nest { space: offset, doc: Rc::new(self) }
}
#[inline]
pub fn annotate(self, style: Rc<AnsiStyle>) -> Self {
Self::Annotated { color: style, doc: Rc::new(self) }
}
#[inline]
pub fn union<E>(self, other: E) -> Self
where
E: Into<PrettyTree>,
{
Self::Union { left: Rc::new(self), right: Rc::new(other.into()) }
}
#[inline]
pub fn align(self) -> Self {
Self::Column {
function: Rc::new(move |col| {
let self_ = self.clone();
Self::Nesting { function: Rc::new(move |nest| self_.clone().nest(col as isize - nest as isize)) }
}),
}
}
#[inline]
pub fn hang(self, adjust: isize) -> Self {
self.nest(adjust).align()
}
#[inline]
pub fn width(self, f: impl Fn(isize) -> Self) -> Self {
todo!()
}
#[inline]
pub fn enclose<E, F>(self, before: E, after: F) -> Self
where
E: Into<Self>,
F: Into<Self>,
{
before.into().append(self).append(after.into())
}
pub fn single_quotes(self) -> Self {
self.enclose("'", "'")
}
pub fn double_quotes(self) -> Self {
self.enclose("\"", "\"")
}
pub fn parens(self) -> Self {
self.enclose("(", ")")
}
pub fn angles(self) -> Self {
self.enclose("<", ">")
}
pub fn braces(self) -> Self {
self.enclose("{", "}")
}
pub fn brackets(self) -> Self {
self.enclose("[", "]")
}
}