5
5
6
6
import { DataTransfers } from 'vs/base/browser/dnd' ;
7
7
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' ;
9
10
import { createStringDataTransferItem , VSDataTransfer } from 'vs/base/common/dataTransfer' ;
10
11
import { Disposable } from 'vs/base/common/lifecycle' ;
11
12
import { Mimes } from 'vs/base/common/mime' ;
@@ -15,14 +16,18 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
15
16
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService' ;
16
17
import { EditorOption } from 'vs/editor/common/config/editorOptions' ;
17
18
import { IRange , Range } from 'vs/editor/common/core/range' ;
19
+ import { Selection } from 'vs/editor/common/core/selection' ;
18
20
import { Handler , IEditorContribution , PastePayload } from 'vs/editor/common/editorCommon' ;
21
+ import { DocumentPasteEdit , DocumentPasteEditProvider } from 'vs/editor/common/languages' ;
19
22
import { ITextModel } from 'vs/editor/common/model' ;
20
23
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures' ;
21
24
import { CodeEditorStateFlag , EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState' ;
22
25
import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2' ;
23
26
import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser' ;
27
+ import { localize } from 'vs/nls' ;
24
28
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService' ;
25
29
import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
30
+ import { IProgressService , ProgressLocation } from 'vs/platform/progress/common/progress' ;
26
31
27
32
const vscodeClipboardMime = 'application/vnd.code.copyMetadata' ;
28
33
@@ -52,6 +57,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
52
57
@IClipboardService private readonly _clipboardService : IClipboardService ,
53
58
@IConfigurationService private readonly _configurationService : IConfigurationService ,
54
59
@ILanguageFeaturesService private readonly _languageFeaturesService : ILanguageFeaturesService ,
60
+ @IProgressService private readonly _progressService : IProgressService ,
55
61
) {
56
62
super ( ) ;
57
63
@@ -160,71 +166,108 @@ export class CopyPasteController extends Disposable implements IEditorContributi
160
166
e . preventDefault ( ) ;
161
167
e . stopImmediatePropagation ( ) ;
162
168
163
- const originalDocVersion = model . getVersionId ( ) ;
164
169
const tokenSource = new EditorStateCancellationTokenSource ( this . _editor , CodeEditorStateFlag . Value | CodeEditorStateFlag . Selection ) ;
165
-
166
170
try {
167
171
const dataTransfer = toVSDataTransfer ( e . clipboardData ) ;
168
172
169
173
if ( metadata ?. id && this . _currentClipboardItem ?. handle === metadata . id ) {
170
174
const toMergeDataTransfer = await this . _currentClipboardItem . dataTransferPromise ;
175
+ if ( tokenSource . token . isCancellationRequested ) {
176
+ return ;
177
+ }
178
+
171
179
toMergeDataTransfer . forEach ( ( value , key ) => {
172
180
dataTransfer . replace ( key , value ) ;
173
181
} ) ;
174
182
}
175
183
176
184
if ( ! dataTransfer . has ( Mimes . uriList ) ) {
177
185
const resources = await this . _clipboardService . readResources ( ) ;
186
+ if ( tokenSource . token . isCancellationRequested ) {
187
+ return ;
188
+ }
189
+
178
190
if ( resources . length ) {
179
191
dataTransfer . append ( Mimes . uriList , createStringDataTransferItem ( UriList . create ( resources ) ) ) ;
180
192
}
181
193
}
182
194
183
195
dataTransfer . delete ( vscodeClipboardMime ) ;
184
196
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
+ } ) ;
194
207
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 } ) ;
198
217
}
218
+ return ;
219
+ }
199
220
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
+ }
202
226
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 ) {
206
231
return ;
207
232
}
208
- }
209
233
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
+ }
215
237
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
+ }
219
242
}
243
+ return undefined ;
244
+ } ) ( ) , token ) ;
245
+ }
220
246
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 ;
228
256
}
257
+
258
+ this . _editor . trigger ( 'keyboard' , Handler . Paste , < PastePayload > {
259
+ text : text ,
260
+ pasteOnNewLine : metadata ?. wasFromEmptySelection ,
261
+ multicursorText : null
262
+ } ) ;
229
263
}
230
264
}
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