fix: distinguish Ctrl+I from Tab in comparisons

This commit is contained in:
Valdemar Lorenzen
2026-10-16 12:48:13 +02:00
parent 92e1e6e8fd
commit 930735c537
8 changed files with 104 additions and 39 deletions
+84
View File
@@ -9,6 +9,10 @@ use std::{
time::{Duration, Instant},
};
#[cfg(unix)]
use crossterm::event::{
KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
};
use crossterm::{
cursor::Show,
event::{
@@ -85,6 +89,8 @@ impl crossterm::Command for ColorSchemeNotifications {
fn restore_terminal() {
let _ = disable_raw_mode();
#[cfg(unix)]
let _ = execute!(io::stdout(), PopKeyboardEnhancementFlags);
let _ = execute!(
io::stdout(),
EndSynchronizedUpdate,
@@ -128,6 +134,14 @@ impl TerminalSession {
.active = true;
if !inherited_screen {
execute!(io::stdout(), EnterAlternateScreen)?;
// Restart inherits this screen and its keyboard stack entry.
#[cfg(unix)]
execute!(
io::stdout(),
PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
)
)?;
}
execute!(
io::stdout(),
@@ -1494,6 +1508,7 @@ fn translate(key: KeyEvent) -> Option<Input> {
KeyCode::Down => Input::Down,
KeyCode::Home => Input::Home,
KeyCode::End => Input::End,
KeyCode::Tab if key.modifiers.contains(KeyModifiers::CONTROL) => Input::Ctrl('i'),
KeyCode::Tab => Input::Char('\t'),
KeyCode::BackTab => Input::BackTab,
_ => return None,
@@ -16028,6 +16043,68 @@ end
assert_eq!(viewports.syntax.len(), 1);
}
#[test]
fn diff_control_i_restores_jump_location_without_cycling_panes() {
for unified in [false, true] {
for focus in [Focus::Before, Focus::After] {
for code in [KeyCode::Char('i'), KeyCode::Tab] {
let mut editor = readonly_diff_editor(syntax_diff_file(
"jump.txt",
"old\nmiddle\ntarget",
"new\nmiddle\ntarget",
));
if unified {
editor.execute("diff.view.toggle");
}
for _ in 0..3 {
if editor.diff().unwrap().focus == focus {
break;
}
editor.handle(Input::Char('\t'));
}
assert_eq!(editor.diff().unwrap().focus, focus);
let buffer = editor.buffer_id();
for ch in "/target".chars() {
editor.handle(Input::Char(ch));
}
editor.handle(Input::Enter);
assert_eq!(editor.cursor(), Cursor { row: 2, column: 0 });
editor.handle(Input::Ctrl('o'));
assert_eq!(editor.cursor(), Cursor::default());
editor.handle(translate(KeyEvent::new(code, KeyModifiers::CONTROL)).unwrap());
assert_eq!(editor.cursor(), Cursor { row: 2, column: 0 });
assert_eq!(editor.diff().unwrap().focus, focus);
assert_eq!(editor.buffer_id(), buffer);
assert!(!editor.dirty());
editor.handle(
translate(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE)).unwrap(),
);
assert_ne!(editor.diff().unwrap().focus, focus);
for _ in 0..3 {
if editor.diff().unwrap().focus == Focus::Files {
break;
}
editor.handle(Input::Char('\t'));
}
assert_eq!(editor.diff().unwrap().focus, Focus::Files);
editor.handle(Input::Ctrl('o'));
assert_eq!(editor.cursor(), Cursor::default());
assert_eq!(editor.diff().unwrap().focus, focus);
for _ in 0..3 {
if editor.diff().unwrap().focus == Focus::Files {
break;
}
editor.handle(Input::Char('\t'));
}
assert_eq!(editor.diff().unwrap().focus, Focus::Files);
editor.handle(translate(KeyEvent::new(code, KeyModifiers::CONTROL)).unwrap());
assert_eq!(editor.cursor(), Cursor { row: 2, column: 0 });
assert_eq!(editor.diff().unwrap().focus, Focus::Files);
}
}
}
}
#[test]
fn translate_distinguishes_control_j_from_enter_and_normalizes_control_letters() {
for (code, modifiers, expected) in [
@@ -20815,6 +20892,13 @@ end
entered,
"{mode}: {before:?}"
);
for sequence in ["\x1b[>1u", "\x1b[<1u"] {
assert_eq!(
before.matches(sequence).count(),
entered,
"{mode}: {before:?}"
);
}
for protocol in [1000, 1002, 1003, 1015, 1006] {
assert_eq!(
before.matches(&format!("\x1b[?{protocol}h")).count(),