Skip to content

Commit b22ede2

Browse files
committed
Update paste and drop proposals
Reworks the document paste and drop API proposals. Main highlights: - Align more with code action api - Allow a single paste provider to return multiple edits - Allow resolving applied edits lazily - Switch from using ids to scoped kinds like used for code actions
1 parent 2a57d41 commit b22ede2

20 files changed

+458
-255
lines changed

extensions/ipynb/src/notebookImagePaste.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ function getImageMimeType(uri: vscode.Uri): string | undefined {
4848

4949
class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscode.DocumentDropEditProvider {
5050

51-
public readonly id = 'insertAttachment';
51+
public static readonly kind = vscode.DocumentPasteEditKind.Empty.append('markdown', 'image', 'attachment');
5252

5353
async provideDocumentPasteEdits(
5454
document: vscode.TextDocument,
5555
_ranges: readonly vscode.Range[],
5656
dataTransfer: vscode.DataTransfer,
57+
_context: vscode.DocumentPasteContext,
5758
token: vscode.CancellationToken,
58-
): Promise<vscode.DocumentPasteEdit | undefined> {
59+
): Promise<vscode.DocumentPasteEdit[] | undefined> {
5960
const enabled = vscode.workspace.getConfiguration('ipynb', document).get('pasteImagesAsAttachments.enabled', true);
6061
if (!enabled) {
6162
return;
@@ -66,10 +67,10 @@ class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscod
6667
return;
6768
}
6869

69-
const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, vscode.l10n.t('Insert Image as Attachment'));
70+
const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, vscode.l10n.t('Insert Image as Attachment'), DropOrPasteEditProvider.kind);
7071
pasteEdit.yieldTo = [{ mimeType: MimeType.plain }];
7172
pasteEdit.additionalEdit = insert.additionalEdit;
72-
return pasteEdit;
73+
return [pasteEdit];
7374
}
7475

7576
async provideDocumentDropEdits(
@@ -86,7 +87,7 @@ class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscod
8687
const dropEdit = new vscode.DocumentDropEdit(insert.insertText);
8788
dropEdit.yieldTo = [{ mimeType: MimeType.plain }];
8889
dropEdit.additionalEdit = insert.additionalEdit;
89-
dropEdit.label = vscode.l10n.t('Insert Image as Attachment');
90+
dropEdit.title = vscode.l10n.t('Insert Image as Attachment');
9091
return dropEdit;
9192
}
9293

