quinjet 0.0.40

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::too_many_lines,
    reason = "the draw pass reads better as one top-to-bottom pass"
)]
pub(super) fn draw_history_sidebar(
    frame: &mut Frame<'_>,
    area: Rect,
    app: &mut App,
    theme: &Theme,
    link_hits: &mut Vec<LinkHit>,
) -> Vec<SidebarHitArea> {
    let title = if app.filter.is_empty() {
        format!(
            " History · {}  {}{}  [b branch] ",
            app.history_branch_label(),
            app.history.len(),
            if app.history_complete { "" } else { "+" }
        )
    } else {
        format!(
            " History · {}  /{} ",
            app.history_branch_label(),
            app.filter
        )
    };
    let block = panel_block(
        title,
        app.focus == Focus::Sidebar && app.modal.is_none(),
        theme,
    );
    let inner = block.inner(area);
    frame.render_widget(block, area);
    if inner.height == 0 {
        return Vec::new();
    }

    let visible = app.visible_commit_indices();
    let height = inner.height as usize;
    app.sidebar_viewport(app.history_cursor, height, visible.len());
    let mut hits = Vec::new();
    let end = (app.sidebar_offset + height).min(visible.len());
    for (row_offset, index) in visible
        .iter()
        .take(end)
        .skip(app.sidebar_offset)
        .enumerate()
    {
        let cursor = app.sidebar_offset + row_offset;
        let Some(commit) = app.history.get(*index) else {
            continue;
        };
        let selected = cursor == app.history_cursor;
        let y = inner.y + cells(row_offset);
        let row_style = Style::default().bg(if selected {
            theme.selected
        } else {
            theme.panel
        });
        let graph = history_glyph(commit, cursor);
        let badge = commit
            .decorations
            .first()
            .map(|decoration| format!("  {}", clean_decoration(decoration)))
            .unwrap_or_default();
        let reserved = commit.short_id.width() + 8;
        let subject = truncate_middle(
            &commit.subject,
            (inner.width as usize).saturating_sub(reserved + badge.width()),
        );
        let line = Line::from(vec![
            Span::styled(
                if selected { "" } else { "   " },
                Style::default().fg(theme.accent),
            ),
            Span::styled(graph, Style::default().fg(graph_color(cursor, theme))),
            Span::styled(
                subject,
                Style::default().fg(theme.text).add_modifier(if selected {
                    Modifier::BOLD
                } else {
                    Modifier::empty()
                }),
            ),
            Span::styled(badge, Style::default().fg(theme.modified)),
        ]);
        frame.render_widget(
            Paragraph::new(line).style(row_style),
            Rect::new(inner.x, y, inner.width.saturating_sub(10), 1),
        );
        let sha_area = Rect::new(
            inner
                .right()
                .saturating_sub(1)
                .saturating_sub(cells(commit.short_id.width())),
            y,
            cells(commit.short_id.width()),
            1,
        );
        frame.render_widget(
            Paragraph::new(Line::from(link_span(
                commit.short_id.clone(),
                app.commit_open_target(&commit.id),
                sha_area,
                theme,
                link_hits,
            )))
            .alignment(Alignment::Right)
            .style(Style::default().bg(if selected {
                theme.selected
            } else {
                theme.panel
            })),
            Rect::new(inner.right().saturating_sub(10), y, 9, 1),
        );
        hits.push(SidebarHitArea {
            area: Rect::new(inner.x, y, inner.width, 1),
            target: SidebarHit::Commit(*index),
        });
    }

    draw_scrollbar(frame, inner, app.sidebar_offset, visible.len(), theme);

    if visible.is_empty() {
        frame.render_widget(
            Paragraph::new(if app.history_loading {
                "\n  Loading commit history…"
            } else if app.history.is_empty() {
                "\n  No commits yet"
            } else {
                "\n  No commits match this filter"
            })
            .style(Style::default().fg(theme.muted)),
            inner,
        );
    } else if app.history_loading && inner.height > 1 {
        let area = Rect::new(inner.x, inner.bottom().saturating_sub(1), inner.width, 1);
        frame.render_widget(
            Paragraph::new("  Loading more commits…")
                .style(Style::default().fg(theme.accent).bg(theme.panel_alt)),
            area,
        );
    }
    hits
}