diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropIntoEditor.ts index 899fd9a106c..1dd8fa78352 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropIntoEditor.ts @@ -47,7 +47,7 @@ export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector) return edit; } }, { - id: 'vscode.markdown.insertLink', + id: 'insertLink', dropMimeTypes: [ 'text/uri-list' ] diff --git a/src/vs/base/common/dataTransfer.ts b/src/vs/base/common/dataTransfer.ts index 7dd6d220b18..9b6ea49d717 100644 --- a/src/vs/base/common/dataTransfer.ts +++ b/src/vs/base/common/dataTransfer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { distinct } from 'vs/base/common/arrays'; +import { Iterable } from 'vs/base/common/iterator'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -46,10 +47,45 @@ export class VSDataTransfer { return this._entries.size; } + /** + * Check if this data transfer contains data for a given mime type. + * + * This uses exact matching and does support wildcards. + */ public has(mimeType: string): boolean { return this._entries.has(this.toKey(mimeType)); } + /** + * Check if this data transfer contains data matching a given mime type glob. + * + * This allows matching for wildcards, such as `image/*`. + */ + public matches(mimeTypeGlob: string): boolean { + // Exact match + if (this.has(mimeTypeGlob)) { + return true; + } + + // Anything glob + if (mimeTypeGlob === '*/*') { + return this._entries.size > 0; + } + + // Wildcard, such as `image/*` + const wildcard = this.toKey(mimeTypeGlob).match(/^([a-z]+)$\/([a-z]+|\*)/i); + if (!wildcard) { + return false; + } + + const [_, type, subtype] = wildcard; + if (subtype === '*') { + return Iterable.some(this._entries.keys(), key => key.startsWith(type + '/')); + } + + return false; + } + public get(mimeType: string): IDataTransferItem | undefined { return this._entries.get(this.toKey(mimeType))?.[0]; } diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 4fec0fec488..3f1626af433 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -1932,7 +1932,7 @@ export interface DocumentOnDropEdit { * @internal */ export interface DocumentOnDropEditProvider { - readonly id?: string; + readonly id: string; readonly dropMimeTypes?: readonly string[]; provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): ProviderResult; diff --git a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index 5a2d5a0904a..075b311d06e 100644 --- a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -94,7 +94,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr // Keep all providers that don't specify mime types return true; } - return provider.dropMimeTypes.some(mime => ourDataTransfer.has(mime)); + return provider.dropMimeTypes.some(mime => ourDataTransfer.matches(mime)); }); const possibleDropEdits = await raceCancellation(Promise.all(providers.map(provider => { diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 8d635e31499..12badd0d5be 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -907,8 +907,8 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread private readonly _documentOnDropEditProviders = new Map(); - $registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[], metadata?: { id: string; dropMimeTypes?: string[] }): void { - const provider = new MainThreadDocumentOnDropEditProvider(handle, metadata, this._proxy, this._uriIdentService); + $registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, metadata: { id: string; dropMimeTypes: string[] }): void { + const provider = new MainThreadDocumentOnDropEditProvider(handle, extensionId, metadata, this._proxy, this._uriIdentService); this._documentOnDropEditProviders.set(handle, provider); this._registrations.set(handle, combinedDisposable( this._languageFeaturesService.documentOnDropEditProvider.register(selector, provider), @@ -990,17 +990,18 @@ class MainThreadDocumentOnDropEditProvider implements languages.DocumentOnDropEd private readonly dataTransfers = new DataTransferCache(); - readonly id?: string; + readonly id: string; readonly dropMimeTypes?: readonly string[]; constructor( private readonly handle: number, - metadata: { id: string; dropMimeTypes?: readonly string[] } | undefined, + extensionId: ExtensionIdentifier, + metadata: { id: string; dropMimeTypes: readonly string[] } | undefined, private readonly _proxy: ExtHostLanguageFeaturesShape, @IUriIdentityService private readonly _uriIdentService: IUriIdentityService ) { - this.id = metadata?.id; - this.dropMimeTypes = metadata?.dropMimeTypes; + this.id = extensionId.value + (metadata ? '.' + metadata.id : ''); + this.dropMimeTypes = metadata?.dropMimeTypes ?? ['*/*']; } async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6533840ed53..b7acb99d2c8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -407,7 +407,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerTypeHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void; - $registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[], metadata?: { id: string; dropMimeTypes?: readonly string[] }): void; + $registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, metadata?: { id: string; dropMimeTypes: readonly string[] }): void; $resolvePasteFileData(handle: number, requestId: number, dataId: string): Promise; $resolveDocumentOnDropFileData(handle: number, requestId: number, dataId: string): Promise; $setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 96d19495803..7cc05ff289d 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -2386,7 +2386,9 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF registerDocumentOnDropEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentDropEditProvider, metadata?: vscode.DocumentDropEditProviderMetadata) { const handle = this._nextHandle(); this._adapter.set(handle, new AdapterData(new DocumentOnDropEditAdapter(this._proxy, this._documents, provider, handle, extension), extension)); - this._proxy.$registerDocumentOnDropEditProvider(handle, this._transformDocumentSelector(selector), metadata); + + this._proxy.$registerDocumentOnDropEditProvider(handle, this._transformDocumentSelector(selector), extension.identifier, isProposedApiEnabled(extension, 'dropMetadata') ? metadata : undefined); + return this._createDisposable(handle); } diff --git a/src/vscode-dts/vscode.proposed.dropMetadata.d.ts b/src/vscode-dts/vscode.proposed.dropMetadata.d.ts index 1a441377644..ea33a2fbafc 100644 --- a/src/vscode-dts/vscode.proposed.dropMetadata.d.ts +++ b/src/vscode-dts/vscode.proposed.dropMetadata.d.ts @@ -17,13 +17,17 @@ declare module 'vscode' { export interface DocumentDropEditProviderMetadata { /** * Unique identifier for the provider. + * + * This id should be unique within the extension but does not need to be unique across extensions. */ readonly id: string; /** * List of data transfer types that the provider supports. + * + * This can either be an exact mime type such as `image/png`, or a wildcard pattern such as `image/*`. */ - readonly dropMimeTypes?: readonly string[]; + readonly dropMimeTypes: readonly string[]; } export namespace languages {