Refactors Liquid syntax highlighters to add line highlights.
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,10
This commit is contained in:
parent
e408e2fe50
commit
963b5d46e6
@ -1,5 +1,5 @@
|
||||
const { DateTime } = require("luxon");
|
||||
const liquidjsSyntaxHighlighter = require("./_src/eleventy-liquidjs-tag-highlight-prismjs");
|
||||
const highlighters = require("./_src/eleventy-liquidjs-tag-highlight");
|
||||
|
||||
function dateToISO(dateObj) {
|
||||
return DateTime.fromJSDate(dateObj).toISO({ includeOffset: true, suppressMilliseconds: true });
|
||||
@ -25,7 +25,7 @@ module.exports = function(eleventyConfig) {
|
||||
});
|
||||
|
||||
// compatibility with existing {% highlight js %} and others
|
||||
eleventyConfig.addLiquidTag("highlight", liquidjsSyntaxHighlighter);
|
||||
eleventyConfig.addLiquidTag("highlight", highlighters.prismjs);
|
||||
|
||||
// only content in the `posts/` directory
|
||||
eleventyConfig.addCollection("posts", function(collection) {
|
||||
|
33
_src/HighlightLines.js
Normal file
33
_src/HighlightLines.js
Normal file
@ -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;
|
83
_src/LiquidHighlight.js
Normal file
83
_src/LiquidHighlight.js
Normal file
@ -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>");
|
||||
}
|
||||
}
|
||||
};
|
28
_src/eleventy-liquidjs-tag-highlight.js
Normal file
28
_src/eleventy-liquidjs-tag-highlight.js
Normal file
@ -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();
|
||||
}
|
||||
};
|
@ -73,6 +73,23 @@ pre {
|
||||
margin: .5em 0;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
.highlight-line {
|
||||
padding: 0.125em 1em; /* 2px 16px /16 */
|
||||
}
|
||||
.highlight-line-isdir {
|
||||
color: #b0b0b0;
|
||||
background-color: #222;
|
||||
}
|
||||
.highlight-line-active {
|
||||
background-color: #444;
|
||||
background-color: hsla(0, 0%, 27%, .8);
|
||||
}
|
||||
.highlight-line-add {
|
||||
background-color: #45844b;
|
||||
}
|
||||
.highlight-line-remove {
|
||||
background-color: #902f2f;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.home {
|
||||
|
@ -17,7 +17,7 @@ code[class*="language-"], pre[class*="language-"] {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
padding: 1.5em 0;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user