Switch to provider based model for on drop

This simplifies implementing the provider and also give potentially gives us more control over how the drop happens
This commit is contained in:
Matt Bierner 2022-04-01 15:50:17 -07:00
parent 5451119820
commit 44ffeba237
No known key found for this signature in database
GPG key ID: 099C331567E11888
13 changed files with 188 additions and 186 deletions

View file

@ -49,8 +49,6 @@ export function activate(context: vscode.ExtensionContext) {
logger.updateConfiguration();
previewManager.updateConfiguration();
}));
context.subscriptions.push(registerDropIntoEditor());
}
function registerMarkdownLanguageFeatures(
@ -72,6 +70,7 @@ function registerMarkdownLanguageFeatures(
vscode.languages.registerReferenceProvider(selector, referencesProvider),
vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesProvider, githubSlugifier)),
MdPathCompletionProvider.register(selector, engine, linkProvider),
registerDropIntoEditor(selector),
);
}

View file

@ -7,10 +7,10 @@ import * as path from 'path';
import * as vscode from 'vscode';
import * as URI from 'vscode-uri';
export function registerDropIntoEditor() {
return vscode.workspace.onWillDropOnTextEditor(e => {
e.waitUntil((async (): Promise<vscode.SnippetTextEdit | undefined> => {
const urlList = await e.dataTransfer.get('text/uri-list')?.asString();
export function registerDropIntoEditor(selector: vscode.DocumentSelector) {
return vscode.languages.registerDocumentOnDropProvider(selector, new class implements vscode.DocumentOnDropProvider {
async provideDocumentOnDropEdits(document: vscode.TextDocument, position: vscode.Position, dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): Promise<vscode.SnippetTextEdit | undefined> {
const urlList = await dataTransfer.get('text/uri-list')?.asString();
if (!urlList) {
return undefined;
}
@ -30,7 +30,7 @@ export function registerDropIntoEditor() {
const snippet = new vscode.SnippetString();
uris.forEach((uri, i) => {
const rel = path.relative(URI.Utils.dirname(e.editor.document.uri).fsPath, uri.fsPath);
const rel = path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath);
snippet.appendText('[');
snippet.appendTabstop();
@ -41,7 +41,7 @@ export function registerDropIntoEditor() {
}
});
return new vscode.SnippetTextEdit(new vscode.Range(e.position, e.position), snippet);
})());
return new vscode.SnippetTextEdit(new vscode.Range(position, position), snippet);
}
});
}

View file

@ -1968,3 +1968,23 @@ export enum ExternalUriOpenerPriority {
Default = 2,
Preferred = 3,
}
/**
* @internal
*/
export interface IDataTransferItem {
asString(): Thenable<string>;
value: any;
}
/**
* @internal
*/
export type IDataTransfer = Map<string, IDataTransferItem>;
/**
* @internal
*/
export interface DocumentOnDropEditProvider {
provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IDataTransfer, token: CancellationToken): ProviderResult<SnippetTextEdit>;
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { LanguageFeatureRegistry, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ILanguageFeaturesService = createDecorator<ILanguageFeaturesService>('ILanguageFeaturesService');
@ -67,6 +67,8 @@ export interface ILanguageFeaturesService {
readonly evaluatableExpressionProvider: LanguageFeatureRegistry<EvaluatableExpressionProvider>;
readonly documentOnDropEditProvider: LanguageFeatureRegistry<DocumentOnDropEditProvider>;
// --
setNotebookTypeResolver(resolver: NotebookInfoResolver | undefined): void;

View file

@ -5,7 +5,7 @@
import { URI } from 'vs/base/common/uri';
import { LanguageFeatureRegistry, NotebookInfo, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
@ -40,7 +40,7 @@ export class LanguageFeaturesService implements ILanguageFeaturesService {
readonly evaluatableExpressionProvider = new LanguageFeatureRegistry<EvaluatableExpressionProvider>(this._score.bind(this));
readonly documentRangeSemanticTokensProvider = new LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>(this._score.bind(this));
readonly documentSemanticTokensProvider = new LanguageFeatureRegistry<DocumentSemanticTokensProvider>(this._score.bind(this));
readonly documentOnDropEditProvider = new LanguageFeatureRegistry<DocumentOnDropEditProvider>(this._score.bind(this));
private _notebookTypeResolver?: NotebookInfoResolver;

View file

@ -304,7 +304,7 @@ export class MainThreadDocumentsAndEditors {
this._mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(extHostContext, this._modelService, this._textFileService, fileService, textModelResolverService, environmentService, uriIdentityService, workingCopyFileService, pathService));
extHostContext.set(MainContext.MainThreadDocuments, this._mainThreadDocuments);
this._mainThreadEditors = this._toDispose.add(new MainThreadTextEditors(this, extHostContext, codeEditorService, this._editorService, this._editorGroupService, instantiationService));
this._mainThreadEditors = this._toDispose.add(new MainThreadTextEditors(this, extHostContext, codeEditorService, this._editorService, this._editorGroupService));
extHostContext.set(MainContext.MainThreadTextEditors, this._mainThreadEditors);
// It is expected that the ctor of the state computer calls our `_onDelta`.

View file

@ -14,7 +14,7 @@ import { IDecorationOptions, IDecorationRenderOptions } from 'vs/editor/common/e
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ITextEditorOptions, IResourceEditorInput, EditorActivation, EditorResolution } from 'vs/platform/editor/common/editor';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { MainThreadTextEditor } from 'vs/workbench/api/browser/mainThreadEditor';
import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType } from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToColumn, columnToEditorGroup, EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
@ -27,13 +27,6 @@ import { ILineChange } from 'vs/editor/common/diff/diffComputer';
import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { IEditorControl } from 'vs/workbench/common/editor';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { DataTransferConverter } from 'vs/workbench/api/common/shared/dataTransfer';
import { IPosition } from 'vs/editor/common/core/position';
import { IDataTransfer, IDataTransferItem } from 'vs/workbench/common/dnd';
import { extractEditorsDropData } from 'vs/workbench/browser/dnd';
import { Mimes } from 'vs/base/common/mime';
import { distinct } from 'vs/base/common/arrays';
import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2';
export interface IMainThreadEditorLocator {
getEditor(id: string): MainThreadTextEditor | undefined;
@ -51,7 +44,6 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
private _textEditorsListenersMap: { [editorId: string]: IDisposable[] };
private _editorPositionData: ITextEditorPositionData | null;
private _registeredDecorationTypes: { [decorationType: string]: boolean };
private readonly _dropIntoEditorListeners = new Map<ICodeEditor, IDisposable>();
constructor(
private readonly _editorLocator: IMainThreadEditorLocator,
@ -59,7 +51,6 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IEditorService private readonly _editorService: IEditorService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
this._instanceId = String(++MainThreadTextEditors.INSTANCE_COUNT);
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditors);
@ -71,22 +62,6 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
this._toDispose.add(this._editorGroupService.onDidRemoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.add(this._editorGroupService.onDidMoveGroup(() => this._updateActiveAndVisibleTextEditors()));
const registerDropListenerOnEditor = (editor: ICodeEditor) => {
this._dropIntoEditorListeners.get(editor)?.dispose();
this._dropIntoEditorListeners.set(editor, editor.onDropIntoEditor(e => this.onDropIntoEditor(editor, e.position, e.event)));
};
this._toDispose.add(_codeEditorService.onCodeEditorAdd(registerDropListenerOnEditor));
this._toDispose.add(_codeEditorService.onCodeEditorRemove(editor => {
this._dropIntoEditorListeners.get(editor)?.dispose();
this._dropIntoEditorListeners.delete(editor);
}));
for (const editor of this._codeEditorService.listCodeEditors()) {
registerDropListenerOnEditor(editor);
}
this._registeredDecorationTypes = Object.create(null);
}
@ -99,8 +74,6 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
for (let decorationType in this._registeredDecorationTypes) {
this._codeEditorService.removeDecorationType(decorationType);
}
dispose(this._dropIntoEditorListeners.values());
this._dropIntoEditorListeners.clear();
this._registeredDecorationTypes = Object.create(null);
}
@ -140,59 +113,6 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
return result;
}
private async onDropIntoEditor(editor: ICodeEditor, position: IPosition, dragEvent: DragEvent) {
if (!dragEvent.dataTransfer || !editor.hasModel()) {
return;
}
const id = this._editorLocator.getIdOfCodeEditor(editor);
if (typeof id !== 'string') {
return;
}
const modelVersionNow = editor.getModel().getVersionId();
const textEditorDataTransfer: IDataTransfer = new Map<string, IDataTransferItem>();
for (const item of dragEvent.dataTransfer.items) {
if (item.kind === 'string') {
const type = item.type;
const asStringValue = new Promise<string>(resolve => item.getAsString(resolve));
textEditorDataTransfer.set(type, {
asString: () => asStringValue,
value: undefined
});
}
}
if (!textEditorDataTransfer.has(Mimes.uriList.toLowerCase())) {
const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent))
.filter(input => input.resource)
.map(input => input.resource!.toString());
if (editorData.length) {
const str = distinct(editorData).join('\n');
textEditorDataTransfer.set(Mimes.uriList.toLowerCase(), {
asString: () => Promise.resolve(str),
value: undefined
});
}
}
if (textEditorDataTransfer.size === 0) {
return;
}
const dataTransferDto = await DataTransferConverter.toDataTransferDTO(textEditorDataTransfer);
const edits = await this._proxy.$textEditorHandleDrop(id, position, dataTransferDto);
if (edits.length === 0) {
return;
}
if (editor.getModel().getVersionId() === modelVersionNow) {
const [first] = edits; // TODO@jrieken define how to pick the "one snippet edit";
performSnippetEdit(editor, first);
}
}
// --- from extension host process
async $tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string | undefined> {

View file

@ -3,14 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { distinct } from 'vs/base/common/arrays';
import { Emitter, Event } from 'vs/base/common/event';
import { ITextModel } from 'vs/editor/common/model';
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
import * as languages from 'vs/editor/common/languages';
import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { IPosition, Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, ILocationLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto, ILanguageWordDefinitionDto, IdentifiableInlineCompletions, IdentifiableInlineCompletion, ITypeHierarchyItemDto, IInlayHintDto } from '../common/extHost.protocol';
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
@ -27,19 +28,33 @@ import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticToken
import { revive } from 'vs/base/common/marshalling';
import { CancellationError } from 'vs/base/common/errors';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { Mimes } from 'vs/base/common/mime';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { DataTransferConverter } from 'vs/workbench/api/common/shared/dataTransfer';
import { extractEditorsDropData } from 'vs/workbench/browser/dnd';
import { IDataTransfer, IDataTransferItem } from 'vs/workbench/common/dnd';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape {
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,
@ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService,
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures);
if (this._languageService) {
@ -69,13 +84,34 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
});
updateAllWordDefinitions();
}
const registerDropListenerOnEditor = (editor: ICodeEditor) => {
this._dropIntoEditorListeners.get(editor)?.dispose();
this._dropIntoEditorListeners.set(editor, editor.onDropIntoEditor(e => this.onDropIntoEditor(editor, e.position, e.event)));
};
this._register(_codeEditorService.onCodeEditorAdd(registerDropListenerOnEditor));
this._register(_codeEditorService.onCodeEditorRemove(editor => {
this._dropIntoEditorListeners.get(editor)?.dispose();
this._dropIntoEditorListeners.delete(editor);
}));
for (const editor of this._codeEditorService.listCodeEditors()) {
registerDropListenerOnEditor(editor);
}
}
dispose(): void {
override dispose(): void {
for (const registration of this._registrations.values()) {
registration.dispose();
}
this._registrations.clear();
dispose(this._dropIntoEditorListeners.values());
this._dropIntoEditorListeners.clear();
super.dispose();
}
$unregister(handle: number): void {
@ -850,6 +886,69 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}));
}
// --- document drop Edits
$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);
}
}));
}
private async onDropIntoEditor(editor: ICodeEditor, position: IPosition, dragEvent: DragEvent) {
if (!dragEvent.dataTransfer || !editor.hasModel()) {
return;
}
const model = editor.getModel();
const modelVersionNow = model.getVersionId();
const textEditorDataTransfer: IDataTransfer = new Map<string, IDataTransferItem>();
for (const item of dragEvent.dataTransfer.items) {
if (item.kind === 'string') {
const type = item.type;
const asStringValue = new Promise<string>(resolve => item.getAsString(resolve));
textEditorDataTransfer.set(type, {
asString: () => asStringValue,
value: undefined
});
}
}
if (!textEditorDataTransfer.has(Mimes.uriList.toLowerCase())) {
const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent))
.filter(input => input.resource)
.map(input => input.resource!.toString());
if (editorData.length) {
const str = distinct(editorData).join('\n');
textEditorDataTransfer.set(Mimes.uriList.toLowerCase(), {
asString: () => Promise.resolve(str),
value: undefined
});
}
}
if (textEditorDataTransfer.size === 0) {
return;
}
const ordered = this._languageFeaturesService.documentOnDropEditProvider.ordered(model);
for (const provider of ordered) {
const edit = await provider.provideDocumentOnDropEdits(model, position, textEditorDataTransfer, CancellationToken.None);
if (editor.getModel().getVersionId() !== modelVersionNow) {
return;
}
if (edit) {
performSnippetEdit(editor, edit);
return;
}
}
}
}
export class MainThreadDocumentSemanticTokensProvider implements languages.DocumentSemanticTokensProvider {

View file

@ -532,6 +532,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
},
createLanguageStatusItem(id: string, selector: vscode.DocumentSelector): vscode.LanguageStatusItem {
return extHostLanguages.createLanguageStatusItem(extension, id, selector);
},
registerDocumentOnDropProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentOnDropProvider): vscode.Disposable {
checkProposedApiEnabled(extension, 'textEditorDrop');
return extHostLanguageFeatures.registerDocumentOnDropProvider(extension, selector, provider);
}
};
@ -872,10 +876,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
onWillSaveTextDocument: (listener, thisArgs?, disposables?) => {
return extHostDocumentSaveParticipant.getOnWillSaveTextDocumentEvent(extension)(listener, thisArgs, disposables);
},
onWillDropOnTextEditor: (listener, thisArgs?, disposables?) => {
checkProposedApiEnabled(extension, 'textEditorDrop');
return extHostEditors.onWillDropOnTextEditor(listener, thisArgs, disposables);
},
get notebookDocuments(): vscode.NotebookDocument[] {
return extHostNotebook.notebookDocuments.map(d => d.apiNotebook);
},

