highlight.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. /*
  2. Syntax highlighting with language autodetection.
  3. https://highlightjs.org/
  4. */
  5. (function(factory) {
  6. // Find the global object for export to both the browser and web workers.
  7. var globalObject = typeof window === 'object' && window ||
  8. typeof self === 'object' && self;
  9. // Setup highlight.js for different environments. First is Node.js or
  10. // CommonJS.
  11. // `nodeType` is checked to ensure that `exports` is not a HTML element.
  12. if(typeof exports !== 'undefined' && !exports.nodeType) {
  13. factory(exports);
  14. } else if(globalObject) {
  15. // Export hljs globally even when using AMD for cases when this script
  16. // is loaded with others that may still expect a global hljs.
  17. globalObject.hljs = factory({});
  18. // Finally register the global hljs with AMD.
  19. if(typeof define === 'function' && define.amd) {
  20. define([], function() {
  21. return globalObject.hljs;
  22. });
  23. }
  24. }
  25. }(function(hljs) {
  26. var showedUpgradeWarning = false;
  27. // Convenience variables for build-in objects
  28. var ArrayProto = [],
  29. objectKeys = Object.keys;
  30. // Global internal variables used within the highlight.js library.
  31. var languages = Object.create(null),
  32. aliases = Object.create(null);
  33. // safe/production mode - swallows more errors, tries to keep running
  34. // even if a single syntax or parse hits a fatal error
  35. var SAFE_MODE = true;
  36. // Regular expressions used throughout the highlight.js library.
  37. var noHighlightRe = /^(no-?highlight|plain|text)$/i,
  38. languagePrefixRe = /\blang(?:uage)?-([\w-]+)\b/i,
  39. fixMarkupRe = /((^(<[^>]+>|\t|)+|(?:\n)))/gm;
  40. // The object will be assigned by the build tool. It used to synchronize API
  41. // of external language files with minified version of the highlight.js library.
  42. var API_REPLACES;
  43. var spanEndTag = '</span>';
  44. var LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?";
  45. // Global options used when within external APIs. This is modified when
  46. // calling the `hljs.configure` function.
  47. var options = {
  48. hideUpgradeWarningAcceptNoSupportOrSecurityUpdates: false,
  49. classPrefix: 'hljs-',
  50. tabReplace: null,
  51. useBR: false,
  52. languages: undefined
  53. };
  54. // keywords that should have no default relevance value
  55. var COMMON_KEYWORDS = 'of and for in not or if then'.split(' ');
  56. /* Utility functions */
  57. function escape(value) {
  58. return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  59. }
  60. function tag(node) {
  61. return node.nodeName.toLowerCase();
  62. }
  63. function testRe(re, lexeme) {
  64. var match = re && re.exec(lexeme);
  65. return match && match.index === 0;
  66. }
  67. function isNotHighlighted(language) {
  68. return noHighlightRe.test(language);
  69. }
  70. function blockLanguage(block) {
  71. var i, match, length, _class;
  72. var classes = block.className + ' ';
  73. classes += block.parentNode ? block.parentNode.className : '';
  74. // language-* takes precedence over non-prefixed class names.
  75. match = languagePrefixRe.exec(classes);
  76. if (match) {
  77. var language = getLanguage(match[1]);
  78. if (!language) {
  79. console.warn(LANGUAGE_NOT_FOUND.replace("{}", match[1]));
  80. console.warn("Falling back to no-highlight mode for this block.", block);
  81. }
  82. return language ? match[1] : 'no-highlight';
  83. }
  84. classes = classes.split(/\s+/);
  85. for (i = 0, length = classes.length; i < length; i++) {
  86. _class = classes[i];
  87. if (isNotHighlighted(_class) || getLanguage(_class)) {
  88. return _class;
  89. }
  90. }
  91. }
  92. /**
  93. * performs a shallow merge of multiple objects into one
  94. *
  95. * @arguments list of objects with properties to merge
  96. * @returns a single new object
  97. */
  98. function inherit(parent) { // inherit(parent, override_obj, override_obj, ...)
  99. var key;
  100. var result = {};
  101. var objects = Array.prototype.slice.call(arguments, 1);
  102. for (key in parent)
  103. result[key] = parent[key];
  104. objects.forEach(function(obj) {
  105. for (key in obj)
  106. result[key] = obj[key];
  107. });
  108. return result;
  109. }
  110. /* Stream merging */
  111. function nodeStream(node) {
  112. var result = [];
  113. (function _nodeStream(node, offset) {
  114. for (var child = node.firstChild; child; child = child.nextSibling) {
  115. if (child.nodeType === 3)
  116. offset += child.nodeValue.length;
  117. else if (child.nodeType === 1) {
  118. result.push({
  119. event: 'start',
  120. offset: offset,
  121. node: child
  122. });
  123. offset = _nodeStream(child, offset);
  124. // Prevent void elements from having an end tag that would actually
  125. // double them in the output. There are more void elements in HTML
  126. // but we list only those realistically expected in code display.
  127. if (!tag(child).match(/br|hr|img|input/)) {
  128. result.push({
  129. event: 'stop',
  130. offset: offset,
  131. node: child
  132. });
  133. }
  134. }
  135. }
  136. return offset;
  137. })(node, 0);
  138. return result;
  139. }
  140. function mergeStreams(original, highlighted, value) {
  141. var processed = 0;
  142. var result = '';
  143. var nodeStack = [];
  144. function selectStream() {
  145. if (!original.length || !highlighted.length) {
  146. return original.length ? original : highlighted;
  147. }
  148. if (original[0].offset !== highlighted[0].offset) {
  149. return (original[0].offset < highlighted[0].offset) ? original : highlighted;
  150. }
  151. /*
  152. To avoid starting the stream just before it should stop the order is
  153. ensured that original always starts first and closes last:
  154. if (event1 == 'start' && event2 == 'start')
  155. return original;
  156. if (event1 == 'start' && event2 == 'stop')
  157. return highlighted;
  158. if (event1 == 'stop' && event2 == 'start')
  159. return original;
  160. if (event1 == 'stop' && event2 == 'stop')
  161. return highlighted;
  162. ... which is collapsed to:
  163. */
  164. return highlighted[0].event === 'start' ? original : highlighted;
  165. }
  166. function open(node) {
  167. function attr_str(a) {
  168. return ' ' + a.nodeName + '="' + escape(a.value).replace(/"/g, '&quot;') + '"';
  169. }
  170. result += '<' + tag(node) + ArrayProto.map.call(node.attributes, attr_str).join('') + '>';
  171. }
  172. function close(node) {
  173. result += '</' + tag(node) + '>';
  174. }
  175. function render(event) {
  176. (event.event === 'start' ? open : close)(event.node);
  177. }
  178. while (original.length || highlighted.length) {
  179. var stream = selectStream();
  180. result += escape(value.substring(processed, stream[0].offset));
  181. processed = stream[0].offset;
  182. if (stream === original) {
  183. /*
  184. On any opening or closing tag of the original markup we first close
  185. the entire highlighted node stack, then render the original tag along
  186. with all the following original tags at the same offset and then
  187. reopen all the tags on the highlighted stack.
  188. */
  189. nodeStack.reverse().forEach(close);
  190. do {
  191. render(stream.splice(0, 1)[0]);
  192. stream = selectStream();
  193. } while (stream === original && stream.length && stream[0].offset === processed);
  194. nodeStack.reverse().forEach(open);
  195. } else {
  196. if (stream[0].event === 'start') {
  197. nodeStack.push(stream[0].node);
  198. } else {
  199. nodeStack.pop();
  200. }
  201. render(stream.splice(0, 1)[0]);
  202. }
  203. }
  204. return result + escape(value.substr(processed));
  205. }
  206. /* Initialization */
  207. function dependencyOnParent(mode) {
  208. if (!mode) return false;
  209. return mode.endsWithParent || dependencyOnParent(mode.starts);
  210. }
  211. function expand_or_clone_mode(mode) {
  212. if (mode.variants && !mode.cached_variants) {
  213. mode.cached_variants = mode.variants.map(function(variant) {
  214. return inherit(mode, {variants: null}, variant);
  215. });
  216. }
  217. // EXPAND
  218. // if we have variants then essentially "replace" the mode with the variants
  219. // this happens in compileMode, where this function is called from
  220. if (mode.cached_variants)
  221. return mode.cached_variants;
  222. // CLONE
  223. // if we have dependencies on parents then we need a unique
  224. // instance of ourselves, so we can be reused with many
  225. // different parents without issue
  226. if (dependencyOnParent(mode))
  227. return [inherit(mode, { starts: mode.starts ? inherit(mode.starts) : null })];
  228. if (Object.isFrozen(mode))
  229. return [inherit(mode)];
  230. // no special dependency issues, just return ourselves
  231. return [mode];
  232. }
  233. function restoreLanguageApi(obj) {
  234. if(API_REPLACES && !obj.langApiRestored) {
  235. obj.langApiRestored = true;
  236. for(var key in API_REPLACES) {
  237. if (obj[key]) {
  238. obj[API_REPLACES[key]] = obj[key];
  239. }
  240. }
  241. (obj.contains || []).concat(obj.variants || []).forEach(restoreLanguageApi);
  242. }
  243. }
  244. function compileKeywords(rawKeywords, case_insensitive) {
  245. var compiled_keywords = {};
  246. if (typeof rawKeywords === 'string') { // string
  247. splitAndCompile('keyword', rawKeywords);
  248. } else {
  249. objectKeys(rawKeywords).forEach(function (className) {
  250. splitAndCompile(className, rawKeywords[className]);
  251. });
  252. }
  253. return compiled_keywords;
  254. // ---
  255. function splitAndCompile(className, str) {
  256. if (case_insensitive) {
  257. str = str.toLowerCase();
  258. }
  259. str.split(' ').forEach(function(keyword) {
  260. var pair = keyword.split('|');
  261. compiled_keywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])];
  262. });
  263. }
  264. }
  265. function scoreForKeyword(keyword, providedScore) {
  266. // manual scores always win over common keywords
  267. // so you can force a score of 1 if you really insist
  268. if (providedScore)
  269. return Number(providedScore);
  270. return commonKeyword(keyword) ? 0 : 1;
  271. }
  272. function commonKeyword(word) {
  273. return COMMON_KEYWORDS.indexOf(word.toLowerCase()) != -1;
  274. }
  275. function compileLanguage(language) {
  276. function reStr(re) {
  277. return (re && re.source) || re;
  278. }
  279. function langRe(value, global) {
  280. return new RegExp(
  281. reStr(value),
  282. 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '')
  283. );
  284. }
  285. function reCountMatchGroups(re) {
  286. return (new RegExp(re.toString() + '|')).exec('').length - 1;
  287. }
  288. // joinRe logically computes regexps.join(separator), but fixes the
  289. // backreferences so they continue to match.
  290. // it also places each individual regular expression into it's own
  291. // match group, keeping track of the sequencing of those match groups
  292. // is currently an exercise for the caller. :-)
  293. function joinRe(regexps, separator) {
  294. // backreferenceRe matches an open parenthesis or backreference. To avoid
  295. // an incorrect parse, it additionally matches the following:
  296. // - [...] elements, where the meaning of parentheses and escapes change
  297. // - other escape sequences, so we do not misparse escape sequences as
  298. // interesting elements
  299. // - non-matching or lookahead parentheses, which do not capture. These
  300. // follow the '(' with a '?'.
  301. var backreferenceRe = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
  302. var numCaptures = 0;
  303. var ret = '';
  304. for (var i = 0; i < regexps.length; i++) {
  305. numCaptures += 1;
  306. var offset = numCaptures;
  307. var re = reStr(regexps[i]);
  308. if (i > 0) {
  309. ret += separator;
  310. }
  311. ret += "(";
  312. while (re.length > 0) {
  313. var match = backreferenceRe.exec(re);
  314. if (match == null) {
  315. ret += re;
  316. break;
  317. }
  318. ret += re.substring(0, match.index);
  319. re = re.substring(match.index + match[0].length);
  320. if (match[0][0] == '\\' && match[1]) {
  321. // Adjust the backreference.
  322. ret += '\\' + String(Number(match[1]) + offset);
  323. } else {
  324. ret += match[0];
  325. if (match[0] == '(') {
  326. numCaptures++;
  327. }
  328. }
  329. }
  330. ret += ")";
  331. }
  332. return ret;
  333. }
  334. function buildModeRegex(mode) {
  335. var matchIndexes = {};
  336. var matcherRe;
  337. var regexes = [];
  338. var matcher = {};
  339. var matchAt = 1;
  340. function addRule(rule, regex) {
  341. matchIndexes[matchAt] = rule;
  342. regexes.push([rule, regex]);
  343. matchAt += reCountMatchGroups(regex) + 1;
  344. }
  345. var term;
  346. for (var i=0; i < mode.contains.length; i++) {
  347. var re;
  348. term = mode.contains[i];
  349. if (term.beginKeywords) {
  350. re = '\\.?(?:' + term.begin + ')\\.?';
  351. } else {
  352. re = term.begin;
  353. }
  354. addRule(term, re);
  355. }
  356. if (mode.terminator_end)
  357. addRule("end", mode.terminator_end);
  358. if (mode.illegal)
  359. addRule("illegal", mode.illegal);
  360. var terminators = regexes.map(function(el) { return el[1]; });
  361. matcherRe = langRe(joinRe(terminators, '|'), true);
  362. matcher.lastIndex = 0;
  363. matcher.exec = function(s) {
  364. var rule;
  365. if( regexes.length === 0) return null;
  366. matcherRe.lastIndex = matcher.lastIndex;
  367. var match = matcherRe.exec(s);
  368. if (!match) { return null; }
  369. for(var i = 0; i<match.length; i++) {
  370. if (match[i] != undefined && matchIndexes["" +i] != undefined ) {
  371. rule = matchIndexes[""+i];
  372. break;
  373. }
  374. }
  375. // illegal or end match
  376. if (typeof rule === "string") {
  377. match.type = rule;
  378. match.extra = [mode.illegal, mode.terminator_end];
  379. } else {
  380. match.type = "begin";
  381. match.rule = rule;
  382. }
  383. return match;
  384. };
  385. return matcher;
  386. }
  387. function compileMode(mode, parent) {
  388. if (mode.compiled)
  389. return;
  390. mode.compiled = true;
  391. mode.keywords = mode.keywords || mode.beginKeywords;
  392. if (mode.keywords)
  393. mode.keywords = compileKeywords(mode.keywords, language.case_insensitive);
  394. mode.lexemesRe = langRe(mode.lexemes || /\w+/, true);
  395. if (parent) {
  396. if (mode.beginKeywords) {
  397. mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')\\b';
  398. }
  399. if (!mode.begin)
  400. mode.begin = /\B|\b/;
  401. mode.beginRe = langRe(mode.begin);
  402. if (mode.endSameAsBegin)
  403. mode.end = mode.begin;
  404. if (!mode.end && !mode.endsWithParent)
  405. mode.end = /\B|\b/;
  406. if (mode.end)
  407. mode.endRe = langRe(mode.end);
  408. mode.terminator_end = reStr(mode.end) || '';
  409. if (mode.endsWithParent && parent.terminator_end)
  410. mode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
  411. }
  412. if (mode.illegal)
  413. mode.illegalRe = langRe(mode.illegal);
  414. if (mode.relevance == null)
  415. mode.relevance = 1;
  416. if (!mode.contains) {
  417. mode.contains = [];
  418. }
  419. mode.contains = Array.prototype.concat.apply([], mode.contains.map(function(c) {
  420. return expand_or_clone_mode(c === 'self' ? mode : c);
  421. }));
  422. mode.contains.forEach(function(c) {compileMode(c, mode);});
  423. if (mode.starts) {
  424. compileMode(mode.starts, parent);
  425. }
  426. mode.terminators = buildModeRegex(mode);
  427. }
  428. // self is not valid at the top-level
  429. if (language.contains && language.contains.indexOf('self') != -1) {
  430. if (!SAFE_MODE) {
  431. throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
  432. } else {
  433. // silently remove the broken rule (effectively ignoring it), this has historically
  434. // been the behavior in the past, so this removal preserves compatibility with broken
  435. // grammars when running in Safe Mode
  436. language.contains = language.contains.filter(function(mode) { return mode != 'self'; });
  437. }
  438. }
  439. compileMode(language);
  440. }
  441. function hideUpgradeWarning() {
  442. if (options.hideUpgradeWarningAcceptNoSupportOrSecurityUpdates)
  443. return true;
  444. if (typeof process === "object" && typeof process.env === "object" && process.env["HLJS_HIDE_UPGRADE_WARNING"])
  445. return true;
  446. }
  447. /**
  448. * Core highlighting function.
  449. *
  450. * @param {string} languageName - the language to use for highlighting
  451. * @param {string} code - the code to highlight
  452. * @param {boolean} ignore_illegals - whether to ignore illegal matches, default is to bail
  453. * @param {array<mode>} continuation - array of continuation modes
  454. *
  455. * @returns an object that represents the result
  456. * @property {string} language - the language name
  457. * @property {number} relevance - the relevance score
  458. * @property {string} value - the highlighted HTML code
  459. * @property {mode} top - top of the current mode stack
  460. * @property {boolean} illegal - indicates whether any illegal matches were found
  461. */
  462. function highlight(languageName, code, ignore_illegals, continuation) {
  463. if (!hideUpgradeWarning()) {
  464. if (!showedUpgradeWarning) {
  465. showedUpgradeWarning = true;
  466. console.log(
  467. "Version 9 of Highlight.js has reached EOL and is no longer supported.\n" +
  468. "Please upgrade or ask whatever dependency you are using to upgrade.\n" +
  469. "https://github.com/highlightjs/highlight.js/issues/2877"
  470. );
  471. }
  472. }
  473. var codeToHighlight = code;
  474. function escapeRe(value) {
  475. return new RegExp(value.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
  476. }
  477. function endOfMode(mode, lexeme) {
  478. if (testRe(mode.endRe, lexeme)) {
  479. while (mode.endsParent && mode.parent) {
  480. mode = mode.parent;
  481. }
  482. return mode;
  483. }
  484. if (mode.endsWithParent) {
  485. return endOfMode(mode.parent, lexeme);
  486. }
  487. }
  488. function keywordMatch(mode, match) {
  489. var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0];
  490. return mode.keywords.hasOwnProperty(match_str) && mode.keywords[match_str];
  491. }
  492. function buildSpan(className, insideSpan, leaveOpen, noPrefix) {
  493. if (!leaveOpen && insideSpan === '') return '';
  494. if (!className) return insideSpan;
  495. var classPrefix = noPrefix ? '' : options.classPrefix,
  496. openSpan = '<span class="' + classPrefix,
  497. closeSpan = leaveOpen ? '' : spanEndTag;
  498. openSpan += className + '">';
  499. return openSpan + insideSpan + closeSpan;
  500. }
  501. function processKeywords() {
  502. var keyword_match, last_index, match, result;
  503. if (!top.keywords)
  504. return escape(mode_buffer);
  505. result = '';
  506. last_index = 0;
  507. top.lexemesRe.lastIndex = 0;
  508. match = top.lexemesRe.exec(mode_buffer);
  509. while (match) {
  510. result += escape(mode_buffer.substring(last_index, match.index));
  511. keyword_match = keywordMatch(top, match);
  512. if (keyword_match) {
  513. relevance += keyword_match[1];
  514. result += buildSpan(keyword_match[0], escape(match[0]));
  515. } else {
  516. result += escape(match[0]);
  517. }
  518. last_index = top.lexemesRe.lastIndex;
  519. match = top.lexemesRe.exec(mode_buffer);
  520. }
  521. return result + escape(mode_buffer.substr(last_index));
  522. }
  523. function processSubLanguage() {
  524. var explicit = typeof top.subLanguage === 'string';
  525. if (explicit && !languages[top.subLanguage]) {
  526. return escape(mode_buffer);
  527. }
  528. var result = explicit ?
  529. highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]) :
  530. highlightAuto(mode_buffer, top.subLanguage.length ? top.subLanguage : undefined);
  531. // Counting embedded language score towards the host language may be disabled
  532. // with zeroing the containing mode relevance. Use case in point is Markdown that
  533. // allows XML everywhere and makes every XML snippet to have a much larger Markdown
  534. // score.
  535. if (top.relevance > 0) {
  536. relevance += result.relevance;
  537. }
  538. if (explicit) {
  539. continuations[top.subLanguage] = result.top;
  540. }
  541. return buildSpan(result.language, result.value, false, true);
  542. }
  543. function processBuffer() {
  544. result += (top.subLanguage != null ? processSubLanguage() : processKeywords());
  545. mode_buffer = '';
  546. }
  547. function startNewMode(mode) {
  548. result += mode.className? buildSpan(mode.className, '', true): '';
  549. top = Object.create(mode, {parent: {value: top}});
  550. }
  551. function doBeginMatch(match) {
  552. var lexeme = match[0];
  553. var new_mode = match.rule;
  554. if (new_mode && new_mode.endSameAsBegin) {
  555. new_mode.endRe = escapeRe( lexeme );
  556. }
  557. if (new_mode.skip) {
  558. mode_buffer += lexeme;
  559. } else {
  560. if (new_mode.excludeBegin) {
  561. mode_buffer += lexeme;
  562. }
  563. processBuffer();
  564. if (!new_mode.returnBegin && !new_mode.excludeBegin) {
  565. mode_buffer = lexeme;
  566. }
  567. }
  568. startNewMode(new_mode);
  569. return new_mode.returnBegin ? 0 : lexeme.length;
  570. }
  571. function doEndMatch(match) {
  572. var lexeme = match[0];
  573. var matchPlusRemainder = codeToHighlight.substr(match.index);
  574. var end_mode = endOfMode(top, matchPlusRemainder);
  575. if (!end_mode) { return; }
  576. var origin = top;
  577. if (origin.skip) {
  578. mode_buffer += lexeme;
  579. } else {
  580. if (!(origin.returnEnd || origin.excludeEnd)) {
  581. mode_buffer += lexeme;
  582. }
  583. processBuffer();
  584. if (origin.excludeEnd) {
  585. mode_buffer = lexeme;
  586. }
  587. }
  588. do {
  589. if (top.className) {
  590. result += spanEndTag;
  591. }
  592. if (!top.skip && !top.subLanguage) {
  593. relevance += top.relevance;
  594. }
  595. top = top.parent;
  596. } while (top !== end_mode.parent);
  597. if (end_mode.starts) {
  598. if (end_mode.endSameAsBegin) {
  599. end_mode.starts.endRe = end_mode.endRe;
  600. }
  601. startNewMode(end_mode.starts);
  602. }
  603. return origin.returnEnd ? 0 : lexeme.length;
  604. }
  605. var lastMatch = {};
  606. function processLexeme(text_before_match, match) {
  607. var lexeme = match && match[0];
  608. // add non-matched text to the current mode buffer
  609. mode_buffer += text_before_match;
  610. if (lexeme == null) {
  611. processBuffer();
  612. return 0;
  613. }
  614. // we've found a 0 width match and we're stuck, so we need to advance
  615. // this happens when we have badly behaved rules that have optional matchers to the degree that
  616. // sometimes they can end up matching nothing at all
  617. // Ref: https://github.com/highlightjs/highlight.js/issues/2140
  618. if (lastMatch.type=="begin" && match.type=="end" && lastMatch.index == match.index && lexeme === "") {
  619. // spit the "skipped" character that our regex choked on back into the output sequence
  620. mode_buffer += codeToHighlight.slice(match.index, match.index + 1);
  621. return 1;
  622. }
  623. // edge case for when illegal matches $ (end of line) which is technically
  624. // a 0 width match but not a begin/end match so it's not caught by the
  625. // first handler (when ignoreIllegals is true)
  626. // https://github.com/highlightjs/highlight.js/issues/2522
  627. if (lastMatch.type==="illegal" && lexeme === "") {
  628. mode_buffer += codeToHighlight.slice(match.index, match.index + 1);
  629. return 1;
  630. }
  631. lastMatch = match;
  632. if (match.type==="begin") {
  633. return doBeginMatch(match);
  634. } else if (match.type==="illegal" && !ignore_illegals) {
  635. // illegal match, we do not continue processing
  636. throw new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '<unnamed>') + '"');
  637. } else if (match.type==="end") {
  638. var processed = doEndMatch(match);
  639. if (processed != undefined)
  640. return processed;
  641. }
  642. /*
  643. Why might be find ourselves here? Only one occasion now. An end match that was
  644. triggered but could not be completed. When might this happen? When an `endSameasBegin`
  645. rule sets the end rule to a specific match. Since the overall mode termination rule that's
  646. being used to scan the text isn't recompiled that means that any match that LOOKS like
  647. the end (but is not, because it is not an exact match to the beginning) will
  648. end up here. A definite end match, but when `doEndMatch` tries to "reapply"
  649. the end rule and fails to match, we wind up here, and just silently ignore the end.
  650. This causes no real harm other than stopping a few times too many.
  651. */
  652. mode_buffer += lexeme;
  653. return lexeme.length;
  654. }
  655. var language = getLanguage(languageName);
  656. if (!language) {
  657. console.error(LANGUAGE_NOT_FOUND.replace("{}", languageName));
  658. throw new Error('Unknown language: "' + languageName + '"');
  659. }
  660. compileLanguage(language);
  661. var top = continuation || language;
  662. var continuations = {}; // keep continuations for sub-languages
  663. var result = '', current;
  664. for(current = top; current !== language; current = current.parent) {
  665. if (current.className) {
  666. result = buildSpan(current.className, '', true) + result;
  667. }
  668. }
  669. var mode_buffer = '';
  670. var relevance = 0;
  671. try {
  672. var match, count, index = 0;
  673. while (true) {
  674. top.terminators.lastIndex = index;
  675. match = top.terminators.exec(codeToHighlight);
  676. if (!match)
  677. break;
  678. count = processLexeme(codeToHighlight.substring(index, match.index), match);
  679. index = match.index + count;
  680. }
  681. processLexeme(codeToHighlight.substr(index));
  682. for(current = top; current.parent; current = current.parent) { // close dangling modes
  683. if (current.className) {
  684. result += spanEndTag;
  685. }
  686. }
  687. return {
  688. relevance: relevance,
  689. value: result,
  690. illegal:false,
  691. language: languageName,
  692. top: top
  693. };
  694. } catch (err) {
  695. if (err.message && err.message.indexOf('Illegal') !== -1) {
  696. return {
  697. illegal: true,
  698. relevance: 0,
  699. value: escape(codeToHighlight)
  700. };
  701. } else if (SAFE_MODE) {
  702. return {
  703. relevance: 0,
  704. value: escape(codeToHighlight),
  705. language: languageName,
  706. top: top,
  707. errorRaised: err
  708. };
  709. } else {
  710. throw err;
  711. }
  712. }
  713. }
  714. /*
  715. Highlighting with language detection. Accepts a string with the code to
  716. highlight. Returns an object with the following properties:
  717. - language (detected language)
  718. - relevance (int)
  719. - value (an HTML string with highlighting markup)
  720. - second_best (object with the same structure for second-best heuristically
  721. detected language, may be absent)
  722. */
  723. function highlightAuto(code, languageSubset) {
  724. languageSubset = languageSubset || options.languages || objectKeys(languages);
  725. var result = {
  726. relevance: 0,
  727. value: escape(code)
  728. };
  729. var second_best = result;
  730. languageSubset.filter(getLanguage).filter(autoDetection).forEach(function(name) {
  731. var current = highlight(name, code, false);
  732. current.language = name;
  733. if (current.relevance > second_best.relevance) {
  734. second_best = current;
  735. }
  736. if (current.relevance > result.relevance) {
  737. second_best = result;
  738. result = current;
  739. }
  740. });
  741. if (second_best.language) {
  742. result.second_best = second_best;
  743. }
  744. return result;
  745. }
  746. /*
  747. Post-processing of the highlighted markup:
  748. - replace TABs with something more useful
  749. - replace real line-breaks with '<br>' for non-pre containers
  750. */
  751. function fixMarkup(value) {
  752. if (!(options.tabReplace || options.useBR)) {
  753. return value;
  754. }
  755. return value.replace(fixMarkupRe, function(match, p1) {
  756. if (options.useBR && match === '\n') {
  757. return '<br>';
  758. } else if (options.tabReplace) {
  759. return p1.replace(/\t/g, options.tabReplace);
  760. }
  761. return '';
  762. });
  763. }
  764. function buildClassName(prevClassName, currentLang, resultLang) {
  765. var language = currentLang ? aliases[currentLang] : resultLang,
  766. result = [prevClassName.trim()];
  767. if (!prevClassName.match(/\bhljs\b/)) {
  768. result.push('hljs');
  769. }
  770. if (prevClassName.indexOf(language) === -1) {
  771. result.push(language);
  772. }
  773. return result.join(' ').trim();
  774. }
  775. /*
  776. Applies highlighting to a DOM node containing code. Accepts a DOM node and
  777. two optional parameters for fixMarkup.
  778. */
  779. function highlightBlock(block) {
  780. var node, originalStream, result, resultNode, text;
  781. var language = blockLanguage(block);
  782. if (isNotHighlighted(language))
  783. return;
  784. if (options.useBR) {
  785. node = document.createElement('div');
  786. node.innerHTML = block.innerHTML.replace(/\n/g, '').replace(/<br[ \/]*>/g, '\n');
  787. } else {
  788. node = block;
  789. }
  790. text = node.textContent;
  791. result = language ? highlight(language, text, true) : highlightAuto(text);
  792. originalStream = nodeStream(node);
  793. if (originalStream.length) {
  794. resultNode = document.createElement('div');
  795. resultNode.innerHTML = result.value;
  796. result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
  797. }
  798. result.value = fixMarkup(result.value);
  799. block.innerHTML = result.value;
  800. block.className = buildClassName(block.className, language, result.language);
  801. block.result = {
  802. language: result.language,
  803. re: result.relevance
  804. };
  805. if (result.second_best) {
  806. block.second_best = {
  807. language: result.second_best.language,
  808. re: result.second_best.relevance
  809. };
  810. }
  811. }
  812. /*
  813. Updates highlight.js global options with values passed in the form of an object.
  814. */
  815. function configure(user_options) {
  816. options = inherit(options, user_options);
  817. }
  818. /*
  819. Applies highlighting to all <pre><code>..</code></pre> blocks on a page.
  820. */
  821. function initHighlighting() {
  822. if (initHighlighting.called)
  823. return;
  824. initHighlighting.called = true;
  825. var blocks = document.querySelectorAll('pre code');
  826. ArrayProto.forEach.call(blocks, highlightBlock);
  827. }
  828. /*
  829. Attaches highlighting to the page load event.
  830. */
  831. function initHighlightingOnLoad() {
  832. window.addEventListener('DOMContentLoaded', initHighlighting, false);
  833. window.addEventListener('load', initHighlighting, false);
  834. }
  835. var PLAINTEXT_LANGUAGE = { disableAutodetect: true };
  836. function registerLanguage(name, language) {
  837. var lang;
  838. try { lang = language(hljs); }
  839. catch (error) {
  840. console.error("Language definition for '{}' could not be registered.".replace("{}", name));
  841. // hard or soft error
  842. if (!SAFE_MODE) { throw error; } else { console.error(error); }
  843. // languages that have serious errors are replaced with essentially a
  844. // "plaintext" stand-in so that the code blocks will still get normal
  845. // css classes applied to them - and one bad language won't break the
  846. // entire highlighter
  847. lang = PLAINTEXT_LANGUAGE;
  848. }
  849. languages[name] = lang;
  850. restoreLanguageApi(lang);
  851. lang.rawDefinition = language.bind(null,hljs);
  852. if (lang.aliases) {
  853. lang.aliases.forEach(function(alias) {aliases[alias] = name;});
  854. }
  855. }
  856. function listLanguages() {
  857. return objectKeys(languages);
  858. }
  859. /*
  860. intended usage: When one language truly requires another
  861. Unlike `getLanguage`, this will throw when the requested language
  862. is not available.
  863. */
  864. function requireLanguage(name) {
  865. var lang = getLanguage(name);
  866. if (lang) { return lang; }
  867. var err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}',name));
  868. throw err;
  869. }
  870. function getLanguage(name) {
  871. name = (name || '').toLowerCase();
  872. return languages[name] || languages[aliases[name]];
  873. }
  874. function autoDetection(name) {
  875. var lang = getLanguage(name);
  876. return lang && !lang.disableAutodetect;
  877. }
  878. /* Interface definition */
  879. hljs.highlight = highlight;
  880. hljs.highlightAuto = highlightAuto;
  881. hljs.fixMarkup = fixMarkup;
  882. hljs.highlightBlock = highlightBlock;
  883. hljs.configure = configure;
  884. hljs.initHighlighting = initHighlighting;
  885. hljs.initHighlightingOnLoad = initHighlightingOnLoad;
  886. hljs.registerLanguage = registerLanguage;
  887. hljs.listLanguages = listLanguages;
  888. hljs.getLanguage = getLanguage;
  889. hljs.requireLanguage = requireLanguage;
  890. hljs.autoDetection = autoDetection;
  891. hljs.inherit = inherit;
  892. hljs.debugMode = function() { SAFE_MODE = false; }
  893. // Common regexps
  894. hljs.IDENT_RE = '[a-zA-Z]\\w*';
  895. hljs.UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*';
  896. hljs.NUMBER_RE = '\\b\\d+(\\.\\d+)?';
  897. hljs.C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float
  898. hljs.BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b...
  899. hljs.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
  900. // Common modes
  901. hljs.BACKSLASH_ESCAPE = {
  902. begin: '\\\\[\\s\\S]', relevance: 0
  903. };
  904. hljs.APOS_STRING_MODE = {
  905. className: 'string',
  906. begin: '\'', end: '\'',
  907. illegal: '\\n',
  908. contains: [hljs.BACKSLASH_ESCAPE]
  909. };
  910. hljs.QUOTE_STRING_MODE = {
  911. className: 'string',
  912. begin: '"', end: '"',
  913. illegal: '\\n',
  914. contains: [hljs.BACKSLASH_ESCAPE]
  915. };
  916. hljs.PHRASAL_WORDS_MODE = {
  917. 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/
  918. };
  919. hljs.COMMENT = function (begin, end, inherits) {
  920. var mode = hljs.inherit(
  921. {
  922. className: 'comment',
  923. begin: begin, end: end,
  924. contains: []
  925. },
  926. inherits || {}
  927. );
  928. mode.contains.push(hljs.PHRASAL_WORDS_MODE);
  929. mode.contains.push({
  930. className: 'doctag',
  931. begin: '(?:TODO|FIXME|NOTE|BUG|XXX):',
  932. relevance: 0
  933. });
  934. return mode;
  935. };
  936. hljs.C_LINE_COMMENT_MODE = hljs.COMMENT('//', '$');
  937. hljs.C_BLOCK_COMMENT_MODE = hljs.COMMENT('/\\*', '\\*/');
  938. hljs.HASH_COMMENT_MODE = hljs.COMMENT('#', '$');
  939. hljs.NUMBER_MODE = {
  940. className: 'number',
  941. begin: hljs.NUMBER_RE,
  942. relevance: 0
  943. };
  944. hljs.C_NUMBER_MODE = {
  945. className: 'number',
  946. begin: hljs.C_NUMBER_RE,
  947. relevance: 0
  948. };
  949. hljs.BINARY_NUMBER_MODE = {
  950. className: 'number',
  951. begin: hljs.BINARY_NUMBER_RE,
  952. relevance: 0
  953. };
  954. hljs.CSS_NUMBER_MODE = {
  955. className: 'number',
  956. begin: hljs.NUMBER_RE + '(' +
  957. '%|em|ex|ch|rem' +
  958. '|vw|vh|vmin|vmax' +
  959. '|cm|mm|in|pt|pc|px' +
  960. '|deg|grad|rad|turn' +
  961. '|s|ms' +
  962. '|Hz|kHz' +
  963. '|dpi|dpcm|dppx' +
  964. ')?',
  965. relevance: 0
  966. };
  967. hljs.REGEXP_MODE = {
  968. className: 'regexp',
  969. begin: /\//, end: /\/[gimuy]*/,
  970. illegal: /\n/,
  971. contains: [
  972. hljs.BACKSLASH_ESCAPE,
  973. {
  974. begin: /\[/, end: /\]/,
  975. relevance: 0,
  976. contains: [hljs.BACKSLASH_ESCAPE]
  977. }
  978. ]
  979. };
  980. hljs.TITLE_MODE = {
  981. className: 'title',
  982. begin: hljs.IDENT_RE,
  983. relevance: 0
  984. };
  985. hljs.UNDERSCORE_TITLE_MODE = {
  986. className: 'title',
  987. begin: hljs.UNDERSCORE_IDENT_RE,
  988. relevance: 0
  989. };
  990. hljs.METHOD_GUARD = {
  991. // excludes method names from keyword processing
  992. begin: '\\.\\s*' + hljs.UNDERSCORE_IDENT_RE,
  993. relevance: 0
  994. };
  995. var constants = [
  996. hljs.BACKSLASH_ESCAPE,
  997. hljs.APOS_STRING_MODE,
  998. hljs.QUOTE_STRING_MODE,
  999. hljs.PHRASAL_WORDS_MODE,
  1000. hljs.COMMENT,
  1001. hljs.C_LINE_COMMENT_MODE,
  1002. hljs.C_BLOCK_COMMENT_MODE,
  1003. hljs.HASH_COMMENT_MODE,
  1004. hljs.NUMBER_MODE,
  1005. hljs.C_NUMBER_MODE,
  1006. hljs.BINARY_NUMBER_MODE,
  1007. hljs.CSS_NUMBER_MODE,
  1008. hljs.REGEXP_MODE,
  1009. hljs.TITLE_MODE,
  1010. hljs.UNDERSCORE_TITLE_MODE,
  1011. hljs.METHOD_GUARD
  1012. ]
  1013. constants.forEach(function(obj) { deepFreeze(obj); });
  1014. // https://github.com/substack/deep-freeze/blob/master/index.js
  1015. function deepFreeze (o) {
  1016. Object.freeze(o);
  1017. var objIsFunction = typeof o === 'function';
  1018. Object.getOwnPropertyNames(o).forEach(function (prop) {
  1019. if (o.hasOwnProperty(prop)
  1020. && o[prop] !== null
  1021. && (typeof o[prop] === "object" || typeof o[prop] === "function")
  1022. // IE11 fix: https://github.com/highlightjs/highlight.js/issues/2318
  1023. // TODO: remove in the future
  1024. && (objIsFunction ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments' : true)
  1025. && !Object.isFrozen(o[prop])) {
  1026. deepFreeze(o[prop]);
  1027. }
  1028. });
  1029. return o;
  1030. };
  1031. return hljs;
  1032. }));