Skip to content

Commit ba679ea

Browse files
committed
feat(transformers): render-whitepsace support position option
1 parent 252c2ca commit ba679ea

10 files changed

+152
-38
lines changed

packages/shikiji-transformers/src/transformers/render-whitespace.ts

+53-16
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@ export interface TransformerRenderWhitespaceOptions {
1818

1919
/**
2020
* Position of rendered whitespace
21-
* @default all positions
21+
* @default all position
2222
*/
23-
positions?: {
24-
startOfLine?: boolean
25-
endOfLine?: boolean
26-
inline?: boolean
27-
}
23+
position?: 'all' | 'boundary' | 'trailing'
2824
}
2925

3026
/**
@@ -39,33 +35,42 @@ export function transformerRenderWhitespace(
3935
'\t': options.classTab ?? 'tab',
4036
}
4137

42-
const keys = Object.keys(classMap)
38+
const position = options.position ?? 'all'
39+
const renderStart = position === 'all' || position === 'boundary'
40+
const renderEnd = position === 'all' || position === 'trailing' || position === 'boundary'
41+
const renderMiddle = position === 'all'
4342

44-
// TODO: support `positions`
43+
const keys = Object.keys(classMap)
4544

4645
return {
4746
name: 'shikiji-transformers:render-whitespace',
47+
// We use `root` hook here to ensure it runs after all other transformers
4848
root(root) {
4949
const pre = root.children[0] as Element
5050
const code = pre.children[0] as Element
5151
code.children.forEach((line) => {
5252
if (line.type !== 'element')
5353
return
54-
const first = line.children[0]
55-
if (!first || first.type !== 'element')
56-
return
57-
const textNode = first.children[0]
58-
if (!textNode || textNode.type !== 'text')
59-
return
60-
line.children = line.children.flatMap((token) => {
54+
const total = line.children.length
55+
line.children = line.children.flatMap((token, idx) => {
6156
if (token.type !== 'element')
6257
return token
58+
if (idx === 0 && !renderStart)
59+
return token
60+
if (idx === total - 1 && !renderEnd)
61+
return token
62+
if (idx > 0 && idx < total - 1 && !renderMiddle)
63+
return token
64+
6365
const node = token.children[0]
6466
if (node.type !== 'text' || !node.value)
6567
return token
6668

6769
// Split by whitespaces
68-
const parts = node.value.split(/([ \t])/).filter(i => i.length)
70+
const parts = mergeSpaces(
71+
node.value.split(/([ \t])/).filter(i => i.length),
72+
position,
73+
)
6974
if (parts.length <= 1)
7075
return token
7176

@@ -87,3 +92,35 @@ export function transformerRenderWhitespace(
8792
},
8893
}
8994
}
95+
96+
function isSpace(part: string) {
97+
return part === ' ' || part === '\t'
98+
}
99+
100+
function mergeSpaces(parts: string[], type: 'all' | 'boundary' | 'trailing') {
101+
if (type === 'all')
102+
return parts
103+
let leftCount = 0
104+
let rightCount = 0
105+
if (type === 'boundary') {
106+
for (let i = 0; i < parts.length; i++) {
107+
if (isSpace(parts[i]))
108+
leftCount++
109+
else
110+
break
111+
}
112+
}
113+
if (type === 'boundary' || type === 'trailing') {
114+
for (let i = parts.length - 1; i >= 0; i--) {
115+
if (isSpace(parts[i]))
116+
rightCount++
117+
else
118+
break
119+
}
120+
}
121+
return [
122+
...parts.slice(0, leftCount),
123+
parts.slice(leftCount, parts.length - rightCount).join(''),
124+
...parts.slice(parts.length - rightCount),
125+
]
126+
}

packages/shikiji-transformers/test/fixtures.test.ts

+35-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function suite(
1818
files: Record<string, string>,
1919
transformers: ShikijiTransformer[],
2020
replace?: (code: string) => string,
21+
outputSuffix = '',
2122
) {
2223
describe(name, () => {
2324
for (const path of Object.keys(files)) {
@@ -37,12 +38,25 @@ function suite(
3738
code = replace(code)
3839

3940
expect(code)
40-
.toMatchFileSnapshot(`${path}.output.html`)
41+
.toMatchFileSnapshot(`${path}${outputSuffix}.output.html`)
4142
})
4243
}
4344
})
4445
}
4546

47+
const CSS_RENDER_WHITESPACE = `
48+
<style>
49+
* { tab-size: 4; }
50+
body { margin: 0; }
51+
.shiki { padding: 1em; }
52+
.tab, .space { position: relative; }
53+
.tab::before { content: "\\21E5"; position: absolute; opacity: 0.3; }
54+
.space::before { content: "\\B7"; position: absolute; opacity: 0.3; }
55+
</style>
56+
`
57+
58+
// ---------
59+
4660
suite(
4761
'diff',
4862
import.meta.glob('./fixtures/diff/*.*', { as: 'raw', eager: true }),
@@ -102,18 +116,27 @@ body { margin: 0; }
102116
)
103117

104118
suite(
105-
'whitespace',
119+
'whitespace:all',
106120
import.meta.glob('./fixtures/whitespace/*.*', { as: 'raw', eager: true }),
107-
[transformerRenderWhitespace()],
108-
code => `${code}
109-
<style>
110-
* { tab-size: 4; }
111-
body { margin: 0; }
112-
.shiki { padding: 1em; }
113-
.tab, .space { position: relative; }
114-
.tab::before { content: "\\21E5"; position: absolute; opacity: 0.3; }
115-
.space::before { content: "\\B7"; position: absolute; opacity: 0.3; }
116-
</style>`,
121+
[transformerRenderWhitespace({ position: 'all' })],
122+
code => `${code}${CSS_RENDER_WHITESPACE}`,
123+
'.all',
124+
)
125+
126+
suite(
127+
'whitespace:boundary',
128+
import.meta.glob('./fixtures/whitespace/*.*', { as: 'raw', eager: true }),
129+
[transformerRenderWhitespace({ position: 'boundary' })],
130+
code => `${code}${CSS_RENDER_WHITESPACE}`,
131+
'.boundary',
132+
)
133+
134+
suite(
135+
'whitespace:trailing',
136+
import.meta.glob('./fixtures/whitespace/*.*', { as: 'raw', eager: true }),
137+
[transformerRenderWhitespace({ position: 'trailing' })],
138+
code => `${code}${CSS_RENDER_WHITESPACE}`,
139+
'.trailing',
117140
)
118141

119142
suite(
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
function block( ) {
2-
space()
3-
table()
2+
space( )
3+
table( )
44
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">function</span><span class="space"> </span><span style="color:#B392F0">block</span><span style="color:#E1E4E8">(</span><span class="space"> </span><span style="color:#E1E4E8">)</span><span class="space"> </span><span style="color:#E1E4E8">{</span></span>
2-
<span class="line"><span class="space"> </span><span class="space"> </span><span style="color:#B392F0">space</span><span style="color:#E1E4E8">()</span></span>
3-
<span class="line"><span class="tab"> </span><span class="tab"> </span><span style="color:#B392F0">table</span><span style="color:#E1E4E8">()</span><span class="space"> </span></span>
2+
<span class="line"><span class="space"> </span><span class="space"> </span><span style="color:#B392F0">space</span><span style="color:#E1E4E8">(</span><span class="space"> </span><span style="color:#E1E4E8">)</span></span>
3+
<span class="line"><span class="tab"> </span><span class="tab"> </span><span style="color:#B392F0">table</span><span style="color:#E1E4E8">(</span><span class="space"> </span><span style="color:#E1E4E8">)</span><span class="space"> </span></span>
44
<span class="line"><span style="color:#E1E4E8">}</span></span>
55
<span class="line"></span></code></pre>
66
<style>
@@ -10,4 +10,4 @@
1010
.tab, .space { position: relative; }
1111
.tab::before { content: "\21E5"; position: absolute; opacity: 0.3; }
1212
.space::before { content: "\B7"; position: absolute; opacity: 0.3; }
13-
</style>
13+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">function</span><span style="color:#B392F0"> block</span><span style="color:#E1E4E8">( ) {</span></span>
2+
<span class="line"><span class="space"> </span><span class="space"> </span><span style="color:#B392F0">space</span><span style="color:#E1E4E8">( )</span></span>
3+
<span class="line"><span class="tab"> </span><span class="tab"> </span><span style="color:#B392F0">table</span><span style="color:#E1E4E8">( )</span><span class="space"> </span></span>
4+
<span class="line"><span style="color:#E1E4E8">}</span></span>
5+
<span class="line"></span></code></pre>
6+
<style>
7+
* { tab-size: 4; }
8+
body { margin: 0; }
9+
.shiki { padding: 1em; }
10+
.tab, .space { position: relative; }
11+
.tab::before { content: "\21E5"; position: absolute; opacity: 0.3; }
12+
.space::before { content: "\B7"; position: absolute; opacity: 0.3; }
13+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">function</span><span style="color:#B392F0"> block</span><span style="color:#E1E4E8">( ) {</span></span>
2+
<span class="line"><span style="color:#B392F0"> space</span><span style="color:#E1E4E8">( )</span></span>
3+
<span class="line"><span style="color:#B392F0"> table</span><span style="color:#E1E4E8">( )</span><span class="space"> </span></span>
4+
<span class="line"><span style="color:#E1E4E8">}</span></span>
5+
<span class="line"></span></code></pre>
6+
<style>
7+
* { tab-size: 4; }
8+
body { margin: 0; }
9+
.shiki { padding: 1em; }
10+
.tab, .space { position: relative; }
11+
.tab::before { content: "\21E5"; position: absolute; opacity: 0.3; }
12+
.space::before { content: "\B7"; position: absolute; opacity: 0.3; }
13+
</style>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
function hello(indentSize, type) {
2-
if (indentSize === 4 && type !== 'tab') {
3-
console.log('Each next indentation will increase on 4 spaces');
2+
if (indentSize === 4 && type !== 'tab') {
3+
console.log('Each next indentation will increase on 4 spaces');
44
}
55
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">function</span><span class="space"> </span><span style="color:#B392F0">hello</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">indentSize</span><span style="color:#E1E4E8">,</span><span class="space"> </span><span style="color:#FFAB70">type</span><span style="color:#E1E4E8">)</span><span class="space"> </span><span style="color:#E1E4E8">{</span></span>
2-
<span class="line"><span class="space"> </span><span class="space"> </span><span style="color:#F97583">if</span><span class="space"> </span><span style="color:#E1E4E8">(indentSize</span><span class="space"> </span><span style="color:#F97583">===</span><span class="space"> </span><span style="color:#79B8FF">4</span><span class="space"> </span><span style="color:#F97583">&#x26;&#x26;</span><span class="space"> </span><span style="color:#E1E4E8">type</span><span class="space"> </span><span style="color:#F97583">!==</span><span class="space"> </span><span style="color:#9ECBFF">'tab'</span><span style="color:#E1E4E8">)</span><span class="space"> </span><span style="color:#E1E4E8">{</span></span>
3-
<span class="line"><span class="space"> </span><span class="space"> </span><span class="space"> </span><span class="space"> </span><span class="space"> </span><span class="space"> </span><span style="color:#E1E4E8">console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'Each</span><span class="space"> </span><span style="color:#9ECBFF">next</span><span class="space"> </span><span style="color:#9ECBFF">indentation</span><span class="space"> </span><span style="color:#9ECBFF">will</span><span class="space"> </span><span style="color:#9ECBFF">increase</span><span class="space"> </span><span style="color:#9ECBFF">on</span><span class="space"> </span><span style="color:#9ECBFF">4</span><span class="space"> </span><span style="color:#9ECBFF">spaces'</span><span style="color:#E1E4E8">);</span></span>
2+
<span class="line"><span class="space"> </span><span class="space"> </span><span style="color:#F97583">if</span><span class="space"> </span><span style="color:#E1E4E8">(indentSize</span><span class="space"> </span><span style="color:#F97583">===</span><span class="space"> </span><span style="color:#79B8FF">4</span><span class="space"> </span><span style="color:#F97583">&#x26;&#x26;</span><span class="space"> </span><span style="color:#E1E4E8">type</span><span class="space"> </span><span style="color:#F97583">!==</span><span class="space"> </span><span style="color:#9ECBFF">'tab'</span><span style="color:#E1E4E8">)</span><span class="space"> </span><span style="color:#E1E4E8">{</span><span class="space"> </span></span>
3+
<span class="line"><span class="space"> </span><span class="space"> </span><span class="space"> </span><span class="space"> </span><span class="tab"> </span><span style="color:#E1E4E8">console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'Each</span><span class="space"> </span><span style="color:#9ECBFF">next</span><span class="space"> </span><span style="color:#9ECBFF">indentation</span><span class="space"> </span><span style="color:#9ECBFF">will</span><span class="space"> </span><span style="color:#9ECBFF">increase</span><span class="space"> </span><span style="color:#9ECBFF">on</span><span class="space"> </span><span style="color:#9ECBFF">4</span><span class="space"> </span><span style="color:#9ECBFF">spaces'</span><span style="color:#E1E4E8">);</span><span class="space"> </span><span class="space"> </span><span class="space"> </span></span>
44
<span class="line"><span class="space"> </span><span class="space"> </span><span style="color:#E1E4E8">}</span></span>
55
<span class="line"><span style="color:#E1E4E8">}</span></span>
66
<span class="line"></span></code></pre>
@@ -11,4 +11,4 @@
1111
.tab, .space { position: relative; }
1212
.tab::before { content: "\21E5"; position: absolute; opacity: 0.3; }
1313
.space::before { content: "\B7"; position: absolute; opacity: 0.3; }
14-
</style>
14+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">function</span><span style="color:#B392F0"> hello</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">indentSize</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">type</span><span style="color:#E1E4E8">) {</span></span>
2+
<span class="line"><span class="space"> </span><span class="space"> </span><span style="color:#F97583">if</span><span style="color:#E1E4E8"> (indentSize </span><span style="color:#F97583">===</span><span style="color:#79B8FF"> 4</span><span style="color:#F97583"> &#x26;&#x26;</span><span style="color:#E1E4E8"> type </span><span style="color:#F97583">!==</span><span style="color:#9ECBFF"> 'tab'</span><span style="color:#E1E4E8">) {</span><span class="space"> </span></span>
3+
<span class="line"><span class="space"> </span><span class="space"> </span><span class="space"> </span><span class="space"> </span><span class="tab"> </span><span style="color:#E1E4E8">console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'Each next indentation will increase on 4 spaces'</span><span style="color:#E1E4E8">);</span><span class="space"> </span><span class="space"> </span><span class="space"> </span></span>
4+
<span class="line"><span class="space"> </span><span class="space"> </span><span style="color:#E1E4E8">}</span></span>
5+
<span class="line"><span style="color:#E1E4E8">}</span></span>
6+
<span class="line"></span></code></pre>
7+
<style>
8+
* { tab-size: 4; }
9+
body { margin: 0; }
10+
.shiki { padding: 1em; }
11+
.tab, .space { position: relative; }
12+
.tab::before { content: "\21E5"; position: absolute; opacity: 0.3; }
13+
.space::before { content: "\B7"; position: absolute; opacity: 0.3; }
14+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">function</span><span style="color:#B392F0"> hello</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">indentSize</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">type</span><span style="color:#E1E4E8">) {</span></span>
2+
<span class="line"><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> (indentSize </span><span style="color:#F97583">===</span><span style="color:#79B8FF"> 4</span><span style="color:#F97583"> &#x26;&#x26;</span><span style="color:#E1E4E8"> type </span><span style="color:#F97583">!==</span><span style="color:#9ECBFF"> 'tab'</span><span style="color:#E1E4E8">) {</span><span class="space"> </span></span>
3+
<span class="line"><span style="color:#E1E4E8"> console.</span><span style="color:#B392F0">log</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'Each next indentation will increase on 4 spaces'</span><span style="color:#E1E4E8">);</span><span class="space"> </span><span class="space"> </span><span class="space"> </span></span>
4+
<span class="line"><span style="color:#E1E4E8"> }</span></span>
5+
<span class="line"><span style="color:#E1E4E8">}</span></span>
6+
<span class="line"></span></code></pre>
7+
<style>
8+
* { tab-size: 4; }
9+
body { margin: 0; }
10+
.shiki { padding: 1em; }
11+
.tab, .space { position: relative; }
12+
.tab::before { content: "\21E5"; position: absolute; opacity: 0.3; }
13+
.space::before { content: "\B7"; position: absolute; opacity: 0.3; }
14+
</style>

0 commit comments

Comments
 (0)