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