Fix alt+left on ghostty (#12503)

This commit is contained in:
Tommaso Sciortino
2025-11-03 20:30:42 -08:00
committed by GitHub
parent b259153403
commit b5315bfc20
3 changed files with 22 additions and 20 deletions
@@ -906,7 +906,6 @@ describe('Kitty Sequence Parsing', () => {
// Should broadcast immediately as it's not a valid kitty pattern // Should broadcast immediately as it's not a valid kitty pattern
expect(keyHandler).toHaveBeenCalledWith( expect(keyHandler).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
name: '',
sequence: '\x1b[m', sequence: '\x1b[m',
paste: false, paste: false,
}), }),
@@ -1007,7 +1006,6 @@ describe('Kitty Sequence Parsing', () => {
expect(keyHandler).toHaveBeenNthCalledWith( expect(keyHandler).toHaveBeenNthCalledWith(
2, 2,
expect.objectContaining({ expect.objectContaining({
name: '',
sequence: '\x1b[!', sequence: '\x1b[!',
}), }),
); );
@@ -686,12 +686,8 @@ export function KeypressProvider({
} }
// Always check if this could start a sequence we need to buffer (Kitty or Mouse) // Always check if this could start a sequence we need to buffer (Kitty or Mouse)
// We only want to intercept if it starts with ESC[ (CSI) or is EXACTLY ESC (waiting for more).
// Other ESC sequences (like Alt+Key which is ESC+Key) should be let through if readline parsed them. // Other ESC sequences (like Alt+Key which is ESC+Key) should be let through if readline parsed them.
const isCSI = key.sequence.startsWith(`${ESC}[`); const shouldBuffer = couldBeKittySequence(key.sequence);
const isExactEsc = key.sequence === ESC;
const shouldBuffer = isCSI || isExactEsc;
const isExcluded = [ const isExcluded = [
PASTE_MODE_START, PASTE_MODE_START,
PASTE_MODE_END, PASTE_MODE_END,
+21 -13
View File
@@ -38,7 +38,7 @@ class MockStdin extends EventEmitter {
} }
} }
describe('useKeypress', () => { describe.each([true, false])(`useKeypress with useKitty=%s`, (useKitty) => {
let stdin: MockStdin; let stdin: MockStdin;
const mockSetRawMode = vi.fn(); const mockSetRawMode = vi.fn();
const onKeypress = vi.fn(); const onKeypress = vi.fn();
@@ -50,7 +50,7 @@ describe('useKeypress', () => {
return null; return null;
} }
return render( return render(
<KeypressProvider kittyProtocolEnabled={false}> <KeypressProvider kittyProtocolEnabled={useKitty}>
<TestComponent /> <TestComponent />
</KeypressProvider>, </KeypressProvider>,
); );
@@ -58,6 +58,7 @@ describe('useKeypress', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
vi.useFakeTimers();
stdin = new MockStdin(); stdin = new MockStdin();
(useStdin as Mock).mockReturnValue({ (useStdin as Mock).mockReturnValue({
stdin, stdin,
@@ -186,21 +187,28 @@ describe('useKeypress', () => {
expect(onKeypress).toHaveBeenCalledTimes(1); expect(onKeypress).toHaveBeenCalledTimes(1);
}); });
it('should handle paste false alarm', () => { it('should handle paste false alarm', async () => {
renderKeypressHook(true); renderKeypressHook(true);
act(() => { act(() => {
stdin.write(PASTE_START.slice(0, 5)); stdin.write(PASTE_START.slice(0, 5));
stdin.write('do'); stdin.write('do');
}); });
expect(onKeypress).toHaveBeenCalledWith(
expect.objectContaining({ sequence: '\x1B[200d' }),
);
expect(onKeypress).toHaveBeenCalledWith(
expect.objectContaining({ sequence: 'o' }),
);
expect(onKeypress).toHaveBeenCalledTimes(2); if (useKitty) {
vi.advanceTimersByTime(60); // wait for kitty timeout
expect(onKeypress).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({ sequence: '\x1B[200do' }),
);
} else {
expect(onKeypress).toHaveBeenCalledWith(
expect.objectContaining({ sequence: '\x1B[200d' }),
);
expect(onKeypress).toHaveBeenCalledWith(
expect.objectContaining({ sequence: 'o' }),
);
expect(onKeypress).toHaveBeenCalledTimes(2);
}
}); });
it('should handle back to back pastes', () => { it('should handle back to back pastes', () => {
@@ -240,11 +248,11 @@ describe('useKeypress', () => {
const pasteText = 'pasted'; const pasteText = 'pasted';
await act(async () => { await act(async () => {
stdin.write(PASTE_START.slice(0, 3)); stdin.write(PASTE_START.slice(0, 3));
await new Promise((r) => setTimeout(r, 50)); vi.advanceTimersByTime(50);
stdin.write(PASTE_START.slice(3) + pasteText.slice(0, 3)); stdin.write(PASTE_START.slice(3) + pasteText.slice(0, 3));
await new Promise((r) => setTimeout(r, 50)); vi.advanceTimersByTime(50);
stdin.write(pasteText.slice(3) + PASTE_END.slice(0, 3)); stdin.write(pasteText.slice(3) + PASTE_END.slice(0, 3));
await new Promise((r) => setTimeout(r, 50)); vi.advanceTimersByTime(50);
stdin.write(PASTE_END.slice(3)); stdin.write(PASTE_END.slice(3));
}); });
expect(onKeypress).toHaveBeenCalledWith( expect(onKeypress).toHaveBeenCalledWith(