You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.1 KiB
244 lines
7.1 KiB
'use strict'; |
|
|
|
Object.defineProperty(exports, '__esModule', { |
|
value: true |
|
}); |
|
exports.default = void 0; |
|
|
|
var _cleanupSemantic = require('./cleanupSemantic'); |
|
|
|
function _defineProperty(obj, key, value) { |
|
if (key in obj) { |
|
Object.defineProperty(obj, key, { |
|
value: value, |
|
enumerable: true, |
|
configurable: true, |
|
writable: true |
|
}); |
|
} else { |
|
obj[key] = value; |
|
} |
|
return obj; |
|
} |
|
|
|
// Given change op and array of diffs, return concatenated string: |
|
// * include common strings |
|
// * include change strings which have argument op with changeColor |
|
// * exclude change strings which have opposite op |
|
const concatenateRelevantDiffs = (op, diffs, changeColor) => |
|
diffs.reduce( |
|
(reduced, diff) => |
|
reduced + |
|
(diff[0] === _cleanupSemantic.DIFF_EQUAL |
|
? diff[1] |
|
: diff[0] === op && diff[1].length !== 0 // empty if change is newline |
|
? changeColor(diff[1]) |
|
: ''), |
|
'' |
|
); // Encapsulate change lines until either a common newline or the end. |
|
|
|
class ChangeBuffer { |
|
// incomplete line |
|
// complete lines |
|
constructor(op, changeColor) { |
|
_defineProperty(this, 'op', void 0); |
|
|
|
_defineProperty(this, 'line', void 0); |
|
|
|
_defineProperty(this, 'lines', void 0); |
|
|
|
_defineProperty(this, 'changeColor', void 0); |
|
|
|
this.op = op; |
|
this.line = []; |
|
this.lines = []; |
|
this.changeColor = changeColor; |
|
} |
|
|
|
pushSubstring(substring) { |
|
this.pushDiff(new _cleanupSemantic.Diff(this.op, substring)); |
|
} |
|
|
|
pushLine() { |
|
// Assume call only if line has at least one diff, |
|
// therefore an empty line must have a diff which has an empty string. |
|
// If line has multiple diffs, then assume it has a common diff, |
|
// therefore change diffs have change color; |
|
// otherwise then it has line color only. |
|
this.lines.push( |
|
this.line.length !== 1 |
|
? new _cleanupSemantic.Diff( |
|
this.op, |
|
concatenateRelevantDiffs(this.op, this.line, this.changeColor) |
|
) |
|
: this.line[0][0] === this.op |
|
? this.line[0] // can use instance |
|
: new _cleanupSemantic.Diff(this.op, this.line[0][1]) // was common diff |
|
); |
|
this.line.length = 0; |
|
} |
|
|
|
isLineEmpty() { |
|
return this.line.length === 0; |
|
} // Minor input to buffer. |
|
|
|
pushDiff(diff) { |
|
this.line.push(diff); |
|
} // Main input to buffer. |
|
|
|
align(diff) { |
|
const string = diff[1]; |
|
|
|
if (string.includes('\n')) { |
|
const substrings = string.split('\n'); |
|
const iLast = substrings.length - 1; |
|
substrings.forEach((substring, i) => { |
|
if (i < iLast) { |
|
// The first substring completes the current change line. |
|
// A middle substring is a change line. |
|
this.pushSubstring(substring); |
|
this.pushLine(); |
|
} else if (substring.length !== 0) { |
|
// The last substring starts a change line, if it is not empty. |
|
// Important: This non-empty condition also automatically omits |
|
// the newline appended to the end of expected and received strings. |
|
this.pushSubstring(substring); |
|
} |
|
}); |
|
} else { |
|
// Append non-multiline string to current change line. |
|
this.pushDiff(diff); |
|
} |
|
} // Output from buffer. |
|
|
|
moveLinesTo(lines) { |
|
if (!this.isLineEmpty()) { |
|
this.pushLine(); |
|
} |
|
|
|
lines.push(...this.lines); |
|
this.lines.length = 0; |
|
} |
|
} // Encapsulate common and change lines. |
|
|
|
class CommonBuffer { |
|
constructor(deleteBuffer, insertBuffer) { |
|
_defineProperty(this, 'deleteBuffer', void 0); |
|
|
|
_defineProperty(this, 'insertBuffer', void 0); |
|
|
|
_defineProperty(this, 'lines', void 0); |
|
|
|
this.deleteBuffer = deleteBuffer; |
|
this.insertBuffer = insertBuffer; |
|
this.lines = []; |
|
} |
|
|
|
pushDiffCommonLine(diff) { |
|
this.lines.push(diff); |
|
} |
|
|
|
pushDiffChangeLines(diff) { |
|
const isDiffEmpty = diff[1].length === 0; // An empty diff string is redundant, unless a change line is empty. |
|
|
|
if (!isDiffEmpty || this.deleteBuffer.isLineEmpty()) { |
|
this.deleteBuffer.pushDiff(diff); |
|
} |
|
|
|
if (!isDiffEmpty || this.insertBuffer.isLineEmpty()) { |
|
this.insertBuffer.pushDiff(diff); |
|
} |
|
} |
|
|
|
flushChangeLines() { |
|
this.deleteBuffer.moveLinesTo(this.lines); |
|
this.insertBuffer.moveLinesTo(this.lines); |
|
} // Input to buffer. |
|
|
|
align(diff) { |
|
const op = diff[0]; |
|
const string = diff[1]; |
|
|
|
if (string.includes('\n')) { |
|
const substrings = string.split('\n'); |
|
const iLast = substrings.length - 1; |
|
substrings.forEach((substring, i) => { |
|
if (i === 0) { |
|
const subdiff = new _cleanupSemantic.Diff(op, substring); |
|
|
|
if ( |
|
this.deleteBuffer.isLineEmpty() && |
|
this.insertBuffer.isLineEmpty() |
|
) { |
|
// If both current change lines are empty, |
|
// then the first substring is a common line. |
|
this.flushChangeLines(); |
|
this.pushDiffCommonLine(subdiff); |
|
} else { |
|
// If either current change line is non-empty, |
|
// then the first substring completes the change lines. |
|
this.pushDiffChangeLines(subdiff); |
|
this.flushChangeLines(); |
|
} |
|
} else if (i < iLast) { |
|
// A middle substring is a common line. |
|
this.pushDiffCommonLine(new _cleanupSemantic.Diff(op, substring)); |
|
} else if (substring.length !== 0) { |
|
// The last substring starts a change line, if it is not empty. |
|
// Important: This non-empty condition also automatically omits |
|
// the newline appended to the end of expected and received strings. |
|
this.pushDiffChangeLines(new _cleanupSemantic.Diff(op, substring)); |
|
} |
|
}); |
|
} else { |
|
// Append non-multiline string to current change lines. |
|
// Important: It cannot be at the end following empty change lines, |
|
// because newline appended to the end of expected and received strings. |
|
this.pushDiffChangeLines(diff); |
|
} |
|
} // Output from buffer. |
|
|
|
getLines() { |
|
this.flushChangeLines(); |
|
return this.lines; |
|
} |
|
} // Given diffs from expected and received strings, |
|
// return new array of diffs split or joined into lines. |
|
// |
|
// To correctly align a change line at the end, the algorithm: |
|
// * assumes that a newline was appended to the strings |
|
// * omits the last newline from the output array |
|
// |
|
// Assume the function is not called: |
|
// * if either expected or received is empty string |
|
// * if neither expected nor received is multiline string |
|
|
|
const getAlignedDiffs = (diffs, changeColor) => { |
|
const deleteBuffer = new ChangeBuffer( |
|
_cleanupSemantic.DIFF_DELETE, |
|
changeColor |
|
); |
|
const insertBuffer = new ChangeBuffer( |
|
_cleanupSemantic.DIFF_INSERT, |
|
changeColor |
|
); |
|
const commonBuffer = new CommonBuffer(deleteBuffer, insertBuffer); |
|
diffs.forEach(diff => { |
|
switch (diff[0]) { |
|
case _cleanupSemantic.DIFF_DELETE: |
|
deleteBuffer.align(diff); |
|
break; |
|
|
|
case _cleanupSemantic.DIFF_INSERT: |
|
insertBuffer.align(diff); |
|
break; |
|
|
|
default: |
|
commonBuffer.align(diff); |
|
} |
|
}); |
|
return commonBuffer.getLines(); |
|
}; |
|
|
|
var _default = getAlignedDiffs; |
|
exports.default = _default;
|
|
|