2016-06-27 12:19:21 -07:00
|
|
|
// Copyright (C) 2016 The Android Open Source Project
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
(function() {
|
|
|
|
'use strict';
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const WHOLE_FILE = -1;
|
2016-06-27 12:19:21 -07:00
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const DiffSide = {
|
2016-06-27 12:19:21 -07:00
|
|
|
LEFT: 'left',
|
|
|
|
RIGHT: 'right',
|
|
|
|
};
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const DiffGroupType = {
|
2016-06-27 12:19:21 -07:00
|
|
|
ADDED: 'b',
|
|
|
|
BOTH: 'ab',
|
|
|
|
REMOVED: 'a',
|
|
|
|
};
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const DiffHighlights = {
|
2016-06-27 12:19:21 -07:00
|
|
|
ADDED: 'edit_b',
|
|
|
|
REMOVED: 'edit_a',
|
|
|
|
};
|
|
|
|
|
2016-08-05 15:56:33 -07:00
|
|
|
/**
|
|
|
|
* The maximum size for an addition or removal chunk before it is broken down
|
|
|
|
* into a series of chunks that are this size at most.
|
|
|
|
*
|
2017-05-04 10:01:52 -07:00
|
|
|
* Note: The value of 120 is chosen so that it is larger than the default
|
2016-08-05 15:56:33 -07:00
|
|
|
* _asyncThreshold of 64, but feel free to tune this constant to your
|
|
|
|
* performance needs.
|
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
const MAX_GROUP_SIZE = 120;
|
2016-08-05 15:56:33 -07:00
|
|
|
|
2016-06-27 12:19:21 -07:00
|
|
|
Polymer({
|
|
|
|
is: 'gr-diff-processor',
|
|
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The amount of context around collapsed groups.
|
|
|
|
*/
|
|
|
|
context: Number,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The array of groups output by the processor.
|
|
|
|
*/
|
|
|
|
groups: {
|
|
|
|
type: Array,
|
|
|
|
notify: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Locations that should not be collapsed, including the locations of
|
|
|
|
* comments.
|
|
|
|
*/
|
|
|
|
keyLocations: {
|
|
|
|
type: Object,
|
2017-05-16 14:28:28 -07:00
|
|
|
value() { return {left: {}, right: {}}; },
|
2016-06-27 12:19:21 -07:00
|
|
|
},
|
2016-06-28 16:40:46 -07:00
|
|
|
|
2016-07-12 15:06:51 -07:00
|
|
|
/**
|
|
|
|
* The maximum number of lines to process synchronously.
|
|
|
|
*/
|
|
|
|
_asyncThreshold: {
|
|
|
|
type: Number,
|
|
|
|
value: 64,
|
|
|
|
},
|
|
|
|
|
2017-08-11 16:32:47 -07:00
|
|
|
/** @type {number|undefined} */
|
2016-06-28 16:40:46 -07:00
|
|
|
_nextStepHandle: Number,
|
2016-08-22 16:56:23 -07:00
|
|
|
_isScrolling: Boolean,
|
|
|
|
},
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
attached() {
|
2016-08-22 16:56:23 -07:00
|
|
|
this.listen(window, 'scroll', '_handleWindowScroll');
|
|
|
|
},
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
detached() {
|
2016-10-07 16:56:43 -07:00
|
|
|
this.cancel();
|
2016-08-22 16:56:23 -07:00
|
|
|
this.unlisten(window, 'scroll', '_handleWindowScroll');
|
|
|
|
},
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
_handleWindowScroll() {
|
2016-08-22 16:56:23 -07:00
|
|
|
this._isScrolling = true;
|
2017-05-16 14:28:28 -07:00
|
|
|
this.debounce('resetIsScrolling', () => {
|
2016-08-22 16:56:23 -07:00
|
|
|
this._isScrolling = false;
|
|
|
|
}, 50);
|
2016-06-27 12:19:21 -07:00
|
|
|
},
|
|
|
|
|
2016-06-28 10:33:33 -07:00
|
|
|
/**
|
|
|
|
* Asynchronously process the diff object into groups. As it processes, it
|
|
|
|
* will splice groups into the `groups` property of the component.
|
|
|
|
* @return {Promise} A promise that resolves when the diff is completely
|
|
|
|
* processed.
|
|
|
|
*/
|
2017-11-27 10:07:44 -08:00
|
|
|
process(content, isBinary) {
|
2017-03-29 17:29:17 -07:00
|
|
|
this.groups = [];
|
|
|
|
this.push('groups', this._makeFileComments());
|
|
|
|
|
2017-11-27 10:07:44 -08:00
|
|
|
// If it's a binary diff, we won't be rendering hunks of text differences
|
|
|
|
// so finish processing.
|
|
|
|
if (isBinary) { return Promise.resolve(); }
|
2016-06-28 10:33:33 -07:00
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
return new Promise(resolve => {
|
|
|
|
const state = {
|
2016-06-28 10:33:33 -07:00
|
|
|
lineNums: {left: 0, right: 0},
|
|
|
|
sectionIndex: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
content = this._splitCommonGroupsWithComments(content);
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
let currentBatch = 0;
|
|
|
|
const nextStep = () => {
|
2016-08-22 16:56:23 -07:00
|
|
|
if (this._isScrolling) {
|
|
|
|
this.async(nextStep, 100);
|
|
|
|
return;
|
|
|
|
}
|
2016-06-28 10:33:33 -07:00
|
|
|
// If we are done, resolve the promise.
|
|
|
|
if (state.sectionIndex >= content.length) {
|
|
|
|
resolve(this.groups);
|
2016-06-28 16:40:46 -07:00
|
|
|
this._nextStepHandle = undefined;
|
2016-06-28 10:33:33 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process the next section and incorporate the result.
|
2017-05-16 14:28:28 -07:00
|
|
|
const result = this._processNext(state, content);
|
|
|
|
for (const group of result.groups) {
|
2016-06-28 10:33:33 -07:00
|
|
|
this.push('groups', group);
|
2016-07-12 15:06:51 -07:00
|
|
|
currentBatch += group.lines.length;
|
2017-05-16 14:28:28 -07:00
|
|
|
}
|
2016-06-28 10:33:33 -07:00
|
|
|
state.lineNums.left += result.lineDelta.left;
|
|
|
|
state.lineNums.right += result.lineDelta.right;
|
|
|
|
|
|
|
|
// Increment the index and recurse.
|
|
|
|
state.sectionIndex++;
|
2016-07-12 15:06:51 -07:00
|
|
|
if (currentBatch >= this._asyncThreshold) {
|
|
|
|
currentBatch = 0;
|
|
|
|
this._nextStepHandle = this.async(nextStep, 1);
|
|
|
|
} else {
|
|
|
|
nextStep.call(this);
|
|
|
|
}
|
2016-06-28 10:33:33 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
nextStep.call(this);
|
2017-05-16 14:28:28 -07:00
|
|
|
});
|
2016-06-27 12:19:21 -07:00
|
|
|
},
|
|
|
|
|
2016-06-28 16:40:46 -07:00
|
|
|
/**
|
|
|
|
* Cancel any jobs that are running.
|
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
cancel() {
|
2016-06-28 16:40:46 -07:00
|
|
|
if (this._nextStepHandle !== undefined) {
|
|
|
|
this.cancelAsync(this._nextStepHandle);
|
|
|
|
this._nextStepHandle = undefined;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-06-28 10:33:33 -07:00
|
|
|
/**
|
|
|
|
* Process the next section of the diff.
|
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
_processNext(state, content) {
|
|
|
|
const section = content[state.sectionIndex];
|
2016-06-27 12:19:21 -07:00
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const rows = {
|
2016-06-28 10:33:33 -07:00
|
|
|
both: section[DiffGroupType.BOTH] || null,
|
|
|
|
added: section[DiffGroupType.ADDED] || null,
|
|
|
|
removed: section[DiffGroupType.REMOVED] || null,
|
|
|
|
};
|
2016-06-27 12:19:21 -07:00
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const highlights = {
|
2016-06-28 10:33:33 -07:00
|
|
|
added: section[DiffHighlights.ADDED] || null,
|
|
|
|
removed: section[DiffHighlights.REMOVED] || null,
|
2016-06-27 12:19:21 -07:00
|
|
|
};
|
|
|
|
|
2016-06-28 10:33:33 -07:00
|
|
|
if (rows.both) { // If it's a shared section.
|
2017-05-16 14:28:28 -07:00
|
|
|
let sectionEnd = null;
|
2016-06-28 10:33:33 -07:00
|
|
|
if (state.sectionIndex === 0) {
|
|
|
|
sectionEnd = 'first';
|
2016-07-13 21:06:03 +02:00
|
|
|
} else if (state.sectionIndex === content.length - 1) {
|
2016-06-28 10:33:33 -07:00
|
|
|
sectionEnd = 'last';
|
2016-06-27 12:19:21 -07:00
|
|
|
}
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const sharedGroups = this._sharedGroupsFromRows(
|
2016-06-28 10:33:33 -07:00
|
|
|
rows.both,
|
|
|
|
content.length > 1 ? this.context : WHOLE_FILE,
|
|
|
|
state.lineNums.left,
|
|
|
|
state.lineNums.right,
|
|
|
|
sectionEnd);
|
|
|
|
|
|
|
|
return {
|
|
|
|
lineDelta: {
|
|
|
|
left: rows.both.length,
|
|
|
|
right: rows.both.length,
|
|
|
|
},
|
|
|
|
groups: sharedGroups,
|
|
|
|
};
|
|
|
|
} else { // Otherwise it's a delta section.
|
2017-05-16 14:28:28 -07:00
|
|
|
const deltaGroup = this._deltaGroupFromRows(
|
2016-06-28 10:33:33 -07:00
|
|
|
rows.added,
|
|
|
|
rows.removed,
|
|
|
|
state.lineNums.left,
|
|
|
|
state.lineNums.right,
|
|
|
|
highlights);
|
2017-05-05 17:48:31 +02:00
|
|
|
deltaGroup.dueToRebase = section.due_to_rebase;
|
2016-06-28 10:33:33 -07:00
|
|
|
|
|
|
|
return {
|
|
|
|
lineDelta: {
|
|
|
|
left: rows.removed ? rows.removed.length : 0,
|
|
|
|
right: rows.added ? rows.added.length : 0,
|
|
|
|
},
|
|
|
|
groups: [deltaGroup],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Take rows of a shared diff section and produce an array of corresponding
|
|
|
|
* (potentially collapsed) groups.
|
2017-08-11 16:32:47 -07:00
|
|
|
* @param {!Array<string>} rows
|
|
|
|
* @param {number} context
|
|
|
|
* @param {number} startLineNumLeft
|
|
|
|
* @param {number} startLineNumRight
|
|
|
|
* @param {?string=} opt_sectionEnd String representing whether this is the
|
2016-06-28 10:33:33 -07:00
|
|
|
* first section or the last section or neither. Use the values 'first',
|
|
|
|
* 'last' and null respectively.
|
2017-08-11 16:32:47 -07:00
|
|
|
* @return {!Array<!Object>} Array of GrDiffGroup
|
2016-06-28 10:33:33 -07:00
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
_sharedGroupsFromRows(rows, context, startLineNumLeft,
|
2016-06-28 10:33:33 -07:00
|
|
|
startLineNumRight, opt_sectionEnd) {
|
2017-05-16 14:28:28 -07:00
|
|
|
const result = [];
|
|
|
|
const lines = [];
|
|
|
|
let line;
|
2016-06-28 10:33:33 -07:00
|
|
|
|
|
|
|
// Map each row to a GrDiffLine.
|
2017-05-16 14:28:28 -07:00
|
|
|
for (let i = 0; i < rows.length; i++) {
|
2016-06-28 10:33:33 -07:00
|
|
|
line = new GrDiffLine(GrDiffLine.Type.BOTH);
|
|
|
|
line.text = rows[i];
|
|
|
|
line.beforeNumber = ++startLineNumLeft;
|
|
|
|
line.afterNumber = ++startLineNumRight;
|
|
|
|
lines.push(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the hidden range based on the user's context preference. If this
|
|
|
|
// is the first or the last section of the diff, make sure the collapsed
|
|
|
|
// part of the section extends to the edge of the file.
|
2017-05-16 14:28:28 -07:00
|
|
|
const hiddenRange = [context, rows.length - context];
|
2016-06-28 10:33:33 -07:00
|
|
|
if (opt_sectionEnd === 'first') {
|
|
|
|
hiddenRange[0] = 0;
|
|
|
|
} else if (opt_sectionEnd === 'last') {
|
|
|
|
hiddenRange[1] = rows.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is a range to hide.
|
2016-10-12 10:45:42 -07:00
|
|
|
if (context !== WHOLE_FILE && hiddenRange[1] - hiddenRange[0] > 1) {
|
2017-05-16 14:28:28 -07:00
|
|
|
const linesBeforeCtx = lines.slice(0, hiddenRange[0]);
|
|
|
|
const hiddenLines = lines.slice(hiddenRange[0], hiddenRange[1]);
|
|
|
|
const linesAfterCtx = lines.slice(hiddenRange[1]);
|
2016-06-28 10:33:33 -07:00
|
|
|
|
|
|
|
if (linesBeforeCtx.length > 0) {
|
|
|
|
result.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, linesBeforeCtx));
|
2016-06-27 12:19:21 -07:00
|
|
|
}
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const ctxLine = new GrDiffLine(GrDiffLine.Type.CONTEXT_CONTROL);
|
2016-06-28 10:33:33 -07:00
|
|
|
ctxLine.contextGroup =
|
|
|
|
new GrDiffGroup(GrDiffGroup.Type.BOTH, hiddenLines);
|
|
|
|
result.push(new GrDiffGroup(GrDiffGroup.Type.CONTEXT_CONTROL,
|
|
|
|
[ctxLine]));
|
|
|
|
|
|
|
|
if (linesAfterCtx.length > 0) {
|
|
|
|
result.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, linesAfterCtx));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, lines));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Take the rows of a delta diff section and produce the corresponding
|
|
|
|
* group.
|
2017-08-11 16:32:47 -07:00
|
|
|
* @param {!Array<string>} rowsAdded
|
|
|
|
* @param {!Array<string>} rowsRemoved
|
|
|
|
* @param {number} startLineNumLeft
|
|
|
|
* @param {number} startLineNumRight
|
|
|
|
* @return {!Object} (Gr-Diff-Group)
|
2016-06-28 10:33:33 -07:00
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
_deltaGroupFromRows(rowsAdded, rowsRemoved, startLineNumLeft,
|
2016-06-28 10:33:33 -07:00
|
|
|
startLineNumRight, highlights) {
|
2017-05-16 14:28:28 -07:00
|
|
|
let lines = [];
|
2016-06-28 10:33:33 -07:00
|
|
|
if (rowsRemoved) {
|
|
|
|
lines = lines.concat(this._deltaLinesFromRows(GrDiffLine.Type.REMOVE,
|
|
|
|
rowsRemoved, startLineNumLeft, highlights.removed));
|
|
|
|
}
|
|
|
|
if (rowsAdded) {
|
|
|
|
lines = lines.concat(this._deltaLinesFromRows(GrDiffLine.Type.ADD,
|
|
|
|
rowsAdded, startLineNumRight, highlights.added));
|
|
|
|
}
|
|
|
|
return new GrDiffGroup(GrDiffGroup.Type.DELTA, lines);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2017-08-11 16:32:47 -07:00
|
|
|
* @return {!Array<!Object>} Array of GrDiffLines
|
2016-06-28 10:33:33 -07:00
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
_deltaLinesFromRows(lineType, rows, startLineNum,
|
2016-06-28 10:33:33 -07:00
|
|
|
opt_highlights) {
|
|
|
|
// Normalize highlights if they have been passed.
|
|
|
|
if (opt_highlights) {
|
|
|
|
opt_highlights = this._normalizeIntralineHighlights(rows,
|
|
|
|
opt_highlights);
|
|
|
|
}
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const lines = [];
|
|
|
|
let line;
|
|
|
|
for (let i = 0; i < rows.length; i++) {
|
2016-06-28 10:33:33 -07:00
|
|
|
line = new GrDiffLine(lineType);
|
|
|
|
line.text = rows[i];
|
|
|
|
if (lineType === GrDiffLine.Type.ADD) {
|
|
|
|
line.afterNumber = ++startLineNum;
|
|
|
|
} else {
|
|
|
|
line.beforeNumber = ++startLineNum;
|
|
|
|
}
|
|
|
|
if (opt_highlights) {
|
2017-05-16 14:28:28 -07:00
|
|
|
line.highlights = opt_highlights.filter(hl => hl.contentIndex === i);
|
2016-06-27 12:19:21 -07:00
|
|
|
}
|
2016-06-28 10:33:33 -07:00
|
|
|
lines.push(line);
|
2016-06-27 12:19:21 -07:00
|
|
|
}
|
2016-06-28 10:33:33 -07:00
|
|
|
return lines;
|
2016-06-27 12:19:21 -07:00
|
|
|
},
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
_makeFileComments() {
|
|
|
|
const line = new GrDiffLine(GrDiffLine.Type.BOTH);
|
2016-06-27 12:19:21 -07:00
|
|
|
line.beforeNumber = GrDiffLine.FILE;
|
|
|
|
line.afterNumber = GrDiffLine.FILE;
|
2016-06-28 10:33:33 -07:00
|
|
|
return new GrDiffGroup(GrDiffGroup.Type.BOTH, [line]);
|
2016-06-27 12:19:21 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* In order to show comments out of the bounds of the selected context,
|
|
|
|
* treat them as separate chunks within the model so that the content (and
|
|
|
|
* context surrounding it) renders correctly.
|
2017-08-11 16:32:47 -07:00
|
|
|
* @param {?} content The diff content object. (has to be iterable)
|
|
|
|
* @return {!Object} A new diff content object with regions split up.
|
2016-06-27 12:19:21 -07:00
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
_splitCommonGroupsWithComments(content) {
|
|
|
|
const result = [];
|
|
|
|
let leftLineNum = 0;
|
|
|
|
let rightLineNum = 0;
|
2016-06-28 10:33:33 -07:00
|
|
|
|
2017-03-13 13:35:47 -07:00
|
|
|
// If the context is set to "whole file", then break down the shared
|
|
|
|
// chunks so they can be rendered incrementally. Note: this is not enabled
|
|
|
|
// for any other context preference because manipulating the chunks in
|
|
|
|
// this way violates assumptions by the context grouper logic.
|
|
|
|
if (this.context === -1) {
|
2017-05-16 14:28:28 -07:00
|
|
|
const newContent = [];
|
|
|
|
for (const group of content) {
|
2017-05-04 10:01:52 -07:00
|
|
|
if (group.ab && group.ab.length > MAX_GROUP_SIZE * 2) {
|
|
|
|
// Split large shared groups in two, where the first is the maximum
|
|
|
|
// group size.
|
|
|
|
newContent.push({ab: group.ab.slice(0, MAX_GROUP_SIZE)});
|
|
|
|
newContent.push({ab: group.ab.slice(MAX_GROUP_SIZE)});
|
2017-03-13 13:35:47 -07:00
|
|
|
} else {
|
|
|
|
newContent.push(group);
|
|
|
|
}
|
2017-05-16 14:28:28 -07:00
|
|
|
}
|
2017-03-13 13:35:47 -07:00
|
|
|
content = newContent;
|
|
|
|
}
|
|
|
|
|
2016-06-28 10:33:33 -07:00
|
|
|
// For each section in the diff.
|
2017-05-16 14:28:28 -07:00
|
|
|
for (let i = 0; i < content.length; i++) {
|
2016-06-28 10:33:33 -07:00
|
|
|
// If it isn't a common group, append it as-is and update line numbers.
|
2016-06-27 12:19:21 -07:00
|
|
|
if (!content[i].ab) {
|
|
|
|
if (content[i].a) {
|
|
|
|
leftLineNum += content[i].a.length;
|
|
|
|
}
|
|
|
|
if (content[i].b) {
|
|
|
|
rightLineNum += content[i].b.length;
|
|
|
|
}
|
2016-08-05 15:56:33 -07:00
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
for (const group of this._breakdownGroup(content[i])) {
|
2016-08-05 15:56:33 -07:00
|
|
|
result.push(group);
|
2017-05-16 14:28:28 -07:00
|
|
|
}
|
2016-08-05 15:56:33 -07:00
|
|
|
|
2016-06-27 12:19:21 -07:00
|
|
|
continue;
|
|
|
|
}
|
2016-06-28 10:33:33 -07:00
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const chunk = content[i].ab;
|
|
|
|
let currentChunk = {ab: []};
|
2016-06-28 10:33:33 -07:00
|
|
|
|
|
|
|
// For each line in the common group.
|
2017-05-16 14:28:28 -07:00
|
|
|
for (const subChunk of chunk) {
|
2016-06-27 12:19:21 -07:00
|
|
|
leftLineNum++;
|
|
|
|
rightLineNum++;
|
|
|
|
|
2016-06-28 10:33:33 -07:00
|
|
|
// If this line should not be collapsed.
|
2016-06-27 12:19:21 -07:00
|
|
|
if (this.keyLocations[DiffSide.LEFT][leftLineNum] ||
|
|
|
|
this.keyLocations[DiffSide.RIGHT][rightLineNum]) {
|
2016-06-28 10:33:33 -07:00
|
|
|
// 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
|
|
|
|
// one.
|
2016-06-27 12:19:21 -07:00
|
|
|
if (currentChunk.ab && currentChunk.ab.length > 0) {
|
|
|
|
result.push(currentChunk);
|
|
|
|
currentChunk = {ab: []};
|
|
|
|
}
|
2016-06-28 10:33:33 -07:00
|
|
|
|
|
|
|
// Add the non-collapse line as its own chunk.
|
2017-05-16 14:28:28 -07:00
|
|
|
result.push({ab: [subChunk]});
|
2016-06-27 12:19:21 -07:00
|
|
|
} else {
|
2016-06-28 10:33:33 -07:00
|
|
|
// Append the current line to the current chunk.
|
2017-05-16 14:28:28 -07:00
|
|
|
currentChunk.ab.push(subChunk);
|
2016-06-27 12:19:21 -07:00
|
|
|
}
|
|
|
|
}
|
2016-06-28 10:33:33 -07:00
|
|
|
|
|
|
|
if (currentChunk.ab && currentChunk.ab.length > 0) {
|
2016-06-27 12:19:21 -07:00
|
|
|
result.push(currentChunk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-28 10:33:33 -07:00
|
|
|
return result;
|
2016-06-27 12:19:21 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The `highlights` array consists of a list of <skip length, mark length>
|
|
|
|
* pairs, where the skip length is the number of characters between the
|
|
|
|
* end of the previous edit and the start of this edit, and the mark
|
|
|
|
* length is the number of edited characters following the skip. The start
|
|
|
|
* of the edits is from the beginning of the related diff content lines.
|
|
|
|
*
|
|
|
|
* Note that the implied newline character at the end of each line is
|
|
|
|
* included in the length calculation, and thus it is possible for the
|
|
|
|
* edits to span newlines.
|
|
|
|
*
|
|
|
|
* A line highlight object consists of three fields:
|
|
|
|
* - contentIndex: The index of the diffChunk `content` field (the line
|
|
|
|
* being referred to).
|
|
|
|
* - startIndex: Where the highlight should begin.
|
|
|
|
* - endIndex: (optional) Where the highlight should end. If omitted, the
|
|
|
|
* highlight is meant to be a continuation onto the next line.
|
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
_normalizeIntralineHighlights(content, highlights) {
|
|
|
|
let contentIndex = 0;
|
|
|
|
let idx = 0;
|
|
|
|
const normalized = [];
|
|
|
|
for (const hl of highlights) {
|
|
|
|
let line = content[contentIndex] + '\n';
|
|
|
|
let j = 0;
|
2016-06-27 12:19:21 -07:00
|
|
|
while (j < hl[0]) {
|
|
|
|
if (idx === line.length) {
|
|
|
|
idx = 0;
|
|
|
|
line = content[++contentIndex] + '\n';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
j++;
|
|
|
|
}
|
2017-05-16 14:28:28 -07:00
|
|
|
let lineHighlight = {
|
|
|
|
contentIndex,
|
2016-06-27 12:19:21 -07:00
|
|
|
startIndex: idx,
|
|
|
|
};
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
while (line && j < hl[1]) {
|
|
|
|
if (idx === line.length) {
|
|
|
|
idx = 0;
|
|
|
|
line = content[++contentIndex] + '\n';
|
|
|
|
normalized.push(lineHighlight);
|
|
|
|
lineHighlight = {
|
2017-05-16 14:28:28 -07:00
|
|
|
contentIndex,
|
2016-06-27 12:19:21 -07:00
|
|
|
startIndex: idx,
|
|
|
|
};
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
lineHighlight.endIndex = idx;
|
|
|
|
normalized.push(lineHighlight);
|
|
|
|
}
|
|
|
|
return normalized;
|
|
|
|
},
|
2016-08-05 15:56:33 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 section
|
|
|
|
* or a delta it is returned as the single element of the result array.
|
2017-08-11 16:32:47 -07:00
|
|
|
* @param {!Object} group A raw chunk from a diff response.
|
2016-08-05 15:56:33 -07:00
|
|
|
* @return {!Array<!Array<!Object>>}
|
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
_breakdownGroup(group) {
|
|
|
|
let key = null;
|
2016-08-05 15:56:33 -07:00
|
|
|
if (group.a && !group.b) {
|
|
|
|
key = 'a';
|
|
|
|
} else if (group.b && !group.a) {
|
|
|
|
key = 'b';
|
2017-03-13 13:35:47 -07:00
|
|
|
} else if (group.ab) {
|
|
|
|
key = 'ab';
|
2016-08-05 15:56:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!key) { return [group]; }
|
|
|
|
|
|
|
|
return this._breakdown(group[key], MAX_GROUP_SIZE)
|
2017-05-16 14:28:28 -07:00
|
|
|
.map(subgroupLines => {
|
|
|
|
const subGroup = {};
|
|
|
|
subGroup[key] = subgroupLines;
|
2017-05-09 13:49:07 +02:00
|
|
|
if (group.due_to_rebase) {
|
|
|
|
subGroup.due_to_rebase = true;
|
|
|
|
}
|
2017-05-16 14:28:28 -07:00
|
|
|
return subGroup;
|
|
|
|
});
|
2016-08-05 15:56:33 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given an array and a size, return an array of arrays where no inner array
|
|
|
|
* is larger than that size, preserving the original order.
|
2016-08-22 16:56:23 -07:00
|
|
|
* @param {!Array<T>} array
|
|
|
|
* @param {number} size
|
2016-08-05 15:56:33 -07:00
|
|
|
* @return {!Array<!Array<T>>}
|
|
|
|
* @template T
|
|
|
|
*/
|
2017-05-16 14:28:28 -07:00
|
|
|
_breakdown(array, size) {
|
2016-08-05 15:56:33 -07:00
|
|
|
if (!array.length) { return []; }
|
|
|
|
if (array.length < size) { return [array]; }
|
|
|
|
|
2017-05-16 14:28:28 -07:00
|
|
|
const head = array.slice(0, array.length - size);
|
|
|
|
const tail = array.slice(array.length - size);
|
2016-08-05 15:56:33 -07:00
|
|
|
|
2016-08-22 16:56:23 -07:00
|
|
|
return this._breakdown(head, size).concat([tail]);
|
2016-08-05 15:56:33 -07:00
|
|
|
},
|
2016-06-27 12:19:21 -07:00
|
|
|
});
|
|
|
|
})();
|