diff --git a/VERSION_11_UPGRADE.md b/VERSION_11_UPGRADE.md index da70df2724..f97d20a454 100644 --- a/VERSION_11_UPGRADE.md +++ b/VERSION_11_UPGRADE.md @@ -2,6 +2,10 @@ - remove apache, http, nginx, properties, coffeescript from :common #2848 +### Feature Removal + +- HTML merging is now no longer included in core. You'll want to use a plugin instead. https://github.com/highlightjs/highlight.js/issues/2889 +- fixMarkup is gone now, provide your own replacement #2534 ### Behavior changes @@ -26,7 +30,6 @@ ## API's changed - rename second_best to secondBest (highlightAuto) -- fixMarkup is gone now, provide your own replacement #2534 - highlightElement/highlightBlock result now no longer returns `re` key, use `relevance` instead - `CSS_NUMBER_MODE` has now been moved into the internal `css-shared` library diff --git a/src/highlight.js b/src/highlight.js index 1fe2cc509f..a5b0cea49f 100644 --- a/src/highlight.js +++ b/src/highlight.js @@ -13,7 +13,6 @@ import * as MODES from './lib/modes.js'; import { compileLanguage } from './lib/mode_compiler.js'; import * as packageJSON from '../package.json'; import { BuildVuePlugin } from "./plugins/vue.js"; -import { mergeHTMLPlugin } from "./plugins/merge_html.js"; import * as logger from "./lib/logger.js"; const escape = utils.escapeHTML; @@ -918,8 +917,6 @@ const HLJS = function(hljs) { // 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(mergeHTMLPlugin); return hljs; }; diff --git a/src/plugins/merge_html.js b/src/plugins/merge_html.js deleted file mode 100644 index a081df649f..0000000000 --- a/src/plugins/merge_html.js +++ /dev/null @@ -1,156 +0,0 @@ -import { escapeHTML } from "../lib/utils.js"; - -/* plugin itself */ - -/** @type {HLJSPlugin} */ -export const mergeHTMLPlugin = { - "after:highlightElement": ({ el, result, text }) => { - const originalStream = nodeStream(el); - 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 - */ -export 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 - */ -export 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 += ''; - } - - /** - * @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)); -} diff --git a/test/fixtures/expect/brInPre.txt b/test/fixtures/expect/brInPre.txt deleted file mode 100644 index e065147ab3..0000000000 --- a/test/fixtures/expect/brInPre.txt +++ /dev/null @@ -1 +0,0 @@ ->> '\x41\x42\x43'
'ABC'


>> '\x61\x62\x63'
'abc' diff --git a/test/fixtures/expect/custommarkup.txt b/test/fixtures/expect/custommarkup.txt deleted file mode 100644 index f0304c200c..0000000000 --- a/test/fixtures/expect/custommarkup.txt +++ /dev/null @@ -1,3 +0,0 @@ -<div id="contents"> - <p>Hello, World!Goodbye, cruel world! -</div> diff --git a/test/fixtures/index.html b/test/fixtures/index.html index 532f83db22..0ca5640bbb 100644 --- a/test/fixtures/index.html +++ b/test/fixtures/index.html @@ -31,20 +31,6 @@ -
- - -
<div id="contents">
-  <p>Hello, World!Goodbye, cruel world!
-</div>
-
- - -
>> '\x41\x42\x43'
'ABC'


>> '\x61\x62\x63'
'abc' -
- -
-
diff --git a/test/special/customMarkup.js b/test/special/customMarkup.js deleted file mode 100644 index 1e8f1db627..0000000000 --- a/test/special/customMarkup.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -const utility = require('../utility'); - -describe('custom markup', () => { - before(() => { - const testHTML = document.querySelectorAll('#custom-markup .hljs'); - - this.blocks = [...testHTML].map(x => x.innerHTML); - }); - - it('should keep custom markup', () => { - const filename = utility.buildPath('fixtures', 'expect', - 'custommarkup.txt'), - actual = this.blocks[0]; - - return utility.expectedFile(filename, 'utf-8', actual); - }); - - it('should keep the same amount of void elements (
,
, ...)', () => { - const filename = utility.buildPath('fixtures', 'expect', 'brInPre.txt'), - actual = this.blocks[1]; - - return utility.expectedFile(filename, 'utf-8', actual); - }); -}); diff --git a/test/special/index.js b/test/special/index.js index c00ce60400..9fd2881c33 100644 --- a/test/special/index.js +++ b/test/special/index.js @@ -32,7 +32,6 @@ describe('special cases tests', () => { }); require('./explicitLanguage'); - require('./customMarkup'); require('./languageAlias'); require('./noHighlight'); require('./subLanguages');