From 4596e047feace2ab67af359e42993ea8cd74c798 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Thu, 6 Feb 2020 17:01:35 -0500 Subject: [PATCH 1/7] callbacks for highlight --- src/highlight.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/highlight.js b/src/highlight.js index e14338e5e6..eb309c3fa6 100644 --- a/src/highlight.js +++ b/src/highlight.js @@ -89,11 +89,21 @@ const HLJS = function(hljs) { * @property {string} language - the language name * @property {number} relevance - the relevance score * @property {string} value - the highlighted HTML code + * @property {string} originalCade - the original raw code * @property {mode} top - top of the current mode stack * @property {boolean} illegal - indicates whether any illegal matches were found */ function highlight(languageName, code, ignore_illegals, continuation) { - var codeToHighlight = code; + var codeToHighlight; + var context = { + originalCode: code, + language: languageName + } + // the plugin can change the desired language or the code to be highlighted + // just be changing the object it was passed + fire("before:highlight", context); + codeToHighlight = context.originalCode; + languageName = context.language; function endOfMode(mode, lexeme) { if (regex.startsWith(mode.endRe, lexeme)) { @@ -330,6 +340,7 @@ const HLJS = function(hljs) { var relevance = 0; var match, processedCount, index = 0; + var returnValue; try { while (true) { top.terminators.lastIndex = index; @@ -345,7 +356,7 @@ const HLJS = function(hljs) { emitter.finalize(); result = emitter.toHTML(); - return { + returnValue = { relevance: relevance, value: result, language: languageName, @@ -355,7 +366,7 @@ const HLJS = function(hljs) { }; } catch (err) { if (err.message && err.message.includes('Illegal')) { - return { + returnValue = { illegal: true, illegalBy: { msg: err.message, @@ -368,7 +379,7 @@ const HLJS = function(hljs) { emitter: emitter, }; } else if (SAFE_MODE) { - return { + returnValue = { relevance: 0, value: escape(codeToHighlight), emitter: emitter, @@ -380,6 +391,12 @@ const HLJS = function(hljs) { throw err; } } + + returnValue.originalCode = codeToHighlight; + // the plugin can change anything in returnValue to suite it + fire("after:highlight", returnValue); + + return returnValue; } /* From acae7434913c0c907a5e94b1ab3f8a9d1faa32b2 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sat, 8 Feb 2020 22:19:22 -0500 Subject: [PATCH 2/7] wrap it up much nicer --- src/highlight.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/highlight.js b/src/highlight.js index eb309c3fa6..094e579643 100644 --- a/src/highlight.js +++ b/src/highlight.js @@ -94,7 +94,6 @@ const HLJS = function(hljs) { * @property {boolean} illegal - indicates whether any illegal matches were found */ function highlight(languageName, code, ignore_illegals, continuation) { - var codeToHighlight; var context = { originalCode: code, language: languageName @@ -102,8 +101,23 @@ const HLJS = function(hljs) { // the plugin can change the desired language or the code to be highlighted // just be changing the object it was passed fire("before:highlight", context); - codeToHighlight = context.originalCode; - languageName = context.language; + + // a before plugin can usurp the result completely by providing it's own + // in which case we don't even need to call highlight + var result = context.result ? + context.result : + _highlight(context.language, context.originalCode, ignore_illegals, continuation); + + result.originalCode = context.originalCode; + // the plugin can change anything in result to suite it + fire("after:highlight", result); + + return result; + } + + // private highlight that's used internally and does not fire callbacks + function _highlight(languageName, code, ignore_illegals, continuation) { + var codeToHighlight = code; function endOfMode(mode, lexeme) { if (regex.startsWith(mode.endRe, lexeme)) { @@ -167,7 +181,7 @@ const HLJS = function(hljs) { } var result = explicit ? - highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]) : + _highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]) : highlightAuto(mode_buffer, top.subLanguage.length ? top.subLanguage : undefined); // Counting embedded language score towards the host language may be disabled @@ -340,7 +354,6 @@ const HLJS = function(hljs) { var relevance = 0; var match, processedCount, index = 0; - var returnValue; try { while (true) { top.terminators.lastIndex = index; @@ -356,7 +369,7 @@ const HLJS = function(hljs) { emitter.finalize(); result = emitter.toHTML(); - returnValue = { + return { relevance: relevance, value: result, language: languageName, @@ -366,7 +379,7 @@ const HLJS = function(hljs) { }; } catch (err) { if (err.message && err.message.includes('Illegal')) { - returnValue = { + return { illegal: true, illegalBy: { msg: err.message, @@ -379,7 +392,7 @@ const HLJS = function(hljs) { emitter: emitter, }; } else if (SAFE_MODE) { - returnValue = { + return { relevance: 0, value: escape(codeToHighlight), emitter: emitter, @@ -391,12 +404,6 @@ const HLJS = function(hljs) { throw err; } } - - returnValue.originalCode = codeToHighlight; - // the plugin can change anything in returnValue to suite it - fire("after:highlight", returnValue); - - return returnValue; } /* From 5f14e5208cb703f4bd74e9945d80c0912df0859f Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 9 Feb 2020 21:04:56 -0500 Subject: [PATCH 3/7] autoHighlight calls private _highlight interally --- src/highlight.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/highlight.js b/src/highlight.js index 094e579643..693b7892fb 100644 --- a/src/highlight.js +++ b/src/highlight.js @@ -97,7 +97,7 @@ const HLJS = function(hljs) { var context = { originalCode: code, language: languageName - } + }; // the plugin can change the desired language or the code to be highlighted // just be changing the object it was passed fire("before:highlight", context); @@ -426,7 +426,7 @@ const HLJS = function(hljs) { }; var second_best = result; languageSubset.filter(getLanguage).filter(autoDetection).forEach(function(name) { - var current = highlight(name, code, false); + var current = _highlight(name, code, false); current.language = name; if (current.relevance > second_best.relevance) { second_best = current; From 29e1d7b43ef02af8230e4ba5d983327369651280 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Wed, 19 Feb 2020 07:39:59 -0500 Subject: [PATCH 4/7] simpler naming --- src/highlight.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/highlight.js b/src/highlight.js index 693b7892fb..2b97470e3a 100644 --- a/src/highlight.js +++ b/src/highlight.js @@ -89,13 +89,13 @@ const HLJS = function(hljs) { * @property {string} language - the language name * @property {number} relevance - the relevance score * @property {string} value - the highlighted HTML code - * @property {string} originalCade - the original raw code + * @property {string} code - the original raw code * @property {mode} top - top of the current mode stack * @property {boolean} illegal - indicates whether any illegal matches were found */ function highlight(languageName, code, ignore_illegals, continuation) { var context = { - originalCode: code, + code, language: languageName }; // the plugin can change the desired language or the code to be highlighted @@ -106,9 +106,9 @@ const HLJS = function(hljs) { // in which case we don't even need to call highlight var result = context.result ? context.result : - _highlight(context.language, context.originalCode, ignore_illegals, continuation); + _highlight(context.language, context.code, ignore_illegals, continuation); - result.originalCode = context.originalCode; + result.code = context.code; // the plugin can change anything in result to suite it fire("after:highlight", result); From 686ec44544c3e424295dc4552ce5b9a23428a216 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sat, 29 Feb 2020 11:44:00 -0500 Subject: [PATCH 5/7] docs --- docs/plugin-api.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/plugin-api.rst b/docs/plugin-api.rst index 83f3fa1ffd..4b9397ddf6 100644 --- a/docs/plugin-api.rst +++ b/docs/plugin-api.rst @@ -61,6 +61,46 @@ This approach is best for simpler plugins. Callbacks --------- +before:highlight({code, language}) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This callback function is passed a context object with two keys: + +code + The code to be highlighted. + +language + The language grammar that should be used for highlighting. + +Your plugin may modify either value and those new values will be used as input +to the highlighting engine. If you add a ``result`` key to the object that +result will be returned as the overall result and the internal highlighting code +will never even be called. + +If you're plugin plans to make its own recursive calls to ``highlight`` you'll +need to manually handle this. Each time ``highlight`` is called your plugin +callbacks will also be called - making it easy to get into an infinite loop. +You'll likely need to use a class based plugin and add a guard so that your +plugin code is only triggered on the initial call to ``highlight`` and not on +any internal calls your plugin itself is making. + +Note: This callback does not fire from highlighting resulting from auto-language detection. + +It returns nothing. + + +after:highlight(result) +^^^^^^^^^^^^^^^^^^^^^^^ + +This callback function is passed the ``result`` object after highlighting is +complete. Your plugin may make any changes it desires to the result object +and that will be the final return value of the initial call to ``highlight``. + +Note: This callback does not fire from highlighting resulting from auto-language detection. + +It returns nothing. + + after:highlightBlock({block, result}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From cad4aa76720eaa1e9527ecfc6c7875180d6f5a0c Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sat, 29 Feb 2020 11:46:21 -0500 Subject: [PATCH 6/7] update api docs --- docs/api.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api.rst b/docs/api.rst index 646b1d2f67..deb6f496c2 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -28,6 +28,7 @@ Returns an object with the following properties: * ``value``: HTML string with highlighting markup * ``top``: top of the current mode stack * ``illegal``: boolean representing whether any illegal matches were found +* ``code``: the original raw code ``highlightAuto(code, languageSubset)`` From 779790d8b9a951b6e9bf02d704ba006215301a01 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 1 Mar 2020 09:23:49 -0500 Subject: [PATCH 7/7] add readme --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 2dbc7b86fe..69622b4231 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,8 +9,10 @@ New themes: - none. -Core Changes: +Parser Engine Changes: +- add `before:highlight` plugin API callback (#2395) [Josh Goebel][] +- add `after:highlight` plugin API callback (#2395) [Josh Goebel][] - split out parse tree generation and HTML rendering concerns (#2404) [Josh Goebel][] - every language can have a `name` attribute now (#2400) [Josh Goebel][] - improve regular expression detect (less false-positives) (#2380) [Josh Goebel][]