Skip to content

Commit ae516ae

Browse files
authoredApr 9, 2024··
feat(core): new structure: inline option for codeToHast (#653)
1 parent 71257dd commit ae516ae

35 files changed

+9674
-10550
lines changed
 

‎packages/core/src/code-to-hast.ts

+32-16
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,15 @@ export function tokensToHast(
7777
const transformers = getTransformers(options)
7878

7979
const lines: (Element | Text)[] = []
80-
const tree: Root = {
80+
const root: Root = {
8181
type: 'root',
8282
children: [],
8383
}
8484

85+
const {
86+
structure = 'classic',
87+
} = options
88+
8589
let preNode: Element = {
8690
type: 'element',
8791
tagName: 'pre',
@@ -110,6 +114,7 @@ export function tokensToHast(
110114

111115
const context: ShikiTransformerContext = {
112116
...transformerContext,
117+
structure,
113118
addClassToHast,
114119
get source() {
115120
return transformerContext.source
@@ -121,7 +126,7 @@ export function tokensToHast(
121126
return options
122127
},
123128
get root() {
124-
return tree
129+
return root
125130
},
126131
get pre() {
127132
return preNode
@@ -135,8 +140,12 @@ export function tokensToHast(
135140
}
136141

137142
tokens.forEach((line, idx) => {
138-
if (idx)
139-
lines.push({ type: 'text', value: '\n' })
143+
if (idx) {
144+
if (structure === 'inline')
145+
root.children.push({ type: 'element', tagName: 'br', properties: {}, children: [] })
146+
else if (structure === 'classic')
147+
lines.push({ type: 'text', value: '\n' })
148+
}
140149

141150
let lineNode: Element = {
142151
type: 'element',
@@ -162,28 +171,35 @@ export function tokensToHast(
162171
for (const transformer of transformers)
163172
tokenNode = transformer?.span?.call(context, tokenNode, idx + 1, col, lineNode) || tokenNode
164173

165-
lineNode.children.push(tokenNode)
174+
if (structure === 'inline')
175+
root.children.push(tokenNode)
176+
else if (structure === 'classic')
177+
lineNode.children.push(tokenNode)
166178
col += token.content.length
167179
}
168180

169-
for (const transformer of transformers)
170-
lineNode = transformer?.line?.call(context, lineNode, idx + 1) || lineNode
181+
if (structure === 'classic') {
182+
for (const transformer of transformers)
183+
lineNode = transformer?.line?.call(context, lineNode, idx + 1) || lineNode
171184

172-
lineNodes.push(lineNode)
173-
lines.push(lineNode)
185+
lineNodes.push(lineNode)
186+
lines.push(lineNode)
187+
}
174188
})
175189

176-
for (const transformer of transformers)
177-
codeNode = transformer?.code?.call(context, codeNode) || codeNode
190+
if (structure === 'classic') {
191+
for (const transformer of transformers)
192+
codeNode = transformer?.code?.call(context, codeNode) || codeNode
178193

179-
preNode.children.push(codeNode)
194+
preNode.children.push(codeNode)
180195

181-
for (const transformer of transformers)
182-
preNode = transformer?.pre?.call(context, preNode) || preNode
196+
for (const transformer of transformers)
197+
preNode = transformer?.pre?.call(context, preNode) || preNode
183198

184-
tree.children.push(preNode)
199+
root.children.push(preNode)
200+
}
185201

186-
let result = tree
202+
let result = root
187203
for (const transformer of transformers)
188204
result = transformer?.root?.call(context, result) || result
189205

‎packages/core/src/types/options.ts

+10
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ export interface CodeToHastOptionsCommon<Languages extends string = string>
132132
* @default true
133133
*/
134134
mergeWhitespaces?: boolean | 'never'
135+
136+
/**
137+
* The structure of the generated HAST and HTML.
138+
*
139+
* - `classic`: The classic structure with `<pre>` and `<code>` elements, each line wrapped with a `<span class="line">` element.
140+
* - `inline`: All tokens are rendered as `<span>`, line breaks are rendered as `<br>`. No `<pre>` or `<code>` elements. Default forground and background colors are not applied.
141+
*
142+
* @default 'classic'
143+
*/
144+
structure?: 'classic' | 'inline'
135145
}
136146

137147
export interface CodeOptionsMeta {

‎packages/core/src/types/transformers.ts

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export interface ShikiTransformerContext extends ShikiTransformerContextSource {
3535
readonly code: Element
3636
readonly lines: Element[]
3737

38+
readonly structure: CodeToHastOptions['structure']
39+
3840
/**
3941
* Utility to append class to a hast node
4042
*

‎packages/shiki/test/hast.test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ describe('should', () => {
2222
<span class="line"><span style="color:#B07D48">foo</span><span style="color:#999999">.</span><span style="color:#B07D48">bar</span></span></code></pre>"
2323
`)
2424
})
25+
26+
it('structure inline', async () => {
27+
const shiki = await getHighlighter({
28+
themes: ['vitesse-light'],
29+
langs: ['javascript'],
30+
})
31+
32+
const hast = shiki.codeToHast('console.log\nfoo.bar', {
33+
lang: 'js',
34+
theme: 'vitesse-light',
35+
structure: 'inline',
36+
})
37+
38+
expect(toHtml(hast))
39+
.toMatchInlineSnapshot(`"<span style="color:#B07D48">console</span><span style="color:#999999">.</span><span style="color:#B07D48">log</span><br><span style="color:#B07D48">foo</span><span style="color:#999999">.</span><span style="color:#B07D48">bar</span>"`)
40+
})
2541
})
2642

2743
it('hasfocus support', async () => {

‎packages/twoslash/src/renderer-rich.ts

+16-10
Original file line numberDiff line numberDiff line change
@@ -223,16 +223,22 @@ export function rendererRich(options: RendererRichOptions = {}): TwoslashRendere
223223

224224
const popupContents: ElementContent[] = []
225225

226-
const typeCode = ((this.codeToHast(
227-
content,
228-
{
229-
...this.options,
230-
transformers: [],
231-
lang: (this.options.lang === 'tsx' || this.options.lang === 'jsx')
232-
? 'tsx'
233-
: 'ts',
234-
},
235-
).children[0] as Element).children as Element[])[0]
226+
const typeCode: Element = {
227+
type: 'element',
228+
tagName: 'code',
229+
properties: {},
230+
children: this.codeToHast(
231+
content,
232+
{
233+
...this.options,
234+
transformers: [],
235+
lang: (this.options.lang === 'tsx' || this.options.lang === 'jsx')
236+
? 'tsx'
237+
: 'ts',
238+
structure: 'inline',
239+
},
240+
).children as ElementContent[],
241+
}
236242
typeCode.properties.class = 'twoslash-popup-code'
237243

238244
popupContents.push(

‎packages/twoslash/test/out/completion-end-multifile-2.ts.html

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/completion-end-multifile-2.ts.json

+102-120
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/completion-end-multifile.ts.html

+5-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/completion-end-multifile.ts.json

+1,000-1,100
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/completion-end.ts.html

+8-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/completion-end.ts.json

+2,135-2,336
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/completion-string.ts.json

+222-240
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/highlights.ts.json

+574-637
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/import-vue.ts.json

+582-609
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/markdown-it/highlight-lines.html

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/markdown-it/works.html

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/query-offset.ts.json

+189-216
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/rich/custom-tags.html

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/rich/no-icons.html

+5-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/rich/rich-error-hover.html

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/rich/rich-none-theme.html

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/twoslash/test/out/rich/rich.html

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/vitepress-twoslash/src/renderer-floating-vue.ts

+14-8
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,20 @@ function renderMarkdown(this: ShikiTransformerContextCommon, md: string): Elemen
163163
code: (state, node) => {
164164
const lang = node.lang || ''
165165
if (lang) {
166-
return this.codeToHast(
167-
node.value,
168-
{
169-
...this.options,
170-
transformers: [],
171-
lang,
172-
},
173-
).children[0] as Element
166+
return <Element>{
167+
type: 'element',
168+
tagName: 'code',
169+
properties: {},
170+
children: this.codeToHast(
171+
node.value,
172+
{
173+
...this.options,
174+
transformers: [],
175+
lang,
176+
structure: 'inline',
177+
},
178+
).children,
179+
}
174180
}
175181
return defaultHandlers.code(state, node)
176182
},

0 commit comments

Comments
 (0)
Please sign in to comment.