@@ -299,14 +300,14 @@ export function notebookImagePasteSetup(): vscode.Disposable {
299300
const provider = new DropOrPasteEditProvider();
300301
return vscode.Disposable.from(
301302
vscode.languages.registerDocumentPasteEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, provider, {
302-
id: provider.id,
303+
providedPasteEditKinds: [DropOrPasteEditProvider.kind],
303304
pasteMimeTypes: [
304305
MimeType.png,
305306
MimeType.uriList,
306307
],
307308
}),
308309
vscode.languages.registerDocumentDropEditProvider(JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR, provider, {
309-
id: provider.id,
310+
providedDropEditKinds: [DropOrPasteEditProvider.kind],
310311
dropMimeTypes: [
311312
...Object.values(imageExtToMime),
312313
MimeType.uriList,

extensions/markdown-language-features/src/languageFeatures/copyFiles/dropOrPasteResource.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { createInsertUriListEdit, createUriListSnippet, getSnippetLabel } from '
2222
*/
2323
class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, vscode.DocumentDropEditProvider {
2424

25-
public static readonly id = 'insertResource';
25+
public static readonly kind = vscode.DocumentPasteEditKind.Empty.append('markdown', 'link');
2626

2727
public static readonly mimeTypes = [
2828
Mime.textUriList,
@@ -32,7 +32,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v
3232

3333
private readonly _yieldTo = [
3434
{ mimeType: 'text/plain' },
35-
{ extensionId: 'vscode.ipynb', providerId: 'insertAttachment' },
35+
{ kind: vscode.DocumentPasteEditKind.Empty.append('markdown', 'image', 'attachment') },
3636
];
3737

3838
public async provideDocumentDropEdits(
@@ -63,22 +63,23 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v
6363
ranges: readonly vscode.Range[],
6464
dataTransfer: vscode.DataTransfer,
6565
token: vscode.CancellationToken,
66-
): Promise<vscode.DocumentPasteEdit | undefined> {
66+
): Promise<vscode.DocumentPasteEdit[] | undefined> {
6767
const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.filePaste.enabled', true);
6868
if (!enabled) {
6969
return;
7070
}
7171

7272
const createEdit = await this._getMediaFilesPasteEdit(document, dataTransfer, token);
7373
if (createEdit) {
74-
return createEdit;
74+
return [createEdit];
7575
}
7676

7777
if (token.isCancellationRequested) {
7878
return;
7979
}
8080

81-
return this._createEditFromUriListData(document, ranges, dataTransfer, token);
81+
const edit = await this._createEditFromUriListData(document, ranges, dataTransfer, token);
82+
return edit ? [edit] : undefined;
8283
}
8384

8485
private async _createEditFromUriListData(
@@ -97,7 +98,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v
9798
return;
9899
}
99100

100-
const uriEdit = new vscode.DocumentPasteEdit('', pasteEdit.label);
101+
const uriEdit = new vscode.DocumentPasteEdit('', pasteEdit.label, ResourcePasteOrDropProvider.kind);
101102
const edit = new vscode.WorkspaceEdit();
102103
edit.set(document.uri, pasteEdit.edits);
103104
uriEdit.additionalEdit = edit;
@@ -124,7 +125,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v
124125
return;
125126
}
126127

127-
const pasteEdit = new vscode.DocumentPasteEdit(edit.snippet, edit.label);
128+
const pasteEdit = new vscode.DocumentPasteEdit(edit.snippet, edit.label, ResourcePasteOrDropProvider.kind);
128129
pasteEdit.additionalEdit = edit.additionalEdits;
129130
pasteEdit.yieldTo = this._yieldTo;
130131
return pasteEdit;
@@ -150,7 +151,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v
150151
}
151152

152153
const dropEdit = new vscode.DocumentDropEdit(edit.snippet);
153-
dropEdit.label = edit.label;
154+
dropEdit.title = edit.label;
154155
dropEdit.additionalEdit = edit.additionalEdits;
155156
dropEdit.yieldTo = this._yieldTo;
156157
return dropEdit;
@@ -226,11 +227,11 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v
226227
export function registerResourceDropOrPasteSupport(selector: vscode.DocumentSelector): vscode.Disposable {
227228
return vscode.Disposable.from(
228229
vscode.languages.registerDocumentPasteEditProvider(selector, new ResourcePasteOrDropProvider(), {
229-
id: ResourcePasteOrDropProvider.id,
230+
providedPasteEditKinds: [ResourcePasteOrDropProvider.kind],
230231
pasteMimeTypes: ResourcePasteOrDropProvider.mimeTypes,
231232
}),
232233
vscode.languages.registerDocumentDropEditProvider(selector, new ResourcePasteOrDropProvider(), {
233-
id: ResourcePasteOrDropProvider.id,
234+
providedDropEditKinds: [ResourcePasteOrDropProvider.kind],
234235
dropMimeTypes: ResourcePasteOrDropProvider.mimeTypes,
235236
}),
236237
);

extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function getPasteUrlAsFormattedLinkSetting(document: vscode.TextDocument): Paste
2929
*/
3030
class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider {
3131

32-
public static readonly id = 'insertMarkdownLink';
32+
public static readonly kind = vscode.DocumentPasteEditKind.Empty.append('markdown', 'link');
3333

3434
public static readonly pasteMimeTypes = [Mime.textPlain];
3535

@@ -42,7 +42,7 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider {
4242
ranges: readonly vscode.Range[],
4343
dataTransfer: vscode.DataTransfer,
4444
token: vscode.CancellationToken,
45-
): Promise<vscode.DocumentPasteEdit | undefined> {
45+
): Promise<vscode.DocumentPasteEdit[] | undefined> {
4646
const pasteUrlSetting = getPasteUrlAsFormattedLinkSetting(document);
4747
if (pasteUrlSetting === PasteUrlAsMarkdownLink.Never) {
4848
return;
@@ -64,7 +64,7 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider {
6464
return;
6565
}
6666

67-
const pasteEdit = new vscode.DocumentPasteEdit('', edit.label);
67+
const pasteEdit = new vscode.DocumentPasteEdit('', edit.label, PasteUrlEditProvider.kind);
6868
const workspaceEdit = new vscode.WorkspaceEdit();
6969
workspaceEdit.set(document.uri, edit.edits);
7070
pasteEdit.additionalEdit = workspaceEdit;
@@ -73,13 +73,13 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider {
7373
pasteEdit.yieldTo = [{ mimeType: Mime.textPlain }];
7474
}
7575

76-
return pasteEdit;
76+
return [pasteEdit];
7777
}
7878
}
7979

8080
export function registerPasteUrlSupport(selector: vscode.DocumentSelector, parser: IMdParser) {
8181
return vscode.languages.registerDocumentPasteEditProvider(selector, new PasteUrlEditProvider(parser), {
82-
id: PasteUrlEditProvider.id,
82+
providedPasteEditKinds: [PasteUrlEditProvider.kind],
8383
pasteMimeTypes: PasteUrlEditProvider.pasteMimeTypes,
8484
});
8585
}

extensions/vscode-api-tests/src/singlefolder-tests/documentPaste.test.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ suite.skip('vscode API - Copy Paste', function () {
3737
dataTransfer.set(textPlain, new vscode.DataTransferItem(reversed));
3838
}
3939
}
40-
}, { id: 'test', copyMimeTypes: [textPlain] }));
40+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
4141

4242
await vscode.commands.executeCommand('editor.action.clipboardCopyAction');
4343
const newDocContent = getNextDocumentText(testDisposables, doc);
@@ -62,7 +62,7 @@ suite.skip('vscode API - Copy Paste', function () {
6262
dataTransfer.set(textPlain, new vscode.DataTransferItem(reversed + '\n'));
6363
}
6464
}
65-
}, { id: 'test', copyMimeTypes: [textPlain] }));
65+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
6666

