feat: navigate jump history with Ctrl+o and Ctrl+i

This commit is contained in:
2026-10-09 23:19:21 +02:00
parent 2264dcb6e9
commit 9a96706c36
8 changed files with 658 additions and 3 deletions
+113
View File
@@ -585,6 +585,17 @@ fn lsp_framing_lifecycle_diagnostics_and_unicode_dirty_definition() {
definition["params"]["position"],
json!({"line": 0, "character": 5})
);
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().cursor(), Cursor { row: 0, column: 3 });
assert_eq!(session.editor().lines(), &["A😀e\u{301}Z", ""]);
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), target_id);
assert_eq!(session.editor().cursor(), Cursor { row: 0, column: 1 });
assert_eq!(session.editor().lines(), &["😀e\u{301}disk", ""]);
assert!(session.editor().dirty());
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), 1);
session.execute("buffer 1");
session.execute("lsp.definition");
@@ -604,6 +615,13 @@ fn lsp_framing_lifecycle_diagnostics_and_unicode_dirty_definition() {
session.editor().message().contains("no definition found")
});
assert_eq!(session.editor().buffer_id(), 1);
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().buffer_id(), target_id);
assert_eq!(session.editor().cursor(), Cursor { row: 1, column: 0 });
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), 1);
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), 3);
session.handle(Input::Char('A'));
session.handle(Input::Char('!'));
@@ -729,6 +747,14 @@ fn lsp_old_source_and_superseded_definitions_never_steal_focus() {
session.handle(Input::Escape);
session.handle(Input::Char('u'));
assert_eq!(session.editor().lines(), &["A😀e\u{301}Z", ""]);
let cursor = session.editor().cursor();
for input in [Input::Ctrl('o'), Input::Ctrl('i')] {
session.handle(input);
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().cursor(), cursor);
}
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), 1);
drop(session);
let fixture = Fixture::new();
@@ -752,6 +778,49 @@ fn lsp_old_source_and_superseded_definitions_never_steal_focus() {
assert_eq!(session.editor().buffer_id(), active_id);
assert_eq!(session.editor().path(), Some(fixture.alternate.as_path()));
assert_eq!(session.editor().buffer_count(), 2);
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().cursor(), Cursor { row: 0, column: 0 });
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), active_id);
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), active_id);
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), 1);
drop(session);
let fixture = Fixture::new();
let mut session = fixture.open("stale");
until(&mut session, &fixture, |session| {
session.editor().diagnostics().count() == 2
});
let source_cursor = session.editor().cursor();
session.execute("lsp.definition");
until(&mut session, &fixture, |_| {
fixture.method_count("textDocument/definition") == 1
});
session.execute(&format!("edit {}", fixture.alternate.display()));
let alternate_id = session.editor().buffer_id();
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().cursor(), source_cursor);
fixture.release();
until(&mut session, &fixture, |session| {
session
.editor()
.message()
.contains("stale response complete")
});
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().cursor(), source_cursor);
assert_eq!(session.editor().buffer_count(), 2);
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), alternate_id);
assert_eq!(session.editor().path(), Some(fixture.alternate.as_path()));
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), alternate_id);
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), 1);
drop(session);
let fixture = Fixture::new();
@@ -772,6 +841,21 @@ fn lsp_old_source_and_superseded_definitions_never_steal_focus() {
.buffers()
.any(|buffer| buffer.path == Some(fixture.alternate.as_path()))
);
let target_id = session.editor().buffer_id();
let destination = session.editor().cursor();
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().cursor(), Cursor { row: 0, column: 0 });
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().buffer_id(), 1);
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), target_id);
assert_eq!(session.editor().cursor(), destination);
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), target_id);
assert_eq!(session.editor().buffer_count(), 2);
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), 2);
}
#[test]
@@ -816,6 +900,15 @@ fn lsp_server_failures_and_unsupported_results_leave_native_editor_available() {
}
}
assert_eq!(session.editor().buffer_id(), 1, "{mode}");
let cursor = session.editor().cursor();
let definitions = fixture.method_count("textDocument/definition");
for input in [Input::Ctrl('o'), Input::Ctrl('i')] {
session.handle(input);
assert_eq!(session.editor().buffer_id(), 1, "{mode}");
assert_eq!(session.editor().cursor(), cursor, "{mode}");
}
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), definitions);
session.handle(Input::Char('A'));
session.handle(Input::Char('!'));
session.handle(Input::Escape);
@@ -907,6 +1000,19 @@ fn lsp_definition_reuses_dirty_targets_without_an_enabled_target_language() {
assert!(session.editor().dirty());
assert_eq!(fs::read_to_string(&fixture.target).unwrap(), "disk\n");
assert_eq!(fixture.method_count("textDocument/didOpen"), 1);
session.handle(Input::Ctrl('o'));
assert_eq!(session.editor().buffer_id(), 1);
session.handle(Input::Ctrl('i'));
assert_eq!(session.editor().buffer_id(), target_id);
assert_eq!(session.editor().cursor(), Cursor { row: 0, column: 1 });
assert_eq!(session.editor().lines(), &["😀e\u{301}disk", ""]);
assert!(session.editor().dirty());
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), 1);
assert_eq!(fixture.method_count("textDocument/didOpen"), 1);
session.handle(Input::Char('u'));
assert_eq!(session.editor().lines(), &["disk", ""]);
assert_eq!(fs::read_to_string(&fixture.target).unwrap(), "disk\n");
}
#[test]
@@ -931,6 +1037,13 @@ fn lsp_definition_does_not_move_a_caret_that_has_moved_since_the_request() {
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().buffer_count(), 1);
assert_eq!(session.editor().cursor(), Cursor { row: 0, column: 1 });
for input in [Input::Ctrl('o'), Input::Ctrl('i')] {
session.handle(input);
assert_eq!(session.editor().buffer_id(), 1);
assert_eq!(session.editor().cursor(), Cursor { row: 0, column: 1 });
}
session.poll();
assert_eq!(fixture.method_count("textDocument/definition"), 1);
}
#[test]