Add support for asymmetric skip chunks in GrDiff
Instead of a single number, `skip` chunks can now alternatively contain
an object with explicit skip lengths for the left and right side of the
diff.
This allows supporting diffs where a different number of lines were
skipped in the left and right file which allows for more optimized
truncation.
A new utility function `normalizeSkipInfo` is introduced to easily
retrieve skip lengths for a given side, regardless of whether the
symmetric or asymmetric representation is used.
go/grdiff-asymmetric-skip-chunks
PiperOrigin-RevId: 828376161
Release-Notes: skip
Change-Id: Ibef576f8fc19b0b27f8301263db065bbdd0ad1e2
diff --git a/polygerrit-ui/app/api/diff.ts b/polygerrit-ui/app/api/diff.ts
index e5d0af7..474ba56 100644
--- a/polygerrit-ui/app/api/diff.ts
+++ b/polygerrit-ui/app/api/diff.ts
@@ -122,6 +122,13 @@
| 'COPIED'
| 'REWRITE';
+export declare type SkipObject = {
+ left: number;
+ right: number;
+};
+
+export declare type SkipInfo = number | SkipObject;
+
/**
* The DiffContent entity contains information about the content differences in
* a file.
@@ -158,7 +165,7 @@
* Count of lines skipped on both sides when the file is too large to include
* all common lines.
*/
- skip?: number;
+ skip?: SkipInfo;
/**
* Set to true if the region is common according to the requested
* ignore-whitespace parameter, but a and b contain differing amounts of
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor.ts b/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor.ts
index 89bb49e..4e9e225 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor.ts
@@ -11,6 +11,7 @@
} from '../gr-diff/gr-diff-group';
import {DiffContent, DiffRangesToFocus} from '../../../types/diff';
import {Side} from '../../../constants/constants';
+import {normalizeSkipInfo} from '../../../utils/diff-util';
import {getStringLength} from '../gr-diff-highlight/gr-annotation';
import {GrDiffLineType, LineNumber} from '../../../api/diff';
import {FULL_CONTEXT, KeyLocations} from '../gr-diff/gr-diff-utils';
@@ -310,7 +311,9 @@
}
private chunkLength(chunk: DiffContent, side: Side) {
- if (chunk.skip || chunk.common || chunk.ab) {
+ if (chunk.skip) {
+ return normalizeSkipInfo(chunk.skip)[side];
+ } else if (chunk.common || chunk.ab) {
return this.commonChunkLength(chunk);
} else if (side === Side.LEFT) {
return this.linesLeft(chunk).length;
@@ -320,9 +323,6 @@
}
private commonChunkLength(chunk: DiffContent) {
- if (chunk.skip) {
- return chunk.skip;
- }
console.assert(!!chunk.ab || !!chunk.common);
console.assert(
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-group.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-group.ts
index ccd208c..38c8d1c 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-group.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-group.ts
@@ -4,9 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {BLANK_LINE, GrDiffLine} from './gr-diff-line';
-import {GrDiffLineType, LineNumber, LineRange, Side} from '../../../api/diff';
+import {
+ GrDiffLineType,
+ LineNumber,
+ LineRange,
+ Side,
+ SkipInfo,
+} from '../../../api/diff';
import {assert, assertIsDefined} from '../../../utils/common-util';
import {isDefined} from '../../../types/types';
+import {normalizeSkipInfo} from '../../../utils/diff-util';
export enum GrDiffGroupType {
/** A group of unchanged diff lines. */
@@ -270,7 +277,7 @@
| {
type: GrDiffGroupType.BOTH | GrDiffGroupType.DELTA;
lines?: undefined;
- skip: number;
+ skip: SkipInfo;
offsetLeft: number;
offsetRight: number;
moveDetails?: GrMoveDetails;
@@ -296,14 +303,15 @@
}
this.skip = options.skip;
if (options.skip !== undefined) {
+ const skip = normalizeSkipInfo(options.skip);
this.lineRange = {
left: {
start_line: options.offsetLeft,
- end_line: options.offsetLeft + options.skip - 1,
+ end_line: options.offsetLeft + skip.left - 1,
},
right: {
start_line: options.offsetRight,
- end_line: options.offsetRight + options.skip - 1,
+ end_line: options.offsetRight + skip.right - 1,
},
};
} else {
@@ -368,7 +376,7 @@
*/
readonly contextGroups: GrDiffGroup[] = [];
- readonly skip?: number;
+ readonly skip?: SkipInfo;
/** Both start and end line are inclusive. */
readonly lineRange: {[side in Side]: LineRange} = {
diff --git a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
index 9e3e087..449a252 100644
--- a/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
+++ b/polygerrit-ui/app/embed/diff/gr-syntax-layer/gr-syntax-layer-worker.ts
@@ -15,6 +15,7 @@
import {ReportingService} from '../../../services/gr-reporting/gr-reporting';
import {GrDiffLineType} from '../../../api/diff';
import {assert} from '../../../utils/common-util';
+import {normalizeSkipInfo} from '../../../utils/diff-util';
const LANGUAGE_MAP = new Map<string, string>([
['application/dart', 'dart'],
@@ -271,10 +272,10 @@
for (const line of b) {
rightContent += line + '\n';
}
- const skip = chunk.skip ?? 0;
- if (skip > 0) {
- leftContent += '\n'.repeat(skip);
- rightContent += '\n'.repeat(skip);
+ if (chunk.skip) {
+ const skip = normalizeSkipInfo(chunk.skip);
+ leftContent += '\n'.repeat(skip.left);
+ rightContent += '\n'.repeat(skip.right);
}
}
leftContent = leftContent.trimEnd();
diff --git a/polygerrit-ui/app/types/diff.ts b/polygerrit-ui/app/types/diff.ts
index 47a96e4..b3880fb 100644
--- a/polygerrit-ui/app/types/diff.ts
+++ b/polygerrit-ui/app/types/diff.ts
@@ -22,7 +22,9 @@
IgnoreWhitespaceType,
MarkLength,
MoveDetails,
+ SkipInfo,
SkipLength,
+ SkipObject,
} from '../api/diff';
export type {
@@ -34,6 +36,8 @@
MarkLength,
MoveDetails,
SkipLength,
+ SkipInfo,
+ SkipObject,
WebLinkInfo,
};
diff --git a/polygerrit-ui/app/utils/diff-util.ts b/polygerrit-ui/app/utils/diff-util.ts
index f6b00d4..b6b51a0 100644
--- a/polygerrit-ui/app/utils/diff-util.ts
+++ b/polygerrit-ui/app/utils/diff-util.ts
@@ -4,7 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Side} from '../constants/constants';
-import {DiffInfo} from '../types/diff';
+import {DiffInfo, SkipInfo, SkipObject} from '../types/diff';
+
+export function normalizeSkipInfo(skip: SkipInfo | undefined): SkipObject {
+ if (!skip) return {left: 0, right: 0};
+ return typeof skip === 'number' ? {left: skip, right: skip} : skip;
+}
export function otherSide(side: Side) {
return side === Side.LEFT ? Side.RIGHT : Side.LEFT;
@@ -14,7 +19,12 @@
if (!diff?.content || !side) return 0;
return diff.content.reduce((sum, chunk) => {
const sideChunk = side === Side.LEFT ? chunk.a : chunk.b;
- return sum + (sideChunk?.length ?? chunk.ab?.length ?? chunk.skip ?? 0);
+ return (
+ sum +
+ (sideChunk?.length ??
+ chunk.ab?.length ??
+ normalizeSkipInfo(chunk.skip)[side])
+ );
}, 0);
}
@@ -27,7 +37,7 @@
let currentLine = 0;
for (const chunk of diff.content) {
if (chunk.skip) {
- currentLine += chunk.skip;
+ currentLine += normalizeSkipInfo(chunk.skip)[side];
if (currentLine >= line) return false;
} else if (chunk.ab) {
currentLine += chunk.ab.length;
@@ -51,7 +61,8 @@
let lines: string[] = [];
for (const chunk of diff.content) {
if (chunk.skip) {
- lines = lines.concat(Array(chunk.skip).fill(''));
+ const skip = normalizeSkipInfo(chunk.skip);
+ lines = lines.concat(Array(skip[side]).fill(''));
} else if (chunk.ab) {
lines = lines.concat(chunk.ab);
} else if (side === Side.LEFT && chunk.a) {