6767
await vscode.commands.executeCommand('editor.action.clipboardCopyAction');
6868
const newDocContent = getNextDocumentText(testDisposables, doc);
@@ -88,7 +88,7 @@ suite.skip('vscode API - Copy Paste', function () {
8888
dataTransfer.set(textPlain, new vscode.DataTransferItem(`(${ranges.length})${selections.join(' ')}`));
8989
}
9090
}
91-
}, { id: 'test', copyMimeTypes: [textPlain] }));
91+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
9292

9393
await vscode.commands.executeCommand('editor.action.clipboardCopyAction');
9494
editor.selections = [new vscode.Selection(0, 0, 0, 0)];
@@ -118,7 +118,7 @@ suite.skip('vscode API - Copy Paste', function () {
118118
dataTransfer.set(textPlain, new vscode.DataTransferItem('a'));
119119
providerAResolve();
120120
}
121-
}, { id: 'test', copyMimeTypes: [textPlain] }));
121+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
122122

123123
// Later registered providers will be called first
124124
testDisposables.push(vscode.languages.registerDocumentPasteEditProvider({ language: 'plaintext' }, new class implements vscode.DocumentPasteEditProvider {
@@ -132,7 +132,7 @@ suite.skip('vscode API - Copy Paste', function () {
132132

133133
dataTransfer.set(textPlain, new vscode.DataTransferItem('b'));
134134
}
135-
}, { id: 'test', copyMimeTypes: [textPlain] }));
135+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
136136

137137
await vscode.commands.executeCommand('editor.action.clipboardCopyAction');
138138
const newDocContent = getNextDocumentText(testDisposables, doc);
@@ -159,7 +159,7 @@ suite.skip('vscode API - Copy Paste', function () {
159159
dataTransfer.set(textPlain, new vscode.DataTransferItem('xyz'));
160160
providerAResolve();
161161
}
162-
}, { id: 'test', copyMimeTypes: [textPlain] }));
162+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
163163

164164
testDisposables.push(vscode.languages.registerDocumentPasteEditProvider({ language: 'plaintext' }, new class implements vscode.DocumentPasteEditProvider {
165165
async prepareDocumentPaste(_document: vscode.TextDocument, _ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): Promise<void> {
@@ -172,7 +172,7 @@ suite.skip('vscode API - Copy Paste', function () {
172172
const str = await entry!.asString();
173173
dataTransfer.set(textPlain, new vscode.DataTransferItem(reverseString(str)));
174174
}
175-
}, { id: 'test', copyMimeTypes: [textPlain] }));
175+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
176176

177177
await vscode.commands.executeCommand('editor.action.clipboardCopyAction');
178178
const newDocContent = getNextDocumentText(testDisposables, doc);
@@ -192,13 +192,13 @@ suite.skip('vscode API - Copy Paste', function () {
192192
async prepareDocumentPaste(_document: vscode.TextDocument, _ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): Promise<void> {
193193
dataTransfer.set(textPlain, new vscode.DataTransferItem('xyz'));
194194
}
195-
}, { id: 'test', copyMimeTypes: [textPlain] }));
195+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
196196

197197
testDisposables.push(vscode.languages.registerDocumentPasteEditProvider({ language: 'plaintext' }, new class implements vscode.DocumentPasteEditProvider {
198198
async prepareDocumentPaste(_document: vscode.TextDocument, _ranges: readonly vscode.Range[], _dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): Promise<void> {
199199
throw new Error('Expected testing error from bad provider');
200200
}
201-
}, { id: 'test', copyMimeTypes: [textPlain] }));
201+
}, { providedPasteEditKinds: [vscode.DocumentPasteEditKind.Empty.append('test')], copyMimeTypes: [textPlain] }));
202202

