fmtview 0.3.4

Fast CLI viewer for highlighting, search, and diffs across JSON, JSONL, markup, Markdown, TOML, text, and Jinja
Documentation
use super::*;

#[test]
#[ignore = "performance smoke; run with cargo test --release perf_huge_wrapped_line_paths -- --ignored --nocapture --test-threads=1"]
fn perf_huge_wrapped_line_paths() {
    let message = format!(
        r#"  "message": "<root>{}</root>""#,
        r#"<item id=\"1\"><name>visible</name></item>"#.repeat(600_000)
    );
    let context = RenderContext {
        gutter_digits: 1,
        x: 0,
        width: 94,
        wrap: true,
        mode: SyntaxKind::Structured,
    };

    let started = Instant::now();
    let rows = render_logical_line_window_with_status(&message, 5, 0, 27, context);
    let first_window = started.elapsed();
    eprintln!("huge wrapped first-window render: {first_window:?}");
    assert_eq!(rows.rows.len(), 27);
    assert!(
        first_window < Duration::from_millis(1_000),
        "first-window render took {first_window:?}"
    );

    let mut temp = NamedTempFile::new().unwrap();
    writeln!(temp, "{{").unwrap();
    writeln!(temp, r#"  "id": 1,"#).unwrap();
    writeln!(temp, r#"  "kind": "huge-single-line-xml-message","#).unwrap();
    writeln!(temp, r#"  "repeats": 600000,"#).unwrap();
    writeln!(temp, "{message}").unwrap();
    writeln!(temp, "}}").unwrap();
    let file = IndexedTempFile::new("huge".to_owned(), temp).unwrap();

    let started = Instant::now();
    let visible_height = 27;
    let tail = compute_tail_position(&file, visible_height, context).unwrap();
    let tail_elapsed = started.elapsed();
    eprintln!("huge wrapped tail position: {tail_elapsed:?}");
    assert_eq!(tail.top, 4);
    assert!(
        tail_elapsed < Duration::from_millis(1_000),
        "tail position took {tail_elapsed:?}"
    );

    let request = RenderRequest {
        context,
        row_limit: render_row_limit(visible_height),
    };
    let lines = file.read_window(tail.top, visible_height).unwrap();
    let mut cache = RenderedLineCache::default();
    let started = Instant::now();
    let viewport = render_viewport(
        &lines,
        tail.top + 1,
        tail.row_offset,
        visible_height,
        request,
        &mut cache,
        ViewportRenderOptions::default(),
    );
    let tail_render = started.elapsed();
    eprintln!("huge wrapped tail-window render: {tail_render:?}");
    assert_eq!(viewport.lines.len(), visible_height);
    assert_eq!(viewport.last_line_number, Some(6));
    assert!(
        tail_render < Duration::from_millis(1_000),
        "tail-window render took {tail_render:?}"
    );

    let checkpointed_row = tail.row_offset.saturating_sub(WRAP_RENDER_CHUNK_ROWS * 2);
    let started = Instant::now();
    let checkpointed_rows = cache.get_or_render_window(
        &lines[0],
        tail.top + 1,
        checkpointed_row,
        visible_height,
        request,
    );
    let checkpointed_render = started.elapsed();
    eprintln!("huge wrapped checkpointed-window render: {checkpointed_render:?}");
    assert_eq!(checkpointed_rows.len(), visible_height);
    assert!(
        checkpointed_render < Duration::from_millis(200),
        "checkpointed-window render took {checkpointed_render:?}"
    );
}

#[test]
#[ignore = "performance smoke; run benches/viewer-performance.sh"]
fn perf_repeated_viewport_scroll_render() {
    let mut lines = Vec::new();
    for index in 0..1_200 {
        lines.push(format!(
            r#"{{"index":{index},"level":"debug","message":"scroll performance viewport {}","payload":"<root><item id=\"{index}\">value</item></root>"}}"#,
            "x".repeat(240)
        ));
    }
    let request = RenderRequest {
        context: RenderContext {
            gutter_digits: 4,
            x: 0,
            width: 96,
            wrap: true,
            mode: SyntaxKind::Structured,
        },
        row_limit: render_row_limit(27),
    };
    let mut cache = RenderedLineCache::default();
    let started = Instant::now();
    let mut rendered_rows = 0_usize;
    let mut background_cells = 0_usize;

    for top in 0..400 {
        let viewport = render_viewport(
            &lines[top..],
            top + 1,
            0,
            27,
            request,
            &mut cache,
            ViewportRenderOptions::default(),
        );
        rendered_rows += viewport.lines.len();
        background_cells += background_cell_count(&viewport.lines);
    }

    let elapsed = started.elapsed();
    eprintln!(
        "repeated viewport scroll render: {elapsed:?}, rows={rendered_rows}, background_cells={background_cells}"
    );
    assert_eq!(
        background_cells, 0,
        "non-search scrolling should not repaint styled background cells"
    );
    assert!(
        elapsed < Duration::from_millis(750),
        "repeated viewport render took {elapsed:?}"
    );
}

#[test]
#[ignore = "performance smoke; run benches/viewer-performance.sh"]
fn perf_terminal_scroll_draw_bytes() {
    let mut lines = Vec::new();
    for index in 0..1_200 {
        lines.push(format!(
            r#"{{"index":{index},"level":"debug","message":"scroll performance viewport {}","payload":"<root><item id=\"{index}\">value</item></root>"}}"#,
            "x".repeat(240)
        ));
    }
    let request = RenderRequest {
        context: RenderContext {
            gutter_digits: 4,
            x: 0,
            width: 111,
            wrap: true,
            mode: SyntaxKind::Structured,
        },
        row_limit: render_row_limit(32),
    };
    let byte_count = Rc::new(Cell::new(0_usize));
    let writer = CountingWriter {
        bytes: Rc::clone(&byte_count),
    };
    let backend = CrosstermBackend::new(writer);
    let mut terminal = ViewerTerminal::new(backend);
    let area = Rect::new(0, 0, 120, 35);
    let mut cache = RenderedLineCache::default();
    let started = Instant::now();
    let mut rendered_rows = 0_usize;
    let mut background_cells = 0_usize;

    for top in 0..400 {
        let viewport = render_viewport(
            &lines[top..],
            top + 1,
            0,
            32,
            request,
            &mut cache,
            ViewportRenderOptions::default(),
        );
        rendered_rows += viewport.lines.len();
        background_cells += background_cell_count(&viewport.lines);
        let body_lines = viewport.lines;
        let position = ViewPosition { top, row_offset: 0 };
        terminal
            .draw(TerminalFrame {
                area,
                styled: body_lines,
                sticky: Vec::new(),
                selection_mode: false,
                title: " perf ".to_owned(),
                footer_text: " q/Esc quit ".to_owned(),
                position,
                scroll_hint: terminal.scroll_hint(position),
            })
            .unwrap();
    }

    let elapsed = started.elapsed();
    eprintln!(
        "terminal scroll draw: {elapsed:?}, rows={rendered_rows}, bytes={}, background_cells={background_cells}",
        byte_count.get()
    );
    assert!(byte_count.get() > 0);
    assert_eq!(
        background_cells, 0,
        "non-search scrolling should not repaint styled background cells"
    );
    assert!(
        elapsed < Duration::from_millis(1_500),
        "terminal scroll draw took {elapsed:?}"
    );
}

#[test]
#[ignore = "performance smoke; run benches/viewer-performance.sh"]
fn perf_terminal_visual_row_scroll_bytes() {
    let message = (0..2_000)
        .map(|index| format!("chunk-{index:04}-abcdefghijklmnopqrstuvwxyz;"))
        .collect::<String>();
    let line = format!(
        r#"{{"index":0,"level":"debug","message":"{}","payload":"<root><item id=\"0\">value</item></root>"}}"#,
        message
    );
    let lines = vec![line];
    let request = RenderRequest {
        context: RenderContext {
            gutter_digits: 4,
            x: 0,
            width: 111,
            wrap: true,
            mode: SyntaxKind::Structured,
        },
        row_limit: render_row_limit(32),
    };
    let byte_count = Rc::new(Cell::new(0_usize));
    let writer = CountingWriter {
        bytes: Rc::clone(&byte_count),
    };
    let backend = CrosstermBackend::new(writer);
    let mut terminal = ViewerTerminal::new(backend);
    let area = Rect::new(0, 0, 120, 35);
    let mut cache = RenderedLineCache::default();
    let started = Instant::now();
    let mut rendered_rows = 0_usize;
    let mut background_cells = 0_usize;

    for row_offset in 0..400 {
        let viewport = render_viewport(
            &lines,
            1,
            row_offset,
            32,
            request,
            &mut cache,
            ViewportRenderOptions::default(),
        );
        rendered_rows += viewport.lines.len();
        background_cells += background_cell_count(&viewport.lines);
        let position = ViewPosition { top: 0, row_offset };
        terminal
            .draw(TerminalFrame {
                area,
                styled: viewport.lines,
                sticky: Vec::new(),
                selection_mode: false,
                title: " perf ".to_owned(),
                footer_text: " q/Esc quit ".to_owned(),
                position,
                scroll_hint: terminal.scroll_hint(position),
            })
            .unwrap();
    }

    let elapsed = started.elapsed();
    eprintln!(
        "terminal visual row scroll: {elapsed:?}, rows={rendered_rows}, bytes={}, background_cells={background_cells}",
        byte_count.get()
    );
    assert!(byte_count.get() > 0);
    assert_eq!(
        background_cells, 0,
        "non-search scrolling should not repaint styled background cells"
    );
    assert!(
        elapsed < Duration::from_millis(1_500),
        "terminal visual row scroll took {elapsed:?}"
    );
}