strop-editor 0.3.1

strop — a modal text editor in Rust: see the cut before you make it
//! `:help` buffer decoration (0.3.1): the help text is generated by
//! editor/help.rs; here its rows get house-style color — the same
//! typed-decoration pattern the git surfaces use (0010 §5), never a
//! special-purpose renderer fork.

use ratatui::style::{Modifier, Style};
use ratatui::text::Span;

use super::{ACCENT, MUTED, TEXT};

/// One help row → styled spans:
/// - the header line wears accent bold
/// - `[section]` headers are accent bold
/// - binding rows: keys in accent, description in text
/// - planned `(soon)` rows are muted end to end
pub(crate) fn row_spans(text: &str) -> Vec<Span<'static>> {
    if text.starts_with("strop help") {
        return vec![Span::styled(
            text.to_string(),
            Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
        )];
    }
    if text.starts_with('[') && text.ends_with(']') {
        return vec![Span::styled(
            text.to_string(),
            Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
        )];
    }
    let planned = text.ends_with("(soon)");
    let fg = if planned { MUTED } else { TEXT };
    let key_fg = if planned { MUTED } else { ACCENT };
    // rows are `  <keys padded>  <desc>` — the two-space gap after the
    // key column is the split
    match text
        .strip_prefix("  ")
        .and_then(|t| t.find("  ").map(|i| i + 2))
    {
        Some(split) if split < text.len() => {
            let (keys, desc) = text.split_at(split);
            vec![
                Span::styled(keys.to_string(), Style::default().fg(key_fg)),
                Span::styled(desc.to_string(), Style::default().fg(fg)),
            ]
        }
        _ => vec![Span::styled(text.to_string(), Style::default().fg(fg))],
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sections_and_keys_get_accent() {
        let header = row_spans("[leader]");
        assert_eq!(header[0].style.fg, Some(ACCENT));

        let row = row_spans("  space f  file finder");
        assert_eq!(row[0].content, "  space f");
        assert_eq!(row[0].style.fg, Some(ACCENT));
        assert_eq!(row[1].content, "  file finder");
        assert_eq!(row[1].style.fg, Some(TEXT));

        let soon = row_spans("  space j  jumplist picker  (soon)");
        assert!(soon.iter().all(|s| s.style.fg == Some(MUTED)));
    }
}