chore: implement faster ansi2html format for console (#26826)
This commit is contained in:
parent
583964f8dd
commit
a339bead09
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -6624,7 +6624,6 @@
|
|||
"packages/web": {
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"codemirror": "^5.65.9",
|
||||
"xterm": "^5.1.0",
|
||||
"xterm-addon-fit": "^0.7.0"
|
||||
|
|
@ -10630,7 +10629,6 @@
|
|||
"web": {
|
||||
"version": "file:packages/web",
|
||||
"requires": {
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"codemirror": "^5.65.9",
|
||||
"xterm": "^5.1.0",
|
||||
"xterm-addon-fit": "^0.7.0"
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ import * as React from 'react';
|
|||
import './consoleTab.css';
|
||||
import * as modelUtil from './modelUtil';
|
||||
import { ListView } from '@web/components/listView';
|
||||
import { ansi2htmlMarkup } from '@web/components/errorMessage';
|
||||
import type { Boundaries } from '../geometry';
|
||||
import { msToString } from '@web/uiUtils';
|
||||
import { ansi2html } from '@web/ansi2html';
|
||||
import type * as trace from '@trace/trace';
|
||||
|
||||
type ConsoleEntry = {
|
||||
|
|
@ -124,10 +124,10 @@ export const ConsoleTab: React.FunctionComponent<{
|
|||
}
|
||||
|
||||
if (nodeMessage?.text)
|
||||
messageInnerHTML = ansi2htmlMarkup(nodeMessage.text.trim()) || '';
|
||||
messageInnerHTML = ansi2html(nodeMessage.text.trim()) || '';
|
||||
|
||||
if (nodeMessage?.base64)
|
||||
messageInnerHTML = ansi2htmlMarkup(atob(nodeMessage.base64).trim()) || '';
|
||||
messageInnerHTML = ansi2html(atob(nodeMessage.base64).trim()) || '';
|
||||
|
||||
return <div className='console-line'>
|
||||
{timestampElement}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
"version": "0.0.0",
|
||||
"scripts": {},
|
||||
"dependencies": {
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"codemirror": "^5.65.9",
|
||||
"xterm": "^5.1.0",
|
||||
"xterm-addon-fit": "^0.7.0"
|
||||
|
|
|
|||
107
packages/web/src/ansi2html.ts
Normal file
107
packages/web/src/ansi2html.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
export function ansi2html(text: string): string {
|
||||
const regex = /(\x1b\[(\d+(;\d+)*)m)|([^\x1b]+)/g;
|
||||
const tokens: string[] = [];
|
||||
let match;
|
||||
let style: any = {};
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
const [, , codeStr, , text] = match;
|
||||
if (codeStr) {
|
||||
const code = +codeStr;
|
||||
switch (code) {
|
||||
case 0: style = {}; break;
|
||||
case 1: style['font-weight'] = 'bold'; break;
|
||||
case 3: style['font-style'] = 'italic'; break;
|
||||
case 4: style['text-decoration'] = 'underline'; break;
|
||||
case 8: style.display = 'none'; break;
|
||||
case 9: style['text-decoration'] = 'line-through'; break;
|
||||
case 22: style = { ...style, 'font-weight': undefined, 'font-style': undefined, 'text-decoration': undefined }; break;
|
||||
case 23: style = { ...style, 'font-weight': undefined, 'font-style': undefined }; break;
|
||||
case 24: style = { ...style, 'text-decoration': undefined }; break;
|
||||
case 30:
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
case 36:
|
||||
case 37: style.color = ansiColors[code - 30]; break;
|
||||
case 39: style = { ...style, color: undefined }; break;
|
||||
case 40:
|
||||
case 41:
|
||||
case 42:
|
||||
case 43:
|
||||
case 44:
|
||||
case 45:
|
||||
case 46:
|
||||
case 47: style['background-color'] = ansiColors[code - 40]; break;
|
||||
case 49: style = { ...style, 'background-color': undefined }; break;
|
||||
case 53: style['text-decoration'] = 'overline'; break;
|
||||
case 90:
|
||||
case 91:
|
||||
case 92:
|
||||
case 93:
|
||||
case 94:
|
||||
case 95:
|
||||
case 96:
|
||||
case 97: style.color = brightAnsiColors[code - 90]; break;
|
||||
case 100:
|
||||
case 101:
|
||||
case 102:
|
||||
case 103:
|
||||
case 104:
|
||||
case 105:
|
||||
case 106:
|
||||
case 107: style['background-color'] = brightAnsiColors[code - 100]; break;
|
||||
}
|
||||
} else if (text) {
|
||||
tokens.push(`<span style="${styleBody(style)}">${escapeHTML(text)}</span>`);
|
||||
}
|
||||
}
|
||||
return tokens.join('');
|
||||
}
|
||||
|
||||
const ansiColors: Record<number, string> = {
|
||||
0: 'var(--vscode-terminal-ansiBlack)',
|
||||
1: 'var(--vscode-terminal-ansiRed)',
|
||||
2: 'var(--vscode-terminal-ansiGreen)',
|
||||
3: 'var(--vscode-terminal-ansiYellow)',
|
||||
4: 'var(--vscode-terminal-ansiBlue)',
|
||||
5: 'var(--vscode-terminal-ansiMagenta)',
|
||||
6: 'var(--vscode-terminal-ansiCyan)',
|
||||
7: 'var(--vscode-terminal-ansiWhite)',
|
||||
};
|
||||
|
||||
const brightAnsiColors: Record<number, string> = {
|
||||
0: 'var(--vscode-terminal-ansiBrightBlack)',
|
||||
1: 'var(--vscode-terminal-ansiBrightRed)',
|
||||
2: 'var(--vscode-terminal-ansiBrightGreen)',
|
||||
3: 'var(--vscode-terminal-ansiBrightYellow)',
|
||||
4: 'var(--vscode-terminal-ansiBrightBlue)',
|
||||
5: 'var(--vscode-terminal-ansiBrightMagenta)',
|
||||
6: 'var(--vscode-terminal-ansiBrightCyan)',
|
||||
7: 'var(--vscode-terminal-ansiBrightWhite)',
|
||||
};
|
||||
|
||||
function escapeHTML(text: string): string {
|
||||
return text.replace(/[&"<>]/g, c => ({ '&': '&', '"': '"', '<': '<', '>': '>' }[c]!));
|
||||
}
|
||||
|
||||
function styleBody(style: any): string {
|
||||
return Object.entries(style).map(([name, value]) => `${name}: ${value}`).join('; ');
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
../theme.ts
|
||||
../third_party/vscode/codicon.css
|
||||
../uiUtils.ts
|
||||
../ansi2html.ts
|
||||
|
||||
[expandable.spec.tsx]
|
||||
***
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
import './codeMirrorWrapper.css';
|
||||
import * as React from 'react';
|
||||
import type { CodeMirror } from './codeMirrorModule';
|
||||
import { ansi2htmlMarkup } from './errorMessage';
|
||||
import { ansi2html } from '../ansi2html';
|
||||
import { useMeasure } from '../uiUtils';
|
||||
|
||||
export type SourceHighlight = {
|
||||
|
|
@ -152,7 +152,7 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
|
|||
}
|
||||
|
||||
const errorWidgetElement = document.createElement('div');
|
||||
errorWidgetElement.innerHTML = ansi2htmlMarkup(h.message || '');
|
||||
errorWidgetElement.innerHTML = ansi2html(h.message || '');
|
||||
errorWidgetElement.className = 'source-line-error-widget';
|
||||
widgets.push(codemirror.addLineWidget(h.line, errorWidgetElement, { above: true, coverGutter: false }));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,45 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ansi2html from 'ansi-to-html';
|
||||
import { ansi2html } from '@web/ansi2html';
|
||||
import * as React from 'react';
|
||||
import './errorMessage.css';
|
||||
|
||||
export const ErrorMessage: React.FC<{
|
||||
error: string;
|
||||
}> = ({ error }) => {
|
||||
const html = React.useMemo(() => ansi2htmlMarkup(error), [error]);
|
||||
const html = React.useMemo(() => ansi2html(error), [error]);
|
||||
return <div className='error-message' dangerouslySetInnerHTML={{ __html: html || '' }}></div>;
|
||||
};
|
||||
|
||||
export function ansi2htmlMarkup(text: string) {
|
||||
const config: any = {
|
||||
bg: 'var(--vscode-panel-background)',
|
||||
fg: 'var(--vscode-foreground)',
|
||||
};
|
||||
config.colors = ansiColors;
|
||||
return new ansi2html(config).toHtml(escapeHTML(text));
|
||||
}
|
||||
|
||||
const ansiColors = {
|
||||
0: '#000',
|
||||
1: '#C00',
|
||||
2: '#0C0',
|
||||
3: '#C50',
|
||||
4: '#00C',
|
||||
5: '#C0C',
|
||||
6: '#0CC',
|
||||
7: '#CCC',
|
||||
8: '#555',
|
||||
9: '#F55',
|
||||
10: '#5F5',
|
||||
11: '#FF5',
|
||||
12: '#55F',
|
||||
13: '#F5F',
|
||||
14: '#5FF',
|
||||
15: '#FFF'
|
||||
};
|
||||
|
||||
function escapeHTML(text: string): string {
|
||||
return text.replace(/[&"<>]/g, c => ({ '&': '&', '"': '"', '<': '<', '>': '>' }[c]!));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ test('should show console messages for test', async ({ runUITest }, testInfo) =>
|
|||
'codicon codicon-file status-none',
|
||||
]);
|
||||
|
||||
await expect(page.getByText('RED', { exact: true })).toHaveCSS('color', 'rgb(204, 0, 0)');
|
||||
await expect(page.getByText('GREEN', { exact: true })).toHaveCSS('color', 'rgb(0, 204, 0)');
|
||||
await expect.soft(page.getByText('RED', { exact: true })).toHaveCSS('color', 'rgb(205, 49, 49)');
|
||||
await expect.soft(page.getByText('GREEN', { exact: true })).toHaveCSS('color', 'rgb(0, 188, 0)');
|
||||
});
|
||||
|
||||
test('should format console messages in page', async ({ runUITest }, testInfo) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue