Fix Git diff rendering

This commit is contained in:
Georg Bauer
2026-07-27 22:01:24 +02:00
parent 6001e7312b
commit a7f26ccb71
3 changed files with 221 additions and 68 deletions

View File

@@ -1,5 +1,6 @@
use super::*;
use crate::app::git::{GitChangeKind, GitDiffLine, GitDiffLineKind, GitDiffMode, GitDiffRow};
use crate::app::{git_diff_new_scroll_id, git_diff_old_scroll_id};
use iced::widget::column;
impl App {
@@ -259,13 +260,7 @@ impl App {
} else {
split_diff(diff.split_rows())
};
let diff_scroll = scrollable(content)
.direction(scrollable::Direction::Both {
vertical: scrollable::Scrollbar::new(),
horizontal: scrollable::Scrollbar::new(),
})
.height(Length::Fill)
.width(Length::Fill);
let diff_scroll = scrollable(content).height(Length::Fill).width(Length::Fill);
let dialog = container(
column![
row![
@@ -342,17 +337,59 @@ fn unified_diff(lines: &[GitDiffLine]) -> Element<'_, Message> {
}
fn split_diff(rows: Vec<GitDiffRow>) -> Element<'static, Message> {
let mut content = column![].spacing(0).width(Length::Fill);
let width = split_diff_width(&rows);
let old = scrollable(split_diff_half(&rows, true, width))
.id(git_diff_old_scroll_id())
.direction(scrollable::Direction::Horizontal(
scrollable::Scrollbar::new(),
))
.on_scroll(Message::GitDiffScrolled)
.width(Length::Fill);
let new = scrollable(split_diff_half(&rows, false, width))
.id(git_diff_new_scroll_id())
.direction(scrollable::Direction::Horizontal(
scrollable::Scrollbar::new(),
))
.on_scroll(Message::GitDiffScrolled)
.width(Length::Fill);
row![old, rule::vertical(1), new].into()
}
fn split_diff_width(rows: &[GitDiffRow]) -> f32 {
rows.iter()
.flat_map(|row| match row {
GitDiffRow::Header(line) => [Some(line), None],
GitDiffRow::Pair { old, new } => [old.as_ref(), new.as_ref()],
})
.flatten()
.map(|line| {
line.text
.chars()
.map(|character| if character == '\t' { 4 } else { 1 })
.sum::<usize>()
})
.max()
.unwrap_or_default() as f32
* 8.0
+ 66.0
}
fn split_diff_half(rows: &[GitDiffRow], old: bool, width: f32) -> Element<'static, Message> {
let mut content = column![].spacing(0).width(width);
for row in rows {
content = content.push(match row {
GitDiffRow::Header(line) => diff_line(&line, false),
GitDiffRow::Pair { old, new } => row![
diff_half(old.as_ref(), true),
rule::vertical(1),
diff_half(new.as_ref(), false),
]
.height(24)
.into(),
GitDiffRow::Header(line) => diff_line(line, old),
GitDiffRow::Pair {
old: old_line,
new: new_line,
} => diff_half(
if old {
old_line.as_ref()
} else {
new_line.as_ref()
},
old,
),
});
}
content.into()
@@ -427,3 +464,28 @@ fn staged_color() -> Color {
fn modal_backdrop_style(_: &Theme) -> container::Style {
container::Style::default().background(Color::from_rgba8(0, 0, 0, 0.72))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_diff_width_covers_the_longest_side_and_tabs() {
let rows = [GitDiffRow::Pair {
old: Some(GitDiffLine {
kind: GitDiffLineKind::Deletion,
old_number: Some(1),
new_number: None,
text: "short".into(),
}),
new: Some(GitDiffLine {
kind: GitDiffLineKind::Addition,
old_number: None,
new_number: Some(1),
text: "\tlong".into(),
}),
}];
assert_eq!(split_diff_width(&rows), 130.0);
}
}