mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-06 22:54:50 +01:00
Move to new module, add tests
This commit is contained in:
@@ -31,7 +31,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { CodeDataTransfers, containsDragType } from 'vs/platform/dnd/browser/dnd';
|
||||
import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -87,6 +86,7 @@ import { importAMDNodeModule } from 'vs/amdX';
|
||||
import type { IMarker, Terminal as XTermTerminal } from '@xterm/xterm';
|
||||
import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands';
|
||||
import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings';
|
||||
import { shouldPasteTerminalText } from 'vs/workbench/contrib/terminal/common/terminalClipboard';
|
||||
|
||||
const enum Constants {
|
||||
/**
|
||||
@@ -346,7 +346,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ITerminalLogService private readonly _logService: ITerminalLogService,
|
||||
@IDialogService private readonly _dialogService: IDialogService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
@@ -1094,80 +1093,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
this._terminalAltBufferActiveContextKey.set(!!(this.xterm && this.xterm.raw.buffer.active === this.xterm.raw.buffer.alternate));
|
||||
}
|
||||
|
||||
private async _shouldPasteText(text: string): Promise<boolean> {
|
||||
// If the clipboard has only one line, a warning should never show
|
||||
const textForLines = text.split(/\r?\n/);
|
||||
if (textForLines.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get config value
|
||||
function parseConfigValue(value: unknown): 'auto' | 'always' | 'never' {
|
||||
// Valid value
|
||||
if (typeof value === 'string') {
|
||||
if (value === 'auto' || value === 'always' || value === 'never') {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
// Legacy backwards compatibility
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'auto' : 'never';
|
||||
}
|
||||
// Invalid value fallback
|
||||
return 'auto';
|
||||
}
|
||||
const configValue = parseConfigValue(this._configurationService.getValue(TerminalSettingId.EnableMultiLinePasteWarning));
|
||||
|
||||
// Never show it
|
||||
if (configValue === 'never') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Special edge cases to not show for auto
|
||||
if (configValue === 'auto') {
|
||||
// Ignore check if the shell is in bracketed paste mode (ie. the shell can handle multi-line
|
||||
// text).
|
||||
if (this.xterm?.raw.modes.bracketedPasteMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const textForLines = text.split(/\r?\n/);
|
||||
// Ignore check when a command is copied with a trailing new line
|
||||
if (textForLines.length === 2 && textForLines[1].trim().length === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const displayItemsCount = 3;
|
||||
const maxPreviewLineLength = 30;
|
||||
|
||||
let detail = nls.localize('preview', "Preview:");
|
||||
for (let i = 0; i < Math.min(textForLines.length, displayItemsCount); i++) {
|
||||
const line = textForLines[i];
|
||||
const cleanedLine = line.length > maxPreviewLineLength ? `${line.slice(0, maxPreviewLineLength)}…` : line;
|
||||
detail += `\n${cleanedLine}`;
|
||||
}
|
||||
|
||||
if (textForLines.length > displayItemsCount) {
|
||||
detail += `\n…`;
|
||||
}
|
||||
|
||||
const { confirmed, checkboxChecked } = await this._dialogService.confirm({
|
||||
message: nls.localize('confirmMoveTrashMessageFilesAndDirectories', "Are you sure you want to paste {0} lines of text into the terminal?", textForLines.length),
|
||||
detail,
|
||||
primaryButton: nls.localize({ key: 'multiLinePasteButton', comment: ['&& denotes a mnemonic'] }, "&&Paste"),
|
||||
checkbox: {
|
||||
label: nls.localize('doNotAskAgain', "Do not ask me again")
|
||||
}
|
||||
});
|
||||
|
||||
if (confirmed && checkboxChecked) {
|
||||
await this._configurationService.updateValue(TerminalSettingId.EnableMultiLinePasteWarning, false);
|
||||
}
|
||||
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
override dispose(reason?: TerminalExitReason): void {
|
||||
if (this.isDisposed) {
|
||||
return;
|
||||
@@ -1246,26 +1171,21 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
}
|
||||
|
||||
async paste(): Promise<void> {
|
||||
if (!this.xterm) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentText: string = await this._clipboardService.readText();
|
||||
if (!await this._shouldPasteText(currentText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.focus();
|
||||
this.xterm.raw.paste(currentText);
|
||||
await this._paste(await this._clipboardService.readText());
|
||||
}
|
||||
|
||||
async pasteSelection(): Promise<void> {
|
||||
await this._paste(await this._clipboardService.readText('selection'));
|
||||
}
|
||||
|
||||
private async _paste(value: string): Promise<void> {
|
||||
if (!this.xterm) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentText: string = await this._clipboardService.readText('selection');
|
||||
if (!await this._shouldPasteText(currentText)) {
|
||||
const currentText: string = value;
|
||||
const shouldPasteText = await this._scopedInstantiationService.invokeFunction(shouldPasteTerminalText, currentText, this.xterm?.raw.modes.bracketedPasteMode);
|
||||
if (!shouldPasteText) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
|
||||
|
||||
export async function shouldPasteTerminalText(accessor: ServicesAccessor, text: string, bracketedPasteMode: boolean | undefined): Promise<boolean> {
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const dialogService = accessor.get(IDialogService);
|
||||
|
||||
// If the clipboard has only one line, a warning should never show
|
||||
const textForLines = text.split(/\r?\n/);
|
||||
if (textForLines.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get config value
|
||||
function parseConfigValue(value: unknown): 'auto' | 'always' | 'never' {
|
||||
// Valid value
|
||||
if (typeof value === 'string') {
|
||||
if (value === 'auto' || value === 'always' || value === 'never') {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
// Legacy backwards compatibility
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'auto' : 'never';
|
||||
}
|
||||
// Invalid value fallback
|
||||
return 'auto';
|
||||
}
|
||||
const configValue = parseConfigValue(configurationService.getValue(TerminalSettingId.EnableMultiLinePasteWarning));
|
||||
|
||||
// Never show it
|
||||
if (configValue === 'never') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Special edge cases to not show for auto
|
||||
if (configValue === 'auto') {
|
||||
// Ignore check if the shell is in bracketed paste mode (ie. the shell can handle multi-line
|
||||
// text).
|
||||
if (bracketedPasteMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const textForLines = text.split(/\r?\n/);
|
||||
// Ignore check when a command is copied with a trailing new line
|
||||
if (textForLines.length === 2 && textForLines[1].trim().length === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const displayItemsCount = 3;
|
||||
const maxPreviewLineLength = 30;
|
||||
|
||||
let detail = localize('preview', "Preview:");
|
||||
for (let i = 0; i < Math.min(textForLines.length, displayItemsCount); i++) {
|
||||
const line = textForLines[i];
|
||||
const cleanedLine = line.length > maxPreviewLineLength ? `${line.slice(0, maxPreviewLineLength)}…` : line;
|
||||
detail += `\n${cleanedLine}`;
|
||||
}
|
||||
|
||||
if (textForLines.length > displayItemsCount) {
|
||||
detail += `\n…`;
|
||||
}
|
||||
|
||||
const { confirmed, checkboxChecked } = await dialogService.confirm({
|
||||
message: localize('confirmMoveTrashMessageFilesAndDirectories', "Are you sure you want to paste {0} lines of text into the terminal?", textForLines.length),
|
||||
detail,
|
||||
primaryButton: localize({ key: 'multiLinePasteButton', comment: ['&& denotes a mnemonic'] }, "&&Paste"),
|
||||
checkbox: {
|
||||
label: localize('doNotAskAgain', "Do not ask me again")
|
||||
}
|
||||
});
|
||||
|
||||
if (confirmed && checkboxChecked) {
|
||||
await configurationService.updateValue(TerminalSettingId.EnableMultiLinePasteWarning, false);
|
||||
}
|
||||
|
||||
return confirmed;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { strictEqual } from 'assert';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
|
||||
import { shouldPasteTerminalText } from 'vs/workbench/contrib/terminal/common/terminalClipboard';
|
||||
|
||||
suite('TerminalClipboard', function () {
|
||||
|
||||
suite('shouldPasteTerminalText', () => {
|
||||
let instantiationService: TestInstantiationService;
|
||||
let configurationService: TestConfigurationService;
|
||||
let dialogService: TestDialogService;
|
||||
|
||||
setup(async () => {
|
||||
instantiationService = new TestInstantiationService();
|
||||
configurationService = new TestConfigurationService({
|
||||
[TerminalSettingId.EnableMultiLinePasteWarning]: 'auto'
|
||||
});
|
||||
dialogService = new TestDialogService({ confirmed: false });
|
||||
|
||||
instantiationService.stub(IConfigurationService, configurationService);
|
||||
instantiationService.stub(IDialogService, dialogService);
|
||||
});
|
||||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
function setConfigValue(value: unknown) {
|
||||
configurationService = new TestConfigurationService({
|
||||
[TerminalSettingId.EnableMultiLinePasteWarning]: value
|
||||
});
|
||||
instantiationService.stub(IConfigurationService, configurationService);
|
||||
}
|
||||
|
||||
test('Single line string', async () => {
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo', undefined), true);
|
||||
|
||||
setConfigValue('always');
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo', undefined), true);
|
||||
|
||||
setConfigValue('never');
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo', undefined), true);
|
||||
});
|
||||
test('Single line string with trailing new line', async () => {
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\n', undefined), true);
|
||||
|
||||
setConfigValue('always');
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\n', undefined), false);
|
||||
|
||||
setConfigValue('never');
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\n', undefined), true);
|
||||
});
|
||||
test('Multi-line string', async () => {
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\nbar', undefined), false);
|
||||
|
||||
setConfigValue('always');
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\nbar', undefined), false);
|
||||
|
||||
setConfigValue('never');
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\nbar', undefined), true);
|
||||
});
|
||||
test('Bracketed paste mode', async () => {
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\nbar', true), true);
|
||||
|
||||
setConfigValue('always');
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\nbar', true), false);
|
||||
|
||||
setConfigValue('never');
|
||||
strictEqual(await instantiationService.invokeFunction(shouldPasteTerminalText, 'foo\nbar', true), true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user