mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-22 01:29:04 +01:00
Add API proposal for reading files in data transfer (#148596)
* Add experimental support for reading files in data transfer
Adds a new `DataTransfer.asFile` method which lets you get file objects from a `DataTransfer`. This is currently only hooked up for drop into editors.
A few follow ups:
- Right now the file data is also read eagerly when it is transfered to the extension host. Before shipping this we would make this happen lazily instead
- The drop into editor api does not provide a nice way to do anything with the dropped files.
We should at least support returning a `WorkspaceEdit`. However `WorkspaceEdit` only supports text files, so we would also need to add an API that lets it deal with binary files
* Make `asFile` return a value instead of a promise
`asFile().data()` already returns a promise so `asFile` doesn't also need to be async
* Trying resolving data files transfer lazily
* Cleaning up code for lazy drop
* Remove testing code
* Remove unneeded buffer serialize
* 💄
This commit is contained in:
@@ -3,16 +3,16 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { CancellationError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position as EditorPosition } from 'vs/editor/common/core/position';
|
||||
import { IPosition, Position as EditorPosition } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import * as languages from 'vs/editor/common/languages';
|
||||
@@ -23,12 +23,14 @@ import { ITextModel } from 'vs/editor/common/model';
|
||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||
import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache';
|
||||
import { DataTransferConverter } from 'vs/workbench/api/common/shared/dataTransfer';
|
||||
import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
|
||||
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
|
||||
import { ExtHostContext, ExtHostLanguageFeaturesShape, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape, reviveWorkspaceEditDto } from '../common/extHost.protocol';
|
||||
import { IDataTransfer } from 'vs/editor/common/dnd';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
|
||||
export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape {
|
||||
@@ -36,8 +38,6 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
|
||||
private readonly _proxy: ExtHostLanguageFeaturesShape;
|
||||
private readonly _registrations = new Map<number, IDisposable>();
|
||||
|
||||
private readonly _dropIntoEditorListeners = new Map<ICodeEditor, IDisposable>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ILanguageService private readonly _languageService: ILanguageService,
|
||||
@@ -83,9 +83,6 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
|
||||
}
|
||||
this._registrations.clear();
|
||||
|
||||
dispose(this._dropIntoEditorListeners.values());
|
||||
this._dropIntoEditorListeners.clear();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -864,13 +861,47 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
|
||||
|
||||
// --- document drop Edits
|
||||
|
||||
private readonly _documentOnDropProviders = new Map<number, MainThreadDocumentOnDropProvider>();
|
||||
|
||||
$registerDocumentOnDropProvider(handle: number, selector: IDocumentFilterDto[]): void {
|
||||
this._registrations.set(handle, this._languageFeaturesService.documentOnDropEditProvider.register(selector, {
|
||||
provideDocumentOnDropEdits: async (model, position, dataTransfer, token) => {
|
||||
const dataTransferDto = await DataTransferConverter.toDataTransferDTO(dataTransfer);
|
||||
return this._proxy.$provideDocumentOnDropEdits(handle, model.uri, position, dataTransferDto, token);
|
||||
}
|
||||
}));
|
||||
const provider = new MainThreadDocumentOnDropProvider(handle, this._proxy);
|
||||
this._documentOnDropProviders.set(handle, provider);
|
||||
this._registrations.set(handle, combinedDisposable(
|
||||
this._languageFeaturesService.documentOnDropEditProvider.register(selector, provider),
|
||||
toDisposable(() => this._documentOnDropProviders.delete(handle)),
|
||||
));
|
||||
}
|
||||
|
||||
async $resolveDocumentOnDropFileData(handle: number, requestId: number, dataIndex: number): Promise<VSBuffer> {
|
||||
const provider = this._documentOnDropProviders.get(handle);
|
||||
if (!provider) {
|
||||
throw new Error('Could not find provider');
|
||||
}
|
||||
return provider.resolveDocumentOnDropFileData(requestId, dataIndex);
|
||||
}
|
||||
}
|
||||
|
||||
class MainThreadDocumentOnDropProvider implements languages.DocumentOnDropEditProvider {
|
||||
|
||||
private readonly dataTransfers = new DataTransferCache();
|
||||
|
||||
constructor(
|
||||
private readonly handle: number,
|
||||
private readonly _proxy: ExtHostLanguageFeaturesShape,
|
||||
) { }
|
||||
|
||||
async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: IDataTransfer, token: CancellationToken): Promise<languages.SnippetTextEdit | null | undefined> {
|
||||
const request = this.dataTransfers.add(dataTransfer);
|
||||
try {
|
||||
const dataTransferDto = await DataTransferConverter.toDataTransferDTO(dataTransfer);
|
||||
return await this._proxy.$provideDocumentOnDropEdits(this.handle, request.id, model.uri, position, dataTransferDto, token);
|
||||
} finally {
|
||||
request.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public resolveDocumentOnDropFileData(requestId: number, dataIndex: number): Promise<VSBuffer> {
|
||||
return this.dataTransfers.resolveDropFileData(requestId, dataIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,16 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { DataTransferConverter } from 'vs/workbench/api/common/shared/dataTransfer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IDataTransfer } from 'vs/workbench/common/dnd';
|
||||
import { IDataTransfer } from 'vs/editor/common/dnd';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
|
||||
export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape {
|
||||
|
||||
private readonly _proxy: ExtHostTreeViewsShape;
|
||||
private readonly _dataProviders: Map<string, TreeViewDataProvider> = new Map<string, TreeViewDataProvider>();
|
||||
private readonly _dndControllers = new Map<string, TreeViewDragAndDropController>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@@ -49,6 +52,9 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
viewer.showCollapseAllAction = !!options.showCollapseAll;
|
||||
viewer.canSelectMany = !!options.canSelectMany;
|
||||
viewer.dragAndDropController = dndController;
|
||||
if (dndController) {
|
||||
this._dndControllers.set(treeViewId, dndController);
|
||||
}
|
||||
viewer.dataProvider = dataProvider;
|
||||
this.registerListeners(treeViewId, viewer);
|
||||
this._proxy.$setVisible(treeViewId, viewer.visible);
|
||||
@@ -111,6 +117,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
}
|
||||
}
|
||||
|
||||
$resolveDropFileData(destinationViewId: string, requestId: number, dataItemIndex: number): Promise<VSBuffer> {
|
||||
const controller = this._dndControllers.get(destinationViewId);
|
||||
if (!controller) {
|
||||
throw new Error('Unknown tree');
|
||||
}
|
||||
return controller.resolveDropFileData(requestId, dataItemIndex);
|
||||
}
|
||||
|
||||
private async reveal(treeView: ITreeView, dataProvider: TreeViewDataProvider, itemIn: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void> {
|
||||
options = options ? options : { select: false, focus: false };
|
||||
const select = isUndefinedOrNull(options.select) ? false : options.select;
|
||||
@@ -170,6 +184,9 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
}
|
||||
});
|
||||
this._dataProviders.clear();
|
||||
|
||||
this._dndControllers.clear();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -178,6 +195,8 @@ type TreeItemHandle = string;
|
||||
|
||||
class TreeViewDragAndDropController implements ITreeViewDragAndDropController {
|
||||
|
||||
private readonly dataTransfersCache = new DataTransferCache();
|
||||
|
||||
constructor(private readonly treeViewId: string,
|
||||
readonly dropMimeTypes: string[],
|
||||
readonly dragMimeTypes: string[],
|
||||
@@ -186,7 +205,12 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController {
|
||||
|
||||
async handleDrop(dataTransfer: IDataTransfer, targetTreeItem: ITreeItem | undefined, token: CancellationToken,
|
||||
operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise<void> {
|
||||
return this._proxy.$handleDrop(this.treeViewId, await DataTransferConverter.toDataTransferDTO(dataTransfer), targetTreeItem?.handle, token, operationUuid, sourceTreeId, sourceTreeItemHandles);
|
||||
const request = this.dataTransfersCache.add(dataTransfer);
|
||||
try {
|
||||
return await this._proxy.$handleDrop(this.treeViewId, request.id, await DataTransferConverter.toDataTransferDTO(dataTransfer), targetTreeItem?.handle, token, operationUuid, sourceTreeId, sourceTreeItemHandles);
|
||||
} finally {
|
||||
request.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
async handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise<IDataTransfer | undefined> {
|
||||
@@ -197,7 +221,11 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController {
|
||||
if (!additionalTransferItems) {
|
||||
return;
|
||||
}
|
||||
return DataTransferConverter.toDataTransfer(additionalTransferItems);
|
||||
return DataTransferConverter.toDataTransfer(additionalTransferItems, () => { throw new Error('not supported'); });
|
||||
}
|
||||
|
||||
public resolveDropFileData(requestId: number, dataItemIndex: number): Promise<VSBuffer> {
|
||||
return this.dataTransfersCache.resolveDropFileData(requestId, dataItemIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -263,6 +263,7 @@ export interface MainThreadTreeViewsShape extends IDisposable {
|
||||
$setMessage(treeViewId: string, message: string): void;
|
||||
$setTitle(treeViewId: string, title: string, description: string | undefined): void;
|
||||
$setBadge(treeViewId: string, badge: IViewBadge | undefined): void;
|
||||
$resolveDropFileData(destinationViewId: string, requestId: number, dataItemIndex: number): Promise<VSBuffer>;
|
||||
}
|
||||
|
||||
export interface MainThreadDownloadServiceShape extends IDisposable {
|
||||
@@ -391,6 +392,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
|
||||
$registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerTypeHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerDocumentOnDropProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$resolveDocumentOnDropFileData(handle: number, requestId: number, dataIndex: number): Promise<VSBuffer>;
|
||||
$setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void;
|
||||
}
|
||||
|
||||
@@ -1368,7 +1370,7 @@ export interface ExtHostDocumentsAndEditorsShape {
|
||||
|
||||
export interface ExtHostTreeViewsShape {
|
||||
$getChildren(treeViewId: string, treeItemHandle?: string): Promise<ITreeItem[] | undefined>;
|
||||
$handleDrop(destinationViewId: string, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise<void>;
|
||||
$handleDrop(destinationViewId: string, requestId: number, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise<void>;
|
||||
$handleDrag(sourceViewId: string, sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise<DataTransferDTO | undefined>;
|
||||
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void;
|
||||
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
|
||||
@@ -1756,7 +1758,7 @@ export interface ExtHostLanguageFeaturesShape {
|
||||
$provideTypeHierarchySupertypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<ITypeHierarchyItemDto[] | undefined>;
|
||||
$provideTypeHierarchySubtypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<ITypeHierarchyItemDto[] | undefined>;
|
||||
$releaseTypeHierarchy(handle: number, sessionId: string): void;
|
||||
$provideDocumentOnDropEdits(handle: number, resource: UriComponents, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise<Dto<languages.SnippetTextEdit> | undefined>;
|
||||
$provideDocumentOnDropEdits(handle: number, requestId: number, resource: UriComponents, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise<Dto<languages.SnippetTextEdit> | undefined>;
|
||||
}
|
||||
|
||||
export interface ExtHostQuickOpenShape {
|
||||
|
||||
@@ -1750,14 +1750,18 @@ class TypeHierarchyAdapter {
|
||||
class DocumentOnDropAdapter {
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape,
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.DocumentOnDropProvider
|
||||
private readonly _provider: vscode.DocumentOnDropProvider,
|
||||
private readonly _handle: number,
|
||||
) { }
|
||||
|
||||
async provideDocumentOnDropEdits(uri: URI, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise<Dto<languages.SnippetTextEdit> | undefined> {
|
||||
async provideDocumentOnDropEdits(requestId: number, uri: URI, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise<Dto<languages.SnippetTextEdit> | undefined> {
|
||||
const doc = this._documents.getDocument(uri);
|
||||
const pos = typeConvert.Position.to(position);
|
||||
const dataTransfer = DataTransferConverter.toDataTransfer(dataTransferDto);
|
||||
const dataTransfer = DataTransferConverter.toDataTransfer(dataTransferDto, async (index) => {
|
||||
return (await this._proxy.$resolveDocumentOnDropFileData(this._handle, requestId, index)).buffer;
|
||||
});
|
||||
|
||||
const edit = await this._provider.provideDocumentOnDropEdits(doc, pos, dataTransfer, token);
|
||||
if (!edit) {
|
||||
@@ -2400,13 +2404,15 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||
// --- Document on drop
|
||||
|
||||
registerDocumentOnDropProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentOnDropProvider) {
|
||||
const handle = this._addNewAdapter(new DocumentOnDropAdapter(this._documents, provider), extension);
|
||||
const handle = this._nextHandle();
|
||||
this._adapter.set(handle, new AdapterData(new DocumentOnDropAdapter(this._proxy, this._documents, provider, handle), extension));
|
||||
this._proxy.$registerDocumentOnDropProvider(handle, this._transformDocumentSelector(selector));
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
$provideDocumentOnDropEdits(handle: number, resource: UriComponents, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise<Dto<languages.SnippetTextEdit> | undefined> {
|
||||
return this._withAdapter(handle, DocumentOnDropAdapter, adapter => Promise.resolve(adapter.provideDocumentOnDropEdits(URI.revive(resource), position, dataTransferDto, token)), undefined, undefined);
|
||||
$provideDocumentOnDropEdits(handle: number, requestId: number, resource: UriComponents, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise<Dto<languages.SnippetTextEdit> | undefined> {
|
||||
return this._withAdapter(handle, DocumentOnDropAdapter, adapter =>
|
||||
Promise.resolve(adapter.provideDocumentOnDropEdits(requestId, URI.revive(resource), position, dataTransferDto, token)), undefined, undefined);
|
||||
}
|
||||
|
||||
// --- configuration
|
||||
|
||||
@@ -25,7 +25,7 @@ import { Command } from 'vs/editor/common/languages';
|
||||
import { DataTransferConverter, DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer';
|
||||
import { ITreeViewsService, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService';
|
||||
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IDataTransfer } from 'vs/workbench/common/dnd';
|
||||
import { IDataTransfer } from 'vs/editor/common/dnd';
|
||||
|
||||
type TreeItemHandle = string;
|
||||
|
||||
@@ -144,14 +144,16 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
return treeView.getChildren(treeItemHandle);
|
||||
}
|
||||
|
||||
async $handleDrop(destinationViewId: string, treeDataTransferDTO: DataTransferDTO, targetItemHandle: string | undefined, token: CancellationToken,
|
||||
async $handleDrop(destinationViewId: string, requestId: number, treeDataTransferDTO: DataTransferDTO, targetItemHandle: string | undefined, token: CancellationToken,
|
||||
operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise<void> {
|
||||
const treeView = this.treeViews.get(destinationViewId);
|
||||
if (!treeView) {
|
||||
return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', destinationViewId)));
|
||||
}
|
||||
|
||||
const treeDataTransfer = DataTransferConverter.toDataTransfer(treeDataTransferDTO);
|
||||
const treeDataTransfer = DataTransferConverter.toDataTransfer(treeDataTransferDTO, async dataItemIndex => {
|
||||
return (await this._proxy.$resolveDropFileData(destinationViewId, requestId, dataItemIndex)).buffer;
|
||||
});
|
||||
if ((sourceViewId === destinationViewId) && sourceTreeItemHandles) {
|
||||
await this.addAdditionalTransferItems(treeDataTransfer, treeView, sourceTreeItemHandles, token, operationUuid);
|
||||
}
|
||||
@@ -164,7 +166,21 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
if (existingTransferOperation) {
|
||||
(await existingTransferOperation)?.forEach((value, key) => {
|
||||
if (value) {
|
||||
treeDataTransfer.set(key, value);
|
||||
const file = value.asFile();
|
||||
treeDataTransfer.set(key, {
|
||||
value: value.value,
|
||||
asString: value.asString,
|
||||
asFile() {
|
||||
if (!file) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
name: file.name,
|
||||
uri: file.uri,
|
||||
data: async () => await file.data()
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (operationUuid && treeView.handleDrag) {
|
||||
|
||||
@@ -2393,6 +2393,10 @@ export class DataTransferItem {
|
||||
return typeof this.value === 'string' ? this.value : JSON.stringify(this.value);
|
||||
}
|
||||
|
||||
asFile(): undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
constructor(public readonly value: any) { }
|
||||
}
|
||||
|
||||
|
||||
@@ -3,23 +3,42 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDataTransfer, IDataTransferItem } from 'vs/workbench/common/dnd';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IDataTransfer, IDataTransferItem } from 'vs/editor/common/dnd';
|
||||
|
||||
export interface IDataTransferFileDTO {
|
||||
readonly name: string;
|
||||
readonly uri?: UriComponents;
|
||||
}
|
||||
|
||||
interface DataTransferItemDTO {
|
||||
asString: string;
|
||||
readonly asString: string;
|
||||
readonly fileData: IDataTransferFileDTO | undefined;
|
||||
}
|
||||
|
||||
export interface DataTransferDTO {
|
||||
types: string[];
|
||||
items: DataTransferItemDTO[];
|
||||
readonly types: string[];
|
||||
readonly items: DataTransferItemDTO[];
|
||||
}
|
||||
|
||||
export namespace DataTransferConverter {
|
||||
export function toDataTransfer(value: DataTransferDTO): IDataTransfer {
|
||||
export function toDataTransfer(value: DataTransferDTO, resolveFileData: (dataItemIndex: number) => Promise<Uint8Array>): IDataTransfer {
|
||||
const newDataTransfer: IDataTransfer = new Map<string, IDataTransferItem>();
|
||||
value.types.forEach((type, index) => {
|
||||
newDataTransfer.set(type, {
|
||||
asString: async () => value.items[index].asString,
|
||||
asFile: () => {
|
||||
const file = value.items[index].fileData;
|
||||
if (!file) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
name: file.name,
|
||||
uri: URI.revive(file.uri),
|
||||
data: once(() => resolveFileData(index)),
|
||||
};
|
||||
},
|
||||
value: undefined
|
||||
});
|
||||
});
|
||||
@@ -31,11 +50,15 @@ export namespace DataTransferConverter {
|
||||
types: [],
|
||||
items: []
|
||||
};
|
||||
|
||||
const entries = Array.from(value.entries());
|
||||
for (const entry of entries) {
|
||||
newDTO.types.push(entry[0]);
|
||||
const stringValue = await entry[1].asString();
|
||||
const fileValue = entry[1].asFile();
|
||||
newDTO.items.push({
|
||||
asString: await entry[1].asString()
|
||||
asString: stringValue,
|
||||
fileData: fileValue ? { name: fileValue.name, uri: fileValue.uri } : undefined,
|
||||
});
|
||||
}
|
||||
return newDTO;
|
||||
|
||||
42
src/vs/workbench/api/common/shared/dataTransferCache.ts
Normal file
42
src/vs/workbench/api/common/shared/dataTransferCache.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IDataTransfer, IDataTransferItem } from 'vs/editor/common/dnd';
|
||||
|
||||
export class DataTransferCache {
|
||||
|
||||
private requestIdPool = 0;
|
||||
private readonly dataTransfers = new Map</* requestId */ number, ReadonlyArray<IDataTransferItem>>();
|
||||
|
||||
public add(dataTransfer: IDataTransfer): { id: number; dispose: () => void } {
|
||||
const requestId = this.requestIdPool++;
|
||||
this.dataTransfers.set(requestId, [...dataTransfer.values()]);
|
||||
return {
|
||||
id: requestId,
|
||||
dispose: () => {
|
||||
this.dataTransfers.delete(requestId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async resolveDropFileData(requestId: number, dataItemIndex: number): Promise<VSBuffer> {
|
||||
const entry = this.dataTransfers.get(requestId);
|
||||
if (!entry) {
|
||||
throw new Error('No data transfer found');
|
||||
}
|
||||
|
||||
const file = entry[dataItemIndex]?.asFile();
|
||||
if (!file) {
|
||||
throw new Error('No file item found in data transfer');
|
||||
}
|
||||
|
||||
return VSBuffer.wrap(await file.data());
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.dataTransfers.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user