Always enable bracketed paste (#16179)

This commit is contained in:
Tommaso Sciortino
2026-01-09 08:07:05 -08:00
committed by GitHub
parent aa480e5fbb
commit 88f1ec8d0a
3 changed files with 3 additions and 97 deletions

View File

@@ -19,7 +19,6 @@ import { ESC } from '../utils/input.js';
import { parseMouseEvent } from '../utils/mouse.js';
import { FOCUS_IN, FOCUS_OUT } from '../hooks/useFocus.js';
import { appEvents, AppEvent } from '../../utils/events.js';
import { terminalCapabilityManager } from '../utils/terminalCapabilityManager.js';
export const BACKSLASH_ENTER_TIMEOUT = 5;
export const ESC_TIMEOUT = 50;
@@ -189,30 +188,6 @@ function bufferBackslashEnter(
return (key: Key) => bufferer.next(key);
}
/**
* Converts return keys pressed quickly after other keys into plain
* insertable return characters.
*
* This is to accommodate older terminals that paste text without bracketing.
*/
function bufferFastReturn(keypressHandler: KeypressHandler): KeypressHandler {
let lastKeyTime = 0;
return (key: Key) => {
const now = Date.now();
if (key.name === 'return' && now - lastKeyTime <= FAST_RETURN_TIMEOUT) {
keypressHandler({
...key,
name: '',
sequence: '\r',
insertable: true,
});
} else {
keypressHandler(key);
}
lastKeyTime = now;
};
}
/**
* Buffers paste events between paste-start and paste-end sequences.
* Will flush the buffer if no data is received for PASTE_TIMEOUT ms or
@@ -661,9 +636,6 @@ export function KeypressProvider({
process.stdin.setEncoding('utf8'); // Make data events emit strings
let processor = nonKeyboardEventFilter(broadcast);
if (!terminalCapabilityManager.isBracketedPasteEnabled()) {
processor = bufferFastReturn(processor);
}
processor = bufferBackslashEnter(processor);
processor = bufferPaste(processor);
let dataListener = createDataListener(processor);

View File

@@ -266,46 +266,4 @@ describe('TerminalCapabilityManager', () => {
expect(manager.isModifyOtherKeysEnabled()).toBe(true);
});
});
describe('bracketed paste detection', () => {
it('should detect bracketed paste support (mode set)', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate bracketed paste response: \x1b[?2004;1$y
stdin.emit('data', Buffer.from('\x1b[?2004;1$y'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isBracketedPasteSupported()).toBe(true);
expect(manager.isBracketedPasteEnabled()).toBe(true);
});
it('should detect bracketed paste support (mode reset)', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Simulate bracketed paste response: \x1b[?2004;2$y
stdin.emit('data', Buffer.from('\x1b[?2004;2$y'));
// Complete detection with DA1
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isBracketedPasteSupported()).toBe(true);
expect(manager.isBracketedPasteEnabled()).toBe(true);
});
it('should not enable bracketed paste if not supported', async () => {
const manager = TerminalCapabilityManager.getInstance();
const promise = manager.detectCapabilities();
// Complete detection with DA1 only
stdin.emit('data', Buffer.from('\x1b[?62c'));
await promise;
expect(manager.isBracketedPasteSupported()).toBe(false);
expect(manager.isBracketedPasteEnabled()).toBe(false);
});
});
});

View File

@@ -25,7 +25,6 @@ export class TerminalCapabilityManager {
private static readonly TERMINAL_NAME_QUERY = '\x1b[>q';
private static readonly DEVICE_ATTRIBUTES_QUERY = '\x1b[c';
private static readonly MODIFY_OTHER_KEYS_QUERY = '\x1b[>4;?m';
private static readonly BRACKETED_PASTE_QUERY = '\x1b[?2004$p';
// Kitty keyboard flags: CSI ? flags u
// eslint-disable-next-line no-control-regex
@@ -43,10 +42,6 @@ export class TerminalCapabilityManager {
// modifyOtherKeys response: CSI > 4 ; level m
// eslint-disable-next-line no-control-regex
private static readonly MODIFY_OTHER_KEYS_REGEX = /\x1b\[>4;(\d+)m/;
// DECRQM response for bracketed paste: CSI ? 2004 ; Ps $ y
// Ps = 1 (set), 2 (reset), 3 (permanently set), 4 (permanently reset)
// eslint-disable-next-line no-control-regex
private static readonly BRACKETED_PASTE_REGEX = /\x1b\[\?2004;([1-4])\$y/;
private terminalBackgroundColor: TerminalBackgroundColor;
private kittySupported = false;
@@ -55,7 +50,6 @@ export class TerminalCapabilityManager {
private terminalName: string | undefined;
private modifyOtherKeysSupported = false;
private modifyOtherKeysEnabled = false;
private bracketedPasteSupported = false;
private bracketedPasteEnabled = false;
private constructor() {}
@@ -107,7 +101,6 @@ export class TerminalCapabilityManager {
let deviceAttributesReceived = false;
let bgReceived = false;
let modifyOtherKeysReceived = false;
let bracketedPasteReceived = false;
// eslint-disable-next-line prefer-const
let timeoutId: NodeJS.Timeout;
@@ -172,17 +165,6 @@ export class TerminalCapabilityManager {
}
}
// check for bracketed paste support
if (!bracketedPasteReceived) {
const match = buffer.match(
TerminalCapabilityManager.BRACKETED_PASTE_REGEX,
);
if (match) {
bracketedPasteReceived = true;
this.bracketedPasteSupported = true;
}
}
// Check for Terminal Name/Version response.
if (!terminalNameReceived) {
const match = buffer.match(
@@ -219,7 +201,6 @@ export class TerminalCapabilityManager {
TerminalCapabilityManager.OSC_11_QUERY +
TerminalCapabilityManager.TERMINAL_NAME_QUERY +
TerminalCapabilityManager.MODIFY_OTHER_KEYS_QUERY +
TerminalCapabilityManager.BRACKETED_PASTE_QUERY +
TerminalCapabilityManager.DEVICE_ATTRIBUTES_QUERY,
);
} catch (e) {
@@ -238,10 +219,9 @@ export class TerminalCapabilityManager {
enableModifyOtherKeys();
this.modifyOtherKeysEnabled = true;
}
if (this.bracketedPasteSupported) {
enableBracketedPasteMode();
this.bracketedPasteEnabled = true;
}
// Always enable bracketed paste since it'll be ignored if unsupported.
enableBracketedPasteMode();
this.bracketedPasteEnabled = true;
} catch (e) {
debugLogger.warn('Failed to enable keyboard protocols:', e);
}
@@ -259,10 +239,6 @@ export class TerminalCapabilityManager {
return this.kittyEnabled;
}
isBracketedPasteSupported(): boolean {
return this.bracketedPasteSupported;
}
isBracketedPasteEnabled(): boolean {
return this.bracketedPasteEnabled;
}