fix(editor): 支持完整 Shiki 语言并修复语言标识与图标展示

This commit is contained in:
2026-09-05 17:16:09 +08:00
parent 41bf2c53d4
commit 08fd62e7c5
9 changed files with 356 additions and 79 deletions
+29 -23
View File
@@ -1,18 +1,8 @@
import DOMPurify from 'dompurify'
import { marked } from 'marked'
import { createHighlighterCore } from 'shiki/core'
import { createJavaScriptRegexEngine } from '@shikijs/engine-javascript'
import css from '@shikijs/langs/css'
import c from '@shikijs/langs/c'
import cpp from '@shikijs/langs/cpp'
import html from '@shikijs/langs/html'
import javascript from '@shikijs/langs/javascript'
import json from '@shikijs/langs/json'
import markdown from '@shikijs/langs/markdown'
import python from '@shikijs/langs/python'
import shell from '@shikijs/langs/shellscript'
import sql from '@shikijs/langs/sql'
import typescript from '@shikijs/langs/typescript'
import { createOnigurumaEngine } from 'shiki/engine/oniguruma'
import { bundledLanguagesInfo } from 'shiki/langs'
import githubDark from '@shikijs/themes/github-dark'
import githubLight from '@shikijs/themes/github-light'
@@ -21,30 +11,46 @@ marked.setOptions({ gfm: true, breaks: true })
// Highlighter 是昂贵的单例;复用初始化 Promise,避免每个代码块重复加载语法与主题。
const highlighter = createHighlighterCore({
themes: [githubLight, githubDark],
langs: [markdown, html, css, javascript, typescript, json, python, shell, sql, c, cpp],
engine: createJavaScriptRegexEngine(),
langs: [],
engine: createOnigurumaEngine(import('shiki/wasm')),
})
const languageAliases: Record<string, string> = {
bash: 'shell', js: 'javascript', md: 'markdown', plaintext: 'text', py: 'python', sh: 'shell', ts: 'typescript',
const languageAliases = new Map(bundledLanguagesInfo.flatMap(info =>
[info.id, info.name, ...(info.aliases ?? [])].map(alias => [alias.toLowerCase(), info.id] as const),
))
const languageLoads = new Map<string, Promise<void>>()
const languageLoaders = new Map(bundledLanguagesInfo.map(info => [info.id, info.import]))
async function loadCodeLanguage(requestedLanguage: string) {
const shiki = await highlighter
const language = languageAliases.get(requestedLanguage.toLowerCase())
if (!language) return { shiki, language: 'text' as const }
let loading = languageLoads.get(language)
if (!loading) {
loading = shiki.loadLanguage(languageLoaders.get(language)!).catch(error => {
languageLoads.delete(language)
throw error
})
languageLoads.set(language, loading)
}
await loading
return { shiki, language }
}
export async function highlightCode(source: string, requestedLanguage = 'text'): Promise<string> {
const shiki = await highlighter
const language = languageAliases[requestedLanguage] ?? requestedLanguage
const loadedLanguage = shiki.getLoadedLanguages().includes(language as never) ? language : 'markdown'
const { shiki, language } = await loadCodeLanguage(requestedLanguage)
return shiki.codeToHtml(source, {
lang: loadedLanguage,
lang: language,
themes: { light: 'github-light', dark: 'github-dark' },
defaultColor: false,
})
}
/** Share the initialized grammar/theme registry with editable code blocks. */
export async function getCodeTokenizer(theme: 'github-light' | 'github-dark') {
const shiki = await highlighter
export async function getCodeTokenizer(theme: 'github-light' | 'github-dark', requestedLanguage = 'text') {
const { shiki } = await loadCodeLanguage(requestedLanguage)
return (source: string, requestedLanguage: string) => {
const language = languageAliases[requestedLanguage] ?? requestedLanguage
const language = languageAliases.get(requestedLanguage.toLowerCase()) ?? 'text'
return shiki.codeToTokens(source, {
lang: shiki.getLoadedLanguages().includes(language as never) ? language : 'text',
theme,