Skip to content

Commit c39ff79

Browse files
feat(transformers): support zeroIndexed option to transformerMetaHighlight (#1149)
1 parent faf0d46 commit c39ff79

File tree

3 files changed

+69
-15
lines changed

3 files changed

+69
-15
lines changed

packages/transformers/src/transformers/meta-highlight.ts

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@ import type { ShikiTransformer } from '@shikijs/types'
33
export function parseMetaHighlightString(meta: string): number[] | null {
44
if (!meta)
55
return null
6+
67
const match = meta.match(/\{([\d,-]+)\}/)
78
if (!match)
89
return null
10+
911
const lines = match[1]
1012
.split(',')
1113
.flatMap((v) => {
12-
const num = v.split('-').map(v => Number.parseInt(v, 10))
13-
if (num.length === 1)
14-
return [num[0]]
15-
return Array.from({ length: num[1] - num[0] + 1 }, (_, i) => i + num[0])
14+
const range = v.split('-').map(n => Number.parseInt(n, 10))
15+
return range.length === 1
16+
? [range[0]]
17+
: Array.from({ length: range[1] - range[0] + 1 }, (_, i) => range[0] + i)
1618
})
19+
1720
return lines
1821
}
1922

@@ -24,6 +27,12 @@ export interface TransformerMetaHighlightOptions {
2427
* @default 'highlighted'
2528
*/
2629
className?: string
30+
/**
31+
* Interpret line numbers as 0-indexed
32+
*
33+
* @default false
34+
*/
35+
zeroIndexed?: boolean
2736
}
2837

2938
const symbol = Symbol('highlighted-lines')
@@ -34,25 +43,23 @@ const symbol = Symbol('highlighted-lines')
3443
export function transformerMetaHighlight(
3544
options: TransformerMetaHighlightOptions = {},
3645
): ShikiTransformer {
37-
const {
38-
className = 'highlighted',
39-
} = options
46+
const { className = 'highlighted', zeroIndexed = false } = options
4047

4148
return {
4249
name: '@shikijs/transformers:meta-highlight',
43-
line(node, line) {
44-
if (!this.options.meta?.__raw) {
50+
line(node, lineNumber) {
51+
if (!this.options.meta?.__raw)
4552
return
46-
}
47-
const meta = this.meta as {
48-
[symbol]: number[] | null
49-
}
5053

54+
const meta = this.meta as { [symbol]: number[] | null }
5155
meta[symbol] ??= parseMetaHighlightString(this.options.meta.__raw)
52-
const lines: number[] = meta[symbol] ?? []
5356

54-
if (lines.includes(line))
57+
const highlightedLines: number[] = meta[symbol] ?? []
58+
const effectiveLine = zeroIndexed ? lineNumber - 1 : lineNumber
59+
60+
if (highlightedLines.includes(effectiveLine))
5561
this.addClassToHast(node, className)
62+
5663
return node
5764
},
5865
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`transformerMetaHighlight - zeroIndexed behavior > default mode should treat meta as 1-indexed 1`] = `
4+
"<pre class="shiki nord" style="background-color:#2e3440ff;color:#d8dee9ff" tabindex="0"><code><span class="line highlighted"><span style="color:#D8DEE9">console</span><span style="color:#ECEFF4">.</span><span style="color:#88C0D0">log</span><span style="color:#D8DEE9FF">(</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">line 0</span><span style="color:#ECEFF4">"</span><span style="color:#D8DEE9FF">)</span></span>
5+
<span class="line"><span style="color:#D8DEE9">console</span><span style="color:#ECEFF4">.</span><span style="color:#88C0D0">log</span><span style="color:#D8DEE9FF">(</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">line 1</span><span style="color:#ECEFF4">"</span><span style="color:#D8DEE9FF">)</span></span>
6+
<span class="line highlighted"><span style="color:#D8DEE9">console</span><span style="color:#ECEFF4">.</span><span style="color:#88C0D0">log</span><span style="color:#D8DEE9FF">(</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">line 2</span><span style="color:#ECEFF4">"</span><span style="color:#D8DEE9FF">)</span></span>
7+
<span class="line"><span style="color:#D8DEE9">console</span><span style="color:#ECEFF4">.</span><span style="color:#88C0D0">log</span><span style="color:#D8DEE9FF">(</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">line 3</span><span style="color:#ECEFF4">"</span><span style="color:#D8DEE9FF">)</span></span></code></pre>"
8+
`;
9+
10+
exports[`transformerMetaHighlight - zeroIndexed behavior > when zeroIndexed=true it should treat meta as 0-indexed 1`] = `
11+
"<pre class="shiki nord" style="background-color:#2e3440ff;color:#d8dee9ff" tabindex="0"><code><span class="line"><span style="color:#D8DEE9">console</span><span style="color:#ECEFF4">.</span><span style="color:#88C0D0">log</span><span style="color:#D8DEE9FF">(</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">line 0</span><span style="color:#ECEFF4">"</span><span style="color:#D8DEE9FF">)</span></span>
12+
<span class="line highlighted"><span style="color:#D8DEE9">console</span><span style="color:#ECEFF4">.</span><span style="color:#88C0D0">log</span><span style="color:#D8DEE9FF">(</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">line 1</span><span style="color:#ECEFF4">"</span><span style="color:#D8DEE9FF">)</span></span>
13+
<span class="line"><span style="color:#D8DEE9">console</span><span style="color:#ECEFF4">.</span><span style="color:#88C0D0">log</span><span style="color:#D8DEE9FF">(</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">line 2</span><span style="color:#ECEFF4">"</span><span style="color:#D8DEE9FF">)</span></span>
14+
<span class="line highlighted"><span style="color:#D8DEE9">console</span><span style="color:#ECEFF4">.</span><span style="color:#88C0D0">log</span><span style="color:#D8DEE9FF">(</span><span style="color:#ECEFF4">"</span><span style="color:#A3BE8C">line 3</span><span style="color:#ECEFF4">"</span><span style="color:#D8DEE9FF">)</span></span></code></pre>"
15+
`;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { codeToHtml } from 'shiki'
2+
import { describe, expect, it } from 'vitest'
3+
import { transformerMetaHighlight } from '../src'
4+
5+
const input = `
6+
console.log("line 0")
7+
console.log("line 1")
8+
console.log("line 2")
9+
console.log("line 3")
10+
`.trim()
11+
12+
describe('transformerMetaHighlight - zeroIndexed behavior', () => {
13+
it('default mode should treat meta as 1-indexed', async () => {
14+
const html = await codeToHtml(input, {
15+
lang: 'js',
16+
theme: 'nord',
17+
meta: { __raw: '{1,3}' },
18+
transformers: [transformerMetaHighlight()],
19+
})
20+
expect(html).toMatchSnapshot()
21+
})
22+
23+
it('when zeroIndexed=true it should treat meta as 0-indexed', async () => {
24+
const html = await codeToHtml(input, {
25+
lang: 'js',
26+
theme: 'nord',
27+
meta: { __raw: '{1,3}' },
28+
transformers: [transformerMetaHighlight({ zeroIndexed: true })],
29+
})
30+
expect(html).toMatchSnapshot()
31+
})
32+
})

0 commit comments

Comments
 (0)