Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): new structure: inline option for codeToHast #653

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 32 additions & 16 deletions packages/core/src/code-to-hast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,15 @@
const transformers = getTransformers(options)

const lines: (Element | Text)[] = []
const tree: Root = {
const root: Root = {
type: 'root',
children: [],
}

const {
structure = 'classic',
} = options

let preNode: Element = {
type: 'element',
tagName: 'pre',
Expand Down Expand Up @@ -110,6 +114,7 @@

const context: ShikiTransformerContext = {
...transformerContext,
structure,
addClassToHast,
get source() {
return transformerContext.source
Expand All @@ -121,7 +126,7 @@
return options
},
get root() {
return tree
return root

Check warning on line 129 in packages/core/src/code-to-hast.ts

View check run for this annotation

Codecov / codecov/patch

packages/core/src/code-to-hast.ts#L129

Added line #L129 was not covered by tests
},
get pre() {
return preNode
Expand All @@ -135,8 +140,12 @@
}

tokens.forEach((line, idx) => {
if (idx)
lines.push({ type: 'text', value: '\n' })
if (idx) {
if (structure === 'inline')
root.children.push({ type: 'element', tagName: 'br', properties: {}, children: [] })
else if (structure === 'classic')
lines.push({ type: 'text', value: '\n' })
}

let lineNode: Element = {
type: 'element',
Expand All @@ -162,28 +171,35 @@
for (const transformer of transformers)
tokenNode = transformer?.span?.call(context, tokenNode, idx + 1, col, lineNode) || tokenNode

lineNode.children.push(tokenNode)
if (structure === 'inline')
root.children.push(tokenNode)
else if (structure === 'classic')
lineNode.children.push(tokenNode)
col += token.content.length
}

for (const transformer of transformers)
lineNode = transformer?.line?.call(context, lineNode, idx + 1) || lineNode
if (structure === 'classic') {
for (const transformer of transformers)
lineNode = transformer?.line?.call(context, lineNode, idx + 1) || lineNode

lineNodes.push(lineNode)
lines.push(lineNode)
lineNodes.push(lineNode)
lines.push(lineNode)
}
})

for (const transformer of transformers)
codeNode = transformer?.code?.call(context, codeNode) || codeNode
if (structure === 'classic') {
for (const transformer of transformers)
codeNode = transformer?.code?.call(context, codeNode) || codeNode

preNode.children.push(codeNode)
preNode.children.push(codeNode)

for (const transformer of transformers)
preNode = transformer?.pre?.call(context, preNode) || preNode
for (const transformer of transformers)
preNode = transformer?.pre?.call(context, preNode) || preNode

tree.children.push(preNode)
root.children.push(preNode)
}

let result = tree
let result = root
for (const transformer of transformers)
result = transformer?.root?.call(context, result) || result

Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ export interface CodeToHastOptionsCommon<Languages extends string = string>
* @default true
*/
mergeWhitespaces?: boolean | 'never'

/**
* The structure of the generated HAST and HTML.
*
* - `classic`: The classic structure with `<pre>` and `<code>` elements, each line wrapped with a `<span class="line">` element.
* - `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.
*
* @default 'classic'
*/
structure?: 'classic' | 'inline'
}

export interface CodeOptionsMeta {
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/types/transformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export interface ShikiTransformerContext extends ShikiTransformerContextSource {
readonly code: Element
readonly lines: Element[]

readonly structure: CodeToHastOptions['structure']

/**
* Utility to append class to a hast node
*
Expand Down
16 changes: 16 additions & 0 deletions packages/shiki/test/hast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ describe('should', () => {
<span class="line"><span style="color:#B07D48">foo</span><span style="color:#999999">.</span><span style="color:#B07D48">bar</span></span></code></pre>"
`)
})

it('structure inline', async () => {
const shiki = await getHighlighter({
themes: ['vitesse-light'],
langs: ['javascript'],
})

const hast = shiki.codeToHast('console.log\nfoo.bar', {
lang: 'js',
theme: 'vitesse-light',
structure: 'inline',
})

expect(toHtml(hast))
.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>"`)
})
})

it('hasfocus support', async () => {
Expand Down
26 changes: 16 additions & 10 deletions packages/twoslash/src/renderer-rich.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,16 +223,22 @@ export function rendererRich(options: RendererRichOptions = {}): TwoslashRendere

const popupContents: ElementContent[] = []

const typeCode = ((this.codeToHast(
content,
{
...this.options,
transformers: [],
lang: (this.options.lang === 'tsx' || this.options.lang === 'jsx')
? 'tsx'
: 'ts',
},
).children[0] as Element).children as Element[])[0]
const typeCode: Element = {
type: 'element',
tagName: 'code',
properties: {},
children: this.codeToHast(
content,
{
...this.options,
transformers: [],
lang: (this.options.lang === 'tsx' || this.options.lang === 'jsx')
? 'tsx'
: 'ts',
structure: 'inline',
},
).children as ElementContent[],
}
typeCode.properties.class = 'twoslash-popup-code'

popupContents.push(
Expand Down
4 changes: 2 additions & 2 deletions packages/twoslash/test/out/completion-end-multifile-2.ts.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.