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
Original file line number Diff line number Diff line change
Expand Up @@ -1343,9 +1343,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}

async sendText(text: string, shouldExecute: boolean, bracketedPasteMode?: boolean): Promise<void> {
const useBracketedPasteMode = (bracketedPasteMode || /[\r\n]/.test(text)) && this.xterm?.raw.modes.bracketedPasteMode;

// Apply bracketed paste sequences if the terminal has the mode enabled, this will prevent
// the text from triggering keybindings and ensure new lines are handled properly
if (bracketedPasteMode && this.xterm?.raw.modes.bracketedPasteMode) {
if (useBracketedPasteMode) {
text = `\x1b[200~${text}\x1b[201~`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { deepStrictEqual, strictEqual } from 'assert';
import { timeout } from '../../../../../base/common/async.js';
import { Event } from '../../../../../base/common/event.js';
import { Disposable } from '../../../../../base/common/lifecycle.js';
import { Schemas } from '../../../../../base/common/network.js';
Expand Down Expand Up @@ -123,7 +124,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 @@ -146,9 +148,25 @@ 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
await new Promise(resolve => setTimeout(resolve, 100));
const instance = store.add(instantiationService.createInstance(TerminalInstance, terminalShellTypeContextKey, {}));
await instance.xtermReadyPromise;
return instance;
}

async function waitForShellLaunchConfigEnv(instance: ITerminalInstance): Promise<void> {
for (let i = 0; i < 50; i++) {
if (instance.shellLaunchConfig.env) {
return;
}
await timeout(0);
}

throw new Error('Timed out waiting for shell launch config env');
}

test('should create an instance of TerminalInstance with env from default profile', async () => {
terminalInstance = await createTerminalInstance();
await waitForShellLaunchConfigEnv(terminalInstance);
deepStrictEqual(terminalInstance.shellLaunchConfig.env, { TEST: 'TEST' });
});

Expand Down Expand Up @@ -192,6 +210,46 @@ suite('Workbench - TerminalInstance', () => {
// Verify that the task name is preserved
strictEqual(taskTerminal.title, 'Test Task Name', 'Task terminal should preserve API-set title');
});

test('should use bracketed paste mode for multiline executed text when available', async () => {
const instance = await createTerminalInstance();
const writes: string[] = [];
const processManager = (instance as unknown as { _processManager: { write(data: string): Promise<void> } })._processManager;
const originalWrite = processManager.write;
const originalXterm = instance.xterm!;
const testRaw = Object.create(originalXterm.raw) as typeof originalXterm.raw;
Object.defineProperty(testRaw, 'modes', {
value: {
...originalXterm.raw.modes,
bracketedPasteMode: true
},
configurable: true
});
const testXterm = Object.create(originalXterm) as typeof originalXterm;
Object.defineProperty(testXterm, 'raw', {
value: testRaw,
configurable: true
});
Object.defineProperty(testXterm, 'scrollToBottom', {
value: () => { },
configurable: true
});

processManager.write = async (data: string) => {
writes.push(data);
};
instance.xterm = testXterm;

try {
await instance.sendText('echo hello\nworld', true);
} finally {
processManager.write = originalWrite;
instance.xterm = originalXterm;
}

strictEqual(writes.length, 1);
strictEqual(writes[0].replace(/\x1b/g, '\\x1b').replace(/\r/g, '\\r'), '\\x1b[200~echo hello\\rworld\\x1b[201~\\r');
});
});
suite('parseExitResult', () => {
test('should return no message for exit code = undefined', () => {
Expand Down
Loading