Skip to content

Commit 69518ea

Browse files
refactor(web): highlighted tokens keep their caching without "use memo" hooks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
1 parent 82f40d7 commit 69518ea

1 file changed

Lines changed: 36 additions & 38 deletions

File tree

‎apps/web/src/components/chat/HighlightedTokens.tsx‎

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,6 @@ function syntaxTokenStyle(token: SyntaxToken): CSSProperties {
2929
* Suspends while the grammar loads; wrap it in Suspense with the plain text as
3030
* the fallback.
3131
*/
32-
function useHighlightedLines(
33-
highlighter: Awaited<ReturnType<typeof getSyntaxHighlighterPromise>>,
34-
code: string,
35-
language: string,
36-
embedded: ReadonlyArray<EmbeddedScript>,
37-
theme: "light" | "dark",
38-
) {
39-
"use memo";
40-
const themeName = resolveDiffThemeName(theme);
41-
const tokenize = (text: string, lang: string) =>
42-
highlighter.codeToTokens(text, { lang, theme: themeName }).tokens;
43-
return keyedLines(code, withEmbeddedScripts(code, tokenize(code, language), embedded, tokenize));
44-
}
45-
4632
export function HighlightedTokens({
4733
code,
4834
language,
@@ -58,32 +44,44 @@ export function HighlightedTokens({
5844
}) {
5945
const highlighter = use(getSyntaxHighlighterPromise(language));
6046
for (const script of embedded) use(getSyntaxHighlighterPromise(script.language));
61-
const lines = useHighlightedLines(highlighter, code, language, embedded, theme);
47+
const themeName = resolveDiffThemeName(theme);
48+
const tokenize = (text: string, lang: string) =>
49+
highlighter.codeToTokens(text, { lang, theme: themeName }).tokens;
50+
const lines = keyedLines(
51+
code,
52+
withEmbeddedScripts(code, tokenize(code, language), embedded, tokenize),
53+
);
6254

63-
return lines.map(({ key, tokens, ending }) => (
64-
<span key={key}>
65-
{wordClassName
66-
? wordsOf(tokens).map((part) =>
67-
typeof part === "string" ? (
68-
part
69-
) : (
70-
<span key={part.key} className={wordClassName}>
71-
{part.pieces.map((piece) => (
72-
<span key={piece.key} style={syntaxTokenStyle(piece.token)}>
73-
{piece.text}
55+
// A fragment, not the bare array: the compiler only treats this as a component when it
56+
// returns JSX, since `use` alone does not count as a hook call for that inference.
57+
return (
58+
<>
59+
{lines.map(({ key, tokens, ending }) => (
60+
<span key={key}>
61+
{wordClassName
62+
? wordsOf(tokens).map((part) =>
63+
typeof part === "string" ? (
64+
part
65+
) : (
66+
<span key={part.key} className={wordClassName}>
67+
{part.pieces.map((piece) => (
68+
<span key={piece.key} style={syntaxTokenStyle(piece.token)}>
69+
{piece.text}
70+
</span>
71+
))}
7472
</span>
75-
))}
76-
</span>
77-
),
78-
)
79-
: tokens.map((token) => (
80-
<span key={`${token.offset}:${token.content}`} style={syntaxTokenStyle(token)}>
81-
{token.content}
82-
</span>
83-
))}
84-
{ending}
85-
</span>
86-
));
73+
),
74+
)
75+
: tokens.map((token) => (
76+
<span key={`${token.offset}:${token.content}`} style={syntaxTokenStyle(token)}>
77+
{token.content}
78+
</span>
79+
))}
80+
{ending}
81+
</span>
82+
))}
83+
</>
84+
);
8785
}
8886

8987
export function withEmbeddedScripts(

0 commit comments

Comments
 (0)