diff --git a/packages/core-components/src/components/LogViewer/AnsiProcessor.test.ts b/packages/core-components/src/components/LogViewer/AnsiProcessor.test.ts
index b2ceee78b0..9ef0353eca 100644
--- a/packages/core-components/src/components/LogViewer/AnsiProcessor.test.ts
+++ b/packages/core-components/src/components/LogViewer/AnsiProcessor.test.ts
@@ -20,37 +20,43 @@ describe('AnsiProcessor', () => {
it('should process a single line', () => {
const processor = new AnsiProcessor();
expect(processor.process('foo\x1b[31mbar\x1b[39mbaz')).toEqual([
- [
- {
- text: 'foo',
- modifiers: {},
- },
- {
- text: 'bar',
- modifiers: { foreground: 'red' },
- },
- {
- text: 'baz',
- modifiers: {},
- },
- ],
+ {
+ chunks: [
+ {
+ text: 'foo',
+ modifiers: {},
+ },
+ {
+ text: 'bar',
+ modifiers: { foreground: 'red' },
+ },
+ {
+ text: 'baz',
+ modifiers: {},
+ },
+ ],
+ lineNumber: 1,
+ },
]);
expect(processor.process(`foo [32mbar[39m: baz`)).toEqual([
- [
- {
- text: 'foo ',
- modifiers: {},
- },
- {
- text: 'bar',
- modifiers: { foreground: 'green' },
- },
- {
- text: ': baz',
- modifiers: {},
- },
- ],
+ {
+ chunks: [
+ {
+ text: 'foo ',
+ modifiers: {},
+ },
+ {
+ text: 'bar',
+ modifiers: { foreground: 'green' },
+ },
+ {
+ text: ': baz',
+ modifiers: {},
+ },
+ ],
+ lineNumber: 1,
+ },
]);
});
@@ -62,36 +68,42 @@ a\x1b[34mb\x1b[39mc
x\x1b[44my\x1b[49mz
`),
).toEqual([
- [{ text: '', modifiers: {} }],
- [
- {
- text: 'a',
- modifiers: {},
- },
- {
- text: 'b',
- modifiers: { foreground: 'blue' },
- },
- {
- text: 'c',
- modifiers: {},
- },
- ],
- [
- {
- text: 'x',
- modifiers: {},
- },
- {
- text: 'y',
- modifiers: { background: 'blue' },
- },
- {
- text: 'z',
- modifiers: {},
- },
- ],
- [{ text: '', modifiers: {} }],
+ { chunks: [{ text: '', modifiers: {} }], lineNumber: 1 },
+ {
+ chunks: [
+ {
+ text: 'a',
+ modifiers: {},
+ },
+ {
+ text: 'b',
+ modifiers: { foreground: 'blue' },
+ },
+ {
+ text: 'c',
+ modifiers: {},
+ },
+ ],
+ lineNumber: 2,
+ },
+ {
+ chunks: [
+ {
+ text: 'x',
+ modifiers: {},
+ },
+ {
+ text: 'y',
+ modifiers: { background: 'blue' },
+ },
+ {
+ text: 'z',
+ modifiers: {},
+ },
+ ],
+ lineNumber: 3,
+ },
+ { chunks: [{ text: '', modifiers: {} }], lineNumber: 4 },
]);
});
@@ -102,35 +114,41 @@ x\x1b[44my\x1b[49mz
a\x1b[45mb\x1b[35mc
x\x1b[39my\x1b[49mz`),
).toEqual([
- [{ text: '', modifiers: {} }],
- [
- {
- text: 'a',
- modifiers: {},
- },
- {
- text: 'b',
- modifiers: { background: 'magenta' },
- },
- {
- text: 'c',
- modifiers: { foreground: 'magenta', background: 'magenta' },
- },
- ],
- [
- {
- text: 'x',
- modifiers: { foreground: 'magenta', background: 'magenta' },
- },
- {
- text: 'y',
- modifiers: { background: 'magenta' },
- },
- {
- text: 'z',
- modifiers: {},
- },
- ],
+ { chunks: [{ text: '', modifiers: {} }], lineNumber: 1 },
+ {
+ chunks: [
+ {
+ text: 'a',
+ modifiers: {},
+ },
+ {
+ text: 'b',
+ modifiers: { background: 'magenta' },
+ },
+ {
+ text: 'c',
+ modifiers: { foreground: 'magenta', background: 'magenta' },
+ },
+ ],
+ lineNumber: 2,
+ },
+ {
+ chunks: [
+ {
+ text: 'x',
+ modifiers: { foreground: 'magenta', background: 'magenta' },
+ },
+ {
+ text: 'y',
+ modifiers: { background: 'magenta' },
+ },
+ {
+ text: 'z',
+ modifiers: {},
+ },
+ ],
+ lineNumber: 3,
+ },
]);
});
@@ -139,56 +157,65 @@ x\x1b[39my\x1b[49mz`),
const out1 = processor.process(`
a\x1b[36mb\x1b[3mc`);
expect(out1).toEqual([
- [{ text: '', modifiers: {} }],
- [
- {
- text: 'a',
- modifiers: {},
- },
- {
- text: 'b',
- modifiers: { foreground: 'cyan' },
- },
- {
- text: 'c',
- modifiers: { foreground: 'cyan', italic: true },
- },
- ],
+ { chunks: [{ text: '', modifiers: {} }], lineNumber: 1 },
+ {
+ chunks: [
+ {
+ text: 'a',
+ modifiers: {},
+ },
+ {
+ text: 'b',
+ modifiers: { foreground: 'cyan' },
+ },
+ {
+ text: 'c',
+ modifiers: { foreground: 'cyan', italic: true },
+ },
+ ],
+ lineNumber: 2,
+ },
]);
const out2 = processor.process(`
a\x1b[36mb\x1b[3mc
x\x1b[39my\x1b[23mz`);
expect(out2).toEqual([
- [{ text: '', modifiers: {} }],
- [
- {
- text: 'a',
- modifiers: {},
- },
- {
- text: 'b',
- modifiers: { foreground: 'cyan' },
- },
- {
- text: 'c',
- modifiers: { foreground: 'cyan', italic: true },
- },
- ],
- [
- {
- text: 'x',
- modifiers: { foreground: 'cyan', italic: true },
- },
- {
- text: 'y',
- modifiers: { italic: true },
- },
- {
- text: 'z',
- modifiers: {},
- },
- ],
+ { chunks: [{ text: '', modifiers: {} }], lineNumber: 1 },
+ {
+ chunks: [
+ {
+ text: 'a',
+ modifiers: {},
+ },
+ {
+ text: 'b',
+ modifiers: { foreground: 'cyan' },
+ },
+ {
+ text: 'c',
+ modifiers: { foreground: 'cyan', italic: true },
+ },
+ ],
+ lineNumber: 2,
+ },
+ {
+ chunks: [
+ {
+ text: 'x',
+ modifiers: { foreground: 'cyan', italic: true },
+ },
+ {
+ text: 'y',
+ modifiers: { italic: true },
+ },
+ {
+ text: 'z',
+ modifiers: {},
+ },
+ ],
+ lineNumber: 3,
+ },
]);
// Verifies that we appended rather than reprocessed
diff --git a/packages/core-components/src/components/LogViewer/AnsiProcessor.ts b/packages/core-components/src/components/LogViewer/AnsiProcessor.ts
index f148005355..f3ea37615c 100644
--- a/packages/core-components/src/components/LogViewer/AnsiProcessor.ts
+++ b/packages/core-components/src/components/LogViewer/AnsiProcessor.ts
@@ -71,34 +71,56 @@ export interface ChunkModifiers {
underline?: boolean;
}
-export interface Chunk {
+// export interface AnsiLine {
+// lineNumber: number;
+// chunks: AnsiChunk[];
+// }
+
+export interface AnsiChunk {
text: string;
modifiers: ChunkModifiers;
}
+export class AnsiLine {
+ constructor(
+ readonly lineNumber: number = 1,
+ readonly chunks: AnsiChunk[] = [],
+ ) {}
+
+ lastChunk(): AnsiChunk | undefined {
+ return this.chunks[this.chunks.length - 1];
+ }
+}
+
export class AnsiProcessor {
private text: string = '';
- private lines: Chunk[][] = [];
+ private lines: AnsiLine[] = [];
/**
* Processes a chunk of text while keeping internal state that optimizes
* subsequent processing that appends to the text.
*/
- process(text: string): Chunk[][] {
+ process(text: string): AnsiLine[] {
if (this.text === text) {
return this.lines;
}
if (text.startsWith(this.text)) {
const lastLineIndex = this.lines.length > 0 ? this.lines.length - 1 : 0;
- const lastLine = this.lines[lastLineIndex] ?? [];
- const lastChunk = lastLine[lastLine.length - 1] as Chunk | undefined;
+ const lastLine = this.lines[lastLineIndex] ?? new AnsiLine();
+ const lastChunk = lastLine.lastChunk();
+
const newLines = this.processLines(
(lastChunk?.text ?? '') + text.slice(this.text.length),
lastChunk?.modifiers,
+ lastLine?.lineNumber,
);
this.text = text;
- lastLine.splice(lastLine.length - 1, 1, ...newLines[0]);
+ lastLine.chunks.splice(
+ lastLine.chunks.length - 1,
+ 1,
+ ...newLines[0]?.chunks,
+ );
this.lines[lastLineIndex] = lastLine;
this.lines.push(...newLines.slice(1));
} else {
@@ -113,16 +135,23 @@ export class AnsiProcessor {
private processLines = (
text: string,
modifiers: ChunkModifiers = {},
- ): Chunk[][] => {
- const lines: Chunk[][] = [];
+ startingLineNumber: number = 1,
+ ): AnsiLine[] => {
+ const lines: AnsiLine[] = [];
+
+ let currentModifiers = modifiers;
+ let currentLineNumber = startingLineNumber;
let prevIndex = 0;
- let currentModifiers = modifiers;
newlineRegex.lastIndex = 0;
for (;;) {
const match = newlineRegex.exec(text);
if (!match) {
- lines.push(this.processText(text.slice(prevIndex), currentModifiers));
+ const chunks = this.processText(
+ text.slice(prevIndex),
+ currentModifiers,
+ );
+ lines.push(new AnsiLine(currentLineNumber, chunks));
return lines;
}
@@ -130,11 +159,12 @@ export class AnsiProcessor {
prevIndex = match.index + match[0].length;
const chunks = this.processText(line, currentModifiers);
- lines.push(chunks);
+ lines.push(new AnsiLine(currentLineNumber, chunks));
// Modifiers that are active in the last chunk are carried over to the next line
currentModifiers =
chunks[chunks.length - 1].modifiers ?? currentModifiers;
+ currentLineNumber += 1;
}
};
@@ -142,11 +172,12 @@ export class AnsiProcessor {
private processText = (
fullText: string,
modifiers: ChunkModifiers,
- ): Chunk[] => {
- const chunks: Chunk[] = [];
+ ): AnsiChunk[] => {
+ const chunks: AnsiChunk[] = [];
+
+ let currentModifiers = modifiers;
let prevIndex = 0;
- let currentModifiers = modifiers;
ansiRegex.lastIndex = 0;
for (;;) {
const match = ansiRegex.exec(fullText);
diff --git a/packages/core-components/src/components/LogViewer/LogViewer.tsx b/packages/core-components/src/components/LogViewer/LogViewer.tsx
index 21d42a1862..a77990f734 100644
--- a/packages/core-components/src/components/LogViewer/LogViewer.tsx
+++ b/packages/core-components/src/components/LogViewer/LogViewer.tsx
@@ -179,7 +179,7 @@ export function LogViewer(props: LogViewerProps) {
{!noLineNumbers && (
{index + 1}
)}
- {data[index].map(({ text, modifiers }, i) => (
+ {data[index].chunks.map(({ text, modifiers }, i) => (