Fix ctrl+/- not working on Windows Copilot CLI (#305037)

* Fix ctrl+/- not working on Windows Copilot CLI

* remove unncessary

* Changes

* more test

---------

Co-authored-by: Raymond Zhao <7199958+rzhao271@users.noreply.github.com>
This commit is contained in:
Anthony Kim
2026-03-26 15:32:42 -07:00
committed by GitHub
parent a086290082
commit 926cb66d57
2 changed files with 76 additions and 4 deletions

View File

@@ -623,6 +623,9 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [
'workbench.action.togglePanel',
'workbench.action.quickOpenView',
'workbench.action.toggleMaximizedPanel',
'workbench.action.zoomIn',
'workbench.action.zoomOut',
'workbench.action.zoomReset',
'notification.acceptPrimaryAction',
'runCommands',
'workbench.action.terminal.chat.start',

View File

@@ -14,6 +14,7 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
import { ResultKind } from '../../../../../platform/keybinding/common/keybindingResolver.js';
import { TerminalCapability, type ICwdDetectionCapability } from '../../../../../platform/terminal/common/capabilities/capabilities.js';
import { TerminalCapabilityStore } from '../../../../../platform/terminal/common/capabilities/terminalCapabilityStore.js';
import { GeneralShellType, ITerminalChildProcess, ITerminalProfile, TerminalLocation, TitleEventSource, type IShellLaunchConfig, type ITerminalBackend, type ITerminalProcessOptions } from '../../../../../platform/terminal/common/terminal.js';
@@ -27,7 +28,7 @@ import { TerminalConfigurationService } from '../../browser/terminalConfiguratio
import { parseExitResult, TerminalInstance, TerminalInstanceColorProvider, TerminalLabelComputer } from '../../browser/terminalInstance.js';
import { IEnvironmentVariableService } from '../../common/environmentVariable.js';
import { EnvironmentVariableService } from '../../common/environmentVariableService.js';
import { ITerminalProfileResolverService, ProcessState } from '../../common/terminal.js';
import { ITerminalProfileResolverService, ProcessState, DEFAULT_COMMANDS_TO_SKIP_SHELL } from '../../common/terminal.js';
import { TERMINAL_BACKGROUND_COLOR } from '../../common/terminalColorRegistry.js';
import { TestViewDescriptorService } from './xterm/xtermTerminal.test.js';
import { fixPath } from '../../../../services/search/test/browser/queryBuilder.test.js';
@@ -128,7 +129,8 @@ suite('Workbench - TerminalInstance', () => {
suite('TerminalInstance', () => {
let terminalInstance: ITerminalInstance;
test('should create an instance of TerminalInstance with env from default profile', async () => {
async function createTerminalInstance(): Promise<TerminalInstance> {
const instantiationService = workbenchInstantiationService({
configurationService: () => new TestConfigurationService({
files: {},
@@ -152,8 +154,14 @@ suite('Workbench - TerminalInstance', () => {
instantiationService.stub(IEnvironmentVariableService, store.add(instantiationService.createInstance(EnvironmentVariableService)));
instantiationService.stub(ITerminalInstanceService, store.add(new TestTerminalInstanceService()));
instantiationService.stub(ITerminalService, { setNextCommandId: async () => { } } as Partial<ITerminalService>);
terminalInstance = store.add(instantiationService.createInstance(TerminalInstance, terminalShellTypeContextKey, {}));
// //Wait for the teminalInstance._xtermReadyPromise to resolve
const instance = store.add(instantiationService.createInstance(TerminalInstance, terminalShellTypeContextKey, {}));
await instance.xtermReadyPromise;
return instance;
}
test('should create an instance of TerminalInstance with env from default profile', async () => {
terminalInstance = await createTerminalInstance();
// Wait for the terminal instance to resolve shell launch config env.
await new Promise(resolve => setTimeout(resolve, 100));
deepStrictEqual(terminalInstance.shellLaunchConfig.env, { TEST: 'TEST' });
});
@@ -198,6 +206,67 @@ suite('Workbench - TerminalInstance', () => {
// Verify that the task name is preserved
strictEqual(taskTerminal.title, 'Test Task Name', 'Task terminal should preserve API-set title');
});
test('custom key event handler should handle commands in DEFAULT_COMMANDS_TO_SKIP_SHELL in VS Code and not xterm when sendKeybindingsToShell is disabled', async () => {
const instance = await createTerminalInstance();
const keybindingService = instance['_keybindingService'];
const originalSoftDispatch = keybindingService.softDispatch;
keybindingService.softDispatch = () => ({ kind: ResultKind.KbFound, commandId: 'workbench.action.zoomIn', commandArgs: undefined, isBubble: false });
let capturedHandler: ((e: KeyboardEvent) => boolean) | undefined;
instance.xterm!.raw.attachCustomKeyEventHandler = handler => { capturedHandler = handler; };
const container = document.createElement('div');
document.body.appendChild(container);
instance.attachToElement(container);
instance.setVisible(true);
const event = new KeyboardEvent('keydown', { key: '=', cancelable: true });
try {
deepStrictEqual(
{ result: capturedHandler?.(event), defaultPrevented: event.defaultPrevented },
{ result: false, defaultPrevented: true }
);
} finally {
keybindingService.softDispatch = originalSoftDispatch;
container.remove();
}
});
test('custom key event handler should intercept Meta-modified keys that resolve to a command when sendKeybindingsToShell is disabled', async () => {
const instance = await createTerminalInstance();
const keybindingService = instance['_keybindingService'];
const originalSoftDispatch = keybindingService.softDispatch;
strictEqual(DEFAULT_COMMANDS_TO_SKIP_SHELL.includes('test.metaKeyInterceptCommand'), false);
keybindingService.softDispatch = () => ({ kind: ResultKind.KbFound, commandId: 'test.metaKeyInterceptCommand', commandArgs: undefined, isBubble: false });
let capturedHandler: ((e: KeyboardEvent) => boolean) | undefined;
instance.xterm!.raw.attachCustomKeyEventHandler = handler => { capturedHandler = handler; };
const container = document.createElement('div');
document.body.appendChild(container);
instance.attachToElement(container);
instance.setVisible(true);
const event = new KeyboardEvent('keydown', { key: '=', metaKey: true, cancelable: true });
try {
deepStrictEqual(
{ result: capturedHandler?.(event), defaultPrevented: event.defaultPrevented },
{ result: false, defaultPrevented: true }
);
} finally {
keybindingService.softDispatch = originalSoftDispatch;
container.remove();
}
});
});
suite('DEFAULT_COMMANDS_TO_SKIP_SHELL', () => {
test('should include zoom commands so they are not consumed by kitty keyboard protocol', () => {
deepStrictEqual(
['workbench.action.zoomIn', 'workbench.action.zoomOut', 'workbench.action.zoomReset'].every(
cmd => DEFAULT_COMMANDS_TO_SKIP_SHELL.includes(cmd)
),
true
);
});
});
suite('parseExitResult', () => {
test('should return no message for exit code = undefined', () => {