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