refactor: Separate matching in line-by-line algorithm

This commit is contained in:
Rodrigo Fernandes 2019-11-26 16:07:53 +00:00
parent a25d06a8d7
commit d8e0a99070
No known key found for this signature in database
GPG key ID: 67157D2E3D4258B4
5 changed files with 141 additions and 226 deletions

View file

@ -89,23 +89,23 @@ const htmlLineExample1 =
' <div class="d2h-code-line d2h-info">@@ -1 +1 @@</div>\n' + ' <div class="d2h-code-line d2h-info">@@ -1 +1 @@</div>\n' +
" </td>\n" + " </td>\n" +
"</tr><tr>\n" + "</tr><tr>\n" +
' <td class="d2h-code-linenumber d2h-del">\n' + ' <td class="d2h-code-linenumber d2h-del d2h-change">\n' +
' <div class="line-num1">1</div>\n' + ' <div class="line-num1">1</div>\n' +
'<div class="line-num2"></div>\n' + '<div class="line-num2"></div>\n' +
" </td>\n" + " </td>\n" +
' <td class="d2h-del">\n' + ' <td class="d2h-del d2h-change">\n' +
' <div class="d2h-code-line d2h-del">\n' + ' <div class="d2h-code-line d2h-del d2h-change">\n' +
' <span class="d2h-code-line-prefix">-</span>\n' + ' <span class="d2h-code-line-prefix">-</span>\n' +
' <span class="d2h-code-line-ctn"><del>test</del></span>\n' + ' <span class="d2h-code-line-ctn"><del>test</del></span>\n' +
" </div>\n" + " </div>\n" +
" </td>\n" + " </td>\n" +
"</tr><tr>\n" + "</tr><tr>\n" +
' <td class="d2h-code-linenumber d2h-ins">\n' + ' <td class="d2h-code-linenumber d2h-ins d2h-change">\n' +
' <div class="line-num1"></div>\n' + ' <div class="line-num1"></div>\n' +
'<div class="line-num2">1</div>\n' + '<div class="line-num2">1</div>\n' +
" </td>\n" + " </td>\n" +
' <td class="d2h-ins">\n' + ' <td class="d2h-ins d2h-change">\n' +
' <div class="d2h-code-line d2h-ins">\n' + ' <div class="d2h-code-line d2h-ins d2h-change">\n' +
' <span class="d2h-code-line-prefix">+</span>\n' + ' <span class="d2h-code-line-prefix">+</span>\n' +
' <span class="d2h-code-line-ctn"><ins>test1</ins></span>\n' + ' <span class="d2h-code-line-ctn"><ins>test1</ins></span>\n' +
" </div>\n" + " </div>\n" +
@ -446,23 +446,23 @@ describe("Diff2Html", () => {
" </div>\n" + " </div>\n" +
" </td>\n" + " </td>\n" +
"</tr><tr>\n" + "</tr><tr>\n" +
' <td class="d2h-code-linenumber d2h-del">\n' + ' <td class="d2h-code-linenumber d2h-del d2h-change">\n' +
' <div class="line-num1">14</div>\n' + ' <div class="line-num1">14</div>\n' +
'<div class="line-num2"></div>\n' + '<div class="line-num2"></div>\n' +
" </td>\n" + " </td>\n" +
' <td class="d2h-del">\n' + ' <td class="d2h-del d2h-change">\n' +
' <div class="d2h-code-line d2h-del">\n' + ' <div class="d2h-code-line d2h-del d2h-change">\n' +
' <span class="d2h-code-line-prefix">-</span>\n' + ' <span class="d2h-code-line-prefix">-</span>\n' +
' <span class="d2h-code-line-ctn"> - Fix HEAD branch order when redraw [#858](https:&#x2F;&#x2F;github.com&#x2F;FredrikNoren&#x2F;ungit&#x2F;issues&#x2F;858)</span>\n' + ' <span class="d2h-code-line-ctn"> - Fix HEAD branch order when redraw [#858](https:&#x2F;&#x2F;github.com&#x2F;FredrikNoren&#x2F;ungit&#x2F;issues&#x2F;858)</span>\n' +
" </div>\n" + " </div>\n" +
" </td>\n" + " </td>\n" +
"</tr><tr>\n" + "</tr><tr>\n" +
' <td class="d2h-code-linenumber d2h-ins">\n' + ' <td class="d2h-code-linenumber d2h-ins d2h-change">\n' +
' <div class="line-num1"></div>\n' + ' <div class="line-num1"></div>\n' +
'<div class="line-num2">13</div>\n' + '<div class="line-num2">13</div>\n' +
" </td>\n" + " </td>\n" +
' <td class="d2h-ins">\n' + ' <td class="d2h-ins d2h-change">\n' +
' <div class="d2h-code-line d2h-ins">\n' + ' <div class="d2h-code-line d2h-ins d2h-change">\n' +
' <span class="d2h-code-line-prefix">+</span>\n' + ' <span class="d2h-code-line-prefix">+</span>\n' +
' <span class="d2h-code-line-ctn"><ins>4</ins> - Fix HEAD branch order when redraw [#858](https:&#x2F;&#x2F;github.com&#x2F;FredrikNoren&#x2F;ungit&#x2F;issues&#x2F;858)</span>\n' + ' <span class="d2h-code-line-ctn"><ins>4</ins> - Fix HEAD branch order when redraw [#858](https:&#x2F;&#x2F;github.com&#x2F;FredrikNoren&#x2F;ungit&#x2F;issues&#x2F;858)</span>\n' +
" </div>\n" + " </div>\n" +
@ -519,7 +519,7 @@ describe("Diff2Html", () => {
"</div>"; "</div>";
const result = html(diffExample2, { drawFileList: false }); const result = html(diffExample2, { drawFileList: false });
expect(htmlExample2).toEqual(result); expect(result).toEqual(htmlExample2);
}); });
}); });
}); });

