Skip to content

Commit

Permalink
Update drop metadata proposal (#179918)
Browse files Browse the repository at this point in the history
- Makes `dropMimeTypes` required
- Prefix the actual `id` used internally with the extension id
- Allow wildcard mime types, such as `image/*`
  • Loading branch information
mjbvz committed Apr 13, 2023
1 parent 830d534 commit 739b93c
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector)
return edit;
}
}, {
id: 'vscode.markdown.insertLink',
id: 'insertLink',
dropMimeTypes: [
'text/uri-list'
]
Expand Down
36 changes: 36 additions & 0 deletions src/vs/base/common/dataTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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];
}
Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/common/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
13 changes: 7 additions & 6 deletions src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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> {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion src/vs/workbench/api/common/extHostLanguageFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
6 changes: 5 additions & 1 deletion src/vscode-dts/vscode.proposed.dropMetadata.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 739b93c

Please sign in to comment.