Skip to content

Commit aad6ffd

Browse files
authored
Add progress notifications for paste actions (microsoft#160468)
Add progress for paste actions This shows a cancellable progress bar when paste actions take a long time to resolve
1 parent ded9a31 commit aad6ffd

File tree

1 file changed

+79
-36
lines changed

1 file changed

+79
-36
lines changed

src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import { DataTransfers } from 'vs/base/browser/dnd';
77
import { addDisposableListener } from 'vs/base/browser/dom';
8-
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
8+
import { CancelablePromise, createCancelablePromise, raceCancellation } from 'vs/base/common/async';
9+
import { CancellationToken } from 'vs/base/common/cancellation';
910
import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer';
1011
import { Disposable } from 'vs/base/common/lifecycle';
1112
import { Mimes } from 'vs/base/common/mime';
@@ -15,14 +16,18 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
1516
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
1617
import { EditorOption } from 'vs/editor/common/config/editorOptions';
1718
import { IRange, Range } from 'vs/editor/common/core/range';
19+
import { Selection } from 'vs/editor/common/core/selection';
1820
import { Handler, IEditorContribution, PastePayload } from 'vs/editor/common/editorCommon';
21+
import { DocumentPasteEdit, DocumentPasteEditProvider } from 'vs/editor/common/languages';
1922
import { ITextModel } from 'vs/editor/common/model';
2023
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
2124
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
2225
import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2';
2326
import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser';
27+
import { localize } from 'vs/nls';
2428
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
2529
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
30+
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
2631

2732
const vscodeClipboardMime = 'application/vnd.code.copyMetadata';
2833

@@ -52,6 +57,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
5257
@IClipboardService private readonly _clipboardService: IClipboardService,
5358
@IConfigurationService private readonly _configurationService: IConfigurationService,
5459
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
60+
@IProgressService private readonly _progressService: IProgressService,
5561
) {
5662
super();
5763

@@ -160,71 +166,108 @@ export class CopyPasteController extends Disposable implements IEditorContributi
160166
e.preventDefault();
161167
e.stopImmediatePropagation();
162168

163-
const originalDocVersion = model.getVersionId();
164169
const tokenSource = new EditorStateCancellationTokenSource(this._editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection);
165-
166170
try {
167171
const dataTransfer = toVSDataTransfer(e.clipboardData);
168172

169173
if (metadata?.id && this._currentClipboardItem?.handle === metadata.id) {
170174
const toMergeDataTransfer = await this._currentClipboardItem.dataTransferPromise;
175+
if (tokenSource.token.isCancellationRequested) {
176+
return;
177+
}
178+
171179
toMergeDataTransfer.forEach((value, key) => {
172180
dataTransfer.replace(key, value);
173181
});
174182
}
175183

176184
if (!dataTransfer.has(Mimes.uriList)) {
177185
const resources = await this._clipboardService.readResources();
186+
if (tokenSource.token.isCancellationRequested) {
187+
return;
188+
}
189+
178190
if (resources.length) {
179191
dataTransfer.append(Mimes.uriList, createStringDataTransferItem(UriList.create(resources)));
180192
}
181193
}
182194

183195
dataTransfer.delete(vscodeClipboardMime);
184196

185-
for (const provider of providers) {
186-
if (!provider.pasteMimeTypes.some(type => {
187-
if (type.toLowerCase() === DataTransfers.FILES.toLowerCase()) {
188-
return [...dataTransfer.values()].some(item => item.asFile());
189-
}
190-
return dataTransfer.has(type);
191-
})) {
192-
continue;
193-
}
197+
const providerEdit = await this._progressService.withProgress({
198+
location: ProgressLocation.Notification,
199+
delay: 750,
200+
title: localize('pasteProgressTitle', "Running paste handlers..."),
201+
cancellable: true,
202+
}, () => {
203+
return this.getProviderPasteEdit(providers, dataTransfer, model, selections, tokenSource.token);
204+
}, () => {
205+
return tokenSource.cancel();
206+
});
194207

195-
const edit = await provider.provideDocumentPasteEdits(model, selections, dataTransfer, tokenSource.token);
196-
if (originalDocVersion !== model.getVersionId()) {
197-
return;
208+
if (tokenSource.token.isCancellationRequested) {
209+
return;
210+
}
211+
212+
if (providerEdit) {
213+
performSnippetEdit(this._editor, typeof providerEdit.insertText === 'string' ? SnippetParser.escape(providerEdit.insertText) : providerEdit.insertText.snippet, selections);
214+
215+
if (providerEdit.additionalEdit) {
216+
await this._bulkEditService.apply(providerEdit.additionalEdit, { editor: this._editor });
198217
}
218+
return;
219+
}
199220

200-
if (edit) {
201-
performSnippetEdit(this._editor, typeof edit.insertText === 'string' ? SnippetParser.escape(edit.insertText) : edit.insertText.snippet, selections);
221+
await this.applyDefaultPasteHandler(dataTransfer, metadata, tokenSource.token);
222+
} finally {
223+
tokenSource.dispose();
224+
}
225+
}
202226

203-
if (edit.additionalEdit) {
204-
await this._bulkEditService.apply(edit.additionalEdit, { editor: this._editor });
205-
}
227+
private getProviderPasteEdit(providers: DocumentPasteEditProvider[], dataTransfer: VSDataTransfer, model: ITextModel, selections: Selection[], token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
228+
return raceCancellation((async () => {
229+
for (const provider of providers) {
230+
if (token.isCancellationRequested) {
206231
return;
207232
}
208-
}
209233

210-
// Default handler
211-
const textDataTransfer = dataTransfer.get(Mimes.text) ?? dataTransfer.get('text');
212-
if (!textDataTransfer) {
213-
return;
214-
}
234+
if (!isSupportedProvider(provider, dataTransfer)) {
235+
continue;
236+
}
215237

216-
const text = await textDataTransfer.asString();
217-
if (originalDocVersion !== model.getVersionId()) {
218-
return;
238+
const edit = await provider.provideDocumentPasteEdits(model, selections, dataTransfer, token);
239+
if (edit) {
240+
return edit;
241+
}
219242
}
243+
return undefined;
244+
})(), token);
245+
}
220246

221-
this._editor.trigger('keyboard', Handler.Paste, <PastePayload>{
222-
text: text,
223-
pasteOnNewLine: metadata?.wasFromEmptySelection,
224-
multicursorText: null
225-
});
226-
} finally {
227-
tokenSource.dispose();
247+
private async applyDefaultPasteHandler(dataTransfer: VSDataTransfer, metadata: CopyMetadata | undefined, token: CancellationToken) {
248+
const textDataTransfer = dataTransfer.get(Mimes.text) ?? dataTransfer.get('text');
249+
if (!textDataTransfer) {
250+
return;
251+
}
252+
253+
const text = await textDataTransfer.asString();
254+
if (token.isCancellationRequested) {
255+
return;
228256
}
257+
258+
this._editor.trigger('keyboard', Handler.Paste, <PastePayload>{
259+
text: text,
260+
pasteOnNewLine: metadata?.wasFromEmptySelection,
261+
multicursorText: null
262+
});
229263
}
230264
}
265+
266+
function isSupportedProvider(provider: DocumentPasteEditProvider, dataTransfer: VSDataTransfer): boolean {
267+
return provider.pasteMimeTypes.some(type => {
268+
if (type.toLowerCase() === DataTransfers.FILES.toLowerCase()) {
269+
return [...dataTransfer.values()].some(item => item.asFile());
270+
}
271+
return dataTransfer.has(type);
272+
});
273+
}

0 commit comments

Comments
 (0)