Stop printing garbage characters for F1,F2.. keys (#12835)

This commit is contained in:
Tommaso Sciortino
2025-11-10 10:56:05 -08:00
committed by GitHub
parent 2136598e87
commit 3c9052a751
10 changed files with 70 additions and 18 deletions
@@ -1450,6 +1450,7 @@ describe('AppContainer State Management', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x13', sequence: '\x13',
}); });
}); });
@@ -1476,6 +1477,7 @@ describe('AppContainer State Management', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x13', sequence: '\x13',
}); });
}); });
@@ -1490,6 +1492,7 @@ describe('AppContainer State Management', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: 'a', sequence: 'a',
}); });
}); });
@@ -1510,6 +1513,7 @@ describe('AppContainer State Management', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x13', sequence: '\x13',
}); });
}); });
@@ -1525,6 +1529,7 @@ describe('AppContainer State Management', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: 'a', sequence: 'a',
}); });
}); });
@@ -188,6 +188,7 @@ describe('<ModelDialog />', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '', sequence: '',
}); });
expect(props.onClose).toHaveBeenCalledTimes(1); expect(props.onClose).toHaveBeenCalledTimes(1);
@@ -198,6 +199,7 @@ describe('<ModelDialog />', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: '', sequence: '',
}); });
expect(props.onClose).toHaveBeenCalledTimes(1); expect(props.onClose).toHaveBeenCalledTimes(1);
@@ -977,6 +977,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: 'h', sequence: 'h',
}), }),
); );
@@ -987,6 +988,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: 'i', sequence: 'i',
}), }),
); );
@@ -1004,6 +1006,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: '\r', sequence: '\r',
}), }),
); );
@@ -1021,6 +1024,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\t', sequence: '\t',
}), }),
); );
@@ -1038,6 +1042,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: true, shift: true,
paste: false, paste: false,
insertable: false,
sequence: '\u001b[9;2u', sequence: '\u001b[9;2u',
}), }),
); );
@@ -1060,6 +1065,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x7f', sequence: '\x7f',
}), }),
); );
@@ -1084,6 +1090,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x7f', sequence: '\x7f',
}); });
result.current.handleInput({ result.current.handleInput({
@@ -1092,6 +1099,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x7f', sequence: '\x7f',
}); });
result.current.handleInput({ result.current.handleInput({
@@ -1100,6 +1108,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x7f', sequence: '\x7f',
}); });
}); });
@@ -1159,6 +1168,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x1b[D', sequence: '\x1b[D',
}), }),
); // cursor [0,1] ); // cursor [0,1]
@@ -1170,6 +1180,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: '\x1b[C', sequence: '\x1b[C',
}), }),
); // cursor [0,2] ); // cursor [0,2]
@@ -1189,6 +1200,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: textWithAnsi, sequence: textWithAnsi,
}), }),
); );
@@ -1206,6 +1218,7 @@ describe('useTextBuffer', () => {
meta: false, meta: false,
shift: true, shift: true,
paste: false, paste: false,
insertable: true,
sequence: '\r', sequence: '\r',
}), }),
); // Simulates Shift+Enter in VSCode terminal ); // Simulates Shift+Enter in VSCode terminal
@@ -1410,6 +1423,7 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence, sequence,
}); });
@@ -1468,6 +1482,7 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: largeTextWithUnsafe, sequence: largeTextWithUnsafe,
}), }),
); );
@@ -1502,6 +1517,7 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: largeTextWithAnsi, sequence: largeTextWithAnsi,
}), }),
); );
@@ -1526,6 +1542,7 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: emojis, sequence: emojis,
}), }),
); );
@@ -1717,12 +1734,35 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
sequence: '\r', sequence: '\r',
}), }),
); );
expect(getBufferState(result).lines).toEqual(['']); expect(getBufferState(result).lines).toEqual(['']);
}); });
it('should not print anything for function keys when singleLine is true', () => {
const { result } = renderHook(() =>
useTextBuffer({
viewport,
isValidPath: () => false,
singleLine: true,
}),
);
act(() =>
result.current.handleInput({
name: 'f1',
ctrl: false,
meta: false,
shift: false,
paste: false,
insertable: false,
sequence: '\u001bOP',
}),
);
expect(getBufferState(result).lines).toEqual(['']);
});
it('should strip newlines from pasted text when singleLine is true', () => { it('should strip newlines from pasted text when singleLine is true', () => {
const { result } = renderHook(() => const { result } = renderHook(() =>
useTextBuffer({ useTextBuffer({
@@ -17,6 +17,7 @@ import {
stripUnsafeCharacters, stripUnsafeCharacters,
getCachedStringWidth, getCachedStringWidth,
} from '../../utils/textUtils.js'; } from '../../utils/textUtils.js';
import type { Key } from '../../contexts/KeypressContext.js';
import type { VimAction } from './vim-buffer-actions.js'; import type { VimAction } from './vim-buffer-actions.js';
import { handleVimAction } from './vim-buffer-actions.js'; import { handleVimAction } from './vim-buffer-actions.js';
@@ -1907,14 +1908,7 @@ export function useTextBuffer({
); );
const handleInput = useCallback( const handleInput = useCallback(
(key: { (key: Key): void => {
name: string;
ctrl: boolean;
meta: boolean;
shift: boolean;
paste: boolean;
sequence: string;
}): void => {
const { sequence: input } = key; const { sequence: input } = key;
if (key.paste) { if (key.paste) {
@@ -1964,7 +1958,7 @@ export function useTextBuffer({
else if (key.name === 'delete' || (key.ctrl && key.name === 'd')) del(); else if (key.name === 'delete' || (key.ctrl && key.name === 'd')) del();
else if (key.ctrl && !key.shift && key.name === 'z') undo(); else if (key.ctrl && !key.shift && key.name === 'z') undo();
else if (key.ctrl && key.shift && key.name === 'z') redo(); else if (key.ctrl && key.shift && key.name === 'z') redo();
else if (input && !key.ctrl && !key.meta && key.name !== 'tab') { else if (key.insertable) {
insert(input, { paste: key.paste }); insert(input, { paste: key.paste });
} }
}, },
@@ -2266,14 +2260,7 @@ export interface TextBuffer {
/** /**
* High level "handleInput" receives what Ink gives us. * High level "handleInput" receives what Ink gives us.
*/ */
handleInput: (key: { handleInput: (key: Key) => void;
name: string;
ctrl: boolean;
meta: boolean;
shift: boolean;
paste: boolean;
sequence: string;
}) => void;
/** /**
* Opens the current buffer contents in the user's preferred terminal text * Opens the current buffer contents in the user's preferred terminal text
* editor ($VISUAL or $EDITOR, falling back to "vi"). The method blocks * editor ($VISUAL or $EDITOR, falling back to "vi"). The method blocks
@@ -224,6 +224,7 @@ function bufferPaste(
meta: false, meta: false,
shift: false, shift: false,
paste: true, paste: true,
insertable: true,
sequence: buffer, sequence: buffer,
}); });
} }
@@ -273,6 +274,7 @@ function* emitKeys(
let meta = false; let meta = false;
let shift = false; let shift = false;
let code = undefined; let code = undefined;
let insertable = false;
if (ch === ESC) { if (ch === ESC) {
escaped = true; escaped = true;
@@ -455,6 +457,7 @@ function* emitKeys(
} else if (ch === ' ') { } else if (ch === ' ') {
name = 'space'; name = 'space';
meta = escaped; meta = escaped;
insertable = true;
} else if (!escaped && ch <= '\x1a') { } else if (!escaped && ch <= '\x1a') {
// ctrl+letter // ctrl+letter
name = String.fromCharCode(ch.charCodeAt(0) + 'a'.charCodeAt(0) - 1); name = String.fromCharCode(ch.charCodeAt(0) + 'a'.charCodeAt(0) - 1);
@@ -464,6 +467,7 @@ function* emitKeys(
name = ch.toLowerCase(); name = ch.toLowerCase();
shift = /^[A-Z]$/.exec(ch) !== null; shift = /^[A-Z]$/.exec(ch) !== null;
meta = escaped; meta = escaped;
insertable = true;
} else if (MAC_ALT_KEY_CHARACTER_MAP[ch] && process.platform === 'darwin') { } else if (MAC_ALT_KEY_CHARACTER_MAP[ch] && process.platform === 'darwin') {
name = MAC_ALT_KEY_CHARACTER_MAP[ch]; name = MAC_ALT_KEY_CHARACTER_MAP[ch];
meta = true; meta = true;
@@ -479,12 +483,16 @@ function* emitKeys(
meta, meta,
shift, shift,
paste: false, paste: false,
insertable: false,
sequence: ESC, sequence: ESC,
}); });
} else if (escaped) { } else if (escaped) {
// Escape sequence timeout // Escape sequence timeout
name = ch.length ? undefined : 'escape'; name = ch.length ? undefined : 'escape';
meta = true; meta = true;
} else {
// Any other character is considered printable.
insertable = true;
} }
if ( if (
@@ -497,6 +505,7 @@ function* emitKeys(
meta, meta,
shift, shift,
paste: false, paste: false,
insertable,
sequence, sequence,
}); });
} }
@@ -510,6 +519,7 @@ export interface Key {
meta: boolean; meta: boolean;
shift: boolean; shift: boolean;
paste: boolean; paste: boolean;
insertable: boolean;
sequence: string; sequence: string;
} }
@@ -38,7 +38,7 @@ class MockStdin extends EventEmitter {
} }
} }
describe(`useKeypress with useKitty=%s`, () => { describe(`useKeypress`, () => {
let stdin: MockStdin; let stdin: MockStdin;
const mockSetRawMode = vi.fn(); const mockSetRawMode = vi.fn();
const onKeypress = vi.fn(); const onKeypress = vi.fn();
@@ -144,6 +144,7 @@ describe(`useKeypress with useKitty=%s`, () => {
meta: false, meta: false,
shift: false, shift: false,
paste: true, paste: true,
insertable: true,
sequence: pasteText, sequence: pasteText,
}); });
}); });
@@ -281,6 +282,7 @@ describe(`useKeypress with useKitty=%s`, () => {
meta: false, meta: false,
shift: false, shift: false,
paste: true, paste: true,
insertable: true,
sequence: pasteText, sequence: pasteText,
}); });
}); });
@@ -62,6 +62,7 @@ describe('useSelectionList', () => {
meta: false, meta: false,
shift: options.shift ?? false, shift: options.shift ?? false,
paste: false, paste: false,
insertable: false,
}; };
activeKeypressHandler(key); activeKeypressHandler(key);
} else { } else {
@@ -331,6 +332,7 @@ describe('useSelectionList', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: true,
}; };
handler(key); handler(key);
}; };
@@ -380,6 +382,7 @@ describe('useSelectionList', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
}; };
handler(key); handler(key);
}; };
+1
View File
@@ -40,6 +40,7 @@ const createKey = (partial: Partial<Key>): Key => ({
meta: partial.meta || false, meta: partial.meta || false,
shift: partial.shift || false, shift: partial.shift || false,
paste: partial.paste || false, paste: partial.paste || false,
insertable: partial.insertable || false,
...partial, ...partial,
}); });
+1
View File
@@ -313,6 +313,7 @@ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
meta: key.meta || false, meta: key.meta || false,
shift: key.shift || false, shift: key.shift || false,
paste: key.paste || false, paste: key.paste || false,
insertable: key.insertable || false,
}), }),
[], [],
); );
+1
View File
@@ -17,6 +17,7 @@ describe('keyMatchers', () => {
meta: false, meta: false,
shift: false, shift: false,
paste: false, paste: false,
insertable: false,
sequence: name, sequence: name,
...mods, ...mods,
}); });