Usage (ranges are space separated): {% highlight js 1,4-6 %} One range Adds `highlight-line-active` to lines 1,4,5,6 {% highlight js 3-4 -1 %} Two ranges (add/remove), remove is N/A Adds `highlight-line-add` to lines 3,4 {% highlight js -1 3-4 %} Two ranges (add/remove), add is N/A Adds `highlight-line-remove` to lines 3,4 {% highlight js 3-4 5,8-10 %} Two ranges, both are used Adds `highlight-line-add` to lines 3-4 Adds `highlight-line-remove` to lines 5,8,9,10upstream
@ -0,0 +1,33 @@ | |||
class HighlightLines { | |||
constructor(rangeStr) { | |||
this.highlights = this.convertRangeToHash(rangeStr); | |||
} | |||
convertRangeToHash(rangeStr) { | |||
let hash = {}; | |||
if( !rangeStr ) { | |||
return hash; | |||
} | |||
let ranges = rangeStr.split(",").map(function(range) { | |||
return range.trim(); | |||
}); | |||
for(let range of ranges) { | |||
let startFinish = range.split('-'); | |||
let start = parseInt(startFinish[0], 10); | |||
let end = parseInt(startFinish[1] || start, 10); | |||
for( let j = start, k = end; j<=k; j++ ) { | |||
hash[j] = true; | |||
} | |||
} | |||
return hash; | |||
} | |||
isHighlighted(lineNumber) { | |||
return !!this.highlights[lineNumber] | |||
} | |||
} | |||
module.exports = HighlightLines; |
@ -0,0 +1,83 @@ | |||
const HighlightLines = require('./HighlightLines'); | |||
class LiquidHighlight { | |||
constructor(liquidEngine) { | |||
this.liquidEngine = liquidEngine; | |||
this.hooks = []; | |||
this.classHooks = []; | |||
} | |||
addHook(hookFunction) { | |||
this.hooks.push(hookFunction); | |||
} | |||
addClassHook(hookFunction) { | |||
this.classHooks.push(hookFunction); | |||
} | |||
getObject() { | |||
let ret = function(highlighter) { | |||
return { | |||
parse: function(tagToken, remainTokens) { | |||
let split = tagToken.args.split(" "); | |||
this.language = split[0]; | |||
this.highlights = new HighlightLines(split.length === 2 ? split[1] : ""); | |||
this.highlightsAdd = new HighlightLines(split.length === 3 ? split[1] : ""); | |||
this.highlightsRemove = new HighlightLines(split.length === 3 ? split[2] : ""); | |||
this.tokens = []; | |||
var stream = highlighter.liquidEngine.parser.parseStream(remainTokens); | |||
stream | |||
.on('token', token => { | |||
if (token.name === 'endhighlight') { | |||
stream.stop(); | |||
} else { | |||
this.tokens.push(token); | |||
} | |||
}) | |||
.on('end', x => { | |||
throw new Error("tag highlight not closed"); | |||
}); | |||
stream.start(); | |||
}, | |||
render: function(scope, hash) { | |||
let tokens = this.tokens.map(token => token.raw); | |||
let tokenStr = tokens.join('').trim(); | |||
for( let hook of highlighter.hooks ) { | |||
tokenStr = hook.call(this, this.language, tokenStr); | |||
} | |||
let lines = tokenStr.split("\n").map(function(line, j) { | |||
let classHookClasses = []; | |||
for( let classHook of highlighter.classHooks ) { | |||
let ret = classHook(this.language, line, j); | |||
if( ret ) { | |||
classHookClasses.push(ret); | |||
} | |||
} | |||
return '<div class="highlight-line' + | |||
(this.highlights.isHighlighted(j) ? ' highlight-line-active' : '') + | |||
(this.highlightsAdd.isHighlighted(j) ? ' highlight-line-add' : '') + | |||
(this.highlightsRemove.isHighlighted(j) ? ' highlight-line-remove' : '') + | |||
(classHookClasses.length ? " " + classHookClasses.join(" ") : "") + | |||
'">' + | |||
line + | |||
'</div>'; | |||
}.bind(this)); | |||
return Promise.resolve(`<pre class="language-${this.language}"><code class="language-${this.language}">` + lines.join("") + "</code></pre>"); | |||
} | |||
}; | |||
}; | |||
return ret(this); | |||
} | |||
} | |||
module.exports = LiquidHighlight; |
@ -1,32 +0,0 @@ | |||
module.exports = function(liquidEngine) { | |||
return { | |||
parse: function(tagToken, remainTokens) { | |||
this.language = tagToken.args; | |||
this.tokens = []; | |||
var stream = liquidEngine.parser.parseStream(remainTokens); | |||
stream | |||
.on('token', token => { | |||
if (token.name === 'endhighlight') { | |||
stream.stop(); | |||
} else { | |||
this.tokens.push(token); | |||
} | |||
}) | |||
.on('end', x => { | |||
throw new Error("tag highlight not closed"); | |||
}); | |||
stream.start(); | |||
}, | |||
render: function(scope, hash) { | |||
var tokens = this.tokens.map(token => { | |||
return token.raw.trim(); | |||
}).join('').trim(); | |||
return Promise.resolve(`<pre class="language-${this.language}"><code class="language-${this.language}">\n` + tokens + "\n</code></pre>"); | |||
} | |||
} | |||
}; |
@ -1,37 +0,0 @@ | |||
const Prism = require('prismjs'); | |||
module.exports = function(liquidEngine) { | |||
let langMap = { | |||
"css": "css", | |||
"html": "markup", | |||
"js": "javascript" | |||
}; | |||
return { | |||
parse: function(tagToken, remainTokens) { | |||
this.language = langMap[ tagToken.args ] || tagToken.args; | |||
this.tokens = []; | |||
var stream = liquidEngine.parser.parseStream(remainTokens); | |||
stream | |||
.on('token', token => { | |||
if (token.name === 'endhighlight') { | |||
stream.stop(); | |||
} else { | |||
this.tokens.push(token); | |||
} | |||
}) | |||
.on('end', x => { | |||
throw new Error("tag highlight not closed"); | |||
}); | |||
stream.start() | |||
}, | |||
render: function(scope, hash) { | |||
var tokens = this.tokens.map(token => token.raw).join('').trim(); | |||
var html = Prism.highlight(tokens, Prism.languages[ this.language ]); | |||
return Promise.resolve(`<pre class="language-${this.language}"><code class="language-${this.language}">` + html + "</code></pre>"); | |||
} | |||
} | |||
}; |
@ -0,0 +1,28 @@ | |||
const Prism = require('prismjs'); | |||
const LiquidHighlight = require( "./LiquidHighlight" ); | |||
module.exports = { | |||
plain: function(liquidEngine) { | |||
let highlight = new LiquidHighlight(liquidEngine); | |||
highlight.addClassHook(function(language, line) { | |||
if( language === "dir" ) { | |||
// has trailing slash | |||
if( line.match(/\/$/) !== null ) { | |||
return "highlight-line-isdir"; | |||
} | |||
} | |||
}); | |||
return highlight.getObject(); | |||
}, | |||
prismjs: function(liquidEngine) { | |||
let highlight = new LiquidHighlight(liquidEngine); | |||
highlight.addHook(function(language, htmlStr, lines) { | |||
return Prism.highlight(htmlStr, Prism.languages[ language ]); | |||
}); | |||
return highlight.getObject(); | |||
} | |||
}; |