Update drop metadata proposal (#179918)

- Makes `dropMimeTypes` required
- Prefix the actual `id` used internally with the extension id
- Allow wildcard mime types, such as `image/*`
This commit is contained in:
Matt Bierner 2023-04-13 14:59:38 -07:00 committed by GitHub
parent 830d534e27
commit 739b93cce8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 55 additions and 12 deletions

View file

@ -47,7 +47,7 @@ export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector)
return edit;
}
}, {
id: 'vscode.markdown.insertLink',
id: 'insertLink',
dropMimeTypes: [
'text/uri-list'
]

View file

@ -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];
}

View file

@ -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<DocumentOnDropEdit>;

View file

@ -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 => {

View file

@ -907,8 +907,8 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
private readonly _documentOnDropEditProviders = new Map<number, MainThreadDocumentOnDropEditProvider>();
$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<languages.DocumentOnDropEdit | null | undefined> {

View file

@ -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<VSBuffer>;
$resolveDocumentOnDropFileData(handle: number, requestId: number, dataId: string): Promise<VSBuffer>;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void;

View file

@ -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);
}

View file

@ -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 {