Iterate on paste edit provider api (#151477)

* Iterate on paste edit provider api

For #30066

- Pass all selections to paste providers. For #151326
- Introduce `DocumentPasteEdit` as return type. This new type uses an `insertText` that is applied to every paste location (for multicursor), plus an optional additional edit
-  Add `DocumentPasteProviderMetadata`. This lets extensions tell us which types of mimetypes they are interested in, letting us avoid round trips if no extensions care about the pasted data

* Correctly batch insertText
This commit is contained in:
Matt Bierner 2022-06-08 12:06:55 -07:00 committed by GitHub
parent 5a32c3ff17
commit 370dfd5fee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 143 additions and 76 deletions

View file

@ -4,23 +4,29 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { tryInsertUriList } from './dropIntoEditor';
import { tryGetUriListSnippet } from './dropIntoEditor';
export function registerPasteProvider(selector: vscode.DocumentSelector) {
return vscode.languages.registerDocumentPasteEditProvider(selector, new class implements vscode.DocumentPasteEditProvider {
async provideDocumentPasteEdits(
document: vscode.TextDocument,
range: vscode.Range,
_ranges: readonly vscode.Range[],
dataTransfer: vscode.DataTransfer,
token: vscode.CancellationToken,
): Promise<vscode.SnippetTextEdit | undefined> {
): Promise<vscode.DocumentPasteEdit | undefined> {
const enabled = vscode.workspace.getConfiguration('markdown', document).get('experimental.editor.pasteLinks.enabled', false);
if (!enabled) {
return;
}
return tryInsertUriList(document, range, dataTransfer, token);
const snippet = await tryGetUriListSnippet(document, dataTransfer, token);
if (snippet) {
return { insertText: snippet };
}
return undefined;
}
}, {
pasteMimeTypes: ['text/uri-list']
});
}

View file

@ -28,16 +28,20 @@ export function registerDropIntoEditor(selector: vscode.DocumentSelector) {
async provideDocumentOnDropEdits(document: vscode.TextDocument, position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.SnippetTextEdit | undefined> {
const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.drop.enabled', true);
if (!enabled) {
return;
return undefined;
}
const replacementRange = new vscode.Range(position, position);
return tryInsertUriList(document, replacementRange, dataTransfer, token);
const snippet = await tryGetUriListSnippet(document, dataTransfer, token);
if (snippet) {
return new vscode.SnippetTextEdit(replacementRange, snippet);
}
return undefined;
}
});
}
export async function tryInsertUriList(document: vscode.TextDocument, replacementRange: vscode.Range, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.SnippetTextEdit | undefined> {
export async function tryGetUriListSnippet(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.SnippetString | undefined> {
const urlList = await dataTransfer.get('text/uri-list')?.asString();
if (!urlList || token.isCancellationRequested) {
return undefined;
@ -72,5 +76,5 @@ export async function tryInsertUriList(document: vscode.TextDocument, replacemen
}
});
return new vscode.SnippetTextEdit(replacementRange, snippet);
return snippet;
}

View file

@ -724,10 +724,21 @@ export interface CodeActionProvider {
/**
* @internal
*/
export interface DocumentPasteEditProvider {
prepareDocumentPaste?(model: model.ITextModel, selection: Selection, dataTransfer: VSDataTransfer, token: CancellationToken): Promise<undefined | VSDataTransfer>;
export interface DocumentPasteEdit {
insertSnippet: string;
additionalEdit?: WorkspaceEdit;
}
provideDocumentPasteEdits(model: model.ITextModel, selection: Selection, dataTransfer: VSDataTransfer, token: CancellationToken): Promise<WorkspaceEdit | SnippetTextEdit | undefined>;
/**
* @internal
*/
export interface DocumentPasteEditProvider {
readonly pasteMimeTypes: readonly string[];
prepareDocumentPaste?(model: model.ITextModel, selections: readonly Selection[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<undefined | VSDataTransfer>;
provideDocumentPasteEdits(model: model.ITextModel, selections: readonly Selection[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined>;
}
/**

View file

@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DataTransfers } from 'vs/base/browser/dnd';
import { addDisposableListener } from 'vs/base/browser/dom';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
@ -15,7 +16,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { DocumentPasteEditProvider, SnippetTextEdit, WorkspaceEdit } from 'vs/editor/common/languages';
import { DocumentPasteEdit, DocumentPasteEditProvider } from 'vs/editor/common/languages';
import { ITextModel } from 'vs/editor/common/model';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
@ -26,15 +27,14 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
const vscodeClipboardMime = 'application/vnd.code.copyId';
const defaultPasteEditProvider = new class implements DocumentPasteEditProvider {
async provideDocumentPasteEdits(model: ITextModel, selection: Selection, dataTransfer: VSDataTransfer, _token: CancellationToken): Promise<WorkspaceEdit | undefined> {
pasteMimeTypes = [Mimes.text, 'text'];
async provideDocumentPasteEdits(model: ITextModel, selections: Selection[], dataTransfer: VSDataTransfer, _token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
const textDataTransfer = dataTransfer.get(Mimes.text) ?? dataTransfer.get('text');
if (textDataTransfer) {
const text = await textDataTransfer.asString();
return {
edits: [{
resource: model.uri,
edit: { range: selection, text },
}]
insertSnippet: text
};
}
@ -76,8 +76,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi
}
const model = editor.getModel();
const selection = this._editor.getSelection();
if (!model || !selection) {
const selections = this._editor.getSelections();
if (!model || !selections?.length) {
return;
}
@ -98,7 +98,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
const promise = createCancelablePromise(async token => {
const results = await Promise.all(providers.map(provider => {
return provider.prepareDocumentPaste!(model, selection, dataTransfer, token);
return provider.prepareDocumentPaste!(model, selections, dataTransfer, token);
}));
for (const result of results) {
@ -115,8 +115,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi
}));
this._register(addDisposableListener(container, 'paste', async (e: ClipboardEvent) => {
const selection = this._editor.getSelection();
if (!e.clipboardData || !selection || !editor.hasModel()) {
const selections = this._editor.getSelections();
if (!e.clipboardData || !selections?.length || !editor.hasModel()) {
return;
}
@ -125,21 +125,20 @@ export class CopyPasteController extends Disposable implements IEditorContributi
return;
}
const originalDocVersion = model.getVersionId();
const handle = e.clipboardData?.getData(vscodeClipboardMime);
if (typeof handle !== 'string') {
return;
}
const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model);
if (!providers.length) {
return;
}
const handle = e.clipboardData?.getData(vscodeClipboardMime);
if (typeof handle !== 'string') {
return;
}
e.preventDefault();
e.stopImmediatePropagation();
const originalDocVersion = model.getVersionId();
const tokenSource = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection);
try {
@ -163,16 +162,25 @@ export class CopyPasteController extends Disposable implements IEditorContributi
dataTransfer.delete(vscodeClipboardMime);
for (const provider of [...providers, defaultPasteEditProvider]) {
const edit = await provider.provideDocumentPasteEdits(model, selection, dataTransfer, tokenSource.token);
if (!provider.pasteMimeTypes.some(type => {
if (type.toLowerCase() === DataTransfers.FILES.toLowerCase()) {
return [...dataTransfer.values()].some(item => item.asFile());
}
return dataTransfer.has(type);
})) {
continue;
}
const edit = await provider.provideDocumentPasteEdits(model, selections, dataTransfer, tokenSource.token);
if (originalDocVersion !== model.getVersionId()) {
return;
}
if (edit) {
if ((edit as WorkspaceEdit).edits) {
await this._bulkEditService.apply(ResourceEdit.convert(edit as WorkspaceEdit), { editor });
} else {
performSnippetEdit(editor, edit as SnippetTextEdit);
performSnippetEdit(editor, edit.insertSnippet, selections);
if (edit.additionalEdit) {
await this._bulkEditService.apply(ResourceEdit.convert(edit.additionalEdit), { editor });
}
return;
}

View file

@ -9,6 +9,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CompletionItem, CompletionItemKind, CompletionItemProvider, SnippetTextEdit } from 'vs/editor/common/languages';
@ -335,13 +336,20 @@ registerEditorCommand(new CommandCtor({
// ---
export function performSnippetEdit(editor: ICodeEditor, edit: SnippetTextEdit) {
export function performSnippetEdit(editor: ICodeEditor, snippet: string, selections: ISelection[]): boolean;
export function performSnippetEdit(editor: ICodeEditor, edit: SnippetTextEdit): boolean;
export function performSnippetEdit(editor: ICodeEditor, editOrSnippet: string | SnippetTextEdit, selections?: ISelection[]): boolean {
const controller = SnippetController2.get(editor);
if (!controller) {
return false;
}
editor.focus();
editor.setSelection(edit.range);
controller.insert(edit.snippet);
if (typeof editOrSnippet === 'string') {
editor.setSelections(selections ?? []);
controller.insert(editOrSnippet);
} else {
editor.setSelection(editOrSnippet.range);
controller.insert(editOrSnippet.snippet);
}
return controller.isInSnippet();
}

View file

@ -30,7 +30,7 @@ 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, IWorkspaceEditDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape, reviveWorkspaceEditDto } from '../common/extHost.protocol';
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';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape {
@ -368,21 +368,22 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
// --- copy paste action provider
$registerPasteEditProvider(handle: number, selector: IDocumentFilterDto[], supportsCopy: boolean): void {
$registerPasteEditProvider(handle: number, selector: IDocumentFilterDto[], supportsCopy: boolean, pasteMimeTypes: readonly string[]): void {
const provider: languages.DocumentPasteEditProvider = {
pasteMimeTypes: pasteMimeTypes,
prepareDocumentPaste: supportsCopy
? async (model: ITextModel, selection: Selection, dataTransfer: VSDataTransfer, token: CancellationToken): Promise<VSDataTransfer | undefined> => {
? async (model: ITextModel, selections: Selection[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<VSDataTransfer | undefined> => {
const dataTransferDto = await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer);
if (token.isCancellationRequested) {
return undefined;
}
const result = await this._proxy.$prepareDocumentPaste(handle, model.uri, selection, dataTransferDto, token);
const result = await this._proxy.$prepareDocumentPaste(handle, model.uri, selections, dataTransferDto, token);
if (!result) {
return undefined;
}
const dataTransferOut = new VSDataTransfer();
result.items.forEach(([type, item]) => {
dataTransferOut.replace(type, createStringDataTransferItem(item.asString));
@ -391,16 +392,17 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
}
: undefined,
provideDocumentPasteEdits: async (model: ITextModel, selection: Selection, dataTransfer: VSDataTransfer, token: CancellationToken) => {
provideDocumentPasteEdits: async (model: ITextModel, selections: Selection[], dataTransfer: VSDataTransfer, token: CancellationToken) => {
const d = await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer);
const result = await this._proxy.$providePasteEdits(handle, model.uri, selection, d, token);
const result = await this._proxy.$providePasteEdits(handle, model.uri, selections, d, token);
if (!result) {
return;
} else if ((result as IWorkspaceEditDto).edits) {
return reviveWorkspaceEditDto(result as IWorkspaceEditDto);
} else {
return result as languages.SnippetTextEdit;
return undefined;
}
return {
insertSnippet: result.insertSnippet,
additionalEdit: result.additionalEdit ? reviveWorkspaceEditDto(result.additionalEdit) : undefined,
};
}
};

View file

@ -457,9 +457,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
return extHostLanguageFeatures.registerCodeActionProvider(extension, checkSelector(selector), provider, metadata);
},
registerDocumentPasteEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentPasteEditProvider): vscode.Disposable {
registerDocumentPasteEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentPasteEditProvider, metadata: vscode.DocumentPasteProviderMetadata): vscode.Disposable {
checkProposedApiEnabled(extension, 'documentPaste');
return extHostLanguageFeatures.registerDocumentPasteEditProvider(extension, checkSelector(selector), provider);
return extHostLanguageFeatures.registerDocumentPasteEditProvider(extension, checkSelector(selector), provider, metadata);
},
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);

View file

@ -372,7 +372,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string, supportsResolve: boolean): void;
$registerPasteEditProvider(handle: number, selector: IDocumentFilterDto[], supportsCopy: boolean): void;
$registerPasteEditProvider(handle: number, selector: IDocumentFilterDto[], supportsCopy: boolean, pasteMimeTypes: readonly string[]): void;
$registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
@ -1713,6 +1713,11 @@ export interface IInlineValueContextDto {
export type ITypeHierarchyItemDto = Dto<TypeHierarchyItem>;
export interface IPasteEditDto {
insertSnippet: string;
additionalEdit?: IWorkspaceEditDto;
}
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<languages.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined>;
@ -1731,8 +1736,8 @@ export interface ExtHostLanguageFeaturesShape {
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: languages.CodeActionContext, token: CancellationToken): Promise<ICodeActionListDto | undefined>;
$resolveCodeAction(handle: number, id: ChainedCacheId, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
$releaseCodeActions(handle: number, cacheId: number): void;
$prepareDocumentPaste(handle: number, uri: UriComponents, range: IRange, dataTransfer: DataTransferDTO, token: CancellationToken): Promise<DataTransferDTO | undefined>;
$providePasteEdits(handle: number, uri: UriComponents, range: IRange, dataTransfer: DataTransferDTO, token: CancellationToken): Promise<IWorkspaceEditDto | Dto<languages.SnippetTextEdit> | undefined>;
$prepareDocumentPaste(handle: number, uri: UriComponents, ranges: IRange[], dataTransfer: DataTransferDTO, token: CancellationToken): Promise<DataTransferDTO | undefined>;
$providePasteEdits(handle: number, uri: UriComponents, ranges: IRange[], dataTransfer: DataTransferDTO, token: CancellationToken): Promise<IPasteEditDto | undefined>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: languages.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: languages.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: languages.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;

View file

@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { mixin } from 'vs/base/common/objects';
import type * as vscode from 'vscode';
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKindNew, InlineCompletionTriggerKind, WorkspaceEdit } from 'vs/workbench/api/common/extHostTypes';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKindNew, InlineCompletionTriggerKind } from 'vs/workbench/api/common/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation';
import * as languages from 'vs/editor/common/languages';
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
@ -494,40 +494,39 @@ class DocumentPasteEditProvider {
private readonly _handle: number,
) { }
async prepareDocumentPaste(resource: URI, range: IRange, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.DataTransferDTO | undefined> {
async prepareDocumentPaste(resource: URI, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.DataTransferDTO | undefined> {
if (!this._provider.prepareDocumentPaste) {
return undefined;
}
const doc = this._documents.getDocument(resource);
const vscodeRange = typeConvert.Range.to(range);
const vscodeRanges = ranges.map(range => typeConvert.Range.to(range));
const dataTransfer = typeConvert.DataTransfer.toDataTransfer(dataTransferDto, () => {
throw new NotImplementedError();
});
await this._provider.prepareDocumentPaste(doc, vscodeRange, dataTransfer, token);
await this._provider.prepareDocumentPaste(doc, vscodeRanges, dataTransfer, token);
return typeConvert.DataTransfer.toDataTransferDTO(dataTransfer);
}
async providePasteEdits(requestId: number, resource: URI, range: IRange, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<undefined | extHostProtocol.IWorkspaceEditDto | Dto<languages.SnippetTextEdit>> {
async providePasteEdits(requestId: number, resource: URI, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<undefined | extHostProtocol.IPasteEditDto> {
const doc = this._documents.getDocument(resource);
const vscodeRange = typeConvert.Range.to(range);
const vscodeRanges = ranges.map(range => typeConvert.Range.to(range));
const dataTransfer = typeConvert.DataTransfer.toDataTransfer(dataTransferDto, async (index) => {
return (await this._proxy.$resolveDocumentOnDropFileData(this._handle, requestId, index)).buffer;
});
const edit = await this._provider.provideDocumentPasteEdits(doc, vscodeRange, dataTransfer, token);
const edit = await this._provider.provideDocumentPasteEdits(doc, vscodeRanges, dataTransfer, token);
if (!edit) {
return;
}
if (edit instanceof WorkspaceEdit) {
return typeConvert.WorkspaceEdit.from(edit);
} else {
return typeConvert.SnippetTextEdit.from(edit as vscode.SnippetTextEdit);
}
return {
insertSnippet: typeof edit.insertText === 'string' ? edit.insertText : edit.insertText.value,
additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit) : undefined,
};
}
}
@ -2461,19 +2460,19 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
// --- copy/paste actions
registerDocumentPasteEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentPasteEditProvider): vscode.Disposable {
registerDocumentPasteEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentPasteEditProvider, metadata: vscode.DocumentPasteProviderMetadata): vscode.Disposable {
const handle = this._nextHandle();
this._adapter.set(handle, new AdapterData(new DocumentPasteEditProvider(this._proxy, this._documents, provider, handle), extension));
this._proxy.$registerPasteEditProvider(handle, this._transformDocumentSelector(selector), !!provider.prepareDocumentPaste);
this._proxy.$registerPasteEditProvider(handle, this._transformDocumentSelector(selector), !!provider.prepareDocumentPaste, metadata.pasteMimeTypes);
return this._createDisposable(handle);
}
$prepareDocumentPaste(handle: number, resource: UriComponents, range: IRange, dataTransfer: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.DataTransferDTO | undefined> {
return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.prepareDocumentPaste(URI.revive(resource), range, dataTransfer, token), undefined, token);
$prepareDocumentPaste(handle: number, resource: UriComponents, ranges: IRange[], dataTransfer: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.DataTransferDTO | undefined> {
return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.prepareDocumentPaste(URI.revive(resource), ranges, dataTransfer, token), undefined, token);
}
$providePasteEdits(handle: number, resource: UriComponents, range: IRange, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.IWorkspaceEditDto | Dto<languages.SnippetTextEdit> | undefined> {
return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.providePasteEdits(0, URI.revive(resource), range, dataTransferDto, token), undefined, token);
$providePasteEdits(handle: number, resource: UriComponents, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.IPasteEditDto | undefined> {
return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.providePasteEdits(0, URI.revive(resource), ranges, dataTransferDto, token), undefined, token);
}
// --- configuration

View file

@ -19,11 +19,11 @@ declare module 'vscode' {
* a {@link DataTransfer} and is passed back to the provider in {@link provideDocumentPasteEdits}.
*
* @param document Document where the copy took place.
* @param range Range being copied in the `document`.
* @param ranges Ranges being copied in the `document`.
* @param dataTransfer The data transfer associated with the copy. You can store additional values on this for later use in {@link provideDocumentPasteEdits}.
* @param token A cancellation token.
*/
prepareDocumentPaste?(document: TextDocument, range: Range, dataTransfer: DataTransfer, token: CancellationToken): void | Thenable<void>;
prepareDocumentPaste?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): void | Thenable<void>;
/**
* Invoked before the user pastes into a document.
@ -31,16 +31,40 @@ declare module 'vscode' {
* In this method, extensions can return a workspace edit that replaces the standard pasting behavior.
*
* @param document Document being pasted into
* @param range Currently selected range in the document.
* @param ranges Currently selected ranges in the document.
* @param dataTransfer The data transfer associated with the paste.
* @param token A cancellation token.
*
* @return Optional workspace edit that applies the paste. Return undefined to use standard pasting.
*/
provideDocumentPasteEdits(document: TextDocument, range: Range, dataTransfer: DataTransfer, token: CancellationToken): ProviderResult<WorkspaceEdit | SnippetTextEdit>;
provideDocumentPasteEdits(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): ProviderResult<DocumentPasteEdit>;
}
/**
* An operation applied on paste
*/
interface DocumentPasteEdit {
/**
* The text or snippet to insert at the pasted locations.
*/
readonly insertText: string | SnippetString;
/**
* An optional additional edit to apply on paste.
*/
readonly additionalEdit?: WorkspaceEdit;
}
interface DocumentPasteProviderMetadata {
/**
* Mime types that `provideDocumentPasteEdits` should be invoked for.
*
* Use the special `files` mimetype to indicate the provider should be invoked if any files are present in the `DataTransfer`.
*/
readonly pasteMimeTypes: readonly string[];
}
namespace languages {
export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider): Disposable;
export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider, metadata: DocumentPasteProviderMetadata): Disposable;
}
}