Skip to content

Commit 48a2d3e

Browse files
committed
if needed , read clipboard inside MainThreadEditors#insertText, microsoft#98497
1 parent dca516e commit 48a2d3e

6 files changed

Lines changed: 55 additions & 6 deletions

File tree

extensions/vscode-api-tests/src/singlefolder-tests/editor.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as assert from 'assert';
7-
import { workspace, window, Position, Range, commands, TextEditor, TextDocument, TextEditorCursorStyle, TextEditorLineNumbersStyle, SnippetString, Selection, Uri } from 'vscode';
7+
import { workspace, window, Position, Range, commands, TextEditor, TextDocument, TextEditorCursorStyle, TextEditorLineNumbersStyle, SnippetString, Selection, Uri, env } from 'vscode';
88
import { createRandomFile, deleteFile, closeAllEditors } from '../utils';
99

1010
suite('vscode API - editors', () => {
@@ -47,6 +47,20 @@ suite('vscode API - editors', () => {
4747
});
4848
});
4949

50+
test('insert snippet with clipboard variables', async () => {
51+
52+
await env.clipboard.writeText('INTEGRATION-TESTS');
53+
54+
const snippetString = new SnippetString('running: $CLIPBOARD');
55+
56+
return withRandomFileEditor('', async (editor, doc) => {
57+
const inserted = await editor.insertSnippet(snippetString);
58+
assert.ok(inserted);
59+
assert.equal(doc.getText(), 'running: INTEGRATION-TESTS');
60+
assert.ok(doc.isDirty);
61+
});
62+
});
63+
5064
test('insert snippet with replacement, editor selection', () => {
5165
const snippetString = new SnippetString()
5266
.appendText('has been');

src/vs/editor/contrib/snippet/snippetController2.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export class SnippetController2 implements IEditorContribution {
4545
return editor.getContribution<SnippetController2>(SnippetController2.ID);
4646
}
4747

48+
static guessNeedsClipboard(template: string): boolean {
49+
return /\${?CLIPBOARD/.test(template);
50+
}
51+
4852
static readonly InSnippetMode = new RawContextKey('inSnippetMode', false);
4953
static readonly HasNextTabstop = new RawContextKey('hasNextTabstop', false);
5054
static readonly HasPrevTabstop = new RawContextKey('hasPrevTabstop', false);

src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
2929
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
3030
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
3131
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
32+
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
3233

3334
namespace delta {
3435

@@ -331,6 +332,7 @@ export class MainThreadDocumentsAndEditors {
331332
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
332333
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService,
333334
@IUriIdentityService uriIdentityService: IUriIdentityService,
335+
@IClipboardService private readonly _clipboardService: IClipboardService,
334336
) {
335337
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors);
336338

@@ -365,7 +367,7 @@ export class MainThreadDocumentsAndEditors {
365367
// added editors
366368
for (const apiEditor of delta.addedEditors) {
367369
const mainThreadEditor = new MainThreadTextEditor(apiEditor.id, apiEditor.editor.getModel(),
368-
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService);
370+
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService, this._clipboardService);
369371

370372
this._textEditors.set(apiEditor.id, mainThreadEditor);
371373
addedEditors.push(mainThreadEditor);

src/vs/workbench/api/browser/mainThreadEditor.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorCon
1717
import { IEditorPane } from 'vs/workbench/common/editor';
1818
import { withNullAsUndefined } from 'vs/base/common/types';
1919
import { equals } from 'vs/base/common/arrays';
20+
import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState';
21+
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
2022

2123
export interface IFocusTracker {
2224
onGainedFocus(): void;
@@ -159,6 +161,7 @@ export class MainThreadTextEditor {
159161
private readonly _id: string;
160162
private _model: ITextModel;
161163
private readonly _modelService: IModelService;
164+
private readonly _clipboardService: IClipboardService;
162165
private readonly _modelListeners = new DisposableStore();
163166
private _codeEditor: ICodeEditor | null;
164167
private readonly _focusTracker: IFocusTracker;
@@ -172,14 +175,16 @@ export class MainThreadTextEditor {
172175
model: ITextModel,
173176
codeEditor: ICodeEditor,
174177
focusTracker: IFocusTracker,
175-
modelService: IModelService
178+
modelService: IModelService,
179+
clipboardService: IClipboardService,
176180
) {
177181
this._id = id;
178182
this._model = model;
179183
this._codeEditor = null;
180184
this._properties = null;
181185
this._focusTracker = focusTracker;
182186
this._modelService = modelService;
187+
this._clipboardService = clipboardService;
183188

184189
this._onPropertiesChanged = new Emitter<IEditorPropertiesChangeData>();
185190

@@ -454,12 +459,23 @@ export class MainThreadTextEditor {
454459
return true;
455460
}
456461

457-
insertSnippet(template: string, ranges: readonly IRange[], opts: IUndoStopOptions) {
462+
async insertSnippet(template: string, ranges: readonly IRange[], opts: IUndoStopOptions) {
458463

459-
if (!this._codeEditor) {
464+
if (!this._codeEditor || !this._codeEditor.hasModel()) {
460465
return false;
461466
}
462467

468+
// check if clipboard is required and only iff read it (async)
469+
let clipboardText: string | undefined;
470+
const needsTemplate = SnippetController2.guessNeedsClipboard(template);
471+
if (needsTemplate) {
472+
const state = new EditorState(this._codeEditor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
473+
clipboardText = await this._clipboardService.readText();
474+
if (!state.validate(this._codeEditor)) {
475+
return false;
476+
}
477+
}
478+
463479
const snippetController = SnippetController2.get(this._codeEditor);
464480

465481
// // cancel previous snippet mode
@@ -471,7 +487,11 @@ export class MainThreadTextEditor {
471487
this._codeEditor.focus();
472488

473489
// make modifications
474-
snippetController.insert(template, { overwriteBefore: 0, overwriteAfter: 0, undoStopBefore: opts.undoStopBefore, undoStopAfter: opts.undoStopAfter });
490+
snippetController.insert(template, {
491+
overwriteBefore: 0, overwriteAfter: 0,
492+
undoStopBefore: opts.undoStopBefore, undoStopAfter: opts.undoStopAfter,
493+
clipboardText
494+
});
475495

476496
return true;
477497
}

src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import * as assert from 'assert';
77
import { SnippetFile, Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile';
88
import { URI } from 'vs/base/common/uri';
9+
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
910

1011
suite('Snippets', function () {
1112

@@ -70,6 +71,8 @@ suite('Snippets', function () {
7071
function assertNeedsClipboard(body: string, expected: boolean): void {
7172
let snippet = new Snippet(['foo'], 'FooSnippet1', 'foo', '', body, 'test', SnippetSource.User);
7273
assert.equal(snippet.needsClipboard, expected);
74+
75+
assert.equal(SnippetController2.guessNeedsClipboard(body), expected);
7376
}
7477

7578
assertNeedsClipboard('foo$CLIPBOARD', true);

src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogSer
2727
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
2828
import { TestTextResourcePropertiesService, TestWorkingCopyFileService } from 'vs/workbench/test/common/workbenchTestServices';
2929
import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService';
30+
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
3031

3132
suite('MainThreadDocumentsAndEditors', () => {
3233

@@ -92,6 +93,11 @@ suite('MainThreadDocumentsAndEditors', () => {
9293
TestEnvironmentService,
9394
new TestWorkingCopyFileService(),
9495
new UriIdentityService(fileService),
96+
new class extends mock<IClipboardService>() {
97+
readText() {
98+
return Promise.resolve('clipboard_contents');
99+
}
100+
}
95101
);
96102
});
97103

0 commit comments

Comments
 (0)