Allow overriding how markdown links are inserted using the keybinding (#234310)
Allows using the `kind` field in the `pasteAs` keybinding to force links to be inserted a certain way, such as as images
This commit is contained in:
Родитель
d6f311dbf5
Коммит
fc5cff7bb6
|
@ -6,7 +6,7 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { Utils } from 'vscode-uri';
|
||||
import { Command } from '../commandManager';
|
||||
import { createUriListSnippet, mediaFileExtensions } from '../languageFeatures/copyFiles/shared';
|
||||
import { createUriListSnippet, linkEditKind, mediaFileExtensions } from '../languageFeatures/copyFiles/shared';
|
||||
import { coalesce } from '../util/arrays';
|
||||
import { getParentDocumentUri } from '../util/document';
|
||||
import { Schemes } from '../util/schemes';
|
||||
|
@ -84,7 +84,7 @@ function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: re
|
|||
const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => {
|
||||
const selectionText = activeEditor.document.getText(selection);
|
||||
const snippet = createUriListSnippet(activeEditor.document.uri, selectedFiles.map(uri => ({ uri })), {
|
||||
insertAsMedia: insertAsMedia,
|
||||
linkKindHint: insertAsMedia ? 'media' : linkEditKind,
|
||||
placeholderText: selectionText,
|
||||
placeholderStartIndex: (i + 1) * selectedFiles.length,
|
||||
separator: insertAsMedia ? '\n' : ' ',
|
||||
|
|
|
@ -106,10 +106,10 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v
|
|||
document: vscode.TextDocument,
|
||||
ranges: readonly vscode.Range[],
|
||||
dataTransfer: vscode.DataTransfer,
|
||||
settings: {
|
||||
settings: Readonly<{
|
||||
insert: InsertMarkdownLink;
|
||||
copyIntoWorkspace: CopyFilesSettings;
|
||||
},
|
||||
}>,
|
||||
context: vscode.DocumentPasteEditContext | undefined,
|
||||
token: vscode.CancellationToken,
|
||||
): Promise<DropOrPasteEdit | undefined> {
|
||||
|
@ -172,7 +172,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v
|
|||
}
|
||||
}
|
||||
|
||||
const edit = createInsertUriListEdit(document, ranges, uriList);
|
||||
const edit = createInsertUriListEdit(document, ranges, uriList, { linkKindHint: context?.only });
|
||||
if (!edit) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider {
|
|||
document: vscode.TextDocument,
|
||||
ranges: readonly vscode.Range[],
|
||||
dataTransfer: vscode.DataTransfer,
|
||||
_context: vscode.DocumentPasteEditContext,
|
||||
context: vscode.DocumentPasteEditContext,
|
||||
token: vscode.CancellationToken,
|
||||
): Promise<vscode.DocumentPasteEdit[] | undefined> {
|
||||
const pasteUrlSetting = vscode.workspace.getConfiguration('markdown', document)
|
||||
|
@ -44,12 +44,17 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: If the user has explicitly requested to paste as a markdown link,
|
||||
// try to paste even if we don't have a valid uri
|
||||
const uriText = findValidUriInText(text);
|
||||
if (!uriText) {
|
||||
return;
|
||||
}
|
||||
|
||||
const edit = createInsertUriListEdit(document, ranges, UriList.from(uriText), { preserveAbsoluteUris: true });
|
||||
const edit = createInsertUriListEdit(document, ranges, UriList.from(uriText), {
|
||||
linkKindHint: context.only,
|
||||
preserveAbsoluteUris: true
|
||||
});
|
||||
if (!edit) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -177,11 +177,11 @@ interface UriListSnippetOptions {
|
|||
readonly placeholderStartIndex?: number;
|
||||
|
||||
/**
|
||||
* Controls if a media link (`![](...)`) is inserted instead of a normal markdown link.
|
||||
* Hints how links should be inserted, e.g. as normal markdown link or as an image.
|
||||
*
|
||||
* By default tries to infer this from the uri.
|
||||
* By default this is inferred from the uri. If you use `media`, we will insert the resource as an image, video, or audio.
|
||||
*/
|
||||
readonly insertAsMedia?: boolean;
|
||||
readonly linkKindHint?: vscode.DocumentDropOrPasteEditKind | 'media';
|
||||
|
||||
readonly separator?: string;
|
||||
|
||||
|
@ -229,12 +229,16 @@ export function createUriListSnippet(
|
|||
uris.forEach((uri, i) => {
|
||||
const mdPath = (!options?.preserveAbsoluteUris ? getRelativeMdPath(documentDir, uri.uri) : undefined) ?? uri.str ?? uri.uri.toString();
|
||||
|
||||
const ext = URI.Utils.extname(uri.uri).toLowerCase().replace('.', '');
|
||||
const insertAsMedia = options?.insertAsMedia || (typeof options?.insertAsMedia === 'undefined' && mediaFileExtensions.has(ext));
|
||||
const desiredKind = getDesiredLinkKind(uri.uri, options);
|
||||
|
||||
if (insertAsMedia) {
|
||||
const insertAsVideo = mediaFileExtensions.get(ext) === MediaKind.Video;
|
||||
const insertAsAudio = mediaFileExtensions.get(ext) === MediaKind.Audio;
|
||||
if (desiredKind === DesiredLinkKind.Link) {
|
||||
insertedLinkCount++;
|
||||
snippet.appendText('[');
|
||||
snippet.appendPlaceholder(escapeBrackets(options?.placeholderText ?? 'text'), placeholderIndex);
|
||||
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
|
||||
} else {
|
||||
const insertAsVideo = desiredKind === DesiredLinkKind.Video;
|
||||
const insertAsAudio = desiredKind === DesiredLinkKind.Audio;
|
||||
if (insertAsVideo || insertAsAudio) {
|
||||
if (insertAsVideo) {
|
||||
insertedVideoCount++;
|
||||
|
@ -255,11 +259,6 @@ export function createUriListSnippet(
|
|||
snippet.appendPlaceholder(placeholderText, placeholderIndex);
|
||||
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
|
||||
}
|
||||
} else {
|
||||
insertedLinkCount++;
|
||||
snippet.appendText('[');
|
||||
snippet.appendPlaceholder(escapeBrackets(options?.placeholderText ?? 'text'), placeholderIndex);
|
||||
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
|
||||
}
|
||||
|
||||
if (i < uris.length - 1 && uris.length > 1) {
|
||||
|
@ -270,6 +269,37 @@ export function createUriListSnippet(
|
|||
return { snippet, insertedAudioCount, insertedVideoCount, insertedImageCount, insertedLinkCount };
|
||||
}
|
||||
|
||||
enum DesiredLinkKind {
|
||||
Link,
|
||||
Image,
|
||||
Video,
|
||||
Audio,
|
||||
}
|
||||
|
||||
function getDesiredLinkKind(uri: vscode.Uri, options: UriListSnippetOptions | undefined): DesiredLinkKind {
|
||||
if (options?.linkKindHint instanceof vscode.DocumentDropOrPasteEditKind) {
|
||||
if (linkEditKind.contains(options.linkKindHint)) {
|
||||
return DesiredLinkKind.Link;
|
||||
} else if (imageEditKind.contains(options.linkKindHint)) {
|
||||
return DesiredLinkKind.Image;
|
||||
} else if (audioEditKind.contains(options.linkKindHint)) {
|
||||
return DesiredLinkKind.Audio;
|
||||
} else if (videoEditKind.contains(options.linkKindHint)) {
|
||||
return DesiredLinkKind.Video;
|
||||
}
|
||||
}
|
||||
|
||||
const normalizedExt = URI.Utils.extname(uri).toLowerCase().replace('.', '');
|
||||
if (options?.linkKindHint === 'media' || mediaFileExtensions.has(normalizedExt)) {
|
||||
switch (mediaFileExtensions.get(normalizedExt)) {
|
||||
case MediaKind.Video: return DesiredLinkKind.Video;
|
||||
case MediaKind.Audio: return DesiredLinkKind.Audio;
|
||||
default: return DesiredLinkKind.Image;
|
||||
}
|
||||
}
|
||||
|
||||
return DesiredLinkKind.Link;
|
||||
}
|
||||
|
||||
function getRelativeMdPath(dir: vscode.Uri | undefined, file: vscode.Uri): string | undefined {
|
||||
if (dir && dir.scheme === file.scheme && dir.authority === file.authority) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as assert from 'assert';
|
|||
import 'mocha';
|
||||
import * as vscode from 'vscode';
|
||||
import { InMemoryDocument } from '../client/inMemoryDocument';
|
||||
import { createInsertUriListEdit } from '../languageFeatures/copyFiles/shared';
|
||||
import { createInsertUriListEdit, imageEditKind, linkEditKind } from '../languageFeatures/copyFiles/shared';
|
||||
import { InsertMarkdownLink, findValidUriInText, shouldInsertMarkdownLinkByDefault } from '../languageFeatures/copyFiles/smartDropOrPaste';
|
||||
import { noopToken } from '../util/cancellation';
|
||||
import { UriList } from '../util/uriList';
|
||||
|
@ -20,8 +20,6 @@ function makeTestDoc(contents: string) {
|
|||
suite('createEditAddingLinksForUriList', () => {
|
||||
|
||||
test('Markdown Link Pasting should occur for a valid link (end to end)', async () => {
|
||||
// createEditAddingLinksForUriList -> checkSmartPaste -> tryGetUriListSnippet -> createUriListSnippet -> createLinkSnippet
|
||||
|
||||
const result = createInsertUriListEdit(
|
||||
new InMemoryDocument(vscode.Uri.file('test.md'), 'hello world!'), [new vscode.Range(0, 0, 0, 12)], UriList.from('https://www.microsoft.com/'));
|
||||
// need to check the actual result -> snippet value
|
||||
|
@ -110,7 +108,6 @@ suite('createEditAddingLinksForUriList', () => {
|
|||
});
|
||||
|
||||
suite('createInsertUriListEdit', () => {
|
||||
|
||||
test('Should create snippet with < > when pasted link has an mismatched parentheses', () => {
|
||||
const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.mic(rosoft.com'));
|
||||
assert.strictEqual(edit?.edits?.[0].snippet.value, '[${1:text}](<https://www.mic(rosoft.com>)');
|
||||
|
@ -135,6 +132,25 @@ suite('createEditAddingLinksForUriList', () => {
|
|||
const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.example.com/path?query=value&another=value#fragment'));
|
||||
assert.strictEqual(edit?.edits?.[0].snippet.value, '[${1:text}](https://www.example.com/path?query=value&another=value#fragment)');
|
||||
});
|
||||
|
||||
test('Should add image for image file by default', () => {
|
||||
const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.example.com/cat.png'));
|
||||
assert.strictEqual(edit?.edits?.[0].snippet.value, '![${1:alt text}](https://www.example.com/cat.png)');
|
||||
});
|
||||
|
||||
test('Should be able to override insert style to use link', () => {
|
||||
const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.example.com/cat.png'), {
|
||||
linkKindHint: linkEditKind,
|
||||
});
|
||||
assert.strictEqual(edit?.edits?.[0].snippet.value, '[${1:text}](https://www.example.com/cat.png)');
|
||||
});
|
||||
|
||||
test('Should be able to override insert style to use images', () => {
|
||||
const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.example.com/'), {
|
||||
linkKindHint: imageEditKind,
|
||||
});
|
||||
assert.strictEqual(edit?.edits?.[0].snippet.value, '![${1:alt text}](https://www.example.com/)');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче