refactor: Extract matching algorithm in line-by-line diff

This commit is contained in:
Rodrigo Fernandes 2019-11-26 10:13:38 +00:00
parent 0f08c85938
commit a25d06a8d7
No known key found for this signature in database
GPG key ID: 67157D2E3D4258B4
3 changed files with 74 additions and 84 deletions

View file

@ -87,7 +87,7 @@ export default class LineByLineRenderer {
return file.blocks return file.blocks
.map(block => { .map(block => {
let lines = this.hoganUtils.render(genericTemplatesPath, "column-line-number", { let lines = this.hoganUtils.render(genericTemplatesPath, "block-header", {
CSSLineClass: renderUtils.CSSLineClass, CSSLineClass: renderUtils.CSSLineClass,
blockHeader: block.header, blockHeader: block.header,
lineClass: "d2h-code-linenumber", lineClass: "d2h-code-linenumber",
@ -96,33 +96,70 @@ export default class LineByLineRenderer {
let oldLines: DiffLine[] = []; let oldLines: DiffLine[] = [];
let newLines: DiffLine[] = []; let newLines: DiffLine[] = [];
const processChangeBlock = (): void => { for (let i = 0; i < block.lines.length; i++) {
let matches; const diffLine = block.lines[i];
let insertType: renderUtils.CSSLineClass; const { prefix, content } = renderUtils.deconstructLine(diffLine.content, file.isCombined);
let deleteType: renderUtils.CSSLineClass;
if (
diffLine.type !== LineType.INSERT &&
(newLines.length > 0 || (diffLine.type !== LineType.DELETE && oldLines.length > 0))
) {
lines += this.processChangeBlock(file, oldLines, newLines, matcher);
oldLines = [];
newLines = [];
}
if (diffLine.type === LineType.CONTEXT || (diffLine.type === LineType.INSERT && !oldLines.length)) {
lines += this.makeLineHtml(
file.isCombined,
renderUtils.toCSSClass(diffLine.type),
content,
diffLine.oldNumber,
diffLine.newNumber,
prefix
);
} else if (diffLine.type === LineType.DELETE) {
oldLines.push(diffLine);
} else if (diffLine.type === LineType.INSERT && Boolean(oldLines.length)) {
newLines.push(diffLine);
} else {
console.error("Unknown state in html line-by-line generator");
lines += this.processChangeBlock(file, oldLines, newLines, matcher);
oldLines = [];
newLines = [];
}
}
lines += this.processChangeBlock(file, oldLines, newLines, matcher);
oldLines = [];
newLines = [];
return lines;
})
.join("\n");
}
processChangeBlock(
file: DiffFile,
oldLines: DiffLine[],
newLines: DiffLine[],
matcher: Rematch.MatcherFn<DiffLine>
): string {
const comparisons = oldLines.length * newLines.length; const comparisons = oldLines.length * newLines.length;
const maxLineSizeInBlock = Math.max.apply( const maxLineSizeInBlock = Math.max.apply(
null, null,
[0].concat(oldLines.concat(newLines).map(elem => elem.content.length)) [0].concat(oldLines.concat(newLines).map(elem => elem.content.length))
); );
const doMatching = const doMatching =
comparisons < this.config.matchingMaxComparisons && comparisons < this.config.matchingMaxComparisons &&
maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison && maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison &&
(this.config.matching === "lines" || this.config.matching === "words"); (this.config.matching === "lines" || this.config.matching === "words");
if (doMatching) { const [matches, insertType, deleteType] = doMatching
matches = matcher(oldLines, newLines); ? [matcher(oldLines, newLines), renderUtils.CSSLineClass.INSERT_CHANGES, renderUtils.CSSLineClass.DELETE_CHANGES]
insertType = renderUtils.CSSLineClass.INSERT_CHANGES; : [[[oldLines, newLines]], renderUtils.CSSLineClass.INSERTS, renderUtils.CSSLineClass.DELETES];
deleteType = renderUtils.CSSLineClass.DELETE_CHANGES;
} else {
matches = [[oldLines, newLines]];
insertType = renderUtils.CSSLineClass.INSERTS;
deleteType = renderUtils.CSSLineClass.DELETES;
}
let lines = "";
matches.forEach(match => { matches.forEach(match => {
oldLines = match[0]; oldLines = match[0];
newLines = match[1]; newLines = match[1];
@ -161,54 +198,7 @@ export default class LineByLineRenderer {
lines += this.processLines(file.isCombined, oldLines.slice(common), newLines.slice(common)); lines += this.processLines(file.isCombined, oldLines.slice(common), newLines.slice(common));
}); });
oldLines = [];
newLines = [];
};
for (let i = 0; i < block.lines.length; i++) {
const diffLine = block.lines[i];
const { prefix, content } = renderUtils.deconstructLine(diffLine.content, file.isCombined);
if (
diffLine.type !== LineType.INSERT &&
(newLines.length > 0 || (diffLine.type !== LineType.DELETE && oldLines.length > 0))
) {
processChangeBlock();
}
if (diffLine.type === LineType.CONTEXT) {
lines += this.makeLineHtml(
file.isCombined,
renderUtils.toCSSClass(diffLine.type),
content,
diffLine.oldNumber,
diffLine.newNumber,
prefix
);
} else if (diffLine.type === LineType.INSERT && !oldLines.length) {
lines += this.makeLineHtml(
file.isCombined,
renderUtils.toCSSClass(diffLine.type),
content,
diffLine.oldNumber,
diffLine.newNumber,
prefix
);
} else if (diffLine.type === LineType.DELETE) {
oldLines.push(diffLine);
} else if (diffLine.type === LineType.INSERT && Boolean(oldLines.length)) {
newLines.push(diffLine);
} else {
console.error("Unknown state in html line-by-line generator");
processChangeBlock();
}
}
processChangeBlock();
return lines; return lines;
})
.join("\n");
} }
// TODO: Make this private after improving tests // TODO: Make this private after improving tests

View file

@ -223,7 +223,7 @@ export default class SideBySideRenderer {
// TODO: Make this private after improving tests // TODO: Make this private after improving tests
makeSideHtml(blockHeader: string): string { makeSideHtml(blockHeader: string): string {
return this.hoganUtils.render(genericTemplatesPath, "column-line-number", { return this.hoganUtils.render(genericTemplatesPath, "block-header", {
CSSLineClass: renderUtils.CSSLineClass, CSSLineClass: renderUtils.CSSLineClass,
blockHeader: blockHeader, blockHeader: blockHeader,
lineClass: "d2h-code-side-linenumber", lineClass: "d2h-code-side-linenumber",