fix: target caret excerpts and retain diff viewport offsets
This commit is contained in:
+125
-1
@@ -172,6 +172,18 @@ impl BufferViewports {
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_session(&mut self, frame: &mut Frame, session: &mut EditorSession) {
|
||||
self.draw(frame, session.editor());
|
||||
if let Some((comparison_id, file, top, left_after)) = self
|
||||
.panels
|
||||
.diff
|
||||
.as_ref()
|
||||
.and_then(|geometry| geometry.viewport)
|
||||
{
|
||||
session.set_diff_viewport(comparison_id, file, top, left_after);
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll(&mut self, session: &mut EditorSession, mouse: MouseEvent) -> bool {
|
||||
if !matches!(
|
||||
mouse.kind,
|
||||
@@ -274,7 +286,7 @@ pub fn run(session: &mut EditorSession) -> io::Result<()> {
|
||||
redraw |= session.poll();
|
||||
if redraw {
|
||||
draw_frame(&mut terminal, &mut output, |frame| {
|
||||
viewports.draw(frame, session.editor())
|
||||
viewports.draw_session(frame, session)
|
||||
})?;
|
||||
redraw = false;
|
||||
}
|
||||
@@ -708,6 +720,7 @@ fn draw(
|
||||
|
||||
struct DiffGeometry {
|
||||
regions: [(Rect, Focus); 3],
|
||||
viewport: Option<(u64, usize, usize, usize)>,
|
||||
}
|
||||
|
||||
fn diff_panel(frame: &mut Frame, area: Rect, title: &str, focused: bool) -> Rect {
|
||||
@@ -1195,6 +1208,7 @@ fn draw_diff(
|
||||
(Rect::default(), Focus::Before),
|
||||
(Rect::default(), Focus::After),
|
||||
],
|
||||
viewport: None,
|
||||
});
|
||||
if let Some(editor) = editor {
|
||||
draw_diff_popups(frame, editor, area, None, panels);
|
||||
@@ -1348,6 +1362,9 @@ fn draw_diff(
|
||||
(before_area, Focus::Before),
|
||||
(after_area, Focus::After),
|
||||
],
|
||||
viewport: diff
|
||||
.comparison_id
|
||||
.map(|id| (id, diff.file, top, after_left)),
|
||||
});
|
||||
if let Some(picker) = &diff.files
|
||||
&& diff.files_visible
|
||||
@@ -3445,6 +3462,113 @@ mod tests {
|
||||
assert!(editor.dirty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_diff_draw_retains_rendered_offsets_until_caret_crosses_edges() {
|
||||
let directory = Directory::new();
|
||||
let original = (0..80)
|
||||
.map(|row| format!("old row {row:03} {}", "x".repeat(100)))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let path = directory.file("source.txt", &original);
|
||||
let git = |args: &[&str]| {
|
||||
let output = std::process::Command::new("git")
|
||||
.args(args)
|
||||
.current_dir(&directory.0)
|
||||
.env("GIT_CONFIG_NOSYSTEM", "1")
|
||||
.env("GIT_CONFIG_GLOBAL", "/dev/null")
|
||||
.env("GIT_AUTHOR_NAME", "Diff fixture")
|
||||
.env("GIT_AUTHOR_EMAIL", "fixture@example.invalid")
|
||||
.env("GIT_COMMITTER_NAME", "Diff fixture")
|
||||
.env("GIT_COMMITTER_EMAIL", "fixture@example.invalid")
|
||||
.env("GIT_AUTHOR_DATE", "2000-01-01T00:00:00Z")
|
||||
.env("GIT_COMMITTER_DATE", "2000-01-01T00:00:00Z")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{output:?}");
|
||||
};
|
||||
git(&["init", "-q"]);
|
||||
git(&["add", "."]);
|
||||
git(&["commit", "-qm", "base"]);
|
||||
directory.file("source.txt", &original.replace("old row", "new row"));
|
||||
let mut session = EditorSession::open(Some(path), vec![], None).unwrap();
|
||||
let ready = |session: &mut EditorSession, phase| {
|
||||
let deadline = std::time::Instant::now() + Duration::from_secs(10);
|
||||
loop {
|
||||
session.poll();
|
||||
let diff = session.editor().diff().unwrap();
|
||||
assert!(diff.error.is_none(), "{:?}", diff.error);
|
||||
if diff.phase == phase && !diff.loading {
|
||||
break;
|
||||
}
|
||||
assert!(std::time::Instant::now() < deadline, "{diff:?}");
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
};
|
||||
session.execute("diff");
|
||||
ready(&mut session, Phase::Before);
|
||||
session.handle(Input::Down);
|
||||
session.handle(Input::Enter);
|
||||
session.handle(Input::Enter);
|
||||
ready(&mut session, Phase::View);
|
||||
session.handle(Input::Char('\t'));
|
||||
session.handle(Input::Char('\t'));
|
||||
assert!(session.editor().diff_editing());
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(120, 18)).unwrap();
|
||||
let mut views = BufferViewports::default();
|
||||
let mut render = |session: &mut EditorSession| {
|
||||
terminal
|
||||
.draw(|frame| views.draw_session(frame, session))
|
||||
.unwrap();
|
||||
terminal.get_cursor_position().unwrap()
|
||||
};
|
||||
render(&mut session);
|
||||
for _ in 0..40 {
|
||||
session.handle(Input::Down);
|
||||
render(&mut session);
|
||||
}
|
||||
let top = session.editor().diff().unwrap().top;
|
||||
assert!(top > 0);
|
||||
let bottom_caret = render(&mut session);
|
||||
session.handle(Input::Up);
|
||||
let reversed_caret = render(&mut session);
|
||||
assert_eq!(session.editor().diff().unwrap().top, top);
|
||||
assert_eq!(reversed_caret.y + 1, bottom_caret.y);
|
||||
while session
|
||||
.editor()
|
||||
.diff()
|
||||
.unwrap()
|
||||
.after_display_row(session.editor().cursor().row)
|
||||
.unwrap()
|
||||
> top
|
||||
{
|
||||
session.handle(Input::Up);
|
||||
render(&mut session);
|
||||
assert_eq!(session.editor().diff().unwrap().top, top);
|
||||
}
|
||||
session.handle(Input::Up);
|
||||
render(&mut session);
|
||||
assert!(session.editor().diff().unwrap().top < top);
|
||||
for _ in 0..80 {
|
||||
session.handle(Input::Right);
|
||||
render(&mut session);
|
||||
}
|
||||
let left = session.editor().diff().unwrap().left_after;
|
||||
assert!(left > 0);
|
||||
let right_caret = render(&mut session);
|
||||
session.handle(Input::Left);
|
||||
let reversed_caret = render(&mut session);
|
||||
assert_eq!(session.editor().diff().unwrap().left_after, left);
|
||||
assert_eq!(reversed_caret.x + 1, right_caret.x);
|
||||
while session.editor().cursor().column > left {
|
||||
session.handle(Input::Left);
|
||||
render(&mut session);
|
||||
assert_eq!(session.editor().diff().unwrap().left_after, left);
|
||||
}
|
||||
session.handle(Input::Left);
|
||||
render(&mut session);
|
||||
assert!(session.editor().diff().unwrap().left_after < left);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_wheel_routes_right_tree_and_both_panes_without_touching_document() {
|
||||
let directory = Directory::new();
|
||||
|
||||
Reference in New Issue
Block a user