Skip to main content

tui_markup/generator/ratatui/
mod.rs

1//! Generator implementations for ratatui crate.
2
3mod span;
4mod tag;
5#[cfg(test)]
6mod test;
7
8use ratatui_core::{
9    style::Style,
10    text::{Line, Text},
11};
12pub use tag::RatatuiTagConvertor;
13
14use crate::{
15    generator::{
16        Generator,
17        helper::{CustomTagParser, GeneratorInfallible, NoopCustomTagParser, flatten},
18    },
19    parser::ItemG,
20};
21
22/// Generator for `ratatui` crate's [Text] type.
23///
24/// See [docs/ratatui-tags.ebnf] for supported tags.
25///
26/// ## Example
27///
28/// ```
29/// # use ratatui::prelude::*;
30/// use tui_markup::{compile, generator::RatatuiTextGenerator};
31///
32/// assert_eq!(
33///     compile::<RatatuiTextGenerator>("I have a <green green text>"),
34///     Ok(Text::from(vec![Line::from(vec![
35///         Span::raw("I have a "),
36///         Span::styled("green text", Style::default().fg(Color::Green)),
37///     ])])),
38/// );
39///
40/// assert_eq!(
41///     compile::<RatatuiTextGenerator>("I can set <bg:blue background>"),
42///     Ok(Text::from(vec![Line::from(vec![
43///         Span::raw("I can set "),
44///         Span::styled("background", Style::default().bg(Color::Blue)),
45///     ])])),
46/// );
47///
48/// assert_eq!(
49///     compile::<RatatuiTextGenerator>("I can add <b bold>, <d dim>, <i italic> modifiers"),
50///     Ok(Text::from(vec![Line::from(vec![
51///         Span::raw("I can add "),
52///         Span::styled("bold", Style::default().add_modifier(Modifier::BOLD)),
53///         Span::raw(", "),
54///         Span::styled("dim", Style::default().add_modifier(Modifier::DIM)),
55///         Span::raw(", "),
56///         Span::styled("italic", Style::default().add_modifier(Modifier::ITALIC)),
57///         Span::raw(" modifiers"),
58///     ])])),
59/// );
60///
61/// assert_eq!(
62///     compile::<RatatuiTextGenerator>("I can <bg:blue combine <green them <b <i all>>>>"),
63///     Ok(Text::from(vec![Line::from(vec![
64///         Span::raw("I can "),
65///         Span::styled("combine ", Style::default().bg(Color::Blue)),
66///         Span::styled("them ", Style::default().bg(Color::Blue).fg(Color::Green)),
67///         Span::styled(
68///             "all",
69///             Style::default()
70///                 .bg(Color::Blue)
71///                 .fg(Color::Green)
72///                 .add_modifier(Modifier::BOLD | Modifier::ITALIC)
73///         ),
74///     ])])),
75/// );
76///
77/// assert_eq!(
78///     compile::<RatatuiTextGenerator>("I can use <bg:66ccff custom color>"),
79///     Ok(Text::from(vec![Line::from(vec![
80///         Span::raw("I can use "),
81///         Span::styled(
82///             "custom color",
83///             Style::default().bg(Color::Rgb(0x66, 0xcc, 0xff))
84///         ),
85///     ])])),
86/// );
87///
88/// assert_eq!(
89///     compile::<RatatuiTextGenerator>("I can set <bg:blue,green,b,i many style> in one tag"),
90///     Ok(Text::from(vec![Line::from(vec![
91///         Span::raw("I can set "),
92///         Span::styled(
93///             "many style",
94///             Style::default()
95///                 .bg(Color::Blue)
96///                 .fg(Color::Green)
97///                 .add_modifier(Modifier::BOLD | Modifier::ITALIC)
98///         ),
99///         Span::raw(" in one tag"),
100///     ])])),
101/// );
102/// ```
103///
104/// ### With custom tags
105///
106/// ```
107/// # use ratatui::prelude::*;
108/// use tui_markup::{compile_with, generator::RatatuiTextGenerator};
109///
110/// let g = RatatuiTextGenerator::new(|tag: &str| match tag {
111///     "keyboard" => Some(
112///         Style::default()
113///             .bg(Color::White)
114///             .fg(Color::Green)
115///             .add_modifier(Modifier::BOLD),
116///     ),
117///     _ => None,
118/// });
119///
120/// assert_eq!(
121///     compile_with("Press <keyboard W> to move up", g),
122///     Ok(Text::from(vec![Line::from(vec![
123///         Span::raw("Press "),
124///         Span::styled(
125///             "W",
126///             Style::default()
127///                 .bg(Color::White)
128///                 .fg(Color::Green)
129///                 .add_modifier(Modifier::BOLD)
130///         ),
131///         Span::raw(" to move up"),
132///     ])])),
133/// );
134/// ```
135///
136/// ### Show output
137///
138/// Use any widget of the `ratatui` crate that supports it's [Text] type, for example:
139/// `ratatui::widgets::Paragraph`.
140///
141/// Note that the Paragraph widget includes a `ratatui::widgets::Paragraph::wrap` option
142/// that defaults to trimming leading whitespace. You need to turn this option off if you require
143/// full control over the output.
144///
145/// [docs/ratatui-tags.ebnf]: https://github.com/7sDream/tui-markup/blob/master/docs/ratatui-tags.ebnf
146#[derive(Debug)]
147pub struct RatatuiTextGenerator<P = NoopCustomTagParser<Style>> {
148    convertor: RatatuiTagConvertor<P>,
149}
150
151impl<P> Default for RatatuiTextGenerator<P> {
152    fn default() -> Self {
153        Self {
154            convertor: RatatuiTagConvertor::<P>::default(),
155        }
156    }
157}
158
159impl<P> RatatuiTextGenerator<P> {
160    /// Create a new generator, with a custom tag parser.
161    pub fn new(p: P) -> Self {
162        RatatuiTextGenerator {
163            convertor: RatatuiTagConvertor::new(p),
164        }
165    }
166}
167
168impl<'a, P> Generator<'a> for RatatuiTextGenerator<P>
169where
170    P: CustomTagParser<Output = Style>,
171{
172    type Convertor = RatatuiTagConvertor<P>;
173    type Err = GeneratorInfallible;
174    type Output = Text<'a>;
175
176    fn convertor(&mut self) -> &mut Self::Convertor {
177        &mut self.convertor
178    }
179
180    fn generate(&mut self, markup: Vec<Vec<ItemG<'a, Self>>>) -> Result<Self::Output, Self::Err> {
181        Ok(Text::from(
182            markup
183                .into_iter()
184                .map(|line| Line::from(flatten(line)))
185                .collect::<Vec<_>>(),
186        ))
187    }
188}