fix: restore tab and diff context through jump history

This commit is contained in:
Valdemar Lorenzen
2026-10-12 14:45:49 +02:00
parent 6b7cfdea20
commit 054225686e
8 changed files with 679 additions and 39 deletions
+176 -2
View File
@@ -682,6 +682,15 @@ fn shown_diff_lines(session: &EditorSession, focus: Focus) -> Vec<usize> {
.collect()
}
fn palette_action(session: &mut EditorSession, action: &str) {
session.handle(Input::Ctrl('p'));
for ch in action.chars() {
session.handle(Input::Char(ch));
}
session.handle(Input::Enter);
assert!(session.editor().palette().is_none());
}
impl Drop for Fixture {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.root);
@@ -3630,6 +3639,10 @@ fn lsp_diff_definition_routes_exact_changed_lines_in_the_same_comparison() {
open_comparison(&mut session, &fixture, &before, after.as_deref(), focus);
let tab = session.editor().tab_id();
let comparison = session.editor().diff().unwrap().comparison_id;
let source_buffer = session.editor().buffer_id();
let source_cursor = session.editor().cursor();
let source_file = session.editor().diff().unwrap().file;
let source_excerpts = shown_diff_lines(&session, focus);
let files = &session
.editor()
.diff()
@@ -3691,6 +3704,125 @@ fn lsp_diff_definition_routes_exact_changed_lines_in_the_same_comparison() {
);
assert_eq!(session.editor().diff().unwrap().comparison_id, comparison);
assert_eq!(session.editor().tab_count(), 1);
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().cursor(), Cursor { row, column: 3 });
assert_eq!(session.editor().diff().unwrap().file, target_index);
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().buffer_id(), source_buffer);
assert_eq!(session.editor().cursor(), source_cursor);
assert_eq!(session.editor().diff().unwrap().file, source_file);
assert_eq!(session.editor().diff().unwrap().focus, focus);
assert_eq!(shown_diff_lines(&session, focus), source_excerpts);
palette_action(&mut session, "core.jump_forward");
assert_eq!(session.editor().cursor(), Cursor { row, column: 3 });
assert_eq!(session.editor().diff().unwrap().file, target_index);
palette_action(&mut session, "core.jump_forward");
assert_eq!(
session.editor().cursor(),
Cursor {
row: same_row,
column: 2
}
);
assert_eq!(session.editor().diff().unwrap().comparison_id, comparison);
assert_eq!(session.editor().tab_id(), tab);
assert_eq!(session.editor().tab_count(), 1);
}
}
#[test]
fn lsp_diff_external_definition_jump_history_restores_the_exact_comparison_view() {
for (historical, focus) in [
(false, Focus::Before),
(false, Focus::After),
(true, Focus::Before),
(true, Focus::After),
] {
let fixture = Fixture::new();
let external = Fixture::new();
let (before, after) = diff_repository(&fixture, historical);
let mut session = fixture.open("controlled-diff");
session.execute(&format!("edit {}", external.source.display()));
session.handle(Input::Char('i'));
session.handle(Input::Paste("dirty ".into()));
session.handle(Input::Escape);
let external_buffer = session.editor().buffer_id();
let external_text = session.editor().context().text;
session.execute("buffer 1");
let sibling = session.editor().pane_id();
let sibling_buffer = session.editor().buffer_id();
session.execute("pane.split_vertical");
open_comparison(&mut session, &fixture, &before, after.as_deref(), focus);
if !historical && focus == Focus::After {
session.handle(Input::Char('A'));
session.handle(Input::Char('!'));
session.handle(Input::Escape);
}
let source_tab = session.editor().tab_id();
let source_pane = session.editor().pane_id();
let source_buffer = session.editor().buffer_id();
let source_cursor = session.editor().cursor();
let source_text = session.editor().context().text;
let source_dirty = session.editor().dirty();
let layout = format!("{:?}", session.editor().pane_layout());
let diff = session.editor().diff().unwrap();
let comparison = diff.comparison_id;
let file = diff.file;
let excerpts = shown_diff_lines(&session, focus);
request_position(&mut session, &fixture, "textDocument/definition");
position_reply(
&mut session,
&fixture,
1,
json!({
"action": "definition-result", "path": external.source, "row": 0, "column": 11
}),
);
let external_tab = session.editor().tab_id();
let external_pane = session.editor().pane_id();
assert_ne!(external_tab, source_tab);
assert_eq!(session.editor().buffer_id(), external_buffer);
for _ in 0..2 {
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().tab_id(), source_tab);
assert_eq!(session.editor().pane_id(), source_pane);
assert_eq!(session.editor().buffer_id(), source_buffer);
assert_eq!(session.editor().cursor(), source_cursor);
assert_eq!(session.editor().context().text, source_text);
assert_eq!(session.editor().dirty(), source_dirty);
assert_eq!(format!("{:?}", session.editor().pane_layout()), layout);
assert_eq!(session.editor().diff().unwrap().comparison_id, comparison);
assert_eq!(session.editor().diff().unwrap().file, file);
assert_eq!(session.editor().diff().unwrap().focus, focus);
assert_eq!(shown_diff_lines(&session, focus), excerpts);
assert_eq!(
session.editor().pane_buffer_id(sibling),
Some(sibling_buffer)
);
palette_action(&mut session, "core.jump_forward");
assert_eq!(session.editor().tab_id(), external_tab);
assert_eq!(session.editor().pane_id(), external_pane);
assert_eq!(session.editor().buffer_id(), external_buffer);
assert_eq!(session.editor().cursor(), Cursor { row: 0, column: 9 });
assert_eq!(session.editor().context().text, external_text);
assert!(session.editor().dirty());
assert!(session.editor().diff().is_none());
assert_eq!(session.editor().tab_count(), 2);
assert!(session.editor().tab_is_diff(0));
assert!(!session.editor().tab_is_diff(1));
}
session.handle(Input::Char('u'));
assert_eq!(session.editor().context().text, "A😀e\u{301}Z\n");
session.execute("tab.previous");
assert_eq!(session.editor().diff().unwrap().comparison_id, comparison);
assert_eq!(session.editor().cursor(), source_cursor);
if source_dirty {
session.handle(Input::Char('u'));
assert_eq!(
session.editor().context().text,
comparison_text("source", "after")
);
}
}
}
@@ -3711,6 +3843,18 @@ fn lsp_diff_unchanged_context_and_omitted_symbols_open_shared_ordinary_tabs() {
] {
session.handle(input);
}
if row == 18 {
for input in [
Input::Char('g'),
Input::Char('t'),
Input::Right,
Input::Right,
Input::Right,
] {
session.handle(input);
}
assert_eq!(session.editor().cursor(), Cursor { row, column: 3 });
}
let text = session.editor().context().text;
let source = session.editor().buffer_id();
let cursor = session.editor().cursor();
@@ -3729,6 +3873,7 @@ fn lsp_diff_unchanged_context_and_omitted_symbols_open_shared_ordinary_tabs() {
"action": "definition-result", "path": fixture.source, "row": row, "column": 5
}),
);
let ordinary_tab = session.editor().tab_id();
assert_eq!(session.editor().tab_count(), 2);
assert_ne!(session.editor().tab_id(), tab);
assert!(session.editor().diff().is_none());
@@ -3736,13 +3881,22 @@ fn lsp_diff_unchanged_context_and_omitted_symbols_open_shared_ordinary_tabs() {
assert_eq!(session.editor().cursor(), Cursor { row, column: 3 });
assert_eq!(session.editor().context().text, text);
assert!(session.editor().dirty());
session.execute("tab.previous");
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().tab_id(), tab);
assert_eq!(session.editor().pane_id(), pane);
assert_eq!(format!("{:?}", session.editor().pane_layout()), layout);
assert_eq!(session.editor().cursor(), cursor);
assert_eq!(session.editor().diff().unwrap().comparison_id, comparison);
assert_eq!(shown_diff_lines(&session, Focus::After), excerpts);
palette_action(&mut session, "core.jump_forward");
assert_eq!(session.editor().tab_id(), ordinary_tab);
assert_eq!(session.editor().buffer_id(), source);
assert_eq!(session.editor().cursor(), Cursor { row, column: 3 });
assert!(session.editor().diff().is_none());
assert_eq!(session.editor().tab_count(), 2);
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().tab_id(), tab);
assert_eq!(session.editor().cursor(), cursor);
session.handle(Input::Char('u'));
assert_eq!(
session.editor().context().text,
@@ -3817,6 +3971,7 @@ fn lsp_diff_unchanged_snapshot_uri_opens_an_immutable_tab_that_outlives_the_comp
let snapshot = session.editor().buffer_id();
let comparison = session.editor().diff().unwrap().comparison_id;
let cursor = session.editor().cursor();
let source_tab = session.editor().tab_id();
request_position(&mut session, &fixture, "textDocument/definition");
position_reply(
&mut session,
@@ -3826,6 +3981,7 @@ fn lsp_diff_unchanged_snapshot_uri_opens_an_immutable_tab_that_outlives_the_comp
"action": "definition-result", "uri": document["uri"], "row": 39, "column": 5
}),
);
let ordinary_tab = session.editor().tab_id();
assert_eq!(session.editor().tab_count(), 2);
assert!(session.editor().diff().is_none());
assert_eq!(session.editor().buffer_id(), snapshot);
@@ -3834,11 +3990,29 @@ fn lsp_diff_unchanged_snapshot_uri_opens_an_immutable_tab_that_outlives_the_comp
session.editor().context().text,
comparison_text("source", version)
);
session.execute("tab.previous");
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().tab_id(), source_tab);
assert_eq!(session.editor().cursor(), cursor);
assert_eq!(session.editor().diff().unwrap().comparison_id, comparison);
palette_action(&mut session, "core.jump_forward");
assert_eq!(session.editor().tab_id(), ordinary_tab);
assert_eq!(session.editor().cursor(), Cursor { row: 39, column: 3 });
assert!(session.editor().diff().is_none());
// Close the source without traversing away from the timeline's destination.
session.execute("tab.previous");
session.execute("diff.close");
session.execute("tab.next");
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().tab_id(), source_tab);
assert_eq!(session.editor().buffer_id(), snapshot);
assert_eq!(session.editor().cursor(), cursor);
assert!(session.editor().diff().is_none());
palette_action(&mut session, "core.jump_forward");
assert_eq!(session.editor().tab_id(), ordinary_tab);
assert_eq!(session.editor().cursor(), Cursor { row: 39, column: 3 });
assert_eq!(session.editor().tab_count(), 2);
assert!(!session.editor().tab_is_diff(0));
assert!(!session.editor().tab_is_diff(1));
for input in [
Input::Char('i'),
Input::Paste("bad".into()),