Enhancements for highlighting
1. Fixed a bug that prevented highlighting when more lines were deleted from a replacement block than added 2. Added similarity search feature to match similar lines for highlighting. 3. Highlighting: Support for matching "ins" and "del" markers to allow for a "change" marker 4. Bugfix: All lines are now split into prefix and content spans
This commit is contained in:
parent
8b9c2473b2
commit
7335c3bab3
6 changed files with 307 additions and 79 deletions
|
|
@ -14,6 +14,8 @@ Diff to Html generates pretty HTML diffs from git diff output.
|
|||
|
||||
* Code syntax highlight
|
||||
|
||||
* Line similarity matching
|
||||
|
||||
## Online Example
|
||||
|
||||
> Go to [Diff2HTML](http://rtfpessoa.github.io/diff2html/)
|
||||
|
|
@ -48,6 +50,8 @@ The HTML output accepts a Javascript object with configuration. Possible options
|
|||
- `inputFormat`: the format of the input data: `'diff'` or `'json'`, default is `'diff'`
|
||||
- `outputFormat`: the format of the output data: `'line-by-line'` or `'side-by-side'`, default is `'line-by-line'`
|
||||
- `showFiles`: show a file list before the diff: `true` or `false`, default is `false`
|
||||
- `matchWords`: matches changed words in diff output lines: `true` or `false`, default is `false`
|
||||
- `matchWordsThreshold`: similarity threshold for word matching, default is 0.25
|
||||
|
||||
|
||||
## Syntax Highlight
|
||||
|
|
|
|||
|
|
@ -256,6 +256,19 @@
|
|||
clear: both;
|
||||
}
|
||||
|
||||
.d2h-del.d2h-change, .d2h-ins.d2h-change {
|
||||
background-color: #ffc;
|
||||
}
|
||||
ins.d2h-change, del.d2h-change {
|
||||
background-color: #fad771;
|
||||
}
|
||||
.d2h-file-diff .d2h-del.d2h-change {
|
||||
background-color: #fae1af;
|
||||
}
|
||||
.d2h-file-diff .d2h-ins.d2h-change {
|
||||
background-color: #faedaf;
|
||||
}
|
||||
|
||||
/* CSS only show/hide */
|
||||
.d2h-show {
|
||||
display: none;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
var diffParser = require('./diff-parser.js').DiffParser;
|
||||
var printerUtils = require('./printer-utils.js').PrinterUtils;
|
||||
var utils = require('./utils.js').Utils;
|
||||
var Rematch = require('./rematch.js').Rematch;
|
||||
|
||||
function LineByLinePrinter() {
|
||||
}
|
||||
|
|
@ -51,6 +52,12 @@
|
|||
'</div>\n';
|
||||
};
|
||||
|
||||
var matcher=Rematch.rematch(function(a,b) {
|
||||
var amod = a.content.substr(1),
|
||||
bmod = b.content.substr(1);
|
||||
return Rematch.distance(amod, bmod);
|
||||
});
|
||||
|
||||
function generateFileHtml(file, config) {
|
||||
return file.blocks.map(function(block) {
|
||||
|
||||
|
|
@ -63,27 +70,18 @@
|
|||
|
||||
var oldLines = [];
|
||||
var newLines = [];
|
||||
function processChangeBlock() {
|
||||
var matches = matcher(oldLines, newLines);
|
||||
matches.forEach(function(match){
|
||||
var oldLines = match[0];
|
||||
var newLines = match[1];
|
||||
var processedOldLines = [];
|
||||
var processedNewLines = [];
|
||||
|
||||
for (var i = 0; i < block.lines.length; i++) {
|
||||
var line = block.lines[i];
|
||||
var escapedLine = utils.escape(line.content);
|
||||
|
||||
if (line.type == diffParser.LINE_TYPE.CONTEXT && !oldLines.length && !newLines.length) {
|
||||
lines += generateLineHtml(line.type, line.oldNumber, line.newNumber, escapedLine);
|
||||
} else if (line.type == diffParser.LINE_TYPE.INSERTS && !oldLines.length && !newLines.length) {
|
||||
lines += generateLineHtml(line.type, line.oldNumber, line.newNumber, escapedLine);
|
||||
} else if (line.type == diffParser.LINE_TYPE.DELETES && !newLines.length) {
|
||||
oldLines.push(line);
|
||||
} else if (line.type == diffParser.LINE_TYPE.INSERTS && oldLines.length > newLines.length) {
|
||||
newLines.push(line);
|
||||
} else {
|
||||
var j = 0;
|
||||
var oldLine, newLine;
|
||||
|
||||
if (oldLines.length === newLines.length) {
|
||||
for (j = 0; j < oldLines.length; j++) {
|
||||
var oldLine, newLine,
|
||||
common = Math.min(oldLines.length, newLines.length),
|
||||
max = Math.max(oldLines.length, newLines.length);
|
||||
for (j = 0; j < common; j++) {
|
||||
oldLine = oldLines[j];
|
||||
newLine = newLines[j];
|
||||
|
||||
|
|
@ -91,27 +89,46 @@
|
|||
var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, config);
|
||||
|
||||
processedOldLines +=
|
||||
generateLineHtml(oldLine.type, oldLine.oldNumber, oldLine.newNumber,
|
||||
generateLineHtml(oldLine.type + ' d2h-change', oldLine.oldNumber, oldLine.newNumber,
|
||||
diff.first.line, diff.first.prefix);
|
||||
processedNewLines +=
|
||||
generateLineHtml(newLine.type, newLine.oldNumber, newLine.newNumber,
|
||||
generateLineHtml(newLine.type + ' d2h-change', newLine.oldNumber, newLine.newNumber,
|
||||
diff.second.line, diff.second.prefix);
|
||||
}
|
||||
|
||||
lines += processedOldLines + processedNewLines;
|
||||
} else {
|
||||
lines += processLines(oldLines, newLines);
|
||||
}
|
||||
lines += processLines(oldLines.slice(common), newLines.slice(common));
|
||||
|
||||
oldLines = [];
|
||||
newLines = [];
|
||||
processedOldLines = [];
|
||||
processedNewLines = [];
|
||||
i--;
|
||||
});
|
||||
oldLines = [];
|
||||
newLines = [];
|
||||
}
|
||||
|
||||
for (var i = 0; i < block.lines.length; i++) {
|
||||
var line = block.lines[i];
|
||||
var escapedLine = utils.escape(line.content);
|
||||
|
||||
if ( line.type !== diffParser.LINE_TYPE.INSERTS &&
|
||||
(newLines.length > 0 || (line.type !== diffParser.LINE_TYPE.DELETES && oldLines > 0))) {
|
||||
processChangeBlock();
|
||||
}
|
||||
if (line.type == diffParser.LINE_TYPE.CONTEXT) {
|
||||
lines += generateLineHtml(line.type, line.oldNumber, line.newNumber, escapedLine);
|
||||
} else if (line.type == diffParser.LINE_TYPE.INSERTS && !oldLines.length) {
|
||||
lines += generateLineHtml(line.type, line.oldNumber, line.newNumber, escapedLine);
|
||||
} else if (line.type == diffParser.LINE_TYPE.DELETES) {
|
||||
oldLines.push(line);
|
||||
} else if (line.type == diffParser.LINE_TYPE.INSERTS && !!oldLines.length) {
|
||||
newLines.push(line);
|
||||
} else {
|
||||
console.error('unknown state in html line-by-line generator');
|
||||
processChangeBlock();
|
||||
}
|
||||
}
|
||||
|
||||
lines += processLines(oldLines, newLines);
|
||||
processChangeBlock();
|
||||
|
||||
return lines;
|
||||
}).join('\n');
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
var jsDiff = require('diff');
|
||||
var utils = require('./utils.js').Utils;
|
||||
var Rematch = require('./rematch.js').Rematch;
|
||||
|
||||
function PrinterUtils() {
|
||||
}
|
||||
|
|
@ -69,12 +70,42 @@
|
|||
|
||||
var highlightedLine = '';
|
||||
|
||||
var changedWords = [];
|
||||
if (!config.charByChar && config.matchWords) {
|
||||
var treshold = 0.25;
|
||||
if (typeof(config.matchWordsThreshold) !== "undefined") {
|
||||
treshold = config.matchWordsThreshold;
|
||||
}
|
||||
var matcher = Rematch.rematch(function(a, b) {
|
||||
var amod = a.value,
|
||||
bmod = b.value,
|
||||
result = Rematch.distance(amod, bmod);
|
||||
return result;
|
||||
});
|
||||
var removed = diff.filter(function isRemoved(element){
|
||||
return element.removed;
|
||||
});
|
||||
var added = diff.filter(function isAdded(element){
|
||||
return element.added;
|
||||
});
|
||||
var chunks = matcher(added, removed);
|
||||
chunks = chunks.forEach(function(chunk){
|
||||
if(chunk[0].length === 1 && chunk[1].length === 1) {
|
||||
var dist = Rematch.distance(chunk[0][0].value, chunk[1][0].value)
|
||||
if (dist < treshold) {
|
||||
changedWords.push(chunk[0][0]);
|
||||
changedWords.push(chunk[1][0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
diff.forEach(function(part) {
|
||||
var addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : '';
|
||||
var elemType = part.added ? 'ins' : part.removed ? 'del' : null;
|
||||
var escapedValue = utils.escape(part.value);
|
||||
|
||||
if (elemType !== null) {
|
||||
highlightedLine += '<' + elemType + '>' + escapedValue + '</' + elemType + '>';
|
||||
highlightedLine += '<' + elemType + addClass + '>' + escapedValue + '</' + elemType + '>';
|
||||
} else {
|
||||
highlightedLine += escapedValue;
|
||||
}
|
||||
|
|
@ -97,11 +128,11 @@
|
|||
}
|
||||
|
||||
function removeIns(line) {
|
||||
return line.replace(/(<ins>((.|\n)*?)<\/ins>)/g, '');
|
||||
return line.replace(/(<ins[^>]*>((.|\n)*?)<\/ins>)/g, '');
|
||||
}
|
||||
|
||||
function removeDel(line) {
|
||||
return line.replace(/(<del>((.|\n)*?)<\/del>)/g, '');
|
||||
return line.replace(/(<del[^>]*>((.|\n)*?)<\/del>)/g, '');
|
||||
}
|
||||
|
||||
module.exports['PrinterUtils'] = new PrinterUtils();
|
||||
|
|
|
|||
134
src/rematch.js
Normal file
134
src/rematch.js
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
*
|
||||
* Rematch (rematch.js)
|
||||
* Matching two sequences of objects by similarity
|
||||
* Author: W. Illmeyer, Nexxar GmbH
|
||||
*
|
||||
*/
|
||||
|
||||
(function(ctx, undefined) {
|
||||
var Rematch = {};
|
||||
Rematch.arrayToString = function arrayToString(a) {
|
||||
if (Object.prototype.toString.apply(a,[]) === "[object Array]") {
|
||||
return "[" + a.map(arrayToString).join(", ") + "]";
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright (c) 2011 Andrei Mackenzie
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function levenshtein(a, b){
|
||||
if(a.length == 0) return b.length;
|
||||
if(b.length == 0) return a.length;
|
||||
|
||||
var matrix = [];
|
||||
|
||||
// increment along the first column of each row
|
||||
var i;
|
||||
for(i = 0; i <= b.length; i++){
|
||||
matrix[i] = [i];
|
||||
}
|
||||
|
||||
// increment each column in the first row
|
||||
var j;
|
||||
for(j = 0; j <= a.length; j++){
|
||||
matrix[0][j] = j;
|
||||
}
|
||||
|
||||
// Fill in the rest of the matrix
|
||||
for(i = 1; i <= b.length; i++){
|
||||
for(j = 1; j <= a.length; j++){
|
||||
if(b.charAt(i-1) == a.charAt(j-1)){
|
||||
matrix[i][j] = matrix[i-1][j-1];
|
||||
} else {
|
||||
matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
|
||||
Math.min(matrix[i][j-1] + 1, // insertion
|
||||
matrix[i-1][j] + 1)); // deletion
|
||||
}
|
||||
}
|
||||
}
|
||||
return matrix[b.length][a.length];
|
||||
}
|
||||
Rematch.levenshtein = levenshtein;
|
||||
|
||||
Rematch.distance = function distance(x,y) {
|
||||
x=x.trim();
|
||||
y=y.trim();
|
||||
var lev = levenshtein(x,y),
|
||||
score = lev / (x.length + y.length);
|
||||
return score;
|
||||
}
|
||||
|
||||
Rematch.rematch = function rematch(distanceFunction) {
|
||||
|
||||
function findBestMatch(a, b, cache) {
|
||||
var cachecount = 0;
|
||||
|
||||
for(var key in cache) {
|
||||
cachecount++;
|
||||
}
|
||||
var bestMatchDist = Infinity;
|
||||
var bestMatch;
|
||||
for (var i = 0; i < a.length; ++i) {
|
||||
for (var j = 0; j < b.length; ++j) {
|
||||
var cacheKey = JSON.stringify([a[i], b[j]]);
|
||||
var md;
|
||||
if (cache.hasOwnProperty(cacheKey)) {
|
||||
md = cache[cacheKey];
|
||||
} else {
|
||||
md = distanceFunction(a[i], b[j]);
|
||||
cache[cacheKey] = md;
|
||||
}
|
||||
if (md < bestMatchDist) {
|
||||
bestMatchDist = md;
|
||||
bestMatch = { indexA: i, indexB: j, score: bestMatchDist };
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestMatch;
|
||||
}
|
||||
function group(a, b, level, cache) {
|
||||
|
||||
if (typeof(cache)==="undefined") {
|
||||
cache = {};
|
||||
}
|
||||
var minLength = Math.min(a.length, b.length);
|
||||
var bm = findBestMatch(a,b, cache);
|
||||
if (!level) {
|
||||
level = 0;
|
||||
}
|
||||
if (!bm || (a.length + b.length < 3)) {
|
||||
return [[a, b]];
|
||||
}
|
||||
var a1 = a.slice(0, bm.indexA),
|
||||
b1 = b.slice(0, bm.indexB),
|
||||
aMatch = [a[bm.indexA]],
|
||||
bMatch = [b[bm.indexB]],
|
||||
tailA = bm.indexA + 1,
|
||||
tailB = bm.indexB + 1,
|
||||
a2 = a.slice(tailA),
|
||||
b2 = b.slice(tailB);
|
||||
|
||||
var group1 = group(a1, b1, level+1, cache);
|
||||
var groupMatch = group(aMatch, bMatch, level+1, cache);
|
||||
var group2 = group(a2, b2, level+1, cache);
|
||||
var result = groupMatch;
|
||||
if (bm.indexA > 0 || bm.indexB > 0) {
|
||||
result = group1.concat(result);
|
||||
}
|
||||
if (a.length > tailA || b.length > tailB ) {
|
||||
result = result.concat(group2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
module.exports['Rematch'] = Rematch;
|
||||
|
||||
})(this);
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
var diffParser = require('./diff-parser.js').DiffParser;
|
||||
var printerUtils = require('./printer-utils.js').PrinterUtils;
|
||||
var utils = require('./utils.js').Utils;
|
||||
var Rematch = require('./rematch.js').Rematch;
|
||||
|
||||
function SideBySidePrinter() {
|
||||
}
|
||||
|
|
@ -62,6 +63,12 @@
|
|||
'</div>\n';
|
||||
};
|
||||
|
||||
var matcher=Rematch.rematch(function(a,b) {
|
||||
var amod = a.content.substr(1),
|
||||
bmod = b.content.substr(1);
|
||||
return Rematch.distance(amod, bmod);
|
||||
});
|
||||
|
||||
function generateSideBySideFileHtml(file, config) {
|
||||
var fileHtml = {};
|
||||
fileHtml.left = '';
|
||||
|
|
@ -87,28 +94,17 @@
|
|||
|
||||
var oldLines = [];
|
||||
var newLines = [];
|
||||
var tmpHtml = '';
|
||||
|
||||
for (var i = 0; i < block.lines.length; i++) {
|
||||
var line = block.lines[i];
|
||||
var escapedLine = utils.escape(line.content);
|
||||
|
||||
if (line.type == diffParser.LINE_TYPE.CONTEXT && !oldLines.length && !newLines.length) {
|
||||
fileHtml.left += generateSingleLineHtml(line.type, line.oldNumber, escapedLine);
|
||||
fileHtml.right += generateSingleLineHtml(line.type, line.newNumber, escapedLine);
|
||||
} else if (line.type == diffParser.LINE_TYPE.INSERTS && !oldLines.length && !newLines.length) {
|
||||
fileHtml.left += generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
|
||||
fileHtml.right += generateSingleLineHtml(line.type, line.newNumber, escapedLine);
|
||||
} else if (line.type == diffParser.LINE_TYPE.DELETES && !newLines.length) {
|
||||
oldLines.push(line);
|
||||
} else if (line.type == diffParser.LINE_TYPE.INSERTS && oldLines.length > newLines.length) {
|
||||
newLines.push(line);
|
||||
} else {
|
||||
function processChangeBlock() {
|
||||
var matches = matcher(oldLines, newLines);
|
||||
matches.forEach(function(match){
|
||||
var oldLines = match[0];
|
||||
var newLines = match[1];
|
||||
var tmpHtml;
|
||||
var j = 0;
|
||||
var oldLine, newLine;
|
||||
|
||||
if (oldLines.length === newLines.length) {
|
||||
for (j = 0; j < oldLines.length; j++) {
|
||||
var oldLine, newLine,
|
||||
common = Math.min(oldLines.length, newLines.length),
|
||||
max = Math.max(oldLines.length, newLines.length);
|
||||
for (j = 0; j < common; j++) {
|
||||
oldLine = oldLines[j];
|
||||
newLine = newLines[j];
|
||||
|
||||
|
|
@ -117,27 +113,49 @@
|
|||
var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, config);
|
||||
|
||||
fileHtml.left +=
|
||||
generateSingleLineHtml(oldLine.type, oldLine.oldNumber,
|
||||
generateSingleLineHtml(oldLine.type + ' d2h-change', oldLine.oldNumber,
|
||||
diff.first.line, diff.first.prefix);
|
||||
fileHtml.right +=
|
||||
generateSingleLineHtml(newLine.type, newLine.newNumber,
|
||||
generateSingleLineHtml(newLine.type + ' d2h-change', newLine.newNumber,
|
||||
diff.second.line, diff.second.prefix);
|
||||
}
|
||||
} else {
|
||||
tmpHtml = processLines(oldLines, newLines);
|
||||
if (max > common) {
|
||||
var oldSlice = oldLines.slice(common),
|
||||
newSlice = newLines.slice(common);
|
||||
tmpHtml = processLines(oldLines.slice(common), newLines.slice(common));
|
||||
fileHtml.left += tmpHtml.left;
|
||||
fileHtml.right += tmpHtml.right;
|
||||
}
|
||||
|
||||
});
|
||||
oldLines = [];
|
||||
newLines = [];
|
||||
i--;
|
||||
}
|
||||
for (var i = 0; i < block.lines.length; i++) {
|
||||
var line = block.lines[i];
|
||||
var prefix = line[0];
|
||||
var escapedLine = utils.escape(line.content.substr(1));
|
||||
|
||||
if ( line.type !== diffParser.LINE_TYPE.INSERTS &&
|
||||
(newLines.length > 0 || (line.type !== diffParser.LINE_TYPE.DELETES && oldLines > 0))) {
|
||||
processChangeBlock();
|
||||
}
|
||||
if (line.type == diffParser.LINE_TYPE.CONTEXT) {
|
||||
fileHtml.left += generateSingleLineHtml(line.type, line.oldNumber, escapedLine, prefix);
|
||||
fileHtml.right += generateSingleLineHtml(line.type, line.newNumber, escapedLine, prefix);
|
||||
} else if (line.type == diffParser.LINE_TYPE.INSERTS && !oldLines.length) {
|
||||
fileHtml.left += generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
|
||||
fileHtml.right += generateSingleLineHtml(line.type, line.newNumber, escapedLine, prefix);
|
||||
} else if (line.type == diffParser.LINE_TYPE.DELETES) {
|
||||
oldLines.push(line);
|
||||
} else if (line.type == diffParser.LINE_TYPE.INSERTS && !!oldLines.length) {
|
||||
newLines.push(line);
|
||||
} else {
|
||||
console.error('unknown state in html side-by-side generator');
|
||||
processChangeBlock();
|
||||
}
|
||||
}
|
||||
|
||||
tmpHtml = processLines(oldLines, newLines);
|
||||
fileHtml.left += tmpHtml.left;
|
||||
fileHtml.right += tmpHtml.right;
|
||||
processChangeBlock();
|
||||
});
|
||||
|
||||
return fileHtml;
|
||||
|
|
@ -152,16 +170,27 @@
|
|||
for (j = 0; j < maxLinesNumber; j++) {
|
||||
var oldLine = oldLines[j];
|
||||
var newLine = newLines[j];
|
||||
|
||||
var oldContent;
|
||||
var newContent;
|
||||
var oldPrefix;
|
||||
var newPrefix;
|
||||
if (oldLine) {
|
||||
oldContent = utils.escape(oldLine.content.substr(1));
|
||||
oldPrefix = oldLine.content[0];
|
||||
}
|
||||
if (newLine) {
|
||||
newContent = utils.escape(newLine.content.substr(1));
|
||||
newPrefix = newLine.content[0];
|
||||
}
|
||||
if (oldLine && newLine) {
|
||||
fileHtml.left += generateSingleLineHtml(oldLine.type, oldLine.oldNumber, utils.escape(oldLine.content));
|
||||
fileHtml.right += generateSingleLineHtml(newLine.type, newLine.newNumber, utils.escape(newLine.content));
|
||||
fileHtml.left += generateSingleLineHtml(oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
|
||||
fileHtml.right += generateSingleLineHtml(newLine.type, newLine.newNumber, newContent, newPrefix);
|
||||
} else if (oldLine) {
|
||||
fileHtml.left += generateSingleLineHtml(oldLine.type, oldLine.oldNumber, utils.escape(oldLine.content));
|
||||
fileHtml.left += generateSingleLineHtml(oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
|
||||
fileHtml.right += generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
|
||||
} else if (newLine) {
|
||||
fileHtml.left += generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
|
||||
fileHtml.right += generateSingleLineHtml(newLine.type, newLine.newNumber, utils.escape(newLine.content));
|
||||
fileHtml.right += generateSingleLineHtml(newLine.type, newLine.newNumber, newContent, newPrefix);
|
||||
} else {
|
||||
console.error('How did it get here?');
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue