quinjet 0.0.74

A fast, live, keyboard-first Git source-control interface for the terminal
#[cfg_attr(not(test), expect(clippy::wildcard_imports, reason = "shared"))]
use super::*;

#[expect(
    clippy::integer_division,
    reason = "pane sizes use whole terminal cells"
)]
pub(super) fn update_diff_scroll(app: &mut App, area: Rect, side_by_side: bool, scroll: usize) {
    let sticky = pull_request_viewport::diff_row_line_index(app, side_by_side, scroll)
        .and_then(|index| {
            app.document
                .lines
                .get(index)
                .filter(|line| line.kind != DiffLineKind::FileHeader)
                .and_then(|_| app.preview_header_at(index))
        })
        .is_some();
    let area = Rect::new(
        area.x,
        area.y.saturating_add(u16::from(sticky)),
        area.width,
        area.height.saturating_sub(u16::from(sticky)),
    );
    let left_width = area
        .width
        .saturating_sub(1)
        .saturating_mul(app.diff_split_percent)
        / 100;
    let right_width = area.width.saturating_sub(1).saturating_sub(left_width);
    for row in scroll..scroll.saturating_add(usize::from(area.height)) {
        let y = area.y.saturating_add(cells(row.saturating_sub(scroll)));
        if side_by_side {
            match app.side_by_side_diff_rows.get(row).copied() {
                Some(SideBySideRow::Split(old, new)) => {
                    register_overflow(app, old, Rect::new(area.x, y, left_width, 1), 7);
                    register_overflow(
                        app,
                        new,
                        Rect::new(area.x + left_width + 1, y, right_width, 1),
                        7,
                    );
                }
                Some(SideBySideRow::Full { index, .. }) => {
                    register_overflow(app, Some(index), Rect::new(area.x, y, area.width, 1), 2);
                }
                _ => {}
            }
        } else {
            register_overflow(
                app,
                app.unified_diff_rows.get(row).copied(),
                Rect::new(area.x, y, area.width, 1),
                12,
            );
        }
    }
    app.horizontal_scroll = app.horizontal_scroll.min(app.horizontal_scroll_limit);
    if app.horizontal_scroll_limit == 0 {
        app.horizontal_scroll_regions.clear();
    }
}

fn register_overflow(app: &mut App, index: Option<usize>, area: Rect, gutter: u16) {
    let overflow = line_overflow(app, index, area.width.saturating_sub(gutter));
    let unwrapped_code = !app.wrap_code
        && index
            .and_then(|index| app.document.lines.get(index))
            .is_some_and(diff_wrapping::code_line);
    if overflow > 0 || unwrapped_code {
        app.horizontal_scroll_regions.push(Rect::new(
            area.x.saturating_add(gutter),
            area.y,
            area.width.saturating_sub(gutter),
            1,
        ));
        app.horizontal_scroll_limit = app.horizontal_scroll_limit.max(overflow);
    }
}

fn line_overflow(app: &App, index: Option<usize>, width: u16) -> usize {
    index
        .and_then(|index| app.document.lines.get(index))
        .map_or(0, |line| {
            if app.wrap_code && diff_wrapping::code_line(line) {
                return 0;
            }
            if matches!(
                line.kind,
                DiffLineKind::FileHeader | DiffLineKind::FileFooter | DiffLineKind::Image
            ) {
                return 0;
            }
            line.spans
                .iter()
                .map(|span| span.text.width())
                .sum::<usize>()
                .saturating_sub(usize::from(width))
        })
}