Skip to main content

tui_markup/generator/ansi/
mod.rs

1//! Generator for ANSI terminal string output.
2
3use anstyle::Style;
4
5mod span;
6mod tag;
7
8pub use span::{StyledSpan, StyledText};
9pub use tag::ANSITagConvertor;
10
11use super::{
12    Generator,
13    helper::{CustomTagParser, GeneratorInfallible, NoopCustomTagParser, flatten},
14};
15
16/// Generator for ANSI terminal strings.
17///
18/// See [docs/ansi-tags.ebnf] for supported tags.
19///
20/// ## Example
21///
22/// ```
23/// use tui_markup::{compile, generator::ANSIStringsGenerator};
24///
25/// let result = compile::<ANSIStringsGenerator>("I have a <green green text>").unwrap();
26///
27/// println!("{}", result);
28/// ```
29///
30/// ### With custom tags
31///
32/// ```
33/// use anstyle::{AnsiColor, Style};
34/// use tui_markup::{compile_with, generator::ANSIStringsGenerator};
35///
36/// let g = ANSIStringsGenerator::new(|tag: &str| match tag {
37///     "keyboard" => Some(
38///         Style::new()
39///             .bold()
40///             .fg_color(Some(AnsiColor::Blue.into()))
41///             .bg_color(Some(AnsiColor::Black.into())),
42///     ),
43///     _ => None,
44/// });
45///
46/// let result = compile_with("Press <keyboard W> to move up", g).unwrap();
47///
48/// println!("{}", result);
49/// ```
50///
51/// ## Show output
52///
53/// The result implements `Display`, so just print it.
54///
55/// [docs/ansi-tags.ebnf]: https://github.com/7sDream/tui-markup/blob/master/docs/ansi-tags.ebnf
56#[derive(Debug)]
57pub struct ANSIStringsGenerator<P = NoopCustomTagParser<Style>> {
58    convertor: ANSITagConvertor<P>,
59}
60
61impl<P> Default for ANSIStringsGenerator<P> {
62    fn default() -> Self {
63        Self {
64            convertor: ANSITagConvertor::<P>::default(),
65        }
66    }
67}
68
69impl<P> ANSIStringsGenerator<P> {
70    /// Create a new ANSI generator from a custom tag parser.
71    pub fn new(p: P) -> Self {
72        Self {
73            convertor: ANSITagConvertor::new(p),
74        }
75    }
76}
77
78impl<'a, P> Generator<'a> for ANSIStringsGenerator<P>
79where
80    P: CustomTagParser<Output = Style>,
81{
82    type Convertor = ANSITagConvertor<P>;
83    type Err = GeneratorInfallible;
84    type Output = StyledText<'a>;
85
86    fn convertor(&mut self) -> &mut Self::Convertor {
87        &mut self.convertor
88    }
89
90    fn generate(
91        &mut self, markup: Vec<Vec<crate::parser::ItemG<'a, Self>>>,
92    ) -> Result<Self::Output, Self::Err> {
93        let mut spans = Vec::with_capacity(markup.len());
94
95        for (i, line) in markup.into_iter().enumerate() {
96            if i > 0 {
97                spans.push(StyledSpan::new(Style::new(), "\n"));
98            }
99            spans.extend(flatten(line));
100        }
101
102        Ok(StyledText::new(spans))
103    }
104}