From 9dbd54adcd88726dc21e8edc36ec6b8da590e83e Mon Sep 17 00:00:00 2001 From: Zach Leatherman Date: Fri, 28 Sep 2018 09:30:22 -0500 Subject: [PATCH] =?UTF-8?q?Big=20release=E2=80=94adds=20support=20for=20Nu?= =?UTF-8?q?njucks=20highlighter,=20adds=20options=20to=20allow=20specifyin?= =?UTF-8?q?g=20using=20only=20some=20of=20the=20syntax=20highlighter=20pac?= =?UTF-8?q?k=20(Fixes=20#2),=20adds=20`options.init`=20to=20make=20it=20po?= =?UTF-8?q?ssible=20to=20further=20customize=20Prism=20(Fixes=20#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eleventy.js | 37 +++-- README.md | 134 ++++++++++++++++-- package.json | 4 +- src/HighlightLinesGroup.js | 4 +- src/HighlightPairedShortcode.js | 24 ++++ ...quidHighlight.js => LiquidHighlightTag.js} | 35 +---- src/hasTemplateFormat.js | 13 ++ src/liquidSyntaxHighlightPrism.js | 21 --- test/HasTemplateFormatTest.js | 25 ++++ test/HighlightPairedShortcodeTest.js | 23 +++ 10 files changed, 241 insertions(+), 79 deletions(-) create mode 100644 src/HighlightPairedShortcode.js rename src/{LiquidHighlight.js => LiquidHighlightTag.js} (50%) create mode 100644 src/hasTemplateFormat.js delete mode 100644 src/liquidSyntaxHighlightPrism.js create mode 100644 test/HasTemplateFormatTest.js create mode 100644 test/HighlightPairedShortcodeTest.js diff --git a/.eleventy.js b/.eleventy.js index 377289f..1facbb4 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -1,14 +1,31 @@ -const liquidPlain = require("./src/liquidSyntaxHighlightPlain"); -const liquidPrismJs = require("./src/liquidSyntaxHighlightPrism"); +const Prism = require("prismjs"); +const hasTemplateFormat = require("./src/hasTemplateFormat"); +const HighlightPairedShortcode = require("./src/HighlightPairedShortcode"); +const LiquidHighlightTag = require("./src/LiquidHighlightTag"); const markdownPrismJs = require("./src/markdownSyntaxHighlight"); -module.exports = function(eleventyConfig, pluginNamespace) { - eleventyConfig.namespace(pluginNamespace, () => { - // compatibility with existing {% highlight js %} and others - eleventyConfig.addLiquidTag("highlight", liquidPrismJs); - eleventyConfig.addMarkdownHighlighter(markdownPrismJs); +module.exports = { + initArguments: { Prism }, + configFunction: function(eleventyConfig, options = {}) { + // TODO hbs? + if( hasTemplateFormat(options.templateFormats, "liquid") ) { + eleventyConfig.addLiquidTag("highlight", (liquidEngine) => { + // {% highlight js 0 2 %} + let highlight = new LiquidHighlightTag(liquidEngine); + return highlight.getObject(); + }); + } - // Deprecated, use {% highlight text %} instead. - eleventyConfig.addLiquidTag("highlight-plain", liquidPlain); - }); + if( hasTemplateFormat(options.templateFormats, "njk") ) { + eleventyConfig.addPairedNunjucksShortcode("highlight", (content, args) => { + // {% highlight "js 0 2-3" %} + let [language, ...highlightNumbers] = args.split(" "); + return HighlightPairedShortcode(content, language, highlightNumbers.join(" ")); + }); + } + + if( hasTemplateFormat(options.templateFormats, "md") ) { + eleventyConfig.addMarkdownHighlighter(markdownPrismJs); + } + } }; diff --git a/README.md b/README.md index 62c3221..b6d2611 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,36 @@ You are responsible for including [your favorite PrismJS theme CSS](https://gith Read more about [Eleventy plugins.](https://www.11ty.io/docs/plugins/) +### Options + +Optionally pass in an options object as the second argument to `addPlugin` to further customize this plugin pack. + +``` +const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight"); +module.exports = function(eleventyConfig) { + eleventyConfig.addPlugin(syntaxHighlight, { + + // Change which syntax highlighters are installed + templateFormats: ["*"], // default + + // Or, just njk and md syntax highlighters (do not install liquid) + // templateFormats: ["njk", "md"], + + // init callback lets you customize Prism + init: function({ Prism }) { + Prism.languages.myCustomLanguage = /* */; + } + }); +}; +``` + ## Usage ### This plugin provides: * Markdown Highlighter: syntax highlights using PrismJS -* Liquid Tag `{% highlight %}`: syntax highlights using PrismJS. +* Liquid Custom Tag `{% highlight %}`: syntax highlights using PrismJS. +* Nunjucks Paired Shortcode `{% highlight %}`: syntax highlights using PrismJS. ### Markdown Highlighter @@ -37,6 +61,7 @@ Optionally specify a language after the start of the markdown fenced code block. * [List of supported PrismJS languages](http://prismjs.com/#languages-list) ```` + ``` js function myFunction() { return true; @@ -45,8 +70,12 @@ function myFunction() { ```` ```` -// Line highlighting classes (single highlight) -// Adds `highlight-line-active` class to lines 1,3,4,5 (for line highlighting) + + + ``` js/1,3-5 function myFunction() { // … @@ -56,9 +85,13 @@ function myFunction() { ```` ```` -// Line highlighting classes (add and remove mode) -// Adds `highlight-line-add` class to lines 1,3 -// Adds `highlight-line-remove` class to lines 5,6,7,8 + + + ``` js/1,3/5-8 function myFunction() { // … @@ -85,6 +118,7 @@ function myFunction() { * [List of supported PrismJS languages](http://prismjs.com/#languages-list) ``` + {% highlight js %} function myFunction() { return true; @@ -93,8 +127,12 @@ function myFunction() { ``` ``` -// Line highlighting classes (single highlight) -// Adds `highlight-line-active` class to lines 1,3,4,5 (for line highlighting) + + + {% highlight js 1,3-5 %} function myFunction() { // … @@ -104,9 +142,13 @@ function myFunction() { ``` ``` -// Line highlighting classes (add and remove) -// Adds `highlight-line-add` class to lines 1,3 -// Adds `highlight-line-remove` class to lines 5,6,7,8 + + + {% highlight js 1,3 5-8 %} function myFunction() { // … @@ -120,6 +162,7 @@ function myFunction() { Use `text` to use the line highlighting features without PrismJS. ``` + {% highlight text 1-2 %} function myFunction() { let highlighted = true; @@ -128,16 +171,79 @@ function myFunction() { {% endhighlight %} ``` -### Sample Line Highlighting CSS +### Nunjucks Paired Shortcode: Prism Syntax Highlighter + +* [List of supported PrismJS languages](http://prismjs.com/#languages-list) + +``` + +{% highlight "js" %} +function myFunction() { + return true; +} +{% endhighlight %} +``` ``` + + + +{% highlight "js 1,3-5" %} +function myFunction() { + // … + return true; +} +{% endhighlight %} +``` + +``` + + + +{% highlight "js 1,3 5-8" %} +function myFunction() { + // … + return true; +} +{% endhighlight %} +``` + +#### Plain text + +Use `text` to use the line highlighting features without PrismJS. + +``` + +{% highlight "text 1-2" %} +function myFunction() { + let highlighted = true; + return highlighted; +} +{% endhighlight %} +``` + +### Sample Line Highlighting CSS + +```css .highlight-line { display: block; - padding: 0.125em 1em; text-decoration: none; /* override del, ins, mark defaults */ color: inherit; /* override del, ins, mark defaults */ } -.highlight-line:not(:empty) + br { + +/* allow highlighting empty lines */ +.highlight-line:empty:before { + content: " "; +} +/* avoid double line breaks when using display: block; */ +.highlight-line + br { display: none; } diff --git a/package.json b/package.json index 51cc18f..58f4aec 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "ava": "^0.25.0" }, "dependencies": { - "@11ty/eleventy": "^0.3.5", - "prismjs": "^1.13.0" + "@11ty/eleventy": "^0.5.4", + "prismjs": "^1.15.0" } } diff --git a/src/HighlightLinesGroup.js b/src/HighlightLinesGroup.js index 1afecba..8f55098 100644 --- a/src/HighlightLinesGroup.js +++ b/src/HighlightLinesGroup.js @@ -5,9 +5,9 @@ class HighlightLinesGroup { this.init(str, delimiter); } - init(str, delimiter) { + init(str = "", delimiter = " ") { this.str = str; - this.delimiter = delimiter || " "; + this.delimiter = delimiter; let split = str.split(this.delimiter); this.highlights = new HighlightLines(split.length === 1 ? split[0] : ""); diff --git a/src/HighlightPairedShortcode.js b/src/HighlightPairedShortcode.js new file mode 100644 index 0000000..e8e0cdb --- /dev/null +++ b/src/HighlightPairedShortcode.js @@ -0,0 +1,24 @@ +const Prism = require("prismjs"); +const PrismLoader = require("prismjs/components/index.js"); +const HighlightLinesGroup = require("./HighlightLinesGroup"); + +module.exports = function(content, language, highlightNumbers) { + let highlightedContent; + if( language === "text" ) { + highlightedContent = content.trim(); + } else { + if( !Prism.languages[ language ] ) { + PrismLoader([language]); + } + + highlightedContent = Prism.highlight(content.trim(), Prism.languages[ language ]); + } + + let group = new HighlightLinesGroup(highlightNumbers); + + let lines = highlightedContent.split("\n").map(function(line, j) { + return group.getLineMarkup(j, line); + }); + + return `
` + lines.join("
") + "
"; +}; diff --git a/src/LiquidHighlight.js b/src/LiquidHighlightTag.js similarity index 50% rename from src/LiquidHighlight.js rename to src/LiquidHighlightTag.js index b59a6ca..3a3242d 100644 --- a/src/LiquidHighlight.js +++ b/src/LiquidHighlightTag.js @@ -1,18 +1,9 @@ const HighlightLinesGroup = require("./HighlightLinesGroup"); +const HighlightPairedShortcode = require("./HighlightPairedShortcode"); -class LiquidHighlight { +class LiquidHighlightTag { constructor(liquidEngine) { this.liquidEngine = liquidEngine; - this.hooks = []; - this.classHooks = []; - } - - addHook(hookFunction) { - this.hooks.push(hookFunction); - } - - addClassHook(hookFunction) { - this.classHooks.push(hookFunction); } getObject() { @@ -22,7 +13,7 @@ class LiquidHighlight { let split = tagToken.args.split(" "); this.language = split.shift(); - this.highlights = new HighlightLinesGroup(split.join(" ")); + this.highlightLines = split.join(" "); this.tokens = []; @@ -46,23 +37,7 @@ class LiquidHighlight { 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 this.highlights.getLineMarkup(j, line, classHookClasses); - }.bind(this)); - - return Promise.resolve(`
` + lines.join("
") + "
"); + return Promise.resolve(HighlightPairedShortcode(tokenStr, this.language, this.highlightLines)); } }; }; @@ -71,4 +46,4 @@ class LiquidHighlight { } } -module.exports = LiquidHighlight; +module.exports = LiquidHighlightTag; diff --git a/src/hasTemplateFormat.js b/src/hasTemplateFormat.js new file mode 100644 index 0000000..ee81e89 --- /dev/null +++ b/src/hasTemplateFormat.js @@ -0,0 +1,13 @@ +module.exports = function(templateFormats = ["*"], format = false) { + if(!Array.isArray(templateFormats)) { + templateFormats = [templateFormats]; + } + + if( Array.isArray(templateFormats) ) { + if( templateFormats.indexOf("*") > -1 || templateFormats.indexOf(format) > -1 ) { + return true; + } + } + + return false; +}; diff --git a/src/liquidSyntaxHighlightPrism.js b/src/liquidSyntaxHighlightPrism.js deleted file mode 100644 index 7fd0ece..0000000 --- a/src/liquidSyntaxHighlightPrism.js +++ /dev/null @@ -1,21 +0,0 @@ -const Prism = require("prismjs"); -const PrismLoader = require("prismjs/components/index.js"); -const LiquidHighlight = require( "./LiquidHighlight" ); - -module.exports = function(liquidEngine) { - let highlight = new LiquidHighlight(liquidEngine); - - highlight.addHook(function(language, htmlStr, lines) { - if( language === "text" ) { - return htmlStr; - } else { - if( !Prism.languages[ language ] ) { - PrismLoader([language]); - } - - return Prism.highlight(htmlStr, Prism.languages[ language ]); - } - }); - - return highlight.getObject(); -}; diff --git a/test/HasTemplateFormatTest.js b/test/HasTemplateFormatTest.js new file mode 100644 index 0000000..f77747b --- /dev/null +++ b/test/HasTemplateFormatTest.js @@ -0,0 +1,25 @@ +import test from "ava"; +import hasTemplateFormat from "../src/hasTemplateFormat"; + +test("hasTemplateFormats", t => { + t.true(hasTemplateFormat("*", "liquid")); + t.false(hasTemplateFormat([], "liquid")); + + // options not specified, defaults to * + t.true(hasTemplateFormat(undefined, "liquid")); + t.false(hasTemplateFormat(null, "liquid")); + + t.true(hasTemplateFormat("*", false)); + t.false(hasTemplateFormat([], false)); + + // options not specified, defaults to * + t.true(hasTemplateFormat(undefined, false)); + t.false(hasTemplateFormat(null, false)); + + t.true(hasTemplateFormat(["*"], "liquid")); + t.true(hasTemplateFormat(["liquid"], "liquid")); + t.true(hasTemplateFormat(["liquid", "njk"], "liquid")); + t.true(hasTemplateFormat(["liquid", "njk"], "njk")); + t.true(hasTemplateFormat(["liquid", "njk", "md"], "md")); + t.false(hasTemplateFormat(["liquid", "njk", "md"], "pug")); +}); diff --git a/test/HighlightPairedShortcodeTest.js b/test/HighlightPairedShortcodeTest.js new file mode 100644 index 0000000..b90a81f --- /dev/null +++ b/test/HighlightPairedShortcodeTest.js @@ -0,0 +1,23 @@ +import test from "ava"; +import HighlightPairedShortcode from "../src/HighlightPairedShortcode"; + +test("Base", async t => { + t.is(await HighlightPairedShortcode(`alert(); +alert();`, "js"), `
alert();
alert();
`); +}); + +test("Highlight Active", async t => { + t.is(await HighlightPairedShortcode(`alert(); +alert();`, "js", "0"), `
alert();
alert();
`); + + t.is(await HighlightPairedShortcode(`alert(); +alert();`, "js", "0-1"), `
alert();
alert();
`); +}); + +test("Highlight Add/Remove", async t => { + t.is(await HighlightPairedShortcode(`alert(); +alert();`, "js", "0 1"), `
alert();
alert();
`); + + t.is(await HighlightPairedShortcode(`alert(); +alert();`, "js", "1 0"), `
alert();
alert();
`); +});