Skip to content

Line: Reduce boilerplate for styling Line #493

Open
@kdheepak

Description

It's not uncommon to write code like this:

    let mut text: Vec<Line> = vec![];
    text.push(
      Line::from(vec![
        "Press ".into(),
        Span::styled("j", Style::default().fg(Color::Red)),
        " or ".into(),
        Span::styled("k", Style::default().fg(Color::Red)),
        " to ".into(),
        Span::styled("increment", Style::default().fg(Color::Blue)),
        " or ".into(),
        Span::styled("decrement", Style::default().fg(Color::Blue)),
        ".".into(),
      ]),
    );
    f.render_widget(Paragraph::new(text), rect)

With style shorthands:

    let mut text: Vec<Line> = vec![];
    text.push(
      Line::from(vec![
        "Press ".into(),
        "j".red(),
        " or ".into(),
        "k".red(),
        " to ".into(),
        "increment".blue(),
        " or ".into(),
        "decrement".blue(),
        ".".into(),
      ]),
    );
    f.render_widget(Paragraph::new(text), rect)

However, I believe this can be made simpler with a macro:

    let mut text: Vec<Line> = vec![];
    text.push(styled_line!("Press {j:Red} or {k:Red} to {increment:Blue} or {decrement:Blue}."));

Here's an example of the macro that works for the above case and just 3 colors:

macro_rules! styled_line {
  ($text:expr) => {{
    let mut elements: Vec<Span> = Vec::new();
    let regex = regex::Regex::new(r"\{([^:]+):([^}]+)\}").unwrap();
    let mut last_end = 0;

    for cap in regex.captures_iter($text) {
      let start = cap.get(0).unwrap().start();
      let key = cap.get(1).unwrap().as_str();
      let value = cap.get(2).unwrap().as_str();

      if start > last_end {
        elements.push(Span::styled(&($text[last_end..start]), Style::default()));
      }
      elements.push(Span::styled(
        key,
        match value {
          "Red" => Style::default().fg(Color::Red),
          "Blue" => Style::default().fg(Color::Blue),
          "Green" => Style::default().fg(Color::Green),
          _ => Style::default(),
        },
      ));

      last_end = cap.get(0).unwrap().end();
    }

    if last_end < $text.len() {
      elements.push(Span::styled(&($text[last_end..]), Style::default()));
    }

    Line::from(elements)
  }};
}

This issue is to discuss whether it makes sense to include such a macro into ratatui. Including this functionality increases the maintenance burden however it will decrease boilerplate for users. If it does make sense to include such a macro, what syntax should it support?

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions