Skip to content

Commit

Permalink
refactor: shiki sync render
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Sep 5, 2024
1 parent a2c1dce commit 9edef25
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 62 deletions.
2 changes: 1 addition & 1 deletion src/components/modules/shared/CodeBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function formatCode(code: string): string {
// 如果是空行,则直接返回,避免移除空行的非空格字符(例如\t)
return line
} else {
return line.substring(minIndent)
return line.slice(Math.max(0, minIndent))
}
})

Expand Down
8 changes: 3 additions & 5 deletions src/components/ui/code-highlighter/CodeHighlighter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,7 @@ export const ShikiFallback: FC<ShikiProps> = (props) => {
return bundledLanguagesKeysSet.has(lang)
}, [lang]),
)

if (!shikiSupported) {
return <HighLighterPrismCdn {...props} />
}
return <ShikiHighLighter {...props} />
return (
<ShikiHighLighter {...props} lang={shikiSupported ? props.lang : 'text'} />
)
}
4 changes: 4 additions & 0 deletions src/components/ui/code-highlighter/shiki/Shiki.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,8 @@
.scroll-container pre::-webkit-scrollbar {
background-color: transparent !important;
}

pre {
@apply rounded-none;
}
}
84 changes: 37 additions & 47 deletions src/components/ui/code-highlighter/shiki/Shiki.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,40 @@
import { useIsomorphicLayoutEffect } from 'foxact/use-isomorphic-layout-effect'
import type { FC } from 'react'
import { use, useEffect, useMemo, useState } from 'react'
import { use, useMemo, useState } from 'react'
import { createHighlighterCoreSync, createJavaScriptRegexEngine } from 'shiki'
import githubDark from 'shiki/themes/github-dark.mjs'
import githubLight from 'shiki/themes/github-light.mjs'

import { isServerSide } from '~/lib/env'

import { codeHighlighter as shiki } from './core'
import { ShikiHighLighterWrapper } from './ShikiWrapper'
import { parseShouldCollapsedFromAttrs } from './utils'

export interface ShikiProps {
lang: string | undefined
content: string

attrs?: string
}

const codeHighlighterPromise = (async () => {
const codeHighlighter = (() => {
if (isServerSide) return
const [{ createHighlighterCore }, getWasm, { codeHighlighter }] =
await Promise.all([
import('shiki/core'),
import('shiki/wasm').then((m) => m.default),
import('./core'),
])

const core = await createHighlighterCore({
themes: [
import('shiki/themes/github-light.mjs'),
import('shiki/themes/github-dark.mjs'),
],
const js = createJavaScriptRegexEngine()
const core = createHighlighterCoreSync({
themes: [githubDark, githubLight],
langs: [],
loadWasm: getWasm,
engine: js,
})

return {
codeHighlighter: core,
fn: (o: { lang: string; attrs: string; code: string }) => {
return codeHighlighter(core, o)
},
fn: (o: { lang: string; attrs: string; code: string }) => shiki(core, o),
}
})()

export const ShikiHighLighter: FC<ShikiProps> = (props) => {
const { lang: language, content: value, attrs } = props
const codeHighlighter = use(codeHighlighterPromise)

use(
useMemo(async () => {
Expand All @@ -59,45 +52,42 @@ export const ShikiHighLighter: FC<ShikiProps> = (props) => {
const importFn = (bundledLanguages as any)[language]
if (!importFn) return
return loadShikiLanguage(language || '', importFn)
}, [codeHighlighter?.codeHighlighter, language]),
}, [language]),
)
const highlightedHtml = useMemo(
() =>
codeHighlighter?.fn?.({
attrs: attrs || '',
// code: `${value.split('\n')[0].repeat(10)} // [!code highlight]\n${value}`,
code: value,
lang: language ? language.toLowerCase() : '',
}),
[attrs, language, value],
)
const highlightedHtml = useMemo(() => {
return codeHighlighter?.fn?.({
attrs: attrs || '',
// code: `${value.split('\n')[0].repeat(10)} // [!code highlight]\n${value}`,
code: value,
lang: language ? language.toLowerCase() : '',
})
}, [attrs, codeHighlighter, language, value])

const [renderedHtml, setRenderedHtml] = useState(highlightedHtml)
// const [renderedHtml, setRenderedHtml] = useState(highlightedHtml)
const [codeBlockRef, setCodeBlockRef] = useState<HTMLDivElement | null>(null)
useEffect(() => {
setRenderedHtml(highlightedHtml)
requestAnimationFrame(() => {
if (!highlightedHtml) return
if (!codeBlockRef) return
useIsomorphicLayoutEffect(() => {
if (!highlightedHtml) return
if (!codeBlockRef) return

const $lines = codeBlockRef.querySelectorAll('.line')
const maxLineWidth = Math.max(
...Array.from($lines).map((el) => {
return (el as HTMLElement).scrollWidth
}),
)
$lines.forEach((el) => {
;(el as HTMLElement).style.width = `${maxLineWidth}px`
})

const pre = codeBlockRef.querySelector('pre')
if (pre) setRenderedHtml(pre.outerHTML)
const $lines = codeBlockRef.querySelectorAll('.line')
const maxLineWidth = Math.max(
...Array.from($lines).map((el) => (el as HTMLElement).scrollWidth),
)
$lines.forEach((el) => {
;(el as HTMLElement).style.width = `${maxLineWidth}px`
})

// const pre = codeBlockRef.querySelector('pre')
// if (pre) setRenderedHtml(pre.outerHTML)
}, [codeBlockRef, highlightedHtml])

return (
<ShikiHighLighterWrapper
shouldCollapsed={parseShouldCollapsedFromAttrs(attrs || '')}
{...props}
renderedHTML={renderedHtml}
renderedHTML={highlightedHtml}
ref={setCodeBlockRef}
/>
)
Expand Down
4 changes: 1 addition & 3 deletions src/components/ui/code-highlighter/shiki/ShikiWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,7 @@ export const ShikiHighLighterWrapper = forwardRef<
}
}, [value, codeBlockRef])

const filename = useMemo(() => {
return parseFilenameFromAttrs(attrs || '')
}, [attrs])
const filename = useMemo(() => parseFilenameFromAttrs(attrs || ''), [attrs])
const [, maskClassName] = useMaskScrollArea({
element: codeBlockRef!,
size: 'lg',
Expand Down
10 changes: 4 additions & 6 deletions src/components/ui/code-highlighter/shiki/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ export const parseFilenameFromAttrs = (attrs: string) => {
return null
}

export const parseShouldCollapsedFromAttrs = (attrs: string) => {
export const parseShouldCollapsedFromAttrs = (attrs: string) =>
// collapsed
return attrs.includes('collapsed') || !attrs.includes('expand')
}
attrs.includes('collapsed') || !attrs.includes('expand')

// const shikiSupportLangSet = new Set(Object.keys(bundledLanguages))
export const isSupportedShikiLang = (lang: string) => {
export const isSupportedShikiLang = (lang: string) =>
// require esm error, fuck nextjs 14.12.x
// @see https://github.com/vercel/next.js/issues/64434
// return shikiSupportLangSet.has(lang)
return true
}
true

0 comments on commit 9edef25

Please sign in to comment.