feat: show base-relative changes in conflict panes
This commit is contained in:
+71
-166
@@ -1286,165 +1286,47 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
let content = diff_inner(area);
|
||||
let x = content.right() - geometry.text_widths[side];
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(buffer[(x, content.y)].symbol(), "v");
|
||||
assert_eq!(
|
||||
buffer[(x, content.y)].bg,
|
||||
views.panels.diff_colors.row[side]
|
||||
);
|
||||
assert_eq!(buffer[(x + 8, content.y)].symbol(), "b");
|
||||
assert_eq!(buffer[(x, content.y)].bg, views.panels.diff_colors.row[0]);
|
||||
assert_eq!(
|
||||
buffer[(x + 8, content.y)].bg,
|
||||
views.panels.diff_colors.character[side]
|
||||
views.panels.diff_colors.character[0]
|
||||
);
|
||||
assert_eq!(buffer[(x, content.y + 1)].symbol(), "v");
|
||||
assert_eq!(
|
||||
buffer[(x, content.y + 1)].bg,
|
||||
views.panels.diff_colors.row[1]
|
||||
);
|
||||
}
|
||||
assert_ne!(
|
||||
line_at(&terminal, diff_inner(draft), draft.y + 1),
|
||||
line_at(&terminal, diff_inner(alternative), alternative.y + 1)
|
||||
line_at(&terminal, diff_inner(draft), draft.y + 2),
|
||||
line_at(&terminal, diff_inner(alternative), alternative.y + 2)
|
||||
);
|
||||
for area in [draft, alternative] {
|
||||
assert!(
|
||||
line_at(&terminal, diff_inner(area), area.y + 2).contains("opening context 00"),
|
||||
line_at(&terminal, diff_inner(area), area.y + 3).contains("opening context 00"),
|
||||
"Preexisting unsaved working-buffer edits must appear in both projections"
|
||||
);
|
||||
}
|
||||
let diff = session.editor().diff().unwrap();
|
||||
let file = &diff.comparison.as_ref().unwrap().files[diff.file];
|
||||
let mut reference_file = syntax_diff_file(
|
||||
&file.path.to_string_lossy(),
|
||||
&file.before.join("\n"),
|
||||
&file.after.join("\n"),
|
||||
);
|
||||
reference_file.before_eof = file.before_eof;
|
||||
reference_file.after_eof = file.after_eof;
|
||||
reference_file.hunks = crate::diff::line_hunks(
|
||||
&reference_file.before,
|
||||
reference_file.before_eof,
|
||||
&reference_file.after,
|
||||
reference_file.after_eof,
|
||||
);
|
||||
let mut reference = Editor::open(None, vec![]).unwrap();
|
||||
for _ in 1..session.editor().tab_count() {
|
||||
reference.execute("tab.new");
|
||||
}
|
||||
reference
|
||||
.set_chrome_config(
|
||||
session.editor().top_bar_config().clone(),
|
||||
session.editor().bottom_bar_config().clone(),
|
||||
BTreeMap::new(),
|
||||
)
|
||||
.unwrap();
|
||||
install_readonly_diff(&mut reference, reference_file);
|
||||
reference.handle(Input::Ctrl('w'));
|
||||
let cursor = session.editor().cursor();
|
||||
for ch in format!("{}g", cursor.row + 1).chars() {
|
||||
reference.handle(Input::Char(ch));
|
||||
}
|
||||
reference.handle(Input::Home);
|
||||
for _ in 0..cursor.column {
|
||||
reference.handle(Input::Right);
|
||||
}
|
||||
assert_eq!(reference.cursor(), cursor);
|
||||
assert_eq!(
|
||||
reference.diff_buffer_focus(),
|
||||
session.editor().diff_buffer_focus()
|
||||
);
|
||||
let mut reference_terminal =
|
||||
Terminal::new(ratatui::backend::TestBackend::new(120, 30)).unwrap();
|
||||
let mut reference_views = BufferViewports::default();
|
||||
reference_views.set_theme(Some(DARK_DIFF_THEME));
|
||||
for command in [
|
||||
None,
|
||||
Some("diff.orientation.toggle"),
|
||||
Some("diff.view.toggle"),
|
||||
Some("diff.view.toggle"),
|
||||
Some("diff.orientation.toggle"),
|
||||
"diff.orientation.toggle",
|
||||
"diff.view.toggle",
|
||||
"diff.view.toggle",
|
||||
"diff.orientation.toggle",
|
||||
] {
|
||||
if let Some(command) = command {
|
||||
session.execute(command);
|
||||
reference.execute(command);
|
||||
}
|
||||
session.execute(command);
|
||||
render(&mut terminal, &mut views, &mut session);
|
||||
render_native_diff(
|
||||
&mut reference_terminal,
|
||||
&mut reference_views,
|
||||
&mut reference,
|
||||
);
|
||||
let actual = views.panels.diff.as_ref().unwrap();
|
||||
let expected = reference_views.panels.diff.as_ref().unwrap();
|
||||
assert_eq!(actual.view_mode, expected.view_mode);
|
||||
assert_eq!(actual.split_axis, expected.split_axis);
|
||||
assert_eq!(actual.text_widths, expected.text_widths);
|
||||
for (side, ((actual_pane, _), (expected_pane, _))) in actual.regions[1..]
|
||||
.iter()
|
||||
.zip(&expected.regions[1..])
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(
|
||||
(
|
||||
actual_pane.x,
|
||||
actual_pane.y,
|
||||
actual_pane.width,
|
||||
actual_pane.height
|
||||
),
|
||||
(
|
||||
expected_pane.x,
|
||||
expected_pane.y,
|
||||
expected_pane.width,
|
||||
expected_pane.height
|
||||
)
|
||||
);
|
||||
let actual_text = Rect {
|
||||
x: diff_inner(*actual_pane).right() - actual.text_widths[side],
|
||||
width: actual.text_widths[side],
|
||||
..diff_inner(*actual_pane)
|
||||
};
|
||||
let expected_text = Rect {
|
||||
x: diff_inner(*expected_pane).right() - expected.text_widths[side],
|
||||
width: expected.text_widths[side],
|
||||
..diff_inner(*expected_pane)
|
||||
};
|
||||
for row in 0..actual_text.height {
|
||||
assert_eq!(
|
||||
line_at(&terminal, actual_text, actual_text.y + row),
|
||||
line_at(&reference_terminal, expected_text, expected_text.y + row,),
|
||||
"{command:?}, row {row}, full rows: {:?} / {:?}",
|
||||
line_at(&terminal, diff_inner(*actual_pane), actual_text.y + row),
|
||||
line_at(
|
||||
&reference_terminal,
|
||||
diff_inner(*expected_pane),
|
||||
expected_text.y + row,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
let screen = screen_text(&terminal);
|
||||
assert!(screen.contains("value = base;"));
|
||||
assert!(screen.contains("value = left;"));
|
||||
assert!(screen.contains("value = right;"));
|
||||
assert_eq!(session.editor().lines(), original_draft);
|
||||
}
|
||||
let folded_context_rows = screen_text(&terminal).matches("context").count();
|
||||
session.handle(Input::Char(']'));
|
||||
reference.handle(Input::Char(']'));
|
||||
render(&mut terminal, &mut views, &mut session);
|
||||
render_native_diff(
|
||||
&mut reference_terminal,
|
||||
&mut reference_views,
|
||||
&mut reference,
|
||||
);
|
||||
let folds = |editor: &Editor| {
|
||||
editor
|
||||
.diff()
|
||||
.unwrap()
|
||||
.display_rows
|
||||
.iter()
|
||||
.filter_map(|row| match row {
|
||||
DisplayRow::Fold { start, end } => Some((*start, *end)),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
assert_eq!(folds(session.editor()), folds(&reference));
|
||||
let expanded_context_rows = screen_text(&terminal).matches("context").count();
|
||||
assert!(expanded_context_rows > folded_context_rows);
|
||||
assert_eq!(
|
||||
expanded_context_rows,
|
||||
screen_text(&reference_terminal).matches("context").count()
|
||||
);
|
||||
let third_source = session.editor().resolver().unwrap().files[current_file]
|
||||
.sources
|
||||
.iter()
|
||||
@@ -1453,7 +1335,7 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
session.execute(&format!("resolve.source {}", third_source + 1));
|
||||
render(&mut terminal, &mut views, &mut session);
|
||||
assert!(
|
||||
line_at(&terminal, diff_inner(alternative), alternative.y + 1).contains("value = third;")
|
||||
line_at(&terminal, diff_inner(alternative), alternative.y + 2).contains("value = third;")
|
||||
);
|
||||
let right_content = diff_inner(alternative);
|
||||
assert!(
|
||||
@@ -1516,9 +1398,15 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
assert!(left_heading.contains("unsaved"));
|
||||
assert!(!right_heading.contains("unsaved"));
|
||||
assert!(right_heading.contains("read-only"));
|
||||
assert!(line_at(&terminal, left, left.y + 1).contains("manual opening context 00"));
|
||||
assert!(line_at(&terminal, right, right.y + 1).contains("opening context 00"));
|
||||
assert!(!line_at(&terminal, right, right.y + 1).contains("manual"));
|
||||
assert!(
|
||||
(left.y..left.bottom())
|
||||
.any(|y| line_at(&terminal, left, y).contains("manual opening context 00"))
|
||||
);
|
||||
assert!(
|
||||
(right.y..right.bottom())
|
||||
.any(|y| line_at(&terminal, right, y).contains("opening context 00"))
|
||||
);
|
||||
assert!(!(right.y..right.bottom()).any(|y| line_at(&terminal, right, y).contains("manual")));
|
||||
session.handle(Input::Char('u'));
|
||||
assert_eq!(session.editor().lines(), original_draft);
|
||||
session.handle(Input::Up);
|
||||
@@ -1637,7 +1525,11 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
let source_rows: Vec<_> = views
|
||||
.rects
|
||||
.iter()
|
||||
.map(|(_, area)| line_at(&terminal, *area, area.y))
|
||||
.map(|(_, area)| {
|
||||
(area.y..area.bottom())
|
||||
.map(|y| line_at(&terminal, *area, y))
|
||||
.collect::<String>()
|
||||
})
|
||||
.collect();
|
||||
assert!(source_rows.iter().any(|row| row.contains("base")));
|
||||
assert!(source_rows.iter().any(|row| row.contains("left")));
|
||||
@@ -1645,19 +1537,15 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
let history = &views.history[&views.history_key.unwrap()];
|
||||
for (column, (_, content)) in views.rects.iter().enumerate() {
|
||||
let x = content.right() - history.widths[column] as u16;
|
||||
let side = usize::from(column != 1);
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(buffer[(x, content.y)].symbol(), "v");
|
||||
assert_eq!(
|
||||
buffer[(x, content.y)].bg,
|
||||
views.panels.diff_colors.row[side]
|
||||
);
|
||||
assert_eq!(buffer[(x, content.y)].bg, views.panels.diff_colors.row[0]);
|
||||
assert_eq!(
|
||||
buffer[(x + 8, content.y)].bg,
|
||||
views.panels.diff_colors.character[side]
|
||||
views.panels.diff_colors.character[0]
|
||||
);
|
||||
assert_eq!(
|
||||
buffer[(x, content.y + 1)].bg,
|
||||
buffer[(x, content.y + 2)].bg,
|
||||
Color::Reset,
|
||||
"Shared historical context must not be highlighted as changed"
|
||||
);
|
||||
@@ -1672,22 +1560,21 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
session.editor_mut().focus_resolver_pane(right_history);
|
||||
session.execute(&format!("resolve.source {}", third_source + 1));
|
||||
render(&mut terminal, &mut views, &mut session);
|
||||
assert!(
|
||||
views
|
||||
.rects
|
||||
.iter()
|
||||
.any(|(_, area)| { line_at(&terminal, *area, area.y).contains("value = third;") })
|
||||
);
|
||||
assert!(views.rects.iter().any(|(_, area)| {
|
||||
(area.y..area.bottom()).any(|y| line_at(&terminal, *area, y).contains("value = third;"))
|
||||
}));
|
||||
let history_areas = [views.rects[0].1, views.rects[1].1, views.rects[2].1];
|
||||
let insertion_y = (history_areas[2].y..history_areas[2].bottom())
|
||||
.find(|&y| line_at(&terminal, history_areas[2], y).contains("third-only"))
|
||||
.unwrap();
|
||||
for content in history_areas.into_iter().take(2) {
|
||||
assert!(
|
||||
line_at(&terminal, content, content.y + 1)
|
||||
line_at(&terminal, content, insertion_y)
|
||||
.chars()
|
||||
.all(|ch| ch == '╱'),
|
||||
"The historical input insertion must leave aligned gaps in input/base"
|
||||
);
|
||||
}
|
||||
assert!(line_at(&terminal, history_areas[2], history_areas[2].y + 1).contains("third-only"));
|
||||
let context_rows: Vec<_> = history_areas
|
||||
.into_iter()
|
||||
.map(|content| {
|
||||
@@ -1734,9 +1621,18 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
)
|
||||
));
|
||||
render(&mut terminal, &mut views, &mut session);
|
||||
for content in history_areas {
|
||||
assert!(line_at(&terminal, content, content.y).contains("context 01"));
|
||||
}
|
||||
let context: Vec<_> = history_areas
|
||||
.into_iter()
|
||||
.map(|content| {
|
||||
line_at(&terminal, content, content.y)
|
||||
.split_once("context ")
|
||||
.unwrap()
|
||||
.1
|
||||
.trim_end()
|
||||
.to_owned()
|
||||
})
|
||||
.collect();
|
||||
assert!(context.iter().all(|line| line == &context[0]));
|
||||
assert_eq!(session.editor().pane_id(), history_pane);
|
||||
assert_eq!(session.editor().cursor(), history_cursor);
|
||||
assert!(views.scroll(
|
||||
@@ -1801,7 +1697,7 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
(right_area, "value = right;"),
|
||||
(base_area, "value = base;"),
|
||||
] {
|
||||
assert!(line_at(&terminal, area, area.y).contains(value));
|
||||
assert!((area.y..area.bottom()).any(|y| line_at(&terminal, area, y).contains(value)));
|
||||
}
|
||||
bottom.handle(Input::Char('\t'));
|
||||
assert_eq!(bottom.editor().pane_id(), right);
|
||||
@@ -1818,9 +1714,18 @@ fn resolver_renders_aligned_native_draft_alternative_and_separate_history() {
|
||||
document_wheel(MouseEventKind::ScrollDown, base_area.x, base_area.y)
|
||||
));
|
||||
render(&mut terminal, &mut views, &mut bottom);
|
||||
for area in [left_area, right_area, base_area] {
|
||||
assert!(line_at(&terminal, area, area.y).contains("context 02"));
|
||||
}
|
||||
let context: Vec<_> = [left_area, right_area, base_area]
|
||||
.into_iter()
|
||||
.map(|area| {
|
||||
line_at(&terminal, area, area.y)
|
||||
.split_once("context ")
|
||||
.unwrap()
|
||||
.1
|
||||
.trim_end()
|
||||
.to_owned()
|
||||
})
|
||||
.collect();
|
||||
assert!(context.iter().all(|line| line == &context[0]));
|
||||
let readonly = bottom.editor().context().text;
|
||||
bottom.handle(Input::Char('i'));
|
||||
bottom.handle(Input::Paste("must not edit base".into()));
|
||||
|
||||
Reference in New Issue
Block a user