From 2fb6e3cf542a9bedda0c69216cba0d6a0b705c12 Mon Sep 17 00:00:00 2001 From: Daniel Pecos Martinez Date: Thu, 16 Apr 2026 13:34:54 +0200 Subject: [PATCH] fix: highlight backtick spans that start at the beginning of a line The highlightSpans pattern required a non-backtick character immediately before the opening backtick, so spans at the start of a line (or the start of a string) were never matched and rendered as plain text. Changed the leading capture group from ([^`]) to (^|[^`]) with the /m flag so that the start-of-line anchor is a valid alternative to a preceding character. Applied the same fix to the custom RegExp path. Closes #2 --- src/__tests__/highlightSpans.test.ts | 50 ++++++++++++++++++++++++++++ src/mdeck/views/slideView.ts | 4 +-- 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/__tests__/highlightSpans.test.ts diff --git a/src/__tests__/highlightSpans.test.ts b/src/__tests__/highlightSpans.test.ts new file mode 100644 index 0000000..207a8de --- /dev/null +++ b/src/__tests__/highlightSpans.test.ts @@ -0,0 +1,50 @@ +import { describe, it, expect } from 'vitest'; + +// Test the highlight-spans regex behaviour directly. +// The bug (issue #2): a backtick span at the very start of a line was not +// recognised because the pattern required a non-backtick character before the +// opening backtick. The fix adds `^` as an alternative via the `m` flag. + +function applyHighlightPattern(html: string): string { + // Mirrors the fixed pattern in highlightBlockSpans (highlightSpans === true). + const pattern = /(^|[^`])`([^`]+?)`/gm; + return html.replace(pattern, (m, e, c) => { + if (e === '\\') return m.slice(1); + return e + `${c}`; + }); +} + +describe('highlightSpans regex (issue #2)', () => { + it('highlights a span preceded by a non-backtick character', () => { + const result = applyHighlightPattern('call `method`(arg)'); + expect(result).toContain('method'); + }); + + it('highlights a span at the very start of a string', () => { + const result = applyHighlightPattern('`highlightedMethod`(arg1, arg2)'); + expect(result).toContain('highlightedMethod'); + }); + + it('highlights a span at the start of a new line', () => { + const html = 'normalMethod(a)\n`highlightedMethod`(b)'; + const result = applyHighlightPattern(html); + expect(result).toContain('highlightedMethod'); + expect(result).toContain('normalMethod(a)'); + }); + + it('does not double-highlight adjacent backtick spans', () => { + const result = applyHighlightPattern('`a` and `b`'); + expect(result).toContain('a'); + expect(result).toContain('b'); + }); + + it('does not highlight a backtick span that is escaped', () => { + const result = applyHighlightPattern('\\`notHighlighted`'); + expect(result).not.toContain('remark-code-span-highlighted'); + }); + + it('does not treat fenced code delimiters (```) as highlight spans', () => { + const result = applyHighlightPattern('```js\ncode\n```'); + expect(result).not.toContain('remark-code-span-highlighted'); + }); +}); diff --git a/src/mdeck/views/slideView.ts b/src/mdeck/views/slideView.ts index e948f0f..a28fc34 100644 --- a/src/mdeck/views/slideView.ts +++ b/src/mdeck/views/slideView.ts @@ -211,10 +211,10 @@ function highlightBlockLines(block: HTMLElement, lines: number[]): void { function highlightBlockSpans(block: HTMLElement, highlightSpans: boolean | RegExp): void { let pattern: RegExp; if (highlightSpans === true) { - pattern = /([^`])`([^`]+?)`/g; + pattern = /(^|[^`])`([^`]+?)`/gm; } else if (highlightSpans instanceof RegExp) { if (!highlightSpans.global) throw new Error('highlightSpans RegExp must have /g flag'); - pattern = new RegExp('([^])' + highlightSpans.source, highlightSpans.flags || 'g'); + pattern = new RegExp('(^|[\\s\\S])' + highlightSpans.source, (highlightSpans.flags || 'g') + 'm'); } else { throw new Error('Illegal value for highlightSpans'); }