mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 12:55:51 +00:00
fix: highlight 缓存改用 LRUCache 降低内存开销
- Fallback.tsx: 手动 Map LRU 替换为 lru-cache 的 LRUCache - Markdown.tsx: tokenCache 同样替换为 LRUCache - color-diff-napi: 新增行级 hljs AST 缓存,避免终端 resize 时重复高亮 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -502,6 +502,50 @@ function hasRootNode(emitter: unknown): emitter is { rootNode: HljsNode } {
|
||||
|
||||
let loggedEmitterShapeError = false
|
||||
|
||||
// Per-line hljs AST cache — ColorFile.render re-highlights every line on
|
||||
// width change (terminal resize). The AST is theme-independent; flattenHljs
|
||||
// applies theme colors separately. Capped at 2048 entries (~1 MB typical).
|
||||
const HL_LINE_CACHE_MAX = 2048
|
||||
const hlLineCache = new Map<string, HljsNode | null>()
|
||||
function cachedHljsAst(
|
||||
lang: string,
|
||||
code: string,
|
||||
): HljsNode | null {
|
||||
const key = lang + '\0' + code
|
||||
const hit = hlLineCache.get(key)
|
||||
if (hit !== undefined) return hit
|
||||
let result
|
||||
try {
|
||||
result = hljsApi().highlight(code, {
|
||||
language: lang,
|
||||
ignoreIllegals: true,
|
||||
})
|
||||
} catch {
|
||||
hlLineCache.set(key, null)
|
||||
return null
|
||||
}
|
||||
const emitter = result._emitter || {}
|
||||
if (!hasRootNode(emitter)) {
|
||||
if (!loggedEmitterShapeError) {
|
||||
loggedEmitterShapeError = true
|
||||
logError(
|
||||
new Error(
|
||||
`color-diff: hljs emitter shape mismatch (keys: ${Object.keys(emitter).join(',')}). Syntax highlighting disabled.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
hlLineCache.set(key, null)
|
||||
return null
|
||||
}
|
||||
const node = emitter.rootNode
|
||||
if (hlLineCache.size >= HL_LINE_CACHE_MAX) {
|
||||
const first = hlLineCache.keys().next().value
|
||||
if (first !== undefined) hlLineCache.delete(first)
|
||||
}
|
||||
hlLineCache.set(key, node)
|
||||
return node
|
||||
}
|
||||
|
||||
function highlightLine(
|
||||
state: { lang: string | null; stack: unknown },
|
||||
line: string,
|
||||
@@ -512,30 +556,12 @@ function highlightLine(
|
||||
if (!state.lang) {
|
||||
return [[defaultStyle(theme), code]]
|
||||
}
|
||||
let result
|
||||
try {
|
||||
result = hljsApi().highlight(code, {
|
||||
language: state.lang,
|
||||
ignoreIllegals: true,
|
||||
})
|
||||
} catch {
|
||||
// hljs throws on unknown language despite ignoreIllegals
|
||||
return [[defaultStyle(theme), code]]
|
||||
}
|
||||
const emitter = result._emitter || {};
|
||||
if (!hasRootNode(emitter)) {
|
||||
if (!loggedEmitterShapeError) {
|
||||
loggedEmitterShapeError = true
|
||||
logError(
|
||||
new Error(
|
||||
`color-diff: hljs emitter shape mismatch (keys: ${Object.keys(emitter).join(',')}). Syntax highlighting disabled.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
const rootNode = cachedHljsAst(state.lang, code)
|
||||
if (!rootNode) {
|
||||
return [[defaultStyle(theme), code]]
|
||||
}
|
||||
const blocks: Block[] = []
|
||||
flattenHljs(emitter.rootNode, theme, undefined, blocks)
|
||||
flattenHljs(rootNode, theme, undefined, blocks)
|
||||
return blocks
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user