mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 21:05:51 +00:00
- 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>
92 lines
2.6 KiB
TypeScript
92 lines
2.6 KiB
TypeScript
import { extname } from 'path'
|
|
import React, { Suspense, use, useMemo } from 'react'
|
|
import { Ansi, Text } from '@anthropic/ink'
|
|
import { LRUCache } from 'lru-cache'
|
|
import { getCliHighlightPromise } from '../../utils/cliHighlight.js'
|
|
import { logForDebugging } from '../../utils/debug.js'
|
|
import { convertLeadingTabsToSpaces } from '../../utils/file.js'
|
|
import { hashPair } from '../../utils/hash.js'
|
|
|
|
type Props = {
|
|
code: string
|
|
filePath: string
|
|
dim?: boolean
|
|
skipColoring?: boolean
|
|
}
|
|
|
|
// Module-level highlight cache — hl.highlight() is the hot cost on virtual-
|
|
// scroll remounts. useMemo doesn't survive unmount→remount. Keyed by hash
|
|
// of code+language to avoid retaining full source strings (#24180 RSS fix).
|
|
const hlCache = new LRUCache<string, string>({ max: 500 })
|
|
function cachedHighlight(
|
|
hl: NonNullable<Awaited<ReturnType<typeof getCliHighlightPromise>>>,
|
|
code: string,
|
|
language: string,
|
|
): string {
|
|
const key = hashPair(language, code)
|
|
const hit = hlCache.get(key)
|
|
if (hit !== undefined) return hit
|
|
const out = hl.highlight(code, { language })
|
|
hlCache.set(key, out)
|
|
return out
|
|
}
|
|
|
|
export function HighlightedCodeFallback({
|
|
code,
|
|
filePath,
|
|
dim = false,
|
|
skipColoring = false,
|
|
}: Props): React.ReactElement {
|
|
const codeWithSpaces = convertLeadingTabsToSpaces(code)
|
|
if (skipColoring) {
|
|
return (
|
|
<Text dimColor={dim}>
|
|
<Ansi>{codeWithSpaces}</Ansi>
|
|
</Text>
|
|
)
|
|
}
|
|
const language = extname(filePath).slice(1)
|
|
return (
|
|
<Text dimColor={dim}>
|
|
<Suspense fallback={<Ansi>{codeWithSpaces}</Ansi>}>
|
|
<Highlighted codeWithSpaces={codeWithSpaces} language={language} />
|
|
</Suspense>
|
|
</Text>
|
|
)
|
|
}
|
|
|
|
function Highlighted({
|
|
codeWithSpaces,
|
|
language,
|
|
}: {
|
|
codeWithSpaces: string
|
|
language: string
|
|
}): React.ReactElement {
|
|
const hl = use(getCliHighlightPromise())
|
|
const out = useMemo(() => {
|
|
if (!hl) return codeWithSpaces
|
|
let highlightLang = 'markdown'
|
|
if (language) {
|
|
if (hl.supportsLanguage(language)) {
|
|
highlightLang = language
|
|
} else {
|
|
logForDebugging(
|
|
`Language not supported while highlighting code, falling back to markdown: ${language}`,
|
|
)
|
|
}
|
|
}
|
|
try {
|
|
return cachedHighlight(hl, codeWithSpaces, highlightLang)
|
|
} catch (e) {
|
|
if (e instanceof Error && e.message.includes('Unknown language')) {
|
|
logForDebugging(
|
|
`Language not supported while highlighting code, falling back to markdown: ${e}`,
|
|
)
|
|
return cachedHighlight(hl, codeWithSpaces, 'markdown')
|
|
}
|
|
return codeWithSpaces
|
|
}
|
|
}, [codeWithSpaces, language, hl])
|
|
return <Ansi>{out}</Ansi>
|
|
}
|