diff --git a/CHANGES.md b/CHANGES.md index a6abc3b73b..46b94d82b0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -35,6 +35,11 @@ Language grammar improvements: Deprecations: +- `highlight(languageName, code, ignoreIllegals, continuation)` deprecated as of 10.7 + - Please use the newer API which takes `code` and then accepts options as an object + - IE: `highlight(code, {language, ignoreIllegals})` + - `continuation` is for internal use only and no longer supported + - `highlightBlock(el)` deprecated as of 10.7. - Please use `highlightElement(el)` instead. - Plugin callbacks renamed `before/after:highlightBlock` => `before/after:highlightElement` diff --git a/README.md b/README.md index d76bcfbf76..7eeadceb36 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ const hljs = require('highlight.js/lib/core'); // require only the core library // separately require languages hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml')); -const highlightedCode = hljs.highlight('xml', 'Hello World!').value +const highlightedCode = hljs.highlight('Hello World!', {language: 'xml'}).value ``` diff --git a/docs/api.rst b/docs/api.rst index 3c2936b0d4..c3f28cb13e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -4,15 +4,12 @@ Library API Highlight.js exports a few functions as methods of the ``hljs`` object. -``highlight(languageName, code, ignore_illegals, continuation)`` ----------------------------------------------------------------- +``highlight(languageName, code, ignoreIllegals, continuation)`` (deprecated with 10.7) +-------------------------------------------------------------------------------------- -Core highlighting function. -Accepts a language name, or an alias, and a string with the code to highlight. -The ``ignore_illegals`` parameter, when present and evaluates to a true value, -forces highlighting to finish even in case of detecting illegal syntax for the -language instead of throwing an exception. -The ``continuation`` is an optional mode stack representing unfinished parsing. +**This is the old API, please see the new API immediately below.** + +``continuation`` is an optional mode stack representing unfinished parsing. When present, the function will restart parsing from this state instead of initializing a new one. This is used internally for `sublanguage` support. @@ -21,6 +18,18 @@ because there is no requirement that a grammar handle linebreaks in any special way. It's quite possible for a grammar to have a single mode/regex that matches MANY lines at once. This is not discouraged and entirely up to the grammar. + + +``highlight(code, {language, ignoreIllegals})`` +-------------------------------------------------------------------------------------- + +Core highlighting function. Accepts the code to highlight (string) and a list of options (object). +The ``language`` parameter must be present and specify the language name or alias +of the grammar to be used for highlighting. +The ``ignoreIllegals`` is an optional parameter than when true forces highlighting +to finish even in case of detecting illegal syntax for the +language instead of throwing an exception. + Returns an object with the following properties: * ``language``: language name, same as the name passed in ``languageName``, returned for consistency with ``highlightAuto`` diff --git a/src/highlight.js b/src/highlight.js index 1073166a83..c1544b7ac8 100644 --- a/src/highlight.js +++ b/src/highlight.js @@ -93,8 +93,14 @@ const HLJS = function(hljs) { /** * Core highlighting function. * - * @param {string} languageName - the language to use for highlighting - * @param {string} code - the code to highlight + * OLD API + * highlight(lang, code, ignoreIllegals, continuation) + * + * NEW API + * highlight(code, {lang, ignoreIllegals}) + * + * @param {string} codeOrlanguageName - the language to use for highlighting + * @param {string | HighlightOptions} optionsOrCode - the code to highlight * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail * @param {CompiledMode} [continuation] - current continuation mode, if any * @@ -106,7 +112,24 @@ const HLJS = function(hljs) { * @property {CompiledMode} top - top of the current mode stack * @property {boolean} illegal - indicates whether any illegal matches were found */ - function highlight(languageName, code, ignoreIllegals, continuation) { + function highlight(codeOrlanguageName, optionsOrCode, ignoreIllegals, continuation) { + let code = ""; + let languageName = ""; + if (typeof optionsOrCode === "object") { + code = codeOrlanguageName; + ignoreIllegals = optionsOrCode.ignoreIllegals; + languageName = optionsOrCode.language; + // continuation not supported at all via the new API + // eslint-disable-next-line no-undefined + continuation = undefined; + } else { + // old API + logger.deprecated("10.7.0", "highlight(lang, code, ...args) has been deprecated."); + logger.deprecated("10.7.0", "Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"); + languageName = codeOrlanguageName; + code = optionsOrCode; + } + /** @type {BeforeHighlightContext} */ const context = { code, @@ -133,14 +156,12 @@ const HLJS = function(hljs) { * private highlight that's used internally and does not fire callbacks * * @param {string} languageName - the language to use for highlighting - * @param {string} code - the code to highlight - * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail - * @param {CompiledMode} [continuation] - current continuation mode, if any + * @param {string} codeToHighlight - the code to highlight + * @param {boolean?} [ignoreIllegals] - whether to ignore illegal matches, default is to bail + * @param {CompiledMode?} [continuation] - current continuation mode, if any * @returns {HighlightResult} - result of the highlight operation */ - function _highlight(languageName, code, ignoreIllegals, continuation) { - const codeToHighlight = code; - + function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) { /** * Return keyword data if a match is a keyword * @param {CompiledMode} mode - current mode @@ -708,7 +729,7 @@ const HLJS = function(hljs) { node = element; const text = node.textContent; - const result = language ? highlight(language, text, true) : highlightAuto(text); + const result = language ? highlight(text, { language, ignoreIllegals: true }) : highlightAuto(text); // support for v10 API fire("after:highlightElement", { el: element, result, text }); diff --git a/test/api/beginKeywords.js b/test/api/beginKeywords.js index 53bf85b796..407f830db6 100644 --- a/test/api/beginKeywords.js +++ b/test/api/beginKeywords.js @@ -27,25 +27,25 @@ describe('beginKeywords', () => { it("should allow subsequence matches to still succeed", () => { let content = "A.class = self"; - let res = hljs.highlight("has-followup", content); + let res = hljs.highlight(content, {language: "has-followup"}); res.value.should.equal('A.class = self'); }); it("should ignore a preceeding .", () => { let content = "A.class = self"; - let res = hljs.highlight("test", content); + let res = hljs.highlight(content, { language: "test" }); res.value.should.equal('A.class = self'); }); it("should ignore a trailing .", () => { let content = "class.config = true"; - let res = hljs.highlight("test", content); + let res = hljs.highlight(content, { language: "test" }); res.value.should.equal('class.config = true'); }); it('should detect keywords', () => { let content = "I have a class yes I do."; - let res = hljs.highlight("test", content); + let res = hljs.highlight(content, { language: "test" }); res.value.should.equal('I have a class yes I do.'); }); }); diff --git a/test/api/highlight.js b/test/api/highlight.js index 962c36e175..043dc499ad 100644 --- a/test/api/highlight.js +++ b/test/api/highlight.js @@ -4,9 +4,47 @@ const hljs = require('../../build'); const should = require('should'); describe('.highlight()', () => { + it('should support ignoreIllegals (old API)', () => { + let code = "float # float"; + let result = hljs.highlight("java", code, true); + result.value.should.equal(`float # float`); + + code = "float # float"; + result = hljs.highlight("java", code, false); + result.value.should.equal("float # float"); + result.illegal.should.equal(true); + }); + it('should support ignoreIllegals (new API)', () => { + let code = "float # float"; + let result = hljs.highlight(code, { language: "java", ignoreIllegals: true }); + result.value.should.equal(`float # float`); + + code = "float # float"; + result = hljs.highlight(code, { language: "java", ignoreIllegals: false }); + result.value.should.equal("float # float"); + result.illegal.should.equal(true); + + // defaults to false + code = "float # float"; + result = hljs.highlight(code, { language: "java" }); + result.value.should.equal("float # float"); + result.illegal.should.equal(true); + }); + it('should use new API with options', () => { + const code = "public void moveTo(int x, int y, int z);"; + const result = hljs.highlight(code, { language: "java" }); + + result.value.should.equal( + 'public ' + + 'void moveTo' + + '(int x, ' + + 'int y, ' + + 'int z);' + ); + }); it('should works without continuation', () => { - const code = "public void moveTo(int x, int y, int z);"; - const result = hljs.highlight('java', code, false, false); + const code = "public void moveTo(int x, int y, int z);"; + const result = hljs.highlight(code, { language: 'java' }); result.value.should.equal( 'public ' + diff --git a/test/api/keywords.js b/test/api/keywords.js index f72a212c08..de1c466a3b 100644 --- a/test/api/keywords.js +++ b/test/api/keywords.js @@ -13,7 +13,7 @@ describe('computing the relevance score of a language', () => { } const code = "farmer and of river weeds"; hljs.registerLanguage("test", grammar) - const result = hljs.highlight('test', code, false, false); + const result = hljs.highlight(code, { language: 'test' }); result.relevance.should.equal(3) }); @@ -27,7 +27,7 @@ describe('computing the relevance score of a language', () => { } const code = "farmer and of river weeds"; hljs.registerLanguage("test", grammar) - const result = hljs.highlight('test', code, false, false); + const result = hljs.highlight(code, { language: 'test' }); result.relevance.should.equal(13) }); @@ -41,7 +41,7 @@ describe('computing the relevance score of a language', () => { } const code = "farmer and of river weeds"; hljs.registerLanguage("test", grammar) - const result = hljs.highlight('test', code, false, false); + const result = hljs.highlight(code, { language: 'test' }); result.relevance.should.equal(4) }); diff --git a/test/browser/plain.js b/test/browser/plain.js index 0416e94d3f..ca6ef222fb 100644 --- a/test/browser/plain.js +++ b/test/browser/plain.js @@ -16,7 +16,7 @@ describe('plain browser', function() { it('should return relevance key', async function() { await buildFakeDOM.bind(this, defaultCase)(); - var out = this.hljs.highlight("javascript",""); + var out = this.hljs.highlight("", { language: "javascript" }); out.relevance.should.equal(0); }); diff --git a/test/browser/worker.js b/test/browser/worker.js index c1e99ca64e..af8dcc2f0d 100644 --- a/test/browser/worker.js +++ b/test/browser/worker.js @@ -13,7 +13,7 @@ describe('web worker', function() { importScripts(event.data.script); postMessage(1); } else { - var result = hljs.highlight('javascript', event.data); + var result = hljs.highlight(event.data, { language: 'javascript' }); postMessage(result.value); } }; diff --git a/test/detect/index.js b/test/detect/index.js index b51adb10b3..cc317e2e9f 100644 --- a/test/detect/index.js +++ b/test/detect/index.js @@ -56,6 +56,6 @@ describe('hljs.highlightAuto()', () => { it("compiling the grammars", async function() { const languages = hljs.listLanguages(); - languages.forEach(l => hljs.highlight(l, "")) + languages.forEach(lang => hljs.highlight("", { language: lang} )) }); // this is also required for the dynamic test generation above to work }); diff --git a/test/markup/index.js b/test/markup/index.js index 28138ffa6b..f653976082 100644 --- a/test/markup/index.js +++ b/test/markup/index.js @@ -27,7 +27,7 @@ function testLanguage(language, {testDir}) { const expectedFile = fs.readFile(filename, 'utf-8'); Promise.all([sourceFile, expectedFile]).then(function([source, expected]) { - const actual = hljs.highlight(language, source).value; + const actual = hljs.highlight(source, { language }).value; // Uncomment this for major changes that rewrite the test expectations // which will then need to be manually compared by hand of course diff --git a/test/parser/compiler-extensions.js b/test/parser/compiler-extensions.js index 90b1d0f37e..7ba26e0199 100644 --- a/test/parser/compiler-extensions.js +++ b/test/parser/compiler-extensions.js @@ -28,7 +28,7 @@ describe.skip("compiler extension plugins", function() { hljs.addPlugin(plugin); // stub highlight to make sure the language gets compiled // since we have no API point to make that happen - hljs.highlight("extension_test", ""); + hljs.highlight("", { language: "extension_test" }); const [first, second] = hljs.getLanguage("extension_test").contains; this.first = first; this.second = second; diff --git a/test/parser/look-ahead-end-matchers.js b/test/parser/look-ahead-end-matchers.js index 241e1fd0f6..5d96e1d8ef 100644 --- a/test/parser/look-ahead-end-matchers.js +++ b/test/parser/look-ahead-end-matchers.js @@ -20,7 +20,7 @@ describe("parser specifics", function () { }; }); - hljs.highlight('test-language', 'ABC123 is the secret. XYZ123. End of string: ABC123').value + hljs.highlight('ABC123 is the secret. XYZ123. End of string: ABC123', {language: 'test-language'}).value .should.equal( // one full match at beginning, other match begins with XYZ but then never terminates, // so the end of the parsing finally closes the span tag diff --git a/test/parser/resume-scan.js b/test/parser/resume-scan.js index fb50a47f37..2e4d6c208a 100644 --- a/test/parser/resume-scan.js +++ b/test/parser/resume-scan.js @@ -6,7 +6,7 @@ hljs.debugMode(); // tests run in debug mode so errors are raised describe("bugs", function() { describe("resume scan when a match is ignored", () => { it("should continue to highlight later matches", () => { - const result = hljs.highlight('java', 'ImmutablePair.of(Stuff.class, "bar")'); + const result = hljs.highlight('ImmutablePair.of(Stuff.class, "bar")', {language: 'java'}); result.value.should.equal( 'ImmutablePair.of(Stuff.class, "bar")' ); @@ -16,7 +16,7 @@ describe("bugs", function() { // rule we really only want to skip searching for THAT rule at that same location, we // do not want to stop searching for ALL the prior rules at that location... it("BUT should not skip ahead too far", () => { - const result = hljs.highlight('java', 'ImmutablePair.of(Stuff.class, "bar");\n23'); + const result = hljs.highlight('ImmutablePair.of(Stuff.class, "bar");\n23', {language: 'java'}); result.value.should.equal( 'ImmutablePair.of(Stuff.class, "bar");\n' + '23' diff --git a/test/parser/reuse-endsWithParent.js b/test/parser/reuse-endsWithParent.js index 7a4d2fe9dc..d8b8f8e621 100644 --- a/test/parser/reuse-endsWithParent.js +++ b/test/parser/reuse-endsWithParent.js @@ -16,7 +16,7 @@ describe("bugs", function () { }; }); - hljs.highlight('test-language', '(abc 123) [abc 123] (abc 123)').value + hljs.highlight('(abc 123) [abc 123] (abc 123)', {language: 'test-language'}).value .should.equal( '(abc 123) ' + '[abc 123] ' + diff --git a/test/parser/should-not-destroyData.js b/test/parser/should-not-destroyData.js index e12a571ed0..d5688112ee 100644 --- a/test/parser/should-not-destroyData.js +++ b/test/parser/should-not-destroyData.js @@ -19,19 +19,19 @@ describe("parser/should not destroy data", function () { }; }); - hljs.highlight('test-language', 'The number is 123_longint yes.').value + hljs.highlight('The number is 123_longint yes.', {language: 'test-language' }).value .should.equal( - // The whole number isn't highlighted (the rule doesn't handle the _type) - // But the key thing is the "1" is registered as a match for the rule - // instead of disappearing from the output completely, which is what - // would happen previously. - 'The number is 123_longint yes.' - // Incorrect prior output: - // 'The number is 23_longint yes.' - ) + // The whole number isn't highlighted (the rule doesn't handle the _type) + // But the key thing is the "1" is registered as a match for the rule + // instead of disappearing from the output completely, which is what + // would happen previously. + 'The number is 123_longint yes.' + // Incorrect prior output: + // 'The number is 23_longint yes.' + ); hljs.debugMode(); should(() => { - hljs.highlight('test-language', 'The number is 123_longint yes.').value + hljs.highlight('The number is 123_longint yes.', {language: 'test-language'}).value }).throw(Error, { message: "0 width match regex", languageName: "test-language"}) diff --git a/test/regex/index.js b/test/regex/index.js index 426568ba4f..29c29f0d22 100644 --- a/test/regex/index.js +++ b/test/regex/index.js @@ -25,7 +25,7 @@ const polyBacktrackingCache = {}; function retrieveRules(language, { name }) { // first we need to get the language compiled so we have // access to the raw regex - hljs.highlight(name, ""); + hljs.highlight("", {language: name}); return regexFor(language, { context: name, depth: 0 }); } diff --git a/types/index.d.ts b/types/index.d.ts index 6e948954dc..89208c0131 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -112,6 +112,11 @@ interface EmitterConstructor { new (opts: any): Emitter } +interface HighlightOptions { + language: string + ignoreIllegals?: boolean +} + interface HLJSOptions { noHighlightRe: RegExp languageDetectRe: RegExp