diff --git a/extensions/markdown-language-features/src/commands/insertResource.ts b/extensions/markdown-language-features/src/commands/insertResource.ts index 8977d6ad39e..776899f0ac7 100644 --- a/extensions/markdown-language-features/src/commands/insertResource.ts +++ b/extensions/markdown-language-features/src/commands/insertResource.ts @@ -60,7 +60,7 @@ export class InsertImageFromWorkspace implements Command { } function getDefaultUri(document: vscode.TextDocument) { - const docUri = getParentDocumentUri(document); + const docUri = getParentDocumentUri(document.uri); if (docUri.scheme === Schemes.untitled) { return vscode.workspace.workspaceFolders?.[0]?.uri; } @@ -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, smartPaste = false, isExternalLink = false) { +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, smartPaste, isExternalLink, { + const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, pasteAsMarkdownLink, isExternalLink, { insertAsMedia, placeholderText: selectionText, placeholderStartIndex: (i + 1) * selectedFiles.length, diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts index cb6a77e8c8d..c8f44fad2bc 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts @@ -81,7 +81,7 @@ export class NewFilePathGenerator { } function getDesiredNewFilePath(config: CopyFileConfiguration, document: vscode.TextDocument, file: vscode.DataTransferFile): vscode.Uri { - const docUri = getParentDocumentUri(document); + const docUri = getParentDocumentUri(document.uri); for (const [rawGlob, rawDest] of Object.entries(config.destination)) { for (const glob of parseGlob(rawGlob)) { if (picomatch.isMatch(docUri.path, glob, { dot: true })) { diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts index 73839caeba2..727216c02bc 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { Schemes } from '../../util/schemes'; -import { createEditForMediaFiles, createEditAddingLinksForUriList, mediaMimes } from './shared'; +import { createEditForMediaFiles, createEditAddingLinksForUriList, mediaMimes, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink } from './shared'; class PasteEditProvider implements vscode.DocumentPasteEditProvider { @@ -32,7 +32,8 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider { if (!urlList) { return; } - const pasteEdit = await createEditAddingLinksForUriList(document, ranges, urlList, token, false); + const pasteUrlSetting = await getPasteUrlAsFormattedLinkSetting(document); + const pasteEdit = await createEditAddingLinksForUriList(document, ranges, urlList, false, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token); if (!pasteEdit) { return; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts index 61aa3a12067..e596e0c2c82 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { externalUriSchemes, createEditAddingLinksForUriList } from './shared'; +import { externalUriSchemes, createEditAddingLinksForUriList, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink } from './shared'; class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { readonly id = 'insertMarkdownLink'; @@ -14,8 +14,8 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken, ): Promise { - const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'smart'); - if (enabled === 'never') { + const pasteUrlSetting = await getPasteUrlAsFormattedLinkSetting(document); + if (pasteUrlSetting === PasteUrlAsFormattedLink.Never) { return; } @@ -26,7 +26,7 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { return; } - if (!validateLink(urlList)) { + if (!validateLink(urlList).isValid) { return; } @@ -34,7 +34,8 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { if (!urlList) { return undefined; } - const pasteEdit = await createEditAddingLinksForUriList(document, ranges, urlList, token, true); + + const pasteEdit = await createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token); if (!pasteEdit) { return; } @@ -45,12 +46,20 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { } } -export function validateLink(urlList: string): boolean { - const url = urlList?.split(/\s+/); - if (url.length > 1 || !externalUriSchemes.includes(vscode.Uri.parse(url[0]).scheme)) { - return false; +export function validateLink(urlList: string): { isValid: boolean; cleanedUrlList: string } { + let isValid = false; + let uri = undefined; + const trimmedUrlList = urlList?.trim(); //remove leading and trailing whitespace and new lines + try { + uri = vscode.Uri.parse(trimmedUrlList); + } catch (error) { + return { isValid: false, cleanedUrlList: urlList }; } - return true; + const splitUrlList = trimmedUrlList.split(' ').filter(item => item !== ''); //split on spaces and remove empty strings + if (uri) { + isValid = splitUrlList.length === 1 && !splitUrlList[0].includes('\n') && externalUriSchemes.includes(vscode.Uri.parse(splitUrlList[0]).scheme); + } + return { isValid, cleanedUrlList: splitUrlList[0] }; } export function registerLinkPasteSupport(selector: vscode.DocumentSelector,) { diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index a6072fa5937..1a0d4f01ae7 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -21,7 +21,6 @@ export const externalUriSchemes = [ 'http', 'https', 'mailto', - 'ftp', ]; export const mediaFileExtensions = new Map([ @@ -64,14 +63,21 @@ export const mediaMimes = new Set([ ]); const smartPasteRegexes = [ - { regex: /\[.*\]\(.*\)/g, is_markdown_link: true }, // Is a Markdown Link - { regex: /!\[.*\]\(.*\)/g, is_markdown_link: true }, // Is a Markdown Image Link - { regex: /\[([^\]]*)\]\(([^)]*)\)/g, is_markdown_link: false }, // In a Markdown link - { regex: /^```[\s\S]*?```$/gm, is_markdown_link: false }, // In a fenced code block - { regex: /^\$\$[\s\S]*?\$\$$/gm, is_markdown_link: false }, // In a fenced math block - { regex: /`[^`]*`/g, is_markdown_link: false }, // In inline code - { regex: /\$[^$]*\$/g, is_markdown_link: false }, // In inline math + { regex: /\[.*\]\(.*\)/g, isMarkdownLink: true, isInline: true }, // Is a Markdown Link + { regex: /!\[.*\]\(.*\)/g, isMarkdownLink: true, isInline: true }, // Is a Markdown Image Link + { regex: /\[([^\]]*)\]\(([^)]*)\)/g, isMarkdownLink: false, isInline: true }, // In a Markdown link + { regex: /^```[\s\S]*?```$/gm, isMarkdownLink: false, isInline: false }, // In a fenced code block + { regex: /^\$\$[\s\S]*?\$\$$/gm, isMarkdownLink: false, isInline: false }, // In a fenced math block + { regex: /`[^`]*`/g, isMarkdownLink: false, isInline: true }, // In inline code + { regex: /\$[^$]*\$/g, isMarkdownLink: false, isInline: true }, // In inline math ]; + +export interface SkinnyTextDocument { + offsetAt(position: vscode.Position): number; + getText(range?: vscode.Range): string; + readonly uri: vscode.Uri; +} + export interface SmartPaste { /** @@ -86,22 +92,43 @@ export interface SmartPaste { } -export async function createEditAddingLinksForUriList(document: vscode.TextDocument, ranges: readonly vscode.Range[], urlList: string, token: vscode.CancellationToken, isExternalLink: boolean): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string } | undefined> { +export enum PasteUrlAsFormattedLink { + Always = 'always', + Smart = 'smart', + Never = 'never' +} + +export async function getPasteUrlAsFormattedLinkSetting(document: vscode.TextDocument): Promise { + return vscode.workspace.getConfiguration('markdown', document).get('editor.pasteUrlAsFormattedLink.enabled', PasteUrlAsFormattedLink.Smart); +} + +export async function createEditAddingLinksForUriList( + document: SkinnyTextDocument, + ranges: readonly vscode.Range[], + urlList: string, + isExternalLink: boolean, + useSmartPaste: boolean, + token: vscode.CancellationToken, +): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string } | undefined> { + 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 = { pasteAsMarkdownLink: true, updateTitle: false }; - for (let i = 0; i < ranges.length; i++) { + for (const range of ranges) { + let title = document.getText(range); + const selectedRange: vscode.Range = new vscode.Range( + new vscode.Position(range.start.line, document.offsetAt(range.start)), + new vscode.Position(range.end.line, document.offsetAt(range.end)) + ); - let title = document.getText(ranges[i]); - if (enabled === 'smart') { - smartPaste = checkSmartPaste(document.getText(), document.offsetAt(ranges[i].start), document.offsetAt(ranges[i].end)); - title = smartPaste.updateTitle ? '' : document.getText(ranges[i]); + if (useSmartPaste) { + smartPaste = checkSmartPaste(document, selectedRange); + title = smartPaste.updateTitle ? '' : document.getText(range); } const snippet = await tryGetUriListSnippet(document, urlList, token, title, placeHolderValue, smartPaste.pasteAsMarkdownLink, isExternalLink); @@ -111,7 +138,7 @@ export async function createEditAddingLinksForUriList(document: vscode.TextDocum smartPaste.pasteAsMarkdownLink = true; placeHolderValue--; - edits.push(new vscode.SnippetTextEdit(ranges[i], snippet.snippet)); + edits.push(new vscode.SnippetTextEdit(range, snippet.snippet)); label = snippet.label; } @@ -121,15 +148,15 @@ export async function createEditAddingLinksForUriList(document: vscode.TextDocum return { additionalEdits, label }; } -export function checkSmartPaste(documentText: string, start: number, end: number): SmartPaste { +export function checkSmartPaste(document: SkinnyTextDocument, selectedRange: vscode.Range): SmartPaste { const SmartPaste: SmartPaste = { pasteAsMarkdownLink: true, updateTitle: false }; for (const regex of smartPasteRegexes) { - const matches = [...documentText.matchAll(regex.regex)]; + const matches = [...document.getText().matchAll(regex.regex)]; for (const match of matches) { if (match.index !== undefined) { - const useDefaultPaste = start > match.index && end < match.index + match[0].length; + const useDefaultPaste = selectedRange.start.character > match.index && selectedRange.end.character < match.index + match[0].length; SmartPaste.pasteAsMarkdownLink = !useDefaultPaste; - SmartPaste.updateTitle = regex.is_markdown_link && start === match.index && end === match.index + match[0].length; + SmartPaste.updateTitle = regex.isMarkdownLink && selectedRange.start.character === match.index && selectedRange.end.character === match.index + match[0].length; if (!SmartPaste.pasteAsMarkdownLink || SmartPaste.updateTitle) { return SmartPaste; } @@ -139,7 +166,7 @@ export function checkSmartPaste(documentText: string, start: number, end: number return SmartPaste; } -export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> { +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; } @@ -171,7 +198,8 @@ interface UriListSnippetOptions { readonly separator?: string; } -export function createLinkSnippet( +export function appendToLinkSnippet( + snippet: vscode.SnippetString, pasteAsMarkdownLink: boolean, mdPath: string, title: string, @@ -180,7 +208,6 @@ export function createLinkSnippet( isExternalLink: boolean, ): vscode.SnippetString { const uriString = uri.toString(true); - const snippet = new vscode.SnippetString(); if (pasteAsMarkdownLink) { snippet.appendText('['); snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); @@ -192,7 +219,7 @@ export function createLinkSnippet( } export function createUriListSnippet( - document: vscode.TextDocument, + document: SkinnyTextDocument, uris: readonly vscode.Uri[], title = '', placeholderValue = 0, @@ -204,7 +231,7 @@ export function createUriListSnippet( return; } - const documentDir = getDocumentDir(document); + const documentDir = getDocumentDir(document.uri); let snippet = new vscode.SnippetString(); let insertedLinkCount = 0; @@ -244,7 +271,7 @@ export function createUriListSnippet( } } else { insertedLinkCount++; - snippet = createLinkSnippet(pasteAsMarkdownLink, mdPath, title, uri, placeholderValue, isExternalLink); + snippet = appendToLinkSnippet(snippet, pasteAsMarkdownLink, mdPath, title, uri, placeholderValue, isExternalLink); } if (i < uris.length - 1 && uris.length > 1) { diff --git a/extensions/markdown-language-features/src/test/markdownLink.test.ts b/extensions/markdown-language-features/src/test/markdownLink.test.ts index 9999a61108f..8568f21d87b 100644 --- a/extensions/markdown-language-features/src/test/markdownLink.test.ts +++ b/extensions/markdown-language-features/src/test/markdownLink.test.ts @@ -5,128 +5,215 @@ import * as vscode from 'vscode'; import * as assert from 'assert'; import 'mocha'; -import { checkSmartPaste, createLinkSnippet } from '../languageFeatures/copyFiles/shared'; +import { SkinnyTextDocument, checkSmartPaste, createEditAddingLinksForUriList, appendToLinkSnippet } from '../languageFeatures/copyFiles/shared'; import { validateLink } from '../languageFeatures/copyFiles/copyPasteLinks'; - suite('createEditAddingLinksForUriList', () => { - // end to end test of checkSmartPaste & createLinkSnippet - // check multicursor (end to end) + test('Markdown Link Pasting should occur for a valid link (end to end)', async () => { + // createEditAddingLinksForUriList -> checkSmartPaste -> tryGetUriListSnippet -> createUriListSnippet -> createLinkSnippet + + const skinnyDocument: SkinnyTextDocument = { + uri: vscode.Uri.parse('file:///path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return 'hello world!'; }, + // lineAt: function (position: vscode.Position) { + // return { + // lineNumber: 0, + // text: 'hello world!', + // range: new vscode.Range(position, position), + // rangeIncludingLineBreak: new vscode.Range(position, position), + // firstNonWhitespaceCharacterIndex: 0, + // isEmptyOrWhitespace: false + // } as vscode.TextLine; + // } + }; + + const result = await createEditAddingLinksForUriList(skinnyDocument, [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true, new vscode.CancellationTokenSource().token); + // need to check the actual result -> snippet value + assert.strictEqual(result?.label, 'Insert Markdown Link'); + }); suite('validateLink', () => { test('Markdown pasting should occur for a valid link.', () => { - const isLink = validateLink('https://www.microsoft.com'); + const isLink = validateLink('https://www.microsoft.com/').isValid; + assert.strictEqual(isLink, true); + }); + + test('Markdown pasting should occur for a valid link preceded by a new line.', () => { + const isLink = validateLink('\r\nhttps://www.microsoft.com/').isValid; + assert.strictEqual(isLink, true); + }); + + test('Markdown pasting should occur for a valid link followed by a new line.', () => { + const isLink = validateLink('https://www.microsoft.com/\r\n').isValid; assert.strictEqual(isLink, true); }); test('Markdown pasting should not occur for a valid hostname and invalid protool.', () => { - const isLink = validateLink('invalid://www.microsoft.com'); + const isLink = validateLink('invalid:www.microsoft.com').isValid; assert.strictEqual(isLink, false); }); test('Markdown pasting should not occur for plain text.', () => { - const isLink = validateLink('hello world!'); + const isLink = validateLink('hello world!').isValid; assert.strictEqual(isLink, false); }); test('Markdown pasting should not occur for plain text including a colon.', () => { - const isLink = validateLink('hello: world!'); + const isLink = validateLink('hello: world!').isValid; assert.strictEqual(isLink, false); }); test('Markdown pasting should not occur for plain text including a slashes.', () => { - const isLink = validateLink('hello//world!'); + const isLink = validateLink('helloworld!').isValid; assert.strictEqual(isLink, false); }); test('Markdown pasting should not occur for a link followed by text.', () => { - const isLink = validateLink('https://www.microsoft.com hello world!'); + const isLink = validateLink('https://www.microsoft.com/ hello world!').isValid; assert.strictEqual(isLink, false); }); - test('Markdown pasting should not occur for a link preceded or followed by spaces.', () => { - const isLink = validateLink(' https://www.microsoft.com '); - assert.strictEqual(isLink, false); + test('Markdown pasting should occur for a link preceded or followed by spaces.', () => { + const isLink = validateLink(' https://www.microsoft.com/ ').isValid; + assert.strictEqual(isLink, true); }); test('Markdown pasting should not occur for a link with an invalid scheme.', () => { - const isLink = validateLink('hello://www.microsoft.com'); + const isLink = validateLink('hello:www.microsoft.com').isValid; assert.strictEqual(isLink, false); }); test('Markdown pasting should not occur for multiple links being pasted.', () => { - const isLink = validateLink('https://www.microsoft.com\r\nhttps://www.microsoft.com\r\nhttps://www.microsoft.com\r\nhttps://www.microsoft.com'); + const isLink = validateLink('https://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/').isValid; assert.strictEqual(isLink, false); }); + test('Markdown pasting should not occur for multiple links with spaces being pasted.', () => { + const isLink = validateLink('https://www.microsoft.com/ \r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\n hello \r\nhttps://www.microsoft.com/').isValid; + assert.strictEqual(isLink, false); + }); }); - suite('createLinkSnippet', () => { + suite('appendToLinkSnippet', () => { test('Should not create Markdown link snippet when pasteAsMarkdownLink is false', () => { - const uri = vscode.Uri.parse('https://www.microsoft.com'); - const snippet = createLinkSnippet(false, 'https://www.microsoft.com', '', uri, 0, true); + const uri = vscode.Uri.parse('https://www.microsoft.com/'); + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), false, 'https:/www.microsoft.com', '', uri, 0, true); assert.strictEqual(snippet?.value, 'https://www.microsoft.com/'); }); test('Should create Markdown link snippet when pasteAsMarkdownLink is true', () => { - const uri = vscode.Uri.parse('https://www.microsoft.com'); - const snippet = createLinkSnippet(true, 'https://www.microsoft.com', '', uri, 0, true); + const uri = vscode.Uri.parse('https://www.microsoft.com/'); + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', '', uri, 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 uri = vscode.Uri.parse('https://www.microsoft.com'); - const snippet = createLinkSnippet(true, 'https://www.microsoft.com', '', uri, 0, true); + const uri = vscode.Uri.parse('https://www.microsoft.com/'); + const snippet = appendToLinkSnippet(new vscode.SnippetString(''), true, 'https:/www.microsoft.com', '', uri, 0, true); assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com/)'); }); }); - suite('pasteAsMarkdownLink', () => { + + suite('checkSmartPaste', () => { + + const skinnyDocument: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return 'hello world!'; }, + // lineAt: function (position: vscode.Position) { + // return { + // lineNumber: 0, + // text: 'hello world!', + // range: new vscode.Range(position, position), + // rangeIncludingLineBreak: new vscode.Range(position, position), + // firstNonWhitespaceCharacterIndex: 0, + // isEmptyOrWhitespace: false + // } as vscode.TextLine; + // } + }; test('Should evaluate pasteAsMarkdownLink as true for selected plain text', () => { - const smartPaste = checkSmartPaste("hello! world", 0, 5); + const range = new vscode.Range(0, 5, 0, 5); + const smartPaste = checkSmartPaste(skinnyDocument, range); assert.strictEqual(smartPaste.pasteAsMarkdownLink, true); }); - - test('Should evaluate updateTitle as true for pasting over a Markdown link', () => { - const smartPaste = checkSmartPaste("[a](bc)", 0, 7); - assert.strictEqual(smartPaste.updateTitle, true); - }); - - test('Should evaluate updateTitle as true for pasting over a Markdown image link', () => { - const smartPaste = checkSmartPaste("![a](bc)", 0, 8); - assert.strictEqual(smartPaste.updateTitle, true); - }); - - test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown link', () => { - const smartPaste = checkSmartPaste("[a](bcdef)", 4, 6); - assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); - }); - - test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown image link', () => { - const smartPaste = checkSmartPaste('![alt](https://)', 7, 15); - assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); - }); - test('Should evaluate pasteAsMarkdownLink as false for pasting within a code block', () => { - const smartPaste = checkSmartPaste('```\r\n\r\n```', 5, 5); - assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); - }); - - test('Should evaluate pasteAsMarkdownLink as false for pasting within inline code', () => { - const smartPaste = checkSmartPaste('``', 1, 1); + skinnyDocument.getText = function () { return '```\r\n\r\n```'; }; + const range = new vscode.Range(0, 5, 0, 5); + const smartPaste = checkSmartPaste(skinnyDocument, range); assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); }); test('Should evaluate pasteAsMarkdownLink as false for pasting within a math block', () => { - const smartPaste = checkSmartPaste('$$$\r\n\r\n$$$', 5, 5); + skinnyDocument.getText = function () { return '$$$\r\n\r\n$$$'; }; + const range = new vscode.Range(0, 5, 0, 5); + const smartPaste = checkSmartPaste(skinnyDocument, range); assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); }); + const linkSkinnyDoc: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return '[a](bcdef)'; }, + }; + + test('Should evaluate updateTitle as true for pasting over a Markdown link', () => { + const range = new vscode.Range(0, 0, 0, 10); + const smartPaste = checkSmartPaste(linkSkinnyDoc, range); + assert.strictEqual(smartPaste.updateTitle, true); + }); + + test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown link', () => { + const range = new vscode.Range(0, 4, 0, 6); + const smartPaste = checkSmartPaste(linkSkinnyDoc, range); + + assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); + }); + + + const imageLinkSkinnyDoc: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return '![a](bcdef)'; }, + }; + + test('Should evaluate updateTitle as true for pasting over a Markdown image link', () => { + const range = new vscode.Range(0, 0, 0, 11); + const smartPaste = checkSmartPaste(imageLinkSkinnyDoc, range); + assert.strictEqual(smartPaste.updateTitle, true); + }); + + test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown image link', () => { + const range = new vscode.Range(0, 5, 0, 10); + const smartPaste = checkSmartPaste(imageLinkSkinnyDoc, range); + assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); + }); + + const inlineCodeSkinnyCode: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return '``'; }, + }; + + test('Should evaluate pasteAsMarkdownLink as false for pasting within inline code', () => { + const range = new vscode.Range(0, 1, 0, 1); + const smartPaste = checkSmartPaste(inlineCodeSkinnyCode, range); + assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); + }); + + const inlineMathSkinnyDoc: SkinnyTextDocument = { + uri: vscode.Uri.file('/path/to/your/file'), + offsetAt: function () { return 0; }, + getText: function () { return '$$'; }, + }; + test('Should evaluate pasteAsMarkdownLink as false for pasting within inline math', () => { - const smartPaste = checkSmartPaste('$$', 1, 1); + const range = new vscode.Range(0, 1, 0, 1); + const smartPaste = checkSmartPaste(inlineMathSkinnyDoc, range); assert.strictEqual(smartPaste.pasteAsMarkdownLink, false); }); }); diff --git a/extensions/markdown-language-features/src/util/document.ts b/extensions/markdown-language-features/src/util/document.ts index 9c192227ee3..856226a7376 100644 --- a/extensions/markdown-language-features/src/util/document.ts +++ b/extensions/markdown-language-features/src/util/document.ts @@ -7,24 +7,24 @@ import * as vscode from 'vscode'; import { Schemes } from './schemes'; import { Utils } from 'vscode-uri'; -export function getDocumentDir(document: vscode.TextDocument): vscode.Uri | undefined { - const docUri = getParentDocumentUri(document); +export function getDocumentDir(uri: vscode.Uri): vscode.Uri | undefined { + const docUri = getParentDocumentUri(uri); if (docUri.scheme === Schemes.untitled) { return vscode.workspace.workspaceFolders?.[0]?.uri; } return Utils.dirname(docUri); } -export function getParentDocumentUri(document: vscode.TextDocument): vscode.Uri { - if (document.uri.scheme === Schemes.notebookCell) { +export function getParentDocumentUri(uri: vscode.Uri): vscode.Uri { + if (uri.scheme === Schemes.notebookCell) { for (const notebook of vscode.workspace.notebookDocuments) { for (const cell of notebook.getCells()) { - if (cell.document === document) { + if (cell.document.uri.toString() === uri.toString()) { return notebook.uri; } } } } - return document.uri; + return uri; }