Skip to content

Commit ecbd011

Browse files
authored
Fix code colorizer ansi escape bug. (#21321)
1 parent a3e39b8 commit ecbd011

4 files changed

Lines changed: 62 additions & 5 deletions

File tree

packages/cli/src/ui/utils/CodeColorizer.test.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,36 @@ describe('colorizeCode', () => {
5050
expect(lastFrame()).toMatch(/line 1\s*\n\s*\n\s*line 3/);
5151
unmount();
5252
});
53+
54+
it('does not let colors from ansi escape codes leak into colorized code', async () => {
55+
const code = 'line 1\n\x1b[41mline 2 with red background\x1b[0m\nline 3';
56+
const settings = new LoadedSettings(
57+
{ path: '', settings: {}, originalSettings: {} },
58+
{ path: '', settings: {}, originalSettings: {} },
59+
{
60+
path: '',
61+
settings: { ui: { useAlternateBuffer: true, showLineNumbers: false } },
62+
originalSettings: {
63+
ui: { useAlternateBuffer: true, showLineNumbers: false },
64+
},
65+
},
66+
{ path: '', settings: {}, originalSettings: {} },
67+
true,
68+
[],
69+
);
70+
71+
const result = colorizeCode({
72+
code,
73+
language: 'javascript',
74+
maxWidth: 80,
75+
settings,
76+
hideLineNumbers: true,
77+
});
78+
79+
const renderResult = renderWithProviders(<>{result}</>);
80+
await renderResult.waitUntilReady();
81+
82+
await expect(renderResult).toMatchSvgSnapshot();
83+
renderResult.unmount();
84+
});
5385
});

packages/cli/src/ui/utils/CodeColorizer.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {
1414
ElementContent,
1515
RootContent,
1616
} from 'hast';
17+
import stripAnsi from 'strip-ansi';
1718
import { themeManager } from '../themes/theme-manager.js';
1819
import type { Theme } from '../themes/theme.js';
1920
import {
@@ -98,16 +99,17 @@ function highlightAndRenderLine(
9899
theme: Theme,
99100
): React.ReactNode {
100101
try {
102+
const strippedLine = stripAnsi(line);
101103
const getHighlightedLine = () =>
102104
!language || !lowlight.registered(language)
103-
? lowlight.highlightAuto(line)
104-
: lowlight.highlight(language, line);
105+
? lowlight.highlightAuto(strippedLine)
106+
: lowlight.highlight(language, strippedLine);
105107

106108
const renderedNode = renderHastNode(getHighlightedLine(), theme, undefined);
107109

108-
return renderedNode !== null ? renderedNode : line;
110+
return renderedNode !== null ? renderedNode : strippedLine;
109111
} catch (_error) {
110-
return line;
112+
return stripAnsi(line);
111113
}
112114
}
113115

@@ -238,7 +240,7 @@ export function colorizeCode({
238240
<Text color={activeTheme.defaultColor}>{`${index + 1}`}</Text>
239241
</Box>
240242
)}
241-
<Text color={activeTheme.colors.Gray}>{line}</Text>
243+
<Text color={activeTheme.colors.Gray}>{stripAnsi(line)}</Text>
242244
</Box>
243245
));
244246

Lines changed: 16 additions & 0 deletions
Loading
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`colorizeCode > does not let colors from ansi escape codes leak into colorized code 1`] = `
4+
"line 1
5+
line 2 with red background
6+
line 3"
7+
`;

0 commit comments

Comments
 (0)