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}