|
1 | 1 | import { describe, it, expect } from 'vitest'; |
2 | 2 |
|
3 | | -// Test the highlight-spans regex behaviour directly. |
4 | | -// The bug (issue #2): a backtick span at the very start of a line was not |
5 | | -// recognised because the pattern required a non-backtick character before the |
6 | | -// opening backtick. The fix adds `^` as an alternative via the `m` flag. |
7 | | - |
| 3 | +// Mirror the fixed pattern from slideView.ts highlightBlockSpans. |
| 4 | +// Uses a lookbehind so no preceding character is consumed, fixing: |
| 5 | +// issue #2 – spans at the start of a line were never matched |
| 6 | +// issue #3 – adjacent spans like `foo`(`bar`) incorrectly captured ( |
8 | 7 | function applyHighlightPattern(html: string): string { |
9 | | - // Mirrors the fixed pattern in highlightBlockSpans (highlightSpans === true). |
10 | | - const pattern = /(^|[^`])`([^`]+?)`/gm; |
11 | | - return html.replace(pattern, (m, e, c) => { |
12 | | - if (e === '\\') return m.slice(1); |
13 | | - return e + `<span class="remark-code-span-highlighted">${c}</span>`; |
| 8 | + const pattern = /(?<![`\\])`([^`]+?)`/g; |
| 9 | + return html.replace(pattern, (_m, c) => { |
| 10 | + return `<span class="remark-code-span-highlighted">${c}</span>`; |
14 | 11 | }); |
15 | 12 | } |
16 | 13 |
|
17 | | -describe('highlightSpans regex (issue #2)', () => { |
18 | | - it('highlights a span preceded by a non-backtick character', () => { |
19 | | - const result = applyHighlightPattern('call `method`(arg)'); |
20 | | - expect(result).toContain('<span class="remark-code-span-highlighted">method</span>'); |
21 | | - }); |
| 14 | +describe('highlightSpans regex', () => { |
| 15 | + describe('basic highlighting', () => { |
| 16 | + it('highlights a span preceded by a non-backtick character', () => { |
| 17 | + const result = applyHighlightPattern('call `method`(arg)'); |
| 18 | + expect(result).toContain('<span class="remark-code-span-highlighted">method</span>'); |
| 19 | + expect(result).toContain('(arg)'); |
| 20 | + }); |
22 | 21 |
|
23 | | - it('highlights a span at the very start of a string', () => { |
24 | | - const result = applyHighlightPattern('`highlightedMethod`(arg1, arg2)'); |
25 | | - expect(result).toContain('<span class="remark-code-span-highlighted">highlightedMethod</span>'); |
| 22 | + it('does not treat fenced code delimiters (```) as highlight spans', () => { |
| 23 | + const result = applyHighlightPattern('```js\ncode\n```'); |
| 24 | + expect(result).not.toContain('remark-code-span-highlighted'); |
| 25 | + }); |
26 | 26 | }); |
27 | 27 |
|
28 | | - it('highlights a span at the start of a new line', () => { |
29 | | - const html = 'normalMethod(a)\n`highlightedMethod`(b)'; |
30 | | - const result = applyHighlightPattern(html); |
31 | | - expect(result).toContain('<span class="remark-code-span-highlighted">highlightedMethod</span>'); |
32 | | - expect(result).toContain('normalMethod(a)'); |
33 | | - }); |
| 28 | + describe('issue #2 – span at start of line', () => { |
| 29 | + it('highlights a span at the very start of a string', () => { |
| 30 | + const result = applyHighlightPattern('`highlightedMethod`(arg1, arg2)'); |
| 31 | + expect(result).toContain('<span class="remark-code-span-highlighted">highlightedMethod</span>'); |
| 32 | + }); |
34 | 33 |
|
35 | | - it('does not double-highlight adjacent backtick spans', () => { |
36 | | - const result = applyHighlightPattern('`a` and `b`'); |
37 | | - expect(result).toContain('<span class="remark-code-span-highlighted">a</span>'); |
38 | | - expect(result).toContain('<span class="remark-code-span-highlighted">b</span>'); |
| 34 | + it('highlights a span at the start of a new line', () => { |
| 35 | + const html = 'normalMethod(a)\n`highlightedMethod`(b)'; |
| 36 | + const result = applyHighlightPattern(html); |
| 37 | + expect(result).toContain('<span class="remark-code-span-highlighted">highlightedMethod</span>'); |
| 38 | + expect(result).toContain('normalMethod(a)'); |
| 39 | + }); |
39 | 40 | }); |
40 | 41 |
|
41 | | - it('does not highlight a backtick span that is escaped', () => { |
42 | | - const result = applyHighlightPattern('\\`notHighlighted`'); |
43 | | - expect(result).not.toContain('remark-code-span-highlighted'); |
| 42 | + describe('issue #3 – trailing parenthesis not captured', () => { |
| 43 | + it('does not include ( after closing backtick in the highlighted span', () => { |
| 44 | + const result = applyHighlightPattern('case `class Token`(value)'); |
| 45 | + expect(result).toContain('<span class="remark-code-span-highlighted">class Token</span>'); |
| 46 | + expect(result).toMatch(/<\/span>\(value\)/); |
| 47 | + }); |
| 48 | + |
| 49 | + it('correctly highlights both spans in `foo`(`bar`)', () => { |
| 50 | + const result = applyHighlightPattern('`foo`(`bar`)'); |
| 51 | + expect(result).toContain('<span class="remark-code-span-highlighted">foo</span>'); |
| 52 | + expect(result).toContain('<span class="remark-code-span-highlighted">bar</span>'); |
| 53 | + // ( must not be inside either span |
| 54 | + expect(result).not.toContain('remark-code-span-highlighted">('); |
| 55 | + expect(result).not.toContain('(</span>'); |
| 56 | + }); |
| 57 | + |
| 58 | + it('does not capture ( when it follows the closing backtick with spaces', () => { |
| 59 | + const result = applyHighlightPattern('`method` (arg)'); |
| 60 | + expect(result).toMatch(/<\/span> \(arg\)/); |
| 61 | + }); |
44 | 62 | }); |
45 | 63 |
|
46 | | - it('does not treat fenced code delimiters (```) as highlight spans', () => { |
47 | | - const result = applyHighlightPattern('```js\ncode\n```'); |
48 | | - expect(result).not.toContain('remark-code-span-highlighted'); |
| 64 | + describe('escape handling', () => { |
| 65 | + it('does not highlight a backtick span preceded by a backslash', () => { |
| 66 | + const result = applyHighlightPattern('\\`notHighlighted`'); |
| 67 | + expect(result).not.toContain('remark-code-span-highlighted'); |
| 68 | + }); |
49 | 69 | }); |
50 | 70 | }); |
0 commit comments