summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Catanzaro <mcatanzaro@gnome.org>2021-01-06 12:53:43 -0600
committerMichael Catanzaro <mcatanzaro@gnome.org>2021-01-06 12:55:58 -0600
commit64c9f9ecf1fc2d457eade173cb97e4cd8c083676 (patch)
tree9f9c7b09e985430652f86436421ce6d1ab6a0051
parent43cc89b82d31c40c4685d546487c116108900441 (diff)
downloadepiphany-mcatanzaro/highlightjs-10.5.0.tar.gz
Update to highlight.js 10.5.0mcatanzaro/highlightjs-10.5.0
-rw-r--r--third-party/highlightjs/highlight.js807
1 files changed, 465 insertions, 342 deletions
diff --git a/third-party/highlightjs/highlight.js b/third-party/highlightjs/highlight.js
index bb7a3c759..de28408ef 100644
--- a/third-party/highlightjs/highlight.js
+++ b/third-party/highlightjs/highlight.js
@@ -1,5 +1,5 @@
/*
- Highlight.js 10.4.0 (4055826e)
+ Highlight.js 10.5.0 (af20048d)
License: BSD-3-Clause
Copyright (c) 2006-2020, Ivan Sagalaev
*/
@@ -86,155 +86,6 @@ var hljs = (function () {
return /** @type {T} */ (result);
}
- /* Stream merging */
-
- /**
- * @typedef Event
- * @property {'start'|'stop'} event
- * @property {number} offset
- * @property {Node} node
- */
-
- /**
- * @param {Node} node
- */
- function tag(node) {
- return node.nodeName.toLowerCase();
- }
-
- /**
- * @param {Node} node
- */
- function nodeStream(node) {
- /** @type Event[] */
- const result = [];
- (function _nodeStream(node, offset) {
- for (let child = node.firstChild; child; child = child.nextSibling) {
- if (child.nodeType === 3) {
- offset += child.nodeValue.length;
- } else if (child.nodeType === 1) {
- result.push({
- event: 'start',
- offset: offset,
- node: child
- });
- offset = _nodeStream(child, offset);
- // Prevent void elements from having an end tag that would actually
- // double them in the output. There are more void elements in HTML
- // but we list only those realistically expected in code display.
- if (!tag(child).match(/br|hr|img|input/)) {
- result.push({
- event: 'stop',
- offset: offset,
- node: child
- });
- }
- }
- }
- return offset;
- })(node, 0);
- return result;
- }
-
- /**
- * @param {any} original - the original stream
- * @param {any} highlighted - stream of the highlighted source
- * @param {string} value - the original source itself
- */
- function mergeStreams(original, highlighted, value) {
- let processed = 0;
- let result = '';
- const nodeStack = [];
-
- function selectStream() {
- if (!original.length || !highlighted.length) {
- return original.length ? original : highlighted;
- }
- if (original[0].offset !== highlighted[0].offset) {
- return (original[0].offset < highlighted[0].offset) ? original : highlighted;
- }
-
- /*
- To avoid starting the stream just before it should stop the order is
- ensured that original always starts first and closes last:
-
- if (event1 == 'start' && event2 == 'start')
- return original;
- if (event1 == 'start' && event2 == 'stop')
- return highlighted;
- if (event1 == 'stop' && event2 == 'start')
- return original;
- if (event1 == 'stop' && event2 == 'stop')
- return highlighted;
-
- ... which is collapsed to:
- */
- return highlighted[0].event === 'start' ? original : highlighted;
- }
-
- /**
- * @param {Node} node
- */
- function open(node) {
- /** @param {Attr} attr */
- function attributeString(attr) {
- return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
- }
- // @ts-ignore
- result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>';
- }
-
- /**
- * @param {Node} node
- */
- function close(node) {
- result += '</' + tag(node) + '>';
- }
-
- /**
- * @param {Event} event
- */
- function render(event) {
- (event.event === 'start' ? open : close)(event.node);
- }
-
- while (original.length || highlighted.length) {
- let stream = selectStream();
- result += escapeHTML(value.substring(processed, stream[0].offset));
- processed = stream[0].offset;
- if (stream === original) {
- /*
- On any opening or closing tag of the original markup we first close
- the entire highlighted node stack, then render the original tag along
- with all the following original tags at the same offset and then
- reopen all the tags on the highlighted stack.
- */
- nodeStack.reverse().forEach(close);
- do {
- render(stream.splice(0, 1)[0]);
- stream = selectStream();
- } while (stream === original && stream.length && stream[0].offset === processed);
- nodeStack.reverse().forEach(open);
- } else {
- if (stream[0].event === 'start') {
- nodeStack.push(stream[0].node);
- } else {
- nodeStack.pop();
- }
- render(stream.splice(0, 1)[0]);
- }
- }
- return result + escapeHTML(value.substr(processed));
- }
-
- var utils = /*#__PURE__*/Object.freeze({
- __proto__: null,
- escapeHTML: escapeHTML,
- inherit: inherit,
- nodeStream: nodeStream,
- mergeStreams: mergeStreams
- });
-
/**
* @typedef {object} Renderer
* @property {(text: string) => void} addText
@@ -513,6 +364,18 @@ var hljs = (function () {
}
/**
+ * Any of the passed expresssions may match
+ *
+ * Creates a huge this | this | that | that match
+ * @param {(RegExp | string)[] } args
+ * @returns {string}
+ */
+ function either(...args) {
+ const joined = '(' + args.map((x) => source(x)).join("|") + ")";
+ return joined;
+ }
+
+ /**
* @param {RegExp} re
* @returns {number}
*/
@@ -777,6 +640,88 @@ var hljs = (function () {
END_SAME_AS_BEGIN: END_SAME_AS_BEGIN
});
+ // Grammar extensions / plugins
+ // See: https://github.com/highlightjs/highlight.js/issues/2833
+
+ // Grammar extensions allow "syntactic sugar" to be added to the grammar modes
+ // without requiring any underlying changes to the compiler internals.
+
+ // `compileMatch` being the perfect small example of now allowing a grammar
+ // author to write `match` when they desire to match a single expression rather
+ // than being forced to use `begin`. The extension then just moves `match` into
+ // `begin` when it runs. Ie, no features have been added, but we've just made
+ // the experience of writing (and reading grammars) a little bit nicer.
+
+ // ------
+
+ // TODO: We need negative look-behind support to do this properly
+ /**
+ * Skip a match if it has a preceding dot
+ *
+ * This is used for `beginKeywords` to prevent matching expressions such as
+ * `bob.keyword.do()`. The mode compiler automatically wires this up as a
+ * special _internal_ 'on:begin' callback for modes with `beginKeywords`
+ * @param {RegExpMatchArray} match
+ * @param {CallbackResponse} response
+ */
+ function skipIfhasPrecedingDot(match, response) {
+ const before = match.input[match.index - 1];
+ if (before === ".") {
+ response.ignoreMatch();
+ }
+ }
+
+
+ /**
+ * `beginKeywords` syntactic sugar
+ * @type {CompilerExt}
+ */
+ function beginKeywords(mode, parent) {
+ if (!parent) return;
+ if (!mode.beginKeywords) return;
+
+ // for languages with keywords that include non-word characters checking for
+ // a word boundary is not sufficient, so instead we check for a word boundary
+ // or whitespace - this does no harm in any case since our keyword engine
+ // doesn't allow spaces in keywords anyways and we still check for the boundary
+ // first
+ mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\.)(?=\\b|\\s)';
+ mode.__beforeBegin = skipIfhasPrecedingDot;
+ mode.keywords = mode.keywords || mode.beginKeywords;
+ delete mode.beginKeywords;
+ }
+
+ /**
+ * Allow `illegal` to contain an array of illegal values
+ * @type {CompilerExt}
+ */
+ function compileIllegal(mode, _parent) {
+ if (!Array.isArray(mode.illegal)) return;
+
+ mode.illegal = either(...mode.illegal);
+ }
+
+ /**
+ * `match` to match a single expression for readability
+ * @type {CompilerExt}
+ */
+ function compileMatch(mode, _parent) {
+ if (!mode.match) return;
+ if (mode.begin || mode.end) throw new Error("begin & end are not supported with match");
+
+ mode.begin = mode.match;
+ delete mode.match;
+ }
+
+ /**
+ * provides the default 1 relevance to all modes
+ * @type {CompilerExt}
+ */
+ function compileRelevance(mode, _parent) {
+ // eslint-disable-next-line no-undefined
+ if (mode.relevance === undefined) mode.relevance = 1;
+ }
+
// keywords that should have no default relevance value
const COMMON_KEYWORDS = [
'of',
@@ -792,6 +737,72 @@ var hljs = (function () {
'value' // common variable name
];
+ /**
+ * Given raw keywords from a language definition, compile them.
+ *
+ * @param {string | Record<string,string>} rawKeywords
+ * @param {boolean} caseInsensitive
+ */
+ function compileKeywords(rawKeywords, caseInsensitive) {
+ /** @type KeywordDict */
+ const compiledKeywords = {};
+
+ if (typeof rawKeywords === 'string') { // string
+ splitAndCompile('keyword', rawKeywords);
+ } else {
+ Object.keys(rawKeywords).forEach(function(className) {
+ splitAndCompile(className, rawKeywords[className]);
+ });
+ }
+ return compiledKeywords;
+
+ // ---
+
+ /**
+ * Compiles an individual list of keywords
+ *
+ * Ex: "for if when while|5"
+ *
+ * @param {string} className
+ * @param {string} keywordList
+ */
+ function splitAndCompile(className, keywordList) {
+ if (caseInsensitive) {
+ keywordList = keywordList.toLowerCase();
+ }
+ keywordList.split(' ').forEach(function(keyword) {
+ const pair = keyword.split('|');
+ compiledKeywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])];
+ });
+ }
+ }
+
+ /**
+ * Returns the proper score for a given keyword
+ *
+ * Also takes into account comment keywords, which will be scored 0 UNLESS
+ * another score has been manually assigned.
+ * @param {string} keyword
+ * @param {string} [providedScore]
+ */
+ function scoreForKeyword(keyword, providedScore) {
+ // manual scores always win over common keywords
+ // so you can force a score of 1 if you really insist
+ if (providedScore) {
+ return Number(providedScore);
+ }
+
+ return commonKeyword(keyword) ? 0 : 1;
+ }
+
+ /**
+ * Determines if a given keyword is common or not
+ *
+ * @param {string} keyword */
+ function commonKeyword(keyword) {
+ return COMMON_KEYWORDS.includes(keyword.toLowerCase());
+ }
+
// compilation
/**
@@ -800,9 +811,10 @@ var hljs = (function () {
* Given the raw result of a language definition (Language), compiles this so
* that it is ready for highlighting code.
* @param {Language} language
+ * @param {{plugins: HLJSPlugin[]}} opts
* @returns {CompiledLanguage}
*/
- function compileLanguage(language) {
+ function compileLanguage(language, { plugins }) {
/**
* Builds a regex with the case sensativility of the current language
*
@@ -1013,8 +1025,8 @@ var hljs = (function () {
mode.contains.forEach(term => mm.addRule(term.begin, { rule: term, type: "begin" }));
- if (mode.terminator_end) {
- mm.addRule(mode.terminator_end, { type: "end" });
+ if (mode.terminatorEnd) {
+ mm.addRule(mode.terminatorEnd, { type: "end" });
}
if (mode.illegal) {
mm.addRule(mode.illegal, { type: "illegal" });
@@ -1023,23 +1035,6 @@ var hljs = (function () {
return mm;
}
- // TODO: We need negative look-behind support to do this properly
- /**
- * Skip a match if it has a preceding dot
- *
- * This is used for `beginKeywords` to prevent matching expressions such as
- * `bob.keyword.do()`. The mode compiler automatically wires this up as a
- * special _internal_ 'on:begin' callback for modes with `beginKeywords`
- * @param {RegExpMatchArray} match
- * @param {CallbackResponse} response
- */
- function skipIfhasPrecedingDot(match, response) {
- const before = match.input[match.index - 1];
- if (before === ".") {
- response.ignoreMatch();
- }
- }
-
/** skip vs abort vs ignore
*
* @skip - The mode is still entered and exited normally (and contains rules apply),
@@ -1082,12 +1077,28 @@ var hljs = (function () {
function compileMode(mode, parent) {
const cmode = /** @type CompiledMode */ (mode);
if (mode.compiled) return cmode;
- mode.compiled = true;
+
+ [
+ // do this early so compiler extensions generally don't have to worry about
+ // the distinction between match/begin
+ compileMatch
+ ].forEach(ext => ext(mode, parent));
+
+ language.compilerExtensions.forEach(ext => ext(mode, parent));
// __beforeBegin is considered private API, internal use only
mode.__beforeBegin = null;
- mode.keywords = mode.keywords || mode.beginKeywords;
+ [
+ beginKeywords,
+ // do this later so compiler extensions that come earlier have access to the
+ // raw array if they wanted to perhaps manipulate it, etc.
+ compileIllegal,
+ // default to 1 relevance if not specified
+ compileRelevance
+ ].forEach(ext => ext(mode, parent));
+
+ mode.compiled = true;
let keywordPattern = null;
if (typeof mode.keywords === "object") {
@@ -1106,31 +1117,21 @@ var hljs = (function () {
// `mode.lexemes` was the old standard before we added and now recommend
// using `keywords.$pattern` to pass the keyword pattern
- cmode.keywordPatternRe = langRe(mode.lexemes || keywordPattern || /\w+/, true);
+ keywordPattern = keywordPattern || mode.lexemes || /\w+/;
+ cmode.keywordPatternRe = langRe(keywordPattern, true);
if (parent) {
- if (mode.beginKeywords) {
- // for languages with keywords that include non-word characters checking for
- // a word boundary is not sufficient, so instead we check for a word boundary
- // or whitespace - this does no harm in any case since our keyword engine
- // doesn't allow spaces in keywords anyways and we still check for the boundary
- // first
- mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\.)(?=\\b|\\s)';
- mode.__beforeBegin = skipIfhasPrecedingDot;
- }
if (!mode.begin) mode.begin = /\B|\b/;
cmode.beginRe = langRe(mode.begin);
if (mode.endSameAsBegin) mode.end = mode.begin;
if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/;
if (mode.end) cmode.endRe = langRe(mode.end);
- cmode.terminator_end = source(mode.end) || '';
- if (mode.endsWithParent && parent.terminator_end) {
- cmode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
+ cmode.terminatorEnd = source(mode.end) || '';
+ if (mode.endsWithParent && parent.terminatorEnd) {
+ cmode.terminatorEnd += (mode.end ? '|' : '') + parent.terminatorEnd;
}
}
- if (mode.illegal) cmode.illegalRe = langRe(mode.illegal);
- // eslint-disable-next-line no-undefined
- if (mode.relevance === undefined) mode.relevance = 1;
+ if (mode.illegal) cmode.illegalRe = langRe(/** @type {RegExp | string} */ (mode.illegal));
if (!mode.contains) mode.contains = [];
mode.contains = [].concat(...mode.contains.map(function(c) {
@@ -1146,6 +1147,8 @@ var hljs = (function () {
return cmode;
}
+ if (!language.compilerExtensions) language.compilerExtensions = [];
+
// self is not valid at the top-level
if (language.contains && language.contains.includes('self')) {
throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");
@@ -1185,8 +1188,8 @@ var hljs = (function () {
* @returns {Mode | Mode[]}
* */
function expandOrCloneMode(mode) {
- if (mode.variants && !mode.cached_variants) {
- mode.cached_variants = mode.variants.map(function(variant) {
+ if (mode.variants && !mode.cachedVariants) {
+ mode.cachedVariants = mode.variants.map(function(variant) {
return inherit(mode, { variants: null }, variant);
});
}
@@ -1194,8 +1197,8 @@ var hljs = (function () {
// EXPAND
// if we have variants then essentially "replace" the mode with the variants
// this happens in compileMode, where this function is called from
- if (mode.cached_variants) {
- return mode.cached_variants;
+ if (mode.cachedVariants) {
+ return mode.cachedVariants;
}
// CLONE
@@ -1214,77 +1217,7 @@ var hljs = (function () {
return mode;
}
- /***********************************************
- Keywords
- ***********************************************/
-
- /**
- * Given raw keywords from a language definition, compile them.
- *
- * @param {string | Record<string,string>} rawKeywords
- * @param {boolean} caseInsensitive
- */
- function compileKeywords(rawKeywords, caseInsensitive) {
- /** @type KeywordDict */
- const compiledKeywords = {};
-
- if (typeof rawKeywords === 'string') { // string
- splitAndCompile('keyword', rawKeywords);
- } else {
- Object.keys(rawKeywords).forEach(function(className) {
- splitAndCompile(className, rawKeywords[className]);
- });
- }
- return compiledKeywords;
-
- // ---
-
- /**
- * Compiles an individual list of keywords
- *
- * Ex: "for if when while|5"
- *
- * @param {string} className
- * @param {string} keywordList
- */
- function splitAndCompile(className, keywordList) {
- if (caseInsensitive) {
- keywordList = keywordList.toLowerCase();
- }
- keywordList.split(' ').forEach(function(keyword) {
- const pair = keyword.split('|');
- compiledKeywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])];
- });
- }
- }
-
- /**
- * Returns the proper score for a given keyword
- *
- * Also takes into account comment keywords, which will be scored 0 UNLESS
- * another score has been manually assigned.
- * @param {string} keyword
- * @param {string} [providedScore]
- */
- function scoreForKeyword(keyword, providedScore) {
- // manual scores always win over common keywords
- // so you can force a score of 1 if you really insist
- if (providedScore) {
- return Number(providedScore);
- }
-
- return commonKeyword(keyword) ? 0 : 1;
- }
-
- /**
- * Determines if a given keyword is common or not
- *
- * @param {string} keyword */
- function commonKeyword(keyword) {
- return COMMON_KEYWORDS.includes(keyword.toLowerCase());
- }
-
- var version = "10.4.0";
+ var version = "10.5.0";
// @ts-nocheck
@@ -1304,7 +1237,7 @@ var hljs = (function () {
computed: {
className() {
if (this.unknownLanguage) return "";
-
+
return "hljs " + this.detectedLanguage;
},
highlighted() {
@@ -1314,8 +1247,8 @@ var hljs = (function () {
this.unknownLanguage = true;
return escapeHTML(this.code);
}
-
- let result;
+
+ let result = {};
if (this.autoDetect) {
result = hljs.highlightAuto(this.code);
this.detectedLanguage = result.language;
@@ -1338,12 +1271,13 @@ var hljs = (function () {
return createElement("pre", {}, [
createElement("code", {
class: this.className,
- domProps: { innerHTML: this.highlighted }})
+ domProps: { innerHTML: this.highlighted }
+ })
]);
}
// template: `<pre><code :class="className" v-html="highlighted"></code></pre>`
};
-
+
const VuePlugin = {
install(Vue) {
Vue.component('highlightjs', Component);
@@ -1353,6 +1287,191 @@ var hljs = (function () {
return { Component, VuePlugin };
}
+ /* plugin itself */
+
+ /** @type {HLJSPlugin} */
+ const mergeHTMLPlugin = {
+ "after:highlightBlock": ({ block, result, text }) => {
+ const originalStream = nodeStream(block);
+ if (!originalStream.length) return;
+
+ const resultNode = document.createElement('div');
+ resultNode.innerHTML = result.value;
+ result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
+ }
+ };
+
+ /* Stream merging support functions */
+
+ /**
+ * @typedef Event
+ * @property {'start'|'stop'} event
+ * @property {number} offset
+ * @property {Node} node
+ */
+
+ /**
+ * @param {Node} node
+ */
+ function tag(node) {
+ return node.nodeName.toLowerCase();
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function nodeStream(node) {
+ /** @type Event[] */
+ const result = [];
+ (function _nodeStream(node, offset) {
+ for (let child = node.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType === 3) {
+ offset += child.nodeValue.length;
+ } else if (child.nodeType === 1) {
+ result.push({
+ event: 'start',
+ offset: offset,
+ node: child
+ });
+ offset = _nodeStream(child, offset);
+ // Prevent void elements from having an end tag that would actually
+ // double them in the output. There are more void elements in HTML
+ // but we list only those realistically expected in code display.
+ if (!tag(child).match(/br|hr|img|input/)) {
+ result.push({
+ event: 'stop',
+ offset: offset,
+ node: child
+ });
+ }
+ }
+ }
+ return offset;
+ })(node, 0);
+ return result;
+ }
+
+ /**
+ * @param {any} original - the original stream
+ * @param {any} highlighted - stream of the highlighted source
+ * @param {string} value - the original source itself
+ */
+ function mergeStreams(original, highlighted, value) {
+ let processed = 0;
+ let result = '';
+ const nodeStack = [];
+
+ function selectStream() {
+ if (!original.length || !highlighted.length) {
+ return original.length ? original : highlighted;
+ }
+ if (original[0].offset !== highlighted[0].offset) {
+ return (original[0].offset < highlighted[0].offset) ? original : highlighted;
+ }
+
+ /*
+ To avoid starting the stream just before it should stop the order is
+ ensured that original always starts first and closes last:
+
+ if (event1 == 'start' && event2 == 'start')
+ return original;
+ if (event1 == 'start' && event2 == 'stop')
+ return highlighted;
+ if (event1 == 'stop' && event2 == 'start')
+ return original;
+ if (event1 == 'stop' && event2 == 'stop')
+ return highlighted;
+
+ ... which is collapsed to:
+ */
+ return highlighted[0].event === 'start' ? original : highlighted;
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function open(node) {
+ /** @param {Attr} attr */
+ function attributeString(attr) {
+ return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
+ }
+ // @ts-ignore
+ result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>';
+ }
+
+ /**
+ * @param {Node} node
+ */
+ function close(node) {
+ result += '</' + tag(node) + '>';
+ }
+
+ /**
+ * @param {Event} event
+ */
+ function render(event) {
+ (event.event === 'start' ? open : close)(event.node);
+ }
+
+ while (original.length || highlighted.length) {
+ let stream = selectStream();
+ result += escapeHTML(value.substring(processed, stream[0].offset));
+ processed = stream[0].offset;
+ if (stream === original) {
+ /*
+ On any opening or closing tag of the original markup we first close
+ the entire highlighted node stack, then render the original tag along
+ with all the following original tags at the same offset and then
+ reopen all the tags on the highlighted stack.
+ */
+ nodeStack.reverse().forEach(close);
+ do {
+ render(stream.splice(0, 1)[0]);
+ stream = selectStream();
+ } while (stream === original && stream.length && stream[0].offset === processed);
+ nodeStack.reverse().forEach(open);
+ } else {
+ if (stream[0].event === 'start') {
+ nodeStack.push(stream[0].node);
+ } else {
+ nodeStack.pop();
+ }
+ render(stream.splice(0, 1)[0]);
+ }
+ }
+ return result + escapeHTML(value.substr(processed));
+ }
+
+ /*
+
+ For the reasoning behind this please see:
+ https://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419
+
+ */
+
+ /**
+ * @param {string} message
+ */
+ const error = (message) => {
+ console.error(message);
+ };
+
+ /**
+ * @param {string} message
+ * @param {any} args
+ */
+ const warn = (message, ...args) => {
+ console.log(`WARN: ${message}`, ...args);
+ };
+
+ /**
+ * @param {string} version
+ * @param {string} message
+ */
+ const deprecated = (version, message) => {
+ console.log(`Deprecated as of ${version}. ${message}`);
+ };
+
/*
Syntax highlighting with language autodetection.
https://highlightjs.org/
@@ -1360,8 +1479,6 @@ var hljs = (function () {
const escape$1 = escapeHTML;
const inherit$1 = inherit;
-
- const { nodeStream: nodeStream$1, mergeStreams: mergeStreams$1 } = utils;
const NO_MATCH = Symbol("nomatch");
/**
@@ -1369,10 +1486,6 @@ var hljs = (function () {
* @returns {HLJSApi}
*/
const HLJS = function(hljs) {
- // Convenience variables for build-in objects
- /** @type {unknown[]} */
- const ArrayProto = [];
-
// Global internal variables used within the highlight.js library.
/** @type {Record<string, Language>} */
const languages = Object.create(null);
@@ -1427,8 +1540,8 @@ var hljs = (function () {
if (match) {
const language = getLanguage(match[1]);
if (!language) {
- console.warn(LANGUAGE_NOT_FOUND.replace("{}", match[1]));
- console.warn("Falling back to no-highlight mode for this block.", block);
+ warn(LANGUAGE_NOT_FOUND.replace("{}", match[1]));
+ warn("Falling back to no-highlight mode for this block.", block);
}
return language ? match[1] : 'no-highlight';
}
@@ -1455,7 +1568,7 @@ var hljs = (function () {
* @property {boolean} illegal - indicates whether any illegal matches were found
*/
function highlight(languageName, code, ignoreIllegals, continuation) {
- /** @type {{ code: string, language: string, result?: any }} */
+ /** @type {BeforeHighlightContext} */
const context = {
code,
language: languageName
@@ -1809,11 +1922,11 @@ var hljs = (function () {
const language = getLanguage(languageName);
if (!language) {
- console.error(LANGUAGE_NOT_FOUND.replace("{}", languageName));
+ error(LANGUAGE_NOT_FOUND.replace("{}", languageName));
throw new Error('Unknown language: "' + languageName + '"');
}
- const md = compileLanguage(language);
+ const md = compileLanguage(language, { plugins });
let result = '';
/** @type {CompiledMode} */
let top = continuation || md;
@@ -1992,24 +2105,42 @@ var hljs = (function () {
/**
* Builds new class name for block given the language name
*
- * @param {string} prevClassName
+ * @param {HTMLElement} element
* @param {string} [currentLang]
* @param {string} [resultLang]
*/
- function buildClassName(prevClassName, currentLang, resultLang) {
+ function updateClassName(element, currentLang, resultLang) {
const language = currentLang ? aliases[currentLang] : resultLang;
- const result = [prevClassName.trim()];
- if (!prevClassName.match(/\bhljs\b/)) {
- result.push('hljs');
- }
+ element.classList.add("hljs");
+ if (language) element.classList.add(language);
+ }
- if (!prevClassName.includes(language)) {
- result.push(language);
+ /** @type {HLJSPlugin} */
+ const brPlugin = {
+ "before:highlightBlock": ({ block }) => {
+ if (options.useBR) {
+ block.innerHTML = block.innerHTML.replace(/\n/g, '').replace(/<br[ /]*>/g, '\n');
+ }
+ },
+ "after:highlightBlock": ({ result }) => {
+ if (options.useBR) {
+ result.value = result.value.replace(/\n/g, "<br>");
+ }
}
+ };
- return result.join(' ').trim();
- }
+ const TAB_REPLACE_RE = /^(<[^>]+>|\t)+/gm;
+ /** @type {HLJSPlugin} */
+ const tabReplacePlugin = {
+ "after:highlightBlock": ({ result }) => {
+ if (options.tabReplace) {
+ result.value = result.value.replace(TAB_REPLACE_RE, (m) =>
+ m.replace(/\t/g, options.tabReplace)
+ );
+ }
+ }
+ };
/**
* Applies highlighting to a DOM node containing code. Accepts a DOM node and
@@ -2027,27 +2158,14 @@ var hljs = (function () {
fire("before:highlightBlock",
{ block: element, language: language });
- if (options.useBR) {
- node = document.createElement('div');
- node.innerHTML = element.innerHTML.replace(/\n/g, '').replace(/<br[ /]*>/g, '\n');
- } else {
- node = element;
- }
+ node = element;
const text = node.textContent;
const result = language ? highlight(language, text, true) : highlightAuto(text);
- const originalStream = nodeStream$1(node);
- if (originalStream.length) {
- const resultNode = document.createElement('div');
- resultNode.innerHTML = result.value;
- result.value = mergeStreams$1(originalStream, nodeStream$1(resultNode), text);
- }
- result.value = fixMarkup(result.value);
-
- fire("after:highlightBlock", { block: element, result: result });
+ fire("after:highlightBlock", { block: element, result, text });
element.innerHTML = result.value;
- element.className = buildClassName(element.className, language, result.language);
+ updateClassName(element, language, result.language);
element.result = {
language: result.language,
// TODO: remove with version 11.0
@@ -2071,8 +2189,8 @@ var hljs = (function () {
*/
function configure(userOptions) {
if (userOptions.useBR) {
- console.warn("'useBR' option is deprecated and will be removed entirely in v11.0");
- console.warn("Please see https://github.com/highlightjs/highlight.js/issues/2559");
+ deprecated("10.3.0", "'useBR' will be removed entirely in v11.0");
+ deprecated("10.3.0", "Please see https://github.com/highlightjs/highlight.js/issues/2559");
}
options = inherit$1(options, userOptions);
}
@@ -2087,7 +2205,7 @@ var hljs = (function () {
initHighlighting.called = true;
const blocks = document.querySelectorAll('pre code');
- ArrayProto.forEach.call(blocks, highlightBlock);
+ blocks.forEach(highlightBlock);
};
// Higlights all when DOMContentLoaded fires
@@ -2106,10 +2224,10 @@ var hljs = (function () {
let lang = null;
try {
lang = languageDefinition(hljs);
- } catch (error) {
- console.error("Language definition for '{}' could not be registered.".replace("{}", languageName));
+ } catch (error$1) {
+ error("Language definition for '{}' could not be registered.".replace("{}", languageName));
// hard or soft error
- if (!SAFE_MODE) { throw error; } else { console.error(error); }
+ if (!SAFE_MODE) { throw error$1; } else { error(error$1); }
// languages that have serious errors are replaced with essentially a
// "plaintext" stand-in so that the code blocks will still get normal
// css classes applied to them - and one bad language won't break the
@@ -2143,8 +2261,8 @@ var hljs = (function () {
@returns {Language | never}
*/
function requireLanguage(name) {
- console.warn("requireLanguage is deprecated and will be removed entirely in the future.");
- console.warn("Please see https://github.com/highlightjs/highlight.js/pull/2844");
+ deprecated("10.4.0", "requireLanguage will be removed entirely in v11.");
+ deprecated("10.4.0", "Please see https://github.com/highlightjs/highlight.js/pull/2844");
const lang = getLanguage(name);
if (lang) { return lang; }
@@ -2211,8 +2329,8 @@ var hljs = (function () {
@returns {string}
*/
function deprecateFixMarkup(arg) {
- console.warn("fixMarkup is deprecated and will be removed entirely in v11.0");
- console.warn("Please see https://github.com/highlightjs/highlight.js/issues/2534");
+ deprecated("10.2.0", "fixMarkup will be removed entirely in v11.0");
+ deprecated("10.2.0", "Please see https://github.com/highlightjs/highlight.js/issues/2534");
return fixMarkup(arg);
}
@@ -2253,6 +2371,10 @@ var hljs = (function () {
// merge all the modes/regexs into our main object
Object.assign(hljs, MODES);
+ // built-in plugins, likely to be moved out of core in the future
+ hljs.addPlugin(brPlugin); // slated to be removed in v11
+ hljs.addPlugin(mergeHTMLPlugin);
+ hljs.addPlugin(tabReplacePlugin);
return hljs;
};
@@ -2715,7 +2837,7 @@ hljs.registerLanguage('javascript', function () {
]
};
const JSDOC_COMMENT = hljs.COMMENT(
- '/\\*\\*',
+ /\/\*\*(?!\/)/,
'\\*/',
{
relevance: 0,
@@ -2862,8 +2984,8 @@ hljs.registerLanguage('javascript', function () {
'[^()]*(\\(' +
'[^()]*(\\(' +
'[^()]*' +
- '\\))*[^()]*' +
- '\\))*[^()]*' +
+ '\\)[^()]*)*' +
+ '\\)[^()]*)*' +
'\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>',
returnBegin: true,
end: '\\s*=>',
@@ -2953,8 +3075,8 @@ hljs.registerLanguage('javascript', function () {
'[^()]*(\\(' +
'[^()]*(\\(' +
'[^()]*' +
- '\\))*[^()]*' +
- '\\))*[^()]*' +
+ '\\)[^()]*)*' +
+ '\\)[^()]*)*' +
'\\)\\s*\\{', // end parens
returnBegin:true,
contains: [
@@ -3076,30 +3198,31 @@ hljs.registerLanguage('xml', function () {
Language: HTML, XML
Website: https://www.w3.org/XML/
Category: common
+ Audit: 2020
*/
/** @type LanguageFn */
function xml(hljs) {
// Element names can contain letters, digits, hyphens, underscores, and periods
const TAG_NAME_RE = concat(/[A-Z_]/, optional(/[A-Z0-9_.-]+:/), /[A-Z0-9_.-]*/);
- const XML_IDENT_RE = '[A-Za-z0-9\\._:-]+';
+ const XML_IDENT_RE = /[A-Za-z0-9._:-]+/;
const XML_ENTITIES = {
className: 'symbol',
- begin: '&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;'
+ begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/
};
const XML_META_KEYWORDS = {
- begin: '\\s',
+ begin: /\s/,
contains: [
{
className: 'meta-keyword',
- begin: '#?[a-z_][a-z1-9_-]+',
- illegal: '\\n'
+ begin: /#?[a-z_][a-z1-9_-]+/,
+ illegal: /\n/
}
]
};
const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, {
- begin: '\\(',
- end: '\\)'
+ begin: /\(/,
+ end: /\)/
});
const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, {
className: 'meta-string'
@@ -3162,8 +3285,8 @@ hljs.registerLanguage('xml', function () {
contains: [
{
className: 'meta',
- begin: '<![a-z]',
- end: '>',
+ begin: /<![a-z]/,
+ end: />/,
relevance: 10,
contains: [
XML_META_KEYWORDS,
@@ -3171,13 +3294,13 @@ hljs.registerLanguage('xml', function () {
APOS_META_STRING_MODE,
XML_META_PAR_KEYWORDS,
{
- begin: '\\[',
- end: '\\]',
+ begin: /\[/,
+ end: /\]/,
contains: [
{
className: 'meta',
- begin: '<![a-z]',
- end: '>',
+ begin: /<![a-z]/,
+ end: />/,
contains: [
XML_META_KEYWORDS,
XML_META_PAR_KEYWORDS,
@@ -3190,15 +3313,15 @@ hljs.registerLanguage('xml', function () {
]
},
hljs.COMMENT(
- '<!--',
- '-->',
+ /<!--/,
+ /-->/,
{
relevance: 10
}
),
{
- begin: '<!\\[CDATA\\[',
- end: '\\]\\]>',
+ begin: /<!\[CDATA\[/,
+ end: /\]\]>/,
relevance: 10
},
XML_ENTITIES,
@@ -3216,14 +3339,14 @@ hljs.registerLanguage('xml', function () {
ending braket. The '$' is needed for the lexeme to be recognized
by hljs.subMode() that tests lexemes outside the stream.
*/
- begin: '<style(?=\\s|>)',
- end: '>',
+ begin: /<style(?=\s|>)/,
+ end: />/,
keywords: {
name: 'style'
},
contains: [ TAG_INTERNALS ],
starts: {
- end: '</style>',
+ end: /<\/style>/,
returnEnd: true,
subLanguage: [
'css',
@@ -3234,8 +3357,8 @@ hljs.registerLanguage('xml', function () {
{
className: 'tag',
// See the comment in the <style tag about the lookahead pattern
- begin: '<script(?=\\s|>)',
- end: '>',
+ begin: /<script(?=\s|>)/,
+ end: />/,
keywords: {
name: 'script'
},