Show coloured diffs in blogs
A quick Google search for JavaScript code to format context-diffs as coloured side-by-side diffs didn't turn up anything obvious (admittedly I haven't the slightest clue where to look for this sort of stuff on the 'net).
So I went and quickly wrote my own formatting function which formats a given context diff and returns a table-formatted side-by-side diff:
function formatDiff(textContent) {
"use strict";
function E(name, attrs) {
var arg, elem = document.createElement(name), prop;
for (prop in attrs) {
elem.setAttribute(prop, attrs[prop]);
}
for (arg = 2; arg != arguments.length; ++arg) {
elem.appendChild(arguments[arg]);
}
return elem;
}
var td = E.bind(undefined, "td");
var th = E.bind(undefined, "th", {});
var tr = E.bind(undefined, "tr", {});
var T = document.createTextNode.bind(document);
var res = E("table", {"class": "diff"});
var sects = {"unchanged": [], "removed": [], "added": []};
var lines = textContent.split("\n");
lines.push("");
lines.forEach(function (l) {
var leftAttrs = {}, rightAttrs = {};
var lineType = !l.length ? "eof"
: "--- " === l.substr(0, 4) ? "oldfile"
: "+++ " === l.substr(0, 4) ? "newfile"
: "@@ " === l.substr(0, 3) ? "range"
: "-" === l[0] ? "removed"
: "+" === l[0] ? "added"
: "unchanged";
/*
* An "unchanged" section ends at a line that isn't "unchanged".
*/
if (sects["unchanged"].length && "unchanged" !== lineType) {
res.appendChild(tr(
td({}, T(sects["unchanged"].join("\n"))),
td({}, T(sects["unchanged"].join("\n")))
));
sects["unchanged"].length = 0;
}
/*
* An added/removed section ends
* o at a line that isn't "added" if there are "added" lines,
* o at a line that isn't "added" or "removed" if there are "removed" lines.
*/
if (sects["added"].length && "added" !== lineType ||
sects["removed"].length && "removed" !== lineType && "added" !== lineType)
{
if (sects["added"].length && sects["removed"].length) {
leftAttrs["class"] = rightAttrs["class"] = "diff-changed";
} else if (sects["removed"].length) {
leftAttrs["class"] = "diff-removed";
} else {
rightAttrs["class"] = "diff-added";
}
res.appendChild(tr(
td(leftAttrs, T(sects["removed"].join("\n"))),
td(rightAttrs, T(sects["added"].join("\n")))
));
sects["added"].length = sects["removed"].length = 0;
}
if ("oldfile" === lineType) {
res.appendChild(tr(th(T(l.substring(4)))));
} else if ("newfile" === lineType) {
res.lastChild.appendChild(th(T(l.substring(4))));
} else if ("range" === lineType) {
var ds = td(
{"class": "diff-section", "colspan": 2},
T(l.split("@@").join(" "))
);
res.appendChild(tr(ds));
} else if ("eof" !== lineType) {
sects[lineType].push(l.substring(1));
}
});
return res;
}
// Clone the HTML node collection before modifying the DOM.
var diffs = Array.prototype.slice.call(document.getElementsByClassName("diff"));
diffs.forEach(function (diff) {
diff.replaceChild(formatDiff(diff.firstChild.textContent), diff.firstChild);
});
The code at the bottom reformats any elements of class diff: see it in action!
Here's a small unit test:
--- file1
+++ file2
@@ -0,+1 @@ something
0
1
-2
+2
+3
-3
-4
+4
-5
6
+7
8
There are already various source code highlighters out there so arguably this posting could be made to look a lot nicer. :-)
Comments
Post a Comment