Make markdown link pasting feature smarter (#187170)

* making markdown link pasting feature smarter

* Update settings description

Co-authored-by: Joyce Er <joyceerhl@gmail.com>

* made checkPaste more concise

* won't paste md link in fenced code or math

---------

Co-authored-by: Joyce Er <joyceerhl@gmail.com>
This commit is contained in:
Meghan Kulkarni 2023-07-10 14:11:49 -07:00 committed by GitHub
parent 261a75e3a4
commit f07abd224b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 17 deletions

View file

@ -499,10 +499,20 @@
]
},
"markdown.editor.pasteUrlAsFormattedLink.enabled": {
"type": "boolean",
"type": "string",
"scope": "resource",
"markdownDescription": "%configuration.markdown.editor.pasteUrlAsFormattedLink.enabled%",
"default": true
"default":"smart",
"enum": [
"always",
"smart",
"never"
],
"markdownEnumDescriptions": [
"%configuration.pasteUrlAsFormattedLink.always%",
"%configuration.pasteUrlAsFormattedLink.smart%",
"%configuration.pasteUrlAsFormattedLink.never%"
]
},
"markdown.validate.enabled": {
"type": "boolean",

View file

@ -41,9 +41,12 @@
"configuration.markdown.editor.drop.copyIntoWorkspace": "Controls if files outside of the workspace that are dropped into a Markdown editor should be copied into the workspace.\n\nUse `#markdown.copyFiles.destination#` to configure where copied dropped files should be created",
"configuration.markdown.editor.filePaste.enabled": "Enable pasting files into a Markdown editor to create Markdown links. Requires enabling `#editor.pasteAs.enabled#`.",
"configuration.markdown.editor.filePaste.copyIntoWorkspace": "Controls if files outside of the workspace that are pasted into a Markdown editor should be copied into the workspace.\n\nUse `#markdown.copyFiles.destination#` to configure where copied files should be created.",
"configuration.markdown.editor.pasteUrlAsFormattedLink.enabled": "Controls if a Markdown link is created when a URL is pasted into the Markdown editor. Requires enabling `#editor.pasteAs.enabled#`.",
"configuration.copyIntoWorkspace.mediaFiles": "Try to copy external image and video files into the workspace.",
"configuration.copyIntoWorkspace.never": "Do not copy external files into the workspace.",
"configuration.markdown.editor.pasteUrlAsFormattedLink.enabled": "Controls how a Markdown link is created when a URL is pasted into the Markdown editor. Requires enabling `#editor.pasteAs.enabled#`.",
"configuration.pasteUrlAsFormattedLink.always": "Always create a Markdown link when a URL is pasted into the Markdown editor.",
"configuration.pasteUrlAsFormattedLink.smart": "Does not create a Markdown link within a link snippet or code bracket.",
"configuration.pasteUrlAsFormattedLink.never": "Never create a Markdown link when a URL is pasted into the Markdown editor.",
"configuration.markdown.validate.enabled.description": "Enable all error reporting in Markdown files.",
"configuration.markdown.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, for example: `[link][ref]`. Requires enabling `#markdown.validate.enabled#`.",
"configuration.markdown.validate.fragmentLinks.enabled.description": "Validate fragment links to headers in the current Markdown file, for example: `[link](#header)`. Requires enabling `#markdown.validate.enabled#`.",

View file

@ -76,10 +76,10 @@ async function insertLink(activeEditor: vscode.TextEditor, selectedFiles: vscode
await vscode.workspace.applyEdit(edit);
}
function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0) {
function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0, smartPaste = 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, {
const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, smartPaste, {
insertAsMedia,
placeholderText: selectionText,
placeholderStartIndex: (i + 1) * selectedFiles.length,

View file

@ -5,7 +5,6 @@
import * as vscode from 'vscode';
import { getMarkdownLink } from './shared';
class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
readonly id = 'insertMarkdownLink';
@ -15,8 +14,8 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
dataTransfer: vscode.DataTransfer,
token: vscode.CancellationToken,
): Promise<vscode.DocumentPasteEdit | undefined> {
const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.pasteUrlAsFormattedLink.enabled', true);
if (!enabled) {
const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'smart');
if (enabled === 'never') {
return;
}

View file

@ -65,15 +65,26 @@ export async function getMarkdownLink(document: vscode.TextDocument, ranges: rea
if (ranges.length === 0) {
return;
}
const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'always');
const edits: vscode.SnippetTextEdit[] = [];
let placeHolderValue: number = ranges.length;
let label: string = '';
let smartPaste: boolean = false;
for (let i = 0; i < ranges.length; i++) {
const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(ranges[i]), placeHolderValue);
if (enabled === 'smart') {
const inMarkdownLink = checkPaste(document, ranges, /\[([^\]]*)\]\(([^)]*)\)/g, i);
const inFencedCode = checkPaste(document, ranges, /^```[\s\S]*?```$/gm, i);
const inFencedMath = checkPaste(document, ranges, /^\$\$[\s\S]*?\$\$$/gm, i);
smartPaste = (inMarkdownLink || inFencedCode || inFencedMath);
}
const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(ranges[i]), placeHolderValue, smartPaste);
if (!snippet) {
return;
}
smartPaste = false;
placeHolderValue--;
edits.push(new vscode.SnippetTextEdit(ranges[i], snippet.snippet));
label = snippet.label;
@ -85,7 +96,20 @@ export async function getMarkdownLink(document: vscode.TextDocument, ranges: rea
return { additionalEdits, label };
}
export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> {
function checkPaste(document: vscode.TextDocument, ranges: readonly vscode.Range[], regex: RegExp, index: number): boolean {
const rangeStartOffset = document.offsetAt(ranges[index].start);
const rangeEndOffset = document.offsetAt(ranges[index].end);
const matches = [...document.getText().matchAll(regex)];
for (const match of matches) {
if (match.index !== undefined && rangeStartOffset > match.index && rangeEndOffset < match.index + match[0].length) {
return true;
}
}
return false;
}
export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, smartPaste = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> {
if (token.isCancellationRequested) {
return undefined;
}
@ -99,7 +123,7 @@ export async function tryGetUriListSnippet(document: vscode.TextDocument, urlLis
}
}
return createUriListSnippet(document, uris, title, placeHolderValue);
return createUriListSnippet(document, uris, title, placeHolderValue, smartPaste);
}
interface UriListSnippetOptions {
@ -122,6 +146,7 @@ export function createUriListSnippet(
uris: readonly vscode.Uri[],
title = '',
placeholderValue = 0,
smartPaste = false,
options?: UriListSnippetOptions,
): { snippet: vscode.SnippetString; label: string } | undefined {
if (!uris.length) {
@ -164,13 +189,21 @@ export function createUriListSnippet(
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
} else {
insertedLinkCount++;
snippet.appendText('[');
snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue);
if (externalUriSchemes.includes(uri.scheme)) {
const uriString = uri.toString(true);
snippet.appendText(`](${uriString})`);
if (smartPaste) {
if (externalUriSchemes.includes(uri.scheme)) {
snippet.appendText(uri.toString(true));
} else {
snippet.appendText(escapeMarkdownLinkPath(mdPath));
}
} else {
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
snippet.appendText('[');
snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue);
if (externalUriSchemes.includes(uri.scheme)) {
const uriString = uri.toString(true);
snippet.appendText(`](${uriString})`);
} else {
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
}
}
}
}