pretty printing

This commit is contained in:
Jana Dönszelmann 2026-04-04 21:37:51 +02:00
parent 52a80cfb0e
commit c867ad379e
No known key found for this signature in database
30 changed files with 1533 additions and 387 deletions

View file

@ -1,11 +1,11 @@
[package]
name = "pretty-print"
version = "0.1.8"
authors = ["Aster <192607617@qq.com>"]
name = "logparse-pretty-print"
version = "0.1.0"
authors = ["Aster <192607617@qq.com>", "Jana Dönszelmann <cratesio@donsz.nl>"]
description = "pretty print tree"
repository = "https://github.com/oovm/pretty-print"
documentation = "https://docs.rs/pretty-print"
readme = "Readme.md"
repository = "https://git.donsz.nl/jana/logviewer"
documentation = "https://docs.rs/logparse-pretty-print"
readme = "../../Readme.md"
license = "MPL-2.0"
edition = "2021"
@ -14,12 +14,9 @@ unicode-segmentation = "1.10.1"
[dependencies.color-ansi]
version = "0.1.0"
#default-features = false
#path = 'C:\Users\Dell\CLionProjects\color-rs\projects\color-ansi'
[dev-dependencies]
[features]
default = ["std"]
std = []
std = []

View file

@ -1,3 +1,7 @@
use std::fmt::Debug;
use crate::Text;
use super::*;
/// A soft block is a block that is not required to be on a new line.
@ -10,8 +14,8 @@ use super::*;
/// b,
/// }
/// ```
#[derive(Clone, Debug)]
pub struct HardBlock {
#[derive(Clone)]
pub struct HardBlock<'a, T> {
/// The indentation of the soft block
pub indent: usize,
/// The left hand side of the soft block
@ -19,10 +23,21 @@ pub struct HardBlock {
/// The right hand side of the soft block
pub rhs: &'static str,
/// The joint node of the soft block
pub joint: PrettyTree,
pub joint: PrettyTree<'a, T>,
}
impl HardBlock {
impl<'a, T: Text<'a> + Debug> Debug for HardBlock<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HardBlock")
.field("indent", &self.indent)
.field("lhs", &self.lhs)
.field("rhs", &self.rhs)
.field("joint", &self.joint)
.finish()
}
}
impl<'a, T: Text<'a>> HardBlock<'a, T> {
/// Build a new soft block
pub fn new(lhs: &'static str, rhs: &'static str) -> Self {
Self { lhs, rhs, indent: 4, joint: PrettyTree::line_or_space() }
@ -40,14 +55,14 @@ impl HardBlock {
Self::new("{", "}")
}
/// Set the joint node of the soft block
pub fn with_joint(self, joint: PrettyTree) -> Self {
pub fn with_joint(self, joint: PrettyTree<'a, T>) -> Self {
Self { joint, ..self }
}
}
impl HardBlock {
impl<'a, T: Clone + Text<'a>> HardBlock<'a, T> {
/// Join a slice of pretty printables with the soft block
pub fn join_slice<T: PrettyPrint>(&self, slice: &[T], theme: &PrettyProvider) -> PrettyTree {
pub fn join_slice<P: PrettyPrint<'a, T>>(&self, slice: &[P], theme: &PrettyProvider) -> PrettyTree<'a, T> {
let mut outer = PrettySequence::new(5);
outer += self.lhs;
outer += PrettyTree::Hardline;

View file

@ -1,5 +1,5 @@
use super::*;
use crate::PrettyBuilder;
use crate::{PrettyBuilder, Text};
/// `K & R` style brackets
///
@ -28,15 +28,15 @@ impl KAndRBracket {
Self { head_space: true, bracket_l: "{", bracket_r: "}" }
}
/// Build a bracketed block
pub fn build<'a, I>(
pub fn build<'b, 'a, I, T: Text<'a>>(
&self,
items: &[I],
allocator: &'a PrettyProvider,
inline_join: PrettyTree,
block_join: PrettyTree,
) -> PrettyTree
allocator: &'b PrettyProvider,
inline_join: PrettyTree<'a, T>,
block_join: PrettyTree<'a, T>,
) -> PrettyTree<'a, T>
where
I: PrettyPrint,
I: PrettyPrint<'a, T>,
{
let mut output = PrettySequence::new(5);
if self.head_space {

View file

@ -1,60 +1,70 @@
use std::fmt::Debug;
use crate::Text;
use super::*;
/// The document sequence type.
#[derive(Clone, Debug, Default)]
pub struct PrettySequence {
items: Vec<PrettyTree>,
#[derive(Clone, Default)]
pub struct PrettySequence<'a, T> {
items: Vec<PrettyTree<'a, T>>,
}
impl PrettySequence {
impl<'a, T: Text<'a> + Debug> Debug for PrettySequence<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PrettySequence").field("items", &self.items).finish()
}
}
impl<'a, T> PrettySequence<'a, T> {
/// Create a new sequence with the given capacity.
pub fn new(capacity: usize) -> Self {
Self { items: Vec::with_capacity(capacity) }
}
/// Create a new sequence with the given capacity.
pub fn push<T>(&mut self, item: T)
pub fn push<U>(&mut self, item: U)
where
T: Into<PrettyTree>,
U: Into<PrettyTree<'a, T>>,
{
self.items.push(item.into());
}
/// Create a new sequence with the given capacity.
pub fn extend<I, T>(&mut self, items: I)
pub fn extend<I, S>(&mut self, items: I)
where
I: IntoIterator<Item = T>,
T: Into<PrettyTree>,
I: IntoIterator<Item = S>,
S: Into<PrettyTree<'a, T>>,
{
self.items.extend(items.into_iter().map(|x| x.into()));
}
}
impl PrettyBuilder for PrettySequence {
fn flat_alt<E>(self, flat: E) -> PrettyTree
impl<'a, T: Text<'a>> PrettyBuilder<'a, T> for PrettySequence<'a, T> {
fn flat_alt<E>(self, flat: E) -> PrettyTree<'a, T>
where
E: Into<PrettyTree>,
E: Into<PrettyTree<'a, T>>,
{
PrettyTree::from(self).flat_alt(flat)
}
fn indent(self, indent: usize) -> PrettyTree {
fn indent(self, indent: usize) -> PrettyTree<'a, T> {
PrettyTree::from(self).indent(indent)
}
fn nest(self, offset: isize) -> PrettyTree {
fn nest(self, offset: isize) -> PrettyTree<'a, T> {
PrettyTree::from(self).nest(offset)
}
}
impl<T> AddAssign<T> for PrettySequence
impl<'a, U, T> AddAssign<U> for PrettySequence<'a, T>
where
T: Into<PrettyTree>,
U: Into<PrettyTree<'a, T>>,
{
fn add_assign(&mut self, rhs: T) {
fn add_assign(&mut self, rhs: U) {
self.push(rhs);
}
}
impl From<PrettySequence> for PrettyTree {
fn from(value: PrettySequence) -> Self {
impl<'a, T: Text<'a>> From<PrettySequence<'a, T>> for PrettyTree<'a, T> {
fn from(value: PrettySequence<'a, T>) -> Self {
Self::concat(value.items)
}
}

View file

@ -1,3 +1,7 @@
use std::fmt::Debug;
use crate::Text;
use super::*;
/// A soft block is a block that is not required to be on a new line.
@ -10,8 +14,8 @@ use super::*;
/// b,
/// }
/// ```
#[derive(Clone, Debug)]
pub struct SoftBlock {
#[derive(Clone)]
pub struct SoftBlock<'a, T> {
/// The indentation of the soft block
pub indent: usize,
/// The left hand side of the soft block
@ -19,12 +23,24 @@ pub struct SoftBlock {
/// The right hand side of the soft block
pub rhs: &'static str,
/// The joint node of the soft block
pub joint: PrettyTree,
pub joint: PrettyTree<'a, T>,
/// The tail node of the soft block
pub tail: PrettyTree,
pub tail: PrettyTree<'a, T>,
}
impl SoftBlock {
impl<'a, T: Text<'a> + Debug> Debug for SoftBlock<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SoftBlock")
.field("indent", &self.indent)
.field("lhs", &self.lhs)
.field("rhs", &self.rhs)
.field("joint", &self.joint)
.field("tail", &self.tail)
.finish()
}
}
impl<'a, T: Text<'a>> SoftBlock<'a, T> {
/// Build a new soft block
pub fn new(lhs: &'static str, rhs: &'static str) -> Self {
Self { lhs, rhs, indent: 4, joint: PrettyTree::line_or_space(), tail: PrettyTree::Nil }
@ -46,14 +62,14 @@ impl SoftBlock {
Self::new("{", "}")
}
/// Set the joint node of the soft block
pub fn with_joint(self, joint: PrettyTree) -> Self {
pub fn with_joint(self, joint: PrettyTree<'a, T>) -> Self {
Self { joint, ..self }
}
}
impl SoftBlock {
impl<'a, T: Text<'a> + Clone> SoftBlock<'a, T> {
/// Join a slice of pretty printables with the soft block
pub fn join_slice<T: PrettyPrint>(&self, slice: &[T], theme: &PrettyProvider) -> PrettyTree {
pub fn join_slice<U: PrettyPrint<'a, T>>(&self, slice: &[U], theme: &PrettyProvider) -> PrettyTree<'a, T> {
let mut outer = PrettySequence::new(5);
outer += self.lhs;
outer += PrettyTree::line_or_space();

View file

@ -11,21 +11,23 @@ extern crate core;
pub mod helpers;
mod providers;
mod render;
mod text;
mod traits;
mod tree;
pub use self::render::{
write_fmt::{BufferWrite, FmtWrite},
PrettyFormatter, Render, RenderAnnotated,
write_fmt::{BufferWrite, FmtWrite},
};
#[cfg(feature = "std")]
pub use crate::render::write_io::{IoWrite, TerminalWriter};
pub use crate::render::write_io::{IoWrite, TerminalWriter, VecWrite};
pub use crate::{
providers::PrettyProvider,
traits::{printer::PrettyPrint, PrettyBuilder},
traits::{PrettyBuilder, printer::PrettyPrint},
tree::PrettyTree,
};
pub use color_ansi::*;
pub use text::Text;
/// Concatenates a number of documents (or values that can be converted into a document via the
/// `Pretty` trait, like `&str`)

View file

@ -1,5 +1,5 @@
use crate::{PrettyPrint, PrettyTree};
use alloc::{borrow::Cow, rc::Rc};
use crate::{PrettyPrint, PrettyTree, Text};
use alloc::rc::Rc;
use color_ansi::AnsiStyle;
use core::fmt::{Debug, Formatter};
@ -57,112 +57,110 @@ impl PrettyProvider {
self.width = width;
}
/// Gets the width of the document.
pub fn text<S>(&self, text: S) -> PrettyTree
pub fn text<'a, S, T>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text)
}
/// Gets the width of the document.
pub fn custom<S>(&self, text: S, style: Rc<AnsiStyle>) -> PrettyTree
pub fn custom<'a, S, T: Text<'a>>(&self, text: S, style: Rc<AnsiStyle>) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(style)
}
/// Allocate a document containing the given text.
pub fn keyword<S>(&self, text: S) -> PrettyTree
pub fn keyword<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.keyword.clone())
}
/// Allocate a document containing the given text.
pub fn identifier<S>(&self, text: S) -> PrettyTree
pub fn identifier<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.operator.clone())
}
/// Allocate a document containing the given text.
pub fn generic<S>(&self, text: S) -> PrettyTree
pub fn generic<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.macros.clone())
}
/// Allocate a document containing the given text.
pub fn variable<S>(&self, text: S, mutable: bool) -> PrettyTree
pub fn variable<'a, S, T: Text<'a>>(&self, text: S, mutable: bool) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
if mutable {
PrettyTree::text(text).annotate(self.local_mut.clone())
}
else {
} else {
PrettyTree::text(text).annotate(self.local.clone())
}
}
/// Allocate a document containing the given text.
pub fn argument<S>(&self, text: S, mutable: bool) -> PrettyTree
pub fn argument<'a, S, T: Text<'a>>(&self, text: S, mutable: bool) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
if mutable {
PrettyTree::text(text).annotate(self.argument_mut.clone())
}
else {
} else {
PrettyTree::text(text).annotate(self.argument.clone())
}
}
/// Allocate a document containing the given text.
pub fn operator<S>(&self, text: S) -> PrettyTree
pub fn operator<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.operator.clone())
}
/// Allocate a document containing the given text.
pub fn string<S>(&self, text: S) -> PrettyTree
pub fn string<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.string.clone())
}
/// Allocate a document containing the given text.
pub fn annotation<S>(&self, text: S) -> PrettyTree
pub fn annotation<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.macros.clone())
}
/// Allocate a document containing the given text.
pub fn number<S>(&self, text: S) -> PrettyTree
pub fn number<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.number.clone())
}
/// Allocate a document containing the given text.
pub fn structure<S>(&self, text: S) -> PrettyTree
pub fn structure<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.structure.clone())
}
/// Allocate a document containing the given text.
pub fn variant<S>(&self, text: S) -> PrettyTree
pub fn variant<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.variant.clone())
}
/// Allocate a document containing the given text.
pub fn interface<S>(&self, text: S) -> PrettyTree
pub fn interface<'a, S, T: Text<'a>>(&self, text: S) -> PrettyTree<'a, T>
where
S: Into<Cow<'static, str>>,
S: Into<T>,
{
PrettyTree::text(text).annotate(self.interface.clone())
}
@ -178,11 +176,11 @@ impl PrettyProvider {
/// let theme = PrettyProvider::new(80);
/// theme.join(vec!["a", "b", "c"], ", ");
/// ```
pub fn join<I, T1, T2>(&self, iter: I, joint: T2) -> PrettyTree
pub fn join<'a, I, T1, T2, T: Text<'a>>(&self, iter: I, joint: T2) -> PrettyTree<'a, T>
where
I: IntoIterator<Item = T1>,
T1: PrettyPrint,
T2: PrettyPrint,
T1: PrettyPrint<'a, T>,
T2: PrettyPrint<'a, T>,
{
PrettyTree::join(iter.into_iter().map(|x| x.pretty(self)), joint.pretty(self))
}
@ -195,10 +193,10 @@ impl PrettyProvider {
/// let theme = PrettyProvider::new(80);
/// theme.join(&["a", "b", "c"], ", ");
/// ```
pub fn join_slice<I, T>(&self, iter: &[I], joint: T) -> PrettyTree
pub fn join_slice<'a, I, U, T: Text<'a>>(&self, iter: &[I], joint: U) -> PrettyTree<'a, T>
where
I: PrettyPrint,
T: PrettyPrint,
I: PrettyPrint<'a, T>,
U: PrettyPrint<'a, T>,
{
PrettyTree::join(iter.iter().map(|s| s.pretty(self)), joint.pretty(self))
}
@ -211,10 +209,10 @@ impl PrettyProvider {
/// let theme = PrettyProvider::new(80);
/// theme.concat(vec!["1", "2", "3"]);
/// ```
pub fn concat<I, T>(&self, iter: I) -> PrettyTree
pub fn concat<'a, I, U, T: Text<'a>>(&self, iter: I) -> PrettyTree<'a, T>
where
I: IntoIterator<Item = T>,
T: PrettyPrint,
I: IntoIterator<Item = U>,
U: PrettyPrint<'a, T>,
{
PrettyTree::concat(iter.into_iter().map(|x| x.pretty(self)))
}
@ -227,9 +225,9 @@ impl PrettyProvider {
/// let theme = PrettyProvider::new(80);
/// theme.concat_slice(&["1", "2", "3"]);
/// ```
pub fn concat_slice<T>(&self, iter: &[T]) -> PrettyTree
pub fn concat_slice<'a, U, T: Text<'a>>(&self, iter: &[U]) -> PrettyTree<'a, T>
where
T: PrettyPrint,
U: PrettyPrint<'a, T>,
{
PrettyTree::concat(iter.iter().map(|s| s.pretty(self)))
}

View file

@ -1,7 +1,10 @@
use crate::{BufferWrite, PrettyTree};
use crate::{BufferWrite, PrettyTree, Text};
use alloc::{rc::Rc, vec, vec::Vec};
use color_ansi::AnsiStyle;
use core::fmt::{Debug, Display, Formatter};
use core::{
fmt::{Debug, Display, Formatter},
slice,
};
#[cfg(feature = "std")]
pub mod write_io;
@ -9,40 +12,36 @@ pub mod write_io;
pub mod write_fmt;
/// Trait representing the operations necessary to render a document
pub trait Render {
pub trait Render<'a, T> {
/// The type of the output
type Error;
/// Write a string to the output
fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;
/// Write to the output
fn write_all(&mut self, s: &[T]) -> Result<(), Self::Error>;
/// Write a character to the output
fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
while !s.is_empty() {
let count = self.write_str(s)?;
s = &s[count..];
}
Ok(())
}
/// Write a character to the output
/// Emit an error
fn fail_doc(&self) -> Self::Error;
}
/// The given text, which must not contain line breaks.
#[derive(Debug)]
pub struct PrettyFormatter<'a> {
tree: &'a PrettyTree,
pub struct PrettyFormatter<'b, 'a, T> {
tree: &'b PrettyTree<'a, T>,
width: usize,
}
impl<'a> Display for PrettyFormatter<'a> {
impl<'a, 'b, T: Text<'a> + Debug> Debug for PrettyFormatter<'b, 'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PrettyFormatter").field("tree", &self.tree).field("width", &self.width).finish()
}
}
impl<'a, 'b, T: Text<'a>> Display for PrettyFormatter<'b, 'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
self.tree.render_fmt(self.width, f)
}
}
impl PrettyTree {
impl<'a, T> PrettyTree<'a, T> {
/// Returns a value which implements `std::fmt::Display`
///
/// ```
@ -52,13 +51,13 @@ impl PrettyTree {
/// assert_eq!(format!("{}", doc.pretty(80)), "hello world");
/// ```
#[inline]
pub fn pretty(&self, width: usize) -> PrettyFormatter<'_> {
pub fn pretty(&self, width: usize) -> PrettyFormatter<'_, 'a, T> {
PrettyFormatter { tree: self, width }
}
}
/// Trait representing the operations necessary to write an annotated document.
pub trait RenderAnnotated: Render {
pub trait RenderAnnotated<'a, T>: Render<'a, T> {
/// Push an annotation onto the stack
fn push_annotation(&mut self, annotation: Rc<AnsiStyle>) -> Result<(), Self::Error>;
/// Pop an annotation from the stack
@ -78,13 +77,20 @@ macro_rules! make_spaces {
pub(crate) const SPACES: &str = make_spaces!(,,,,,,,,,,);
fn append_docs2(ldoc: Rc<PrettyTree>, rdoc: Rc<PrettyTree>, mut consumer: impl FnMut(Rc<PrettyTree>)) -> Rc<PrettyTree> {
fn append_docs2<'a, T>(
ldoc: Rc<PrettyTree<'a, T>>,
rdoc: Rc<PrettyTree<'a, T>>,
mut consumer: impl FnMut(Rc<PrettyTree<'a, T>>),
) -> Rc<PrettyTree<'a, T>> {
let d = append_docs(rdoc, &mut consumer);
consumer(d);
append_docs(ldoc, &mut consumer)
}
fn append_docs(mut doc: Rc<PrettyTree>, consumer: &mut impl FnMut(Rc<PrettyTree>)) -> Rc<PrettyTree> {
fn append_docs<'a, T>(
mut doc: Rc<PrettyTree<'a, T>>,
consumer: &mut impl FnMut(Rc<PrettyTree<'a, T>>),
) -> Rc<PrettyTree<'a, T>> {
loop {
// Since appended documents often appear in sequence on the left side we
// gain a slight performance increase by batching these pushes (avoiding
@ -100,9 +106,9 @@ fn append_docs(mut doc: Rc<PrettyTree>, consumer: &mut impl FnMut(Rc<PrettyTree>
}
}
pub fn best<W>(doc: Rc<PrettyTree>, width: usize, out: &mut W) -> Result<(), W::Error>
pub fn best<'a, W, T: Text<'a>>(doc: Rc<PrettyTree<'a, T>>, width: usize, out: &mut W) -> Result<(), W::Error>
where
W: RenderAnnotated,
W: RenderAnnotated<'a, T>,
W: ?Sized,
{
Best {
@ -123,43 +129,44 @@ enum Mode {
Flat,
}
struct RenderCommand {
struct RenderCommand<'a, T> {
indent: usize,
mode: Mode,
node: Rc<PrettyTree>,
node: Rc<PrettyTree<'a, T>>,
}
fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
fn write_newline<'a, W, T: Text<'a>>(ind: usize, out: &mut W) -> Result<(), W::Error>
where
W: ?Sized + Render,
W: ?Sized + Render<'a, T>,
{
out.write_str_all("\n")?;
out.write_all(&[T::newline()])?;
write_spaces(ind, out)
}
fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
fn write_spaces<'a, W, T: Text<'a>>(spaces: usize, out: &mut W) -> Result<(), W::Error>
where
W: ?Sized + Render,
W: ?Sized + Render<'a, T>,
{
let mut inserted = 0;
while inserted < spaces {
let insert = core::cmp::min(SPACES.len(), spaces - inserted);
inserted += out.write_str(&SPACES[..insert])?;
out.write_all(&[T::from_static_spaces(&SPACES[..insert])])?;
inserted += insert;
}
Ok(())
}
struct Best {
struct Best<'a, T> {
pos: usize,
back_cmds: Vec<RenderCommand>,
front_cmds: Vec<Rc<PrettyTree>>,
back_cmds: Vec<RenderCommand<'a, T>>,
front_cmds: Vec<Rc<PrettyTree<'a, T>>>,
annotation_levels: Vec<usize>,
width: usize,
}
impl Best {
fn fitting(&mut self, next: Rc<PrettyTree>, mut pos: usize, ind: usize) -> bool {
impl<'a, T: Text<'a>> Best<'a, T> {
fn fitting(&mut self, next: Rc<PrettyTree<'a, T>>, mut pos: usize, ind: usize) -> bool {
let mut bidx = self.back_cmds.len();
self.front_cmds.clear(); // clear from previous calls from best
self.front_cmds.push(next);
@ -196,14 +203,8 @@ impl Best {
return false;
}
}
PrettyTree::StaticText(str) => {
pos += str.len();
if pos > self.width {
return false;
}
}
PrettyTree::Text(ref str) => {
pos += str.len();
PrettyTree::Text(ref s) => {
pos += s.len();
if pos > self.width {
return false;
}
@ -240,7 +241,7 @@ impl Best {
fn best<W>(&mut self, top: usize, out: &mut W) -> Result<bool, W::Error>
where
W: RenderAnnotated,
W: RenderAnnotated<'a, T>,
W: ?Sized,
{
let mut fits = true;
@ -302,24 +303,14 @@ impl Best {
}
PrettyTree::RenderLength { length: len, body: doc } => match doc.as_ref() {
PrettyTree::Text(s) => {
out.write_str_all(s)?;
self.pos += len;
fits &= self.pos <= self.width;
}
PrettyTree::StaticText(s) => {
out.write_str_all(s)?;
out.write_all(slice::from_ref(s))?;
self.pos += len;
fits &= self.pos <= self.width;
}
_ => unreachable!(),
},
PrettyTree::Text(ref s) => {
out.write_str_all(s)?;
self.pos += s.len();
fits &= self.pos <= self.width;
}
PrettyTree::StaticText(s) => {
out.write_str_all(s)?;
out.write_all(slice::from_ref(s))?;
self.pos += s.len();
fits &= self.pos <= self.width;
}

View file

@ -1,7 +1,7 @@
use crate::{render::Annotation, Render, RenderAnnotated};
use crate::{Render, RenderAnnotated, Text, render::Annotation};
use alloc::rc::Rc;
use color_ansi::AnsiStyle;
use core::fmt::{Debug, Formatter};
use core::fmt::{Debug, Error, Formatter};
/// Writes to something implementing `std::fmt::Write`
pub struct FmtWrite<W> {
@ -9,27 +9,27 @@ pub struct FmtWrite<W> {
}
/// Represents a terminal writer.
#[derive(Debug)]
pub struct BufferWrite {
buffer: String,
pub struct BufferWrite<T> {
buffer: Vec<T>,
annotations: Vec<(usize, Annotation<AnsiStyle>)>,
}
impl BufferWrite {
impl<'a, T: Text<'a>> BufferWrite<T> {
/// Creates a new terminal writer.
pub fn new(capacity: usize) -> Self {
BufferWrite { buffer: String::with_capacity(capacity), annotations: Vec::new() }
BufferWrite { buffer: Vec::with_capacity(capacity), annotations: Vec::new() }
}
/// Creates a new terminal writer.
pub fn render<W>(&mut self, render: &mut W) -> Result<(), W::Error>
where
W: RenderAnnotated,
W: RenderAnnotated<'a, T>,
W: ?Sized,
{
let mut start = 0;
for (end, annotation) in &self.annotations {
let s = &self.buffer[start..*end];
if !s.is_empty() {
render.write_str_all(s)?;
render.write_all(s)?;
}
start = *end;
match annotation {
@ -39,31 +39,26 @@ impl BufferWrite {
}
let s = &self.buffer[start..];
if !s.is_empty() {
render.write_str_all(s)?;
render.write_all(s)?;
}
Ok(())
}
}
impl Render for BufferWrite {
type Error = core::fmt::Error;
impl<'a, T: Clone> Render<'a, T> for BufferWrite<T> {
type Error = Error;
fn write_str(&mut self, s: &str) -> Result<usize, Self::Error> {
self.buffer.push_str(s);
Ok(s.len())
}
fn write_str_all(&mut self, s: &str) -> Result<(), Self::Error> {
self.buffer.push_str(s);
fn write_all(&mut self, s: &[T]) -> Result<(), Self::Error> {
self.buffer.extend(s.iter().cloned());
Ok(())
}
fn fail_doc(&self) -> Self::Error {
core::fmt::Error::default()
Error
}
}
impl<W> RenderAnnotated for FmtWrite<W>
impl<'a, W, T: Text<'a>> RenderAnnotated<'a, T> for FmtWrite<W>
where
W: core::fmt::Write,
{
@ -76,7 +71,7 @@ where
}
}
impl RenderAnnotated for BufferWrite {
impl<'a, T: Clone> RenderAnnotated<'a, T> for BufferWrite<T> {
fn push_annotation(&mut self, annotation: Rc<AnsiStyle>) -> Result<(), Self::Error> {
self.annotations.push((self.buffer.len(), Annotation::Push(annotation)));
Ok(())
@ -101,21 +96,20 @@ impl<W> FmtWrite<W> {
}
}
impl<W> Render for FmtWrite<W>
impl<'a, W, T: Text<'a>> Render<'a, T> for FmtWrite<W>
where
W: core::fmt::Write,
{
type Error = core::fmt::Error;
type Error = Error;
fn write_str(&mut self, s: &str) -> Result<usize, core::fmt::Error> {
self.write_str_all(s).map(|_| s.len())
}
fn write_str_all(&mut self, s: &str) -> core::fmt::Result {
self.upstream.write_str(s)
fn write_all(&mut self, s: &[T]) -> core::fmt::Result {
for i in s {
self.upstream.write_str(i.as_str().as_ref())?
}
Ok(())
}
fn fail_doc(&self) -> Self::Error {
core::fmt::Error
Error
}
}

View file

@ -1,8 +1,13 @@
#[cfg(feature = "std")]
use crate::Text;
use crate::{Render, RenderAnnotated};
use alloc::rc::Rc;
use color_ansi::{AnsiAbility, AnsiStyle, AnsiWriter};
use core::fmt::{Debug, Formatter};
use std::io::{Error, ErrorKind, Write};
#[cfg(feature = "std")]
use std::io::{Error, Write};
/// Represents a terminal writer.
pub struct TerminalWriter<W> {
color_stack: Vec<Rc<AnsiStyle>>,
@ -28,29 +33,29 @@ impl<W> IoWrite<W> {
}
#[cfg(feature = "std")]
impl<W> Render for IoWrite<W>
where
W: std::io::Write,
impl<'a, W, T: Text<'a>> Render<'a, T> for IoWrite<W>
where
W: std::io::Write,
{
type Error = std::io::Error;
fn write_str(&mut self, s: &str) -> std::io::Result<usize> {
self.upstream.write(s.as_bytes())
}
fn write_all(&mut self, s: &[T]) -> std::io::Result<()> {
for i in s {
self.upstream.write_all(i.as_str().as_bytes())?;
}
fn write_str_all(&mut self, s: &str) -> std::io::Result<()> {
self.upstream.write_all(s.as_bytes())
Ok(())
}
fn fail_doc(&self) -> Self::Error {
std::io::Error::new(std::io::ErrorKind::Other, "Document failed to render")
std::io::Error::other("Document failed to render")
}
}
#[cfg(feature = "std")]
impl<W> RenderAnnotated for IoWrite<W>
where
W: std::io::Write,
impl<'a, W, T: Text<'a>> RenderAnnotated<'a, T> for IoWrite<W>
where
W: std::io::Write,
{
fn push_annotation(&mut self, _: Rc<AnsiStyle>) -> Result<(), Self::Error> {
Ok(())
@ -79,26 +84,26 @@ impl<W: Write> TerminalWriter<W> {
}
}
impl<W> Render for TerminalWriter<W>
impl<'a, W, T: Text<'a>> Render<'a, T> for TerminalWriter<W>
where
W: Write,
{
type Error = Error;
fn write_str(&mut self, s: &str) -> std::io::Result<usize> {
self.upstream.write(s.as_bytes())
}
fn write_all(&mut self, s: &[T]) -> std::io::Result<()> {
for i in s {
self.upstream.write_all(i.as_str().as_bytes())?;
}
fn write_str_all(&mut self, s: &str) -> std::io::Result<()> {
self.upstream.write_all(s.as_bytes())
Ok(())
}
fn fail_doc(&self) -> Self::Error {
Error::new(ErrorKind::Other, "Document failed to render")
Error::other("Document failed to render")
}
}
impl<W: Write> RenderAnnotated for TerminalWriter<W> {
impl<'a, W: Write, T: Text<'a>> RenderAnnotated<'a, T> for TerminalWriter<W> {
fn push_annotation(&mut self, color: Rc<AnsiStyle>) -> Result<(), Self::Error> {
self.color_stack.push(color.clone());
self.upstream.set_style(&color)
@ -112,3 +117,49 @@ impl<W: Write> RenderAnnotated for TerminalWriter<W> {
}
}
}
/// Writes to something implementing `std::io::Write`
pub struct VecWrite<'v, T> {
upstream: &'v mut Vec<T>,
}
impl<'v, W> Debug for VecWrite<'v, W> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("VecWrite").finish()
}
}
impl<'v, T> VecWrite<'v, T> {
/// Creates a new terminal writer.
pub fn new(upstream: &'v mut Vec<T>) -> VecWrite<'v, T> {
VecWrite { upstream }
}
}
#[cfg(feature = "std")]
impl<'a, T: Text<'a>> Render<'a, T> for VecWrite<'_, T> {
type Error = &'static str;
fn write_all(&mut self, s: &[T]) -> Result<(), &'static str> {
for i in s {
self.upstream.push(i.clone());
}
Ok(())
}
fn fail_doc(&self) -> Self::Error {
"Document failed to render"
}
}
#[cfg(feature = "std")]
impl<'a, T: Text<'a>> RenderAnnotated<'a, T> for VecWrite<'_, T> {
fn push_annotation(&mut self, _: Rc<AnsiStyle>) -> Result<(), Self::Error> {
Ok(())
}
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}

View file

@ -0,0 +1,58 @@
use std::borrow::Cow;
/// Pretty printing can work with arbitrary text-like objects,
/// as long as they implement this trait.
#[allow(clippy::len_without_is_empty)]
pub trait Text<'a>: Sized + Clone + 'a {
/// Turn a &'static str into Self, without any further knowledge about it.
///
/// Used for a bunch of functions to give a default.
/// Often not the most optimal or even desired implementation.
fn from_static_str(s: &'static str) -> Self;
/// We foten insert spaces as a static str.
///
/// By default this is implemented with [`from_static_str`].
fn from_static_spaces(s: &'static str) -> Self {
Self::from_static_str(s)
}
/// Turn custom text into a `Cow<'a, str>`.
fn as_str(&self) -> Cow<'_, str>;
/// Get the length of the custom text.
fn len(&self) -> usize {
str::len(&self.as_str())
}
/// A space character
///
/// By default this is implemented with [`from_static_str`].
fn space() -> Self {
Self::from_static_str(" ")
}
/// A newline character
///
/// By default this is implemented with [`from_static_str`].
fn newline() -> Self {
Self::from_static_str("\n")
}
/// A comma and space character
///
/// By default this is implemented with [`from_static_str`].
fn comma_space() -> Self {
Self::from_static_str(", ")
}
}
impl<'a> Text<'a> for Cow<'a, str> {
fn from_static_str(s: &'static str) -> Self {
Cow::Borrowed(s)
}
fn as_str(&self) -> Cow<'a, str> {
self.clone()
}
}

View file

@ -1,10 +1,10 @@
use crate::{providers::PrettyProvider, PrettyTree};
use crate::{PrettyTree, providers::PrettyProvider};
use alloc::string::String;
pub mod printer;
/// The `PrettyPrint` trait is implemented by types that can be pretty-printed.
pub trait PrettyBuilder {
pub trait PrettyBuilder<'a, T> {
/// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line.
///
/// ```
@ -23,12 +23,12 @@ pub trait PrettyBuilder {
/// assert_eq!(doc.1.pretty(100).to_string(), "let x in x");
/// assert_eq!(doc.1.pretty(8).to_string(), "let x\nx");
/// ```
fn flat_alt<E>(self, inline: E) -> PrettyTree
fn flat_alt<E>(self, inline: E) -> PrettyTree<'a, T>
where
E: Into<PrettyTree>;
E: Into<PrettyTree<'a, T>>;
/// Acts as `self` when laid out on a single line and acts as `that` when laid out on multiple lines.
fn indent(self, indent: usize) -> PrettyTree;
fn indent(self, indent: usize) -> PrettyTree<'a, T>;
/// Increase the indentation level of this document.
fn nest(self, offset: isize) -> PrettyTree;
fn nest(self, offset: isize) -> PrettyTree<'a, T>;
}

View file

@ -1,13 +1,15 @@
use crate::Text;
use super::*;
/// Marker trait for types that can be pretty printed.
pub trait PrettyPrint {
pub trait PrettyPrint<'a, T: Text<'a>> {
/// Build a pretty tree for this type.
fn pretty(&self, theme: &PrettyProvider) -> PrettyTree;
fn pretty(&self, theme: &PrettyProvider) -> PrettyTree<'a, T>;
/// Get a pretty string for this type.
fn pretty_string(&self, theme: &PrettyProvider) -> String {
let mut buffer = String::new();
if let Err(e) = self.pretty(&theme).render_fmt(theme.get_width(), &mut buffer) {
if let Err(e) = self.pretty(theme).render_fmt(theme.get_width(), &mut buffer) {
panic!("Error: {}", e);
}
buffer
@ -15,7 +17,7 @@ pub trait PrettyPrint {
/// Print a pretty string for this type.
fn pretty_colorful(&self, theme: &PrettyProvider) -> String {
let mut buffer = vec![];
if let Err(e) = self.pretty(&theme).render_colored(theme.get_width(), &mut buffer) {
if let Err(e) = self.pretty(theme).render_colored(theme.get_width(), &mut buffer) {
panic!("Error: {}", e);
}
match String::from_utf8(buffer) {
@ -25,13 +27,13 @@ pub trait PrettyPrint {
}
}
impl PrettyPrint for PrettyTree {
fn pretty(&self, _: &PrettyProvider) -> PrettyTree {
impl<'a, T: Text<'a>> PrettyPrint<'a, T> for PrettyTree<'a, T> {
fn pretty(&self, _: &PrettyProvider) -> PrettyTree<'a, T> {
self.clone()
}
}
impl PrettyPrint for &'static str {
fn pretty(&self, _: &PrettyProvider) -> PrettyTree {
PrettyTree::StaticText(*self)
impl<'a, T: Text<'a>> PrettyPrint<'a, T> for &'static str {
fn pretty(&self, _: &PrettyProvider) -> PrettyTree<'a, T> {
PrettyTree::Text(T::from_static_str(self))
}
}

View file

@ -1,18 +1,11 @@
use super::*;
impl Default for PrettyTree {
fn default() -> Self {
Self::Nil
}
}
impl Clone for PrettyTree {
impl<'a, T: Clone> Clone for PrettyTree<'a, T> {
fn clone(&self) -> Self {
match self {
Self::Nil => Self::Nil,
Self::Hardline => Self::Hardline,
Self::Text(s) => Self::Text(s.clone()),
Self::StaticText(s) => Self::StaticText(*s),
Self::Annotated { style: color, body: doc } => Self::Annotated { style: color.clone(), body: doc.clone() },
Self::Append { lhs, rhs } => Self::Append { lhs: lhs.clone(), rhs: rhs.clone() },
Self::Group { items } => Self::Group { items: items.clone() },
@ -27,15 +20,18 @@ impl Clone for PrettyTree {
}
}
impl Debug for PrettyTree {
impl<'a, T> Debug for PrettyTree<'a, T>
where
T: Text<'a> + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let is_line = |doc: &PrettyTree| match doc {
let is_line = |doc: &PrettyTree<'a, T>| match doc {
PrettyTree::MaybeInline { block: flat, inline: alt } => {
matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::StaticText(" ")))
matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::Text(t)) if t.as_str() == " ")
}
_ => false,
};
let is_line_ = |doc: &PrettyTree| match doc {
let is_line_ = |doc: &PrettyTree<'a, T>| match doc {
PrettyTree::MaybeInline { block: flat, inline: alt } => {
matches!((&**flat, &**alt), (PrettyTree::Hardline, PrettyTree::Nil))
}
@ -66,7 +62,6 @@ impl Debug for PrettyTree {
PrettyTree::Hardline => f.debug_tuple("Hardline").finish(),
PrettyTree::RenderLength { body: doc, .. } => doc.fmt(f),
PrettyTree::Text(s) => Debug::fmt(s, f),
PrettyTree::StaticText(s) => Debug::fmt(s, f),
PrettyTree::Annotated { style: color, body: doc } => f.debug_tuple("Annotated").field(color).field(doc).finish(),
PrettyTree::Union { lhs: left, rhs: right } => f.debug_tuple("Union").field(left).field(right).finish(),
PrettyTree::Column { .. } => f.debug_tuple("Column(..)").finish(),
@ -76,7 +71,7 @@ impl Debug for PrettyTree {
}
}
fn append_docs(mut doc: &PrettyTree, consumer: &mut impl FnMut(&PrettyTree)) {
fn append_docs<'a, T>(mut doc: &PrettyTree<'a, T>, consumer: &mut impl FnMut(&PrettyTree<'a, T>)) {
loop {
match doc {
PrettyTree::Append { lhs, rhs } => {

View file

@ -1,29 +1,29 @@
use super::*;
impl<T> Add<T> for PrettyTree
impl<'a, U, T: Text<'a>> Add<U> for PrettyTree<'a, T>
where
T: Into<Self>,
U: Into<Self>,
{
type Output = Self;
fn add(self, rhs: T) -> Self::Output {
fn add(self, rhs: U) -> Self::Output {
self.append(rhs.into())
}
}
impl<T> AddAssign<T> for PrettyTree
impl<'a, U, T: Text<'a>> AddAssign<U> for PrettyTree<'a, T>
where
T: Into<Self>,
U: Into<Self>,
{
fn add_assign(&mut self, rhs: T) {
fn add_assign(&mut self, rhs: U) {
*self = self.clone().append(rhs.into());
}
}
impl<T> From<Option<T>> for PrettyTree
impl<'a, U, T> From<Option<U>> for PrettyTree<'a, T>
where
Self: From<T>,
Self: From<U>,
{
fn from(x: Option<T>) -> Self {
fn from(x: Option<U>) -> Self {
match x {
Some(x) => x.into(),
None => Self::Nil,
@ -31,20 +31,20 @@ where
}
}
impl From<()> for PrettyTree {
impl<'a, T> From<()> for PrettyTree<'a, T> {
fn from(_: ()) -> Self {
Self::Nil
}
}
impl From<&'static str> for PrettyTree {
impl<'a, T: Text<'a>> From<&'static str> for PrettyTree<'a, T> {
fn from(s: &'static str) -> Self {
Self::StaticText(s)
Self::Text(T::from_static_str(s))
}
}
impl From<String> for PrettyTree {
fn from(s: String) -> Self {
Self::Text(Rc::from(s))
impl<'a, T: Text<'a>> From<T> for PrettyTree<'a, T> {
fn from(s: T) -> Self {
Self::Text(s)
}
}

View file

@ -1,12 +1,11 @@
use crate::{helpers::PrettySequence, render, FmtWrite, PrettyBuilder, RenderAnnotated};
use alloc::{borrow::Cow, rc::Rc, string::String};
use crate::{FmtWrite, PrettyBuilder, RenderAnnotated, Text, helpers::PrettySequence, render};
use alloc::rc::Rc;
use color_ansi::AnsiStyle;
use core::{
fmt::{Debug, Formatter},
ops::{Add, AddAssign},
};
use std::io::Write;
use unicode_segmentation::UnicodeSegmentation;
use std::{borrow::Cow, io::Write};
mod display;
mod into;
@ -16,15 +15,15 @@ mod into;
///
/// The `T` parameter is used to abstract over pointers to `Doc`. See `RefDoc` and `BoxDoc` for how
/// it is used
pub enum PrettyTree {
#[derive(Default)]
pub enum PrettyTree<'a, T = Cow<'a, str>> {
/// Nothing to show
#[default]
Nil,
/// A hard line break
Hardline,
/// A dynamic text document, all newlines are hard line breaks
Text(Rc<str>),
/// A static text document, all newlines are hard line breaks
StaticText(&'static str),
Text(T),
/// A document with ansi styles
Annotated {
/// The style to use for the text
@ -75,51 +74,45 @@ pub enum PrettyTree {
/// Concatenates two documents with a line in between
Column {
/// The first document
invoke: Rc<dyn Fn(usize) -> Self>,
invoke: Rc<dyn Fn(usize) -> Self + 'a>,
},
/// Concatenates two documents with a line in between
Nesting {
/// The first document
invoke: Rc<dyn Fn(usize) -> Self>,
invoke: Rc<dyn Fn(usize) -> Self + 'a>,
},
/// Concatenates two documents with a line in between
Fail,
}
#[allow(non_upper_case_globals)]
impl PrettyTree {
/// A hard line break
pub const Space: Self = PrettyTree::StaticText(" ");
impl<'a, T: Text<'a>> PrettyTree<'a, T> {
/// A line acts like a `\n` but behaves like `space` if it is grouped on a single line.
#[inline]
pub fn line_or_space() -> Self {
Self::Hardline.flat_alt(Self::Space).into()
Self::Hardline.flat_alt(T::space())
}
/// A line acts like `\n` but behaves like `nil` if it is grouped on a single line.
#[inline]
pub fn line_or_comma() -> Self {
Self::Hardline.flat_alt(PrettyTree::StaticText(", ")).into()
Self::Hardline.flat_alt(PrettyTree::Text(T::comma_space()))
}
/// Acts like `line` but behaves like `nil` if grouped on a single line
#[inline]
pub fn line_or_nil() -> Self {
Self::Hardline.flat_alt(Self::Nil).into()
Self::Hardline.flat_alt(Self::Nil)
}
}
impl PrettyTree {
impl<'a, T> PrettyTree<'a, T> {
/// The given text, which must not contain line breaks.
#[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()
pub fn text(data: impl Into<T>) -> Self {
Self::Text(data.into())
}
}
impl PrettyTree {
impl<'a, T: Text<'a>> PrettyTree<'a, T> {
/// Writes a rendered document to a `std::io::Write` object.
#[inline]
#[cfg(feature = "std")]
@ -130,6 +123,13 @@ impl PrettyTree {
self.render_raw(width, &mut crate::IoWrite::new(out))
}
/// Writes a rendered document to a `std::io::Write` object.
#[inline]
#[cfg(feature = "std")]
pub fn render_vec(&self, width: usize, out: &mut Vec<T>) -> Result<(), &'static str> {
self.render_raw(width, &mut crate::VecWrite::new(out))
}
/// Writes a rendered document to a `std::fmt::Write` object.
#[inline]
pub fn render_fmt<W>(&self, width: usize, out: &mut W) -> core::fmt::Result
@ -143,14 +143,14 @@ impl PrettyTree {
#[inline]
pub fn render_raw<W>(&self, width: usize, out: &mut W) -> Result<(), W::Error>
where
W: RenderAnnotated,
W: RenderAnnotated<'a, T>,
W: ?Sized,
{
render::best(Rc::new(self.clone()), width, out)
}
}
impl PrettyTree {
impl<'a, T: Text<'a>> PrettyTree<'a, T> {
/// The given text, which must not contain line breaks.
#[inline]
#[cfg(feature = "std")]
@ -159,7 +159,7 @@ impl PrettyTree {
}
}
impl PrettyBuilder for PrettyTree {
impl<'a, T: Text<'a>> PrettyBuilder<'a, T> for PrettyTree<'a, T> {
/// Acts as `self` when laid out on multiple lines and acts as `that` when laid out on a single line.
///
/// ```
@ -181,7 +181,7 @@ impl PrettyBuilder for PrettyTree {
#[inline]
fn flat_alt<E>(self, flat: E) -> Self
where
E: Into<PrettyTree>,
E: Into<PrettyTree<'a, T>>,
{
Self::MaybeInline { block: Rc::new(self), inline: Rc::new(flat.into()) }
}
@ -217,7 +217,7 @@ impl PrettyBuilder for PrettyTree {
while remaining != 0 {
let i = SPACES.len().min(remaining);
remaining -= i;
doc = doc.append(PrettyTree::text(&SPACES[..i]))
doc = doc.append(PrettyTree::text(T::from_static_spaces(&SPACES[..i])))
}
doc
};
@ -237,28 +237,12 @@ impl PrettyBuilder for PrettyTree {
}
}
impl PrettyTree {
fn with_utf8_len(self) -> Self {
let s = match &self {
Self::Text(s) => s.as_ref(),
Self::StaticText(s) => s,
// Doc::SmallText(s) => s,
_ => return self,
};
if s.is_ascii() {
self
}
else {
let grapheme_len = s.graphemes(true).count();
Self::RenderLength { length: grapheme_len, body: Rc::new(self) }
}
}
impl<'a, T: Text<'a>> PrettyTree<'a, T> {
/// Append the given document after this document.
#[inline]
pub fn append<E>(self, follow: E) -> Self
where
E: Into<PrettyTree>,
E: Into<PrettyTree<'a, T>>,
{
let rhs = follow.into();
match (&self, &rhs) {
@ -275,11 +259,11 @@ impl PrettyTree {
/// NOTE: The separator type, `S` may need to be cloned. Consider using cheaply cloneable ptr
/// like `RefDoc` or `RcDoc`
#[inline]
pub fn join<I, T1, T2>(terms: I, joint: T2) -> PrettyTree
pub fn join<I, T1, T2>(terms: I, joint: T2) -> PrettyTree<'a, T>
where
I: IntoIterator<Item = T1>,
T1: Into<PrettyTree>,
T2: Into<PrettyTree>,
T1: Into<PrettyTree<'a, T>>,
T2: Into<PrettyTree<'a, T>>,
{
let joint = joint.into();
let mut iter = terms.into_iter().map(|s| s.into());
@ -295,7 +279,7 @@ impl PrettyTree {
pub fn concat<I>(docs: I) -> Self
where
I: IntoIterator,
I::Item: Into<PrettyTree>,
I::Item: Into<PrettyTree<'a, T>>,
{
let mut head = Self::Nil;
for item in docs.into_iter() {
@ -313,7 +297,7 @@ impl PrettyTree {
#[inline]
pub fn group(self) -> Self {
match self {
Self::Group { .. } | Self::Text(_) | Self::StaticText(_) | Self::Nil => self,
Self::Group { .. } | Self::Text(_) | Self::Nil => self,
_ => Self::Group { items: Rc::new(self) },
}
}
@ -321,13 +305,13 @@ impl PrettyTree {
/// Mark this document as a comment.
#[inline]
pub fn annotate(self, style: Rc<AnsiStyle>) -> Self {
Self::Annotated { style: style, body: Rc::new(self) }
Self::Annotated { style, body: Rc::new(self) }
}
/// Mark this document as a hard line break.
#[inline]
pub fn union<E>(self, other: E) -> Self
where
E: Into<PrettyTree>,
E: Into<PrettyTree<'a, T>>,
{
Self::Union { lhs: Rc::new(self), rhs: Rc::new(other.into()) }
}
@ -402,6 +386,7 @@ impl PrettyTree {
pub fn width<F>(self, f: F) -> Self
where
F: Fn(isize) -> Self + Clone + 'static,
T: Clone,
{
Self::Column {
invoke: Rc::new(move |start| {

View file

@ -1,5 +1,6 @@
use pretty_print::*;
#![allow(unused)]
use SExp::*;
use pretty_print::*;
enum SExp {
Atom(u32),
List(Vec<SExp>),
@ -7,11 +8,11 @@ enum SExp {
impl SExp {
/// Return a pretty printed format of self.
pub fn to_doc(&self) -> PrettyTree {
pub fn to_doc<'a>(&self) -> PrettyTree<'a> {
match self {
Atom(x) => PrettyTree::text(x.to_string()),
List(xs) => PrettyTree::text("(")
.append(PrettyTree::join(xs.into_iter().map(|x| x.to_doc()), PrettyTree::line_or_space()).nest(1).group())
.append(PrettyTree::join(xs.iter().map(|x| x.to_doc()), PrettyTree::line_or_space()).nest(1).group())
.append(PrettyTree::text(")")),
}
}

View file

@ -6,7 +6,7 @@ authors = ["Aster <192607617@qq.com>"]
description = "..."
repository = "https://github.com/oovm/sub_projects"
documentation = "https://docs.rs/sub_projects"
readme = "Readme.md"
readme = "../../Readme.md"
license = "MPL-2.0"
edition = "2021"
exclude = ["package.json", "tests/**"]
@ -14,11 +14,12 @@ exclude = ["package.json", "tests/**"]
[dependencies]
pretty = "0.12.1"
[dependencies.pretty-print]
[dependencies.logparse-pretty-print]
version = "*"
default-features = false
features = ["std"]
path = "../pretty-print"
name = "pretty-print"
[dev-dependencies]

View file

@ -1,6 +1,8 @@
#![allow(dead_code, unused)]
use pretty_print::{AnsiColor, AnsiStyle, PrettyBuilder, PrettyTree};
use std::{io::stdout, rc::Rc};
use pretty_print::{AnsiColor, AnsiStyle, PrettyBuilder, PrettyTree as RawPrettyTree};
use std::{borrow::Cow, io::stdout, rc::Rc};
type PrettyTree<'a> = RawPrettyTree<'a, Cow<'a, str>>;
#[test]
fn ready() {