Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Omit unchanged lines over threshold in console formatter #347

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 77 additions & 17 deletions src/formatters/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,42 @@ import type {
TextDiffDelta,
} from '../types';

const colors: { [key: string]: chalk.Chalk | undefined } = {
added: chalk.green,
deleted: chalk.red,
movedestination: chalk.gray,
moved: chalk.yellow,
unchanged: chalk.gray,
error: chalk.white.bgRed,
textDiffLine: chalk.gray,
};
export interface ConsoleFormatterOptions {
colors: {
added: chalk.Chalk;
deleted: chalk.Chalk;
movedestination: chalk.Chalk;
moved: chalk.Chalk;
unchanged: chalk.Chalk;
error: chalk.Chalk;
textDiffLine: chalk.Chalk;

// TODO: should all of the DeltaType's have a color?
[key: string]: chalk.Chalk | undefined
};

omitUnchangedAfter?: number;
}

const defaultOptions: ConsoleFormatterOptions = {
omitUnchangedAfter: undefined,
colors: {
added: chalk.green,
deleted: chalk.red,
movedestination: chalk.gray,
moved: chalk.yellow,
unchanged: chalk.gray,
error: chalk.white.bgRed,
textDiffLine: chalk.gray,
}
}


interface ConsoleFormatterContext extends BaseFormatterContext {
indentLevel?: number;
indentPad?: string;
unchangedCounter: number;
omittedCount: number;
outLine: () => void;
indent: (levels?: number) => void;
color?: (chalk.Chalk | undefined)[];
Expand All @@ -33,13 +56,21 @@ interface ConsoleFormatterContext extends BaseFormatterContext {
}

class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
constructor() {

public readonly options: ConsoleFormatterOptions;
constructor(options?: Partial<ConsoleFormatterOptions>) {
super();
this.includeMoveDestinations = false;
this.options = {
...defaultOptions,
...options,
};
}

prepareContext(context: Partial<ConsoleFormatterContext>) {
super.prepareContext(context);
context.unchangedCounter = 0;
context.omittedCount = 0;
context.indent = function (levels) {
this.indentLevel =
(this.indentLevel || 0) + (typeof levels === 'undefined' ? 1 : levels);
Expand Down Expand Up @@ -70,7 +101,7 @@ class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
}

typeFormattterErrorFormatter(context: ConsoleFormatterContext, err: unknown) {
context.pushColor(colors.error);
context.pushColor(this.options.colors.error);
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
context.out(`[ERROR]${err}`);
context.popColor();
Expand All @@ -85,7 +116,7 @@ class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
context.indent();
for (let i = 0, l = lines.length; i < l; i++) {
const line = lines[i];
context.pushColor(colors.textDiffLine);
context.pushColor(this.options.colors.textDiffLine);
context.out(`${line.location.line},${line.location.chr} `);
context.popColor();
const pieces = line.pieces;
Expand All @@ -95,7 +126,7 @@ class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
pieceIndex++
) {
const piece = pieces[pieceIndex];
context.pushColor(colors[piece.type]);
context.pushColor(this.options.colors[piece.type]);
context.out(piece.text);
context.popColor();
}
Expand All @@ -111,7 +142,7 @@ class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
type: DeltaType,
nodeType: NodeType,
) {
context.pushColor(colors[type]);
context.pushColor(this.options.colors[type]);
if (type === 'node') {
context.out(nodeType === 'array' ? '[' : '{');
context.indent();
Expand All @@ -137,7 +168,27 @@ class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
type: DeltaType,
nodeType: NodeType,
) {
context.pushColor(colors[type]);
if (this.options.omitUnchangedAfter && this.options.omitUnchangedAfter > 0) {
if (type === 'unchanged') {
context.unchangedCounter++;
if (context.unchangedCounter >= this.options.omitUnchangedAfter) {
context.omittedCount++;
return;
}
} else {
if (context.omittedCount > 0) {
context.pushColor(this.options.colors.unchanged);
context.out(`... omitted ${context.omittedCount} unchanged fields`);
context.outLine();
context.popColor();
}

context.omittedCount = 0
context.unchangedCounter = 0;
}
}

context.pushColor(this.options.colors[type]);
context.out(`${leftKey}: `);
if (type === 'node') {
context.out(nodeType === 'array' ? '[' : '{');
Expand All @@ -153,6 +204,10 @@ class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
nodeType: NodeType,
isLast: boolean,
) {
if (context.omittedCount > 0) {
return; // skip node end when omitting unchanged...
}

if (type === 'node') {
context.indent(-1);
context.out(nodeType === 'array' ? ']' : `}${isLast ? '' : ','}`);
Expand All @@ -171,6 +226,11 @@ class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
if (typeof left === 'undefined') {
return;
}

if (context.omittedCount > 0) {
return; // skip node end when omitting unchanged...
}

this.formatValue(context, left);
}

Expand Down Expand Up @@ -199,11 +259,11 @@ class ConsoleFormatter extends BaseFormatter<ConsoleFormatterContext> {
}

format_modified(context: ConsoleFormatterContext, delta: ModifiedDelta) {
context.pushColor(colors.deleted);
context.pushColor(this.options.colors.deleted);
this.formatValue(context, delta[0]);
context.popColor();
context.out(' => ');
context.pushColor(colors.added);
context.pushColor(this.options.colors.added);
this.formatValue(context, delta[1]);
context.popColor();
}
Expand Down
80 changes: 80 additions & 0 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import * as jsondiffpatch from '../src/main';
import lcs from '../src/filters/lcs';

import examples from './examples/diffpatch';
import ConsoleFormatter from '../src/formatters/console';
import { format } from '../src/formatters/html';

describe('jsondiffpatch', () => {
it('has a diff method', () => {
Expand Down Expand Up @@ -746,6 +748,84 @@ describe('DiffPatcher', () => {
expectFormat(before, after, expectedHtml(expectedDiff));
});
});

describe('console', () => {
let instance: jsondiffpatch.DiffPatcher;
let formatter: ConsoleFormatter;

beforeAll(() => {
instance = new DiffPatcher();
formatter = new ConsoleFormatter({
omitUnchangedAfter: 5
})
});

const unchanged = (value: string): string => formatter.options.colors.unchanged(value);
const deleted = (value: string): string => formatter.options.colors.deleted(value);
const added = (value: string): string => formatter.options.colors.added(value);

it('should format diffs', () => {
const left = {
foo: 'foo',
bar: 'bar',
baz: 'baz',
};
const right = {
foo: 'baz',
bar: 'bar',
}

const delta = instance.diff(left, right);
const diff = formatter.format(delta, left);
// console.log(diff); // remove to see in console with colors.

// this check feels a little brittle but can't think of a better option..
expect(diff).toEqual(`\
{
${unchanged('bar: ')}${unchanged('"bar"')}
${deleted('baz: ')}${deleted('"baz"')}
foo: ${deleted('"foo"')} => ${added('"baz"')}
}`)
});

it('should omit unchanged lines', () => {
const lots = {} as any;
for (let i = 0; i < 1000; i++) {
const v = `baz${i.toString().padStart(3, '0')}`
lots[v] = v;
}

const left = {
foo: 'foo',
...lots,
bar: 'bar',
baz: 'baz',
};
const right = {
foo: 'baz',
...lots,
bar: 'bar',
baz: 'foo',
}

const delta = instance.diff(left, right);
const diff = formatter.format(delta, left);
// console.log(diff); remove to see in console with colors

// this check feels a little brittle but can't think of a better option..
expect(diff).toEqual(`\
{
${unchanged('bar: ')}${unchanged('"bar"')}
baz: ${deleted('"baz"')} => ${added('"foo"')}
${unchanged('baz000: ')}${unchanged('"baz000"')}
${unchanged('baz001: ')}${unchanged('"baz001"')}
${unchanged('baz002: ')}${unchanged('"baz002"')}
${unchanged('baz003: ')}${unchanged('"baz003"')}
${unchanged('... omitted 996 unchanged fields')}
foo: ${deleted('"foo"')} => ${added('"baz"')}
}`)
});
})
});
});

Expand Down