View file

@ -390,6 +390,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerTypeHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerDocumentOnDropProvider(handle: number, selector: IDocumentFilterDto[]): void;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void;
}
@ -1329,7 +1330,6 @@ export interface ISelectionChangeEvent {
export interface ExtHostEditorsShape {
$acceptEditorPropertiesChanged(id: string, props: IEditorPropertiesChangeData): void;
$acceptEditorPositionData(data: ITextEditorPositionData): void;
$textEditorHandleDrop(id: string, position: IPosition, dataTransferDto: DataTransferDTO): Promise<Dto<languages.SnippetTextEdit[]>>;
}
export interface IDocumentsAndEditorsDelta {
@ -1734,6 +1734,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>;
}
export interface ExtHostQuickOpenShape {

View file

@ -35,6 +35,8 @@ import { isCancellationError } from 'vs/base/common/errors';
import { Emitter } from 'vs/base/common/event';
import { raceCancellationError } from 'vs/base/common/async';
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { DataTransferConverter, DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer';
import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier';
// --- adapter
@ -1712,6 +1714,27 @@ class TypeHierarchyAdapter {
return map?.get(itemId);
}
}
class DocumentOnDropAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.DocumentOnDropProvider
) { }
async provideDocumentOnDropEdits(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 edit = await this._provider.provideDocumentOnDropEdits(doc, pos, dataTransfer, token);
if (!edit) {
return undefined;
}
return typeConvert.SnippetTextEdit.from(edit);
}
}
type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
@ -1720,7 +1743,8 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
| SelectionRangeAdapter | CallHierarchyAdapter | TypeHierarchyAdapter
| DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter
| EvaluatableExpressionAdapter | InlineValuesAdapter
| LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter | InlineCompletionAdapterNew;
| LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter | InlineCompletionAdapterNew
| DocumentOnDropAdapter;
class AdapterData {
constructor(
@ -2341,6 +2365,18 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
this._withAdapter(handle, TypeHierarchyAdapter, adapter => Promise.resolve(adapter.releaseSession(sessionId)), undefined, undefined);
}
// --- Document on drop
registerDocumentOnDropProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentOnDropProvider) {
const handle = this._addNewAdapter(new DocumentOnDropAdapter(this._documents, provider), 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);
}
// --- configuration
private static _serializeRegExp(regExp: RegExp): extHostProtocol.IRegExpDto {

View file

@ -3,20 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event';
import * as arrays from 'vs/base/common/arrays';
import { Emitter, Event } from 'vs/base/common/event';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostEditorsShape, IEditorPropertiesChangeData, IMainContext, ITextDocumentShowOptions, ITextEditorPositionData, MainContext, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { ExtHostTextEditor, TextEditorDecorationType } from 'vs/workbench/api/common/extHostTextEditor';
import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { SnippetTextEdit, TextEditorSelectionChangeKind } from 'vs/workbench/api/common/extHostTypes';
import { TextEditorSelectionChangeKind } from 'vs/workbench/api/common/extHostTypes';
import * as vscode from 'vscode';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { DataTransferConverter, DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer';
import { IPosition } from 'vs/editor/common/core/position';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import * as languages from 'vs/editor/common/languages';
export class ExtHostEditors implements ExtHostEditorsShape {
@ -26,7 +21,6 @@ export class ExtHostEditors implements ExtHostEditorsShape {
private readonly _onDidChangeTextEditorViewColumn = new Emitter<vscode.TextEditorViewColumnChangeEvent>();
private readonly _onDidChangeActiveTextEditor = new Emitter<vscode.TextEditor | undefined>();
private readonly _onDidChangeVisibleTextEditors = new Emitter<vscode.TextEditor[]>();
private readonly _onWillDropOnTextEditor = new AsyncEmitter<vscode.TextEditorDropEvent>();
readonly onDidChangeTextEditorSelection: Event<vscode.TextEditorSelectionChangeEvent> = this._onDidChangeTextEditorSelection.event;
readonly onDidChangeTextEditorOptions: Event<vscode.TextEditorOptionsChangeEvent> = this._onDidChangeTextEditorOptions.event;
@ -34,7 +28,6 @@ export class ExtHostEditors implements ExtHostEditorsShape {
readonly onDidChangeTextEditorViewColumn: Event<vscode.TextEditorViewColumnChangeEvent> = this._onDidChangeTextEditorViewColumn.event;
readonly onDidChangeActiveTextEditor: Event<vscode.TextEditor | undefined> = this._onDidChangeActiveTextEditor.event;
readonly onDidChangeVisibleTextEditors: Event<vscode.TextEditor[]> = this._onDidChangeVisibleTextEditors.event;
readonly onWillDropOnTextEditor: Event<vscode.TextEditorDropEvent> = this._onWillDropOnTextEditor.event;
private readonly _proxy: MainThreadTextEditorsShape;
@ -166,33 +159,4 @@ export class ExtHostEditors implements ExtHostEditorsShape {
getDiffInformation(id: string): Promise<vscode.LineChange[]> {
return Promise.resolve(this._proxy.$getDiffInformation(id));
}
// --- Text editor drag and drop
async $textEditorHandleDrop(id: string, position: IPosition, dataTransferDto: DataTransferDTO): Promise<Dto<languages.SnippetTextEdit[]>> {
const textEditor = this._extHostDocumentsAndEditors.getEditor(id);
if (!textEditor) {
throw new Error('Unknown text editor');
}
const pos = TypeConverters.Position.to(position);
const dataTransfer = DataTransferConverter.toDataTransfer(dataTransferDto);
const event = Object.freeze({
editor: textEditor.value,
position: pos,
dataTransfer: dataTransfer
});
const edits: SnippetTextEdit[] = [];
await this._onWillDropOnTextEditor.fireAsync(event, CancellationToken.None, async p => {
const value = await p;
if (value instanceof SnippetTextEdit) {
edits.push(value);
}
});
return edits.map(TypeConverters.SnippetTextEdit.from);
}
}

View file

@ -13,50 +13,11 @@ declare module 'vscode' {
constructor(range: Range, snippet: SnippetString);
}
export interface TextEditorDropEvent {
/**
* The {@link TextEditor} the resource was dropped onto.
*/
readonly editor: TextEditor;
/**
* The position in the file where the drop occurred
*/
readonly position: Position;
/**
* The {@link DataTransfer data transfer} associated with this drop.
*/
readonly dataTransfer: DataTransfer;
/**
* Allows to pause the event to delay apply the drop.
*
* *Note:* This function can only be called during event dispatch and not
* in an asynchronous manner:
*
* ```ts
* workspace.onWillDropOnTextEditor(event => {
* // async, will *throw* an error
* setTimeout(() => event.waitUntil(promise));
*
* // sync, OK
* event.waitUntil(promise);
* })
* ```
*
* @param thenable A thenable that delays saving.
*/
waitUntil(thenable: Thenable<SnippetTextEdit | undefined>): void;
token: CancellationToken;
export interface DocumentOnDropProvider {
provideDocumentOnDropEdits(document: TextDocument, position: Position, dataTransfer: DataTransfer, token: CancellationToken): ProviderResult<SnippetTextEdit>;
}
export namespace workspace {
/**
* Event fired when the user drops a resource into a text editor.
*/
export const onWillDropOnTextEditor: Event<TextEditorDropEvent>;
export namespace languages {
export function registerDocumentOnDropProvider(selector: DocumentSelector, provider: DocumentOnDropProvider): Disposable;
}
}