Add dual text-diff

This commit is contained in:
Nick O'Leary 2017-08-20 22:59:51 +01:00
parent 9a2fd0e2b2
commit 51bad3bf3c
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
3 changed files with 396 additions and 5 deletions

View File

@ -11,6 +11,15 @@ RED.diff = (function() {
// RED.keyboard.add("*","ctrl-shift-l","core:show-current-diff");
RED.keyboard.add("*","ctrl-shift-r","core:show-remote-diff");
RED.actions.add("core:show-test-flow-diff-1",function(){showTestFlowDiff(1)});
RED.keyboard.add("*","ctrl-shift-f 1","core:show-test-flow-diff-1");
RED.actions.add("core:show-test-flow-diff-2",function(){showTestFlowDiff(2)});
RED.keyboard.add("*","ctrl-shift-f 2","core:show-test-flow-diff-2");
RED.actions.add("core:show-test-flow-diff-3",function(){showTestFlowDiff(3)});
RED.keyboard.add("*","ctrl-shift-f 3","core:show-test-flow-diff-3");
}
function buildDiffPanel(container) {
@ -794,6 +803,15 @@ RED.diff = (function() {
remoteCell.addClass("node-diff-empty");
}
}
if (localNode && remoteNode && typeof localNode[d] === "string") {
if (/\n/.test(localNode[d]) || /\n/.test(remoteNode[d])) {
$('<button class="editor-button editor-button-small node-diff-text-diff-button"><i class="fa fa-file-o"> <i class="fa fa-caret-left"></i> <i class="fa fa-caret-right"></i> <i class="fa fa-file-o"></i></button>').click(function() {
showTextDiff(localNode[d],remoteNode[d]);
}).appendTo(propertyNameCell);
}
}
});
return nodePropertiesDiv;
}
@ -1333,6 +1351,304 @@ RED.diff = (function() {
RED.workspaces.refresh();
RED.sidebar.config.refresh();
}
function showTestFlowDiff(index) {
if (index === 1) {
var localFlow = RED.nodes.createCompleteNodeSet();
var originalFlow = RED.nodes.originalFlow();
showTextDiff(JSON.stringify(localFlow,null,4),JSON.stringify(originalFlow,null,4))
} else if (index === 2) {
var local = "1\n2\n3\n4\n5\nA\n6\n7\n8\n9\n";
var remote = "1\nA\n2\n3\nD\nE\n6\n7\n8\n9\n";
showTextDiff(local,remote);
} else if (index === 3) {
var local = "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22";
var remote = "1\nTWO\nTHREE\nEXTRA\n4\n5\n6\n7\n8\n9\n10\n11\n12\nTHIRTEEN\n14\n15\n16\n17\n18\n19\n20\n21\n22";
showTextDiff(local,remote);
}
}
function showTextDiff(textA,textB) {
var trayOptions = {
title: "Compare Changes", //TODO: nls
width: Infinity,
overlay: true,
buttons: [
{
text: RED._("common.label.done"),
click: function() {
RED.tray.close();
}
}
],
resize: function(dimensions) {
// trayWidth = dimensions.width;
},
open: function(tray) {
var trayBody = tray.find('.editor-tray-body');
var diffPanel = $('<div class="node-text-diff"></div>').appendTo(trayBody);
var codeTable = $("<table>").appendTo(diffPanel);
$('<colgroup><col width="50"><col width="50%"><col width="50"><col width="50%"></colgroup>').appendTo(codeTable);
var codeBody = $('<tbody>').appendTo(codeTable);
var diffSummary = diffText(textA||"",textB||"");
var aIndex = 0;
var bIndex = 0;
var diffLength = Math.max(diffSummary.a.length, diffSummary.b.length);
var diffLines = [];
var diffBlocks = [];
var currentBlock;
var blockLength = 0;
var blockType = 0;
for (var i=0;i<diffLength;i++) {
var diffLine = diffSummary[i];
var Adiff = (aIndex < diffSummary.a.length)?diffSummary.a[aIndex]:{type:2,line:""};
var Bdiff = (bIndex < diffSummary.b.length)?diffSummary.b[bIndex]:{type:2,line:""};
if (Adiff.type === 0 && Bdiff.type !== 0) {
Adiff = {type:2,line:""};
bIndex++;
} else if (Bdiff.type === 0 && Adiff.type !== 0) {
Bdiff = {type:2,line:""};
aIndex++;
} else {
aIndex++;
bIndex++;
}
diffLines.push({
a: Adiff,
b: Bdiff
});
if (currentBlock === undefined) {
currentBlock = {start:i,end:i};
blockLength = 0;
blockType = (Adiff.type === 0 && Bdiff.type === 0)?0:1;
} else {
if (Adiff.type === 0 && Bdiff.type === 0) {
// Unchanged line
if (blockType === 0) {
// still unchanged - extend the block
currentBlock.end = i;
blockLength++;
} else if (blockType === 1) {
// end of a change
currentBlock.end = i;
blockType = 2;
blockLength = 0;
} else if (blockType === 2) {
// post-change unchanged
currentBlock.end = i;
blockLength++;
if (blockLength === 8) {
currentBlock.end -= 5; // rollback the end
diffBlocks.push(currentBlock);
currentBlock = {start:i-5,end:i-5};
blockType = 0;
blockLength = 0;
}
}
} else {
// in a change
currentBlock.end = i;
blockLength++;
if (blockType === 0) {
if (currentBlock.end > 3) {
currentBlock.end -= 3;
currentBlock.empty = true;
diffBlocks.push(currentBlock);
currentBlock = {start:i-3,end:i-3};
}
blockType = 1;
} else if (blockType === 2) {
// we were in unchanged, but hit a change again
blockType = 1;
}
}
}
}
if (blockType === 0) {
currentBlock.empty = true;
}
currentBlock.end = diffLength;
diffBlocks.push(currentBlock);
console.table(diffBlocks);
var diffRow;
for (var b = 0; b<diffBlocks.length; b++) {
currentBlock = diffBlocks[b];
if (currentBlock.empty) {
diffRow = createExpandLine(currentBlock.start,currentBlock.end,diffLines).appendTo(codeBody);
} else {
for (var i=currentBlock.start;i<currentBlock.end;i++) {
var row = createDiffLine(diffLines[i]).appendTo(codeBody);
if (i === currentBlock.start) {
row.addClass("start-block");
} else if (i === currentBlock.end-1) {
row.addClass("end-block");
}
}
}
}
},
close: function() {
diffVisible = false;
},
show: function() {
}
}
RED.tray.show(trayOptions);
}
function createExpandLine(start,end,diffLines) {
diffRow = $('<tr class="node-text-diff-expand">');
var content = $('<td colspan="4"> <i class="fa fa-arrows-v"></i> </td>').appendTo(diffRow);
var label = $('<span></span>').appendTo(content);
if (end < diffLines.length-1) {
label.text("@@ -"+(diffLines[end-1].a.i+1)+" +"+(diffLines[end-1].b.i+1));
}
diffRow.click(function(evt) {
console.log(start,end,diffLines.length);
if (end - start > 20) {
var startPos = $(this).offset();
console.log(startPos);
if (start > 0) {
for (var i=start;i<start+10;i++) {
createDiffLine(diffLines[i]).addClass("unchanged").insertBefore($(this));
}
start += 10;
}
if (end < diffLines.length-1) {
for (var i=end-1;i>end-11;i--) {
createDiffLine(diffLines[i]).addClass("unchanged").insertAfter($(this));
}
end -= 10;
}
if (end < diffLines.length-1) {
label.text("@@ -"+(diffLines[end-1].a.i+1)+" +"+(diffLines[end-1].b.i+1));
}
var endPos = $(this).offset();
var delta = endPos.top - startPos.top;
$(".node-text-diff").scrollTop($(".node-text-diff").scrollTop() + delta);
} else {
for (var i=start;i<end;i++) {
createDiffLine(diffLines[i]).addClass("unchanged").insertBefore($(this));
}
$(this).remove();
}
});
return diffRow;
}
function createDiffLine(diffLine) {
var diffRow = $('<tr>');
var Adiff = diffLine.a;
var Bdiff = diffLine.b;
//console.log(diffLine);
var cellNo = $("<td>").text(Adiff.type === 2?"":Adiff.i).appendTo(diffRow);
var cellLine = $("<td>").text(Adiff.line).appendTo(diffRow);
if (Adiff.type === 2) {
cellNo.addClass('blank');
cellLine.addClass('blank');
} else if (Adiff.type === 1) {
cellNo.addClass('added');
cellLine.addClass('added');
} else if (Adiff.type === 4) {
cellNo.addClass('removed');
cellLine.addClass('removed');
}
cellNo = $("<td>").text(Bdiff.type === 2?"":Bdiff.i).appendTo(diffRow);
cellLine = $("<td>").text(Bdiff.line).appendTo(diffRow);
if (Bdiff.type === 2) {
cellNo.addClass('blank');
cellLine.addClass('blank');
} else if (Bdiff.type === 1) {
cellNo.addClass('added');
cellLine.addClass('added');
} else if (Bdiff.type === 4) {
cellNo.addClass('removed');
cellLine.addClass('removed');
}
return diffRow;
}
function diffText(string1, string2,ignoreWhitespace) {
var lines1 = string1.split(/\r?\n/);
var lines2 = string2.split(/\r?\n/);
var i = lines1.length;
var j = lines2.length;
var k;
var m;
var diffSummary = {a:[],b:[]};
var diffMap = [];
for (k = 0; k < i + 1; k++) {
diffMap[k] = [];
for (m = 0; m < j + 1; m++) {
diffMap[k][m] = 0;
}
}
var c = 0;
for (k = i - 1; k >= 0; k--) {
for (m = j - 1; m >=0; m--) {
c++;
if (compareLines(lines1[k],lines2[m],ignoreWhitespace) !== 1) {
diffMap[k][m] = diffMap[k+1][m+1]+1;
} else {
diffMap[k][m] = Math.max(diffMap[(k + 1)][m], diffMap[k][(m + 1)]);
}
}
}
//console.log(c);
k = 0;
m = 0;
while ((k < i) && (m < j)) {
var n = compareLines(lines1[k],lines2[m],ignoreWhitespace);
if (n !== 1) {
var d = 0;
if (n===0) {
d = 0;
} else if (n==2) {
d = 3;
}
diffSummary.a.push({i:k+1,j:m+1,line:lines1[k],type:d});
diffSummary.b.push({i:m+1,j:k+1,line:lines2[m],type:d});
k++;
m++;
} else if (diffMap[(k + 1)][m] >= diffMap[k][(m + 1)]) {
diffSummary.a.push({i:k+1,line:lines1[k],type:1});
k++;
} else {
diffSummary.b.push({i:m+1,line:lines2[m],type:4});
m++;
}
}
while ((k < i) || (m < j)) {
if (k == i) {
diffSummary.b.push({i:m+1,line:lines2[m],type:4});
m++;
} else if (m == j) {
diffSummary.a.push({i:k+1,line:lines1[k],type:1});
k++;
}
}
return diffSummary;
}
function compareLines(string1, string2, ignoreWhitespace) {
if (ignoreWhitespace) {
if (string1 === string2) {
return 0;
}
return string1.trim() === string2.trime() ? 2 : 1;
}
return string1 === string2 ? 0 : 1;
}
return {
init: init,
getRemoteDiff: getRemoteDiff,

View File

@ -210,7 +210,7 @@ RED.tray = (function() {
});
},
show: function show(options) {
if (stack.length > 0) {
if (stack.length > 0 && !options.overlay) {
var oldTray = stack[stack.length-1];
oldTray.tray.css({
right: -(oldTray.tray.width()+10)+"px"
@ -238,14 +238,21 @@ RED.tray = (function() {
tray.tray.remove();
if (stack.length > 0) {
var oldTray = stack[stack.length-1];
oldTray.tray.appendTo("#editor-stack");
setTimeout(function() {
if (!oldTray.options.overlay) {
oldTray.tray.appendTo("#editor-stack");
setTimeout(function() {
handleWindowResize();
oldTray.tray.css({right:0});
if (oldTray.options.show) {
oldTray.options.show();
}
},0);
} else {
handleWindowResize();
oldTray.tray.css({right:0});
if (oldTray.options.show) {
oldTray.options.show();
}
},0);
}
}
if (done) {
done();

View File

@ -49,6 +49,10 @@
height: 25px;
display: inline-block;
box-sizing: border-box;
padding-top: 2px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 50%;
background: #f9f9f9;
text-align: center;
@ -535,3 +539,67 @@
#node-diff-toolbar-resolved-conflicts .node-diff-status {
margin:0;
}
.node-diff-text-diff-button {
float: right;
margin: 2px 3px;
line-height: 14px;
height: 16px;
}
.node-text-diff {
height: 100%;
overflow-y:auto;
table {
margin: 10px;
border: 1px solid $secondary-border-color;
border-radius: 3px;
font-family: monospace;
table-layout: fixed;
width: calc(100% - 20px);
}
td {
vertical-align: top;
word-wrap: break-word;
}
td:nth-child(odd) {
text-align: right;
color: #999;
background: #fafafa;
padding: 1px 5px;
}
td:nth-child(3) {
border-left: 1px solid $secondary-border-color;
}
td:nth-child(even) {
white-space: pre-wrap;
padding: 1px 5px;
}
td.blank {
background: #f6f6f6;
}
td.added {
background: #eefaee;
}
td.removed {
background: #fadddd;
}
tr.unchanged {
background: #fefefe;
}
tr.start-block {
border-top: 1px solid #f3f3f3;
}
tr.end-block {
border-bottom: 1px solid #f3f3f3;
}
tr.node-text-diff-expand td {
text-align: left;
color: #999;
background: #ffd;
&:hover {
cursor: pointer;
background: #ffc;
}
}
}