Skip to content

Commit 3729f50

Browse files
authored
Support creating image in workspace when pasting image data into markdown (microsoft#160554)
For microsoft#157043
1 parent d2c241f commit 3729f50

File tree

2 files changed

+95
-20
lines changed

2 files changed

+95
-20
lines changed

extensions/markdown-language-features/src/languageFeatures/copyPaste.ts

Lines changed: 90 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,99 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import * as path from 'path';
67
import * as vscode from 'vscode';
7-
import { tryGetUriListSnippet } from './dropIntoEditor';
8-
9-
export function registerPasteSupport(selector: vscode.DocumentSelector) {
10-
return vscode.languages.registerDocumentPasteEditProvider(selector, new class implements vscode.DocumentPasteEditProvider {
11-
12-
async provideDocumentPasteEdits(
13-
document: vscode.TextDocument,
14-
_ranges: readonly vscode.Range[],
15-
dataTransfer: vscode.DataTransfer,
16-
token: vscode.CancellationToken,
17-
): Promise<vscode.DocumentPasteEdit | undefined> {
18-
const enabled = vscode.workspace.getConfiguration('markdown', document).get('experimental.editor.pasteLinks.enabled', true);
19-
if (!enabled) {
20-
return;
8+
import { Utils } from 'vscode-uri';
9+
import { createUriListSnippet, tryGetUriListSnippet } from './dropIntoEditor';
10+
11+
const supportedImageMimes = new Set([
12+
'image/png',
13+
'image/jpg',
14+
]);
15+
16+
class PasteEditProvider implements vscode.DocumentPasteEditProvider {
17+
18+
async provideDocumentPasteEdits(
19+
document: vscode.TextDocument,
20+
_ranges: readonly vscode.Range[],
21+
dataTransfer: vscode.DataTransfer,
22+
token: vscode.CancellationToken,
23+
): Promise<vscode.DocumentPasteEdit | undefined> {
24+
const enabled = vscode.workspace.getConfiguration('markdown', document).get('experimental.editor.pasteLinks.enabled', true);
25+
if (!enabled) {
26+
return;
27+
}
28+
29+
for (const imageMime of supportedImageMimes) {
30+
const file = dataTransfer.get(imageMime)?.asFile();
31+
if (file) {
32+
const edit = await this.makeCreateImagePasteEdit(document, file, token);
33+
if (token.isCancellationRequested) {
34+
return;
35+
}
36+
37+
if (edit) {
38+
return edit;
39+
}
2140
}
41+
}
42+
43+
const snippet = await tryGetUriListSnippet(document, dataTransfer, token);
44+
return snippet ? new vscode.DocumentPasteEdit(snippet) : undefined;
45+
}
46+
47+
private async makeCreateImagePasteEdit(document: vscode.TextDocument, file: vscode.DataTransferFile, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> {
48+
if (file.uri) {
49+
// If file is already in workspace, we don't want to create a copy of it
50+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(file.uri);
51+
if (workspaceFolder) {
52+
const snippet = createUriListSnippet(document, [file.uri]);
53+
return snippet ? new vscode.DocumentPasteEdit(snippet) : undefined;
54+
}
55+
}
2256

23-
const snippet = await tryGetUriListSnippet(document, dataTransfer, token);
24-
return snippet ? new vscode.DocumentPasteEdit(snippet) : undefined;
57+
const uri = await this.getNewFileName(document, file);
58+
if (token.isCancellationRequested) {
59+
return;
2560
}
26-
}, {
27-
pasteMimeTypes: ['text/uri-list']
61+
62+
const snippet = createUriListSnippet(document, [uri]);
63+
if (!snippet) {
64+
return;
65+
}
66+
67+
// Note that there is currently no way to undo the file creation :/
68+
const workspaceEdit = new vscode.WorkspaceEdit();
69+
workspaceEdit.createFile(uri, { contents: await file.data() });
70+
71+
const pasteEdit = new vscode.DocumentPasteEdit(snippet);
72+
pasteEdit.additionalEdit = workspaceEdit;
73+
return pasteEdit;
74+
}
75+
76+
private async getNewFileName(document: vscode.TextDocument, file: vscode.DataTransferFile): Promise<vscode.Uri> {
77+
const root = Utils.dirname(document.uri);
78+
79+
const ext = path.extname(file.name);
80+
const baseName = path.basename(file.name, ext);
81+
for (let i = 0; ; ++i) {
82+
const name = i === 0 ? baseName : `${baseName}-${i}`;
83+
const uri = vscode.Uri.joinPath(root, `${name}.${ext}`);
84+
try {
85+
await vscode.workspace.fs.stat(uri);
86+
} catch {
87+
// Does not exist
88+
return uri;
89+
}
90+
}
91+
}
92+
}
93+
94+
export function registerPasteSupport(selector: vscode.DocumentSelector,) {
95+
return vscode.languages.registerDocumentPasteEditProvider(selector, new PasteEditProvider(), {
96+
pasteMimeTypes: [
97+
'text/uri-list',
98+
...supportedImageMimes,
99+
]
28100
});
29101
}

extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,12 @@ export async function tryGetUriListSnippet(document: vscode.TextDocument, dataTr
5353
}
5454
}
5555

56+
return createUriListSnippet(document, uris);
57+
}
58+
59+
export function createUriListSnippet(document: vscode.TextDocument, uris: readonly vscode.Uri[]): vscode.SnippetString | undefined {
5660
if (!uris.length) {
57-
return;
61+
return undefined;
5862
}
5963

6064
const dir = getDocumentDir(document);
@@ -74,7 +78,6 @@ export async function tryGetUriListSnippet(document: vscode.TextDocument, dataTr
7478
snippet.appendText(' ');
7579
}
7680
});
77-
7881
return snippet;
7982
}
8083

0 commit comments

Comments
 (0)