Fix highlight for multiline elements
* Now highlight should be perfect except for cases where the diff context is not enough to understant what is the code
This commit is contained in:
parent
1e242ded9a
commit
affd71eca0
2 changed files with 158 additions and 26 deletions
|
|
@ -24,6 +24,20 @@
|
|||
|
||||
<script>
|
||||
var lineDiffExample =
|
||||
'diff --git a/src/core/init.java b/src/core/init.java\n' +
|
||||
'index e49196a..50f310c 100644\n' +
|
||||
'--- a/src/core/init.java\n' +
|
||||
'+++ b/src/core/init.java\n' +
|
||||
'@@ -101,7 +101,7 @@\n' +
|
||||
' /**\n' +
|
||||
' * Setter for property filesize.\n' +
|
||||
' *\n' +
|
||||
" * @param filesize value of property 'filesize'.\n" +
|
||||
' */\n' +
|
||||
' public void setFilesize(int filesize) {\n' +
|
||||
'- this.filesizeOld = filesizeOld;\n' +
|
||||
'+ this.filesizeNew = filesizeNew;\n' +
|
||||
' }\n' +
|
||||
'diff --git a/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/coverage.init b/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/coverage.init\n' +
|
||||
'index fc56817..e8e7e49 100644\n' +
|
||||
'--- a/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/src/very/long/file/path/coverage.init\n' +
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/*global $, hljs*/
|
||||
/*global $, hljs, Diff2Html*/
|
||||
|
||||
(function() {
|
||||
|
||||
|
|
@ -69,15 +69,28 @@
|
|||
|
||||
var $target = that._getTarget(targetId);
|
||||
|
||||
var languages = that._getLanguages($target);
|
||||
|
||||
// pass the languages to the highlightjs plugin
|
||||
hljs.configure({languages: languages});
|
||||
// collect all the diff files and execute the highlight on their lines
|
||||
var $files = $target.find(".d2h-file-wrapper");
|
||||
$files.map(function(i, file) {
|
||||
var state;
|
||||
var $file = $(file);
|
||||
var language = $file.data("lang");
|
||||
|
||||
// collect all the code lines and execute the highlight on them
|
||||
var $codeLines = $target.find(".d2h-code-line-ctn");
|
||||
var $codeLines = $file.find(".d2h-code-line-ctn");
|
||||
$codeLines.map(function(i, line) {
|
||||
hljs.highlightBlock(line);
|
||||
var text = line.textContent;
|
||||
var result = hljs.getLanguage(language) ? hljs.highlight(language, text, true, state) : hljs.highlightAuto(text);
|
||||
state = result.top;
|
||||
|
||||
var originalStream = nodeStream(line);
|
||||
if (originalStream.length) {
|
||||
var resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
resultNode.innerHTML = result.value;
|
||||
result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
|
||||
}
|
||||
$(line).html(result.value);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -97,24 +110,6 @@
|
|||
return $target;
|
||||
};
|
||||
|
||||
Diff2HtmlUI.prototype._getLanguages = function($target) {
|
||||
var allFileLanguages = [];
|
||||
|
||||
if (diffJson) {
|
||||
// collect all the file extensions in the json
|
||||
allFileLanguages = diffJson.map(function(line) {
|
||||
return line.language;
|
||||
});
|
||||
} else {
|
||||
$target.find(".d2h-file-wrapper").map(function(i, file) {
|
||||
allFileLanguages.push($(file).data("lang"));
|
||||
});
|
||||
}
|
||||
|
||||
// return only distinct languages
|
||||
return this._distinct(allFileLanguages);
|
||||
};
|
||||
|
||||
Diff2HtmlUI.prototype._getHashTag = function() {
|
||||
var docUrl = document.URL;
|
||||
var hashTagIndex = docUrl.indexOf('#');
|
||||
|
|
@ -181,6 +176,129 @@
|
|||
return text;
|
||||
};
|
||||
|
||||
/*
|
||||
* Copied from Highlight.js Private API
|
||||
* Will be removed when this part of the API is exposed
|
||||
*/
|
||||
|
||||
/* Utility functions */
|
||||
|
||||
function escape(value) {
|
||||
return value.replace(/&/gm, '&').replace(/</gm, '<').replace(/>/gm, '>');
|
||||
}
|
||||
|
||||
function tag(node) {
|
||||
return node.nodeName.toLowerCase();
|
||||
}
|
||||
|
||||
/* Stream merging */
|
||||
|
||||
function nodeStream(node) {
|
||||
var result = [];
|
||||
(function _nodeStream(node, offset) {
|
||||
for (var child = node.firstChild; child; child = child.nextSibling) {
|
||||
if (child.nodeType == 3)
|
||||
offset += child.nodeValue.length;
|
||||
else if (child.nodeType == 1) {
|
||||
result.push({
|
||||
event: 'start',
|
||||
offset: offset,
|
||||
node: child
|
||||
});
|
||||
offset = _nodeStream(child, offset);
|
||||
// Prevent void elements from having an end tag that would actually
|
||||
// double them in the output. There are more void elements in HTML
|
||||
// but we list only those realistically expected in code display.
|
||||
if (!tag(child).match(/br|hr|img|input/)) {
|
||||
result.push({
|
||||
event: 'stop',
|
||||
offset: offset,
|
||||
node: child
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
})(node, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
function mergeStreams(original, highlighted, value) {
|
||||
var processed = 0;
|
||||
var result = '';
|
||||
var nodeStack = [];
|
||||
|
||||
function selectStream() {
|
||||
if (!original.length || !highlighted.length) {
|
||||
return original.length ? original : highlighted;
|
||||
}
|
||||
if (original[0].offset != highlighted[0].offset) {
|
||||
return (original[0].offset < highlighted[0].offset) ? original : highlighted;
|
||||
}
|
||||
|
||||
/*
|
||||
To avoid starting the stream just before it should stop the order is
|
||||
ensured that original always starts first and closes last:
|
||||
if (event1 == 'start' && event2 == 'start')
|
||||
return original;
|
||||
if (event1 == 'start' && event2 == 'stop')
|
||||
return highlighted;
|
||||
if (event1 == 'stop' && event2 == 'start')
|
||||
return original;
|
||||
if (event1 == 'stop' && event2 == 'stop')
|
||||
return highlighted;
|
||||
... which is collapsed to:
|
||||
*/
|
||||
return highlighted[0].event == 'start' ? original : highlighted;
|
||||
}
|
||||
|
||||
function open(node) {
|
||||
function attrStr(a) {
|
||||
return ' ' + a.nodeName + '="' + escape(a.value) + '"';
|
||||
}
|
||||
|
||||
result += '<' + tag(node) + Array.prototype.map.call(node.attributes, attrStr).join('') + '>';
|
||||
}
|
||||
|
||||
function close(node) {
|
||||
result += '</' + tag(node) + '>';
|
||||
}
|
||||
|
||||
function render(event) {
|
||||
(event.event == 'start' ? open : close)(event.node);
|
||||
}
|
||||
|
||||
while (original.length || highlighted.length) {
|
||||
var stream = selectStream();
|
||||
result += escape(value.substr(processed, stream[0].offset - processed));
|
||||
processed = stream[0].offset;
|
||||
if (stream == original) {
|
||||
/*
|
||||
On any opening or closing tag of the original markup we first close
|
||||
the entire highlighted node stack, then render the original tag along
|
||||
with all the following original tags at the same offset and then
|
||||
reopen all the tags on the highlighted stack.
|
||||
*/
|
||||
nodeStack.reverse().forEach(close);
|
||||
do {
|
||||
render(stream.splice(0, 1)[0]);
|
||||
stream = selectStream();
|
||||
} while (stream == original && stream.length && stream[0].offset == processed);
|
||||
nodeStack.reverse().forEach(open);
|
||||
} else {
|
||||
if (stream[0].event == 'start') {
|
||||
nodeStack.push(stream[0].node);
|
||||
} else {
|
||||
nodeStack.pop();
|
||||
}
|
||||
render(stream.splice(0, 1)[0]);
|
||||
}
|
||||
}
|
||||
return result + escape(value.substr(processed));
|
||||
}
|
||||
|
||||
/* **** Highlight.js Private API **** */
|
||||
|
||||
module.exports.Diff2HtmlUI = Diff2HtmlUI;
|
||||
|
||||
// Expose diff2html in the browser
|
||||
|
|
|
|||
Loading…
Reference in a new issue