rho-coding-agent 0.24.1

A lightweight agent harness inspired by Pi
Documentation
use super::*;
use ratatui::style::{Modifier, Style};

fn line_text(line: &Line<'_>) -> String {
    line.spans
        .iter()
        .map(|span| span.content.as_ref())
        .collect()
}

fn line_styles(line: &Line<'_>) -> Vec<Style> {
    line.spans.iter().map(|span| span.style).collect()
}

#[test]
fn styles_inline_code_bold_italic_and_links_without_markers() {
    let mut in_code_block = false;
    let lines = markdown_lines(
        "use `cargo test`, then **ship** the *fix*, [docs](https://example.com), and https://example.com",
        120,
        &mut in_code_block,
    );

    assert_eq!(
        line_text(&lines[0]),
        "use cargo test, then ship the fix, docs: https://example.com, and https://example.com"
    );
    let styles = line_styles(&lines[0]);
    assert!(styles.contains(&Theme::markdown_inline_code()));
    assert!(styles.contains(&Theme::markdown_bold()));
    assert!(styles.contains(&Theme::markdown_italic()));
    assert!(styles.contains(&Theme::markdown_link()));
    assert_eq!(Theme::markdown_bold().fg, None);
    assert_eq!(Theme::markdown_italic().fg, None);
    assert_eq!(Theme::markdown_link().fg, Theme::accent().fg);
    assert!(Theme::markdown_link().has_modifier(Modifier::UNDERLINED));
    assert_eq!(
        styles
            .iter()
            .filter(|style| **style == Theme::markdown_link())
            .count(),
        2
    );
}

#[test]
fn preserves_underscores_inside_identifiers() {
    let mut in_code_block = false;
    let lines = markdown_lines(
        "keep foo_bar_baz literal but style _this_",
        120,
        &mut in_code_block,
    );

    assert_eq!(
        line_text(&lines[0]),
        "keep foo_bar_baz literal but style this"
    );
    assert!(line_styles(&lines[0]).contains(&Theme::markdown_italic()));
}

#[test]
fn wraps_long_unicode_styled_lines_without_losing_text_or_styles() {
    let plain_prefix = "éλ".repeat(256);
    let bold = "你🙂".repeat(256);
    let plain_suffix = "界ß".repeat(256);
    let markdown = format!("{plain_prefix} **{bold}** {plain_suffix}");
    let expected = format!("{plain_prefix} {bold} {plain_suffix}");
    let mut in_code_block = false;

    let lines = markdown_lines(&markdown, 17, &mut in_code_block);
    let rendered = lines.iter().map(line_text).collect::<String>();
    let rendered_bold = lines
        .iter()
        .flat_map(|line| &line.spans)
        .filter(|span| span.style == Theme::markdown_bold())
        .map(|span| span.content.as_ref())
        .collect::<String>();

    assert_eq!(rendered, expected);
    assert_eq!(rendered_bold, bold);
    assert!(lines
        .iter()
        .all(|line| display_width(&line_text(line)) <= 17));
}

#[test]
fn stream_preview_renderer_can_hide_inactive_copy_buttons() {
    let mut lines = Vec::new();
    let mut in_code_block = false;
    push_wrapped_markdown_without_copy_button(
        &mut lines,
        "```rust\nlet x = 1;",
        40,
        &mut in_code_block,
    );
    let rendered = lines.iter().map(line_text).collect::<Vec<_>>().join("\n");

    assert!(rendered.contains("let x = 1;"), "{rendered}");
    assert!(!rendered.contains("COPY"), "{rendered}");
}

#[test]
fn renders_code_blocks_with_closed_borders() {
    let mut in_code_block = false;
    let lines = markdown_lines("```rust\nlet x = 1;\n```", 20, &mut in_code_block);

    assert_eq!(line_text(&lines[0]), "╭──────────── COPY ╮");
    assert_eq!(line_text(&lines[1]), "│ let x = 1;       │");
    assert_eq!(line_text(&lines[2]), "╰──────────────────╯");
    assert_eq!(lines[0].spans[1].content.as_ref(), " COPY ");
    assert_eq!(
        lines[0].spans[1].style,
        Theme::markdown_code_copy_button(/*hovered*/ false)
    );
    assert_eq!(lines[1].spans[0].style, Theme::markdown_code_block());
}

#[test]
fn code_block_padding_uses_display_width() {
    let mut in_code_block = false;
    let lines = markdown_lines("```\n\n```", 6, &mut in_code_block);

    assert_eq!(line_text(&lines[1]), "│ 你 │");
    assert_eq!(display_width(&line_text(&lines[1])), 6);
}

#[test]
fn code_blocks_preserve_markdown_markers_as_literal_text() {
    let mut in_code_block = false;
    let lines = markdown_lines(
        "```rust\nfn __init__() { println!(\"*ok*\"); }\n```",
        80,
        &mut in_code_block,
    );

    assert!(line_text(&lines[1]).contains("fn __init__() { println!(\"*ok*\"); }"));
    assert_eq!(line_styles(&lines[1]), vec![Theme::markdown_code_block()]);
}

#[test]
fn renders_divider_lines() {
    let mut in_code_block = false;
    let lines = markdown_lines("before\n---\nafter", 20, &mut in_code_block);

    assert_eq!(line_text(&lines[0]), "before");
    assert_eq!(line_text(&lines[1]), "".repeat(20));
    assert_eq!(lines[1].spans[0].style, Theme::dim());
    assert_eq!(line_text(&lines[2]), "after");
}

#[test]
#[ignore]
fn measure_unicode_wrap_prefix_scans() {
    use std::{hint::black_box, time::Instant};

    fn legacy_wrap(segments: &[StyledSegment], width: usize) -> Vec<Line<'static>> {
        let text = segments
            .iter()
            .map(|segment| segment.text.as_str())
            .collect::<String>();
        let chars = segments
            .iter()
            .flat_map(|segment| segment.text.chars().map(|ch| (ch, segment.style)))
            .collect::<Vec<_>>();
        wrap_line_at_whitespace_ranges(&text, width)
            .into_iter()
            .map(|range| {
                let start = text[..range.start].chars().count();
                let end = start + text[range].chars().count();
                Line::from(merge_styled_chars(&chars[start..end]))
            })
            .collect()
    }

    let text = format!(
        "{} **{}** {}",
        "éλ".repeat(4_096),
        "你🙂".repeat(4_096),
        "界ß".repeat(4_096)
    );
    let segments = markdown_inline_segments(&text);
    let iterations = 20;
    let started = Instant::now();
    for _ in 0..iterations {
        black_box(legacy_wrap(black_box(&segments), 17));
    }
    let legacy = started.elapsed();
    let started = Instant::now();
    for _ in 0..iterations {
        black_box(wrap_styled_segments(black_box(&segments), 17));
    }
    let optimized = started.elapsed();
    eprintln!(
        "legacy={legacy:?} optimized={optimized:?} speedup={:.2}x",
        legacy.as_secs_f64() / optimized.as_secs_f64()
    );
}