feat: highlight character-level changes in diff panes
This commit is contained in:
+82
-34
@@ -2483,6 +2483,19 @@ fn draw_diff_pane(
|
||||
}
|
||||
paint_syntax_line(frame, source_text, syntax.line(index), text_area, left);
|
||||
paint_semantic_line(frame, source_text, semantic, index, text_area, left);
|
||||
if !matches!(file.status, FileStatus::Added | FileStatus::Removed) {
|
||||
let style = Style::default().bg(if before {
|
||||
Color::Rgb(112, 40, 40)
|
||||
} else {
|
||||
Color::Rgb(32, 88, 48)
|
||||
});
|
||||
for change in &row.changes {
|
||||
let range = &change[usize::from(!before)];
|
||||
if !range.is_empty() {
|
||||
paint_grapheme_range(frame, text_area, left, source_text, range.clone(), style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Rect::new(
|
||||
content.x + gutter as u16,
|
||||
@@ -3869,11 +3882,9 @@ mod tests {
|
||||
let after = panels.diff.as_ref().unwrap().regions[2].0;
|
||||
let cell = &terminal.backend().buffer()[(after.x + 4, after.y + 1)];
|
||||
assert_eq!(cell.fg, foreground, "{source}");
|
||||
assert_eq!(cell.bg, Color::Rgb(24, 48, 32));
|
||||
let before = panels.diff.as_ref().unwrap().regions[1].0;
|
||||
let cell = &terminal.backend().buffer()[(before.x + 4, before.y + 1)];
|
||||
assert_eq!(cell.fg, Color::Magenta);
|
||||
assert_eq!(cell.bg, Color::Rgb(64, 24, 24));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3909,19 +3920,12 @@ mod tests {
|
||||
assert!(syntax.before.parse_counts().1 > 0);
|
||||
assert_eq!(syntax.before.line(1)[0].kind, SyntaxKind::Keyword);
|
||||
assert_eq!(syntax.after.line(1)[0].kind, SyntaxKind::Keyword);
|
||||
for (region, background) in [
|
||||
(
|
||||
panels.diff.as_ref().unwrap().regions[1].0,
|
||||
Color::Rgb(64, 24, 24),
|
||||
),
|
||||
(
|
||||
panels.diff.as_ref().unwrap().regions[2].0,
|
||||
Color::Rgb(24, 48, 32),
|
||||
),
|
||||
for region in [
|
||||
panels.diff.as_ref().unwrap().regions[1].0,
|
||||
panels.diff.as_ref().unwrap().regions[2].0,
|
||||
] {
|
||||
let cell = &terminal.backend().buffer()[(region.x + 4, region.y + 2)];
|
||||
assert_eq!(cell.fg, Color::Magenta);
|
||||
assert_eq!(cell.bg, background);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4146,6 +4150,71 @@ mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character_diff_backgrounds_preserve_common_unicode_and_syntax_across_viewports() {
|
||||
let mut diff = diff_comparison(vec![syntax_diff_file(
|
||||
"changes.rs",
|
||||
"let 界e\u{301} = old + old;",
|
||||
"let 界e\u{301} = new + new;",
|
||||
)]);
|
||||
diff.files_visible = false;
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(100, 12)).unwrap();
|
||||
let mut panels = WheelPanels::default();
|
||||
render_diff(&mut terminal, &diff, &mut panels);
|
||||
for (side, base, strong) in [
|
||||
(1, Color::Rgb(64, 24, 24), Color::Rgb(112, 40, 40)),
|
||||
(2, Color::Rgb(24, 48, 32), Color::Rgb(32, 88, 48)),
|
||||
] {
|
||||
let pane = panels.diff.as_ref().unwrap().regions[side].0;
|
||||
let buffer = terminal.backend().buffer();
|
||||
for column in [0, 3, 4, 6, 8, 14, 19] {
|
||||
assert_eq!(buffer[(pane.x + 4 + column, pane.y + 1)].bg, base);
|
||||
}
|
||||
for column in [10, 11, 12, 16, 17, 18] {
|
||||
assert_eq!(buffer[(pane.x + 4 + column, pane.y + 1)].bg, strong);
|
||||
}
|
||||
assert_eq!(buffer[(pane.x + 4, pane.y + 1)].fg, Color::Magenta);
|
||||
assert_eq!(buffer[(pane.x + 8, pane.y + 1)].symbol(), "界");
|
||||
assert_eq!(buffer[(pane.x + 10, pane.y + 1)].symbol(), "e\u{301}");
|
||||
}
|
||||
diff.left_before = 11;
|
||||
diff.left_after = 11;
|
||||
render_diff(&mut terminal, &diff, &mut panels);
|
||||
let after = panels.diff.as_ref().unwrap().regions[2].0;
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(after.x + 4, after.y + 1)].symbol(),
|
||||
"e"
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(after.x + 4, after.y + 1)].bg,
|
||||
Color::Rgb(32, 88, 48)
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(after.x + 7, after.y + 1)].bg,
|
||||
Color::Rgb(24, 48, 32)
|
||||
);
|
||||
diff.word_wrap = true;
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(24, 12)).unwrap();
|
||||
render_diff(&mut terminal, &diff, &mut panels);
|
||||
let after = panels.diff.as_ref().unwrap().regions[2].0;
|
||||
let buffer = terminal.backend().buffer();
|
||||
let mut words = 0;
|
||||
for y in after.y + 1..after.bottom() - 1 {
|
||||
for x in after.x + 4..after.right().saturating_sub(3) {
|
||||
if buffer[(x, y)].symbol() == "n"
|
||||
&& buffer[(x + 1, y)].symbol() == "e"
|
||||
&& buffer[(x + 2, y)].symbol() == "w"
|
||||
{
|
||||
words += 1;
|
||||
for column in x..x + 3 {
|
||||
assert_eq!(buffer[(column, y)].bg, Color::Rgb(32, 88, 48));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(words, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_picker_keeps_working_pinned_while_windowing_searching_and_changing_stage() {
|
||||
let mut diff = DiffState::new();
|
||||
@@ -4404,24 +4473,7 @@ mod tests {
|
||||
let before = geometry.regions[1].0;
|
||||
let after = geometry.regions[2].0;
|
||||
let buffer = terminal.backend().buffer();
|
||||
for (pane, background) in [
|
||||
(before, Color::Rgb(64, 24, 24)),
|
||||
(after, Color::Rgb(24, 48, 32)),
|
||||
] {
|
||||
for row in 0..5 {
|
||||
// TestBackend emits the wide glyph, not its continuation cells.
|
||||
let mut x = pane.x + 1;
|
||||
while x < pane.right() - 1 {
|
||||
assert_eq!(
|
||||
buffer[(x, pane.y + 1 + row)].bg,
|
||||
background,
|
||||
"pane={pane:?} x={x} row={row} symbol={:?}",
|
||||
buffer[(x, pane.y + 1 + row)].symbol()
|
||||
);
|
||||
x += UnicodeWidthStr::width(buffer[(x, pane.y + 1 + row)].symbol()).max(1)
|
||||
as u16;
|
||||
}
|
||||
}
|
||||
for pane in [before, after] {
|
||||
for (column, row, foreground) in [
|
||||
(0, 0, Color::Magenta),
|
||||
(3, 0, Color::LightBlue),
|
||||
@@ -5805,10 +5857,6 @@ mod tests {
|
||||
terminal.backend().buffer()[(text_x, caret.y)].fg,
|
||||
Color::DarkGray
|
||||
);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(text_x, caret.y)].bg,
|
||||
Color::Rgb(24, 48, 32)
|
||||
);
|
||||
editor.handle(Input::Home);
|
||||
editor.handle(Input::Char('v'));
|
||||
editor.handle(Input::Right);
|
||||
|
||||
Reference in New Issue
Block a user