mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Responding to ds-review-bot round 2 on #662: - LANG_ALIASES is a Map: an assistant-authored fence label like constructor or __proto__ now misses (plain render) instead of resolving an inherited object property and crashing shiki mid-conversation. Test sweeps the inherited-key labels. - The singleton is pre-warmed in a deferred task at plugin boot (the ~120-175ms engine+grammar construction long task moves off the first finalized fence's render); the lazy path remains the correctness fallback, and unref keeps non-browser imports from pinning the loop. Agent Note updated (both languages).
83 lines
3.5 KiB
TypeScript
83 lines
3.5 KiB
TypeScript
/**
|
|
* The client's ONE syntax highlighter: a synchronous fine-grained shiki core
|
|
* (JavaScript regex engine — no oniguruma WASM, bundle-friendly) with an
|
|
* explicit grammar allowlist and a CSS-variables theme. Colors live in the
|
|
* theme package's token sheets as `--shiki-*` custom properties (light and
|
|
* dark blocks), never here — the repo's tokens-only styling rule.
|
|
*
|
|
* Grammars are the set the harness actually renders: TypeScript programs
|
|
* (`run_code` bodies; TS pulls in JS via grammar embedding), shell commands,
|
|
* and JSON payloads. An unknown or absent language falls back to plain text
|
|
* (no highlighting, still monospace) — never an error.
|
|
*/
|
|
|
|
import { createHighlighterCoreSync, createCssVariablesTheme } from 'shiki/core'
|
|
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'
|
|
import langTs from '@shikijs/langs/typescript'
|
|
import langBash from '@shikijs/langs/shellscript'
|
|
import langJson from '@shikijs/langs/json'
|
|
import type { HighlighterCore } from 'shiki/core'
|
|
|
|
/**
|
|
* Language ids (and aliases) the singleton registers; everything else renders
|
|
* plain. A Map, not an object: fence info strings are assistant-authored, so
|
|
* a label like `constructor` or `__proto__` must miss instead of resolving an
|
|
* inherited property and crashing the renderer inside shiki.
|
|
*/
|
|
const LANG_ALIASES = new Map<string, string>([
|
|
['typescript', 'typescript'],
|
|
['ts', 'typescript'],
|
|
['tsx', 'typescript'],
|
|
['javascript', 'typescript'],
|
|
['js', 'typescript'],
|
|
['shellscript', 'shellscript'],
|
|
['bash', 'shellscript'],
|
|
['sh', 'shellscript'],
|
|
['shell', 'shellscript'],
|
|
['zsh', 'shellscript'],
|
|
['json', 'json'],
|
|
['jsonc', 'json'],
|
|
])
|
|
|
|
/** All token colors resolve through `--shiki-*` custom properties (theme package sheets). */
|
|
const cssVariablesTheme = createCssVariablesTheme({
|
|
name: 'css-variables',
|
|
variablePrefix: '--shiki-',
|
|
fontStyle: true,
|
|
})
|
|
|
|
let singleton: HighlighterCore | undefined
|
|
|
|
/** The synchronous highlighter (one instance per document); pre-warmed below, lazy as the fallback. */
|
|
function highlighter(): HighlighterCore {
|
|
singleton ??= createHighlighterCoreSync({
|
|
themes: [cssVariablesTheme],
|
|
langs: [langTs, langBash, langJson],
|
|
engine: createJavaScriptRegexEngine({ forgiving: true }),
|
|
})
|
|
return singleton
|
|
}
|
|
|
|
// Engine + grammar construction costs a long task (~120-175ms); building it
|
|
// during the first finalized fence's render would jank exactly when a stream
|
|
// completes. Warm the singleton in a deferred task at module load (= plugin
|
|
// boot) instead; the lazy path above stays as the correctness fallback for a
|
|
// fence that renders before the timer fires. `unref` (Node-only) keeps a
|
|
// non-browser import from pinning the event loop.
|
|
const warmupTimer = setTimeout(() => { highlighter() }, 0)
|
|
;(warmupTimer as { unref?: () => void }).unref?.()
|
|
|
|
/**
|
|
* Highlight `code` into shiki's HTML (a single `<pre class="shiki">` tree)
|
|
* when `lang` maps to a registered grammar; `undefined` means the caller
|
|
* renders its plain fallback.
|
|
* @param code - the source text.
|
|
* @param lang - the language hint (a markdown fence info string or a fixed caller id).
|
|
* @returns the highlighted HTML, or `undefined` for unknown languages.
|
|
*/
|
|
export function highlightToHtml(code: string, lang: string | undefined): string | undefined {
|
|
const resolved = lang === undefined ? undefined : LANG_ALIASES.get(lang.toLowerCase())
|
|
if (resolved === undefined) return undefined
|
|
return highlighter().codeToHtml(code, { lang: resolved, theme: 'css-variables' })
|
|
}
|