203203
await vscode.commands.executeCommand('editor.action.clipboardCopyAction');
204204
const newDocContent = getNextDocumentText(testDisposables, doc);
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
export class HierarchicalKind {
7+
public static readonly sep = '.';
8+
9+
constructor(
10+
public readonly value: string
11+
) { }
12+
13+
public equals(other: HierarchicalKind): boolean {
14+
return this.value === other.value;
15+
}
16+
17+
public contains(other: HierarchicalKind): boolean {
18+
return this.equals(other) || this.value === '' || other.value.startsWith(this.value + HierarchicalKind.sep);
19+
}
20+
21+
public intersects(other: HierarchicalKind): boolean {
22+
return this.contains(other) || other.contains(this);
23+
}
24+
25+
public append(...parts: string[]): HierarchicalKind {
26+
return new HierarchicalKind((this.value ? [this.value, ...parts] : parts).join(HierarchicalKind.sep));
27+
}
28+
}

src/vs/editor/common/languages.ts

+21-10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { localize } from 'vs/nls';
2525
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
2626
import { IMarkerData } from 'vs/platform/markers/common/markers';
2727
import { LanguageFilter } from 'vs/editor/common/languageSelector';
28+
import { HierarchicalKind } from 'vs/base/common/hierarchicalKind';
2829

2930
/**
3031
* @internal
@@ -805,8 +806,8 @@ export interface CodeActionProvider {
805806
* @internal
806807
*/
807808
export interface DocumentPasteEdit {
808-
readonly label: string;
809-
readonly detail: string;
809+
readonly title: string;
810+
readonly kind: HierarchicalKind;
810811
readonly handledMimeType?: string;
811812
readonly yieldTo?: readonly DropYieldTo[];
812813
insertText: string | { readonly snippet: string };
@@ -817,23 +818,32 @@ export interface DocumentPasteEdit {
817818
* @internal
818819
*/
819820
export interface DocumentPasteContext {
820-
readonly only?: string;
821+
readonly only?: HierarchicalKind;
821822
readonly trigger: 'explicit' | 'implicit';
822823
}
823824

824825
/**
825826
* @internal
826827
*/
827-
export interface DocumentPasteEditProvider {
828-
829-
readonly id: string;
828+
export interface DocumentPasteEditsSession {
829+
edits: readonly DocumentPasteEdit[];
830+
dispose(): void;
831+
}
830832

833+
/**
834+
* @internal
835+
*/
836+
export interface DocumentPasteEditProvider {
837+
readonly id?: string;
831838
readonly copyMimeTypes?: readonly string[];
832839
readonly pasteMimeTypes?: readonly string[];
840+
readonly providedPasteEditKinds?: readonly HierarchicalKind[];
833841

834842
prepareDocumentPaste?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<undefined | IReadonlyVSDataTransfer>;
835843

836-
provideDocumentPasteEdits?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, context: DocumentPasteContext, token: CancellationToken): Promise<DocumentPasteEdit | undefined>;
844+
provideDocumentPasteEdits?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, context: DocumentPasteContext, token: CancellationToken): Promise<DocumentPasteEditsSession | undefined>;
845+
846+
resolveDocumentPasteEdit?(edit: DocumentPasteEdit, token: CancellationToken): Promise<DocumentPasteEdit>;
837847
}
838848

839849
/**
@@ -2098,13 +2108,14 @@ export enum ExternalUriOpenerPriority {
20982108
/**
20992109
* @internal
21002110
*/
2101-
export type DropYieldTo = { readonly providerId: string } | { readonly mimeType: string };
2111+
export type DropYieldTo = { readonly kind: string } | { readonly mimeType: string };
21022112

21032113
/**
21042114
* @internal
21052115
*/
21062116
export interface DocumentOnDropEdit {
2107-
readonly label: string;
2117+
readonly title: string;
2118+
readonly kind: HierarchicalKind | undefined;
21082119
readonly handledMimeType?: string;
21092120
readonly yieldTo?: readonly DropYieldTo[];
21102121
insertText: string | { readonly snippet: string };
@@ -2118,7 +2129,7 @@ export interface DocumentOnDropEditProvider {
21182129
readonly id?: string;
21192130
readonly dropMimeTypes?: readonly string[];
21202131

2121-
provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult<DocumentOnDropEdit>;
2132+
provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult<DocumentOnDropEdit[]>;
21222133
}
21232134

21242135
export interface DocumentContextItem {

src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteContribution.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { HierarchicalKind } from 'vs/base/common/hierarchicalKind';
67
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
78
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
89
import { EditorAction, EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
@@ -76,6 +77,6 @@ registerEditorAction(class extends EditorAction {
7677
}
7778

7879
public override run(_accessor: ServicesAccessor, editor: ICodeEditor, args: any) {
79-
return CopyPasteController.get(editor)?.pasteAs('text');
80+
return CopyPasteController.get(editor)?.pasteAs(new HierarchicalKind('text'));
8081
}
8182
});

0 commit comments

Comments
 (0)