mirror of
https://github.com/Microsoft/vscode
synced 2024-10-02 09:18:59 +00:00
Allow creating multiple files or attachments on paste (#181975)
This commit is contained in:
parent
bbc33498b3
commit
83c12a2da2
|
@ -18,6 +18,16 @@ enum MimeType {
|
|||
uriList = 'text/uri-list',
|
||||
}
|
||||
|
||||
const imageMimeTypes: ReadonlySet<string> = new Set<string>([
|
||||
MimeType.bmp,
|
||||
MimeType.gif,
|
||||
MimeType.ico,
|
||||
MimeType.jpeg,
|
||||
MimeType.png,
|
||||
MimeType.tiff,
|
||||
MimeType.webp,
|
||||
]);
|
||||
|
||||
const imageExtToMime: ReadonlyMap<string, string> = new Map<string, string>([
|
||||
['.bmp', MimeType.bmp],
|
||||
['.gif', MimeType.gif],
|
||||
|
@ -126,16 +136,21 @@ async function getDroppedImageData(
|
|||
): Promise<readonly ImageAttachmentData[]> {
|
||||
|
||||
// Prefer using image data in the clipboard
|
||||
// TODO: dataTransfer.get() limits to one image pasted. Should we support multiple?
|
||||
const pngDataItem = dataTransfer.get(MimeType.png);
|
||||
if (pngDataItem) {
|
||||
const fileItem = pngDataItem.asFile();
|
||||
if (!fileItem) {
|
||||
return [];
|
||||
const files = coalesce(await Promise.all(Array.from(dataTransfer, async ([mimeType, item]): Promise<ImageAttachmentData | undefined> => {
|
||||
if (!imageMimeTypes.has(mimeType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await fileItem.data();
|
||||
return [{ fileName: fileItem.name, mimeType: MimeType.png, data }];
|
||||
const file = item.asFile();
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await file.data();
|
||||
return { fileName: file.name, mimeType, data };
|
||||
})));
|
||||
if (files.length) {
|
||||
return files;
|
||||
}
|
||||
|
||||
// Then fallback to image files in the uri-list
|
||||
|
|
|
@ -8,23 +8,42 @@ import * as vscode from 'vscode';
|
|||
import { Utils } from 'vscode-uri';
|
||||
import { getParentDocumentUri } from './dropIntoEditor';
|
||||
|
||||
export class NewFilePathGenerator {
|
||||
|
||||
export async function getNewFileName(document: vscode.TextDocument, file: vscode.DataTransferFile): Promise<vscode.Uri> {
|
||||
const desiredPath = getDesiredNewFilePath(document, file);
|
||||
private readonly _usedPaths = new Set<string>();
|
||||
|
||||
const root = Utils.dirname(desiredPath);
|
||||
const ext = path.extname(file.name);
|
||||
const baseName = path.basename(file.name, ext);
|
||||
for (let i = 0; ; ++i) {
|
||||
const name = i === 0 ? baseName : `${baseName}-${i}`;
|
||||
const uri = vscode.Uri.joinPath(root, `${name}${ext}`);
|
||||
try {
|
||||
await vscode.workspace.fs.stat(uri);
|
||||
} catch {
|
||||
// Does not exist
|
||||
return uri;
|
||||
async getNewFilePath(document: vscode.TextDocument, file: vscode.DataTransferFile, token: vscode.CancellationToken): Promise<vscode.Uri | undefined> {
|
||||
const desiredPath = getDesiredNewFilePath(document, file);
|
||||
|
||||
const root = Utils.dirname(desiredPath);
|
||||
const ext = path.extname(file.name);
|
||||
const baseName = path.basename(file.name, ext);
|
||||
for (let i = 0; ; ++i) {
|
||||
if (token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const name = i === 0 ? baseName : `${baseName}-${i}`;
|
||||
const uri = vscode.Uri.joinPath(root, name + ext);
|
||||
if (this._wasPathAlreadyUsed(uri)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await vscode.workspace.fs.stat(uri);
|
||||
} catch {
|
||||
if (!this._wasPathAlreadyUsed(uri)) {
|
||||
// Does not exist
|
||||
this._usedPaths.add(uri.toString());
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _wasPathAlreadyUsed(uri: vscode.Uri) {
|
||||
return this._usedPaths.has(uri.toString());
|
||||
}
|
||||
}
|
||||
|
||||
function getDesiredNewFilePath(document: vscode.TextDocument, file: vscode.DataTransferFile): vscode.Uri {
|
||||
|
|
|
@ -4,13 +4,17 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { coalesce } from '../../util/arrays';
|
||||
import { Schemes } from '../../util/schemes';
|
||||
import { getNewFileName } from './copyFiles';
|
||||
import { NewFilePathGenerator } from './copyFiles';
|
||||
import { createUriListSnippet, tryGetUriListSnippet } from './dropIntoEditor';
|
||||
|
||||
const supportedImageMimes = new Set([
|
||||
'image/bmp',
|
||||
'image/gif',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/jpg',
|
||||
'image/webp',
|
||||
]);
|
||||
|
||||
class PasteEditProvider implements vscode.DocumentPasteEditProvider {
|
||||
|
@ -26,53 +30,63 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
for (const imageMime of supportedImageMimes) {
|
||||
const item = dataTransfer.get(imageMime);
|
||||
const file = item?.asFile();
|
||||
if (item && file) {
|
||||
const edit = await this._makeCreateImagePasteEdit(document, file, token);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (edit) {
|
||||
return edit;
|
||||
}
|
||||
}
|
||||
const edit = await this._makeCreateImagePasteEdit(document, dataTransfer, token);
|
||||
if (edit) {
|
||||
return edit;
|
||||
}
|
||||
|
||||
const snippet = await tryGetUriListSnippet(document, dataTransfer, token);
|
||||
return snippet ? new vscode.DocumentPasteEdit(snippet.snippet, snippet.label) : undefined;
|
||||
}
|
||||
|
||||
private async _makeCreateImagePasteEdit(document: vscode.TextDocument, file: vscode.DataTransferFile, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> {
|
||||
private async _makeCreateImagePasteEdit(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> {
|
||||
if (document.uri.scheme === Schemes.untitled) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.uri) {
|
||||
// If file is already in workspace, we don't want to create a copy of it
|
||||
const workspaceFolder = vscode.workspace.getWorkspaceFolder(file.uri);
|
||||
if (workspaceFolder) {
|
||||
const snippet = createUriListSnippet(document, [file.uri]);
|
||||
return snippet ? new vscode.DocumentPasteEdit(snippet.snippet, snippet.label) : undefined;
|
||||
interface FileEntry {
|
||||
readonly uri: vscode.Uri;
|
||||
readonly newFileContents?: vscode.DataTransferFile;
|
||||
}
|
||||
|
||||
const pathGenerator = new NewFilePathGenerator();
|
||||
const fileEntries = coalesce(await Promise.all(Array.from(dataTransfer, async ([mime, item]): Promise<FileEntry | undefined> => {
|
||||
if (!supportedImageMimes.has(mime)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const file = item?.asFile();
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.uri) {
|
||||
// If the file is already in a workspace, we don't want to create a copy of it
|
||||
const workspaceFolder = vscode.workspace.getWorkspaceFolder(file.uri);
|
||||
if (workspaceFolder) {
|
||||
return { uri: file.uri };
|
||||
}
|
||||
}
|
||||
|
||||
const uri = await pathGenerator.getNewFilePath(document, file, token);
|
||||
return uri ? { uri, newFileContents: file } : undefined;
|
||||
})));
|
||||
if (!fileEntries.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
for (const entry of fileEntries) {
|
||||
if (entry.newFileContents) {
|
||||
workspaceEdit.createFile(entry.uri, { contents: entry.newFileContents });
|
||||
}
|
||||
}
|
||||
|
||||
const uri = await getNewFileName(document, file);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const snippet = createUriListSnippet(document, [uri]);
|
||||
const snippet = createUriListSnippet(document, fileEntries.map(entry => entry.uri));
|
||||
if (!snippet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note that there is currently no way to undo the file creation :/
|
||||
const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
workspaceEdit.createFile(uri, { contents: file });
|
||||
|
||||
const pasteEdit = new vscode.DocumentPasteEdit(snippet.snippet, snippet.label);
|
||||
pasteEdit.additionalEdit = workspaceEdit;
|
||||
return pasteEdit;
|
||||
|
|
Loading…
Reference in a new issue