From 948af2951a12105ce446e6cc57ea4a3930241f87 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Jul 2024 10:08:37 -0500 Subject: [PATCH 1/4] test: Port to snapbox --- Cargo.lock | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/tests.rs | 35 +++++++--- 3 files changed, 203 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69736c1..97a97b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,183 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" + +[[package]] +name = "snapbox" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e14d10e4c2b4331ac24c33baa5a03e1fbca81c045b285b53b2a612d28569fb" +dependencies = [ + "anstream", + "anstyle", + "normalize-line-endings", + "similar", + "snapbox-macros", +] + +[[package]] +name = "snapbox-macros" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d" +dependencies = [ + "anstream", +] + [[package]] name = "termtree" version = "0.4.1" +dependencies = [ + "snapbox", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index dcc59a3..c34816f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -116,6 +116,7 @@ pre-release-replacements = [ [dependencies] [dev-dependencies] +snapbox = "0.6.10" [lints] workspace = true diff --git a/src/tests.rs b/src/tests.rs index 30f5221..f12e210 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,32 +1,45 @@ +use snapbox::assert_data_eq; +use snapbox::str; + use super::*; #[test] fn render_tree_root() { let tree = Tree::new("foo"); - assert_eq!(format!("{}", tree), "foo\n"); + assert_data_eq!( + format!("{}", tree), + str![[r#" +foo + +"#]] + ); } #[test] fn render_tree_with_leaves() { let tree = Tree::new("foo").with_leaves([Tree::new("bar").with_leaves(["baz"])]); - assert_eq!( + assert_data_eq!( format!("{}", tree), - r#"foo + str![[r#" +foo └── bar └── baz -"# + +"#]] ); } #[test] fn render_tree_with_multiple_leaves() { let tree = Tree::new("foo").with_leaves(["bar", "baz"]); - assert_eq!( + assert_data_eq!( format!("{}", tree), - r#"foo + str![[r#" +foo ├── bar └── baz -"# + +"#]] ); } @@ -36,13 +49,15 @@ fn render_tree_with_multiline_leaf() { Tree::new("hello\nworld").with_multiline(true), Tree::new("goodbye\nworld").with_multiline(true), ]); - assert_eq!( + assert_data_eq!( format!("{}", tree), - r#"foo + str![[r#" +foo ├── hello │ world └── goodbye world -"# + +"#]] ); } From 913d8d64b9efdd4ea0db34c358cd14c534a2d8b2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Jul 2024 10:19:35 -0500 Subject: [PATCH 2/4] test: Show current glyph behavior --- src/tests.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/tests.rs b/src/tests.rs index f12e210..5500a96 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -61,3 +61,63 @@ foo "#]] ); } + +#[test] +fn render_custom_glyphs() { + let root = GlyphPalette { + middle_item: "[mid ]", + last_item: "[last ]", + item_indent: "[indent ]", + + middle_skip: "[mskip]", + last_skip: "[lskip]", + skip_indent: "[iskip ]", + }; + let middle = GlyphPalette { + middle_item: "(mid )", + last_item: "(last )", + item_indent: "(indent )", + + middle_skip: "(mskip)", + last_skip: "(lskip)", + skip_indent: "(iskip )", + }; + + let tree = Tree::new("node 1").with_glyphs(root).with_leaves([ + Tree::new("node 1.1"), + Tree::new("node 1.2"), + Tree::new("node 1.3").with_leaves([ + Tree::new("node 1.3.1").with_glyphs(middle), + Tree::new("node 1.3.2").with_glyphs(middle), + Tree::new("node 1.3.3") + .with_glyphs(middle) + .with_leaves(["node 1.3.3.1", "node 1.3.3.2"]), + ]), + Tree::new("node 1.4").with_leaves([ + Tree::new("node 1.4.1"), + Tree::new("node 1.4.2"), + Tree::new("node 1.4.3").with_leaves(["node 1.4.3.1", "node 1.4.3.2"]), + ]), + ]); + assert_data_eq!( + format!("{}", tree), + str![[r#" +node 1 +├── node 1.1 +├── node 1.2 +├── node 1.3 +[mskip][iskip ](mid )(indent )node 1.3.1 +[mskip][iskip ](mid )(indent )node 1.3.2 +[mskip][iskip ](last )(indent )node 1.3.3 +[mskip][iskip ][lskip][iskip ]├── node 1.3.3.1 +[mskip][iskip ][lskip][iskip ]└── node 1.3.3.2 +└── node 1.4 +[lskip][iskip ]├── node 1.4.1 +[lskip][iskip ]├── node 1.4.2 +[lskip][iskip ]└── node 1.4.3 +[lskip][iskip ][lskip][iskip ]├── node 1.4.3.1 +[lskip][iskip ][lskip][iskip ]└── node 1.4.3.2 + +"#]] + ); +} From 28ba078fd2d4adb93999e3c92e6c9e6600d878c2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Jul 2024 10:27:58 -0500 Subject: [PATCH 3/4] fix: Use space glyphs from the right layer --- src/lib.rs | 50 ++++++++++++++++++++++++++++++++------------------ src/tests.rs | 20 ++++++++++---------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a009cca..c4623cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,13 +123,8 @@ impl Display for Tree { for line in root.lines() { // print single line for s in spaces.as_slice() { - if *s { - self.glyphs.last_skip.fmt(f)?; - self.glyphs.skip_indent.fmt(f)?; - } else { - self.glyphs.middle_skip.fmt(f)?; - self.glyphs.skip_indent.fmt(f)?; - } + s.skip.fmt(f)?; + s.indent.fmt(f)?; } prefix.0.fmt(f)?; prefix.1.fmt(f)?; @@ -140,13 +135,8 @@ impl Display for Tree { } else { // print single line for s in spaces.as_slice() { - if *s { - self.glyphs.last_skip.fmt(f)?; - self.glyphs.skip_indent.fmt(f)?; - } else { - self.glyphs.middle_skip.fmt(f)?; - self.glyphs.skip_indent.fmt(f)?; - } + s.skip.fmt(f)?; + s.indent.fmt(f)?; } prefix.0.fmt(f)?; prefix.1.fmt(f)?; @@ -156,9 +146,13 @@ impl Display for Tree { // recurse if !leaf.leaves.is_empty() { - let s: &Vec = &spaces; + let s: &Vec = &spaces; let mut child_spaces = s.clone(); - child_spaces.push(last); + child_spaces.push(if last { + leaf.glyphs.last_space() + } else { + leaf.glyphs.middle_space() + }); let child_spaces = Rc::new(child_spaces); enqueue_leaves(&mut queue, leaf, child_spaces); } @@ -167,12 +161,12 @@ impl Display for Tree { } } -type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree, Rc>)>; +type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree, Rc>)>; fn enqueue_leaves<'t, D: Display>( queue: &mut DisplauQueue<'t, D>, parent: &'t Tree, - spaces: Rc>, + spaces: Rc>, ) { for (i, leaf) in parent.leaves.iter().rev().enumerate() { let last = i == 0; @@ -180,6 +174,12 @@ fn enqueue_leaves<'t, D: Display>( } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct SpacePalette { + skip: &'static str, + indent: &'static str, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct GlyphPalette { pub middle_item: &'static str, @@ -203,6 +203,20 @@ impl GlyphPalette { skip_indent: " ", } } + + fn middle_space(&self) -> SpacePalette { + SpacePalette { + skip: self.middle_skip, + indent: self.skip_indent, + } + } + + fn last_space(&self) -> SpacePalette { + SpacePalette { + skip: self.last_skip, + indent: self.skip_indent, + } + } } impl Default for GlyphPalette { diff --git a/src/tests.rs b/src/tests.rs index 5500a96..f723358 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -106,17 +106,17 @@ node 1 ├── node 1.1 ├── node 1.2 ├── node 1.3 -[mskip][iskip ](mid )(indent )node 1.3.1 -[mskip][iskip ](mid )(indent )node 1.3.2 -[mskip][iskip ](last )(indent )node 1.3.3 -[mskip][iskip ][lskip][iskip ]├── node 1.3.3.1 -[mskip][iskip ][lskip][iskip ]└── node 1.3.3.2 +│ (mid )(indent )node 1.3.1 +│ (mid )(indent )node 1.3.2 +│ (last )(indent )node 1.3.3 +│ (lskip)(iskip )├── node 1.3.3.1 +│ (lskip)(iskip )└── node 1.3.3.2 └── node 1.4 -[lskip][iskip ]├── node 1.4.1 -[lskip][iskip ]├── node 1.4.2 -[lskip][iskip ]└── node 1.4.3 -[lskip][iskip ][lskip][iskip ]├── node 1.4.3.1 -[lskip][iskip ][lskip][iskip ]└── node 1.4.3.2 + ├── node 1.4.1 + ├── node 1.4.2 + └── node 1.4.3 + ├── node 1.4.3.1 + └── node 1.4.3.2 "#]] ); From 58263417cc7b9a15fb242fbced2d259d804cb387 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Jul 2024 10:33:51 -0500 Subject: [PATCH 4/4] feat: Inherit glyphs Fixes #30 --- src/lib.rs | 38 +++++++++++++++++++++----------------- src/tests.rs | 28 ++++++++++++++-------------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c4623cb..c232582 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ pub struct Tree { pub root: D, pub leaves: Vec>, multiline: bool, - glyphs: GlyphPalette, + glyphs: Option, } impl Tree { @@ -25,7 +25,7 @@ impl Tree { root, leaves: Vec::new(), multiline: false, - glyphs: GlyphPalette::new(), + glyphs: None, } } @@ -42,7 +42,7 @@ impl Tree { /// Customize the rendering of this node pub fn with_glyphs(mut self, glyphs: GlyphPalette) -> Self { - self.glyphs = glyphs; + self.glyphs = Some(glyphs); self } } @@ -56,7 +56,7 @@ impl Tree { /// Customize the rendering of this node pub fn set_glyphs(&mut self, glyphs: GlyphPalette) -> &mut Self { - self.glyphs = glyphs; + self.glyphs = Some(glyphs); self } } @@ -92,25 +92,27 @@ impl Display for Tree { writeln!(f)?; let mut queue = DisplauQueue::new(); let no_space = Rc::new(Vec::new()); - enqueue_leaves(&mut queue, self, no_space); - while let Some((last, leaf, spaces)) = queue.pop_front() { + let default_glyphs = GlyphPalette::new(); + let glyphs = self.glyphs.as_ref().unwrap_or(&default_glyphs); + enqueue_leaves(&mut queue, self, glyphs, no_space); + while let Some((last, leaf, glyphs, spaces)) = queue.pop_front() { let mut prefix = ( if last { - leaf.glyphs.last_item + glyphs.last_item } else { - leaf.glyphs.middle_item + glyphs.middle_item }, - leaf.glyphs.item_indent, + glyphs.item_indent, ); if leaf.multiline { let rest_prefix = ( if last { - leaf.glyphs.last_skip + glyphs.last_skip } else { - leaf.glyphs.middle_skip + glyphs.middle_skip }, - leaf.glyphs.skip_indent, + glyphs.skip_indent, ); debug_assert_eq!(prefix.0.chars().count(), rest_prefix.0.chars().count()); debug_assert_eq!(prefix.1.chars().count(), rest_prefix.1.chars().count()); @@ -149,28 +151,30 @@ impl Display for Tree { let s: &Vec = &spaces; let mut child_spaces = s.clone(); child_spaces.push(if last { - leaf.glyphs.last_space() + glyphs.last_space() } else { - leaf.glyphs.middle_space() + glyphs.middle_space() }); let child_spaces = Rc::new(child_spaces); - enqueue_leaves(&mut queue, leaf, child_spaces); + enqueue_leaves(&mut queue, leaf, glyphs, child_spaces); } } Ok(()) } } -type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree, Rc>)>; +type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree, &'t GlyphPalette, Rc>)>; fn enqueue_leaves<'t, D: Display>( queue: &mut DisplauQueue<'t, D>, parent: &'t Tree, + parent_glyphs: &'t GlyphPalette, spaces: Rc>, ) { for (i, leaf) in parent.leaves.iter().rev().enumerate() { let last = i == 0; - queue.push_front((last, leaf, spaces.clone())); + let glyphs = leaf.glyphs.as_ref().unwrap_or(parent_glyphs); + queue.push_front((last, leaf, glyphs, spaces.clone())); } } diff --git a/src/tests.rs b/src/tests.rs index f723358..ff3e7f0 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -103,20 +103,20 @@ fn render_custom_glyphs() { format!("{}", tree), str![[r#" node 1 -├── node 1.1 -├── node 1.2 -├── node 1.3 -│ (mid )(indent )node 1.3.1 -│ (mid )(indent )node 1.3.2 -│ (last )(indent )node 1.3.3 -│ (lskip)(iskip )├── node 1.3.3.1 -│ (lskip)(iskip )└── node 1.3.3.2 -└── node 1.4 - ├── node 1.4.1 - ├── node 1.4.2 - └── node 1.4.3 - ├── node 1.4.3.1 - └── node 1.4.3.2 +[mid ][indent ]node 1.1 +[mid ][indent ]node 1.2 +[mid ][indent ]node 1.3 +[mskip][iskip ](mid )(indent )node 1.3.1 +[mskip][iskip ](mid )(indent )node 1.3.2 +[mskip][iskip ](last )(indent )node 1.3.3 +[mskip][iskip ](lskip)(iskip )(mid )(indent )node 1.3.3.1 +[mskip][iskip ](lskip)(iskip )(last )(indent )node 1.3.3.2 +[last ][indent ]node 1.4 +[lskip][iskip ][mid ][indent ]node 1.4.1 +[lskip][iskip ][mid ][indent ]node 1.4.2 +[lskip][iskip ][last ][indent ]node 1.4.3 +[lskip][iskip ][lskip][iskip ][mid ][indent ]node 1.4.3.1 +[lskip][iskip ][lskip][iskip ][last ][indent ]node 1.4.3.2 "#]] );