Convert files to typescript

The change converts the following files to typescript:

* elements/diff/gr-diff-processor/gr-diff-processor.ts

Change-Id: I4aa8427d25547d2211e0dfb40cf13a53c47f4b6b
This commit is contained in:
Ben Rohlfs
2020-08-14 22:08:37 +02:00
parent 60cfc4b201
commit 32b8382c64
4 changed files with 304 additions and 272 deletions

View File

@@ -14,24 +14,44 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js'; import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
import {PolymerElement} from '@polymer/polymer/polymer-element.js'; import {PolymerElement} from '@polymer/polymer/polymer-element';
import {GrDiffLine, GrDiffLineType, FILE} from '../gr-diff/gr-diff-line.js'; import {
import {GrDiffGroup, GrDiffGroupType, hideInContextControl} from '../gr-diff/gr-diff-group.js'; GrDiffLine,
import {util} from '../../../scripts/util.js'; GrDiffLineType,
FILE,
Highlights,
} from '../gr-diff/gr-diff-line';
import {
GrDiffGroup,
GrDiffGroupType,
hideInContextControl,
} from '../gr-diff/gr-diff-group';
import {CancelablePromise, util} from '../../../scripts/util';
import {customElement, property} from '@polymer/decorators';
import {DiffContent} from '../../../types/common';
import {DiffSide} from '../gr-diff/gr-diff-utils';
const WHOLE_FILE = -1; const WHOLE_FILE = -1;
const DiffSide = { interface State {
LEFT: 'left', lineNums: {
RIGHT: 'right', left: number;
}; right: number;
};
chunkIndex: number;
}
const DiffHighlights = { interface ChunkEnd {
ADDED: 'edit_b', offset: number;
REMOVED: 'edit_a', keyLocation: boolean;
}; }
interface KeyLocations {
left: {[key: string]: boolean};
right: {[key: string]: boolean};
}
/** /**
* The maximum size for an addition or removal chunk before it is broken down * The maximum size for an addition or removal chunk before it is broken down
@@ -67,63 +87,31 @@ const MAX_GROUP_SIZE = 120;
* "expand context" widget. This may require splitting a chunk/group so * "expand context" widget. This may require splitting a chunk/group so
* that the part that is within the context or has comments is shown, while * that the part that is within the context or has comments is shown, while
* the rest is not. * the rest is not.
*
* @extends PolymerElement
*/ */
class GrDiffProcessor extends GestureEventListeners( @customElement('gr-diff-processor')
LegacyElementMixin( export class GrDiffProcessor extends GestureEventListeners(
PolymerElement)) { LegacyElementMixin(PolymerElement)
static get is() { return 'gr-diff-processor'; } ) {
@property({type: Number})
context = 3;
static get properties() { @property({type: Array, notify: true})
return { groups: GrDiffGroup[] = [];
/** @property({type: Object})
* The amount of context around collapsed groups. keyLocations: KeyLocations = {left: {}, right: {}};
*/
context: Number,
/** @property({type: Number})
* The array of groups output by the processor. _asyncThreshold = 64;
*/
groups: {
type: Array,
notify: true,
},
/** @property({type: Number})
* Locations that should not be collapsed, including the locations of _nextStepHandle: number | null = null;
* comments.
*/
keyLocations: {
type: Object,
value() { return {left: {}, right: {}}; },
},
/** @property({type: Object})
* The maximum number of lines to process synchronously. _processPromise: CancelablePromise<void> | null = null;
*/
_asyncThreshold: {
type: Number,
value: 64,
},
/** @type {?number} */ @property({type: Boolean})
_nextStepHandle: Number, _isScrolling?: boolean;
/**
* The promise last returned from `process()` while the asynchronous
* processing is running - `null` otherwise. Provides a `cancel()`
* method that rejects it with `{isCancelled: true}`.
*
* @type {?Object}
*/
_processPromise: {
type: Object,
value: null,
},
_isScrolling: Boolean,
};
}
/** @override */ /** @override */
attached() { attached() {
@@ -140,22 +128,23 @@ class GrDiffProcessor extends GestureEventListeners(
_handleWindowScroll() { _handleWindowScroll() {
this._isScrolling = true; this._isScrolling = true;
this.debounce('resetIsScrolling', () => { this.debounce(
this._isScrolling = false; 'resetIsScrolling',
}, 50); () => {
this._isScrolling = false;
},
50
);
} }
/** /**
* Asynchronously process the diff chunks into groups. As it processes, it * Asynchronously process the diff chunks into groups. As it processes, it
* will splice groups into the `groups` property of the component. * will splice groups into the `groups` property of the component.
* *
* @param {!Array<!Gerrit.DiffChunk>} chunks * @return A promise that resolves with an
* @param {boolean} isBinary * array of GrDiffGroups when the diff is completely processed.
*
* @return {!Promise<!Array<!Object>>} A promise that resolves with an
* array of GrDiffGroups when the diff is completely processed.
*/ */
process(chunks, isBinary) { process(chunks: DiffContent[], isBinary: boolean) {
// Cancel any still running process() calls, because they append to the // Cancel any still running process() calls, because they append to the
// same groups field. // same groups field.
this.cancel(); this.cancel();
@@ -165,61 +154,65 @@ class GrDiffProcessor extends GestureEventListeners(
// If it's a binary diff, we won't be rendering hunks of text differences // If it's a binary diff, we won't be rendering hunks of text differences
// so finish processing. // so finish processing.
if (isBinary) { return Promise.resolve(); } if (isBinary) {
return Promise.resolve();
}
this._processPromise = util.makeCancelable( this._processPromise = util.makeCancelable(
new Promise(resolve => { new Promise(resolve => {
const state = { const state = {
lineNums: {left: 0, right: 0}, lineNums: {left: 0, right: 0},
chunkIndex: 0, chunkIndex: 0,
}; };
chunks = this._splitLargeChunks(chunks); chunks = this._splitLargeChunks(chunks);
chunks = this._splitCommonChunksWithKeyLocations(chunks); chunks = this._splitCommonChunksWithKeyLocations(chunks);
let currentBatch = 0; let currentBatch = 0;
const nextStep = () => { const nextStep = () => {
if (this._isScrolling) { if (this._isScrolling) {
this._nextStepHandle = this.async(nextStep, 100); this._nextStepHandle = this.async(nextStep, 100);
return; return;
} }
// If we are done, resolve the promise. // If we are done, resolve the promise.
if (state.chunkIndex >= chunks.length) { if (state.chunkIndex >= chunks.length) {
resolve(); resolve();
this._nextStepHandle = null; this._nextStepHandle = null;
return; return;
} }
// Process the next chunk and incorporate the result. // Process the next chunk and incorporate the result.
const stateUpdate = this._processNext(state, chunks); const stateUpdate = this._processNext(state, chunks);
for (const group of stateUpdate.groups) { for (const group of stateUpdate.groups) {
this.push('groups', group); this.push('groups', group);
currentBatch += group.lines.length; currentBatch += group.lines.length;
} }
state.lineNums.left += stateUpdate.lineDelta.left; state.lineNums.left += stateUpdate.lineDelta.left;
state.lineNums.right += stateUpdate.lineDelta.right; state.lineNums.right += stateUpdate.lineDelta.right;
// Increment the index and recurse. // Increment the index and recurse.
state.chunkIndex = stateUpdate.newChunkIndex; state.chunkIndex = stateUpdate.newChunkIndex;
if (currentBatch >= this._asyncThreshold) { if (currentBatch >= this._asyncThreshold) {
currentBatch = 0; currentBatch = 0;
this._nextStepHandle = this.async(nextStep, 1); this._nextStepHandle = this.async(nextStep, 1);
} else { } else {
nextStep.call(this); nextStep.call(this);
} }
}; };
nextStep.call(this); nextStep.call(this);
})); })
return this._processPromise );
.finally(() => { this._processPromise = null; }); return this._processPromise.finally(() => {
this._processPromise = null;
});
} }
/** /**
* Cancel any jobs that are running. * Cancel any jobs that are running.
*/ */
cancel() { cancel() {
if (this._nextStepHandle != null) { if (this._nextStepHandle !== null) {
this.cancelAsync(this._nextStepHandle); this.cancelAsync(this._nextStepHandle);
this._nextStepHandle = null; this._nextStepHandle = null;
} }
@@ -230,14 +223,12 @@ class GrDiffProcessor extends GestureEventListeners(
/** /**
* Process the next uncollapsible chunk, or the next collapsible chunks. * Process the next uncollapsible chunk, or the next collapsible chunks.
*
* @param {!Object} state
* @param {!Array<!Object>} chunks
* @return {{lineDelta: {left: number, right: number}, groups: !Array<!Object>, newChunkIndex: number}}
*/ */
_processNext(state, chunks) { _processNext(state: State, chunks: DiffContent[]) {
const firstUncollapsibleChunkIndex = const firstUncollapsibleChunkIndex = this._firstUncollapsibleChunkIndex(
this._firstUncollapsibleChunkIndex(chunks, state.chunkIndex); chunks,
state.chunkIndex
);
if (firstUncollapsibleChunkIndex === state.chunkIndex) { if (firstUncollapsibleChunkIndex === state.chunkIndex) {
const chunk = chunks[state.chunkIndex]; const chunk = chunks[state.chunkIndex];
return { return {
@@ -245,34 +236,44 @@ class GrDiffProcessor extends GestureEventListeners(
left: this._linesLeft(chunk).length, left: this._linesLeft(chunk).length,
right: this._linesRight(chunk).length, right: this._linesRight(chunk).length,
}, },
groups: [this._chunkToGroup( groups: [
chunk, state.lineNums.left + 1, state.lineNums.right + 1)], this._chunkToGroup(
chunk,
state.lineNums.left + 1,
state.lineNums.right + 1
),
],
newChunkIndex: state.chunkIndex + 1, newChunkIndex: state.chunkIndex + 1,
}; };
} }
return this._processCollapsibleChunks( return this._processCollapsibleChunks(
state, chunks, firstUncollapsibleChunkIndex); state,
chunks,
firstUncollapsibleChunkIndex
);
} }
_linesLeft(chunk) { _linesLeft(chunk: DiffContent) {
return chunk.ab || chunk.a || []; return chunk.ab || chunk.a || [];
} }
_linesRight(chunk) { _linesRight(chunk: DiffContent) {
return chunk.ab || chunk.b || []; return chunk.ab || chunk.b || [];
} }
_firstUncollapsibleChunkIndex(chunks, offset) { _firstUncollapsibleChunkIndex(chunks: DiffContent[], offset: number) {
let chunkIndex = offset; let chunkIndex = offset;
while (chunkIndex < chunks.length && while (
this._isCollapsibleChunk(chunks[chunkIndex])) { chunkIndex < chunks.length &&
this._isCollapsibleChunk(chunks[chunkIndex])
) {
chunkIndex++; chunkIndex++;
} }
return chunkIndex; return chunkIndex;
} }
_isCollapsibleChunk(chunk) { _isCollapsibleChunk(chunk: DiffContent) {
return (chunk.ab || chunk.common) && !chunk.keyLocation; return (chunk.ab || chunk.common) && !chunk.keyLocation;
} }
@@ -280,36 +281,38 @@ class GrDiffProcessor extends GestureEventListeners(
* Process a stretch of collapsible chunks. * Process a stretch of collapsible chunks.
* *
* Outputs up to three groups: * Outputs up to three groups:
* 1) Visible context before the hidden common code, unless it's the * 1) Visible context before the hidden common code, unless it's the
* very beginning of the file. * very beginning of the file.
* 2) Context hidden behind a context bar, unless empty. * 2) Context hidden behind a context bar, unless empty.
* 3) Visible context after the hidden common code, unless it's the very * 3) Visible context after the hidden common code, unless it's the very
* end of the file. * end of the file.
*
* @param {!Object} state
* @param {!Array<Object>} chunks
* @param {number} firstUncollapsibleChunkIndex
* @return {{lineDelta: {left: number, right: number}, groups: !Array<!Object>, newChunkIndex: number}}
*/ */
_processCollapsibleChunks( _processCollapsibleChunks(
state, chunks, firstUncollapsibleChunkIndex) { state: State,
chunks: DiffContent[],
firstUncollapsibleChunkIndex: number
) {
const collapsibleChunks = chunks.slice( const collapsibleChunks = chunks.slice(
state.chunkIndex, firstUncollapsibleChunkIndex); state.chunkIndex,
firstUncollapsibleChunkIndex
);
const lineCount = collapsibleChunks.reduce( const lineCount = collapsibleChunks.reduce(
(sum, chunk) => sum + this._commonChunkLength(chunk), 0); (sum, chunk) => sum + this._commonChunkLength(chunk),
0
);
let groups = this._chunksToGroups( let groups = this._chunksToGroups(
collapsibleChunks, collapsibleChunks,
state.lineNums.left + 1, state.lineNums.left + 1,
state.lineNums.right + 1); state.lineNums.right + 1
);
if (this.context !== WHOLE_FILE) { if (this.context !== WHOLE_FILE) {
const hiddenStart = state.chunkIndex === 0 ? 0 : this.context; const hiddenStart = state.chunkIndex === 0 ? 0 : this.context;
const hiddenEnd = lineCount - ( const hiddenEnd =
firstUncollapsibleChunkIndex === chunks.length ? lineCount -
0 : this.context); (firstUncollapsibleChunkIndex === chunks.length ? 0 : this.context);
groups = hideInContextControl( groups = hideInContextControl(groups, hiddenStart, hiddenEnd);
groups, hiddenStart, hiddenEnd);
} }
return { return {
@@ -322,21 +325,21 @@ class GrDiffProcessor extends GestureEventListeners(
}; };
} }
_commonChunkLength(chunk) { _commonChunkLength(chunk: DiffContent) {
console.assert(chunk.ab || chunk.common); console.assert(!!chunk.ab || !!chunk.common);
console.assert( console.assert(
!chunk.a || (chunk.b && chunk.a.length === chunk.b.length), !chunk.a || (!!chunk.b && chunk.a.length === chunk.b.length),
`common chunk needs same number of a and b lines: `, chunk); 'common chunk needs same number of a and b lines: ',
chunk
);
return this._linesLeft(chunk).length; return this._linesLeft(chunk).length;
} }
/** _chunksToGroups(
* @param {!Array<!Object>} chunks chunks: DiffContent[],
* @param {number} offsetLeft offsetLeft: number,
* @param {number} offsetRight offsetRight: number
* @return {!Array<!Object>} (GrDiffGroup) ): GrDiffGroup[] {
*/
_chunksToGroups(chunks, offsetLeft, offsetRight) {
return chunks.map(chunk => { return chunks.map(chunk => {
const group = this._chunkToGroup(chunk, offsetLeft, offsetRight); const group = this._chunkToGroup(chunk, offsetLeft, offsetRight);
const chunkLength = this._commonChunkLength(chunk); const chunkLength = this._commonChunkLength(chunk);
@@ -346,76 +349,83 @@ class GrDiffProcessor extends GestureEventListeners(
}); });
} }
/** _chunkToGroup(
* @param {!Object} chunk chunk: DiffContent,
* @param {number} offsetLeft offsetLeft: number,
* @param {number} offsetRight offsetRight: number
* @return {!Object} (GrDiffGroup) ): GrDiffGroup {
*/
_chunkToGroup(chunk, offsetLeft, offsetRight) {
const type = chunk.ab ? GrDiffGroupType.BOTH : GrDiffGroupType.DELTA; const type = chunk.ab ? GrDiffGroupType.BOTH : GrDiffGroupType.DELTA;
const lines = this._linesFromChunk(chunk, offsetLeft, offsetRight); const lines = this._linesFromChunk(chunk, offsetLeft, offsetRight);
const group = new GrDiffGroup(type, lines); const group = new GrDiffGroup(type, lines);
group.keyLocation = chunk.keyLocation; group.keyLocation = !!chunk.keyLocation;
group.dueToRebase = chunk.due_to_rebase; group.dueToRebase = !!chunk.due_to_rebase;
group.ignoredWhitespaceOnly = chunk.common; group.ignoredWhitespaceOnly = !!chunk.common;
return group; return group;
} }
_linesFromChunk(chunk, offsetLeft, offsetRight) { _linesFromChunk(chunk: DiffContent, offsetLeft: number, offsetRight: number) {
if (chunk.ab) { if (chunk.ab) {
return chunk.ab.map((row, i) => this._lineFromRow( return chunk.ab.map((row, i) =>
GrDiffLineType.BOTH, offsetLeft, offsetRight, row, i)); this._lineFromRow(GrDiffLineType.BOTH, offsetLeft, offsetRight, row, i)
);
} }
let lines = []; let lines: GrDiffLine[] = [];
if (chunk.a) { if (chunk.a) {
// Avoiding a.push(...b) because that causes callstack overflows for // Avoiding a.push(...b) because that causes callstack overflows for
// large b, which can occur when large files are added removed. // large b, which can occur when large files are added removed.
lines = lines.concat(this._linesFromRows( lines = lines.concat(
GrDiffLineType.REMOVE, chunk.a, offsetLeft, this._linesFromRows(
chunk[DiffHighlights.REMOVED])); GrDiffLineType.REMOVE,
chunk.a,
offsetLeft,
chunk.edit_a
)
);
} }
if (chunk.b) { if (chunk.b) {
// Avoiding a.push(...b) because that causes callstack overflows for // Avoiding a.push(...b) because that causes callstack overflows for
// large b, which can occur when large files are added removed. // large b, which can occur when large files are added removed.
lines = lines.concat(this._linesFromRows( lines = lines.concat(
GrDiffLineType.ADD, chunk.b, offsetRight, this._linesFromRows(
chunk[DiffHighlights.ADDED])); GrDiffLineType.ADD,
chunk.b,
offsetRight,
chunk.edit_b
)
);
} }
return lines; return lines;
} }
/** _linesFromRows(
* @param {string} lineType (GrDiffLineType) lineType: GrDiffLineType,
* @param {!Array<string>} rows rows: string[],
* @param {number} offset offset: number,
* @param {?Array<!Gerrit.IntralineInfo>=} opt_intralineInfos intralineInfos?: number[][]
* @return {!Array<!Object>} (GrDiffLine) ): GrDiffLine[] {
*/ const grDiffHighlights = intralineInfos
_linesFromRows(lineType, rows, offset, opt_intralineInfos) { ? this._convertIntralineInfos(rows, intralineInfos)
const grDiffHighlights = opt_intralineInfos ? : undefined;
this._convertIntralineInfos(rows, opt_intralineInfos) : undefined; return rows.map((row, i) =>
return rows.map((row, i) => this._lineFromRow( this._lineFromRow(lineType, offset, offset, row, i, grDiffHighlights)
lineType, offset, offset, row, i, grDiffHighlights)); );
} }
/** _lineFromRow(
* @param {string} type (GrDiffLineType) type: GrDiffLineType,
* @param {number} offsetLeft offsetLeft: number,
* @param {number} offsetRight offsetRight: number,
* @param {string} row row: string,
* @param {number} i i: number,
* @param {!Array<!Object>=} opt_highlights highlights: Highlights[] = []
* @return {!Object} (GrDiffLine) ): GrDiffLine {
*/
_lineFromRow(type, offsetLeft, offsetRight, row, i, opt_highlights) {
const line = new GrDiffLine(type); const line = new GrDiffLine(type);
line.text = row; line.text = row;
if (type !== GrDiffLineType.ADD) line.beforeNumber = offsetLeft + i; if (type !== GrDiffLineType.ADD) line.beforeNumber = offsetLeft + i;
if (type !== GrDiffLineType.REMOVE) line.afterNumber = offsetRight + i; if (type !== GrDiffLineType.REMOVE) line.afterNumber = offsetRight + i;
if (opt_highlights) { if (highlights) {
line.hasIntralineInfo = true; line.hasIntralineInfo = true;
line.highlights = opt_highlights.filter(hl => hl.contentIndex === i); line.highlights = highlights.filter(hl => hl.contentIndex === i);
} else { } else {
line.hasIntralineInfo = false; line.hasIntralineInfo = false;
} }
@@ -441,10 +451,10 @@ class GrDiffProcessor extends GestureEventListeners(
* into 2 chunks, one max sized one and the rest (for reasons that are * into 2 chunks, one max sized one and the rest (for reasons that are
* unclear to me). * unclear to me).
* *
* @param {!Array<!Gerrit.DiffChunk>} chunks Chunks as returned from the server * @param chunks Chunks as returned from the server
* @return {!Array<!Gerrit.DiffChunk>} Finer grained chunks. * @return Finer grained chunks.
*/ */
_splitLargeChunks(chunks) { _splitLargeChunks(chunks: DiffContent[]): DiffContent[] {
const newChunks = []; const newChunks = [];
for (const chunk of chunks) { for (const chunk of chunks) {
@@ -476,10 +486,10 @@ class GrDiffProcessor extends GestureEventListeners(
* the selected context, treat them as separate chunks within the model so * the selected context, treat them as separate chunks within the model so
* that the content (and context surrounding it) renders correctly. * that the content (and context surrounding it) renders correctly.
* *
* @param {!Array<!Object>} chunks DiffContents as returned from server. * @param chunks DiffContents as returned from server.
* @return {!Array<!Object>} Finer grained DiffContents. * @return Finer grained DiffContents.
*/ */
_splitCommonChunksWithKeyLocations(chunks) { _splitCommonChunksWithKeyLocations(chunks: DiffContent[]): DiffContent[] {
const result = []; const result = [];
let leftLineNum = 1; let leftLineNum = 1;
let rightLineNum = 1; let rightLineNum = 1;
@@ -497,32 +507,45 @@ class GrDiffProcessor extends GestureEventListeners(
continue; continue;
} }
if (chunk.common && chunk.a.length != chunk.b.length) { if (chunk.common && chunk.a!.length !== chunk.b!.length) {
throw new Error( throw new Error(
'DiffContent with common=true must always have equal length'); 'DiffContent with common=true must always have equal length'
);
} }
const numLines = this._commonChunkLength(chunk); const numLines = this._commonChunkLength(chunk);
const chunkEnds = this._findChunkEndsAtKeyLocations( const chunkEnds = this._findChunkEndsAtKeyLocations(
numLines, leftLineNum, rightLineNum); numLines,
leftLineNum,
rightLineNum
);
leftLineNum += numLines; leftLineNum += numLines;
rightLineNum += numLines; rightLineNum += numLines;
if (chunk.ab) { if (chunk.ab) {
result.push(...this._splitAtChunkEnds(chunk.ab, chunkEnds) result.push(
.map(({lines, keyLocation}) => { ...this._splitAtChunkEnds(chunk.ab, chunkEnds).map(
({lines, keyLocation}) => {
return { return {
...chunk, ...chunk,
ab: lines, ab: lines,
keyLocation, keyLocation,
}; };
})); }
)
);
} else if (chunk.common) { } else if (chunk.common) {
const aChunks = this._splitAtChunkEnds(chunk.a, chunkEnds); const aChunks = this._splitAtChunkEnds(chunk.a!, chunkEnds);
const bChunks = this._splitAtChunkEnds(chunk.b, chunkEnds); const bChunks = this._splitAtChunkEnds(chunk.b!, chunkEnds);
result.push(...aChunks.map(({lines, keyLocation}, i) => { result.push(
return { ...aChunks.map(({lines, keyLocation}, i) => {
...chunk, a: lines, b: bChunks[i].lines, keyLocation}; return {
})); ...chunk,
a: lines,
b: bChunks[i].lines,
keyLocation,
};
})
);
} }
} }
@@ -530,16 +553,22 @@ class GrDiffProcessor extends GestureEventListeners(
} }
/** /**
* @return {!Array<{offset: number, keyLocation: boolean}>} Offsets of the * @return Offsets of the new chunk ends, including whether it's a key
* new chunk ends, including whether it's a key location. * location.
*/ */
_findChunkEndsAtKeyLocations(numLines, leftOffset, rightOffset) { _findChunkEndsAtKeyLocations(
numLines: number,
leftOffset: number,
rightOffset: number
): ChunkEnd[] {
const result = []; const result = [];
let lastChunkEnd = 0; let lastChunkEnd = 0;
for (let i=0; i<numLines; i++) { for (let i = 0; i < numLines; i++) {
// If this line should not be collapsed. // If this line should not be collapsed.
if (this.keyLocations[DiffSide.LEFT][leftOffset + i] || if (
this.keyLocations[DiffSide.RIGHT][rightOffset + i]) { this.keyLocations[DiffSide.LEFT][leftOffset + i] ||
this.keyLocations[DiffSide.RIGHT][rightOffset + i]
) {
// If any lines have been accumulated into the chunk leading up to // If any lines have been accumulated into the chunk leading up to
// this non-collapse line, then add them as a chunk and start a new // this non-collapse line, then add them as a chunk and start a new
// one. // one.
@@ -560,12 +589,14 @@ class GrDiffProcessor extends GestureEventListeners(
return result; return result;
} }
_splitAtChunkEnds(lines, chunkEnds) { _splitAtChunkEnds(lines: string[], chunkEnds: ChunkEnd[]) {
const result = []; const result = [];
let lastChunkEndOffset = 0; let lastChunkEndOffset = 0;
for (const {offset, keyLocation} of chunkEnds) { for (const {offset, keyLocation} of chunkEnds) {
result.push( result.push({
{lines: lines.slice(lastChunkEndOffset, offset), keyLocation}); lines: lines.slice(lastChunkEndOffset, offset),
keyLocation,
});
lastChunkEndOffset = offset; lastChunkEndOffset = offset;
} }
return result; return result;
@@ -574,12 +605,11 @@ class GrDiffProcessor extends GestureEventListeners(
/** /**
* Converts `IntralineInfo`s return by the API to `GrLineHighlights` used * Converts `IntralineInfo`s return by the API to `GrLineHighlights` used
* for rendering. * for rendering.
*
* @param {!Array<string>} rows
* @param {!Array<!Gerrit.IntralineInfo>} intralineInfos
* @return {!Array<!Object>} (Highlights[] from GrDiffLine)
*/ */
_convertIntralineInfos(rows, intralineInfos) { _convertIntralineInfos(
rows: string[],
intralineInfos: number[][]
): Highlights[] {
let rowIndex = 0; let rowIndex = 0;
let idx = 0; let idx = 0;
const normalized = []; const normalized = [];
@@ -595,7 +625,7 @@ class GrDiffProcessor extends GestureEventListeners(
idx++; idx++;
j++; j++;
} }
let lineHighlight = { let lineHighlight: Highlights = {
contentIndex: rowIndex, contentIndex: rowIndex,
startIndex: idx, startIndex: idx,
}; };
@@ -625,12 +655,9 @@ class GrDiffProcessor extends GestureEventListeners(
* If a group is an addition or a removal, break it down into smaller groups * If a group is an addition or a removal, break it down into smaller groups
* of that type using the MAX_GROUP_SIZE. If the group is a shared chunk * of that type using the MAX_GROUP_SIZE. If the group is a shared chunk
* or a delta it is returned as the single element of the result array. * or a delta it is returned as the single element of the result array.
*
* @param {!Gerrit.DiffChunk} chunk A raw chunk from a diff response.
* @return {!Array<!Array<!Object>>}
*/ */
_breakdownChunk(chunk) { _breakdownChunk(chunk: DiffContent): DiffContent[] {
let key = null; let key: 'a' | 'b' | 'ab' | null = null;
if (chunk.a && !chunk.b) { if (chunk.a && !chunk.b) {
key = 'a'; key = 'a';
} else if (chunk.b && !chunk.a) { } else if (chunk.b && !chunk.a) {
@@ -639,31 +666,31 @@ class GrDiffProcessor extends GestureEventListeners(
key = 'ab'; key = 'ab';
} }
if (!key) { return [chunk]; } if (!key) {
return [chunk];
}
return this._breakdown(chunk[key], MAX_GROUP_SIZE) return this._breakdown(chunk[key]!, MAX_GROUP_SIZE).map(subChunkLines => {
.map(subChunkLines => { const subChunk: DiffContent = {};
const subChunk = {}; subChunk[key!] = subChunkLines;
subChunk[key] = subChunkLines; if (chunk.due_to_rebase) {
if (chunk.due_to_rebase) { subChunk.due_to_rebase = true;
subChunk.due_to_rebase = true; }
} return subChunk;
return subChunk; });
});
} }
/** /**
* Given an array and a size, return an array of arrays where no inner array * Given an array and a size, return an array of arrays where no inner array
* is larger than that size, preserving the original order. * is larger than that size, preserving the original order.
*
* @param {!Array<T>} array
* @param {number} size
* @return {!Array<!Array<T>>}
* @template T
*/ */
_breakdown(array, size) { _breakdown<T>(array: T[], size: number): T[][] {
if (!array.length) { return []; } if (!array.length) {
if (array.length < size) { return [array]; } return [];
}
if (array.length < size) {
return [array];
}
const head = array.slice(0, array.length - size); const head = array.slice(0, array.length - size);
const tail = array.slice(array.length - size); const tail = array.slice(array.length - size);
@@ -672,4 +699,8 @@ class GrDiffProcessor extends GestureEventListeners(
} }
} }
customElements.define(GrDiffProcessor.is, GrDiffProcessor); declare global {
interface HTMLElementTagNameMap {
'gr-diff-processor': GrDiffProcessor;
}
}

View File

@@ -35,7 +35,7 @@ export class GrDiffLine {
hasIntralineInfo = false; hasIntralineInfo = false;
readonly highlights: Highlights[] = []; highlights: Highlights[] = [];
text = ''; text = '';

View File

@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
interface CancelablePromise<T> extends Promise<T> { export interface CancelablePromise<T> extends Promise<T> {
cancel(): void; cancel(): void;
} }

View File

@@ -1043,16 +1043,17 @@ export interface LabelTypeInfo {
* https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#diff-content * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#diff-content
*/ */
export interface DiffContent { export interface DiffContent {
a?: string; a?: string[];
b?: string; b?: string[];
ab?: string; ab?: string[];
// The inner array is always of length two. The first entry is the 'skip' // The inner array is always of length two. The first entry is the 'skip'
// length. The second entry is the 'edit' length. // length. The second entry is the 'edit' length.
edit_a: number[][]; edit_a?: number[][];
edit_b: number[][]; edit_b?: number[][];
due_to_rebase: boolean; due_to_rebase?: boolean;
skip?: string; skip?: string;
common?: string; common?: string;
keyLocation?: boolean;
} }
/** /**