core-components: add tests for LogLine highlighting + fix and refactor

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-12-05 14:54:45 +01:00
committed by blam
parent 4dd0511074
commit 563635a0f8
2 changed files with 272 additions and 19 deletions
@@ -14,8 +14,12 @@
* limitations under the License.
*/
import { ChunkModifiers } from './AnsiProcessor';
import { findSearchResults, getModifierClasses } from './LogLine';
import { AnsiLine, ChunkModifiers } from './AnsiProcessor';
import {
calculateHighlightedChunks,
findSearchResults,
getModifierClasses,
} from './LogLine';
describe('getModifierClasses', () => {
const classes = {
@@ -110,3 +114,243 @@ describe('findSearchResults', () => {
]);
});
});
describe('calculateHighlightedChunks', () => {
it('should pass through chunks if there are no results', () => {
const chunks = [{ text: 'Foo', modifiers: {} }];
const line = new AnsiLine(0, chunks);
expect(calculateHighlightedChunks(line, 'bar')).toBe(chunks);
});
it('should highlight one result from plain text', () => {
const line = new AnsiLine(0, [{ text: 'FooBarBaz', modifiers: {} }]);
expect(calculateHighlightedChunks(line, 'foo')).toEqual([
{
text: 'Foo',
modifiers: {},
highlight: true,
},
{
text: 'BarBaz',
modifiers: {},
},
]);
expect(calculateHighlightedChunks(line, 'bar')).toEqual([
{
text: 'Foo',
modifiers: {},
},
{
text: 'Bar',
modifiers: {},
highlight: true,
},
{
text: 'Baz',
modifiers: {},
},
]);
expect(calculateHighlightedChunks(line, 'baz')).toEqual([
{
text: 'FooBar',
modifiers: {},
},
{
text: 'Baz',
modifiers: {},
highlight: true,
},
]);
});
it('should highlight multiple results from plain text', () => {
const line = new AnsiLine(0, [
{ text: 'FooBarBazBazBarFoo', modifiers: {} },
]);
expect(calculateHighlightedChunks(line, 'foo')).toEqual([
{
text: 'Foo',
modifiers: {},
highlight: true,
},
{
text: 'BarBazBazBar',
modifiers: {},
},
{
text: 'Foo',
modifiers: {},
highlight: true,
},
]);
expect(calculateHighlightedChunks(line, 'bar')).toEqual([
{
text: 'Foo',
modifiers: {},
},
{
text: 'Bar',
modifiers: {},
highlight: true,
},
{
text: 'BazBaz',
modifiers: {},
},
{
text: 'Bar',
modifiers: {},
highlight: true,
},
{
text: 'Foo',
modifiers: {},
},
]);
expect(calculateHighlightedChunks(line, 'baz')).toEqual([
{
text: 'FooBar',
modifiers: {},
},
{
text: 'Baz',
modifiers: {},
highlight: true,
},
{
text: 'Baz',
modifiers: {},
highlight: true,
},
{
text: 'BarFoo',
modifiers: {},
},
]);
});
it('should forward modifiers to result', () => {
const line = new AnsiLine(0, [
{ text: 'FooBarBazBazBarFoo', modifiers: { bold: true } },
]);
expect(calculateHighlightedChunks(line, 'foo')).toEqual([
{
text: 'Foo',
modifiers: { bold: true },
highlight: true,
},
{
text: 'BarBazBazBar',
modifiers: { bold: true },
},
{
text: 'Foo',
modifiers: { bold: true },
highlight: true,
},
]);
});
it('should highlight full chunks', () => {
const line = new AnsiLine(0, [
{ text: 'Foo', modifiers: { bold: true } },
{ text: 'BarBaz', modifiers: { bold: true } },
{ text: 'BazBar', modifiers: { italic: true } },
{ text: 'Foo', modifiers: { italic: true } },
]);
expect(calculateHighlightedChunks(line, 'foo')).toEqual([
{
text: 'Foo',
modifiers: { bold: true },
highlight: true,
},
{
text: 'BarBaz',
modifiers: { bold: true },
},
{
text: 'BazBar',
modifiers: { italic: true },
},
{
text: 'Foo',
modifiers: { italic: true },
highlight: true,
},
]);
});
it('should highlight partial chunks', () => {
const line = new AnsiLine(0, [
{ text: 'Fo', modifiers: { bold: true } },
{ text: 'oFooFo', modifiers: {} },
{ text: 'oBarBaz', modifiers: { italic: true } },
{ text: 'Foo', modifiers: { foreground: 'blue' } },
{ text: 'FooFoo', modifiers: { italic: true } },
{ text: 'F', modifiers: { bold: true } },
{ text: 'o', modifiers: {} },
{ text: 'o', modifiers: { bold: true } },
]);
expect(calculateHighlightedChunks(line, 'foo')).toEqual([
{
text: 'Fo',
modifiers: { bold: true },
highlight: true,
},
{
text: 'o',
modifiers: {},
highlight: true,
},
{
text: 'Foo',
modifiers: {},
highlight: true,
},
{
text: 'Fo',
modifiers: {},
highlight: true,
},
{
text: 'o',
modifiers: { italic: true },
highlight: true,
},
{
text: 'BarBaz',
modifiers: { italic: true },
},
{
text: 'Foo',
modifiers: { foreground: 'blue' },
highlight: true,
},
{
text: 'Foo',
modifiers: { italic: true },
highlight: true,
},
{
text: 'Foo',
modifiers: { italic: true },
highlight: true,
},
{
text: 'F',
modifiers: { bold: true },
highlight: true,
},
{
text: 'o',
modifiers: {},
highlight: true,
},
{
text: 'o',
modifiers: { bold: true },
highlight: true,
},
]);
});
});
@@ -82,45 +82,54 @@ export function calculateHighlightedChunks(
const chunks = new Array<HighlightAnsiChunk>();
let chunkOffset = 0;
let lineOffset = 0;
let nextResult = results.shift();
for (const chunk of line.chunks) {
const { text, modifiers } = chunk;
if (!nextResult || chunkOffset + text.length < nextResult.start) {
if (!nextResult || lineOffset + text.length < nextResult.start) {
chunks.push(chunk);
chunkOffset += text.length;
lineOffset += text.length;
continue;
}
let localOffset = 0;
while (nextResult) {
let localStart = nextResult.start - chunkOffset;
if (localStart < 0) {
localStart = 0;
const localStart = Math.max(nextResult.start - lineOffset, 0);
if (localStart > text.length) {
break; // The next result is not in this chunk
}
const localEnd = nextResult.end - chunkOffset;
const beforeMatch = text.slice(localOffset, localStart);
const match = text.slice(localStart, localEnd);
if (beforeMatch) {
chunks.push({ text: beforeMatch, modifiers });
const localEnd = Math.min(nextResult.end - lineOffset, text.length);
const hasTextBeforeResult = localStart > localOffset;
if (hasTextBeforeResult) {
chunks.push({ text: text.slice(localOffset, localStart), modifiers });
}
const hasResultText = localEnd > localStart;
if (hasResultText) {
chunks.push({
modifiers,
highlight: true,
text: text.slice(localStart, localEnd),
});
}
chunks.push({ text: match, modifiers, highlight: true });
localOffset = localStart + match.length;
localOffset = localEnd;
if (match.length === searchText.length) {
const foundCompleteResult = nextResult.end - lineOffset === localEnd;
if (foundCompleteResult) {
nextResult = results.shift();
} else {
break;
break; // The rest of the result is in the following chunks
}
}
if (localOffset < text.length) {
const hasTextAfterResult = localOffset < text.length;
if (hasTextAfterResult) {
chunks.push({ text: text.slice(localOffset), modifiers });
}
chunkOffset += text.length;
lineOffset += text.length;
}
return chunks;