mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-21 09:08:53 +01:00
- Reduce margin on button - Change icon to insert - Try to align styling to match other editor widgets - Use capital casing on entries
189 lines
5.6 KiB
TypeScript
189 lines
5.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 path from 'path';
|
|
import * as vscode from 'vscode';
|
|
import * as URI from 'vscode-uri';
|
|
import { Schemes } from '../../util/schemes';
|
|
|
|
export const imageFileExtensions = new Set<string>([
|
|
'bmp',
|
|
'gif',
|
|
'ico',
|
|
'jpe',
|
|
'jpeg',
|
|
'jpg',
|
|
'png',
|
|
'psd',
|
|
'svg',
|
|
'tga',
|
|
'tif',
|
|
'tiff',
|
|
'webp',
|
|
]);
|
|
|
|
const videoFileExtensions = new Set<string>([
|
|
'ogg',
|
|
'mp4'
|
|
]);
|
|
|
|
export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector) {
|
|
return vscode.languages.registerDocumentDropEditProvider(selector, new class implements vscode.DocumentDropEditProvider {
|
|
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 undefined;
|
|
}
|
|
|
|
const snippet = await tryGetUriListSnippet(document, dataTransfer, token);
|
|
if (!snippet) {
|
|
return undefined;
|
|
}
|
|
|
|
const edit = new vscode.DocumentDropEdit(snippet.snippet);
|
|
edit.label = snippet.label;
|
|
return edit;
|
|
}
|
|
}, {
|
|
id: 'insertLink',
|
|
dropMimeTypes: [
|
|
'text/uri-list'
|
|
]
|
|
});
|
|
}
|
|
|
|
export async function tryGetUriListSnippet(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> {
|
|
const urlList = await dataTransfer.get('text/uri-list')?.asString();
|
|
if (!urlList || token.isCancellationRequested) {
|
|
return undefined;
|
|
}
|
|
|
|
const uris: vscode.Uri[] = [];
|
|
for (const resource of urlList.split(/\r?\n/g)) {
|
|
try {
|
|
uris.push(vscode.Uri.parse(resource));
|
|
} catch {
|
|
// noop
|
|
}
|
|
}
|
|
|
|
return createUriListSnippet(document, uris);
|
|
}
|
|
|
|
interface UriListSnippetOptions {
|
|
readonly placeholderText?: string;
|
|
|
|
readonly placeholderStartIndex?: number;
|
|
|
|
/**
|
|
* Should the snippet be for an image?
|
|
*
|
|
* If `undefined`, tries to infer this from the uri.
|
|
*/
|
|
readonly insertAsImage?: boolean;
|
|
|
|
readonly separator?: string;
|
|
}
|
|
|
|
|
|
export function createUriListSnippet(document: vscode.TextDocument, uris: readonly vscode.Uri[], options?: UriListSnippetOptions): { snippet: vscode.SnippetString; label: string } | undefined {
|
|
if (!uris.length) {
|
|
return undefined;
|
|
}
|
|
|
|
const dir = getDocumentDir(document);
|
|
|
|
const snippet = new vscode.SnippetString();
|
|
|
|
let insertedLinkCount = 0;
|
|
let insertedImageCount = 0;
|
|
|
|
uris.forEach((uri, i) => {
|
|
const mdPath = getMdPath(dir, uri);
|
|
|
|
const ext = URI.Utils.extname(uri).toLowerCase().replace('.', '');
|
|
const insertAsImage = typeof options?.insertAsImage === 'undefined' ? imageFileExtensions.has(ext) : !!options.insertAsImage;
|
|
const insertAsVideo = videoFileExtensions.has(ext);
|
|
|
|
if (insertAsVideo) {
|
|
insertedImageCount++;
|
|
snippet.appendText(`<video src="${mdPath}" controls title="`);
|
|
snippet.appendPlaceholder('Title');
|
|
snippet.appendText('"></video>');
|
|
} else {
|
|
if (insertAsImage) {
|
|
insertedImageCount++;
|
|
} else {
|
|
insertedLinkCount++;
|
|
}
|
|
|
|
snippet.appendText(insertAsImage ? '`);
|
|
}
|
|
|
|
if (i < uris.length - 1 && uris.length > 1) {
|
|
snippet.appendText(options?.separator ?? ' ');
|
|
}
|
|
});
|
|
|
|
let label: string;
|
|
if (insertedImageCount > 0 && insertedLinkCount > 0) {
|
|
label = vscode.l10n.t('Insert Markdown Images and Links');
|
|
} else if (insertedImageCount > 0) {
|
|
label = insertedImageCount > 1
|
|
? vscode.l10n.t('Insert Markdown Images')
|
|
: vscode.l10n.t('Insert Markdown Image');
|
|
} else {
|
|
label = insertedLinkCount > 1
|
|
? vscode.l10n.t('Insert Markdown Links')
|
|
: vscode.l10n.t('Insert Markdown Link');
|
|
}
|
|
|
|
return { snippet, label };
|
|
}
|
|
|
|
function getMdPath(dir: vscode.Uri | undefined, file: vscode.Uri) {
|
|
if (dir && dir.scheme === file.scheme && dir.authority === file.authority) {
|
|
if (file.scheme === Schemes.file) {
|
|
// On windows, we must use the native `path.relative` to generate the relative path
|
|
// so that drive-letters are resolved cast insensitively. However we then want to
|
|
// convert back to a posix path to insert in to the document.
|
|
const relativePath = path.relative(dir.fsPath, file.fsPath);
|
|
return encodeURI(path.posix.normalize(relativePath.split(path.sep).join(path.posix.sep)));
|
|
}
|
|
|
|
return encodeURI(path.posix.relative(dir.path, file.path));
|
|
}
|
|
|
|
return file.toString(false);
|
|
}
|
|
|
|
function getDocumentDir(document: vscode.TextDocument): vscode.Uri | undefined {
|
|
const docUri = getParentDocumentUri(document);
|
|
if (docUri.scheme === Schemes.untitled) {
|
|
return vscode.workspace.workspaceFolders?.[0]?.uri;
|
|
}
|
|
return URI.Utils.dirname(docUri);
|
|
}
|
|
|
|
export function getParentDocumentUri(document: vscode.TextDocument): vscode.Uri {
|
|
if (document.uri.scheme === Schemes.notebookCell) {
|
|
for (const notebook of vscode.workspace.notebookDocuments) {
|
|
for (const cell of notebook.getCells()) {
|
|
if (cell.document === document) {
|
|
return notebook.uri;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return document.uri;
|
|
}
|