fmtview 0.2.1

Fast terminal formatter and viewer for JSON, JSONL, XML-compatible markup, and formatted diffs
Documentation
use std::time::Instant;

use crate::line_index::ViewFile;

use super::super::{
    PREWARM_BUDGET, PREWARM_MAX_LINE_BYTES, PREWARM_MAX_LINES, PREWARM_PAGES,
    RENDER_CACHE_MAX_ROWS_PER_LINE, WRAP_PREWARM_LOGICAL_LINES, WRAP_RENDER_CHUNK_ROWS,
};
use super::{
    cache::{LineWindowCache, RenderedLineCache},
    types::RenderRequest,
};

pub(in crate::viewer) fn prewarm_render_cache(
    file: &dyn ViewFile,
    line_cache: &mut LineWindowCache,
    render_cache: &mut RenderedLineCache,
    top: usize,
    top_row_offset: usize,
    visible_height: usize,
    request: RenderRequest,
) {
    if visible_height == 0 || file.line_count() == 0 {
        return;
    }
    if request.context.wrap {
        prewarm_wrapped_render_cache(
            file,
            line_cache,
            render_cache,
            top,
            top_row_offset,
            visible_height,
            request,
        );
        return;
    }

    let side = visible_height.saturating_mul(PREWARM_PAGES);
    let start = top.saturating_sub(side);
    let count = visible_height
        .saturating_add(side.saturating_mul(2))
        .min(PREWARM_MAX_LINES)
        .min(file.line_count().saturating_sub(start));
    let margin = visible_height.saturating_mul(2).max(32);
    let Ok(lines) = line_cache.read(file, start, count, margin) else {
        return;
    };

    let started = Instant::now();
    for (index, line) in lines.lines.iter().enumerate() {
        if line.len() > PREWARM_MAX_LINE_BYTES {
            continue;
        }
        render_cache.get_or_render(line, start + index + 1, request);
        if started.elapsed() >= PREWARM_BUDGET {
            break;
        }
    }
}

pub(in crate::viewer) fn prewarm_wrapped_render_cache(
    file: &dyn ViewFile,
    line_cache: &mut LineWindowCache,
    render_cache: &mut RenderedLineCache,
    top: usize,
    top_row_offset: usize,
    visible_height: usize,
    request: RenderRequest,
) {
    let count = visible_height
        .saturating_add(WRAP_PREWARM_LOGICAL_LINES)
        .min(file.line_count().saturating_sub(top));
    let Ok(lines) = line_cache.read(file, top, count, WRAP_PREWARM_LOGICAL_LINES) else {
        return;
    };

    let started = Instant::now();
    if let Some(line) = lines.lines.first() {
        prewarm_wrapped_line_chunks(
            render_cache,
            line,
            top + 1,
            top_row_offset,
            visible_height,
            request,
        );
        if started.elapsed() >= PREWARM_BUDGET {
            return;
        }
    }

    for (index, line) in lines
        .lines
        .iter()
        .enumerate()
        .skip(1)
        .take(WRAP_PREWARM_LOGICAL_LINES)
    {
        render_cache.get_or_render_window(line, top + index + 1, 0, visible_height, request);
        if started.elapsed() >= PREWARM_BUDGET {
            break;
        }
    }
}

pub(in crate::viewer) fn prewarm_wrapped_line_chunks(
    render_cache: &mut RenderedLineCache,
    line: &str,
    line_number: usize,
    top_row_offset: usize,
    visible_height: usize,
    request: RenderRequest,
) {
    let status = render_cache.status(line_number);
    if status.total_rows.is_none() && status.known_rows > 0 {
        render_cache.get_or_render_window(
            line,
            line_number,
            status.known_rows,
            visible_height,
            request,
        );
    }

    if top_row_offset > 0 {
        let previous = top_row_offset.saturating_sub(WRAP_RENDER_CHUNK_ROWS);
        render_cache.get_or_render_window(line, line_number, previous, visible_height, request);
    }
}

pub(in crate::viewer) fn render_row_limit(visible_height: usize) -> usize {
    visible_height
        .saturating_mul(2)
        .clamp(32, RENDER_CACHE_MAX_ROWS_PER_LINE)
}