mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-19 08:08:39 +01:00
240 lines
7.6 KiB
TypeScript
240 lines
7.6 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as vscode from 'vscode';
|
|
import { coalesce } from '../../util/arrays';
|
|
import { getParentDocumentUri } from '../../util/document';
|
|
import { Mime, mediaMimes } from '../../util/mimes';
|
|
import { Schemes } from '../../util/schemes';
|
|
import { NewFilePathGenerator } from './newFilePathGenerator';
|
|
import { createInsertUriListEdit, createUriListSnippet, getSnippetLabel } from './shared';
|
|
|
|
/**
|
|
* Provides support for pasting or dropping resources into markdown documents.
|
|
*
|
|
* This includes:
|
|
*
|
|
* - `text/uri-list` data in the data transfer.
|
|
* - File object in the data transfer.
|
|
* - Media data in the data transfer, such as `image/png`.
|
|
*/
|
|
class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, vscode.DocumentDropEditProvider {
|
|
|
|
public static readonly kind = vscode.DocumentPasteEditKind.Empty.append('markdown', 'link');
|
|
|
|
public static readonly mimeTypes = [
|
|
Mime.textUriList,
|
|
'files',
|
|
...mediaMimes,
|
|
];
|
|
|
|
private readonly _yieldTo = [
|
|
vscode.DocumentPasteEditKind.Empty.append('text'),
|
|
vscode.DocumentPasteEditKind.Empty.append('markdown', 'image', 'attachment'),
|
|
];
|
|
|
|
public async provideDocumentDropEdits(
|
|
document: vscode.TextDocument,
|
|
position: vscode.Position,
|
|
dataTransfer: vscode.DataTransfer,
|
|
token: vscode.CancellationToken,
|
|
): Promise<vscode.DocumentDropEdit | undefined> {
|
|
const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.drop.enabled', true);
|
|
if (!enabled) {
|
|
return;
|
|
}
|
|
|
|
const filesEdit = await this._getMediaFilesDropEdit(document, dataTransfer, token);
|
|
if (filesEdit) {
|
|
return filesEdit;
|
|
}
|
|
|
|
if (token.isCancellationRequested) {
|
|
return;
|
|
}
|
|
|
|
return this._createEditFromUriListData(document, [new vscode.Range(position, position)], dataTransfer, token);
|
|
}
|
|
|
|
public async provideDocumentPasteEdits(
|
|
document: vscode.TextDocument,
|
|
ranges: readonly vscode.Range[],
|
|
dataTransfer: vscode.DataTransfer,
|
|
_context: vscode.DocumentPasteEditContext,
|
|
token: vscode.CancellationToken,
|
|
): Promise<vscode.DocumentPasteEdit[] | undefined> {
|
|
const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.filePaste.enabled', true);
|
|
if (!enabled) {
|
|
return;
|
|
}
|
|
|
|
const createEdit = await this._getMediaFilesPasteEdit(document, dataTransfer, token);
|
|
if (createEdit) {
|
|
return [createEdit];
|
|
}
|
|
|
|
if (token.isCancellationRequested) {
|
|
return;
|
|
}
|
|
|
|
const edit = await this._createEditFromUriListData(document, ranges, dataTransfer, token);
|
|
return edit ? [edit] : undefined;
|
|
}
|
|
|
|
private async _createEditFromUriListData(
|
|
document: vscode.TextDocument,
|
|
ranges: readonly vscode.Range[],
|
|
dataTransfer: vscode.DataTransfer,
|
|
token: vscode.CancellationToken,
|
|
): Promise<vscode.DocumentPasteEdit | undefined> {
|
|
const uriList = await dataTransfer.get(Mime.textUriList)?.asString();
|
|
if (!uriList || token.isCancellationRequested) {
|
|
return;
|
|
}
|
|
|
|
const pasteEdit = createInsertUriListEdit(document, ranges, uriList);
|
|
if (!pasteEdit) {
|
|
return;
|
|
}
|
|
|
|
const uriEdit = new vscode.DocumentPasteEdit('', pasteEdit.label, ResourcePasteOrDropProvider.kind);
|
|
const edit = new vscode.WorkspaceEdit();
|
|
edit.set(document.uri, pasteEdit.edits);
|
|
uriEdit.additionalEdit = edit;
|
|
uriEdit.yieldTo = this._yieldTo;
|
|
return uriEdit;
|
|
}
|
|
|
|
private async _getMediaFilesPasteEdit(
|
|
document: vscode.TextDocument,
|
|
dataTransfer: vscode.DataTransfer,
|
|
token: vscode.CancellationToken,
|
|
): Promise<vscode.DocumentPasteEdit | undefined> {
|
|
if (getParentDocumentUri(document.uri).scheme === Schemes.untitled) {
|
|
return;
|
|
}
|
|
|
|
const copyFilesIntoWorkspace = vscode.workspace.getConfiguration('markdown', document).get<'mediaFiles' | 'never'>('editor.filePaste.copyIntoWorkspace', 'mediaFiles');
|
|
if (copyFilesIntoWorkspace !== 'mediaFiles') {
|
|
return;
|
|
}
|
|
|
|
const edit = await this._createEditForMediaFiles(document, dataTransfer, token);
|
|
if (!edit) {
|
|
return;
|
|
}
|
|
|
|
const pasteEdit = new vscode.DocumentPasteEdit(edit.snippet, edit.label, ResourcePasteOrDropProvider.kind);
|
|
pasteEdit.additionalEdit = edit.additionalEdits;
|
|
pasteEdit.yieldTo = this._yieldTo;
|
|
return pasteEdit;
|
|
}
|
|
|
|
private async _getMediaFilesDropEdit(
|
|
document: vscode.TextDocument,
|
|
dataTransfer: vscode.DataTransfer,
|
|
token: vscode.CancellationToken,
|
|
): Promise<vscode.DocumentDropEdit | undefined> {
|
|
if (getParentDocumentUri(document.uri).scheme === Schemes.untitled) {
|
|
return;
|
|
}
|
|
|
|
const copyIntoWorkspace = vscode.workspace.getConfiguration('markdown', document).get<'mediaFiles' | 'never'>('editor.drop.copyIntoWorkspace', 'mediaFiles');
|
|
if (copyIntoWorkspace !== 'mediaFiles') {
|
|
return;
|
|
}
|
|
|
|
const edit = await this._createEditForMediaFiles(document, dataTransfer, token);
|
|
if (!edit) {
|
|
return;
|
|
}
|
|
|
|
const dropEdit = new vscode.DocumentDropEdit(edit.snippet);
|
|
dropEdit.title = edit.label;
|
|
dropEdit.additionalEdit = edit.additionalEdits;
|
|
dropEdit.yieldTo = this._yieldTo;
|
|
return dropEdit;
|
|
}
|
|
|
|
/**
|
|
* Create a new edit for media files in a data transfer.
|
|
*
|
|
* This tries copying files outside of the workspace into the workspace.
|
|
*/
|
|
private async _createEditForMediaFiles(
|
|
document: vscode.TextDocument,
|
|
dataTransfer: vscode.DataTransfer,
|
|
token: vscode.CancellationToken,
|
|
): Promise<{ snippet: vscode.SnippetString; label: string; additionalEdits: vscode.WorkspaceEdit } | undefined> {
|
|
interface FileEntry {
|
|
readonly uri: vscode.Uri;
|
|
readonly newFile?: { readonly contents: vscode.DataTransferFile; readonly overwrite: boolean };
|
|
}
|
|
|
|
const pathGenerator = new NewFilePathGenerator();
|
|
const fileEntries = coalesce(await Promise.all(Array.from(dataTransfer, async ([mime, item]): Promise<FileEntry | undefined> => {
|
|
if (!mediaMimes.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 newFile = await pathGenerator.getNewFilePath(document, file, token);
|
|
if (!newFile) {
|
|
return;
|
|
}
|
|
return { uri: newFile.uri, newFile: { contents: file, overwrite: newFile.overwrite } };
|
|
})));
|
|
if (!fileEntries.length) {
|
|
return;
|
|
}
|
|
|
|
const workspaceEdit = new vscode.WorkspaceEdit();
|
|
for (const entry of fileEntries) {
|
|
if (entry.newFile) {
|
|
workspaceEdit.createFile(entry.uri, {
|
|
contents: entry.newFile.contents,
|
|
overwrite: entry.newFile.overwrite,
|
|
});
|
|
}
|
|
}
|
|
|
|
const snippet = createUriListSnippet(document.uri, fileEntries);
|
|
if (!snippet) {
|
|
return;
|
|
}
|
|
|
|
return {
|
|
snippet: snippet.snippet,
|
|
label: getSnippetLabel(snippet),
|
|
additionalEdits: workspaceEdit,
|
|
};
|
|
}
|
|
}
|
|
|
|
export function registerResourceDropOrPasteSupport(selector: vscode.DocumentSelector): vscode.Disposable {
|
|
return vscode.Disposable.from(
|
|
vscode.languages.registerDocumentPasteEditProvider(selector, new ResourcePasteOrDropProvider(), {
|
|
providedPasteEditKinds: [ResourcePasteOrDropProvider.kind],
|
|
pasteMimeTypes: ResourcePasteOrDropProvider.mimeTypes,
|
|
}),
|
|
vscode.languages.registerDocumentDropEditProvider(selector, new ResourcePasteOrDropProvider(), {
|
|
providedDropEditKinds: [ResourcePasteOrDropProvider.kind],
|
|
dropMimeTypes: ResourcePasteOrDropProvider.mimeTypes,
|
|
}),
|
|
);
|
|
}
|