diff --git a/.eleventy.js b/.eleventy.js index b7faaec..4a41844 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -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) { diff --git a/_src/HighlightLines.js b/_src/HighlightLines.js new file mode 100644 index 0000000..f3e0d50 --- /dev/null +++ b/_src/HighlightLines.js @@ -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; \ No newline at end of file diff --git a/_src/LiquidHighlight.js b/_src/LiquidHighlight.js new file mode 100644 index 0000000..ebaa79c --- /dev/null +++ b/_src/LiquidHighlight.js @@ -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 '
` + lines.join("") + "
");
+ }
+ };
+ };
+
+ return ret(this);
+ }
+}
+
+module.exports = LiquidHighlight;
\ No newline at end of file
diff --git a/_src/eleventy-liquidjs-tag-highlight-plain.js b/_src/eleventy-liquidjs-tag-highlight-plain.js
deleted file mode 100644
index e5a30d4..0000000
--- a/_src/eleventy-liquidjs-tag-highlight-plain.js
+++ /dev/null
@@ -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(`\n` + tokens + "\n
");
- }
- }
-};
\ No newline at end of file
diff --git a/_src/eleventy-liquidjs-tag-highlight-prismjs.js b/_src/eleventy-liquidjs-tag-highlight-prismjs.js
deleted file mode 100644
index 1e8d039..0000000
--- a/_src/eleventy-liquidjs-tag-highlight-prismjs.js
+++ /dev/null
@@ -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(`` + html + "
");
- }
- }
-};
\ No newline at end of file
diff --git a/_src/eleventy-liquidjs-tag-highlight.js b/_src/eleventy-liquidjs-tag-highlight.js
new file mode 100644
index 0000000..4f91e8a
--- /dev/null
+++ b/_src/eleventy-liquidjs-tag-highlight.js
@@ -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();
+ }
+};
\ No newline at end of file
diff --git a/css/index.css b/css/index.css
index 38f91fe..5459a5b 100644
--- a/css/index.css
+++ b/css/index.css
@@ -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 {
diff --git a/css/prism-base16-monokai.dark.css b/css/prism-base16-monokai.dark.css
index 7a872a7..dceb2a6 100644
--- a/css/prism-base16-monokai.dark.css
+++ b/css/prism-base16-monokai.dark.css
@@ -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;
}