pretty printing
This commit is contained in:
parent
52a80cfb0e
commit
c867ad379e
30 changed files with 1533 additions and 387 deletions
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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`)
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
pretty-print/projects/pretty-print/src/text.rs
Normal file
58
pretty-print/projects/pretty-print/src/text.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 } => {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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(")")),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue