Make GrDiffGroup immutable
...and consolidate all three location that were computing lineRange in
GrDiffGroup. This is to prevent lineRange from getting out of sync.
Change-Id: I44ac7d5cafa1ce811a2db824f66abcf3c154f03d
diff --git a/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls.ts b/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls.ts
index 1d68335..773e22e 100644
--- a/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls.ts
+++ b/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls.ts
@@ -242,8 +242,11 @@
}
private numLines() {
- const {leftStart, leftEnd} = this.contextRange();
- return leftEnd - leftStart + 1;
+ assertIsDefined(this.group);
+ // In context groups, there is the same number of lines left and right
+ const left = this.group.lineRange.left;
+ // Both start and end inclusive, so we need to add 1.
+ return left.end_line - left.start_line + 1;
}
private createExpandAllButtonContainer() {
@@ -351,21 +354,11 @@
groups: GrDiffGroup[]
) {
return (e: Event) => {
+ assertIsDefined(this.group);
e.stopPropagation();
if (type === ContextButtonType.ALL && this.partialContent) {
- const {leftStart, leftEnd, rightStart, rightEnd} = this.contextRange();
- const lineRange = {
- left: {
- start_line: leftStart,
- end_line: leftEnd,
- },
- right: {
- start_line: rightStart,
- end_line: rightEnd,
- },
- };
fire(this, 'content-load-needed', {
- lineRange,
+ lineRange: this.group.lineRange,
});
} else {
assertIsDefined(this.section, 'section');
@@ -423,6 +416,7 @@
* Creates a container div with block expansion buttons (above and/or below).
*/
private createBlockExpansionButtons() {
+ assertIsDefined(this.group, 'group');
if (
!this.showPartialLinks() ||
!this.renderPreferences?.use_block_expansion ||
@@ -436,14 +430,14 @@
aboveBlockButton = this.createBlockButton(
ContextButtonType.BLOCK_ABOVE,
this.numLines(),
- this.contextRange().rightStart - 1
+ this.group.lineRange.right.start_line - 1
);
}
if (this.showBelow()) {
belowBlockButton = this.createBlockButton(
ContextButtonType.BLOCK_BELOW,
this.numLines(),
- this.contextRange().rightEnd + 1
+ this.group.lineRange.right.end_line + 1
);
}
if (aboveBlockButton || belowBlockButton) {
@@ -503,22 +497,6 @@
return this.createContextButton(buttonType, linesToExpand, tooltip);
}
- private contextRange() {
- if (!this.group) {
- throw new Error('Range can only be computed when group is set.');
- }
- return {
- leftStart: this.group.contextGroups[0].lineRange.left.start_line,
- leftEnd:
- this.group.contextGroups[this.group.contextGroups.length - 1].lineRange
- .left.end_line,
- rightStart: this.group.contextGroups[0].lineRange.right.start_line,
- rightEnd:
- this.group.contextGroups[this.group.contextGroups.length - 1].lineRange
- .right.end_line,
- };
- }
-
private hasValidProperties() {
return !!(this.diff && this.section && this.group?.contextGroups?.length);
}
diff --git a/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls_test.ts b/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls_test.ts
index a5f8566..92debda 100644
--- a/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-context-controls/gr-context-controls_test.ts
@@ -50,9 +50,10 @@
line.text = 'lorem upsum';
lines.push(line);
}
- const result = new GrDiffGroup(GrDiffGroupType.CONTEXT_CONTROL);
- result.contextGroups = [new GrDiffGroup(GrDiffGroupType.BOTH, lines)];
- return result;
+ return new GrDiffGroup({
+ type: GrDiffGroupType.CONTEXT_CONTROL,
+ contextGroups: [new GrDiffGroup({type: GrDiffGroupType.BOTH, lines})],
+ });
}
test('no +10 buttons for 10 or less lines', async () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
index 44b0b8b..413a292 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
@@ -252,31 +252,34 @@
suite('_isTotal', () => {
test('is total for add', () => {
- const group = new GrDiffGroup(GrDiffGroupType.DELTA);
+ const lines = [];
for (let idx = 0; idx < 10; idx++) {
- group.addLine(new GrDiffLine(GrDiffLineType.ADD));
+ lines.push(new GrDiffLine(GrDiffLineType.ADD));
}
+ const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
assert.isTrue(GrDiffBuilder.prototype._isTotal(group));
});
test('is total for remove', () => {
- const group = new GrDiffGroup(GrDiffGroupType.DELTA);
+ const lines = [];
for (let idx = 0; idx < 10; idx++) {
- group.addLine(new GrDiffLine(GrDiffLineType.REMOVE));
+ lines.push(new GrDiffLine(GrDiffLineType.REMOVE));
}
+ const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
assert.isTrue(GrDiffBuilder.prototype._isTotal(group));
});
test('not total for empty', () => {
- const group = new GrDiffGroup(GrDiffGroupType.BOTH);
+ const group = new GrDiffGroup({type: GrDiffGroupType.BOTH});
assert.isFalse(GrDiffBuilder.prototype._isTotal(group));
});
test('not total for non-delta', () => {
- const group = new GrDiffGroup(GrDiffGroupType.DELTA);
+ const lines = [];
for (let idx = 0; idx < 10; idx++) {
- group.addLine(new GrDiffLine(GrDiffLineType.BOTH));
+ lines.push(new GrDiffLine(GrDiffLineType.BOTH));
}
+ const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
assert.isFalse(GrDiffBuilder.prototype._isTotal(group));
});
});
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.js
index 2be85c3..5f3fb72 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified_test.js
@@ -52,7 +52,7 @@
lines[1].text = ' print "Hello World";';
lines[2].text = ' return True';
- group = new GrDiffGroup(GrDiffGroupType.BOTH, lines);
+ group = new GrDiffGroup({type: GrDiffGroupType.BOTH, lines});
});
test('creates the section', () => {
@@ -104,7 +104,7 @@
];
lines[0].text = 'def hello_world():';
lines[1].text = ' print "Hello World"';
- const group = new GrDiffGroup(GrDiffGroupType.DELTA, lines);
+ const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
group.moveDetails = {changed: false};
const sectionEl = diffBuilder.buildSectionElement(group);
@@ -127,7 +127,7 @@
];
lines[0].text = 'def hello_world():';
lines[1].text = ' print "Hello World"';
- const group = new GrDiffGroup(GrDiffGroupType.DELTA, lines);
+ const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
group.moveDetails = {changed: false};
const sectionEl = diffBuilder.buildSectionElement(group);
@@ -160,7 +160,7 @@
lines[2].text = 'def hello_universe()';
lines[3].text = ' print "Hello Universe"';
- group = new GrDiffGroup(GrDiffGroupType.DELTA, lines);
+ group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
});
test('creates the section', () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.ts b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.ts
index b1e15a8..4d8efed 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.ts
@@ -365,22 +365,27 @@
const type =
chunk.ab || chunk.skip ? GrDiffGroupType.BOTH : GrDiffGroupType.DELTA;
const lines = this._linesFromChunk(chunk, offsetLeft, offsetRight);
- const group = new GrDiffGroup(type, lines);
- group.keyLocation = !!chunk.keyLocation;
- group.dueToRebase = !!chunk.due_to_rebase;
- group.moveDetails = chunk.move_details;
- group.skip = chunk.skip;
- group.ignoredWhitespaceOnly = !!chunk.common;
+ const options = {
+ moveDetails: chunk.move_details,
+ dueToRebase: !!chunk.due_to_rebase,
+ ignoredWhitespaceOnly: !!chunk.common,
+ keyLocation: !!chunk.keyLocation,
+ };
if (chunk.skip) {
- group.lineRange = {
- left: {start_line: offsetLeft, end_line: offsetLeft + chunk.skip - 1},
- right: {
- start_line: offsetRight,
- end_line: offsetRight + chunk.skip - 1,
- },
- };
+ return new GrDiffGroup({
+ type,
+ skip: chunk.skip,
+ offsetLeft,
+ offsetRight,
+ ...options,
+ });
+ } else {
+ return new GrDiffGroup({
+ type,
+ lines,
+ ...options,
+ });
}
- return group;
}
_linesFromChunk(chunk: DiffContent, offsetLeft: number, offsetRight: number) {
@@ -456,7 +461,7 @@
const line = new GrDiffLine(GrDiffLineType.BOTH);
line.beforeNumber = number;
line.afterNumber = number;
- return new GrDiffGroup(GrDiffGroupType.BOTH, [line]);
+ return new GrDiffGroup({type: GrDiffGroupType.BOTH, lines: [line]});
}
/**
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.ts
index 40047f3..9778bb7 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.ts
@@ -90,9 +90,12 @@
const result = [...before];
if (hidden.length) {
- const ctxGroup = new GrDiffGroup(GrDiffGroupType.CONTEXT_CONTROL, []);
- ctxGroup.contextGroups = [...hidden];
- result.push(ctxGroup);
+ result.push(
+ new GrDiffGroup({
+ type: GrDiffGroupType.CONTEXT_CONTROL,
+ contextGroups: [...hidden],
+ })
+ );
}
result.push(...after);
return result;
@@ -210,57 +213,131 @@
return [beforeGroups, afterGroups];
}
+export interface GrMoveDetails {
+ changed: boolean;
+ range?: {
+ start: number;
+ end: number;
+ };
+}
+
+/** A chunk of the diff that should be rendered together. */
export class GrDiffGroup {
- /**
- * A chunk of the diff that should be rendered together.
- *
- * @constructor
- * @param type
- * @param opt_lines
- */
- constructor(readonly type: GrDiffGroupType, lines: GrDiffLine[] = []) {
- lines.forEach((line: GrDiffLine) => this.addLine(line));
+ constructor(
+ options:
+ | {
+ type: GrDiffGroupType.BOTH | GrDiffGroupType.DELTA;
+ lines?: GrDiffLine[];
+ skip?: undefined;
+ moveDetails?: GrMoveDetails;
+ dueToRebase?: boolean;
+ ignoredWhitespaceOnly?: boolean;
+ keyLocation?: boolean;
+ }
+ | {
+ type: GrDiffGroupType.BOTH | GrDiffGroupType.DELTA;
+ lines?: undefined;
+ skip: number;
+ offsetLeft: number;
+ offsetRight: number;
+ moveDetails?: GrMoveDetails;
+ dueToRebase?: boolean;
+ ignoredWhitespaceOnly?: boolean;
+ keyLocation?: boolean;
+ }
+ | {
+ type: GrDiffGroupType.CONTEXT_CONTROL;
+ contextGroups: GrDiffGroup[];
+ }
+ ) {
+ this.type = options.type;
+ switch (options.type) {
+ case GrDiffGroupType.BOTH:
+ case GrDiffGroupType.DELTA: {
+ this.moveDetails = options.moveDetails;
+ this.dueToRebase = options.dueToRebase ?? false;
+ this.ignoredWhitespaceOnly = options.ignoredWhitespaceOnly ?? false;
+ this.keyLocation = options.keyLocation ?? false;
+ if (options.skip && options.lines) {
+ throw new Error('Cannot set skip and lines');
+ }
+ this.skip = options.skip;
+ if (options.skip) {
+ this.lineRange = {
+ left: {
+ start_line: options.offsetLeft,
+ end_line: options.offsetLeft + options.skip - 1,
+ },
+ right: {
+ start_line: options.offsetRight,
+ end_line: options.offsetRight + options.skip - 1,
+ },
+ };
+ } else {
+ for (const line of options.lines ?? []) {
+ this.addLine(line);
+ }
+ }
+ break;
+ }
+ case GrDiffGroupType.CONTEXT_CONTROL: {
+ this.contextGroups = options.contextGroups;
+ if (this.contextGroups.length > 0) {
+ const firstGroup = this.contextGroups[0];
+ const lastGroup = this.contextGroups[this.contextGroups.length - 1];
+ this.lineRange = {
+ left: {
+ start_line: firstGroup.lineRange.left.start_line,
+ end_line: lastGroup.lineRange.left.end_line,
+ },
+ right: {
+ start_line: firstGroup.lineRange.right.start_line,
+ end_line: lastGroup.lineRange.right.end_line,
+ },
+ };
+ }
+ break;
+ }
+ default:
+ throw new Error(`Unknown group type: ${this.type}`);
+ }
}
- dueToRebase = false;
+ readonly type: GrDiffGroupType;
+
+ readonly dueToRebase: boolean = false;
/**
* True means all changes in this line are whitespace changes that should
* not be highlighted as changed as per the user settings.
*/
- ignoredWhitespaceOnly = false;
+ readonly ignoredWhitespaceOnly: boolean = false;
/**
* True means it should not be collapsed (because it was in the URL, or
* there is a comment on that line)
*/
- keyLocation = false;
+ readonly keyLocation: boolean = false;
element?: HTMLElement;
- lines: GrDiffLine[] = [];
+ readonly lines: GrDiffLine[] = [];
- adds: GrDiffLine[] = [];
+ readonly adds: GrDiffLine[] = [];
- removes: GrDiffLine[] = [];
+ readonly removes: GrDiffLine[] = [];
- contextGroups: GrDiffGroup[] = [];
+ readonly contextGroups: GrDiffGroup[] = [];
- skip?: number;
+ readonly skip?: number;
/** Both start and end line are inclusive. */
- lineRange = {
- [Side.LEFT]: {start_line: 0, end_line: 0} as LineRange,
- [Side.RIGHT]: {start_line: 0, end_line: 0} as LineRange,
+ readonly lineRange: {[side in Side]: LineRange} = {
+ [Side.LEFT]: {start_line: 0, end_line: 0},
+ [Side.RIGHT]: {start_line: 0, end_line: 0},
};
- moveDetails?: {
- changed: boolean;
- range?: {
- start: number;
- end: number;
- };
- };
+ readonly moveDetails?: GrMoveDetails;
/**
* Creates a new group with the same properties but different lines.
@@ -269,13 +346,22 @@
* rendering of the old lines, so that would not make sense.
*/
cloneWithLines(lines: GrDiffLine[]): GrDiffGroup {
- const group = new GrDiffGroup(this.type, lines);
- group.dueToRebase = this.dueToRebase;
- group.ignoredWhitespaceOnly = this.ignoredWhitespaceOnly;
+ if (
+ this.type !== GrDiffGroupType.BOTH &&
+ this.type !== GrDiffGroupType.DELTA
+ ) {
+ throw new Error('Cannot clone context group with lines');
+ }
+ const group = new GrDiffGroup({
+ type: this.type,
+ lines,
+ dueToRebase: this.dueToRebase,
+ ignoredWhitespaceOnly: this.ignoredWhitespaceOnly,
+ });
return group;
}
- addLine(line: GrDiffLine) {
+ private addLine(line: GrDiffLine) {
this.lines.push(line);
const notDelta =
@@ -293,7 +379,7 @@
} else if (line.type === GrDiffLineType.REMOVE) {
this.removes.push(line);
}
- this._updateRange(line);
+ this._updateRangeWithNewLine(line);
}
getSideBySidePairs(): GrDiffLinePair[] {
@@ -323,7 +409,7 @@
return pairs;
}
- _updateRange(line: GrDiffLine) {
+ private _updateRangeWithNewLine(line: GrDiffLine) {
if (
line.beforeNumber === 'FILE' ||
line.afterNumber === 'FILE' ||
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.js
index 4c7d346..73c12bf 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.js
@@ -21,13 +21,12 @@
suite('gr-diff-group tests', () => {
test('delta line pairs', () => {
- let group = new GrDiffGroup(GrDiffGroupType.DELTA);
const l1 = new GrDiffLine(GrDiffLineType.ADD, 0, 128);
const l2 = new GrDiffLine(GrDiffLineType.ADD, 0, 129);
const l3 = new GrDiffLine(GrDiffLineType.REMOVE, 64, 0);
- group.addLine(l1);
- group.addLine(l2);
- group.addLine(l3);
+ let group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines: [
+ l1, l2, l3,
+ ]});
assert.deepEqual(group.lines, [l1, l2, l3]);
assert.deepEqual(group.adds, [l1, l2]);
assert.deepEqual(group.removes, [l3]);
@@ -42,7 +41,7 @@
{left: BLANK_LINE, right: l2},
]);
- group = new GrDiffGroup(GrDiffGroupType.DELTA, [l1, l2, l3]);
+ group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines: [l1, l2, l3]});
assert.deepEqual(group.lines, [l1, l2, l3]);
assert.deepEqual(group.adds, [l1, l2]);
assert.deepEqual(group.removes, [l3]);
@@ -59,7 +58,8 @@
const l2 = new GrDiffLine(GrDiffLineType.BOTH, 65, 129);
const l3 = new GrDiffLine(GrDiffLineType.BOTH, 66, 130);
- let group = new GrDiffGroup(GrDiffGroupType.BOTH, [l1, l2, l3]);
+ const group = new GrDiffGroup({
+ type: GrDiffGroupType.BOTH, lines: [l1, l2, l3]});
assert.deepEqual(group.lines, [l1, l2, l3]);
assert.deepEqual(group.adds, []);
@@ -70,19 +70,7 @@
right: {start_line: 128, end_line: 130},
});
- let pairs = group.getSideBySidePairs();
- assert.deepEqual(pairs, [
- {left: l1, right: l1},
- {left: l2, right: l2},
- {left: l3, right: l3},
- ]);
-
- group = new GrDiffGroup(GrDiffGroupType.CONTEXT_CONTROL, [l1, l2, l3]);
- assert.deepEqual(group.lines, [l1, l2, l3]);
- assert.deepEqual(group.adds, []);
- assert.deepEqual(group.removes, []);
-
- pairs = group.getSideBySidePairs();
+ const pairs = group.getSideBySidePairs();
assert.deepEqual(pairs, [
{left: l1, right: l1},
{left: l2, right: l2},
@@ -95,27 +83,20 @@
const l2 = new GrDiffLine(GrDiffLineType.REMOVE);
const l3 = new GrDiffLine(GrDiffLineType.BOTH);
- let group = new GrDiffGroup(GrDiffGroupType.BOTH);
- assert.throws(group.addLine.bind(group, l1));
- assert.throws(group.addLine.bind(group, l2));
- assert.doesNotThrow(group.addLine.bind(group, l3));
-
- group = new GrDiffGroup(GrDiffGroupType.CONTEXT_CONTROL);
- assert.throws(group.addLine.bind(group, l1));
- assert.throws(group.addLine.bind(group, l2));
- assert.doesNotThrow(group.addLine.bind(group, l3));
+ assert.throws(() =>
+ new GrDiffGroup({type: GrDiffGroupType.BOTH, lines: [l1, l2, l3]}));
});
suite('hideInContextControl', () => {
let groups;
setup(() => {
groups = [
- new GrDiffGroup(GrDiffGroupType.BOTH, [
+ new GrDiffGroup({type: GrDiffGroupType.BOTH, lines: [
new GrDiffLine(GrDiffLineType.BOTH, 5, 7),
new GrDiffLine(GrDiffLineType.BOTH, 6, 8),
new GrDiffLine(GrDiffLineType.BOTH, 7, 9),
- ]),
- new GrDiffGroup(GrDiffGroupType.DELTA, [
+ ]}),
+ new GrDiffGroup({type: GrDiffGroupType.DELTA, lines: [
new GrDiffLine(GrDiffLineType.REMOVE, 8),
new GrDiffLine(GrDiffLineType.ADD, 0, 10),
new GrDiffLine(GrDiffLineType.REMOVE, 9),
@@ -124,12 +105,12 @@
new GrDiffLine(GrDiffLineType.ADD, 0, 12),
new GrDiffLine(GrDiffLineType.REMOVE, 11),
new GrDiffLine(GrDiffLineType.ADD, 0, 13),
- ]),
- new GrDiffGroup(GrDiffGroupType.BOTH, [
+ ]}),
+ new GrDiffGroup({type: GrDiffGroupType.BOTH, lines: [
new GrDiffLine(GrDiffLineType.BOTH, 12, 14),
new GrDiffLine(GrDiffLineType.BOTH, 13, 15),
new GrDiffLine(GrDiffLineType.BOTH, 14, 16),
- ]),
+ ]}),
];
});
@@ -181,24 +162,23 @@
suite('with skip chunks', () => {
setup(() => {
- const skipGroup = new GrDiffGroup(GrDiffGroupType.BOTH);
- skipGroup.skip = 60;
- skipGroup.lineRange = {
- left: {start_line: 8, end_line: 67},
- right: {start_line: 10, end_line: 69},
- };
+ const skipGroup = new GrDiffGroup({
+ type: GrDiffGroupType.BOTH,
+ skip: 60,
+ offsetLeft: 8,
+ offsetRight: 10});
groups = [
- new GrDiffGroup(GrDiffGroupType.BOTH, [
+ new GrDiffGroup({type: GrDiffGroupType.BOTH, lines: [
new GrDiffLine(GrDiffLineType.BOTH, 5, 7),
new GrDiffLine(GrDiffLineType.BOTH, 6, 8),
new GrDiffLine(GrDiffLineType.BOTH, 7, 9),
- ]),
+ ]}),
skipGroup,
- new GrDiffGroup(GrDiffGroupType.BOTH, [
+ new GrDiffGroup({type: GrDiffGroupType.BOTH, lines: [
new GrDiffLine(GrDiffLineType.BOTH, 68, 70),
new GrDiffLine(GrDiffLineType.BOTH, 69, 71),
new GrDiffLine(GrDiffLineType.BOTH, 70, 72),
- ]),
+ ]}),
];
});