Skip to content

Commit f07abd2

Browse files
Make markdown link pasting feature smarter (microsoft#187170)
* making markdown link pasting feature smarter * Update settings description Co-authored-by: Joyce Er <[email protected]> * made checkPaste more concise * won't paste md link in fenced code or math --------- Co-authored-by: Joyce Er <[email protected]>
1 parent 261a75e commit f07abd2

File tree

5 files changed

+62
-17
lines changed

5 files changed

+62
-17
lines changed

extensions/markdown-language-features/package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,10 +499,20 @@
499499
]
500500
},
501501
"markdown.editor.pasteUrlAsFormattedLink.enabled": {
502-
"type": "boolean",
502+
"type": "string",
503503
"scope": "resource",
504504
"markdownDescription": "%configuration.markdown.editor.pasteUrlAsFormattedLink.enabled%",
505-
"default": true
505+
"default":"smart",
506+
"enum": [
507+
"always",
508+
"smart",
509+
"never"
510+
],
511+
"markdownEnumDescriptions": [
512+
"%configuration.pasteUrlAsFormattedLink.always%",
513+
"%configuration.pasteUrlAsFormattedLink.smart%",
514+
"%configuration.pasteUrlAsFormattedLink.never%"
515+
]
506516
},
507517
"markdown.validate.enabled": {
508518
"type": "boolean",

extensions/markdown-language-features/package.nls.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@
4141
"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",
4242
"configuration.markdown.editor.filePaste.enabled": "Enable pasting files into a Markdown editor to create Markdown links. Requires enabling `#editor.pasteAs.enabled#`.",
4343
"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.",
44-
"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#`.",
4544
"configuration.copyIntoWorkspace.mediaFiles": "Try to copy external image and video files into the workspace.",
4645
"configuration.copyIntoWorkspace.never": "Do not copy external files into the workspace.",
46+
"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#`.",
47+
"configuration.pasteUrlAsFormattedLink.always": "Always create a Markdown link when a URL is pasted into the Markdown editor.",
48+
"configuration.pasteUrlAsFormattedLink.smart": "Does not create a Markdown link within a link snippet or code bracket.",
49+
"configuration.pasteUrlAsFormattedLink.never": "Never create a Markdown link when a URL is pasted into the Markdown editor.",
4750
"configuration.markdown.validate.enabled.description": "Enable all error reporting in Markdown files.",
4851
"configuration.markdown.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, for example: `[link][ref]`. Requires enabling `#markdown.validate.enabled#`.",
4952
"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#`.",

extensions/markdown-language-features/src/commands/insertResource.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ async function insertLink(activeEditor: vscode.TextEditor, selectedFiles: vscode
7676
await vscode.workspace.applyEdit(edit);
7777
}
7878

79-
function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0) {
79+
function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0, smartPaste = false) {
8080
const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => {
8181
const selectionText = activeEditor.document.getText(selection);
82-
const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, {
82+
const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, smartPaste, {
8383
insertAsMedia,
8484
placeholderText: selectionText,
8585
placeholderStartIndex: (i + 1) * selectedFiles.length,

extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import * as vscode from 'vscode';
77
import { getMarkdownLink } from './shared';
8-
98
class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
109

1110
readonly id = 'insertMarkdownLink';
@@ -15,8 +14,8 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider {
1514
dataTransfer: vscode.DataTransfer,
1615
token: vscode.CancellationToken,
1716
): Promise<vscode.DocumentPasteEdit | undefined> {
18-
const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.pasteUrlAsFormattedLink.enabled', true);
19-
if (!enabled) {
17+
const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'smart');
18+
if (enabled === 'never') {
2019
return;
2120
}
2221

extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,26 @@ export async function getMarkdownLink(document: vscode.TextDocument, ranges: rea
6565
if (ranges.length === 0) {
6666
return;
6767
}
68+
const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'always');
6869

6970
const edits: vscode.SnippetTextEdit[] = [];
7071
let placeHolderValue: number = ranges.length;
7172
let label: string = '';
73+
let smartPaste: boolean = false;
7274
for (let i = 0; i < ranges.length; i++) {
73-
const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(ranges[i]), placeHolderValue);
75+
if (enabled === 'smart') {
76+
const inMarkdownLink = checkPaste(document, ranges, /\[([^\]]*)\]\(([^)]*)\)/g, i);
77+
const inFencedCode = checkPaste(document, ranges, /^```[\s\S]*?```$/gm, i);
78+
const inFencedMath = checkPaste(document, ranges, /^\$\$[\s\S]*?\$\$$/gm, i);
79+
smartPaste = (inMarkdownLink || inFencedCode || inFencedMath);
80+
}
81+
82+
const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(ranges[i]), placeHolderValue, smartPaste);
7483
if (!snippet) {
7584
return;
7685
}
86+
87+
smartPaste = false;
7788
placeHolderValue--;
7889
edits.push(new vscode.SnippetTextEdit(ranges[i], snippet.snippet));
7990
label = snippet.label;
@@ -85,7 +96,20 @@ export async function getMarkdownLink(document: vscode.TextDocument, ranges: rea
8596
return { additionalEdits, label };
8697
}
8798

88-
export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> {
99+
function checkPaste(document: vscode.TextDocument, ranges: readonly vscode.Range[], regex: RegExp, index: number): boolean {
100+
const rangeStartOffset = document.offsetAt(ranges[index].start);
101+
const rangeEndOffset = document.offsetAt(ranges[index].end);
102+
const matches = [...document.getText().matchAll(regex)];
103+
for (const match of matches) {
104+
if (match.index !== undefined && rangeStartOffset > match.index && rangeEndOffset < match.index + match[0].length) {
105+
return true;
106+
}
107+
}
108+
109+
return false;
110+
}
111+
112+
export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, smartPaste = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> {
89113
if (token.isCancellationRequested) {
90114
return undefined;
91115
}
@@ -99,7 +123,7 @@ export async function tryGetUriListSnippet(document: vscode.TextDocument, urlLis
99123
}
100124
}
101125

102-
return createUriListSnippet(document, uris, title, placeHolderValue);
126+
return createUriListSnippet(document, uris, title, placeHolderValue, smartPaste);
103127
}
104128

105129
interface UriListSnippetOptions {
@@ -122,6 +146,7 @@ export function createUriListSnippet(
122146
uris: readonly vscode.Uri[],
123147
title = '',
124148
placeholderValue = 0,
149+
smartPaste = false,
125150
options?: UriListSnippetOptions,
126151
): { snippet: vscode.SnippetString; label: string } | undefined {
127152
if (!uris.length) {
@@ -164,13 +189,21 @@ export function createUriListSnippet(
164189
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
165190
} else {
166191
insertedLinkCount++;
167-
snippet.appendText('[');
168-
snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue);
169-
if (externalUriSchemes.includes(uri.scheme)) {
170-
const uriString = uri.toString(true);
171-
snippet.appendText(`](${uriString})`);
192+
if (smartPaste) {
193+
if (externalUriSchemes.includes(uri.scheme)) {
194+
snippet.appendText(uri.toString(true));
195+
} else {
196+
snippet.appendText(escapeMarkdownLinkPath(mdPath));
197+
}
172198
} else {
173-
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
199+
snippet.appendText('[');
200+
snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue);
201+
if (externalUriSchemes.includes(uri.scheme)) {
202+
const uriString = uri.toString(true);
203+
snippet.appendText(`](${uriString})`);
204+
} else {
205+
snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`);
206+
}
174207
}
175208
}
176209
}

0 commit comments

Comments
 (0)