diff --git a/.eslintrc.js b/.eslintrc.js index 930b631331..e531d00a73 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,6 +17,10 @@ module.exports = { "ecmaVersion": 2018, "sourceType": "module" }, + "parser": '@typescript-eslint/parser', + "plugins": [ + "@typescript-eslint" + ], "rules": { "array-callback-return": "error", "block-scoped-var": "error", @@ -27,7 +31,7 @@ module.exports = { // for now ignore diff between types of quoting "quotes": "off", // this is the style we are already using - "operator-linebreak": ["error","after", { "overrides": { "?": "after", ":": "after" } }], + "operator-linebreak": ["error","before", { "overrides": { "?": "after", ":": "after", "+": "after" } }], // sometimes we declare variables with extra spacing "indent": ["error", 2, {"VariableDeclarator":2}], // seems like a good idea not to use explicit undefined diff --git a/CHANGES.md b/CHANGES.md index 03e90acb8e..684bea0c5d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,8 @@ Parser Engine: Deprecations: +- when using `highlightBlock` `result.re` deprecated. Use `result.relevance` instead. (#2552) [Josh Goebel][] +- ditto for `result.second_best.re` => `result.second_best.relevance` (#2552) - `lexemes` is now deprecated in favor of `keywords.$pattern` key (#2519) [Josh Goebel][] - `endSameAsBegin` is now deprecated. (#2261) [Josh Goebel][] @@ -34,6 +36,7 @@ Language Improvements: - fix(yaml) Fix tags to include non-word characters (#2486) [Peter Plantinga][] - fix(swift) `@objcMembers` was being partially highlighted (#2543) [Nick Randall][] - enh(dart) Add `late` and `required` keywords, and `Never` built-in type (#2550) [Sam Rawlins][] +- enh(erlang) Add underscore separators to numeric literals (#2554) [Sergey Prokhorov][] [Josh Goebel]: https://github.com/yyyc514 [Peter Plantinga]: https://github.com/pplantinga @@ -42,6 +45,7 @@ Language Improvements: [Hankun Lin]: https://github.com/Linhk1606 [Nick Randall]: https://github.com/nicked [Sam Rawlins]: https://github.com/srawlins +[Sergey Prokhorov]: https://github.com/seriyps ## Version 10.0.2 diff --git a/SUPPORTED_LANGUAGES.md b/SUPPORTED_LANGUAGES.md index ad4bbdc950..5d3d3ea152 100644 --- a/SUPPORTED_LANGUAGES.md +++ b/SUPPORTED_LANGUAGES.md @@ -92,6 +92,7 @@ Languages that listed a **Package** below are 3rd party languages and are not bu | JSON | json | | | Java | java, jsp | | | JavaScript | javascript, js, jsx | | +| Jolie | jolie, iol, ol | [highlightjs-jolie](https://github.com/xiroV/highlightjs-jolie) | | Kotlin | kotlin, kt | | | LaTeX | tex | | | Leaf | leaf | | diff --git a/docs/api.rst b/docs/api.rst index 41f7014ad6..8b5ee44540 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -101,13 +101,13 @@ Applies highlighting to all ``
...
`` blocks on a page.
Attaches highlighting to the page load event.
-``registerLanguage(name, language)``
+``registerLanguage(languageName, languageDefinition)``
------------------------------------
Adds new language to the library under the specified name. Used mostly internally.
-* ``name``: a string with the name of the language being registered
-* ``language``: a function that returns an object which represents the
+* ``languageName``: a string with the name of the language being registered
+* ``languageDefinition``: a function that returns an object which represents the
language definition. The function is passed the ``hljs`` object to be able
to use common regular expressions defined within it.
diff --git a/package-lock.json b/package-lock.json
index 09fe06c654..318acfc140 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -62,6 +62,12 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
+ "@types/eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
+ "dev": true
+ },
"@types/events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@@ -79,6 +85,12 @@
"@types/node": "*"
}
},
+ "@types/json-schema": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
+ "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==",
+ "dev": true
+ },
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -91,6 +103,98 @@
"integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==",
"dev": true
},
+ "@typescript-eslint/eslint-plugin": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.32.0.tgz",
+ "integrity": "sha512-nb1kSUa8cd22hGgxpGdVT6/iyP7IKyrnyZEGYo+tN8iyDdXvXa+nfsX03tJVeFfhbkwR/0CDk910zPbqSflAsg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/experimental-utils": "2.32.0",
+ "functional-red-black-tree": "^1.0.1",
+ "regexpp": "^3.0.0",
+ "tsutils": "^3.17.1"
+ },
+ "dependencies": {
+ "regexpp": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+ "dev": true
+ }
+ }
+ },
+ "@typescript-eslint/experimental-utils": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.32.0.tgz",
+ "integrity": "sha512-oDWuB2q5AXsQ/mLq2N4qtWiBASWXPf7KhqXgeGH4QsyVKx+km8F6Vfqd3bspJQyhyCqxcbLO/jKJuIV3DzHZ6A==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.3",
+ "@typescript-eslint/typescript-estree": "2.32.0",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^2.0.0"
+ },
+ "dependencies": {
+ "eslint-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
+ "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.32.0.tgz",
+ "integrity": "sha512-swRtH835fUfm2khchiOVNchU3gVNaZNj2pY92QSx4kXan+RzaGNrwIRaCyX8uqzmK0xNPzseaUYHP8CsmrsjFw==",
+ "dev": true,
+ "requires": {
+ "@types/eslint-visitor-keys": "^1.0.0",
+ "@typescript-eslint/experimental-utils": "2.32.0",
+ "@typescript-eslint/typescript-estree": "2.32.0",
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.32.0.tgz",
+ "integrity": "sha512-hQpbWM/Y2iq6jB9FHYJBqa3h1R9IEGodOtajhb261cVHt9cz30AKjXM6WP7LxJdEPPlyJ9rPTZVgBUgZgiyPgw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "eslint-visitor-keys": "^1.1.0",
+ "glob": "^7.1.6",
+ "is-glob": "^4.0.1",
+ "lodash": "^4.17.15",
+ "semver": "^7.3.2",
+ "tsutils": "^3.17.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "semver": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+ "dev": true
+ }
+ }
+ },
"abab": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
@@ -3719,6 +3823,15 @@
"integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
"dev": true
},
+ "tsutils": {
+ "version": "3.17.1",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
+ "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -3749,6 +3862,12 @@
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true
},
+ "typescript": {
+ "version": "4.0.0-dev.20200512",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.0-dev.20200512.tgz",
+ "integrity": "sha512-ZsVvhdxpQaA6KpjlT8wNNtweORzNsMtwgCo8viKWQmOvaU+BlMsd3MjD2LONQjFSiETCaw4uq0nNdyfKrCjjIw==",
+ "dev": true
+ },
"uglify-js": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.2.tgz",
diff --git a/package.json b/package.json
index f442149c96..c6f72791e3 100644
--- a/package.json
+++ b/package.json
@@ -21,14 +21,13 @@
"url": "git://github.com/highlightjs/highlight.js.git"
},
"main": "./lib/index.js",
+ "types": "./types/index.d.ts",
"scripts": {
"mocha": "mocha",
-
"build_and_test": "npm run build && npm run test",
"build": "node ./tools/build.js -t node",
"build-cdn": "node ./tools/build.js -t cdn",
"build-browser": "node ./tools/build.js -t browser :common",
-
"test": "mocha --globals document test",
"test-markup": "mocha --globals document test/markup",
"test-detect": "mocha --globals document test/detect",
@@ -38,6 +37,8 @@
"node": "*"
},
"devDependencies": {
+ "@typescript-eslint/eslint-plugin": "^2.32.0",
+ "@typescript-eslint/parser": "^2.32.0",
"clean-css": "^4.2.1",
"cli-table": "^0.3.1",
"colors": "^1.1.2",
@@ -62,7 +63,8 @@
"rollup-plugin-json": "^4.0.0",
"should": "^13.2.3",
"terser": "^4.3.9",
- "tiny-worker": "^2.3.0"
+ "tiny-worker": "^2.3.0",
+ "typescript": "^4.0.0-dev.20200512"
},
"dependencies": {}
}
diff --git a/src/highlight.js b/src/highlight.js
index 2b3f776429..fbcca7a24d 100644
--- a/src/highlight.js
+++ b/src/highlight.js
@@ -18,26 +18,33 @@ const inherit = utils.inherit;
const { nodeStream, mergeStreams } = utils;
const NO_MATCH = Symbol("nomatch");
+/**
+ * @param {any} hljs - object that is extended (legacy)
+ */
const HLJS = function(hljs) {
// Convenience variables for build-in objects
+ /** @type {unknown[]} */
var ArrayProto = [];
// Global internal variables used within the highlight.js library.
+ /** @type {Record..
blocks on a page.
- */
- function initHighlighting() {
+ /**
+ * Highlights to all blocks on a page
+ *
+ * @type {Function & {called?: boolean}}
+ */
+ const initHighlighting = () => {
if (initHighlighting.called) return;
initHighlighting.called = true;
var blocks = document.querySelectorAll('pre code');
ArrayProto.forEach.call(blocks, highlightBlock);
- }
+ };
- /*
- Attaches highlighting to the page load event.
- */
+ // Higlights all when DOMContentLoaded fires
function initHighlightingOnLoad() {
+ // @ts-ignore
window.addEventListener('DOMContentLoaded', initHighlighting, false);
}
- const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text' };
-
- function registerLanguage(name, language) {
+ /**
+ * Register a language grammar module
+ *
+ * @param {string} languageName
+ * @param {LanguageFn} languageDefinition
+ */
+ function registerLanguage(languageName, languageDefinition) {
var lang = null;
try {
- lang = language(hljs);
+ lang = languageDefinition(hljs);
} catch (error) {
- console.error("Language definition for '{}' could not be registered.".replace("{}", name));
+ console.error("Language definition for '{}' could not be registered.".replace("{}", languageName));
// hard or soft error
if (!SAFE_MODE) { throw error; } else { console.error(error); }
// languages that have serious errors are replaced with essentially a
@@ -653,24 +745,30 @@ const HLJS = function(hljs) {
lang = PLAINTEXT_LANGUAGE;
}
// give it a temporary name if it doesn't have one in the meta-data
- if (!lang.name) lang.name = name;
- languages[name] = lang;
- lang.rawDefinition = language.bind(null, hljs);
+ if (!lang.name) lang.name = languageName;
+ languages[languageName] = lang;
+ lang.rawDefinition = languageDefinition.bind(null, hljs);
if (lang.aliases) {
- registerAliases(lang.aliases, { languageName: name });
+ registerAliases(lang.aliases, { languageName });
}
}
+ /**
+ * @returns {string[]} List of language internal names
+ */
function listLanguages() {
return Object.keys(languages);
}
- /*
+ /**
intended usage: When one language truly requires another
Unlike `getLanguage`, this will throw when the requested language
is not available.
+
+ @param {string} name - name of the language to fetch/require
+ @returns {Language | never}
*/
function requireLanguage(name) {
var lang = getLanguage(name);
@@ -680,27 +778,48 @@ const HLJS = function(hljs) {
throw err;
}
+ /**
+ * @param {string} name - name of the language to retrieve
+ * @returns {Language | undefined}
+ */
function getLanguage(name) {
name = (name || '').toLowerCase();
return languages[name] || languages[aliases[name]];
}
- function registerAliases(aliasList, {languageName}) {
+ /**
+ *
+ * @param {string|string[]} aliasList - single alias or list of aliases
+ * @param {{languageName: string}} opts
+ */
+ function registerAliases(aliasList, { languageName }) {
if (typeof aliasList === 'string') {
- aliasList = [aliasList]
+ aliasList = [aliasList];
}
- aliasList.forEach(alias => aliases[alias] = languageName);
+ aliasList.forEach(alias => { aliases[alias] = languageName; });
}
+ /**
+ * Determines if a given language has auto-detection enabled
+ * @param {string} name - name of the language
+ */
function autoDetection(name) {
var lang = getLanguage(name);
return lang && !lang.disableAutodetect;
}
+ /**
+ * @param {HLJSPlugin} plugin
+ */
function addPlugin(plugin) {
plugins.push(plugin);
}
+ /**
+ *
+ * @param {PluginEvent} event
+ * @param {any} args
+ */
function fire(event, args) {
var cb = event;
plugins.forEach(function(plugin) {
@@ -735,7 +854,9 @@ const HLJS = function(hljs) {
hljs.versionString = packageJSON.version;
for (const key in MODES) {
+ // @ts-ignore
if (typeof MODES[key] === "object") {
+ // @ts-ignore
deepFreeze(MODES[key]);
}
}
diff --git a/src/languages/abnf.js b/src/languages/abnf.js
index 3b45dff974..505a35426f 100644
--- a/src/languages/abnf.js
+++ b/src/languages/abnf.js
@@ -4,6 +4,7 @@ Author: Alex McKibben
Website: https://tools.ietf.org/html/rfc5234
*/
+/** @type LanguageFn */
export default function(hljs) {
var regexes = {
ruleDeclaration: "^[a-zA-Z][a-zA-Z0-9-]*",
diff --git a/src/languages/accesslog.js b/src/languages/accesslog.js
index 8d26334647..7826f8631e 100644
--- a/src/languages/accesslog.js
+++ b/src/languages/accesslog.js
@@ -5,6 +5,7 @@
Website: https://httpd.apache.org/docs/2.4/logs.html#accesslog
*/
+ /** @type LanguageFn */
export default function(hljs) {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
var HTTP_VERBS = [
diff --git a/src/languages/actionscript.js b/src/languages/actionscript.js
index 80e4f9b724..bd290ef823 100644
--- a/src/languages/actionscript.js
+++ b/src/languages/actionscript.js
@@ -4,6 +4,7 @@ Author: Alexander Myadzel
Category: scripting
*/
+/** @type LanguageFn */
export default function(hljs) {
var IDENT_RE = '[a-zA-Z_$][a-zA-Z0-9_$]*';
var IDENT_FUNC_RETURN_TYPE_RE = '([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)';
diff --git a/src/languages/ada.js b/src/languages/ada.js
index 64a96f6176..eba10a79d5 100644
--- a/src/languages/ada.js
+++ b/src/languages/ada.js
@@ -17,6 +17,7 @@ Description: Ada is a general-purpose programming language that has great suppor
// xml (broken by Foo : Bar type), elm (broken by Foo : Bar type), vbscript-html (broken by body keyword)
// sql (ada default.txt has a lot of sql keywords)
+/** @type LanguageFn */
export default function(hljs) {
// Regular expression for Ada numeric literals.
// stolen form the VHDL highlighter
diff --git a/src/languages/angelscript.js b/src/languages/angelscript.js
index f192efa352..d04be25d50 100644
--- a/src/languages/angelscript.js
+++ b/src/languages/angelscript.js
@@ -5,6 +5,7 @@ Category: scripting
Website: https://www.angelcode.com/angelscript/
*/
+/** @type LanguageFn */
export default function(hljs) {
var builtInTypeMode = {
className: 'built_in',
diff --git a/src/languages/apache.js b/src/languages/apache.js
index 2ee76a6861..9c8fd7db6c 100644
--- a/src/languages/apache.js
+++ b/src/languages/apache.js
@@ -7,6 +7,7 @@ Description: language definition for Apache configuration files (httpd.conf & .h
Category: common, config
*/
+/** @type LanguageFn */
export default function(hljs) {
var NUMBER_REF = {className: 'number', begin: '[\\$%]\\d+'};
var NUMBER = {className: 'number', begin: '\\d+'};
diff --git a/src/languages/applescript.js b/src/languages/applescript.js
index 0fd9cc71c5..339f9b906d 100644
--- a/src/languages/applescript.js
+++ b/src/languages/applescript.js
@@ -5,6 +5,7 @@ Category: scripting
Website: https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html
*/
+/** @type LanguageFn */
export default function(hljs) {
var STRING = hljs.inherit(hljs.QUOTE_STRING_MODE, {illegal: ''});
var PARAMS = {
diff --git a/src/languages/arcade.js b/src/languages/arcade.js
index 9485e20f91..8370158ebb 100644
--- a/src/languages/arcade.js
+++ b/src/languages/arcade.js
@@ -5,6 +5,8 @@
Website: https://developers.arcgis.com/arcade/
Description: ArcGIS Arcade is an expression language used in many Esri ArcGIS products such as Pro, Online, Server, Runtime, JavaScript, and Python
*/
+
+/** @type LanguageFn */
export default function(hljs) {
var IDENT_RE = '[A-Za-z_][0-9A-Za-z_]*';
var KEYWORDS = {
@@ -25,7 +27,6 @@ export default function(hljs) {
'TrackGeometryWindow TrackIndex TrackStartTime TrackWindow TypeOf Union UrlEncode Variance ' +
'Weekday When Within Year '
};
- var EXPRESSIONS;
var SYMBOL = {
className: 'symbol',
begin: '\\$[datastore|feature|layer|map|measure|sourcefeature|sourcelayer|targetfeature|targetlayer|value|view]+'
@@ -43,7 +44,7 @@ export default function(hljs) {
className: 'subst',
begin: '\\$\\{', end: '\\}',
keywords: KEYWORDS,
- contains: [] // defined later
+ contains: [] // defined later
};
var TEMPLATE_STRING = {
className: 'string',
diff --git a/src/languages/arduino.js b/src/languages/arduino.js
index 8bd1098ce8..823e20395c 100644
--- a/src/languages/arduino.js
+++ b/src/languages/arduino.js
@@ -6,6 +6,7 @@ Requires: cpp.js
Website: https://www.arduino.cc
*/
+/** @type LanguageFn */
export default function(hljs) {
var ARDUINO_KW = {
diff --git a/src/languages/armasm.js b/src/languages/armasm.js
index a8b993534a..eadcaf077d 100644
--- a/src/languages/armasm.js
+++ b/src/languages/armasm.js
@@ -5,6 +5,7 @@ Description: ARM Assembly including Thumb and Thumb2 instructions
Category: assembler
*/
+/** @type LanguageFn */
export default function(hljs) {
//local labels: %?[FB]?[AT]?\d{1,2}\w+
diff --git a/src/languages/asciidoc.js b/src/languages/asciidoc.js
index 84774a937f..419a2d551a 100644
--- a/src/languages/asciidoc.js
+++ b/src/languages/asciidoc.js
@@ -7,6 +7,7 @@ Description: A semantic, text-based document format that can be exported to HTML
Category: markup
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'AsciiDoc',
diff --git a/src/languages/aspectj.js b/src/languages/aspectj.js
index f12c3c8ecb..5b5d5740da 100644
--- a/src/languages/aspectj.js
+++ b/src/languages/aspectj.js
@@ -4,7 +4,9 @@ Author: Hakan Ozler
Website: https://www.eclipse.org/aspectj/
Description: Syntax Highlighting for the AspectJ Language which is a general-purpose aspect-oriented extension to the Java programming language.
*/
-export default function (hljs) {
+
+/** @type LanguageFn */
+export default function(hljs) {
var KEYWORDS =
'false synchronized int abstract float private char boolean static null if const ' +
'for true while long throw strictfp finally protected import native final return void ' +
diff --git a/src/languages/autohotkey.js b/src/languages/autohotkey.js
index 030715c6fd..94c5d19ad6 100644
--- a/src/languages/autohotkey.js
+++ b/src/languages/autohotkey.js
@@ -5,6 +5,7 @@ Description: AutoHotkey language definition
Category: scripting
*/
+/** @type LanguageFn */
export default function(hljs) {
var BACKTICK_ESCAPE = {
begin: '`[\\s\\S]'
diff --git a/src/languages/autoit.js b/src/languages/autoit.js
index 8a6f05439e..f5ecd57cf9 100644
--- a/src/languages/autoit.js
+++ b/src/languages/autoit.js
@@ -5,6 +5,7 @@ Description: AutoIt language definition
Category: scripting
*/
+/** @type LanguageFn */
export default function(hljs) {
var KEYWORDS = 'ByRef Case Const ContinueCase ContinueLoop ' +
'Default Dim Do Else ElseIf EndFunc EndIf EndSelect ' +
diff --git a/src/languages/avrasm.js b/src/languages/avrasm.js
index 4d3c5add9c..de2c7a46d9 100644
--- a/src/languages/avrasm.js
+++ b/src/languages/avrasm.js
@@ -5,6 +5,7 @@ Category: assembler
Website: https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'AVR Assembly',
diff --git a/src/languages/awk.js b/src/languages/awk.js
index 48ef1de47b..0d61c54cc5 100644
--- a/src/languages/awk.js
+++ b/src/languages/awk.js
@@ -5,6 +5,7 @@ Website: https://www.gnu.org/software/gawk/manual/gawk.html
Description: language definition for Awk scripts
*/
+/** @type LanguageFn */
export default function(hljs) {
var VARIABLE = {
className: 'variable',
diff --git a/src/languages/axapta.js b/src/languages/axapta.js
index 5f4a3633ea..e91207963b 100644
--- a/src/languages/axapta.js
+++ b/src/languages/axapta.js
@@ -5,6 +5,7 @@ Website: https://dynamics.microsoft.com/en-us/ax-overview/
Category: enterprise
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'Dynamics 365',
diff --git a/src/languages/bash.js b/src/languages/bash.js
index 0e52636449..e199f4f448 100644
--- a/src/languages/bash.js
+++ b/src/languages/bash.js
@@ -6,6 +6,7 @@ Website: https://www.gnu.org/software/bash/
Category: common
*/
+/** @type LanguageFn */
export default function(hljs) {
const VAR = {};
const BRACED_VAR = {
diff --git a/src/languages/basic.js b/src/languages/basic.js
index 6b5f57e14b..c37bedac71 100644
--- a/src/languages/basic.js
+++ b/src/languages/basic.js
@@ -5,6 +5,7 @@ Description: Based on the BASIC reference from the Tandy 1000 guide
Website: https://en.wikipedia.org/wiki/Tandy_1000
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'BASIC',
diff --git a/src/languages/bnf.js b/src/languages/bnf.js
index 6c83904089..d4edc03bc6 100644
--- a/src/languages/bnf.js
+++ b/src/languages/bnf.js
@@ -4,6 +4,7 @@ Website: https://en.wikipedia.org/wiki/Backus–Naur_form
Author: Oleg Efimov
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'Backus–Naur Form',
diff --git a/src/languages/brainfuck.js b/src/languages/brainfuck.js
index e5d813142b..957d19618b 100644
--- a/src/languages/brainfuck.js
+++ b/src/languages/brainfuck.js
@@ -4,6 +4,7 @@ Author: Evgeny Stepanischev
Website: https://esolangs.org/wiki/Brainfuck
*/
+/** @type LanguageFn */
export default function(hljs) {
var LITERAL = {
className: 'literal',
diff --git a/src/languages/c-like.js b/src/languages/c-like.js
index ef795072e9..11fad83a89 100644
--- a/src/languages/c-like.js
+++ b/src/languages/c-like.js
@@ -13,6 +13,7 @@ change in v10 and don't have to change the requirements again later.
See: https://github.com/highlightjs/highlight.js/issues/2146
*/
+/** @type LanguageFn */
export default function(hljs) {
function optional(s) {
return '(?:' + s + ')?';
diff --git a/src/languages/c.js b/src/languages/c.js
index 854c9da016..70f0afc9ba 100644
--- a/src/languages/c.js
+++ b/src/languages/c.js
@@ -5,6 +5,7 @@ Website: https://en.wikipedia.org/wiki/C_(programming_language)
Requires: c-like.js
*/
+/** @type LanguageFn */
export default function(hljs) {
var lang = hljs.getLanguage('c-like').rawDefinition();
diff --git a/src/languages/cal.js b/src/languages/cal.js
index bb00952506..ab7342e2b7 100644
--- a/src/languages/cal.js
+++ b/src/languages/cal.js
@@ -5,6 +5,7 @@ Description: Provides highlighting of Microsoft Dynamics NAV C/AL code files
Website: https://docs.microsoft.com/en-us/dynamics-nav/programming-in-c-al
*/
+/** @type LanguageFn */
export default function(hljs) {
var KEYWORDS =
'div mod in and or not xor asserterror begin case do downto else end exit for if of repeat then to ' +
diff --git a/src/languages/capnproto.js b/src/languages/capnproto.js
index ecff298499..5fea8fc84f 100644
--- a/src/languages/capnproto.js
+++ b/src/languages/capnproto.js
@@ -6,6 +6,7 @@ Website: https://capnproto.org/capnp-tool.html
Category: protocols
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'Cap’n Proto',
diff --git a/src/languages/ceylon.js b/src/languages/ceylon.js
index 0f7eed3c96..f9b1b7e29c 100644
--- a/src/languages/ceylon.js
+++ b/src/languages/ceylon.js
@@ -3,6 +3,8 @@ Language: Ceylon
Author: Lucas Werkmeister
Website: https://ceylon-lang.org
*/
+
+/** @type LanguageFn */
export default function(hljs) {
// 2.3. Identifiers and keywords
var KEYWORDS =
diff --git a/src/languages/clean.js b/src/languages/clean.js
index a7ee3d96d8..f756675f02 100644
--- a/src/languages/clean.js
+++ b/src/languages/clean.js
@@ -5,6 +5,7 @@ Category: functional
Website: http://clean.cs.ru.nl
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'Clean',
diff --git a/src/languages/clojure-repl.js b/src/languages/clojure-repl.js
index a0fae9a918..6cc1146c6e 100644
--- a/src/languages/clojure-repl.js
+++ b/src/languages/clojure-repl.js
@@ -7,6 +7,7 @@ Website: https://clojure.org
Category: lisp
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'Clojure REPL',
diff --git a/src/languages/clojure.js b/src/languages/clojure.js
index cab6d2593c..bf0d877810 100644
--- a/src/languages/clojure.js
+++ b/src/languages/clojure.js
@@ -6,6 +6,7 @@ Website: https://clojure.org
Category: lisp
*/
+/** @type LanguageFn */
export default function(hljs) {
var SYMBOLSTART = 'a-zA-Z_\\-!.?+*=<>\'';
var SYMBOL_RE = '[' + SYMBOLSTART + '][' + SYMBOLSTART + '0-9/;:]*';
diff --git a/src/languages/cmake.js b/src/languages/cmake.js
index 6fb70f3262..040769c610 100644
--- a/src/languages/cmake.js
+++ b/src/languages/cmake.js
@@ -5,6 +5,7 @@ Author: Igor Kalnitsky
Website: https://cmake.org
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'CMake',
diff --git a/src/languages/coffeescript.js b/src/languages/coffeescript.js
index 60c8bb5a4a..458ce3872a 100644
--- a/src/languages/coffeescript.js
+++ b/src/languages/coffeescript.js
@@ -9,6 +9,7 @@ Website: https://coffeescript.org
import * as ECMAScript from "./lib/ecmascript";
+/** @type LanguageFn */
export default function(hljs) {
var COFFEE_BUILT_INS = [
'npm',
diff --git a/src/languages/coq.js b/src/languages/coq.js
index 032c16988e..8f8a4e573d 100644
--- a/src/languages/coq.js
+++ b/src/languages/coq.js
@@ -5,6 +5,7 @@ Category: functional
Website: https://coq.inria.fr
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'Coq',
diff --git a/src/languages/cos.js b/src/languages/cos.js
index 18f1c8692a..48ae01b58e 100644
--- a/src/languages/cos.js
+++ b/src/languages/cos.js
@@ -4,6 +4,8 @@ Author: Nikita Savchenko
Category: enterprise, scripting
Website: https://cedocs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls
*/
+
+/** @type LanguageFn */
export default function cos (hljs) {
var STRINGS = {
diff --git a/src/languages/cpp.js b/src/languages/cpp.js
index aa41288a14..2f2bd748b6 100644
--- a/src/languages/cpp.js
+++ b/src/languages/cpp.js
@@ -5,8 +5,8 @@ Website: https://isocpp.org
Requires: c-like.js
*/
+/** @type LanguageFn */
export default function(hljs) {
-
var lang = hljs.getLanguage('c-like').rawDefinition();
// return auto-detection back on
lang.disableAutodetect = false;
diff --git a/src/languages/crmsh.js b/src/languages/crmsh.js
index 72165d1d12..ecbcefe836 100644
--- a/src/languages/crmsh.js
+++ b/src/languages/crmsh.js
@@ -6,6 +6,7 @@ Description: Syntax Highlighting for the crmsh DSL
Category: config
*/
+/** @type LanguageFn */
export default function(hljs) {
var RESOURCES = 'primitive rsc_template';
diff --git a/src/languages/crystal.js b/src/languages/crystal.js
index 6cd26ff1be..7230d6cb7b 100644
--- a/src/languages/crystal.js
+++ b/src/languages/crystal.js
@@ -4,6 +4,7 @@ Author: TSUYUSATO Kitsune
Website: https://crystal-lang.org
*/
+/** @type LanguageFn */
export default function(hljs) {
var INT_SUFFIX = '(_*[ui](8|16|32|64|128))?';
var FLOAT_SUFFIX = '(_*f(32|64))?';
diff --git a/src/languages/csharp.js b/src/languages/csharp.js
index 8294552c85..a68c265d17 100644
--- a/src/languages/csharp.js
+++ b/src/languages/csharp.js
@@ -6,6 +6,7 @@ Website: https://docs.microsoft.com/en-us/dotnet/csharp/
Category: common
*/
+/** @type LanguageFn */
export default function(hljs) {
var KEYWORDS = {
keyword:
diff --git a/src/languages/csp.js b/src/languages/csp.js
index b024ec6de3..930c381f3c 100644
--- a/src/languages/csp.js
+++ b/src/languages/csp.js
@@ -7,6 +7,7 @@ Website: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
vim: ts=2 sw=2 st=2
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'CSP',
diff --git a/src/languages/css.js b/src/languages/css.js
index 85e3dc1222..beabf84543 100644
--- a/src/languages/css.js
+++ b/src/languages/css.js
@@ -4,6 +4,7 @@ Category: common, css
Website: https://developer.mozilla.org/en-US/docs/Web/CSS
*/
+/** @type LanguageFn */
export default function(hljs) {
var FUNCTION_LIKE = {
begin: /[\w-]+\(/, returnBegin: true,
diff --git a/src/languages/d.js b/src/languages/d.js
index b17be1696f..9f1d8be95e 100644
--- a/src/languages/d.js
+++ b/src/languages/d.js
@@ -23,6 +23,7 @@ Date: 2012-04-08
* up to the end of line is matched as special token sequence)
*/
+/** @type LanguageFn */
export default function(hljs) {
/**
* Language keywords
diff --git a/src/languages/delphi.js b/src/languages/delphi.js
index b72d3bc2d2..334dddaae4 100644
--- a/src/languages/delphi.js
+++ b/src/languages/delphi.js
@@ -3,6 +3,7 @@ Language: Delphi
Website: https://www.embarcadero.com/products/delphi
*/
+/** @type LanguageFn */
export default function(hljs) {
var KEYWORDS =
'exports register file shl array record property for mod while set ally label uses raise not ' +
diff --git a/src/languages/diff.js b/src/languages/diff.js
index 213ff264ce..4bcbe27540 100644
--- a/src/languages/diff.js
+++ b/src/languages/diff.js
@@ -6,6 +6,7 @@ Website: https://www.gnu.org/software/diffutils/
Category: common
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'Diff',
diff --git a/src/languages/dns.js b/src/languages/dns.js
index 0092c07c60..aa4f29a612 100644
--- a/src/languages/dns.js
+++ b/src/languages/dns.js
@@ -5,6 +5,7 @@ Category: config
Website: https://en.wikipedia.org/wiki/Zone_file
*/
+/** @type LanguageFn */
export default function(hljs) {
return {
name: 'DNS Zone',
diff --git a/src/languages/erlang-repl.js b/src/languages/erlang-repl.js
index 6ba6e0ed49..5d1e6679e9 100644
--- a/src/languages/erlang-repl.js
+++ b/src/languages/erlang-repl.js
@@ -23,7 +23,7 @@ export default function(hljs) {
hljs.COMMENT('%', '$'),
{
className: 'number',
- begin: '\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)',
+ begin: '\\b(\\d+(_\\d+)*#[a-fA-F0-9]+(_[a-fA-F0-9]+)*|\\d+(_\\d+)*(\\.\\d+(_\\d+)*)?([eE][-+]?\\d+)?)',
relevance: 0
},
hljs.APOS_STRING_MODE,
diff --git a/src/languages/erlang.js b/src/languages/erlang.js
index 2cec6aecf5..e37b94ea53 100644
--- a/src/languages/erlang.js
+++ b/src/languages/erlang.js
@@ -20,7 +20,7 @@ export default function(hljs) {
var COMMENT = hljs.COMMENT('%', '$');
var NUMBER = {
className: 'number',
- begin: '\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)',
+ begin: '\\b(\\d+(_\\d+)*#[a-fA-F0-9]+(_[a-fA-F0-9]+)*|\\d+(_\\d+)*(\\.\\d+(_\\d+)*)?([eE][-+]?\\d+)?)',
relevance: 0
};
var NAMED_FUN = {
diff --git a/src/lib/html_renderer.js b/src/lib/html_renderer.js
index bd6bb20307..9e98a07ab4 100644
--- a/src/lib/html_renderer.js
+++ b/src/lib/html_renderer.js
@@ -1,23 +1,53 @@
import { escapeHTML } from './utils';
+/**
+ * @typedef {object} Renderer
+ * @property {(text: string) => void} addText
+ * @property {(node: Node) => void} openNode
+ * @property {(node: Node) => void} closeNode
+ * @property {() => string} value
+ */
+
+/** @typedef {{kind?: string, sublanguage?: boolean}} Node */
+/** @typedef {{walk: (r: Renderer) => void}} Tree */
+/** */
+
const SPAN_CLOSE = '';
+
+/**
+ * Determines if a node needs to be wrapped in
+ *
+ * @param {Node} node */
const emitsWrappingTags = (node) => {
return !!node.kind;
};
+/** @type {Renderer} */
export default class HTMLRenderer {
- constructor(tree, options) {
+ /**
+ * Creates a new HTMLRenderer
+ *
+ * @param {Tree} parseTree - the parse tree (must support `walk` API)
+ * @param {{classPrefix: string}} options
+ */
+ constructor(parseTree, options) {
this.buffer = "";
this.classPrefix = options.classPrefix;
- tree.walk(this);
+ parseTree.walk(this);
}
- // renderer API
-
+ /**
+ * Adds texts to the output stream
+ *
+ * @param {string} text */
addText(text) {
this.buffer += escapeHTML(text);
}
+ /**
+ * Adds a node open to the output stream (if needed)
+ *
+ * @param {Node} node */
openNode(node) {
if (!emitsWrappingTags(node)) return;
@@ -28,19 +58,31 @@ export default class HTMLRenderer {
this.span(className);
}
+ /**
+ * Adds a node close to the output stream (if needed)
+ *
+ * @param {Node} node */
closeNode(node) {
if (!emitsWrappingTags(node)) return;
this.buffer += SPAN_CLOSE;
}
+ /**
+ * returns the accumulated buffer
+ */
+ value() {
+ return this.buffer;
+ }
+
// helpers
+ /**
+ * Builds a span element
+ *
+ * @param {string} className */
span(className) {
this.buffer += ``;
}
- value() {
- return this.buffer;
- }
}
diff --git a/src/lib/mode_compiler.js b/src/lib/mode_compiler.js
index 8e63eeba17..063283b173 100644
--- a/src/lib/mode_compiler.js
+++ b/src/lib/mode_compiler.js
@@ -6,8 +6,21 @@ var COMMON_KEYWORDS = 'of and for in not or if then'.split(' ');
// compilation
+/**
+ * Compiles a language definition result
+ *
+ * Given the raw result of a language definition (Language), compiles this so
+ * that it is ready for highlighting code.
+ * @param {Language} language
+ * @returns {CompiledLanguage}
+ */
export function compileLanguage(language) {
-
+ /**
+ * Builds a regex with the case sensativility of the current language
+ *
+ * @param {RegExp | string} value
+ * @param {boolean} [global]
+ */
function langRe(value, global) {
return new RegExp(
regex.source(value),
@@ -31,13 +44,16 @@ export function compileLanguage(language) {
class MultiRegex {
constructor() {
this.matchIndexes = {};
+ // @ts-ignore
this.regexes = [];
this.matchAt = 1;
this.position = 0;
}
+ // @ts-ignore
addRule(re, opts) {
opts.position = this.position++;
+ // @ts-ignore
this.matchIndexes[this.matchAt] = opts;
this.regexes.push([opts, re]);
this.matchAt += regex.countMatchGroups(re) + 1;
@@ -46,13 +62,15 @@ export function compileLanguage(language) {
compile() {
if (this.regexes.length === 0) {
// avoids the need to check length every time exec is called
+ // @ts-ignore
this.exec = () => null;
}
const terminators = this.regexes.map(el => el[1]);
- this.matcherRe = langRe(regex.join(terminators, '|'), true);
+ this.matcherRe = langRe(regex.join(terminators), true);
this.lastIndex = 0;
}
+ /** @param {string} s */
exec(s) {
this.matcherRe.lastIndex = this.lastIndex;
const match = this.matcherRe.exec(s);
@@ -60,6 +78,7 @@ export function compileLanguage(language) {
// eslint-disable-next-line no-undefined
const i = match.findIndex((el, i) => i > 0 && el !== undefined);
+ // @ts-ignore
const matchData = this.matchIndexes[i];
// trim off any earlier non-relevant match groups (ie, the other regex
// match groups that make up the multi-matcher)
@@ -102,7 +121,9 @@ export function compileLanguage(language) {
*/
class ResumableMultiRegex {
constructor() {
+ // @ts-ignore
this.rules = [];
+ // @ts-ignore
this.multiRegexes = [];
this.count = 0;
@@ -110,6 +131,7 @@ export function compileLanguage(language) {
this.regexIndex = 0;
}
+ // @ts-ignore
getMatcher(index) {
if (this.multiRegexes[index]) return this.multiRegexes[index];
@@ -124,11 +146,13 @@ export function compileLanguage(language) {
this.regexIndex = 0;
}
+ // @ts-ignore
addRule(re, opts) {
this.rules.push([re, opts]);
if (opts.type === "begin") this.count++;
}
+ /** @param {string} s */
exec(s) {
const m = this.getMatcher(this.regexIndex);
m.lastIndex = this.lastIndex;
@@ -145,6 +169,13 @@ export function compileLanguage(language) {
}
}
+ /**
+ * Given a mode, builds a huge ResumableMultiRegex that can be used to walk
+ * the content and find matches.
+ *
+ * @param {CompiledMode} mode
+ * @returns {ResumableMultiRegex}
+ */
function buildModeRegex(mode) {
const mm = new ResumableMultiRegex();
@@ -161,11 +192,20 @@ export function compileLanguage(language) {
}
// TODO: We need negative look-behind support to do this properly
- function skipIfhasPrecedingOrTrailingDot(match, resp) {
+ /**
+ * Skip a match if it has a preceding or trailing 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 skipIfhasPrecedingOrTrailingDot(match, response) {
const before = match.input[match.index - 1];
const after = match.input[match.index + match[0].length];
if (before === "." || after === ".") {
- resp.ignoreMatch();
+ response.ignoreMatch();
}
}
@@ -199,8 +239,18 @@ export function compileLanguage(language) {
* - The parser cursor is not moved forward.
*/
+ /**
+ * Compiles an individual mode
+ *
+ * This can raise an error if the mode contains certain detectable known logic
+ * issues.
+ * @param {Mode} mode
+ * @param {CompiledMode | null} [parent]
+ * @returns {CompiledMode | never}
+ */
function compileMode(mode, parent) {
- if (mode.compiled) return;
+ const cmode = /** @type CompiledMode */ (mode);
+ if (mode.compiled) return cmode;
mode.compiled = true;
// __beforeBegin is considered private API, internal use only
@@ -225,7 +275,7 @@ export function compileLanguage(language) {
// `mode.lexemes` was the old standard before we added and now recommend
// using `keywords.$pattern` to pass the keyword pattern
- mode.keywordPatternRe = langRe(mode.lexemes || kw_pattern || /\w+/, true);
+ cmode.keywordPatternRe = langRe(mode.lexemes || kw_pattern || /\w+/, true);
if (parent) {
if (mode.beginKeywords) {
@@ -237,51 +287,68 @@ export function compileLanguage(language) {
mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?=\\b|\\s)';
mode.__beforeBegin = skipIfhasPrecedingOrTrailingDot;
}
- if (!mode.begin)
- mode.begin = /\B|\b/;
- mode.beginRe = langRe(mode.begin);
- if (mode.endSameAsBegin)
- mode.end = mode.begin;
- if (!mode.end && !mode.endsWithParent)
- mode.end = /\B|\b/;
- if (mode.end)
- mode.endRe = langRe(mode.end);
- mode.terminator_end = regex.source(mode.end) || '';
- if (mode.endsWithParent && parent.terminator_end)
- mode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
- }
- if (mode.illegal)
- mode.illegalRe = langRe(mode.illegal);
- if (mode.relevance == null)
- mode.relevance = 1;
- if (!mode.contains) {
- mode.contains = [];
+ 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 = regex.source(mode.end) || '';
+ if (mode.endsWithParent && parent.terminator_end) {
+ cmode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
+ }
}
+ if (mode.illegal) cmode.illegalRe = langRe(mode.illegal);
+ // eslint-disable-next-line no-undefined
+ if (mode.relevance === undefined) mode.relevance = 1;
+ if (!mode.contains) mode.contains = [];
+
mode.contains = [].concat(...mode.contains.map(function(c) {
return expand_or_clone_mode(c === 'self' ? mode : c);
}));
- mode.contains.forEach(function(c) { compileMode(c, mode); });
+ mode.contains.forEach(function(c) { compileMode(/** @type Mode */ (c), cmode); });
if (mode.starts) {
compileMode(mode.starts, parent);
}
- mode.matcher = buildModeRegex(mode);
+ cmode.matcher = buildModeRegex(cmode);
+ return cmode;
}
// 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.");
}
- compileMode(language);
+ return compileMode(/** @type Mode */ (language));
}
+/**
+ * Determines if a mode has a dependency on it's parent or not
+ *
+ * If a mode does have a parent dependency then often we need to clone it if
+ * it's used in multiple places so that each copy points to the correct parent,
+ * where-as modes without a parent can often safely be re-used at the bottom of
+ * a mode chain.
+ *
+ * @param {Mode | null} mode
+ * @returns {boolean} - is there a dependency on the parent?
+ * */
function dependencyOnParent(mode) {
if (!mode) return false;
return mode.endsWithParent || dependencyOnParent(mode.starts);
}
+/**
+ * Expands a mode or clones it if necessary
+ *
+ * This is necessary for modes with parental dependenceis (see notes on
+ * `dependencyOnParent`) and for nodes that have `variants` - which must then be
+ * exploded into their own individual modes at compile time.
+ *
+ * @param {Mode} mode
+ * @returns {Mode | Mode[]}
+ * */
function expand_or_clone_mode(mode) {
if (mode.variants && !mode.cached_variants) {
mode.cached_variants = mode.variants.map(function(variant) {
@@ -312,9 +379,18 @@ function expand_or_clone_mode(mode) {
return mode;
}
-// keywords
+/***********************************************
+ Keywords
+***********************************************/
+/**
+ * Given raw keywords from a language definition, compile them.
+ *
+ * @param {string | Record} rawKeywords
+ * @param {boolean} case_insensitive
+ */
function compileKeywords(rawKeywords, case_insensitive) {
+ /** @type KeywordDict */
var compiled_keywords = {};
if (typeof rawKeywords === 'string') { // string
@@ -328,17 +404,33 @@ function compileKeywords(rawKeywords, case_insensitive) {
// ---
- function splitAndCompile(className, str) {
+ /**
+ * Compiles an individual list of keywords
+ *
+ * Ex: "for if when while|5"
+ *
+ * @param {string} className
+ * @param {string} keywordList
+ */
+ function splitAndCompile(className, keywordList) {
if (case_insensitive) {
- str = str.toLowerCase();
+ keywordList = keywordList.toLowerCase();
}
- str.split(' ').forEach(function(keyword) {
+ keywordList.split(' ').forEach(function(keyword) {
var pair = keyword.split('|');
compiled_keywords[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
@@ -349,6 +441,10 @@ function scoreForKeyword(keyword, providedScore) {
return commonKeyword(keyword) ? 0 : 1;
}
-function commonKeyword(word) {
- return COMMON_KEYWORDS.includes(word.toLowerCase());
+/**
+ * Determines if a given keyword is common or not
+ *
+ * @param {string} keyword */
+function commonKeyword(keyword) {
+ return COMMON_KEYWORDS.includes(keyword.toLowerCase());
}
diff --git a/src/lib/modes.js b/src/lib/modes.js
index 669968d58f..33d18a9105 100644
--- a/src/lib/modes.js
+++ b/src/lib/modes.js
@@ -9,6 +9,9 @@ export const C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+
export const BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b...
export const RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
+/**
+* @param { Partial & {binary?: string | RegExp} } opts
+*/
export const SHEBANG = (opts = {}) => {
const beginShebang = /^#![ ]*\//;
if (opts.binary) {
@@ -23,6 +26,7 @@ export const SHEBANG = (opts = {}) => {
begin: beginShebang,
end: /$/,
relevance: 0,
+ /** @type {ModeCallback} */
"on:begin": (m, resp) => {
if (m.index !== 0) resp.ignoreMatch();
}
@@ -50,15 +54,23 @@ export const QUOTE_STRING_MODE = {
export const PHRASAL_WORDS_MODE = {
begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
};
-export const COMMENT = function(begin, end, inherits) {
+/**
+ * Creates a comment mode
+ *
+ * @param {string | RegExp} begin
+ * @param {string | RegExp} end
+ * @param {Mode | {}} [modeOptions]
+ * @returns {Partial}
+ */
+export const COMMENT = function(begin, end, modeOptions = {}) {
var mode = inherit(
{
className: 'comment',
- begin: begin,
- end: end,
+ begin,
+ end,
contains: []
},
- inherits || {}
+ modeOptions
);
mode.contains.push(PHRASAL_WORDS_MODE);
mode.contains.push({
@@ -139,10 +151,19 @@ export const METHOD_GUARD = {
relevance: 0
};
+/**
+ * Adds end same as begin mechanics to a mode
+ *
+ * Your mode must include at least a single () match group as that first match
+ * group is what is used for comparison
+ * @param {Partial} mode
+ */
export const END_SAME_AS_BEGIN = function(mode) {
return Object.assign(mode,
{
- 'on:begin': (m, resp) => { resp.data._beginMatch = m[1]; },
- 'on:end': (m, resp) => { if (resp.data._beginMatch !== m[1]) resp.ignoreMatch() }
+ /** @type {ModeCallback} */
+ 'on:begin': (m, resp) => { resp.data._beginMatch = m[1]; },
+ /** @type {ModeCallback} */
+ 'on:end': (m, resp) => { if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); }
});
};
diff --git a/src/lib/regex.js b/src/lib/regex.js
index 80a9ce1b70..1727cb57b9 100644
--- a/src/lib/regex.js
+++ b/src/lib/regex.js
@@ -1,26 +1,52 @@
+/**
+ * @param {string} value
+ * @returns {RegExp}
+ * */
export function escape(value) {
return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
}
+/**
+ * @param {RegExp | string } re
+ * @returns {string}
+ */
export function source(re) {
- // if it's a regex get it's source,
- // otherwise it's a string already so just return it
- return (re && re.source) || re;
+ if (!re) return null;
+ if (typeof re === "string") return re;
+
+ return re.source;
}
-export function lookahead(regex) {
- return concat('(?=', regex, ')');
+/**
+ * @param {RegExp | string } re
+ * @returns {string}
+ */
+export function lookahead(re) {
+ return concat('(?=', re, ')');
}
+/**
+ * @param {(RegExp | string)[] } args
+ * @returns {string}
+ */
export function concat(...args) {
const joined = args.map((x) => source(x)).join("");
return joined;
}
+/**
+ * @param {RegExp} re
+ * @returns {number}
+ */
export function countMatchGroups(re) {
return (new RegExp(re.toString() + '|')).exec('').length - 1;
}
+/**
+ * Does lexeme start with a regular expression match at the beginning
+ * @param {RegExp} re
+ * @param {string} lexeme
+ */
export function startsWith(re, lexeme) {
var match = re && re.exec(lexeme);
return match && match.index === 0;
@@ -31,7 +57,12 @@ export function startsWith(re, lexeme) {
// it also places each individual regular expression into it's own
// match group, keeping track of the sequencing of those match groups
// is currently an exercise for the caller. :-)
-export function join(regexps, separator) {
+/**
+ * @param {(string | RegExp)[]} regexps
+ * @param {string} separator
+ * @returns {string}
+ */
+export function join(regexps, separator = "|") {
// backreferenceRe matches an open parenthesis or backreference. To avoid
// an incorrect parse, it additionally matches the following:
// - [...] elements, where the meaning of parentheses and escapes change
diff --git a/src/lib/response.js b/src/lib/response.js
index 9c5bcfa95c..c49c6df59f 100644
--- a/src/lib/response.js
+++ b/src/lib/response.js
@@ -1,7 +1,11 @@
export default class Response {
+ /**
+ * @param {CompiledMode} mode
+ */
constructor(mode) {
- if (mode.data === undefined)
- mode.data = {};
+ // eslint-disable-next-line no-undefined
+ if (mode.data === undefined) mode.data = {};
+
this.data = mode.data;
}
diff --git a/src/lib/token_tree.js b/src/lib/token_tree.js
index f3f851f29e..6e2fba63b2 100644
--- a/src/lib/token_tree.js
+++ b/src/lib/token_tree.js
@@ -1,7 +1,12 @@
import HTMLRenderer from './html_renderer';
+/** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} | string} Node */
+/** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} } DataNode */
+/** */
+
class TokenTree {
constructor() {
+ /** @type DataNode */
this.rootNode = { children: [] };
this.stack = [this.rootNode];
}
@@ -12,11 +17,14 @@ class TokenTree {
get root() { return this.rootNode; }
+ /** @param {Node} node */
add(node) {
this.top.children.push(node);
}
+ /** @param {string} kind */
openNode(kind) {
+ /** @type Node */
const node = { kind, children: [] };
this.add(node);
this.stack.push(node);
@@ -26,6 +34,8 @@ class TokenTree {
if (this.stack.length > 1) {
return this.stack.pop();
}
+ // eslint-disable-next-line no-undefined
+ return undefined;
}
closeAllNodes() {
@@ -36,10 +46,21 @@ class TokenTree {
return JSON.stringify(this.rootNode, null, 4);
}
+ /**
+ * @typedef { import("./html_renderer").Renderer } Renderer
+ * @param {Renderer} builder
+ */
walk(builder) {
+ // this does not
return this.constructor._walk(builder, this.rootNode);
+ // this works
+ // return TokenTree._walk(builder, this.rootNode);
}
+ /**
+ * @param {Renderer} builder
+ * @param {Node} node
+ */
static _walk(builder, node) {
if (typeof node === "string") {
builder.addText(node);
@@ -51,16 +72,19 @@ class TokenTree {
return builder;
}
+ /**
+ * @param {Node} node
+ */
static _collapse(node) {
- if (!node.children) {
- return;
- }
+ if (typeof node === "string") return;
+ if (!node.children) return;
+
if (node.children.every(el => typeof el === "string")) {
- node.text = node.children.join("");
- delete node.children;
+ // node.text = node.children.join("");
+ // delete node.children;
+ node.children = [node.children.join("")];
} else {
node.children.forEach((child) => {
- if (typeof child === "string") return;
TokenTree._collapse(child);
});
}
@@ -83,12 +107,23 @@ class TokenTree {
- toHTML()
*/
+
+/**
+ * @implements {Emitter}
+ */
export default class TokenTreeEmitter extends TokenTree {
+ /**
+ * @param {*} options
+ */
constructor(options) {
super();
this.options = options;
}
+ /**
+ * @param {string} text
+ * @param {string} kind
+ */
addKeyword(text, kind) {
if (text === "") { return; }
@@ -97,13 +132,21 @@ export default class TokenTreeEmitter extends TokenTree {
this.closeNode();
}
+ /**
+ * @param {string} text
+ */
addText(text) {
if (text === "") { return; }
this.add(text);
}
+ /**
+ * @param {Emitter & {root: DataNode}} emitter
+ * @param {string} name
+ */
addSublanguage(emitter, name) {
+ /** @type DataNode */
const node = emitter.root;
node.kind = name;
node.sublanguage = true;
diff --git a/src/lib/utils.js b/src/lib/utils.js
index d727c46e66..3b9a0f1069 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -1,3 +1,7 @@
+/**
+ * @param {string} value
+ * @returns {string}
+ */
export function escapeHTML(value) {
return value
.replace(/&/g, '&')
@@ -10,31 +14,47 @@ export function escapeHTML(value) {
/**
* performs a shallow merge of multiple objects into one
*
- * @arguments list of objects with properties to merge
- * @returns a single new object
+ * @template T
+ * @param {T} original
+ * @param {Record[]} objects
+ * @returns {T} a single new object
*/
-export function inherit(parent) { // inherit(parent, override_obj, override_obj, ...)
+export function inherit(original, ...objects) {
+ /** @type Record */
var result = {};
- var objects = Array.prototype.slice.call(arguments, 1);
- for (const key in parent) {
- result[key] = parent[key];
+ for (const key in original) {
+ result[key] = original[key];
}
objects.forEach(function(obj) {
for (const key in obj) {
result[key] = obj[key];
}
});
- return result;
+ 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
+ */
export function nodeStream(node) {
+ /** @type Event[] */
var result = [];
(function _nodeStream(node, offset) {
for (var child = node.firstChild; child; child = child.nextSibling) {
@@ -64,6 +84,11 @@ export function nodeStream(node) {
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) {
var processed = 0;
var result = '';
@@ -95,17 +120,28 @@ export function mergeStreams(original, highlighted, value) {
return highlighted[0].event === 'start' ? original : highlighted;
}
+ /**
+ * @param {Node} node
+ */
function open(node) {
- function attr_str(a) {
- return ' ' + a.nodeName + '="' + escapeHTML(a.value) + '"';
+ /** @param {Attr} attr */
+ function attr_str(attr) {
+ return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
}
+ // @ts-ignore
result += '<' + tag(node) + [].map.call(node.attributes, attr_str).join('') + '>';
}
+ /**
+ * @param {Node} node
+ */
function close(node) {
result += '' + tag(node) + '>';
}
+ /**
+ * @param {Event} event
+ */
function render(event) {
(event.event === 'start' ? open : close)(event.node);
}
diff --git a/src/vendor/deep_freeze.js b/src/vendor/deep_freeze.js
index 0bc2c2d003..c5ce3eb560 100644
--- a/src/vendor/deep_freeze.js
+++ b/src/vendor/deep_freeze.js
@@ -1,20 +1,21 @@
// https://github.com/substack/deep-freeze/blob/master/index.js
-export default function deepFreeze (o) {
- Object.freeze(o);
+/** @param {any} obj */
+export default function deepFreeze(obj) {
+ Object.freeze(obj);
- var objIsFunction = typeof o === 'function';
+ var objIsFunction = typeof obj === 'function';
- Object.getOwnPropertyNames(o).forEach(function (prop) {
- if (o.hasOwnProperty(prop)
- && o[prop] !== null
- && (typeof o[prop] === "object" || typeof o[prop] === "function")
+ Object.getOwnPropertyNames(obj).forEach(function(prop) {
+ if (Object.hasOwnProperty.call(obj, prop)
+ && obj[prop] !== null
+ && (typeof obj[prop] === "object" || typeof obj[prop] === "function")
// IE11 fix: https://github.com/highlightjs/highlight.js/issues/2318
// TODO: remove in the future
&& (objIsFunction ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments' : true)
- && !Object.isFrozen(o[prop])) {
- deepFreeze(o[prop]);
+ && !Object.isFrozen(obj[prop])) {
+ deepFreeze(obj[prop]);
}
});
- return o;
-};
+ return obj;
+}
diff --git a/test/markup/erlang/numbers.expect.txt b/test/markup/erlang/numbers.expect.txt
new file mode 100644
index 0000000000..9fdb67b1ae
--- /dev/null
+++ b/test/markup/erlang/numbers.expect.txt
@@ -0,0 +1,14 @@
+Integer = 1234
+BigInteger = 1_234_000
+NegInteger = -20_000
+Float = 2.34
+BigFloat = 3_333.14159_26535_89793
+SciFloat = 2.4e23
+PlusSciFloat = 2.4e+23
+SmallSciFloat = 2.4e-23
+Binary = 2#1010
+StrangeBinary = 2#1010_1010_1010
+Octal = 8#777
+StrangeOctal = 8#777_666_555
+Hex = 16#1ABEF
+StrangeHex = 16#1234_FACE_987D
diff --git a/test/markup/erlang/numbers.txt b/test/markup/erlang/numbers.txt
new file mode 100644
index 0000000000..d14aff8819
--- /dev/null
+++ b/test/markup/erlang/numbers.txt
@@ -0,0 +1,14 @@
+Integer = 1234
+BigInteger = 1_234_000
+NegInteger = -20_000
+Float = 2.34
+BigFloat = 3_333.14159_26535_89793
+SciFloat = 2.4e23
+PlusSciFloat = 2.4e+23
+SmallSciFloat = 2.4e-23
+Binary = 2#1010
+StrangeBinary = 2#1010_1010_1010
+Octal = 8#777
+StrangeOctal = 8#777_666_555
+Hex = 16#1ABEF
+StrangeHex = 16#1234_FACE_987D
diff --git a/test/markup/index.js b/test/markup/index.js
index 12686c25c5..e2461a3311 100644
--- a/test/markup/index.js
+++ b/test/markup/index.js
@@ -6,6 +6,8 @@ const hljs = require('../../build');
const path = require('path');
const utility = require('../utility');
+hljs.debugMode();
+
const { getThirdPartyPackages } = require("../../tools/lib/external_language")
function testLanguage(language, {testDir}) {
diff --git a/tools/build_node.js b/tools/build_node.js
index 2e258a1c9b..ed30b5d675 100644
--- a/tools/build_node.js
+++ b/tools/build_node.js
@@ -75,9 +75,11 @@ async function buildNode(options) {
mkdir("lib/languages");
mkdir("scss");
mkdir("styles");
+ mkdir("types");
install("./LICENSE", "LICENSE");
install("./README.md","README.md");
+ install("./types/index.d.ts","types/index.d.ts");
log("Writing styles.");
const styles = await fs.readdir("./src/styles/");
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000000..10bdf3ba1e
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,64 @@
+{
+ "compilerOptions": {
+ /* Basic Options */
+ "target": "es2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
+ "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
+ // "lib": [], /* Specify library files to be included in the compilation. */
+ "allowJs": true, /* Allow javascript files to be compiled. */
+ "checkJs": true, /* Report errors in .js files. */
+ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
+ // "declaration": true, /* Generates corresponding '.d.ts' file. */
+ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
+ // "sourceMap": true, /* Generates corresponding '.map' file. */
+ // "outFile": "./", /* Concatenate and emit output to single file. */
+ "outDir": "./build", /* Redirect output structure to the directory. */
+ // "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+ // "composite": true, /* Enable project compilation */
+ // "removeComments": true, /* Do not emit comments to output. */
+ // "noEmit": true, /* Do not emit outputs. */
+ // "importHelpers": true, /* Import emit helpers from 'tslib'. */
+ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
+ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
+
+ /* Strict Type-Checking Options */
+ "strict": false, /* Enable all strict type-checking options. */
+ "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
+ "strictNullChecks": false, /* Enable strict null checks. */
+ // "strictFunctionTypes": true, /* Enable strict checking of function types. */
+ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
+ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
+ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
+
+ /* Additional Checks */
+ "noUnusedLocals": true, /* Report errors on unused locals. */
+ "noUnusedParameters": true, /* Report errors on unused parameters. */
+ "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
+ "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
+
+ /* Module Resolution Options */
+ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
+ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
+ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
+ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
+ // "typeRoots": ["./"], /* List of folders to include type definitions from. */
+ "types": [
+ "./types/index"
+ ], /* Type declaration files to be included in compilation. */
+ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
+ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
+ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
+ "resolveJsonModule": true,
+
+ /* Source Map Options */
+ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
+ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
+
+ /* Experimental Options */
+ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
+ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */,
+
+ },
+ "include": ["src/**/*", "tests/**/*"]
+}
diff --git a/types/index.d.ts b/types/index.d.ts
new file mode 100644
index 0000000000..b9eb0d317a
--- /dev/null
+++ b/types/index.d.ts
@@ -0,0 +1,212 @@
+/* Public API */
+
+// eslint-disable-next-line
+declare const hljs : HLJSApi;
+
+interface HLJSApi {
+ highlight: (languageName: string, code: string, ignoreIllegals?: boolean, continuation?: Mode) => HighlightResult
+ highlightAuto: (code: string, languageSubset?: string[]) => AutoHighlightResult
+ fixMarkup: (html: string) => string
+ highlightBlock: (element: HTMLElement) => void
+ configure: (options: Partial) => void
+ initHighlighting: () => void
+ initHighlightingOnLoad: () => void
+ registerLanguage: (languageName: string, language: LanguageFn) => void
+ listLanguages: () => string[]
+ registerAliases: (aliasList: string | string[], { languageName } : {languageName: string}) => void
+ getLanguage: (languageName: string) => Language | undefined
+ requireLanguage: (languageName: string) => Language | never
+ autoDetection: (languageName: string) => boolean
+ inherit: (original: T, ...args: Record[]) => T
+ addPlugin: (plugin: HLJSPlugin) => void
+ debugMode: () => void
+ safeMode: () => void
+ versionString: string
+}
+
+interface HLJSApi {
+ SHEBANG: (mode?: Partial & {binary?: string | RegExp}) => Mode
+ BACKSLASH_ESCAPE: Mode
+ QUOTE_STRING_MODE: Mode
+ APOS_STRING_MODE: Mode
+ PHRASAL_WORDS_MODE: Mode
+ COMMENT: (begin: string | RegExp, end: string | RegExp, modeOpts?: Mode | {}) => Mode
+ C_LINE_COMMENT_MODE: Mode
+ C_BLOCK_COMMENT_MODE: Mode
+ HASH_COMMENT_MODE: Mode
+ NUMBER_MODE: Mode
+ C_NUMBER_MODE: Mode
+ BINARY_NUMBER_MODE: Mode
+ CSS_NUMBER_MODE: Mode
+ REGEXP_MODE: Mode
+ TITLE_MODE: Mode
+ UNDERSCORE_TITLE_MODE: Mode
+ METHOD_GUARD: Mode
+ END_SAME_AS_BEGIN: (mode: Mode) => Mode
+ // build in regex
+ IDENT_RE: string
+ UNDERSCORE_IDENT_RE: string
+ NUMBER_RE: string
+ C_NUMBER_RE: string
+ BINARY_NUMBER_RE: string
+ RE_STARTERS_RE: string
+}
+
+type LanguageFn = (hljs: HLJSApi) => Language
+
+// interface RawLanguage {
+// name?: string
+// aliases?: string[]
+// rawDefinition?: () => Language
+// }
+
+interface HighlightResult {
+ relevance : number
+ value : string
+ language? : string
+ emitter : Emitter
+ illegal : boolean
+ top? : Language | CompiledMode
+ illegalBy? : illegalData
+ sofar? : string
+ errorRaised? : Error
+ // * for auto-highlight
+ second_best? : Omit
+}
+
+interface illegalData {
+ msg: string
+ context: string
+ mode: CompiledMode
+}
+
+interface AutoHighlightResult extends HighlightResult {
+}
+
+type PluginEvent =
+ 'before:highlight'
+ | 'after:highlight'
+ | 'before:highlightBlock'
+ | 'after:highlightBlock'
+
+type HLJSPlugin = { [K in PluginEvent]? : any }
+
+interface EmitterConstructor {
+ new (opts: any): Emitter
+}
+
+interface HLJSOptions {
+ noHighlightRe: RegExp
+ languageDetectRe: RegExp
+ classPrefix: string
+ tabReplace?: string
+ useBR: boolean
+ languages?: string[]
+ __emitter: EmitterConstructor
+}
+
+interface CallbackResponse {
+ data: Record
+ ignoreMatch: () => void
+}
+
+/************
+ PRIVATE API
+ ************/
+
+/* for jsdoc annotations in the JS source files */
+
+type AnnotatedError = Error & {mode?: Mode | Language, languageName?: string, badRule?: Mode}
+
+type ModeCallback = (match: RegExpMatchArray, response: CallbackResponse) => void
+type HighlightedHTMLElement = HTMLElement & {result?: object, second_best?: object, parentNode: HTMLElement}
+type EnhancedMatch = RegExpMatchArray & {rule: CompiledMode, type: MatchType}
+type MatchType = "begin" | "end" | "illegal"
+
+ interface Emitter {
+ addKeyword(text: string, kind: string): void
+ addText(text: string): void
+ toHTML(): string
+ finalize(): void
+ closeAllNodes(): void
+ openNode(kind: string): void
+ closeNode(): void
+ addSublanguage(emitter: Emitter, subLanguageName: string): void
+ }
+
+/* modes */
+
+ interface ModeCallbacks {
+ "on:end"?: Function,
+ "on:begin"?: Function,
+ }
+
+interface Mode extends ModeCallbacks, ModeDetails {
+
+}
+
+interface LanguageDetail {
+ name?: string
+ rawDefinition?: () => Language
+ aliases?: string[]
+ disableAutodetect?: boolean
+ contains: ("self"|Mode)[]
+ case_insensitive?: boolean
+ keywords?: Record | string
+ compiled?: boolean
+}
+
+type Language = LanguageDetail & Partial
+
+interface CompiledLanguage extends LanguageDetail, CompiledMode {
+ compiled: true
+ contains: CompiledMode[]
+ keywords: Record
+}
+
+type KeywordData = [string, number];
+type KeywordDict = Record
+
+type CompiledMode = Omit &
+ {
+ contains: CompiledMode[]
+ keywords: KeywordDict
+ data: Record
+ terminator_end: string
+ keywordPatternRe: RegExp
+ beginRe: RegExp
+ endRe: RegExp
+ illegalRe: RegExp
+ matcher: any
+ compiled: true
+ starts?: CompiledMode
+ parent?: CompiledMode
+ }
+
+interface ModeDetails {
+ begin?: RegExp | string
+ end?: RegExp | string
+ className?: string
+ contains?: ("self" | Mode)[]
+ endsParent?: boolean
+ endsWithParent?: boolean
+ endSameAsBegin?: boolean
+ skip?: boolean
+ excludeBegin?: boolean
+ excludeEnd?: boolean
+ returnBegin?: boolean
+ returnEnd?: boolean
+ __beforeBegin?: Function
+ parent?: Mode
+ starts?:Mode
+ lexemes?: string | RegExp
+ keywords?: Record | string
+ beginKeywords?: string
+ relevance?: number
+ illegal?: string | RegExp
+ variants?: Mode[]
+ cached_variants?: Mode[]
+ // parsed
+ subLanguage?: string | string[]
+ compiled?: boolean
+}