Fix code colorizer ansi escape bug. (#21321)

This commit is contained in:
Jacob Richman
2026-03-05 13:43:13 -08:00
committed by GitHub
parent 1ba9d71284
commit e8bc7bea44
4 changed files with 62 additions and 5 deletions

View File

@@ -50,4 +50,36 @@ describe('colorizeCode', () => {
expect(lastFrame()).toMatch(/line 1\s*\n\s*\n\s*line 3/);
unmount();
});
it('does not let colors from ansi escape codes leak into colorized code', async () => {
const code = 'line 1\n\x1b[41mline 2 with red background\x1b[0m\nline 3';
const settings = new LoadedSettings(
{ path: '', settings: {}, originalSettings: {} },
{ path: '', settings: {}, originalSettings: {} },
{
path: '',
settings: { ui: { useAlternateBuffer: true, showLineNumbers: false } },
originalSettings: {
ui: { useAlternateBuffer: true, showLineNumbers: false },
},
},
{ path: '', settings: {}, originalSettings: {} },
true,
[],
);
const result = colorizeCode({
code,
language: 'javascript',
maxWidth: 80,
settings,
hideLineNumbers: true,
});
const renderResult = renderWithProviders(<>{result}</>);
await renderResult.waitUntilReady();
await expect(renderResult).toMatchSvgSnapshot();
renderResult.unmount();
});
});

View File

@@ -14,6 +14,7 @@ import type {
ElementContent,
RootContent,
} from 'hast';
import stripAnsi from 'strip-ansi';
import { themeManager } from '../themes/theme-manager.js';
import type { Theme } from '../themes/theme.js';
import {
@@ -98,16 +99,17 @@ function highlightAndRenderLine(
theme: Theme,
): React.ReactNode {
try {
const strippedLine = stripAnsi(line);
const getHighlightedLine = () =>
!language || !lowlight.registered(language)
? lowlight.highlightAuto(line)
: lowlight.highlight(language, line);
? lowlight.highlightAuto(strippedLine)
: lowlight.highlight(language, strippedLine);
const renderedNode = renderHastNode(getHighlightedLine(), theme, undefined);
return renderedNode !== null ? renderedNode : line;
return renderedNode !== null ? renderedNode : strippedLine;
} catch (_error) {
return line;
return stripAnsi(line);
}
}
@@ -238,7 +240,7 @@ export function colorizeCode({
<Text color={activeTheme.defaultColor}>{`${index + 1}`}</Text>
</Box>
)}
<Text color={activeTheme.colors.Gray}>{line}</Text>
<Text color={activeTheme.colors.Gray}>{stripAnsi(line)}</Text>
</Box>
));

View File

@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="71" viewBox="0 0 920 71">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="71" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="2" fill="#ffffff" textLength="45" lengthAdjust="spacingAndGlyphs">line </text>
<text x="45" y="2" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">1</text>
<text x="0" y="19" fill="#ffffff" textLength="45" lengthAdjust="spacingAndGlyphs">line </text>
<text x="45" y="19" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">2</text>
<text x="63" y="19" fill="#89b4fa" textLength="36" lengthAdjust="spacingAndGlyphs">with</text>
<text x="99" y="19" fill="#ffffff" textLength="801" lengthAdjust="spacingAndGlyphs"> red background </text>
<text x="0" y="36" fill="#ffffff" textLength="45" lengthAdjust="spacingAndGlyphs">line </text>
<text x="45" y="36" fill="#a6e3a1" textLength="9" lengthAdjust="spacingAndGlyphs">3</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`colorizeCode > does not let colors from ansi escape codes leak into colorized code 1`] = `
"line 1
line 2 with red background
line 3"
`;