Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/vs/workbench/contrib/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -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: {},
Expand All @@ -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' });
});
Expand Down Expand Up @@ -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', () => {
Expand Down
Loading