Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix markdown link inserting #198702

Merged
merged 1 commit into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async function insertLink(activeEditor: vscode.TextEditor, selectedFiles: vscode
function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false) {
const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => {
const selectionText = activeEditor.document.getText(selection);
const snippet = createUriListSnippet(activeEditor.document, selectedFiles, [], title, placeholderValue, pasteAsMarkdownLink, isExternalLink, {
const snippet = createUriListSnippet(activeEditor.document, selectedFiles.map(uri => ({ uri })), title, placeholderValue, pasteAsMarkdownLink, isExternalLink, {
insertAsMedia,
placeholderText: selectionText,
placeholderStartIndex: (i + 1) * selectedFiles.length,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ class ResourceDropProvider implements vscode.DocumentDropEditProvider {
private async _getUriListEdit(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.DocumentDropEdit | undefined> {
const urlList = await dataTransfer.get(Mime.textUriList)?.asString();
if (!urlList || token.isCancellationRequested) {
return undefined;
return;
}

const snippet = await tryGetUriListSnippet(document, urlList, token);
const snippet = tryGetUriListSnippet(document, urlList);
if (!snippet) {
return undefined;
return;
}

const edit = new vscode.DocumentDropEdit(snippet.snippet);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class PasteResourceEditProvider implements vscode.DocumentPasteEditProvider {
}

const pasteUrlSetting = getPasteUrlAsFormattedLinkSetting(document);
const pasteEdit = await createEditAddingLinksForUriList(document, ranges, uriList, false, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token);
const pasteEdit = createEditAddingLinksForUriList(document, ranges, uriList, false, pasteUrlSetting === PasteUrlAsFormattedLink.Smart);
if (!pasteEdit) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider {
return;
}

const pasteEdit = await createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token);
const pasteEdit = createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart);
if (!pasteEdit) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,13 @@ export function getPasteUrlAsFormattedLinkSetting(document: vscode.TextDocument)
return vscode.workspace.getConfiguration('markdown', document).get<PasteUrlAsFormattedLink>('editor.pasteUrlAsFormattedLink.enabled', PasteUrlAsFormattedLink.Smart);
}

export async function createEditAddingLinksForUriList(
export function createEditAddingLinksForUriList(
document: SkinnyTextDocument,
ranges: readonly vscode.Range[],
urlList: string,
isExternalLink: boolean,
useSmartPaste: boolean,
token: vscode.CancellationToken,
): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string; markdownLink: boolean } | undefined> {
useSmartPaste: boolean
): { additionalEdits: vscode.WorkspaceEdit; label: string; markdownLink: boolean } | undefined {

if (ranges.length === 0) {
return;
Expand All @@ -104,7 +103,7 @@ export async function createEditAddingLinksForUriList(
markdownLink = pasteAsMarkdownLink; // FIX: this will only match the last range
}

const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(range), placeHolderValue, pasteAsMarkdownLink, isExternalLink);
const snippet = tryGetUriListSnippet(document, urlList, document.getText(range), placeHolderValue, pasteAsMarkdownLink, isExternalLink);
if (!snippet) {
return;
}
Expand Down Expand Up @@ -158,21 +157,16 @@ export function validateLink(urlList: string): { isValid: boolean; cleanedUrlLis
return { isValid, cleanedUrlList: splitUrlList[0] };
}

export async function tryGetUriListSnippet(document: SkinnyTextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> {
if (token.isCancellationRequested) {
return undefined;
}
const uriStrings: string[] = [];
const uris: vscode.Uri[] = [];
for (const resource of urlList.split(/\r?\n/g)) {
export function tryGetUriListSnippet(document: SkinnyTextDocument, urlList: String, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): { snippet: vscode.SnippetString; label: string } | undefined {
const entries = coalesce(urlList.split(/\r?\n/g).map(resource => {
try {
uris.push(vscode.Uri.parse(resource));
uriStrings.push(resource);
return { uri: vscode.Uri.parse(resource), str: resource };
} catch {
// noop
// Uri parse failure
return undefined;
}
}
return createUriListSnippet(document, uris, uriStrings, title, placeHolderValue, pasteAsMarkdownLink, isExternalLink);
}));
return createUriListSnippet(document, entries, title, placeHolderValue, pasteAsMarkdownLink, isExternalLink);
}

interface UriListSnippetOptions {
Expand All @@ -193,20 +187,21 @@ interface UriListSnippetOptions {
export function appendToLinkSnippet(
snippet: vscode.SnippetString,
title: string,
uriString: string,
link: string,
placeholderValue: number,
isExternalLink: boolean,
): vscode.SnippetString {
): void {
snippet.appendText('[');
snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue);
snippet.appendText(`](${escapeMarkdownLinkPath(uriString, isExternalLink)})`);
return snippet;
snippet.appendText(`](${escapeMarkdownLinkPath(link, isExternalLink)})`);
}

export function createUriListSnippet(
document: SkinnyTextDocument,
uris: readonly vscode.Uri[],
uriStrings?: readonly string[],
uris: ReadonlyArray<{
readonly uri: vscode.Uri;
readonly str?: string;
}>,
title = '',
placeholderValue = 0,
pasteAsMarkdownLink = true,
Expand All @@ -219,15 +214,15 @@ export function createUriListSnippet(

const documentDir = getDocumentDir(document.uri);

let snippet = new vscode.SnippetString();
const snippet = new vscode.SnippetString();
let insertedLinkCount = 0;
let insertedImageCount = 0;
let insertedAudioVideoCount = 0;

uris.forEach((uri, i) => {
const mdPath = getMdPath(documentDir, uri);
const mdPath = getRelativeMdPath(documentDir, uri.uri) ?? uri.str ?? uri.uri.toString();

const ext = URI.Utils.extname(uri).toLowerCase().replace('.', '');
const ext = URI.Utils.extname(uri.uri).toLowerCase().replace('.', '');
const insertAsMedia = typeof options?.insertAsMedia === 'undefined' ? mediaFileExtensions.has(ext) : !!options.insertAsMedia;
const insertAsVideo = mediaFileExtensions.get(ext) === MediaKind.Video;
const insertAsAudio = mediaFileExtensions.get(ext) === MediaKind.Audio;
Expand Down Expand Up @@ -257,11 +252,7 @@ export function createUriListSnippet(
}
} else {
insertedLinkCount++;
if (uriStrings && isExternalLink) {
snippet = appendToLinkSnippet(snippet, title, uriStrings[i], placeholderValue, isExternalLink);
} else {
snippet.appendText(escapeMarkdownLinkPath(mdPath, isExternalLink));
}
appendToLinkSnippet(snippet, title, mdPath, placeholderValue, isExternalLink);
}

if (i < uris.length - 1 && uris.length > 1) {
Expand Down Expand Up @@ -349,7 +340,7 @@ export async function createEditForMediaFiles(
}
}

const snippet = createUriListSnippet(document, fileEntries.map(entry => entry.uri));
const snippet = createUriListSnippet(document, fileEntries);
if (!snippet) {
return;
}
Expand All @@ -361,7 +352,7 @@ export async function createEditForMediaFiles(
};
}

function getMdPath(dir: vscode.Uri | undefined, file: vscode.Uri) {
function getRelativeMdPath(dir: vscode.Uri | undefined, file: vscode.Uri): string | undefined {
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
Expand All @@ -373,8 +364,7 @@ function getMdPath(dir: vscode.Uri | undefined, file: vscode.Uri) {

return path.posix.relative(dir.path, file.path);
}

return file.toString(false);
return undefined;
}

function escapeHtmlAttribute(attr: string): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ suite('createEditAddingLinksForUriList', () => {
getText: function () { return 'hello world!'; },
};

const result = await createEditAddingLinksForUriList(skinnyDocument, [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true, new vscode.CancellationTokenSource().token);
const result = createEditAddingLinksForUriList(skinnyDocument, [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true);
// need to check the actual result -> snippet value
assert.strictEqual(result?.label, 'Insert Markdown Link');
});
Expand Down Expand Up @@ -95,31 +95,36 @@ suite('createEditAddingLinksForUriList', () => {

test('Should create snippet with < > when pasted link has an mismatched parentheses', () => {
const uriString = 'https://www.mic(rosoft.com';
const snippet = appendToLinkSnippet(new vscode.SnippetString(''), 'abc', uriString, 0, true);
const snippet = new vscode.SnippetString('');
appendToLinkSnippet(snippet, 'abc', uriString, 0, true);
assert.strictEqual(snippet?.value, '[${0:abc}](<https://www.mic(rosoft.com>)');
});

test('Should create Markdown link snippet when pasteAsMarkdownLink is true', () => {
const uriString = 'https://www.microsoft.com';
const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', uriString, 0, true);
const snippet = new vscode.SnippetString('');
appendToLinkSnippet(snippet, '', uriString, 0, true);
assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com)');
});

test('Should use an unencoded URI string in Markdown link when passing in an external browser link', () => {
const uriString = 'https://www.microsoft.com';
const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', uriString, 0, true);
const snippet = new vscode.SnippetString('');
appendToLinkSnippet(snippet, '', uriString, 0, true);
assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com)');
});

test('Should not decode an encoded URI string when passing in an external browser link', () => {
const uriString = 'https://www.microsoft.com/%20';
const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', uriString, 0, true);
const snippet = new vscode.SnippetString('');
appendToLinkSnippet(snippet, '', uriString, 0, true);
assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com/%20)');
});

test('Should not encode an unencoded URI string when passing in an external browser link', () => {
const uriString = 'https://www.example.com/path?query=value&another=value#fragment';
const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', uriString, 0, true);
const snippet = new vscode.SnippetString('');
appendToLinkSnippet(snippet, '', uriString, 0, true);
assert.strictEqual(snippet?.value, '[${0:Title}](https://www.example.com/path?query=value&another=value#fragment)');
});
});
Expand Down