Add paste as command (#181959)

For #30066

This command shows a quick pick that lets you select how to paste content
This commit is contained in:
Matt Bierner 2023-05-10 06:17:42 -07:00 committed by GitHub
parent 4182ec739b
commit ca51a4b04c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 223 additions and 33 deletions

View file

@ -45,6 +45,7 @@ function getImageMimeType(uri: vscode.Uri): string | undefined {
return imageExtToMime.get(extname(uri.fsPath).toLowerCase()); return imageExtToMime.get(extname(uri.fsPath).toLowerCase());
} }
const id = 'insertAttachment';
class CopyPasteEditProvider implements vscode.DocumentPasteEditProvider { class CopyPasteEditProvider implements vscode.DocumentPasteEditProvider {
async provideDocumentPasteEdits( async provideDocumentPasteEdits(
@ -63,7 +64,7 @@ class CopyPasteEditProvider implements vscode.DocumentPasteEditProvider {
return; return;
} }
const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, vscode.l10n.t('Insert Image as Attachment')); const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, id, vscode.l10n.t('Insert Image as Attachment'));
pasteEdit.additionalEdit = insert.additionalEdit; pasteEdit.additionalEdit = insert.additionalEdit;
return pasteEdit; return pasteEdit;
} }
@ -83,6 +84,7 @@ class DropEditProvider implements vscode.DocumentDropEditProvider {
} }
const dropEdit = new vscode.DocumentDropEdit(insert.insertText); const dropEdit = new vscode.DocumentDropEdit(insert.insertText);
dropEdit.id = id;
dropEdit.additionalEdit = insert.additionalEdit; dropEdit.additionalEdit = insert.additionalEdit;
dropEdit.label = vscode.l10n.t('Insert Image as Attachment'); dropEdit.label = vscode.l10n.t('Insert Image as Attachment');
return dropEdit; return dropEdit;
@ -302,7 +304,6 @@ export function notebookImagePasteSetup(): vscode.Disposable {
], ],
}), }),
vscode.languages.registerDocumentDropEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, new DropEditProvider(), { vscode.languages.registerDocumentDropEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, new DropEditProvider(), {
id: 'imageAttachment',
dropMimeTypes: [ dropMimeTypes: [
...Object.values(imageExtToMime), ...Object.values(imageExtToMime),
MimeType.uriList, MimeType.uriList,

View file

@ -19,6 +19,8 @@ const supportedImageMimes = new Set([
class PasteEditProvider implements vscode.DocumentPasteEditProvider { class PasteEditProvider implements vscode.DocumentPasteEditProvider {
private readonly _id = 'insertLink';
async provideDocumentPasteEdits( async provideDocumentPasteEdits(
document: vscode.TextDocument, document: vscode.TextDocument,
_ranges: readonly vscode.Range[], _ranges: readonly vscode.Range[],
@ -36,7 +38,7 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
} }
const snippet = await tryGetUriListSnippet(document, dataTransfer, token); const snippet = await tryGetUriListSnippet(document, dataTransfer, token);
return snippet ? new vscode.DocumentPasteEdit(snippet.snippet, snippet.label) : undefined; return snippet ? new vscode.DocumentPasteEdit(snippet.snippet, this._id, snippet.label) : undefined;
} }
private async _makeCreateImagePasteEdit(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> { private async _makeCreateImagePasteEdit(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> {
@ -87,7 +89,7 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
return; return;
} }
const pasteEdit = new vscode.DocumentPasteEdit(snippet.snippet, snippet.label); const pasteEdit = new vscode.DocumentPasteEdit(snippet.snippet, '', snippet.label);
pasteEdit.additionalEdit = workspaceEdit; pasteEdit.additionalEdit = workspaceEdit;
return pasteEdit; return pasteEdit;
} }

View file

@ -43,11 +43,11 @@ export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector)
} }
const edit = new vscode.DocumentDropEdit(snippet.snippet); const edit = new vscode.DocumentDropEdit(snippet.snippet);
edit.id = 'insertLink';
edit.label = snippet.label; edit.label = snippet.label;
return edit; return edit;
} }
}, { }, {
id: 'insertLink',
dropMimeTypes: [ dropMimeTypes: [
'text/uri-list' 'text/uri-list'
] ]

View file

@ -783,7 +783,9 @@ export interface CodeActionProvider {
* @internal * @internal
*/ */
export interface DocumentPasteEdit { export interface DocumentPasteEdit {
readonly id: string;
readonly label: string; readonly label: string;
readonly detail: string;
insertText: string | { readonly snippet: string }; insertText: string | { readonly snippet: string };
additionalEdit?: WorkspaceEdit; additionalEdit?: WorkspaceEdit;
} }
@ -1944,8 +1946,8 @@ export enum ExternalUriOpenerPriority {
* @internal * @internal
*/ */
export interface DocumentOnDropEdit { export interface DocumentOnDropEdit {
readonly id: string;
readonly label: string; readonly label: string;
insertText: string | { readonly snippet: string }; insertText: string | { readonly snippet: string };
additionalEdit?: WorkspaceEdit; additionalEdit?: WorkspaceEdit;
} }
@ -1954,7 +1956,6 @@ export interface DocumentOnDropEdit {
* @internal * @internal
*/ */
export interface DocumentOnDropEditProvider { export interface DocumentOnDropEditProvider {
readonly id: string;
readonly dropMimeTypes?: readonly string[]; readonly dropMimeTypes?: readonly string[];
provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): ProviderResult<DocumentOnDropEdit>; provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): ProviderResult<DocumentOnDropEdit>;

View file

@ -5,14 +5,17 @@
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { EditorAction, EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { registerEditorFeature } from 'vs/editor/common/editorFeatures'; import { registerEditorFeature } from 'vs/editor/common/editorFeatures';
import { CopyPasteController, changePasteTypeCommandId, pasteWidgetVisibleCtx } from 'vs/editor/contrib/dropOrPasteInto/browser/copyPasteController'; import { CopyPasteController, changePasteTypeCommandId, pasteWidgetVisibleCtx } from 'vs/editor/contrib/dropOrPasteInto/browser/copyPasteController';
import { DefaultPasteProvidersFeature } from 'vs/editor/contrib/dropOrPasteInto/browser/defaultProviders'; import { DefaultPasteProvidersFeature } from 'vs/editor/contrib/dropOrPasteInto/browser/defaultProviders';
import * as nls from 'vs/nls';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
registerEditorContribution(CopyPasteController.ID, CopyPasteController, EditorContributionInstantiation.Eager); // eager because it listens to events on the container dom node of the editor registerEditorContribution(CopyPasteController.ID, CopyPasteController, EditorContributionInstantiation.Eager); // eager because it listens to events on the container dom node of the editor
registerEditorFeature(DefaultPasteProvidersFeature);
registerEditorCommand(new class extends EditorCommand { registerEditorCommand(new class extends EditorCommand {
constructor() { constructor() {
super({ super({
@ -26,8 +29,37 @@ registerEditorCommand(new class extends EditorCommand {
} }
public override runEditorCommand(_accessor: ServicesAccessor | null, editor: ICodeEditor, _args: any) { public override runEditorCommand(_accessor: ServicesAccessor | null, editor: ICodeEditor, _args: any) {
CopyPasteController.get(editor)?.changePasteType(); return CopyPasteController.get(editor)?.changePasteType();
} }
}); });
registerEditorFeature(DefaultPasteProvidersFeature); registerEditorAction(class extends EditorAction {
constructor() {
super({
id: 'editor.action.pasteAs',
label: nls.localize('pasteAs', "Paste As..."),
alias: 'Paste As...',
precondition: undefined,
description: {
description: 'Paste as',
args: [{
name: 'args',
schema: {
type: 'object',
properties: {
'id': {
type: 'string',
description: nls.localize('pasteAs.id', "The id of the paste edit to try applying. If not provided, the editor will show a picker."),
}
},
}
}]
}
});
}
public override run(_accessor: ServicesAccessor, editor: ICodeEditor, args: any) {
const id = typeof args?.id === 'string' ? args.id : undefined;
return CopyPasteController.get(editor)?.pasteAs(id);
}
});

View file

@ -14,6 +14,7 @@ import * as platform from 'vs/base/common/platform';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import { toExternalVSDataTransfer, toVSDataTransfer } from 'vs/editor/browser/dnd'; import { toExternalVSDataTransfer, toVSDataTransfer } from 'vs/editor/browser/dnd';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IRange, Range } from 'vs/editor/common/core/range'; import { IRange, Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection'; import { Selection } from 'vs/editor/common/core/selection';
@ -21,12 +22,15 @@ import { Handler, IEditorContribution, PastePayload } from 'vs/editor/common/edi
import { DocumentPasteEdit, DocumentPasteEditProvider } from 'vs/editor/common/languages'; import { DocumentPasteEdit, DocumentPasteEditProvider } from 'vs/editor/common/languages';
import { ITextModel } from 'vs/editor/common/model'; import { ITextModel } from 'vs/editor/common/model';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { createCombinedWorkspaceEdit } from 'vs/editor/contrib/dropOrPasteInto/browser/edit';
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
import { InlineProgressManager } from 'vs/editor/contrib/inlineProgress/browser/inlineProgress'; import { InlineProgressManager } from 'vs/editor/contrib/inlineProgress/browser/inlineProgress';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { PostEditWidgetManager } from './postEditWidget'; import { PostEditWidgetManager } from './postEditWidget';
export const changePasteTypeCommandId = 'editor.changePasteType'; export const changePasteTypeCommandId = 'editor.changePasteType';
@ -58,6 +62,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
}; };
private _currentPasteOperation?: CancelablePromise<void>; private _currentPasteOperation?: CancelablePromise<void>;
private _pasteAsActionContext?: { readonly preferredId: string | undefined };
private readonly _pasteProgressManager: InlineProgressManager; private readonly _pasteProgressManager: InlineProgressManager;
private readonly _postPasteWidgetManager: PostEditWidgetManager; private readonly _postPasteWidgetManager: PostEditWidgetManager;
@ -65,8 +70,11 @@ export class CopyPasteController extends Disposable implements IEditorContributi
constructor( constructor(
editor: ICodeEditor, editor: ICodeEditor,
@IInstantiationService instantiationService: IInstantiationService, @IInstantiationService instantiationService: IInstantiationService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@IClipboardService private readonly _clipboardService: IClipboardService, @IClipboardService private readonly _clipboardService: IClipboardService,
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@IQuickInputService private readonly _quickInputService: IQuickInputService,
@IProgressService private readonly _progressService: IProgressService,
) { ) {
super(); super();
@ -86,6 +94,16 @@ export class CopyPasteController extends Disposable implements IEditorContributi
this._postPasteWidgetManager.tryShowSelector(); this._postPasteWidgetManager.tryShowSelector();
} }
public pasteAs(preferredId?: string) {
this._editor.focus();
try {
this._pasteAsActionContext = { preferredId };
document.execCommand('paste');
} finally {
this._pasteAsActionContext = undefined;
}
}
public clearWidgets() { public clearWidgets() {
this._postPasteWidgetManager.clear(); this._postPasteWidgetManager.clear();
} }
@ -208,11 +226,20 @@ export class CopyPasteController extends Disposable implements IEditorContributi
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
if (this._pasteAsActionContext) {
this.showPasteAsPick(this._pasteAsActionContext.preferredId, allProviders, selections, dataTransfer, metadata);
} else {
this.doPasteInline(allProviders, selections, dataTransfer, metadata);
}
}
private doPasteInline(allProviders: readonly DocumentPasteEditProvider[], selections: readonly Selection[], dataTransfer: VSDataTransfer, metadata: CopyMetadata | undefined): void {
const p = createCancelablePromise(async (token) => { const p = createCancelablePromise(async (token) => {
const editor = this._editor; const editor = this._editor;
if (!editor.hasModel()) { if (!editor.hasModel()) {
return; return;
} }
const model = editor.getModel();
const tokenSource = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection, undefined, token); const tokenSource = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection, undefined, token);
try { try {
@ -253,6 +280,71 @@ export class CopyPasteController extends Disposable implements IEditorContributi
this._currentPasteOperation = p; this._currentPasteOperation = p;
} }
private showPasteAsPick(preferredId: string | undefined, allProviders: readonly DocumentPasteEditProvider[], selections: readonly Selection[], dataTransfer: VSDataTransfer, metadata: CopyMetadata | undefined): void {
const p = createCancelablePromise(async (token) => {
const editor = this._editor;
if (!editor.hasModel()) {
return;
}
const model = editor.getModel();
const tokenSource = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection, undefined, token);
try {
await this.mergeInDataFromCopy(dataTransfer, metadata, tokenSource.token);
if (tokenSource.token.isCancellationRequested) {
return;
}
// Filter out any providers the don't match the full data transfer we will send them.
const supportedProviders = allProviders.filter(provider => isSupportedProvider(provider, dataTransfer));
const providerEdits = await this.getPasteEdits(supportedProviders, dataTransfer, model, selections, tokenSource.token);
if (tokenSource.token.isCancellationRequested) {
return;
}
if (!providerEdits.length) {
return;
}
let pickedEdit: DocumentPasteEdit | undefined;
if (typeof preferredId === 'string') {
// We are looking for a specific edit
pickedEdit = providerEdits.find(edit => edit.id === preferredId);
} else {
const selected = await this._quickInputService.pick(
providerEdits.map((edit): IQuickPickItem & { edit: DocumentPasteEdit } => ({
label: edit.label,
description: edit.id,
detail: edit.detail,
edit,
})), {
placeHolder: localize('pasteAsPickerPlaceholder', "Select Paste Action"),
});
pickedEdit = selected?.edit;
}
if (!pickedEdit) {
return;
}
const combinedWorkspaceEdit = createCombinedWorkspaceEdit(model.uri, selections, pickedEdit);
await this._bulkEditService.apply(combinedWorkspaceEdit, { editor: this._editor });
} finally {
tokenSource.dispose();
if (this._currentPasteOperation === p) {
this._currentPasteOperation = undefined;
}
}
});
this._progressService.withProgress({
location: ProgressLocation.Window,
title: localize('pasteAsProgress', "Running paste handlers"),
}, () => p);
}
private setCopyMetadata(dataTransfer: DataTransfer, metadata: CopyMetadata) { private setCopyMetadata(dataTransfer: DataTransfer, metadata: CopyMetadata) {
dataTransfer.setData(vscodeClipboardMime, JSON.stringify(metadata)); dataTransfer.setData(vscodeClipboardMime, JSON.stringify(metadata));
} }
@ -293,7 +385,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
} }
} }
private async getPasteEdits(providers: readonly DocumentPasteEditProvider[], dataTransfer: VSDataTransfer, model: ITextModel, selections: Selection[], token: CancellationToken): Promise<DocumentPasteEdit[]> { private async getPasteEdits(providers: readonly DocumentPasteEditProvider[], dataTransfer: VSDataTransfer, model: ITextModel, selections: readonly Selection[], token: CancellationToken): Promise<DocumentPasteEdit[]> {
const result = await raceCancellation( const result = await raceCancellation(
Promise.all( Promise.all(
providers.map(provider => provider.provideDocumentPasteEdits(model, selections, dataTransfer, token)) providers.map(provider => provider.provideDocumentPasteEdits(model, selections, dataTransfer, token))

View file

@ -19,6 +19,8 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
const builtInLabel = localize('builtIn', 'Built-in');
abstract class SimplePasteAndDropProvider implements DocumentOnDropEditProvider, DocumentPasteEditProvider { abstract class SimplePasteAndDropProvider implements DocumentOnDropEditProvider, DocumentPasteEditProvider {
abstract readonly id: string; abstract readonly id: string;
@ -27,12 +29,12 @@ abstract class SimplePasteAndDropProvider implements DocumentOnDropEditProvider,
async provideDocumentPasteEdits(_model: ITextModel, _ranges: readonly IRange[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined> { async provideDocumentPasteEdits(_model: ITextModel, _ranges: readonly IRange[], dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
const edit = await this.getEdit(dataTransfer, token); const edit = await this.getEdit(dataTransfer, token);
return edit ? { insertText: edit.insertText, label: edit.label } : undefined; return edit ? { id: this.id, insertText: edit.insertText, label: edit.label, detail: edit.detail } : undefined;
} }
async provideDocumentOnDropEdits(_model: ITextModel, _position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentOnDropEdit | undefined> { async provideDocumentOnDropEdits(_model: ITextModel, _position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentOnDropEdit | undefined> {
const edit = await this.getEdit(dataTransfer, token); const edit = await this.getEdit(dataTransfer, token);
return edit ? { insertText: edit.insertText, label: edit.label } : undefined; return edit ? { id: this.id, insertText: edit.insertText, label: edit.label } : undefined;
} }
protected abstract getEdit(dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined>; protected abstract getEdit(dataTransfer: VSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined>;
@ -58,7 +60,9 @@ class DefaultTextProvider extends SimplePasteAndDropProvider {
const insertText = await textEntry.asString(); const insertText = await textEntry.asString();
return { return {
id: this.id,
label: localize('text.label', "Insert Plain Text"), label: localize('text.label', "Insert Plain Text"),
detail: builtInLabel,
insertText insertText
}; };
} }
@ -101,7 +105,12 @@ class PathProvider extends SimplePasteAndDropProvider {
: localize('defaultDropProvider.uriList.path', "Insert Path"); : localize('defaultDropProvider.uriList.path', "Insert Path");
} }
return { insertText, label }; return {
id: this.id,
insertText,
label,
detail: builtInLabel,
};
} }
} }
@ -133,10 +142,12 @@ class RelativePathProvider extends SimplePasteAndDropProvider {
} }
return { return {
id: this.id,
insertText: relativeUris.join(' '), insertText: relativeUris.join(' '),
label: entries.length > 1 label: entries.length > 1
? localize('defaultDropProvider.uriList.relativePaths', "Insert Relative Paths") ? localize('defaultDropProvider.uriList.relativePaths', "Insert Relative Paths")
: localize('defaultDropProvider.uriList.relativePath', "Insert Relative Path") : localize('defaultDropProvider.uriList.relativePath', "Insert Relative Path"),
detail: builtInLabel,
}; };
} }
} }

View file

@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
import { WorkspaceEdit } from 'vs/editor/common/languages';
import { Range } from 'vs/editor/common/core/range';
export interface DropOrPasteEdit {
readonly label: string;
readonly insertText: string | { readonly snippet: string };
readonly additionalEdit?: WorkspaceEdit;
}
export function createCombinedWorkspaceEdit(uri: URI, ranges: readonly Range[], edit: DropOrPasteEdit): WorkspaceEdit {
return {
edits: [
...ranges.map(range =>
new ResourceTextEdit(uri,
typeof edit.insertText === 'string'
? { range, text: edit.insertText, insertAsSnippet: false }
: { range, text: edit.insertText.snippet, insertAsSnippet: true }
)),
...(edit.additionalEdit?.edits ?? [])
]
};
}

View file

@ -32,7 +32,7 @@ import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'
import * as search from 'vs/workbench/contrib/search/common/search'; import * as search from 'vs/workbench/contrib/search/common/search';
import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; 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, IPasteEditProviderMetadataDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape } from '../common/extHost.protocol'; import { ExtHostContext, ExtHostLanguageFeaturesShape, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentDropEditProviderMetadata, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IPasteEditProviderMetadataDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape } from '../common/extHost.protocol';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape { export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape {
@ -907,8 +907,8 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
private readonly _documentOnDropEditProviders = new Map<number, MainThreadDocumentOnDropEditProvider>(); private readonly _documentOnDropEditProviders = new Map<number, MainThreadDocumentOnDropEditProvider>();
$registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, metadata: { id: string; dropMimeTypes: string[] }): void { $registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[], metadata: IDocumentDropEditProviderMetadata): void {
const provider = new MainThreadDocumentOnDropEditProvider(handle, extensionId, metadata, this._proxy, this._uriIdentService); const provider = new MainThreadDocumentOnDropEditProvider(handle, metadata, this._proxy, this._uriIdentService);
this._documentOnDropEditProviders.set(handle, provider); this._documentOnDropEditProviders.set(handle, provider);
this._registrations.set(handle, combinedDisposable( this._registrations.set(handle, combinedDisposable(
this._languageFeaturesService.documentOnDropEditProvider.register(selector, provider), this._languageFeaturesService.documentOnDropEditProvider.register(selector, provider),
@ -974,7 +974,9 @@ class MainThreadPasteEditProvider implements languages.DocumentPasteEditProvider
} }
return { return {
id: result.id,
label: result.label, label: result.label,
detail: result.detail,
insertText: result.insertText, insertText: result.insertText,
additionalEdit: result.additionalEdit ? reviveWorkspaceEditDto(result.additionalEdit, this._uriIdentService, dataId => this.resolveFileData(request.id, dataId)) : undefined, additionalEdit: result.additionalEdit ? reviveWorkspaceEditDto(result.additionalEdit, this._uriIdentService, dataId => this.resolveFileData(request.id, dataId)) : undefined,
}; };
@ -992,17 +994,14 @@ class MainThreadDocumentOnDropEditProvider implements languages.DocumentOnDropEd
private readonly dataTransfers = new DataTransferCache(); private readonly dataTransfers = new DataTransferCache();
readonly id: string;
readonly dropMimeTypes?: readonly string[]; readonly dropMimeTypes?: readonly string[];
constructor( constructor(
private readonly handle: number, private readonly handle: number,
extensionId: ExtensionIdentifier, metadata: IDocumentDropEditProviderMetadata | undefined,
metadata: { id: string; dropMimeTypes: readonly string[] } | undefined,
private readonly _proxy: ExtHostLanguageFeaturesShape, private readonly _proxy: ExtHostLanguageFeaturesShape,
@IUriIdentityService private readonly _uriIdentService: IUriIdentityService @IUriIdentityService private readonly _uriIdentService: IUriIdentityService
) { ) {
this.id = extensionId.value + (metadata ? '.' + metadata.id : '');
this.dropMimeTypes = metadata?.dropMimeTypes ?? ['*/*']; this.dropMimeTypes = metadata?.dropMimeTypes ?? ['*/*'];
} }
@ -1015,6 +1014,7 @@ class MainThreadDocumentOnDropEditProvider implements languages.DocumentOnDropEd
return undefined; return undefined;
} }
return { return {
id: edit.id,
label: edit.label, label: edit.label,
insertText: edit.insertText, insertText: edit.insertText,
additionalEdit: reviveWorkspaceEditDto(edit.additionalEdit, this._uriIdentService, dataId => this.resolveDocumentOnDropFileData(request.id, dataId)), additionalEdit: reviveWorkspaceEditDto(edit.additionalEdit, this._uriIdentService, dataId => this.resolveDocumentOnDropFileData(request.id, dataId)),

View file

@ -413,7 +413,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerTypeHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerTypeHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, metadata?: { id: string; dropMimeTypes: readonly string[] }): void; $registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[], metadata?: IDocumentDropEditProviderMetadata): void;
$resolvePasteFileData(handle: number, requestId: number, dataId: string): Promise<VSBuffer>; $resolvePasteFileData(handle: number, requestId: number, dataId: string): Promise<VSBuffer>;
$resolveDocumentOnDropFileData(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; $setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void;
@ -1835,12 +1835,19 @@ export interface IPasteEditProviderMetadataDto {
} }
export interface IPasteEditDto { export interface IPasteEditDto {
id: string;
label: string; label: string;
detail: string;
insertText: string | { snippet: string }; insertText: string | { snippet: string };
additionalEdit?: IWorkspaceEditDto; additionalEdit?: IWorkspaceEditDto;
} }
export interface IDocumentDropEditProviderMetadata {
dropMimeTypes: readonly string[];
}
export interface IDocumentOnDropEditDto { export interface IDocumentOnDropEditDto {
id: string;
label: string; label: string;
insertText: string | { snippet: string }; insertText: string | { snippet: string };
additionalEdit?: IWorkspaceEditDto; additionalEdit?: IWorkspaceEditDto;

View file

@ -538,7 +538,9 @@ class DocumentPasteEditProvider {
} }
return { return {
id: edit.id ? this._extension.identifier.value + '.' + edit.id : this._extension.identifier.value,
label: edit.label ?? localize('defaultPasteLabel', "Paste using '{0}' extension", this._extension.displayName || this._extension.name), label: edit.label ?? localize('defaultPasteLabel', "Paste using '{0}' extension", this._extension.displayName || this._extension.name),
detail: this._extension.displayName || this._extension.name,
insertText: typeof edit.insertText === 'string' ? edit.insertText : { snippet: edit.insertText.value }, insertText: typeof edit.insertText === 'string' ? edit.insertText : { snippet: edit.insertText.value },
additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit, undefined) : undefined, additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit, undefined) : undefined,
}; };
@ -1750,6 +1752,7 @@ class DocumentOnDropEditAdapter {
return undefined; return undefined;
} }
return { return {
id: edit.id ? this._extension.identifier.value + '.' + edit.id : this._extension.identifier.value,
label: edit.label ?? localize('defaultDropLabel', "Drop using '{0}' extension", this._extension.displayName || this._extension.name), label: edit.label ?? localize('defaultDropLabel', "Drop using '{0}' extension", this._extension.displayName || this._extension.name),
insertText: typeof edit.insertText === 'string' ? edit.insertText : { snippet: edit.insertText.value }, insertText: typeof edit.insertText === 'string' ? edit.insertText : { snippet: edit.insertText.value },
additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit, undefined) : undefined, additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit, undefined) : undefined,
@ -2393,7 +2396,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
const handle = this._nextHandle(); const handle = this._nextHandle();
this._adapter.set(handle, new AdapterData(new DocumentOnDropEditAdapter(this._proxy, this._documents, provider, handle, extension), extension)); this._adapter.set(handle, new AdapterData(new DocumentOnDropEditAdapter(this._proxy, this._documents, provider, handle, extension), extension));
this._proxy.$registerDocumentOnDropEditProvider(handle, this._transformDocumentSelector(selector, extension), extension.identifier, isProposedApiEnabled(extension, 'dropMetadata') ? metadata : undefined); this._proxy.$registerDocumentOnDropEditProvider(handle, this._transformDocumentSelector(selector, extension), isProposedApiEnabled(extension, 'dropMetadata') ? metadata : undefined);
return this._createDisposable(handle); return this._createDisposable(handle);
} }

View file

@ -2675,6 +2675,8 @@ export class DataTransfer implements vscode.DataTransfer {
@es5ClassCompat @es5ClassCompat
export class DocumentDropEdit { export class DocumentDropEdit {
id: string | undefined;
insertText: string | SnippetString; insertText: string | SnippetString;
additionalEdit?: WorkspaceEdit; additionalEdit?: WorkspaceEdit;
@ -2686,13 +2688,16 @@ export class DocumentDropEdit {
@es5ClassCompat @es5ClassCompat
export class DocumentPasteEdit { export class DocumentPasteEdit {
id: string;
label: string; label: string;
insertText: string | SnippetString; insertText: string | SnippetString;
additionalEdit?: WorkspaceEdit; additionalEdit?: WorkspaceEdit;
constructor(insertText: string | SnippetString, label: string) { constructor(insertText: string | SnippetString, id: string, label: string) {
this.id = id;
this.label = label; this.label = label;
this.insertText = insertText; this.insertText = insertText;
} }

View file

@ -44,6 +44,13 @@ declare module 'vscode' {
* An operation applied on paste * An operation applied on paste
*/ */
class DocumentPasteEdit { class DocumentPasteEdit {
/**
* Identifies the type of edit.
*
* This id should be unique within the extension but does not need to be unique across extensions.
*/
id: string;
/** /**
* Human readable label that describes the edit. * Human readable label that describes the edit.
*/ */
@ -64,7 +71,7 @@ declare module 'vscode' {
* *
* TODO: Reverse args, but this will break existing consumers :( * TODO: Reverse args, but this will break existing consumers :(
*/ */
constructor(insertText: string | SnippetString, label: string); constructor(insertText: string | SnippetString, id: string, label: string);
} }
interface DocumentPasteProviderMetadata { interface DocumentPasteProviderMetadata {

View file

@ -8,6 +8,13 @@ declare module 'vscode' {
// https://github.com/microsoft/vscode/issues/179430 // https://github.com/microsoft/vscode/issues/179430
export interface DocumentDropEdit { export interface DocumentDropEdit {
/**
* Identifies the type of edit.
*
* This id should be unique within the extension but does not need to be unique across extensions.
*/
id: string;
/** /**
* Human readable label that describes the edit. * Human readable label that describes the edit.
*/ */
@ -15,13 +22,6 @@ declare module 'vscode' {
} }
export interface DocumentDropEditProviderMetadata { 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. * List of data transfer types that the provider supports.
* *