View file

@ -1,6 +1,6 @@
import LineByLineRenderer from "../line-by-line-renderer"; import LineByLineRenderer from "../line-by-line-renderer";
import HoganJsUtils from "../hoganjs-utils"; import HoganJsUtils from "../hoganjs-utils";
import { LineType, DiffLine, DiffFile, LineMatchingType } from "../types"; import { LineType, DiffFile, LineMatchingType } from "../types";
import { CSSLineClass } from "../render-utils"; import { CSSLineClass } from "../render-utils";
describe("LineByLineRenderer", () => { describe("LineByLineRenderer", () => {
@ -26,7 +26,7 @@ describe("LineByLineRenderer", () => {
it("should work for insertions", () => { it("should work for insertions", () => {
const hoganUtils = new HoganJsUtils({}); const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
let fileHtml = lineByLineRenderer.makeLineHtml(false, CSSLineClass.INSERTS, "test", undefined, 30, "+"); let fileHtml = lineByLineRenderer.makeLineHtml(CSSLineClass.INSERTS, "+", "test", undefined, 30);
fileHtml = fileHtml.replace(/\n\n+/g, "\n"); fileHtml = fileHtml.replace(/\n\n+/g, "\n");
const expected = const expected =
"<tr>\n" + "<tr>\n" +
@ -48,7 +48,7 @@ describe("LineByLineRenderer", () => {
it("should work for deletions", () => { it("should work for deletions", () => {
const hoganUtils = new HoganJsUtils({}); const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
let fileHtml = lineByLineRenderer.makeLineHtml(false, CSSLineClass.DELETES, "test", 30, undefined, "-"); let fileHtml = lineByLineRenderer.makeLineHtml(CSSLineClass.DELETES, "-", "test", 30, undefined);
fileHtml = fileHtml.replace(/\n\n+/g, "\n"); fileHtml = fileHtml.replace(/\n\n+/g, "\n");
const expected = const expected =
"<tr>\n" + "<tr>\n" +
@ -70,7 +70,7 @@ describe("LineByLineRenderer", () => {
it("should convert indents into non breakin spaces (2 white spaces)", () => { it("should convert indents into non breakin spaces (2 white spaces)", () => {
const hoganUtils = new HoganJsUtils({}); const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
let fileHtml = lineByLineRenderer.makeLineHtml(false, CSSLineClass.INSERTS, " test", undefined, 30, "+"); let fileHtml = lineByLineRenderer.makeLineHtml(CSSLineClass.INSERTS, "+", " test", undefined, 30);
fileHtml = fileHtml.replace(/\n\n+/g, "\n"); fileHtml = fileHtml.replace(/\n\n+/g, "\n");
const expected = const expected =
"<tr>\n" + "<tr>\n" +
@ -92,7 +92,7 @@ describe("LineByLineRenderer", () => {
it("should convert indents into non breakin spaces (4 white spaces)", () => { it("should convert indents into non breakin spaces (4 white spaces)", () => {
const hoganUtils = new HoganJsUtils({}); const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
let fileHtml = lineByLineRenderer.makeLineHtml(false, CSSLineClass.INSERTS, " test", undefined, 30, "+"); let fileHtml = lineByLineRenderer.makeLineHtml(CSSLineClass.INSERTS, "+", " test", undefined, 30);
fileHtml = fileHtml.replace(/\n\n+/g, "\n"); fileHtml = fileHtml.replace(/\n\n+/g, "\n");
const expected = const expected =
"<tr>\n" + "<tr>\n" +
@ -114,7 +114,7 @@ describe("LineByLineRenderer", () => {
it("should preserve tabs", () => { it("should preserve tabs", () => {
const hoganUtils = new HoganJsUtils({}); const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
let fileHtml = lineByLineRenderer.makeLineHtml(false, CSSLineClass.INSERTS, "\ttest", undefined, 30, "+"); let fileHtml = lineByLineRenderer.makeLineHtml(CSSLineClass.INSERTS, "+", "\ttest", undefined, 30);
fileHtml = fileHtml.replace(/\n\n+/g, "\n"); fileHtml = fileHtml.replace(/\n\n+/g, "\n");
const expected = const expected =
"<tr>\n" + "<tr>\n" +
@ -473,58 +473,6 @@ describe("LineByLineRenderer", () => {
}); });
}); });
describe("_processLines", () => {
it("should work for simple block header", () => {
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const oldLines: DiffLine[] = [
{
content: "-test",
type: LineType.DELETE,
oldNumber: 1,
newNumber: undefined
}
];
const newLines: DiffLine[] = [
{
content: "+test1r",
type: LineType.INSERT,
oldNumber: undefined,
newNumber: 1
}
];
const html = lineByLineRenderer.processLines(false, oldLines, newLines);
const expected =
"<tr>\n" +
' <td class="d2h-code-linenumber d2h-del">\n' +
' <div class="line-num1">1</div>\n' +
'<div class="line-num2"></div>\n' +
" </td>\n" +
' <td class="d2h-del">\n' +
' <div class="d2h-code-line d2h-del">\n' +
' <span class="d2h-code-line-prefix">-</span>\n' +
' <span class="d2h-code-line-ctn">test</span>\n' +
" </div>\n" +
" </td>\n" +
"</tr><tr>\n" +
' <td class="d2h-code-linenumber d2h-ins">\n' +
' <div class="line-num1"></div>\n' +
'<div class="line-num2">1</div>\n' +
" </td>\n" +
' <td class="d2h-ins">\n' +
' <div class="d2h-code-line d2h-ins">\n' +
' <span class="d2h-code-line-prefix">+</span>\n' +
' <span class="d2h-code-line-ctn">test1r</span>\n' +
" </div>\n" +
" </td>\n" +
"</tr>";
expect(html).toEqual(expected);
});
});
describe("_generateFileHtml", () => { describe("_generateFileHtml", () => {
it("should work for simple file", () => { it("should work for simple file", () => {
const hoganUtils = new HoganJsUtils({}); const hoganUtils = new HoganJsUtils({});
@ -595,23 +543,23 @@ describe("LineByLineRenderer", () => {
" </div>\n" + " </div>\n" +
" </td>\n" + " </td>\n" +
"</tr><tr>\n" + "</tr><tr>\n" +
' <td class="d2h-code-linenumber d2h-del">\n' + ' <td class="d2h-code-linenumber d2h-del d2h-change">\n' +
' <div class="line-num1">2</div>\n' + ' <div class="line-num1">2</div>\n' +
'<div class="line-num2"></div>\n' + '<div class="line-num2"></div>\n' +
" </td>\n" + " </td>\n" +
' <td class="d2h-del">\n' + ' <td class="d2h-del d2h-change">\n' +
' <div class="d2h-code-line d2h-del">\n' + ' <div class="d2h-code-line d2h-del d2h-change">\n' +
' <span class="d2h-code-line-prefix">-</span>\n' + ' <span class="d2h-code-line-prefix">-</span>\n' +
' <span class="d2h-code-line-ctn"><del>test</del></span>\n' + ' <span class="d2h-code-line-ctn"><del>test</del></span>\n' +
" </div>\n" + " </div>\n" +
" </td>\n" + " </td>\n" +
"</tr><tr>\n" + "</tr><tr>\n" +
' <td class="d2h-code-linenumber d2h-ins">\n' + ' <td class="d2h-code-linenumber d2h-ins d2h-change">\n' +
' <div class="line-num1"></div>\n' + ' <div class="line-num1"></div>\n' +
'<div class="line-num2">2</div>\n' + '<div class="line-num2">2</div>\n' +
" </td>\n" + " </td>\n" +
' <td class="d2h-ins">\n' + ' <td class="d2h-ins d2h-change">\n' +
' <div class="d2h-code-line d2h-ins">\n' + ' <div class="d2h-code-line d2h-ins d2h-change">\n' +
' <span class="d2h-code-line-prefix">+</span>\n' + ' <span class="d2h-code-line-prefix">+</span>\n' +
' <span class="d2h-code-line-ctn"><ins>test1r</ins></span>\n' + ' <span class="d2h-code-line-ctn"><ins>test1r</ins></span>\n' +
" </div>\n" + " </div>\n" +

View file

@ -1,7 +1,7 @@
import HoganJsUtils from "./hoganjs-utils"; import HoganJsUtils from "./hoganjs-utils";
import * as Rematch from "./rematch"; import * as Rematch from "./rematch";
import * as renderUtils from "./render-utils"; import * as renderUtils from "./render-utils";
import { DiffFile, DiffLine, LineType } from "./types"; import { DiffFile, DiffLine, LineType, DiffBlock } from "./types";
export interface LineByLineRendererConfig extends renderUtils.RenderConfig { export interface LineByLineRendererConfig extends renderUtils.RenderConfig {
renderNothingWhenEmpty?: boolean; renderNothingWhenEmpty?: boolean;
@ -79,7 +79,6 @@ export default class LineByLineRenderer {
}); });
} }
// TODO: Make this private after improving tests
generateFileHtml(file: DiffFile): string { generateFileHtml(file: DiffFile): string {
const matcher = Rematch.newMatcherFn( const matcher = Rematch.newMatcherFn(
Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content) Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content)
@ -93,58 +92,79 @@ export default class LineByLineRenderer {
lineClass: "d2h-code-linenumber", lineClass: "d2h-code-linenumber",
contentClass: "d2h-code-line" contentClass: "d2h-code-line"
}); });
let oldLines: DiffLine[] = [];
let newLines: DiffLine[] = [];
for (let i = 0; i < block.lines.length; i++) { this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => {
const diffLine = block.lines[i]; if (oldLines.length && newLines.length && !contextLines.length) {
const { prefix, content } = renderUtils.deconstructLine(diffLine.content, file.isCombined); lines += this.applyRematchMatching(oldLines, newLines, matcher)
.map(([oldLines, newLines]) => this.applyLineDiff(file, oldLines, newLines))
if ( .join("");
diffLine.type !== LineType.INSERT && } else if (oldLines.length || newLines.length || contextLines.length) {
(newLines.length > 0 || (diffLine.type !== LineType.DELETE && oldLines.length > 0)) lines += (contextLines || [])
) { .concat((oldLines || []).concat(newLines || []))
lines += this.processChangeBlock(file, oldLines, newLines, matcher); .map(line => {
oldLines = []; const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined);
newLines = []; return this.makeLineHtml(
} renderUtils.toCSSClass(line.type),
prefix,
if (diffLine.type === LineType.CONTEXT || (diffLine.type === LineType.INSERT && !oldLines.length)) { content,
lines += this.makeLineHtml( line.oldNumber,
file.isCombined, line.newNumber
renderUtils.toCSSClass(diffLine.type), );
content, })
diffLine.oldNumber, .join("");
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 { } else {
console.error("Unknown state in html line-by-line generator"); console.error("Unknown state reached while processing groups of lines", contextLines, oldLines, newLines);
lines += this.processChangeBlock(file, oldLines, newLines, matcher);
oldLines = [];
newLines = [];
} }
} });
lines += this.processChangeBlock(file, oldLines, newLines, matcher);
oldLines = [];
newLines = [];
return lines; return lines;
}) })
.join("\n"); .join("\n");
} }
processChangeBlock( applyLineGroupping(block: DiffBlock): DiffLine[][][] {
file: DiffFile, const blockLinesGroups: DiffLine[][][] = [];
let oldLines: DiffLine[] = [];
let newLines: DiffLine[] = [];
for (let i = 0; i < block.lines.length; i++) {
const diffLine = block.lines[i];
if (
(diffLine.type !== LineType.INSERT && newLines.length) ||
(diffLine.type === LineType.CONTEXT && oldLines.length > 0)
) {
blockLinesGroups.push([[], oldLines, newLines]);
oldLines = [];
newLines = [];
}
if (diffLine.type === LineType.CONTEXT) {
blockLinesGroups.push([[diffLine], [], []]);
} else if (diffLine.type === LineType.INSERT && oldLines.length === 0) {
blockLinesGroups.push([[], [], [diffLine]]);
} else if (diffLine.type === LineType.INSERT && oldLines.length > 0) {
newLines.push(diffLine);
} else if (diffLine.type === LineType.DELETE) {
oldLines.push(diffLine);
}
}
if (oldLines.length || newLines.length) {
blockLinesGroups.push([[], oldLines, newLines]);
oldLines = [];
newLines = [];
}
return blockLinesGroups;
}
applyRematchMatching(
oldLines: DiffLine[], oldLines: DiffLine[],
newLines: DiffLine[], newLines: DiffLine[],
matcher: Rematch.MatcherFn<DiffLine> matcher: Rematch.MatcherFn<DiffLine>
): string { ): DiffLine[][][] {
const comparisons = oldLines.length * newLines.length; const comparisons = oldLines.length * newLines.length;
const maxLineSizeInBlock = Math.max.apply( const maxLineSizeInBlock = Math.max.apply(
null, null,
@ -155,115 +175,72 @@ export default class LineByLineRenderer {
maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison && maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison &&
(this.config.matching === "lines" || this.config.matching === "words"); (this.config.matching === "lines" || this.config.matching === "words");
const [matches, insertType, deleteType] = doMatching const matches = doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
? [matcher(oldLines, newLines), renderUtils.CSSLineClass.INSERT_CHANGES, renderUtils.CSSLineClass.DELETE_CHANGES]
: [[[oldLines, newLines]], renderUtils.CSSLineClass.INSERTS, renderUtils.CSSLineClass.DELETES];
let lines = ""; return matches;
matches.forEach(match => {
oldLines = match[0];
newLines = match[1];
let processedOldLines = "";
let processedNewLines = "";
const common = Math.min(oldLines.length, newLines.length);
let oldLine, newLine;
for (let j = 0; j < common; j++) {
oldLine = oldLines[j];
newLine = newLines[j];
const diff = renderUtils.diffHighlight(oldLine.content, newLine.content, file.isCombined, this.config);
processedOldLines += this.makeLineHtml(
file.isCombined,
deleteType,
diff.oldLine.content,
oldLine.oldNumber,
oldLine.newNumber,
diff.oldLine.prefix
);
processedNewLines += this.makeLineHtml(
file.isCombined,
insertType,
diff.newLine.content,
newLine.oldNumber,
newLine.newNumber,
diff.newLine.prefix
);
}
lines += processedOldLines + processedNewLines;
lines += this.processLines(file.isCombined, oldLines.slice(common), newLines.slice(common));
});
return lines;
} }
// TODO: Make this private after improving tests applyLineDiff(file: DiffFile, oldLines: DiffLine[], newLines: DiffLine[]): string {
makeLineHtml( let oldLinesHtml = "";
isCombined: boolean, let newLinesHtml = "";
type: renderUtils.CSSLineClass,
content: string,
oldNumber?: number,
newNumber?: number,
possiblePrefix?: string
): string {
const lineNumberTemplate = this.hoganUtils.render(baseTemplatesPath, "numbers", {
oldNumber: oldNumber || "",
newNumber: newNumber || ""
});
let lineWithoutPrefix = content; const common = Math.min(oldLines.length, newLines.length);
let prefix = possiblePrefix;
if (!prefix) { let oldLine, newLine;
const lineWithPrefix = renderUtils.deconstructLine(content, isCombined); for (let j = 0; j < common; j++) {
prefix = lineWithPrefix.prefix; oldLine = oldLines[j];
lineWithoutPrefix = lineWithPrefix.content; newLine = newLines[j];
}
if (prefix === " ") { const diff = renderUtils.diffHighlight(oldLine.content, newLine.content, file.isCombined, this.config);
prefix = "&nbsp;";
}
return this.hoganUtils.render(genericTemplatesPath, "line", { oldLinesHtml += this.makeLineHtml(
type: type, renderUtils.CSSLineClass.DELETE_CHANGES,
lineClass: "d2h-code-linenumber", diff.oldLine.prefix,
contentClass: "d2h-code-line", diff.oldLine.content,
prefix: prefix,
content: lineWithoutPrefix,
lineNumber: lineNumberTemplate
});
}
// TODO: Make this private after improving tests
processLines(isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): string {
let lines = "";
for (let i = 0; i < oldLines.length; i++) {
const oldLine = oldLines[i];
lines += this.makeLineHtml(
isCombined,
renderUtils.toCSSClass(oldLine.type),
oldLine.content,
oldLine.oldNumber, oldLine.oldNumber,
oldLine.newNumber oldLine.newNumber
); );
} newLinesHtml += this.makeLineHtml(
renderUtils.CSSLineClass.INSERT_CHANGES,
for (let j = 0; j < newLines.length; j++) { diff.newLine.prefix,
const newLine = newLines[j]; diff.newLine.content,
lines += this.makeLineHtml(
isCombined,
renderUtils.toCSSClass(newLine.type),
newLine.content,
newLine.oldNumber, newLine.oldNumber,
newLine.newNumber newLine.newNumber
); );
} }
return lines; const remainingLines = oldLines
.slice(common)
.concat(newLines.slice(common))
.map(line => {
const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined);
return this.makeLineHtml(renderUtils.toCSSClass(line.type), prefix, content, line.oldNumber, line.newNumber);
})
.join("");
return oldLinesHtml + newLinesHtml + remainingLines;
}
// TODO: Make this private after improving tests
makeLineHtml(
type: renderUtils.CSSLineClass,
prefix: string,
content: string,
oldNumber?: number,
newNumber?: number
): string {
const lineNumberHtml = this.hoganUtils.render(baseTemplatesPath, "numbers", {
oldNumber: oldNumber || "",
newNumber: newNumber || ""
});
return this.hoganUtils.render(genericTemplatesPath, "line", {
type: type,
lineClass: "d2h-code-linenumber",
contentClass: "d2h-code-line",
prefix: prefix === " " ? "&nbsp;" : prefix,
content: content,
lineNumber: lineNumberHtml
});
} }
} }

View file

@ -240,19 +240,13 @@ export function diffHighlight(
const changedWords: jsDiff.Change[] = []; const changedWords: jsDiff.Change[] = [];
if (diffStyle === "word" && matching === "words") { if (diffStyle === "word" && matching === "words") {
let treshold = 0.25;
if (typeof matchWordsThreshold !== "undefined") {
treshold = matchWordsThreshold;
}
const removed = diff.filter(element => element.removed); const removed = diff.filter(element => element.removed);
const added = diff.filter(element => element.added); const added = diff.filter(element => element.added);
const chunks = matcher(added, removed); const chunks = matcher(added, removed);
chunks.forEach(chunk => { chunks.forEach(chunk => {
if (chunk[0].length === 1 && chunk[1].length === 1) { if (chunk[0].length === 1 && chunk[1].length === 1) {
const dist = distance(chunk[0][0], chunk[1][0]); const dist = distance(chunk[0][0], chunk[1][0]);
if (dist < treshold) { if (dist < matchWordsThreshold) {
changedWords.push(chunk[0][0]); changedWords.push(chunk[0][0]);
changedWords.push(chunk[1][0]); changedWords.push(chunk[1][0]);
} }

View file

@ -331,15 +331,11 @@ export default class SideBySideRenderer {
lineWithoutPrefix = lineWithPrefix.content; lineWithoutPrefix = lineWithPrefix.content;
} }
if (prefix === " ") {
prefix = "&nbsp;";
}
return this.hoganUtils.render(genericTemplatesPath, "line", { return this.hoganUtils.render(genericTemplatesPath, "line", {
type: preparedType, type: preparedType,
lineClass: lineClass, lineClass: lineClass,
contentClass: contentClass, contentClass: contentClass,
prefix: prefix, prefix: prefix === " " ? "&nbsp;" : prefix,
content: lineWithoutPrefix, content: lineWithoutPrefix,
lineNumber: number lineNumber: number
}); });