diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts index 68dca194702..4776ccd2599 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts @@ -124,6 +124,8 @@ export async function shouldInsertMarkdownLinkByDefault( } } +const textTokenTypes = new Set(['paragraph_open', 'inline', 'heading_open', 'ordered_list_open', 'bullet_list_open', 'list_item_open', 'blockquote_open']); + async function shouldSmartPasteForSelection( parser: IMdParser, document: ITextDocument, @@ -150,9 +152,29 @@ async function shouldSmartPasteForSelection( if (token.isCancellationRequested) { return false; } - for (const token of tokens) { - if (token.map && token.map[0] <= selectedRange.start.line && token.map[1] > selectedRange.start.line) { - if (!['paragraph_open', 'inline', 'heading_open', 'ordered_list_open', 'bullet_list_open', 'list_item_open', 'blockquote_open'].includes(token.type)) { + + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (!token.map) { + continue; + } + if (token.map[0] <= selectedRange.start.line && token.map[1] > selectedRange.start.line) { + if (!textTokenTypes.has(token.type)) { + return false; + } + } + + // Special case for html such as: + // + // + // | + // + // + // In this case pasting will cause the html block to be created even though the cursor is not currently inside a block + if (token.type === 'html_block' && token.map[1] === selectedRange.start.line) { + const nextToken = tokens.at(i + 1); + // The next token does not need to be a html_block, but it must be on the next line + if (nextToken?.map?.[0] === selectedRange.end.line + 1) { return false; } } diff --git a/extensions/markdown-language-features/src/test/markdownLink.test.ts b/extensions/markdown-language-features/src/test/pasteUrl.test.ts similarity index 92% rename from extensions/markdown-language-features/src/test/markdownLink.test.ts rename to extensions/markdown-language-features/src/test/pasteUrl.test.ts index 7b5849e47e2..4835af35e59 100644 --- a/extensions/markdown-language-features/src/test/markdownLink.test.ts +++ b/extensions/markdown-language-features/src/test/pasteUrl.test.ts @@ -203,6 +203,23 @@ suite('createEditAddingLinksForUriList', () => { false); }); + test('Smart should be disabled in html blocks where paste creates the block', async () => { + assert.strictEqual( + await shouldInsertMarkdownLinkByDefault(createNewMarkdownEngine(), makeTestDoc('
\n\n
'), PasteUrlAsMarkdownLink.Smart, [new vscode.Range(1, 0, 1, 0)], noopToken), + false, + 'Between two html tags should be treated as html block'); + + assert.strictEqual( + await shouldInsertMarkdownLinkByDefault(createNewMarkdownEngine(), makeTestDoc('\n\ntext'), PasteUrlAsMarkdownLink.Smart, [new vscode.Range(1, 0, 1, 0)], noopToken), + false, + 'Between opening html tag and text should be treated as html block'); + + assert.strictEqual( + await shouldInsertMarkdownLinkByDefault(createNewMarkdownEngine(), makeTestDoc('
\n\n\n
'), PasteUrlAsMarkdownLink.Smart, [new vscode.Range(1, 0, 1, 0)], noopToken), + true, + 'Extra new line after paste should not be treated as html block'); + }); + test('Smart should be disabled in Markdown links', async () => { assert.strictEqual( await shouldInsertMarkdownLinkByDefault(createNewMarkdownEngine(), makeTestDoc('[a](bcdef)'), PasteUrlAsMarkdownLink.Smart, [new vscode.Range(0, 4, 0, 6)], noopToken), @@ -266,7 +283,5 @@ suite('createEditAddingLinksForUriList', () => { await shouldInsertMarkdownLinkByDefault(createNewMarkdownEngine(), makeTestDoc(' \r\n\r\n'), PasteUrlAsMarkdownLink.SmartWithSelection, [new vscode.Range(0, 0, 0, 7)], noopToken), false); }); - - }); });