fix: improve handling of italics in the presence of links (#11064)

* fix(formatters): don't escape * in links

* add test for * -> * in url

* `\S+` -> `\S*` (tested locally)

* @Qjuh: handle italics both inside+outside <url>s

* more accurate <link> matcher

---------

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
This commit is contained in:
Kendell R
2025-11-09 01:02:25 -08:00
committed by GitHub
parent 3cb3ecae21
commit 9723cc590b
2 changed files with 26 additions and 8 deletions

View File

@@ -25,6 +25,8 @@ const testURLs = [
'https://example.com/name_with_underscores', 'https://example.com/name_with_underscores',
'https://example.com/name__with__underscores', 'https://example.com/name__with__underscores',
'https://example.com/name_with_underscores_and__double__underscores', 'https://example.com/name_with_underscores_and__double__underscores',
'https://*.example.com/globbed/*',
'https://example.com/*before*/>/*after*',
]; ];
describe('Markdown escapers', () => { describe('Markdown escapers', () => {
@@ -89,6 +91,16 @@ describe('Markdown escapers', () => {
test('url', () => { test('url', () => {
for (const url of testURLs) expect(escapeItalic(url)).toBe(url); for (const url of testURLs) expect(escapeItalic(url)).toBe(url);
}); });
test('after-url', () => {
const url = '<https://example.com>';
for (const [input, output] of [
['*hi*', '\\*hi\\*'],
['_hi_', '\\_hi\\_'],
]) {
expect(escapeItalic(url + input)).toBe(url + output);
}
});
}); });
describe('escapeUnderline', () => { describe('escapeUnderline', () => {

View File

@@ -234,15 +234,21 @@ export function escapeInlineCode(text: string): string {
*/ */
export function escapeItalic(text: string): string { export function escapeItalic(text: string): string {
let idx = 0; let idx = 0;
const newText = text.replaceAll(/(?<=^|[^*])\*([^*]|\*\*|$)/g, (_, match) => { const newText = text.replaceAll(
/(?<=^|[^*])(?<!(?<!<)https?:\/\/\S*|<[^\s:]+:\/[^\s>]*)\*([^*]|\*\*|$)/g,
(_, match) => {
if (match === '**') return ++idx % 2 ? `\\*${match}` : `${match}\\*`; if (match === '**') return ++idx % 2 ? `\\*${match}` : `${match}\\*`;
return `\\*${match}`; return `\\*${match}`;
}); },
);
idx = 0; idx = 0;
return newText.replaceAll(/(?<=^|[^_])(?<!<a?:.+|https?:\/\/\S+)_(?!:\d+>)([^_]|__|$)/g, (_, match) => { return newText.replaceAll(
/(?<=^|[^_])(?<!<a?:.+|(?<!<)https?:\/\/\S*|<[^\s:]:\/[^\s>]*)_(?!:\d+>)([^_]|__|$)/g,
(_, match) => {
if (match === '__') return ++idx % 2 ? `\\_${match}` : `${match}\\_`; if (match === '__') return ++idx % 2 ? `\\_${match}` : `${match}\\_`;
return `\\_${match}`; return `\\_${match}`;
}); },
);
} }
/** /**