syntree/
print.rs

1//! Helper utilities for pretty-printing trees.
2
3#![cfg(feature = "std")]
4#![cfg_attr(docsrs, doc(cfg(feature = "std")))]
5
6use std::fmt;
7use std::io::{Error, Write};
8
9use crate::flavor::Flavor;
10use crate::span::Span;
11use crate::tree::Tree;
12
13/// Pretty-print a tree without a source.
14///
15/// This will replace all source references with `+`. If you have a source
16/// available you can use [`print_with_source`] instead.
17///
18/// # Examples
19///
20/// ```
21/// #[derive(Debug, Clone, Copy)]
22/// enum Syntax {
23///     NUMBER,
24///     WHITESPACE,
25///     OPERATOR,
26///     PLUS,
27/// }
28///
29/// use Syntax::*;
30///
31/// let tree = syntree::tree! {
32///     NUMBER => {
33///         (NUMBER, 3),
34///     },
35///     (WHITESPACE, 1),
36///     OPERATOR => {
37///         (PLUS, 1)
38///     },
39///     (WHITESPACE, 1),
40///     NUMBER => {
41///         (NUMBER, 2),
42///     },
43/// };
44///
45/// let mut s = Vec::new();
46/// syntree::print::print(&mut s, &tree)?;
47/// # let s = String::from_utf8(s)?;
48/// # assert_eq!(s, "NUMBER@0..3\n  NUMBER@0..3 +\nWHITESPACE@3..4 +\nOPERATOR@4..5\n  PLUS@4..5 +\nWHITESPACE@5..6 +\nNUMBER@6..8\n  NUMBER@6..8 +\n");
49/// # Ok::<_, Box<dyn core::error::Error>>(())
50/// ```
51///
52/// This would write:
53///
54/// ```text
55/// NUMBER@0..3
56///   NUMBER@0..3 +
57/// WHITESPACE@3..4 +
58/// OPERATOR@4..5
59///   PLUS@4..5 +
60/// WHITESPACE@5..6 +
61/// NUMBER@6..8
62///   NUMBER@6..8 +
63/// ```
64pub fn print<O, T, F>(o: O, tree: &Tree<T, F>) -> Result<(), Error>
65where
66    O: Write,
67    T: Copy + fmt::Debug,
68    F: Flavor<Index: fmt::Display>,
69{
70    print_with_lookup(o, tree, |_| None)
71}
72
73/// Pretty-print a tree with the source spans printed.
74///
75/// # Examples
76///
77/// ```
78/// #[derive(Debug, Clone, Copy)]
79/// enum Syntax {
80///     NUMBER,
81///     WHITESPACE,
82///     OPERATOR,
83///     PLUS,
84/// }
85///
86/// use Syntax::*;
87///
88/// let source = "128 + 64";
89///
90/// let tree = syntree::tree! {
91///     NUMBER => {
92///         (NUMBER, 3),
93///     },
94///     (WHITESPACE, 1),
95///     OPERATOR => {
96///         (PLUS, 1)
97///     },
98///     (WHITESPACE, 1),
99///     NUMBER => {
100///         (NUMBER, 2),
101///     },
102/// };
103///
104/// let mut s = Vec::new();
105/// syntree::print::print_with_source(&mut s, &tree, source)?;
106/// # let s = String::from_utf8(s)?;
107/// # assert_eq!(s, "NUMBER@0..3\n  NUMBER@0..3 \"128\"\nWHITESPACE@3..4 \" \"\nOPERATOR@4..5\n  PLUS@4..5 \"+\"\nWHITESPACE@5..6 \" \"\nNUMBER@6..8\n  NUMBER@6..8 \"64\"\n");
108/// # Ok::<_, Box<dyn core::error::Error>>(())
109/// ```
110///
111/// This would write:
112///
113/// ```text
114/// NUMBER@0..3
115///   NUMBER@0..3 "128"
116/// WHITESPACE@3..4 " "
117/// OPERATOR@4..5
118///   PLUS@4..5 "+"
119/// WHITESPACE@5..6 " "
120/// NUMBER@6..8
121///   NUMBER@6..8 "64"
122/// ```
123pub fn print_with_source<O, T, F>(o: O, tree: &Tree<T, F>, source: &str) -> Result<(), Error>
124where
125    O: Write,
126    T: Copy + fmt::Debug,
127    F: Flavor<Index: fmt::Display>,
128{
129    print_with_lookup(o, tree, |span| source.get(span.range()))
130}
131
132fn print_with_lookup<'a, O, T, F>(
133    mut o: O,
134    tree: &Tree<T, F>,
135    source: impl Fn(&Span<F::Index>) -> Option<&'a str>,
136) -> Result<(), Error>
137where
138    O: Write,
139    T: Copy + fmt::Debug,
140    F: Flavor<Index: fmt::Display>,
141{
142    for (depth, node) in tree.walk().with_depths() {
143        let n = (depth * 2) as usize;
144        let data = node.value();
145        let span = node.span();
146
147        if node.has_children() {
148            writeln!(o, "{:n$}{:?}@{}", "", data, span)?;
149        } else if let Some(source) = source(span) {
150            writeln!(o, "{:n$}{:?}@{} {:?}", "", data, span, source)?;
151        } else {
152            writeln!(o, "{:n$}{:?}@{} +", "", data, span)?;
153        }
154    }
155
156    Ok(())
157}