@tiptap/extension-code-block-lowlight 2.0.0-beta.20 → 2.0.0-beta.200

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,15 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/extension-code-block'), require('prosemirror-state'), require('prosemirror-view'), require('@tiptap/core')) :
3
- typeof define === 'function' && define.amd ? define(['exports', '@tiptap/extension-code-block', 'prosemirror-state', 'prosemirror-view', '@tiptap/core'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global['@tiptap/extension-code-block-lowlight'] = {}, global.CodeBlock, global.prosemirrorState, global.prosemirrorView, global.core$2));
5
- }(this, (function (exports, CodeBlock, prosemirrorState, prosemirrorView, core$2) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/extension-code-block'), require('@tiptap/core'), require('prosemirror-state'), require('prosemirror-view')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@tiptap/extension-code-block', '@tiptap/core', 'prosemirror-state', 'prosemirror-view'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/extension-code-block-lowlight"] = {}, global.CodeBlock, global.core$1, global.prosemirrorState, global.prosemirrorView));
5
+ })(this, (function (exports, CodeBlock, core$1, prosemirrorState, prosemirrorView) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
9
9
  var CodeBlock__default = /*#__PURE__*/_interopDefaultLegacy(CodeBlock);
10
10
 
11
+ var deepFreezeEs6 = {exports: {}};
12
+
11
13
  function deepFreeze(obj) {
12
14
  if (obj instanceof Map) {
13
15
  obj.clear = obj.delete = obj.set = function () {
@@ -34,11 +36,13 @@
34
36
  return obj;
35
37
  }
36
38
 
37
- var deepFreezeEs6 = deepFreeze;
38
- var _default = deepFreeze;
39
- deepFreezeEs6.default = _default;
39
+ deepFreezeEs6.exports = deepFreeze;
40
+ deepFreezeEs6.exports.default = deepFreeze;
40
41
 
42
+ /** @typedef {import('highlight.js').CallbackResponse} CallbackResponse */
43
+ /** @typedef {import('highlight.js').CompiledMode} CompiledMode */
41
44
  /** @implements CallbackResponse */
45
+
42
46
  class Response {
43
47
  /**
44
48
  * @param {CompiledMode} mode
@@ -77,7 +81,7 @@
77
81
  * @param {Record<string,any>[]} objects
78
82
  * @returns {T} a single new object
79
83
  */
80
- function inherit(original, ...objects) {
84
+ function inherit$1(original, ...objects) {
81
85
  /** @type Record<string,any> */
82
86
  const result = Object.create(null);
83
87
 
@@ -100,7 +104,7 @@
100
104
  * @property {() => string} value
101
105
  */
102
106
 
103
- /** @typedef {{kind?: string, sublanguage?: boolean}} Node */
107
+ /** @typedef {{scope?: string, language?: string, sublanguage?: boolean}} Node */
104
108
  /** @typedef {{walk: (r: Renderer) => void}} Tree */
105
109
  /** */
106
110
 
@@ -111,7 +115,25 @@
111
115
  *
112
116
  * @param {Node} node */
113
117
  const emitsWrappingTags = (node) => {
114
- return !!node.kind;
118
+ // rarely we can have a sublanguage where language is undefined
119
+ // TODO: track down why
120
+ return !!node.scope || (node.sublanguage && node.language);
121
+ };
122
+
123
+ /**
124
+ *
125
+ * @param {string} name
126
+ * @param {{prefix:string}} options
127
+ */
128
+ const scopeToCSSClass = (name, { prefix }) => {
129
+ if (name.includes(".")) {
130
+ const pieces = name.split(".");
131
+ return [
132
+ `${prefix}${pieces.shift()}`,
133
+ ...(pieces.map((x, i) => `${x}${"_".repeat(i + 1)}`))
134
+ ].join(" ");
135
+ }
136
+ return `${prefix}${name}`;
115
137
  };
116
138
 
117
139
  /** @type {Renderer} */
@@ -143,9 +165,11 @@
143
165
  openNode(node) {
144
166
  if (!emitsWrappingTags(node)) return;
145
167
 
146
- let className = node.kind;
147
- if (!node.sublanguage) {
148
- className = `${this.classPrefix}${className}`;
168
+ let className = "";
169
+ if (node.sublanguage) {
170
+ className = `language-${node.language}`;
171
+ } else {
172
+ className = scopeToCSSClass(node.scope, { prefix: this.classPrefix });
149
173
  }
150
174
  this.span(className);
151
175
  }
@@ -178,14 +202,23 @@
178
202
  }
179
203
  }
180
204
 
181
- /** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} | string} Node */
182
- /** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} } DataNode */
205
+ /** @typedef {{scope?: string, language?: string, sublanguage?: boolean, children: Node[]} | string} Node */
206
+ /** @typedef {{scope?: string, language?: string, sublanguage?: boolean, children: Node[]} } DataNode */
207
+ /** @typedef {import('highlight.js').Emitter} Emitter */
183
208
  /** */
184
209
 
210
+ /** @returns {DataNode} */
211
+ const newNode = (opts = {}) => {
212
+ /** @type DataNode */
213
+ const result = { children: [] };
214
+ Object.assign(result, opts);
215
+ return result;
216
+ };
217
+
185
218
  class TokenTree {
186
219
  constructor() {
187
220
  /** @type DataNode */
188
- this.rootNode = { children: [] };
221
+ this.rootNode = newNode();
189
222
  this.stack = [this.rootNode];
190
223
  }
191
224
 
@@ -200,10 +233,10 @@
200
233
  this.top.children.push(node);
201
234
  }
202
235
 
203
- /** @param {string} kind */
204
- openNode(kind) {
236
+ /** @param {string} scope */
237
+ openNode(scope) {
205
238
  /** @type Node */
206
- const node = { kind, children: [] };
239
+ const node = newNode({ scope });
207
240
  this.add(node);
208
241
  this.stack.push(node);
209
242
  }
@@ -275,11 +308,11 @@
275
308
 
276
309
  Minimal interface:
277
310
 
278
- - addKeyword(text, kind)
311
+ - addKeyword(text, scope)
279
312
  - addText(text)
280
313
  - addSublanguage(emitter, subLanguageName)
281
314
  - finalize()
282
- - openNode(kind)
315
+ - openNode(scope)
283
316
  - closeNode()
284
317
  - closeAllNodes()
285
318
  - toHTML()
@@ -300,12 +333,12 @@
300
333
 
301
334
  /**
302
335
  * @param {string} text
303
- * @param {string} kind
336
+ * @param {string} scope
304
337
  */
305
- addKeyword(text, kind) {
338
+ addKeyword(text, scope) {
306
339
  if (text === "") { return; }
307
340
 
308
- this.openNode(kind);
341
+ this.openNode(scope);
309
342
  this.addText(text);
310
343
  this.closeNode();
311
344
  }
@@ -326,8 +359,8 @@
326
359
  addSublanguage(emitter, name) {
327
360
  /** @type DataNode */
328
361
  const node = emitter.root;
329
- node.kind = name;
330
362
  node.sublanguage = true;
363
+ node.language = name;
331
364
  this.add(node);
332
365
  }
333
366
 
@@ -345,9 +378,6 @@
345
378
  * @param {string} value
346
379
  * @returns {RegExp}
347
380
  * */
348
- function escape(value) {
349
- return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
350
- }
351
381
 
352
382
  /**
353
383
  * @param {RegExp | string } re
@@ -360,6 +390,30 @@
360
390
  return re.source;
361
391
  }
362
392
 
393
+ /**
394
+ * @param {RegExp | string } re
395
+ * @returns {string}
396
+ */
397
+ function lookahead(re) {
398
+ return concat('(?=', re, ')');
399
+ }
400
+
401
+ /**
402
+ * @param {RegExp | string } re
403
+ * @returns {string}
404
+ */
405
+ function anyNumberOfTimes(re) {
406
+ return concat('(?:', re, ')*');
407
+ }
408
+
409
+ /**
410
+ * @param {RegExp | string } re
411
+ * @returns {string}
412
+ */
413
+ function optional(re) {
414
+ return concat('(?:', re, ')?');
415
+ }
416
+
363
417
  /**
364
418
  * @param {...(RegExp | string) } args
365
419
  * @returns {string}
@@ -369,20 +423,41 @@
369
423
  return joined;
370
424
  }
371
425
 
426
+ /**
427
+ * @param { Array<string | RegExp | Object> } args
428
+ * @returns {object}
429
+ */
430
+ function stripOptionsFromArgs(args) {
431
+ const opts = args[args.length - 1];
432
+
433
+ if (typeof opts === 'object' && opts.constructor === Object) {
434
+ args.splice(args.length - 1, 1);
435
+ return opts;
436
+ } else {
437
+ return {};
438
+ }
439
+ }
440
+
441
+ /** @typedef { {capture?: boolean} } RegexEitherOptions */
442
+
372
443
  /**
373
444
  * Any of the passed expresssions may match
374
445
  *
375
446
  * Creates a huge this | this | that | that match
376
- * @param {(RegExp | string)[] } args
447
+ * @param {(RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]} args
377
448
  * @returns {string}
378
449
  */
379
450
  function either(...args) {
380
- const joined = '(' + args.map((x) => source(x)).join("|") + ")";
451
+ /** @type { object & {capture?: boolean} } */
452
+ const opts = stripOptionsFromArgs(args);
453
+ const joined = '('
454
+ + (opts.capture ? "" : "?:")
455
+ + args.map((x) => source(x)).join("|") + ")";
381
456
  return joined;
382
457
  }
383
458
 
384
459
  /**
385
- * @param {RegExp} re
460
+ * @param {RegExp | string} re
386
461
  * @returns {number}
387
462
  */
388
463
  function countMatchGroups(re) {
@@ -408,6 +483,7 @@
408
483
  // follow the '(' with a '?'.
409
484
  const BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
410
485
 
486
+ // **INTERNAL** Not intended for outside usage
411
487
  // join logically computes regexps.join(separator), but fixes the
412
488
  // backreferences so they continue to match.
413
489
  // it also places each individual regular expression into it's own
@@ -415,10 +491,10 @@
415
491
  // is currently an exercise for the caller. :-)
416
492
  /**
417
493
  * @param {(string | RegExp)[]} regexps
418
- * @param {string} separator
494
+ * @param {{joinWith: string}} opts
419
495
  * @returns {string}
420
496
  */
421
- function join(regexps, separator = "|") {
497
+ function _rewriteBackreferences(regexps, { joinWith }) {
422
498
  let numCaptures = 0;
423
499
 
424
500
  return regexps.map((regex) => {
@@ -446,9 +522,12 @@
446
522
  }
447
523
  }
448
524
  return out;
449
- }).map(re => `(${re})`).join(separator);
525
+ }).map(re => `(${re})`).join(joinWith);
450
526
  }
451
527
 
528
+ /** @typedef {import('highlight.js').Mode} Mode */
529
+ /** @typedef {import('highlight.js').ModeCallback} ModeCallback */
530
+
452
531
  // Common regexps
453
532
  const MATCH_NOTHING_RE = /\b\B/;
454
533
  const IDENT_RE = '[a-zA-Z]\\w*';
@@ -470,8 +549,8 @@
470
549
  opts.binary,
471
550
  /\b.*/);
472
551
  }
473
- return inherit({
474
- className: 'meta',
552
+ return inherit$1({
553
+ scope: 'meta',
475
554
  begin: beginShebang,
476
555
  end: /$/,
477
556
  relevance: 0,
@@ -487,14 +566,14 @@
487
566
  begin: '\\\\[\\s\\S]', relevance: 0
488
567
  };
489
568
  const APOS_STRING_MODE = {
490
- className: 'string',
569
+ scope: 'string',
491
570
  begin: '\'',
492
571
  end: '\'',
493
572
  illegal: '\\n',
494
573
  contains: [BACKSLASH_ESCAPE]
495
574
  };
496
575
  const QUOTE_STRING_MODE = {
497
- className: 'string',
576
+ scope: 'string',
498
577
  begin: '"',
499
578
  end: '"',
500
579
  illegal: '\\n',
@@ -512,54 +591,88 @@
512
591
  * @returns {Partial<Mode>}
513
592
  */
514
593
  const COMMENT = function(begin, end, modeOptions = {}) {
515
- const mode = inherit(
594
+ const mode = inherit$1(
516
595
  {
517
- className: 'comment',
596
+ scope: 'comment',
518
597
  begin,
519
598
  end,
520
599
  contains: []
521
600
  },
522
601
  modeOptions
523
602
  );
524
- mode.contains.push(PHRASAL_WORDS_MODE);
525
603
  mode.contains.push({
526
- className: 'doctag',
527
- begin: '(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):',
604
+ scope: 'doctag',
605
+ // hack to avoid the space from being included. the space is necessary to
606
+ // match here to prevent the plain text rule below from gobbling up doctags
607
+ begin: '[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)',
608
+ end: /(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,
609
+ excludeBegin: true,
528
610
  relevance: 0
529
611
  });
612
+ const ENGLISH_WORD = either(
613
+ // list of common 1 and 2 letter words in English
614
+ "I",
615
+ "a",
616
+ "is",
617
+ "so",
618
+ "us",
619
+ "to",
620
+ "at",
621
+ "if",
622
+ "in",
623
+ "it",
624
+ "on",
625
+ // note: this is not an exhaustive list of contractions, just popular ones
626
+ /[A-Za-z]+['](d|ve|re|ll|t|s|n)/, // contractions - can't we'd they're let's, etc
627
+ /[A-Za-z]+[-][a-z]+/, // `no-way`, etc.
628
+ /[A-Za-z][a-z]{2,}/ // allow capitalized words at beginning of sentences
629
+ );
630
+ // looking like plain text, more likely to be a comment
631
+ mode.contains.push(
632
+ {
633
+ // TODO: how to include ", (, ) without breaking grammars that use these for
634
+ // comment delimiters?
635
+ // begin: /[ ]+([()"]?([A-Za-z'-]{3,}|is|a|I|so|us|[tT][oO]|at|if|in|it|on)[.]?[()":]?([.][ ]|[ ]|\))){3}/
636
+ // ---
637
+
638
+ // this tries to find sequences of 3 english words in a row (without any
639
+ // "programming" type syntax) this gives us a strong signal that we've
640
+ // TRULY found a comment - vs perhaps scanning with the wrong language.
641
+ // It's possible to find something that LOOKS like the start of the
642
+ // comment - but then if there is no readable text - good chance it is a
643
+ // false match and not a comment.
644
+ //
645
+ // for a visual example please see:
646
+ // https://github.com/highlightjs/highlight.js/issues/2827
647
+
648
+ begin: concat(
649
+ /[ ]+/, // necessary to prevent us gobbling up doctags like /* @author Bob Mcgill */
650
+ '(',
651
+ ENGLISH_WORD,
652
+ /[.]?[:]?([.][ ]|[ ])/,
653
+ '){3}') // look for 3 words in a row
654
+ }
655
+ );
530
656
  return mode;
531
657
  };
532
658
  const C_LINE_COMMENT_MODE = COMMENT('//', '$');
533
659
  const C_BLOCK_COMMENT_MODE = COMMENT('/\\*', '\\*/');
534
660
  const HASH_COMMENT_MODE = COMMENT('#', '$');
535
661
  const NUMBER_MODE = {
536
- className: 'number',
662
+ scope: 'number',
537
663
  begin: NUMBER_RE,
538
664
  relevance: 0
539
665
  };
540
666
  const C_NUMBER_MODE = {
541
- className: 'number',
667
+ scope: 'number',
542
668
  begin: C_NUMBER_RE,
543
669
  relevance: 0
544
670
  };
545
671
  const BINARY_NUMBER_MODE = {
546
- className: 'number',
672
+ scope: 'number',
547
673
  begin: BINARY_NUMBER_RE,
548
674
  relevance: 0
549
675
  };
550
- const CSS_NUMBER_MODE = {
551
- className: 'number',
552
- begin: NUMBER_RE + '(' +
553
- '%|em|ex|ch|rem' +
554
- '|vw|vh|vmin|vmax' +
555
- '|cm|mm|in|pt|pc|px' +
556
- '|deg|grad|rad|turn' +
557
- '|s|ms' +
558
- '|Hz|kHz' +
559
- '|dpi|dpcm|dppx' +
560
- ')?',
561
- relevance: 0
562
- };
563
676
  const REGEXP_MODE = {
564
677
  // this outer rule makes sure we actually have a WHOLE regex and not simply
565
678
  // an expression such as:
@@ -569,7 +682,7 @@
569
682
  // (which will then blow up when regex's `illegal` sees the newline)
570
683
  begin: /(?=\/[^/\n]*\/)/,
571
684
  contains: [{
572
- className: 'regexp',
685
+ scope: 'regexp',
573
686
  begin: /\//,
574
687
  end: /\/[gimuy]*/,
575
688
  illegal: /\n/,
@@ -585,12 +698,12 @@
585
698
  }]
586
699
  };
587
700
  const TITLE_MODE = {
588
- className: 'title',
701
+ scope: 'title',
589
702
  begin: IDENT_RE,
590
703
  relevance: 0
591
704
  };
592
705
  const UNDERSCORE_TITLE_MODE = {
593
- className: 'title',
706
+ scope: 'title',
594
707
  begin: UNDERSCORE_IDENT_RE,
595
708
  relevance: 0
596
709
  };
@@ -638,7 +751,6 @@
638
751
  NUMBER_MODE: NUMBER_MODE,
639
752
  C_NUMBER_MODE: C_NUMBER_MODE,
640
753
  BINARY_NUMBER_MODE: BINARY_NUMBER_MODE,
641
- CSS_NUMBER_MODE: CSS_NUMBER_MODE,
642
754
  REGEXP_MODE: REGEXP_MODE,
643
755
  TITLE_MODE: TITLE_MODE,
644
756
  UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE,
@@ -646,6 +758,11 @@
646
758
  END_SAME_AS_BEGIN: END_SAME_AS_BEGIN
647
759
  });
648
760
 
761
+ /**
762
+ @typedef {import('highlight.js').CallbackResponse} CallbackResponse
763
+ @typedef {import('highlight.js').CompilerExt} CompilerExt
764
+ */
765
+
649
766
  // Grammar extensions / plugins
650
767
  // See: https://github.com/highlightjs/highlight.js/issues/2833
651
768
 
@@ -670,13 +787,24 @@
670
787
  * @param {RegExpMatchArray} match
671
788
  * @param {CallbackResponse} response
672
789
  */
673
- function skipIfhasPrecedingDot(match, response) {
790
+ function skipIfHasPrecedingDot(match, response) {
674
791
  const before = match.input[match.index - 1];
675
792
  if (before === ".") {
676
793
  response.ignoreMatch();
677
794
  }
678
795
  }
679
796
 
797
+ /**
798
+ *
799
+ * @type {CompilerExt}
800
+ */
801
+ function scopeClassName(mode, _parent) {
802
+ // eslint-disable-next-line no-undefined
803
+ if (mode.className !== undefined) {
804
+ mode.scope = mode.className;
805
+ delete mode.className;
806
+ }
807
+ }
680
808
 
681
809
  /**
682
810
  * `beginKeywords` syntactic sugar
@@ -692,7 +820,7 @@
692
820
  // doesn't allow spaces in keywords anyways and we still check for the boundary
693
821
  // first
694
822
  mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\.)(?=\\b|\\s)';
695
- mode.__beforeBegin = skipIfhasPrecedingDot;
823
+ mode.__beforeBegin = skipIfHasPrecedingDot;
696
824
  mode.keywords = mode.keywords || mode.beginKeywords;
697
825
  delete mode.beginKeywords;
698
826
 
@@ -733,6 +861,30 @@
733
861
  if (mode.relevance === undefined) mode.relevance = 1;
734
862
  }
735
863
 
864
+ // allow beforeMatch to act as a "qualifier" for the match
865
+ // the full match begin must be [beforeMatch][begin]
866
+ const beforeMatchExt = (mode, parent) => {
867
+ if (!mode.beforeMatch) return;
868
+ // starts conflicts with endsParent which we need to make sure the child
869
+ // rule is not matched multiple times
870
+ if (mode.starts) throw new Error("beforeMatch cannot be used with starts");
871
+
872
+ const originalMode = Object.assign({}, mode);
873
+ Object.keys(mode).forEach((key) => { delete mode[key]; });
874
+
875
+ mode.keywords = originalMode.keywords;
876
+ mode.begin = concat(originalMode.beforeMatch, lookahead(originalMode.begin));
877
+ mode.starts = {
878
+ relevance: 0,
879
+ contains: [
880
+ Object.assign(originalMode, { endsParent: true })
881
+ ]
882
+ };
883
+ mode.relevance = 0;
884
+
885
+ delete originalMode.beforeMatch;
886
+ };
887
+
736
888
  // keywords that should have no default relevance value
737
889
  const COMMON_KEYWORDS = [
738
890
  'of',
@@ -748,7 +900,7 @@
748
900
  'value' // common variable name
749
901
  ];
750
902
 
751
- const DEFAULT_KEYWORD_CLASSNAME = "keyword";
903
+ const DEFAULT_KEYWORD_SCOPE = "keyword";
752
904
 
753
905
  /**
754
906
  * Given raw keywords from a language definition, compile them.
@@ -756,22 +908,22 @@
756
908
  * @param {string | Record<string,string|string[]> | Array<string>} rawKeywords
757
909
  * @param {boolean} caseInsensitive
758
910
  */
759
- function compileKeywords(rawKeywords, caseInsensitive, className = DEFAULT_KEYWORD_CLASSNAME) {
911
+ function compileKeywords(rawKeywords, caseInsensitive, scopeName = DEFAULT_KEYWORD_SCOPE) {
760
912
  /** @type KeywordDict */
761
- const compiledKeywords = {};
913
+ const compiledKeywords = Object.create(null);
762
914
 
763
915
  // input can be a string of keywords, an array of keywords, or a object with
764
- // named keys representing className (which can then point to a string or array)
916
+ // named keys representing scopeName (which can then point to a string or array)
765
917
  if (typeof rawKeywords === 'string') {
766
- compileList(className, rawKeywords.split(" "));
918
+ compileList(scopeName, rawKeywords.split(" "));
767
919
  } else if (Array.isArray(rawKeywords)) {
768
- compileList(className, rawKeywords);
920
+ compileList(scopeName, rawKeywords);
769
921
  } else {
770
- Object.keys(rawKeywords).forEach(function(className) {
922
+ Object.keys(rawKeywords).forEach(function(scopeName) {
771
923
  // collapse all our objects back into the parent object
772
924
  Object.assign(
773
925
  compiledKeywords,
774
- compileKeywords(rawKeywords[className], caseInsensitive, className)
926
+ compileKeywords(rawKeywords[scopeName], caseInsensitive, scopeName)
775
927
  );
776
928
  });
777
929
  }
@@ -784,16 +936,16 @@
784
936
  *
785
937
  * Ex: "for if when while|5"
786
938
  *
787
- * @param {string} className
939
+ * @param {string} scopeName
788
940
  * @param {Array<string>} keywordList
789
941
  */
790
- function compileList(className, keywordList) {
942
+ function compileList(scopeName, keywordList) {
791
943
  if (caseInsensitive) {
792
944
  keywordList = keywordList.map(x => x.toLowerCase());
793
945
  }
794
946
  keywordList.forEach(function(keyword) {
795
947
  const pair = keyword.split('|');
796
- compiledKeywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])];
948
+ compiledKeywords[pair[0]] = [scopeName, scoreForKeyword(pair[0], pair[1])];
797
949
  });
798
950
  }
799
951
  }
@@ -824,6 +976,183 @@
824
976
  return COMMON_KEYWORDS.includes(keyword.toLowerCase());
825
977
  }
826
978
 
979
+ /*
980
+
981
+ For the reasoning behind this please see:
982
+ https://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419
983
+
984
+ */
985
+
986
+ /**
987
+ * @type {Record<string, boolean>}
988
+ */
989
+ const seenDeprecations = {};
990
+
991
+ /**
992
+ * @param {string} message
993
+ */
994
+ const error = (message) => {
995
+ console.error(message);
996
+ };
997
+
998
+ /**
999
+ * @param {string} message
1000
+ * @param {any} args
1001
+ */
1002
+ const warn = (message, ...args) => {
1003
+ console.log(`WARN: ${message}`, ...args);
1004
+ };
1005
+
1006
+ /**
1007
+ * @param {string} version
1008
+ * @param {string} message
1009
+ */
1010
+ const deprecated = (version, message) => {
1011
+ if (seenDeprecations[`${version}/${message}`]) return;
1012
+
1013
+ console.log(`Deprecated as of ${version}. ${message}`);
1014
+ seenDeprecations[`${version}/${message}`] = true;
1015
+ };
1016
+
1017
+ /* eslint-disable no-throw-literal */
1018
+
1019
+ /**
1020
+ @typedef {import('highlight.js').CompiledMode} CompiledMode
1021
+ */
1022
+
1023
+ const MultiClassError = new Error();
1024
+
1025
+ /**
1026
+ * Renumbers labeled scope names to account for additional inner match
1027
+ * groups that otherwise would break everything.
1028
+ *
1029
+ * Lets say we 3 match scopes:
1030
+ *
1031
+ * { 1 => ..., 2 => ..., 3 => ... }
1032
+ *
1033
+ * So what we need is a clean match like this:
1034
+ *
1035
+ * (a)(b)(c) => [ "a", "b", "c" ]
1036
+ *
1037
+ * But this falls apart with inner match groups:
1038
+ *
1039
+ * (a)(((b)))(c) => ["a", "b", "b", "b", "c" ]
1040
+ *
1041
+ * Our scopes are now "out of alignment" and we're repeating `b` 3 times.
1042
+ * What needs to happen is the numbers are remapped:
1043
+ *
1044
+ * { 1 => ..., 2 => ..., 5 => ... }
1045
+ *
1046
+ * We also need to know that the ONLY groups that should be output
1047
+ * are 1, 2, and 5. This function handles this behavior.
1048
+ *
1049
+ * @param {CompiledMode} mode
1050
+ * @param {Array<RegExp | string>} regexes
1051
+ * @param {{key: "beginScope"|"endScope"}} opts
1052
+ */
1053
+ function remapScopeNames(mode, regexes, { key }) {
1054
+ let offset = 0;
1055
+ const scopeNames = mode[key];
1056
+ /** @type Record<number,boolean> */
1057
+ const emit = {};
1058
+ /** @type Record<number,string> */
1059
+ const positions = {};
1060
+
1061
+ for (let i = 1; i <= regexes.length; i++) {
1062
+ positions[i + offset] = scopeNames[i];
1063
+ emit[i + offset] = true;
1064
+ offset += countMatchGroups(regexes[i - 1]);
1065
+ }
1066
+ // we use _emit to keep track of which match groups are "top-level" to avoid double
1067
+ // output from inside match groups
1068
+ mode[key] = positions;
1069
+ mode[key]._emit = emit;
1070
+ mode[key]._multi = true;
1071
+ }
1072
+
1073
+ /**
1074
+ * @param {CompiledMode} mode
1075
+ */
1076
+ function beginMultiClass(mode) {
1077
+ if (!Array.isArray(mode.begin)) return;
1078
+
1079
+ if (mode.skip || mode.excludeBegin || mode.returnBegin) {
1080
+ error("skip, excludeBegin, returnBegin not compatible with beginScope: {}");
1081
+ throw MultiClassError;
1082
+ }
1083
+
1084
+ if (typeof mode.beginScope !== "object" || mode.beginScope === null) {
1085
+ error("beginScope must be object");
1086
+ throw MultiClassError;
1087
+ }
1088
+
1089
+ remapScopeNames(mode, mode.begin, { key: "beginScope" });
1090
+ mode.begin = _rewriteBackreferences(mode.begin, { joinWith: "" });
1091
+ }
1092
+
1093
+ /**
1094
+ * @param {CompiledMode} mode
1095
+ */
1096
+ function endMultiClass(mode) {
1097
+ if (!Array.isArray(mode.end)) return;
1098
+
1099
+ if (mode.skip || mode.excludeEnd || mode.returnEnd) {
1100
+ error("skip, excludeEnd, returnEnd not compatible with endScope: {}");
1101
+ throw MultiClassError;
1102
+ }
1103
+
1104
+ if (typeof mode.endScope !== "object" || mode.endScope === null) {
1105
+ error("endScope must be object");
1106
+ throw MultiClassError;
1107
+ }
1108
+
1109
+ remapScopeNames(mode, mode.end, { key: "endScope" });
1110
+ mode.end = _rewriteBackreferences(mode.end, { joinWith: "" });
1111
+ }
1112
+
1113
+ /**
1114
+ * this exists only to allow `scope: {}` to be used beside `match:`
1115
+ * Otherwise `beginScope` would necessary and that would look weird
1116
+
1117
+ {
1118
+ match: [ /def/, /\w+/ ]
1119
+ scope: { 1: "keyword" , 2: "title" }
1120
+ }
1121
+
1122
+ * @param {CompiledMode} mode
1123
+ */
1124
+ function scopeSugar(mode) {
1125
+ if (mode.scope && typeof mode.scope === "object" && mode.scope !== null) {
1126
+ mode.beginScope = mode.scope;
1127
+ delete mode.scope;
1128
+ }
1129
+ }
1130
+
1131
+ /**
1132
+ * @param {CompiledMode} mode
1133
+ */
1134
+ function MultiClass(mode) {
1135
+ scopeSugar(mode);
1136
+
1137
+ if (typeof mode.beginScope === "string") {
1138
+ mode.beginScope = { _wrap: mode.beginScope };
1139
+ }
1140
+ if (typeof mode.endScope === "string") {
1141
+ mode.endScope = { _wrap: mode.endScope };
1142
+ }
1143
+
1144
+ beginMultiClass(mode);
1145
+ endMultiClass(mode);
1146
+ }
1147
+
1148
+ /**
1149
+ @typedef {import('highlight.js').Mode} Mode
1150
+ @typedef {import('highlight.js').CompiledMode} CompiledMode
1151
+ @typedef {import('highlight.js').Language} Language
1152
+ @typedef {import('highlight.js').HLJSPlugin} HLJSPlugin
1153
+ @typedef {import('highlight.js').CompiledLanguage} CompiledLanguage
1154
+ */
1155
+
827
1156
  // compilation
828
1157
 
829
1158
  /**
@@ -832,12 +1161,11 @@
832
1161
  * Given the raw result of a language definition (Language), compiles this so
833
1162
  * that it is ready for highlighting code.
834
1163
  * @param {Language} language
835
- * @param {{plugins: HLJSPlugin[]}} opts
836
1164
  * @returns {CompiledLanguage}
837
1165
  */
838
- function compileLanguage(language, { plugins }) {
1166
+ function compileLanguage(language) {
839
1167
  /**
840
- * Builds a regex with the case sensativility of the current language
1168
+ * Builds a regex with the case sensitivity of the current language
841
1169
  *
842
1170
  * @param {RegExp | string} value
843
1171
  * @param {boolean} [global]
@@ -845,7 +1173,10 @@
845
1173
  function langRe(value, global) {
846
1174
  return new RegExp(
847
1175
  source(value),
848
- 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '')
1176
+ 'm'
1177
+ + (language.case_insensitive ? 'i' : '')
1178
+ + (language.unicodeRegex ? 'u' : '')
1179
+ + (global ? 'g' : '')
849
1180
  );
850
1181
  }
851
1182
 
@@ -887,7 +1218,7 @@
887
1218
  this.exec = () => null;
888
1219
  }
889
1220
  const terminators = this.regexes.map(el => el[1]);
890
- this.matcherRe = langRe(join(terminators), true);
1221
+ this.matcherRe = langRe(_rewriteBackreferences(terminators, { joinWith: '|' }), true);
891
1222
  this.lastIndex = 0;
892
1223
  }
893
1224
 
@@ -1100,9 +1431,12 @@
1100
1431
  if (mode.isCompiled) return cmode;
1101
1432
 
1102
1433
  [
1434
+ scopeClassName,
1103
1435
  // do this early so compiler extensions generally don't have to worry about
1104
1436
  // the distinction between match/begin
1105
- compileMatch
1437
+ compileMatch,
1438
+ MultiClass,
1439
+ beforeMatchExt
1106
1440
  ].forEach(ext => ext(mode, parent));
1107
1441
 
1108
1442
  language.compilerExtensions.forEach(ext => ext(mode, parent));
@@ -1122,32 +1456,28 @@
1122
1456
  mode.isCompiled = true;
1123
1457
 
1124
1458
  let keywordPattern = null;
1125
- if (typeof mode.keywords === "object") {
1459
+ if (typeof mode.keywords === "object" && mode.keywords.$pattern) {
1460
+ // we need a copy because keywords might be compiled multiple times
1461
+ // so we can't go deleting $pattern from the original on the first
1462
+ // pass
1463
+ mode.keywords = Object.assign({}, mode.keywords);
1126
1464
  keywordPattern = mode.keywords.$pattern;
1127
1465
  delete mode.keywords.$pattern;
1128
1466
  }
1467
+ keywordPattern = keywordPattern || /\w+/;
1129
1468
 
1130
1469
  if (mode.keywords) {
1131
1470
  mode.keywords = compileKeywords(mode.keywords, language.case_insensitive);
1132
1471
  }
1133
1472
 
1134
- // both are not allowed
1135
- if (mode.lexemes && keywordPattern) {
1136
- throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");
1137
- }
1138
-
1139
- // `mode.lexemes` was the old standard before we added and now recommend
1140
- // using `keywords.$pattern` to pass the keyword pattern
1141
- keywordPattern = keywordPattern || mode.lexemes || /\w+/;
1142
1473
  cmode.keywordPatternRe = langRe(keywordPattern, true);
1143
1474
 
1144
1475
  if (parent) {
1145
1476
  if (!mode.begin) mode.begin = /\B|\b/;
1146
- cmode.beginRe = langRe(mode.begin);
1147
- if (mode.endSameAsBegin) mode.end = mode.begin;
1477
+ cmode.beginRe = langRe(cmode.begin);
1148
1478
  if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/;
1149
- if (mode.end) cmode.endRe = langRe(mode.end);
1150
- cmode.terminatorEnd = source(mode.end) || '';
1479
+ if (mode.end) cmode.endRe = langRe(cmode.end);
1480
+ cmode.terminatorEnd = source(cmode.end) || '';
1151
1481
  if (mode.endsWithParent && parent.terminatorEnd) {
1152
1482
  cmode.terminatorEnd += (mode.end ? '|' : '') + parent.terminatorEnd;
1153
1483
  }
@@ -1176,7 +1506,7 @@
1176
1506
  }
1177
1507
 
1178
1508
  // we need a null object, which inherit will guarantee
1179
- language.classNameAliases = inherit(language.classNameAliases || {});
1509
+ language.classNameAliases = inherit$1(language.classNameAliases || {});
1180
1510
 
1181
1511
  return compileMode(/** @type Mode */ (language));
1182
1512
  }
@@ -1211,7 +1541,7 @@
1211
1541
  function expandOrCloneMode(mode) {
1212
1542
  if (mode.variants && !mode.cachedVariants) {
1213
1543
  mode.cachedVariants = mode.variants.map(function(variant) {
1214
- return inherit(mode, { variants: null }, variant);
1544
+ return inherit$1(mode, { variants: null }, variant);
1215
1545
  });
1216
1546
  }
1217
1547
 
@@ -1227,288 +1557,58 @@
1227
1557
  // instance of ourselves, so we can be reused with many
1228
1558
  // different parents without issue
1229
1559
  if (dependencyOnParent(mode)) {
1230
- return inherit(mode, { starts: mode.starts ? inherit(mode.starts) : null });
1560
+ return inherit$1(mode, { starts: mode.starts ? inherit$1(mode.starts) : null });
1231
1561
  }
1232
1562
 
1233
1563
  if (Object.isFrozen(mode)) {
1234
- return inherit(mode);
1564
+ return inherit$1(mode);
1235
1565
  }
1236
1566
 
1237
1567
  // no special dependency issues, just return ourselves
1238
1568
  return mode;
1239
1569
  }
1240
1570
 
1241
- var version = "10.7.2";
1242
-
1243
- // @ts-nocheck
1244
-
1245
- function hasValueOrEmptyAttribute(value) {
1246
- return Boolean(value || value === "");
1247
- }
1248
-
1249
- function BuildVuePlugin(hljs) {
1250
- const Component = {
1251
- props: ["language", "code", "autodetect"],
1252
- data: function() {
1253
- return {
1254
- detectedLanguage: "",
1255
- unknownLanguage: false
1256
- };
1257
- },
1258
- computed: {
1259
- className() {
1260
- if (this.unknownLanguage) return "";
1261
-
1262
- return "hljs " + this.detectedLanguage;
1263
- },
1264
- highlighted() {
1265
- // no idea what language to use, return raw code
1266
- if (!this.autoDetect && !hljs.getLanguage(this.language)) {
1267
- console.warn(`The language "${this.language}" you specified could not be found.`);
1268
- this.unknownLanguage = true;
1269
- return escapeHTML(this.code);
1270
- }
1271
-
1272
- let result = {};
1273
- if (this.autoDetect) {
1274
- result = hljs.highlightAuto(this.code);
1275
- this.detectedLanguage = result.language;
1276
- } else {
1277
- result = hljs.highlight(this.language, this.code, this.ignoreIllegals);
1278
- this.detectedLanguage = this.language;
1279
- }
1280
- return result.value;
1281
- },
1282
- autoDetect() {
1283
- return !this.language || hasValueOrEmptyAttribute(this.autodetect);
1284
- },
1285
- ignoreIllegals() {
1286
- return true;
1287
- }
1288
- },
1289
- // this avoids needing to use a whole Vue compilation pipeline just
1290
- // to build Highlight.js
1291
- render(createElement) {
1292
- return createElement("pre", {}, [
1293
- createElement("code", {
1294
- class: this.className,
1295
- domProps: { innerHTML: this.highlighted }
1296
- })
1297
- ]);
1298
- }
1299
- // template: `<pre><code :class="className" v-html="highlighted"></code></pre>`
1300
- };
1301
-
1302
- const VuePlugin = {
1303
- install(Vue) {
1304
- Vue.component('highlightjs', Component);
1305
- }
1306
- };
1307
-
1308
- return { Component, VuePlugin };
1309
- }
1310
-
1311
- /* plugin itself */
1312
-
1313
- /** @type {HLJSPlugin} */
1314
- const mergeHTMLPlugin = {
1315
- "after:highlightElement": ({ el, result, text }) => {
1316
- const originalStream = nodeStream(el);
1317
- if (!originalStream.length) return;
1318
-
1319
- const resultNode = document.createElement('div');
1320
- resultNode.innerHTML = result.value;
1321
- result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
1322
- }
1323
- };
1324
-
1325
- /* Stream merging support functions */
1326
-
1327
- /**
1328
- * @typedef Event
1329
- * @property {'start'|'stop'} event
1330
- * @property {number} offset
1331
- * @property {Node} node
1332
- */
1333
-
1334
- /**
1335
- * @param {Node} node
1336
- */
1337
- function tag(node) {
1338
- return node.nodeName.toLowerCase();
1339
- }
1340
-
1341
- /**
1342
- * @param {Node} node
1343
- */
1344
- function nodeStream(node) {
1345
- /** @type Event[] */
1346
- const result = [];
1347
- (function _nodeStream(node, offset) {
1348
- for (let child = node.firstChild; child; child = child.nextSibling) {
1349
- if (child.nodeType === 3) {
1350
- offset += child.nodeValue.length;
1351
- } else if (child.nodeType === 1) {
1352
- result.push({
1353
- event: 'start',
1354
- offset: offset,
1355
- node: child
1356
- });
1357
- offset = _nodeStream(child, offset);
1358
- // Prevent void elements from having an end tag that would actually
1359
- // double them in the output. There are more void elements in HTML
1360
- // but we list only those realistically expected in code display.
1361
- if (!tag(child).match(/br|hr|img|input/)) {
1362
- result.push({
1363
- event: 'stop',
1364
- offset: offset,
1365
- node: child
1366
- });
1367
- }
1368
- }
1369
- }
1370
- return offset;
1371
- })(node, 0);
1372
- return result;
1373
- }
1374
-
1375
- /**
1376
- * @param {any} original - the original stream
1377
- * @param {any} highlighted - stream of the highlighted source
1378
- * @param {string} value - the original source itself
1379
- */
1380
- function mergeStreams(original, highlighted, value) {
1381
- let processed = 0;
1382
- let result = '';
1383
- const nodeStack = [];
1384
-
1385
- function selectStream() {
1386
- if (!original.length || !highlighted.length) {
1387
- return original.length ? original : highlighted;
1388
- }
1389
- if (original[0].offset !== highlighted[0].offset) {
1390
- return (original[0].offset < highlighted[0].offset) ? original : highlighted;
1391
- }
1392
-
1393
- /*
1394
- To avoid starting the stream just before it should stop the order is
1395
- ensured that original always starts first and closes last:
1571
+ var version = "11.6.0";
1396
1572
 
1397
- if (event1 == 'start' && event2 == 'start')
1398
- return original;
1399
- if (event1 == 'start' && event2 == 'stop')
1400
- return highlighted;
1401
- if (event1 == 'stop' && event2 == 'start')
1402
- return original;
1403
- if (event1 == 'stop' && event2 == 'stop')
1404
- return highlighted;
1405
-
1406
- ... which is collapsed to:
1407
- */
1408
- return highlighted[0].event === 'start' ? original : highlighted;
1409
- }
1410
-
1411
- /**
1412
- * @param {Node} node
1413
- */
1414
- function open(node) {
1415
- /** @param {Attr} attr */
1416
- function attributeString(attr) {
1417
- return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
1418
- }
1419
- // @ts-ignore
1420
- result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>';
1421
- }
1422
-
1423
- /**
1424
- * @param {Node} node
1425
- */
1426
- function close(node) {
1427
- result += '</' + tag(node) + '>';
1428
- }
1429
-
1430
- /**
1431
- * @param {Event} event
1432
- */
1433
- function render(event) {
1434
- (event.event === 'start' ? open : close)(event.node);
1573
+ class HTMLInjectionError extends Error {
1574
+ constructor(reason, html) {
1575
+ super(reason);
1576
+ this.name = "HTMLInjectionError";
1577
+ this.html = html;
1435
1578
  }
1436
-
1437
- while (original.length || highlighted.length) {
1438
- let stream = selectStream();
1439
- result += escapeHTML(value.substring(processed, stream[0].offset));
1440
- processed = stream[0].offset;
1441
- if (stream === original) {
1442
- /*
1443
- On any opening or closing tag of the original markup we first close
1444
- the entire highlighted node stack, then render the original tag along
1445
- with all the following original tags at the same offset and then
1446
- reopen all the tags on the highlighted stack.
1447
- */
1448
- nodeStack.reverse().forEach(close);
1449
- do {
1450
- render(stream.splice(0, 1)[0]);
1451
- stream = selectStream();
1452
- } while (stream === original && stream.length && stream[0].offset === processed);
1453
- nodeStack.reverse().forEach(open);
1454
- } else {
1455
- if (stream[0].event === 'start') {
1456
- nodeStack.push(stream[0].node);
1457
- } else {
1458
- nodeStack.pop();
1459
- }
1460
- render(stream.splice(0, 1)[0]);
1461
- }
1462
- }
1463
- return result + escapeHTML(value.substr(processed));
1464
1579
  }
1465
1580
 
1466
1581
  /*
1467
-
1468
- For the reasoning behind this please see:
1469
- https://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419
1470
-
1582
+ Syntax highlighting with language autodetection.
1583
+ https://highlightjs.org/
1471
1584
  */
1472
1585
 
1473
1586
  /**
1474
- * @type {Record<string, boolean>}
1475
- */
1476
- const seenDeprecations = {};
1477
-
1478
- /**
1479
- * @param {string} message
1480
- */
1481
- const error = (message) => {
1482
- console.error(message);
1483
- };
1484
-
1485
- /**
1486
- * @param {string} message
1487
- * @param {any} args
1488
- */
1489
- const warn = (message, ...args) => {
1490
- console.log(`WARN: ${message}`, ...args);
1491
- };
1492
-
1493
- /**
1494
- * @param {string} version
1495
- * @param {string} message
1496
- */
1497
- const deprecated = (version, message) => {
1498
- if (seenDeprecations[`${version}/${message}`]) return;
1499
-
1500
- console.log(`Deprecated as of ${version}. ${message}`);
1501
- seenDeprecations[`${version}/${message}`] = true;
1502
- };
1503
-
1504
- /*
1505
- Syntax highlighting with language autodetection.
1506
- https://highlightjs.org/
1587
+ @typedef {import('highlight.js').Mode} Mode
1588
+ @typedef {import('highlight.js').CompiledMode} CompiledMode
1589
+ @typedef {import('highlight.js').CompiledScope} CompiledScope
1590
+ @typedef {import('highlight.js').Language} Language
1591
+ @typedef {import('highlight.js').HLJSApi} HLJSApi
1592
+ @typedef {import('highlight.js').HLJSPlugin} HLJSPlugin
1593
+ @typedef {import('highlight.js').PluginEvent} PluginEvent
1594
+ @typedef {import('highlight.js').HLJSOptions} HLJSOptions
1595
+ @typedef {import('highlight.js').LanguageFn} LanguageFn
1596
+ @typedef {import('highlight.js').HighlightedHTMLElement} HighlightedHTMLElement
1597
+ @typedef {import('highlight.js').BeforeHighlightContext} BeforeHighlightContext
1598
+ @typedef {import('highlight.js/private').MatchType} MatchType
1599
+ @typedef {import('highlight.js/private').KeywordData} KeywordData
1600
+ @typedef {import('highlight.js/private').EnhancedMatch} EnhancedMatch
1601
+ @typedef {import('highlight.js/private').AnnotatedError} AnnotatedError
1602
+ @typedef {import('highlight.js').AutoHighlightResult} AutoHighlightResult
1603
+ @typedef {import('highlight.js').HighlightOptions} HighlightOptions
1604
+ @typedef {import('highlight.js').HighlightResult} HighlightResult
1507
1605
  */
1508
1606
 
1509
- const escape$1 = escapeHTML;
1510
- const inherit$1 = inherit;
1607
+
1608
+ const escape = escapeHTML;
1609
+ const inherit = inherit$1;
1511
1610
  const NO_MATCH = Symbol("nomatch");
1611
+ const MAX_KEYWORD_HITS = 7;
1512
1612
 
1513
1613
  /**
1514
1614
  * @param {any} hljs - object that is extended (legacy)
@@ -1526,7 +1626,6 @@
1526
1626
  // safe/production mode - swallows more errors, tries to keep running
1527
1627
  // even if a single syntax or parse hits a fatal error
1528
1628
  let SAFE_MODE = true;
1529
- const fixMarkupRe = /(^(<[^>]+>|\t|)+|\n)/gm;
1530
1629
  const LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?";
1531
1630
  /** @type {Language} */
1532
1631
  const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text', contains: [] };
@@ -1535,11 +1634,12 @@
1535
1634
  // calling the `hljs.configure` function.
1536
1635
  /** @type HLJSOptions */
1537
1636
  let options = {
1637
+ ignoreUnescapedHTML: false,
1638
+ throwUnescapedHTML: false,
1538
1639
  noHighlightRe: /^(no-?highlight)$/i,
1539
1640
  languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i,
1540
1641
  classPrefix: 'hljs-',
1541
- tabReplace: null,
1542
- useBR: false,
1642
+ cssSelector: 'pre code',
1543
1643
  languages: null,
1544
1644
  // beta configuration options, subject to change, welcome to discuss
1545
1645
  // https://github.com/highlightjs/highlight.js/issues/1086
@@ -1589,10 +1689,9 @@
1589
1689
  * NEW API
1590
1690
  * highlight(code, {lang, ignoreIllegals})
1591
1691
  *
1592
- * @param {string} codeOrlanguageName - the language to use for highlighting
1692
+ * @param {string} codeOrLanguageName - the language to use for highlighting
1593
1693
  * @param {string | HighlightOptions} optionsOrCode - the code to highlight
1594
1694
  * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail
1595
- * @param {CompiledMode} [continuation] - current continuation mode, if any
1596
1695
  *
1597
1696
  * @returns {HighlightResult} Result - an object that represents the result
1598
1697
  * @property {string} language - the language name
@@ -1602,24 +1701,25 @@
1602
1701
  * @property {CompiledMode} top - top of the current mode stack
1603
1702
  * @property {boolean} illegal - indicates whether any illegal matches were found
1604
1703
  */
1605
- function highlight(codeOrlanguageName, optionsOrCode, ignoreIllegals, continuation) {
1704
+ function highlight(codeOrLanguageName, optionsOrCode, ignoreIllegals) {
1606
1705
  let code = "";
1607
1706
  let languageName = "";
1608
1707
  if (typeof optionsOrCode === "object") {
1609
- code = codeOrlanguageName;
1708
+ code = codeOrLanguageName;
1610
1709
  ignoreIllegals = optionsOrCode.ignoreIllegals;
1611
1710
  languageName = optionsOrCode.language;
1612
- // continuation not supported at all via the new API
1613
- // eslint-disable-next-line no-undefined
1614
- continuation = undefined;
1615
1711
  } else {
1616
1712
  // old API
1617
1713
  deprecated("10.7.0", "highlight(lang, code, ...args) has been deprecated.");
1618
1714
  deprecated("10.7.0", "Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277");
1619
- languageName = codeOrlanguageName;
1715
+ languageName = codeOrLanguageName;
1620
1716
  code = optionsOrCode;
1621
1717
  }
1622
1718
 
1719
+ // https://github.com/highlightjs/highlight.js/issues/3149
1720
+ // eslint-disable-next-line no-undefined
1721
+ if (ignoreIllegals === undefined) { ignoreIllegals = true; }
1722
+
1623
1723
  /** @type {BeforeHighlightContext} */
1624
1724
  const context = {
1625
1725
  code,
@@ -1633,7 +1733,7 @@
1633
1733
  // in which case we don't even need to call highlight
1634
1734
  const result = context.result
1635
1735
  ? context.result
1636
- : _highlight(context.language, context.code, ignoreIllegals, continuation);
1736
+ : _highlight(context.language, context.code, ignoreIllegals);
1637
1737
 
1638
1738
  result.code = context.code;
1639
1739
  // the plugin can change anything in result to suite it
@@ -1652,15 +1752,16 @@
1652
1752
  * @returns {HighlightResult} - result of the highlight operation
1653
1753
  */
1654
1754
  function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) {
1755
+ const keywordHits = Object.create(null);
1756
+
1655
1757
  /**
1656
1758
  * Return keyword data if a match is a keyword
1657
1759
  * @param {CompiledMode} mode - current mode
1658
- * @param {RegExpMatchArray} match - regexp match data
1760
+ * @param {string} matchText - the textual match
1659
1761
  * @returns {KeywordData | false}
1660
1762
  */
1661
- function keywordData(mode, match) {
1662
- const matchText = language.case_insensitive ? match[0].toLowerCase() : match[0];
1663
- return Object.prototype.hasOwnProperty.call(mode.keywords, matchText) && mode.keywords[matchText];
1763
+ function keywordData(mode, matchText) {
1764
+ return mode.keywords[matchText];
1664
1765
  }
1665
1766
 
1666
1767
  function processKeywords() {
@@ -1676,13 +1777,15 @@
1676
1777
 
1677
1778
  while (match) {
1678
1779
  buf += modeBuffer.substring(lastIndex, match.index);
1679
- const data = keywordData(top, match);
1780
+ const word = language.case_insensitive ? match[0].toLowerCase() : match[0];
1781
+ const data = keywordData(top, word);
1680
1782
  if (data) {
1681
1783
  const [kind, keywordRelevance] = data;
1682
1784
  emitter.addText(buf);
1683
1785
  buf = "";
1684
1786
 
1685
- relevance += keywordRelevance;
1787
+ keywordHits[word] = (keywordHits[word] || 0) + 1;
1788
+ if (keywordHits[word] <= MAX_KEYWORD_HITS) relevance += keywordRelevance;
1686
1789
  if (kind.startsWith("_")) {
1687
1790
  // _ implied for relevance only, do not highlight
1688
1791
  // by applying a class name
@@ -1697,7 +1800,7 @@
1697
1800
  lastIndex = top.keywordPatternRe.lastIndex;
1698
1801
  match = top.keywordPatternRe.exec(modeBuffer);
1699
1802
  }
1700
- buf += modeBuffer.substr(lastIndex);
1803
+ buf += modeBuffer.substring(lastIndex);
1701
1804
  emitter.addText(buf);
1702
1805
  }
1703
1806
 
@@ -1712,7 +1815,7 @@
1712
1815
  return;
1713
1816
  }
1714
1817
  result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]);
1715
- continuations[top.subLanguage] = /** @type {CompiledMode} */ (result.top);
1818
+ continuations[top.subLanguage] = /** @type {CompiledMode} */ (result._top);
1716
1819
  } else {
1717
1820
  result = highlightAuto(modeBuffer, top.subLanguage.length ? top.subLanguage : null);
1718
1821
  }
@@ -1724,7 +1827,7 @@
1724
1827
  if (top.relevance > 0) {
1725
1828
  relevance += result.relevance;
1726
1829
  }
1727
- emitter.addSublanguage(result.emitter, result.language);
1830
+ emitter.addSublanguage(result._emitter, result.language);
1728
1831
  }
1729
1832
 
1730
1833
  function processBuffer() {
@@ -1737,12 +1840,47 @@
1737
1840
  }
1738
1841
 
1739
1842
  /**
1740
- * @param {Mode} mode - new mode to start
1843
+ * @param {CompiledScope} scope
1844
+ * @param {RegExpMatchArray} match
1845
+ */
1846
+ function emitMultiClass(scope, match) {
1847
+ let i = 1;
1848
+ const max = match.length - 1;
1849
+ while (i <= max) {
1850
+ if (!scope._emit[i]) { i++; continue; }
1851
+ const klass = language.classNameAliases[scope[i]] || scope[i];
1852
+ const text = match[i];
1853
+ if (klass) {
1854
+ emitter.addKeyword(text, klass);
1855
+ } else {
1856
+ modeBuffer = text;
1857
+ processKeywords();
1858
+ modeBuffer = "";
1859
+ }
1860
+ i++;
1861
+ }
1862
+ }
1863
+
1864
+ /**
1865
+ * @param {CompiledMode} mode - new mode to start
1866
+ * @param {RegExpMatchArray} match
1741
1867
  */
1742
- function startNewMode(mode) {
1743
- if (mode.className) {
1744
- emitter.openNode(language.classNameAliases[mode.className] || mode.className);
1868
+ function startNewMode(mode, match) {
1869
+ if (mode.scope && typeof mode.scope === "string") {
1870
+ emitter.openNode(language.classNameAliases[mode.scope] || mode.scope);
1871
+ }
1872
+ if (mode.beginScope) {
1873
+ // beginScope just wraps the begin match itself in a scope
1874
+ if (mode.beginScope._wrap) {
1875
+ emitter.addKeyword(modeBuffer, language.classNameAliases[mode.beginScope._wrap] || mode.beginScope._wrap);
1876
+ modeBuffer = "";
1877
+ } else if (mode.beginScope._multi) {
1878
+ // at this point modeBuffer should just be the match
1879
+ emitMultiClass(mode.beginScope, match);
1880
+ modeBuffer = "";
1881
+ }
1745
1882
  }
1883
+
1746
1884
  top = Object.create(mode, { parent: { value: top } });
1747
1885
  return top;
1748
1886
  }
@@ -1784,7 +1922,7 @@
1784
1922
  */
1785
1923
  function doIgnore(lexeme) {
1786
1924
  if (top.matcher.regexIndex === 0) {
1787
- // no more regexs to potentially match here, so we move the cursor forward one
1925
+ // no more regexes to potentially match here, so we move the cursor forward one
1788
1926
  // space
1789
1927
  modeBuffer += lexeme[0];
1790
1928
  return 1;
@@ -1815,10 +1953,6 @@
1815
1953
  if (resp.isMatchIgnored) return doIgnore(lexeme);
1816
1954
  }
1817
1955
 
1818
- if (newMode && newMode.endSameAsBegin) {
1819
- newMode.endRe = escape(lexeme);
1820
- }
1821
-
1822
1956
  if (newMode.skip) {
1823
1957
  modeBuffer += lexeme;
1824
1958
  } else {
@@ -1830,11 +1964,7 @@
1830
1964
  modeBuffer = lexeme;
1831
1965
  }
1832
1966
  }
1833
- startNewMode(newMode);
1834
- // if (mode["after:begin"]) {
1835
- // let resp = new Response(mode);
1836
- // mode["after:begin"](match, resp);
1837
- // }
1967
+ startNewMode(newMode, match);
1838
1968
  return newMode.returnBegin ? 0 : lexeme.length;
1839
1969
  }
1840
1970
 
@@ -1845,13 +1975,19 @@
1845
1975
  */
1846
1976
  function doEndMatch(match) {
1847
1977
  const lexeme = match[0];
1848
- const matchPlusRemainder = codeToHighlight.substr(match.index);
1978
+ const matchPlusRemainder = codeToHighlight.substring(match.index);
1849
1979
 
1850
1980
  const endMode = endOfMode(top, match, matchPlusRemainder);
1851
1981
  if (!endMode) { return NO_MATCH; }
1852
1982
 
1853
1983
  const origin = top;
1854
- if (origin.skip) {
1984
+ if (top.endScope && top.endScope._wrap) {
1985
+ processBuffer();
1986
+ emitter.addKeyword(lexeme, top.endScope._wrap);
1987
+ } else if (top.endScope && top.endScope._multi) {
1988
+ processBuffer();
1989
+ emitMultiClass(top.endScope, match);
1990
+ } else if (origin.skip) {
1855
1991
  modeBuffer += lexeme;
1856
1992
  } else {
1857
1993
  if (!(origin.returnEnd || origin.excludeEnd)) {
@@ -1863,7 +1999,7 @@
1863
1999
  }
1864
2000
  }
1865
2001
  do {
1866
- if (top.className) {
2002
+ if (top.scope) {
1867
2003
  emitter.closeNode();
1868
2004
  }
1869
2005
  if (!top.skip && !top.subLanguage) {
@@ -1872,10 +2008,7 @@
1872
2008
  top = top.parent;
1873
2009
  } while (top !== endMode.parent);
1874
2010
  if (endMode.starts) {
1875
- if (endMode.endSameAsBegin) {
1876
- endMode.starts.endRe = endMode.endRe;
1877
- }
1878
- startNewMode(endMode.starts);
2011
+ startNewMode(endMode.starts, match);
1879
2012
  }
1880
2013
  return origin.returnEnd ? 0 : lexeme.length;
1881
2014
  }
@@ -1883,8 +2016,8 @@
1883
2016
  function processContinuations() {
1884
2017
  const list = [];
1885
2018
  for (let current = top; current !== language; current = current.parent) {
1886
- if (current.className) {
1887
- list.unshift(current.className);
2019
+ if (current.scope) {
2020
+ list.unshift(current.scope);
1888
2021
  }
1889
2022
  }
1890
2023
  list.forEach(item => emitter.openNode(item));
@@ -1896,7 +2029,7 @@
1896
2029
  /**
1897
2030
  * Process an individual match
1898
2031
  *
1899
- * @param {string} textBeforeMatch - text preceeding the match (since the last match)
2032
+ * @param {string} textBeforeMatch - text preceding the match (since the last match)
1900
2033
  * @param {EnhancedMatch} [match] - the match itself
1901
2034
  */
1902
2035
  function processLexeme(textBeforeMatch, match) {
@@ -1919,7 +2052,7 @@
1919
2052
  modeBuffer += codeToHighlight.slice(match.index, match.index + 1);
1920
2053
  if (!SAFE_MODE) {
1921
2054
  /** @type {AnnotatedError} */
1922
- const err = new Error('0 width match regex');
2055
+ const err = new Error(`0 width match regex (${languageName})`);
1923
2056
  err.languageName = languageName;
1924
2057
  err.badRule = lastMatch.rule;
1925
2058
  throw err;
@@ -1933,7 +2066,7 @@
1933
2066
  } else if (match.type === "illegal" && !ignoreIllegals) {
1934
2067
  // illegal match, we do not continue processing
1935
2068
  /** @type {AnnotatedError} */
1936
- const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '<unnamed>') + '"');
2069
+ const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.scope || '<unnamed>') + '"');
1937
2070
  err.mode = top;
1938
2071
  throw err;
1939
2072
  } else if (match.type === "end") {
@@ -1961,13 +2094,9 @@
1961
2094
  }
1962
2095
 
1963
2096
  /*
1964
- Why might be find ourselves here? Only one occasion now. An end match that was
1965
- triggered but could not be completed. When might this happen? When an `endSameasBegin`
1966
- rule sets the end rule to a specific match. Since the overall mode termination rule that's
1967
- being used to scan the text isn't recompiled that means that any match that LOOKS like
1968
- the end (but is not, because it is not an exact match to the beginning) will
1969
- end up here. A definite end match, but when `doEndMatch` tries to "reapply"
1970
- the end rule and fails to match, we wind up here, and just silently ignore the end.
2097
+ Why might be find ourselves here? An potential end match that was
2098
+ triggered but could not be completed. IE, `doEndMatch` returned NO_MATCH.
2099
+ (this could be because a callback requests the match be ignored, etc)
1971
2100
 
1972
2101
  This causes no real harm other than stopping a few times too many.
1973
2102
  */
@@ -1982,7 +2111,7 @@
1982
2111
  throw new Error('Unknown language: "' + languageName + '"');
1983
2112
  }
1984
2113
 
1985
- const md = compileLanguage(language, { plugins });
2114
+ const md = compileLanguage(language);
1986
2115
  let result = '';
1987
2116
  /** @type {CompiledMode} */
1988
2117
  let top = continuation || md;
@@ -2019,44 +2148,44 @@
2019
2148
  const processedCount = processLexeme(beforeMatch, match);
2020
2149
  index = match.index + processedCount;
2021
2150
  }
2022
- processLexeme(codeToHighlight.substr(index));
2151
+ processLexeme(codeToHighlight.substring(index));
2023
2152
  emitter.closeAllNodes();
2024
2153
  emitter.finalize();
2025
2154
  result = emitter.toHTML();
2026
2155
 
2027
2156
  return {
2028
- // avoid possible breakage with v10 clients expecting
2029
- // this to always be an integer
2030
- relevance: Math.floor(relevance),
2031
- value: result,
2032
2157
  language: languageName,
2158
+ value: result,
2159
+ relevance: relevance,
2033
2160
  illegal: false,
2034
- emitter: emitter,
2035
- top: top
2161
+ _emitter: emitter,
2162
+ _top: top
2036
2163
  };
2037
2164
  } catch (err) {
2038
2165
  if (err.message && err.message.includes('Illegal')) {
2039
2166
  return {
2167
+ language: languageName,
2168
+ value: escape(codeToHighlight),
2040
2169
  illegal: true,
2041
- illegalBy: {
2042
- msg: err.message,
2170
+ relevance: 0,
2171
+ _illegalBy: {
2172
+ message: err.message,
2173
+ index: index,
2043
2174
  context: codeToHighlight.slice(index - 100, index + 100),
2044
- mode: err.mode
2175
+ mode: err.mode,
2176
+ resultSoFar: result
2045
2177
  },
2046
- sofar: result,
2047
- relevance: 0,
2048
- value: escape$1(codeToHighlight),
2049
- emitter: emitter
2178
+ _emitter: emitter
2050
2179
  };
2051
2180
  } else if (SAFE_MODE) {
2052
2181
  return {
2182
+ language: languageName,
2183
+ value: escape(codeToHighlight),
2053
2184
  illegal: false,
2054
2185
  relevance: 0,
2055
- value: escape$1(codeToHighlight),
2056
- emitter: emitter,
2057
- language: languageName,
2058
- top: top,
2059
- errorRaised: err
2186
+ errorRaised: err,
2187
+ _emitter: emitter,
2188
+ _top: top
2060
2189
  };
2061
2190
  } else {
2062
2191
  throw err;
@@ -2073,13 +2202,13 @@
2073
2202
  */
2074
2203
  function justTextHighlightResult(code) {
2075
2204
  const result = {
2076
- relevance: 0,
2077
- emitter: new options.__emitter(options),
2078
- value: escape$1(code),
2205
+ value: escape(code),
2079
2206
  illegal: false,
2080
- top: PLAINTEXT_LANGUAGE
2207
+ relevance: 0,
2208
+ _top: PLAINTEXT_LANGUAGE,
2209
+ _emitter: new options.__emitter(options)
2081
2210
  };
2082
- result.emitter.addText(code);
2211
+ result._emitter.addText(code);
2083
2212
  return result;
2084
2213
  }
2085
2214
 
@@ -2090,7 +2219,7 @@
2090
2219
  - language (detected language)
2091
2220
  - relevance (int)
2092
2221
  - value (an HTML string with highlighting markup)
2093
- - second_best (object with the same structure for second-best heuristically
2222
+ - secondBest (object with the same structure for second-best heuristically
2094
2223
  detected language, may be absent)
2095
2224
 
2096
2225
  @param {string} code
@@ -2131,35 +2260,11 @@
2131
2260
 
2132
2261
  /** @type {AutoHighlightResult} */
2133
2262
  const result = best;
2134
- result.second_best = secondBest;
2263
+ result.secondBest = secondBest;
2135
2264
 
2136
2265
  return result;
2137
2266
  }
2138
2267
 
2139
- /**
2140
- Post-processing of the highlighted markup:
2141
-
2142
- - replace TABs with something more useful
2143
- - replace real line-breaks with '<br>' for non-pre containers
2144
-
2145
- @param {string} html
2146
- @returns {string}
2147
- */
2148
- function fixMarkup(html) {
2149
- if (!(options.tabReplace || options.useBR)) {
2150
- return html;
2151
- }
2152
-
2153
- return html.replace(fixMarkupRe, match => {
2154
- if (match === '\n') {
2155
- return options.useBR ? '<br>' : match;
2156
- } else if (options.tabReplace) {
2157
- return match.replace(/\t/g, options.tabReplace);
2158
- }
2159
- return match;
2160
- });
2161
- }
2162
-
2163
2268
  /**
2164
2269
  * Builds new class name for block given the language name
2165
2270
  *
@@ -2168,41 +2273,14 @@
2168
2273
  * @param {string} [resultLang]
2169
2274
  */
2170
2275
  function updateClassName(element, currentLang, resultLang) {
2171
- const language = currentLang ? aliases[currentLang] : resultLang;
2276
+ const language = (currentLang && aliases[currentLang]) || resultLang;
2172
2277
 
2173
2278
  element.classList.add("hljs");
2174
- if (language) element.classList.add(language);
2279
+ element.classList.add(`language-${language}`);
2175
2280
  }
2176
2281
 
2177
- /** @type {HLJSPlugin} */
2178
- const brPlugin = {
2179
- "before:highlightElement": ({ el }) => {
2180
- if (options.useBR) {
2181
- el.innerHTML = el.innerHTML.replace(/\n/g, '').replace(/<br[ /]*>/g, '\n');
2182
- }
2183
- },
2184
- "after:highlightElement": ({ result }) => {
2185
- if (options.useBR) {
2186
- result.value = result.value.replace(/\n/g, "<br>");
2187
- }
2188
- }
2189
- };
2190
-
2191
- const TAB_REPLACE_RE = /^(<[^>]+>|\t)+/gm;
2192
- /** @type {HLJSPlugin} */
2193
- const tabReplacePlugin = {
2194
- "after:highlightElement": ({ result }) => {
2195
- if (options.tabReplace) {
2196
- result.value = result.value.replace(TAB_REPLACE_RE, (m) =>
2197
- m.replace(/\t/g, options.tabReplace)
2198
- );
2199
- }
2200
- }
2201
- };
2202
-
2203
2282
  /**
2204
- * Applies highlighting to a DOM node containing code. Accepts a DOM node and
2205
- * two optional parameters for fixMarkup.
2283
+ * Applies highlighting to a DOM node containing code.
2206
2284
  *
2207
2285
  * @param {HighlightedHTMLElement} element - the HTML element to highlight
2208
2286
  */
@@ -2213,33 +2291,50 @@
2213
2291
 
2214
2292
  if (shouldNotHighlight(language)) return;
2215
2293
 
2216
- // support for v10 API
2217
2294
  fire("before:highlightElement",
2218
2295
  { el: element, language: language });
2219
2296
 
2297
+ // we should be all text, no child nodes (unescaped HTML) - this is possibly
2298
+ // an HTML injection attack - it's likely too late if this is already in
2299
+ // production (the code has likely already done its damage by the time
2300
+ // we're seeing it)... but we yell loudly about this so that hopefully it's
2301
+ // more likely to be caught in development before making it to production
2302
+ if (element.children.length > 0) {
2303
+ if (!options.ignoreUnescapedHTML) {
2304
+ console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk.");
2305
+ console.warn("https://github.com/highlightjs/highlight.js/wiki/security");
2306
+ console.warn("The element with unescaped HTML:");
2307
+ console.warn(element);
2308
+ }
2309
+ if (options.throwUnescapedHTML) {
2310
+ const err = new HTMLInjectionError(
2311
+ "One of your code blocks includes unescaped HTML.",
2312
+ element.innerHTML
2313
+ );
2314
+ throw err;
2315
+ }
2316
+ }
2317
+
2220
2318
  node = element;
2221
2319
  const text = node.textContent;
2222
2320
  const result = language ? highlight(text, { language, ignoreIllegals: true }) : highlightAuto(text);
2223
2321
 
2224
- // support for v10 API
2225
- fire("after:highlightElement", { el: element, result, text });
2226
-
2227
2322
  element.innerHTML = result.value;
2228
2323
  updateClassName(element, language, result.language);
2229
2324
  element.result = {
2230
2325
  language: result.language,
2231
2326
  // TODO: remove with version 11.0
2232
2327
  re: result.relevance,
2233
- relavance: result.relevance
2328
+ relevance: result.relevance
2234
2329
  };
2235
- if (result.second_best) {
2236
- element.second_best = {
2237
- language: result.second_best.language,
2238
- // TODO: remove with version 11.0
2239
- re: result.second_best.relevance,
2240
- relavance: result.second_best.relevance
2330
+ if (result.secondBest) {
2331
+ element.secondBest = {
2332
+ language: result.secondBest.language,
2333
+ relevance: result.secondBest.relevance
2241
2334
  };
2242
2335
  }
2336
+
2337
+ fire("after:highlightElement", { el: element, result, text });
2243
2338
  }
2244
2339
 
2245
2340
  /**
@@ -2248,34 +2343,19 @@
2248
2343
  * @param {Partial<HLJSOptions>} userOptions
2249
2344
  */
2250
2345
  function configure(userOptions) {
2251
- if (userOptions.useBR) {
2252
- deprecated("10.3.0", "'useBR' will be removed entirely in v11.0");
2253
- deprecated("10.3.0", "Please see https://github.com/highlightjs/highlight.js/issues/2559");
2254
- }
2255
- options = inherit$1(options, userOptions);
2346
+ options = inherit(options, userOptions);
2256
2347
  }
2257
2348
 
2258
- /**
2259
- * Highlights to all <pre><code> blocks on a page
2260
- *
2261
- * @type {Function & {called?: boolean}}
2262
- */
2263
2349
  // TODO: remove v12, deprecated
2264
2350
  const initHighlighting = () => {
2265
- if (initHighlighting.called) return;
2266
- initHighlighting.called = true;
2267
-
2268
- deprecated("10.6.0", "initHighlighting() is deprecated. Use highlightAll() instead.");
2269
-
2270
- const blocks = document.querySelectorAll('pre code');
2271
- blocks.forEach(highlightElement);
2351
+ highlightAll();
2352
+ deprecated("10.6.0", "initHighlighting() deprecated. Use highlightAll() now.");
2272
2353
  };
2273
2354
 
2274
- // Higlights all when DOMContentLoaded fires
2275
2355
  // TODO: remove v12, deprecated
2276
2356
  function initHighlightingOnLoad() {
2277
- deprecated("10.6.0", "initHighlightingOnLoad() is deprecated. Use highlightAll() instead.");
2278
- wantsHighlight = true;
2357
+ highlightAll();
2358
+ deprecated("10.6.0", "initHighlightingOnLoad() deprecated. Use highlightAll() now.");
2279
2359
  }
2280
2360
 
2281
2361
  let wantsHighlight = false;
@@ -2290,7 +2370,7 @@
2290
2370
  return;
2291
2371
  }
2292
2372
 
2293
- const blocks = document.querySelectorAll('pre code');
2373
+ const blocks = document.querySelectorAll(options.cssSelector);
2294
2374
  blocks.forEach(highlightElement);
2295
2375
  }
2296
2376
 
@@ -2355,26 +2435,6 @@
2355
2435
  return Object.keys(languages);
2356
2436
  }
2357
2437
 
2358
- /**
2359
- intended usage: When one language truly requires another
2360
-
2361
- Unlike `getLanguage`, this will throw when the requested language
2362
- is not available.
2363
-
2364
- @param {string} name - name of the language to fetch/require
2365
- @returns {Language | never}
2366
- */
2367
- function requireLanguage(name) {
2368
- deprecated("10.4.0", "requireLanguage will be removed entirely in v11.");
2369
- deprecated("10.4.0", "Please see https://github.com/highlightjs/highlight.js/pull/2844");
2370
-
2371
- const lang = getLanguage(name);
2372
- if (lang) { return lang; }
2373
-
2374
- const err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}', name));
2375
- throw err;
2376
- }
2377
-
2378
2438
  /**
2379
2439
  * @param {string} name - name of the language to retrieve
2380
2440
  * @returns {Language | undefined}
@@ -2451,20 +2511,7 @@
2451
2511
  }
2452
2512
 
2453
2513
  /**
2454
- Note: fixMarkup is deprecated and will be removed entirely in v11
2455
-
2456
- @param {string} arg
2457
- @returns {string}
2458
- */
2459
- function deprecateFixMarkup(arg) {
2460
- deprecated("10.2.0", "fixMarkup will be removed entirely in v11.0");
2461
- deprecated("10.2.0", "Please see https://github.com/highlightjs/highlight.js/issues/2534");
2462
-
2463
- return fixMarkup(arg);
2464
- }
2465
-
2466
- /**
2467
- *
2514
+ * DEPRECATED
2468
2515
  * @param {HighlightedHTMLElement} el
2469
2516
  */
2470
2517
  function deprecateHighlightBlock(el) {
@@ -2479,7 +2526,6 @@
2479
2526
  highlight,
2480
2527
  highlightAuto,
2481
2528
  highlightAll,
2482
- fixMarkup: deprecateFixMarkup,
2483
2529
  highlightElement,
2484
2530
  // TODO: Remove with v12 API
2485
2531
  highlightBlock: deprecateHighlightBlock,
@@ -2491,394 +2537,45 @@
2491
2537
  listLanguages,
2492
2538
  getLanguage,
2493
2539
  registerAliases,
2494
- requireLanguage,
2495
2540
  autoDetection,
2496
- inherit: inherit$1,
2497
- addPlugin,
2498
- // plugins for frameworks
2499
- vuePlugin: BuildVuePlugin(hljs).VuePlugin
2541
+ inherit,
2542
+ addPlugin
2500
2543
  });
2501
2544
 
2502
2545
  hljs.debugMode = function() { SAFE_MODE = false; };
2503
2546
  hljs.safeMode = function() { SAFE_MODE = true; };
2504
2547
  hljs.versionString = version;
2505
2548
 
2549
+ hljs.regex = {
2550
+ concat: concat,
2551
+ lookahead: lookahead,
2552
+ either: either,
2553
+ optional: optional,
2554
+ anyNumberOfTimes: anyNumberOfTimes
2555
+ };
2556
+
2506
2557
  for (const key in MODES) {
2507
2558
  // @ts-ignore
2508
2559
  if (typeof MODES[key] === "object") {
2509
2560
  // @ts-ignore
2510
- deepFreezeEs6(MODES[key]);
2561
+ deepFreezeEs6.exports(MODES[key]);
2511
2562
  }
2512
2563
  }
2513
2564
 
2514
- // merge all the modes/regexs into our main object
2565
+ // merge all the modes/regexes into our main object
2515
2566
  Object.assign(hljs, MODES);
2516
2567
 
2517
- // built-in plugins, likely to be moved out of core in the future
2518
- hljs.addPlugin(brPlugin); // slated to be removed in v11
2519
- hljs.addPlugin(mergeHTMLPlugin);
2520
- hljs.addPlugin(tabReplacePlugin);
2521
2568
  return hljs;
2522
2569
  };
2523
2570
 
2524
2571
  // export an "instance" of the highlighter
2525
- var highlight$1 = HLJS({});
2526
-
2527
- var core$1 = highlight$1;
2528
-
2529
- function createCommonjsModule(fn) {
2530
- var module = { exports: {} };
2531
- return fn(module, module.exports), module.exports;
2532
- }
2533
-
2534
- var format = createCommonjsModule(function (module) {
2535
- (function() {
2536
-
2537
- //// Export the API
2538
- var namespace;
2539
-
2540
- // CommonJS / Node module
2541
- {
2542
- namespace = module.exports = format;
2543
- }
2544
-
2545
- namespace.format = format;
2546
- namespace.vsprintf = vsprintf;
2547
-
2548
- if (typeof console !== 'undefined' && typeof console.log === 'function') {
2549
- namespace.printf = printf;
2550
- }
2551
-
2552
- function printf(/* ... */) {
2553
- console.log(format.apply(null, arguments));
2554
- }
2555
-
2556
- function vsprintf(fmt, replacements) {
2557
- return format.apply(null, [fmt].concat(replacements));
2558
- }
2559
-
2560
- function format(fmt) {
2561
- var argIndex = 1 // skip initial format argument
2562
- , args = [].slice.call(arguments)
2563
- , i = 0
2564
- , n = fmt.length
2565
- , result = ''
2566
- , c
2567
- , escaped = false
2568
- , arg
2569
- , tmp
2570
- , leadingZero = false
2571
- , precision
2572
- , nextArg = function() { return args[argIndex++]; }
2573
- , slurpNumber = function() {
2574
- var digits = '';
2575
- while (/\d/.test(fmt[i])) {
2576
- digits += fmt[i++];
2577
- c = fmt[i];
2578
- }
2579
- return digits.length > 0 ? parseInt(digits) : null;
2580
- }
2581
- ;
2582
- for (; i < n; ++i) {
2583
- c = fmt[i];
2584
- if (escaped) {
2585
- escaped = false;
2586
- if (c == '.') {
2587
- leadingZero = false;
2588
- c = fmt[++i];
2589
- }
2590
- else if (c == '0' && fmt[i + 1] == '.') {
2591
- leadingZero = true;
2592
- i += 2;
2593
- c = fmt[i];
2594
- }
2595
- else {
2596
- leadingZero = true;
2597
- }
2598
- precision = slurpNumber();
2599
- switch (c) {
2600
- case 'b': // number in binary
2601
- result += parseInt(nextArg(), 10).toString(2);
2602
- break;
2603
- case 'c': // character
2604
- arg = nextArg();
2605
- if (typeof arg === 'string' || arg instanceof String)
2606
- result += arg;
2607
- else
2608
- result += String.fromCharCode(parseInt(arg, 10));
2609
- break;
2610
- case 'd': // number in decimal
2611
- result += parseInt(nextArg(), 10);
2612
- break;
2613
- case 'f': // floating point number
2614
- tmp = String(parseFloat(nextArg()).toFixed(precision || 6));
2615
- result += leadingZero ? tmp : tmp.replace(/^0/, '');
2616
- break;
2617
- case 'j': // JSON
2618
- result += JSON.stringify(nextArg());
2619
- break;
2620
- case 'o': // number in octal
2621
- result += '0' + parseInt(nextArg(), 10).toString(8);
2622
- break;
2623
- case 's': // string
2624
- result += nextArg();
2625
- break;
2626
- case 'x': // lowercase hexadecimal
2627
- result += '0x' + parseInt(nextArg(), 10).toString(16);
2628
- break;
2629
- case 'X': // uppercase hexadecimal
2630
- result += '0x' + parseInt(nextArg(), 10).toString(16).toUpperCase();
2631
- break;
2632
- default:
2633
- result += c;
2634
- break;
2635
- }
2636
- } else if (c === '%') {
2637
- escaped = true;
2638
- } else {
2639
- result += c;
2640
- }
2641
- }
2642
- return result;
2643
- }
2644
-
2645
- }());
2646
- });
2647
-
2648
- var fault = create(Error);
2649
-
2650
- var fault_1 = fault;
2651
-
2652
- fault.eval = create(EvalError);
2653
- fault.range = create(RangeError);
2654
- fault.reference = create(ReferenceError);
2655
- fault.syntax = create(SyntaxError);
2656
- fault.type = create(TypeError);
2657
- fault.uri = create(URIError);
2658
-
2659
- fault.create = create;
2660
-
2661
- // Create a new `EConstructor`, with the formatted `format` as a first argument.
2662
- function create(EConstructor) {
2663
- FormattedError.displayName = EConstructor.displayName || EConstructor.name;
2664
-
2665
- return FormattedError
2666
-
2667
- function FormattedError(format$1) {
2668
- if (format$1) {
2669
- format$1 = format.apply(null, arguments);
2670
- }
2671
-
2672
- return new EConstructor(format$1)
2673
- }
2674
- }
2675
-
2676
- var highlight_1 = highlight;
2677
- var highlightAuto_1 = highlightAuto;
2678
- var registerLanguage_1 = registerLanguage;
2679
- var listLanguages_1 = listLanguages;
2680
- var registerAlias_1 = registerAlias;
2681
-
2682
- Emitter.prototype.addText = text;
2683
- Emitter.prototype.addKeyword = addKeyword;
2684
- Emitter.prototype.addSublanguage = addSublanguage;
2685
- Emitter.prototype.openNode = open;
2686
- Emitter.prototype.closeNode = close;
2687
- Emitter.prototype.closeAllNodes = noop;
2688
- Emitter.prototype.finalize = noop;
2689
- Emitter.prototype.toHTML = toHtmlNoop;
2690
-
2691
- var defaultPrefix = 'hljs-';
2692
-
2693
- // Highlighting `value` in the language `name`.
2694
- function highlight(name, value, options) {
2695
- var before = core$1.configure({});
2696
- var settings = options || {};
2697
- var prefix = settings.prefix;
2698
- var result;
2699
-
2700
- if (typeof name !== 'string') {
2701
- throw fault_1('Expected `string` for name, got `%s`', name)
2702
- }
2703
-
2704
- if (!core$1.getLanguage(name)) {
2705
- throw fault_1('Unknown language: `%s` is not registered', name)
2706
- }
2707
-
2708
- if (typeof value !== 'string') {
2709
- throw fault_1('Expected `string` for value, got `%s`', value)
2710
- }
2711
-
2712
- if (prefix === null || prefix === undefined) {
2713
- prefix = defaultPrefix;
2714
- }
2715
-
2716
- core$1.configure({__emitter: Emitter, classPrefix: prefix});
2717
-
2718
- result = core$1.highlight(value, {language: name, ignoreIllegals: true});
2719
-
2720
- core$1.configure(before || {});
2721
-
2722
- /* istanbul ignore if - Highlight.js seems to use this (currently) for broken
2723
- * grammars, so let’s keep it in there just to be sure. */
2724
- if (result.errorRaised) {
2725
- throw result.errorRaised
2726
- }
2727
-
2728
- return {
2729
- relevance: result.relevance,
2730
- language: result.language,
2731
- value: result.emitter.rootNode.children
2732
- }
2733
- }
2734
-
2735
- function highlightAuto(value, options) {
2736
- var settings = options || {};
2737
- var subset = settings.subset || core$1.listLanguages();
2738
- settings.prefix;
2739
- var length = subset.length;
2740
- var index = -1;
2741
- var result;
2742
- var secondBest;
2743
- var current;
2744
- var name;
2745
-
2746
- if (typeof value !== 'string') {
2747
- throw fault_1('Expected `string` for value, got `%s`', value)
2748
- }
2749
-
2750
- secondBest = {relevance: 0, language: null, value: []};
2751
- result = {relevance: 0, language: null, value: []};
2752
-
2753
- while (++index < length) {
2754
- name = subset[index];
2755
-
2756
- if (!core$1.getLanguage(name)) {
2757
- continue
2758
- }
2759
-
2760
- current = highlight(name, value, options);
2761
- current.language = name;
2762
-
2763
- if (current.relevance > secondBest.relevance) {
2764
- secondBest = current;
2765
- }
2766
-
2767
- if (current.relevance > result.relevance) {
2768
- secondBest = result;
2769
- result = current;
2770
- }
2771
- }
2772
-
2773
- if (secondBest.language) {
2774
- result.secondBest = secondBest;
2775
- }
2776
-
2777
- return result
2778
- }
2779
-
2780
- // Register a language.
2781
- function registerLanguage(name, syntax) {
2782
- core$1.registerLanguage(name, syntax);
2783
- }
2784
-
2785
- // Get a list of all registered languages.
2786
- function listLanguages() {
2787
- return core$1.listLanguages()
2788
- }
2789
-
2790
- // Register more aliases for an already registered language.
2791
- function registerAlias(name, alias) {
2792
- var map = name;
2793
- var key;
2794
-
2795
- if (alias) {
2796
- map = {};
2797
- map[name] = alias;
2798
- }
2799
-
2800
- for (key in map) {
2801
- core$1.registerAliases(map[key], {languageName: key});
2802
- }
2803
- }
2804
-
2805
- function Emitter(options) {
2806
- this.options = options;
2807
- this.rootNode = {children: []};
2808
- this.stack = [this.rootNode];
2809
- }
2810
-
2811
- function addKeyword(value, name) {
2812
- this.openNode(name);
2813
- this.addText(value);
2814
- this.closeNode();
2815
- }
2816
-
2817
- function addSublanguage(other, name) {
2818
- var stack = this.stack;
2819
- var current = stack[stack.length - 1];
2820
- var results = other.rootNode.children;
2821
- var node = name
2822
- ? {
2823
- type: 'element',
2824
- tagName: 'span',
2825
- properties: {className: [name]},
2826
- children: results
2827
- }
2828
- : results;
2829
-
2830
- current.children = current.children.concat(node);
2831
- }
2832
-
2833
- function text(value) {
2834
- var stack = this.stack;
2835
- var current;
2836
- var tail;
2837
-
2838
- if (value === '') return
2839
-
2840
- current = stack[stack.length - 1];
2841
- tail = current.children[current.children.length - 1];
2842
-
2843
- if (tail && tail.type === 'text') {
2844
- tail.value += value;
2845
- } else {
2846
- current.children.push({type: 'text', value: value});
2847
- }
2848
- }
2849
-
2850
- function open(name) {
2851
- var stack = this.stack;
2852
- var className = this.options.classPrefix + name;
2853
- var current = stack[stack.length - 1];
2854
- var child = {
2855
- type: 'element',
2856
- tagName: 'span',
2857
- properties: {className: [className]},
2858
- children: []
2859
- };
2860
-
2861
- current.children.push(child);
2862
- stack.push(child);
2863
- }
2864
-
2865
- function close() {
2866
- this.stack.pop();
2867
- }
2868
-
2869
- function toHtmlNoop() {
2870
- return ''
2871
- }
2572
+ var highlight = HLJS({});
2872
2573
 
2873
- function noop() {}
2574
+ var core = highlight;
2575
+ highlight.HighlightJS = highlight;
2576
+ highlight.default = highlight;
2874
2577
 
2875
- var core = {
2876
- highlight: highlight_1,
2877
- highlightAuto: highlightAuto_1,
2878
- registerLanguage: registerLanguage_1,
2879
- listLanguages: listLanguages_1,
2880
- registerAlias: registerAlias_1
2881
- };
2578
+ var HighlightJS = core;
2882
2579
 
2883
2580
  function parseNodes(nodes, className = []) {
2884
2581
  return nodes
@@ -2899,16 +2596,23 @@
2899
2596
  })
2900
2597
  .flat();
2901
2598
  }
2902
- function getDecorations({ doc, name, lowlight }) {
2599
+ function getHighlightNodes(result) {
2600
+ // `.value` for lowlight v1, `.children` for lowlight v2
2601
+ return result.value || result.children || [];
2602
+ }
2603
+ function registered(aliasOrLanguage) {
2604
+ return Boolean(HighlightJS.getLanguage(aliasOrLanguage));
2605
+ }
2606
+ function getDecorations({ doc, name, lowlight, defaultLanguage, }) {
2903
2607
  const decorations = [];
2904
- core$2.findChildren(doc, node => node.type.name === name)
2608
+ core$1.findChildren(doc, node => node.type.name === name)
2905
2609
  .forEach(block => {
2906
2610
  let from = block.pos + 1;
2907
- const { language } = block.node.attrs;
2611
+ const language = block.node.attrs.language || defaultLanguage;
2908
2612
  const languages = lowlight.listLanguages();
2909
- const nodes = language && languages.includes(language)
2910
- ? lowlight.highlight(language, block.node.textContent).value
2911
- : lowlight.highlightAuto(block.node.textContent).value;
2613
+ const nodes = language && (languages.includes(language) || registered(language))
2614
+ ? getHighlightNodes(lowlight.highlight(language, block.node.textContent))
2615
+ : getHighlightNodes(lowlight.highlightAuto(block.node.textContent));
2912
2616
  parseNodes(nodes).forEach(node => {
2913
2617
  const to = from + node.text.length;
2914
2618
  if (node.classes.length) {
@@ -2922,16 +2626,27 @@
2922
2626
  });
2923
2627
  return prosemirrorView.DecorationSet.create(doc, decorations);
2924
2628
  }
2925
- function LowlightPlugin({ name, lowlight }) {
2926
- return new prosemirrorState.Plugin({
2629
+ function isFunction(param) {
2630
+ return typeof param === 'function';
2631
+ }
2632
+ function LowlightPlugin({ name, lowlight, defaultLanguage }) {
2633
+ if (!['highlight', 'highlightAuto', 'listLanguages'].every(api => isFunction(lowlight[api]))) {
2634
+ throw Error('You should provide an instance of lowlight to use the code-block-lowlight extension');
2635
+ }
2636
+ const lowlightPlugin = new prosemirrorState.Plugin({
2927
2637
  key: new prosemirrorState.PluginKey('lowlight'),
2928
2638
  state: {
2929
- init: (_, { doc }) => getDecorations({ doc, name, lowlight }),
2639
+ init: (_, { doc }) => getDecorations({
2640
+ doc,
2641
+ name,
2642
+ lowlight,
2643
+ defaultLanguage,
2644
+ }),
2930
2645
  apply: (transaction, decorationSet, oldState, newState) => {
2931
2646
  const oldNodeName = oldState.selection.$head.parent.type.name;
2932
2647
  const newNodeName = newState.selection.$head.parent.type.name;
2933
- const oldNodes = core$2.findChildren(oldState.doc, node => node.type.name === name);
2934
- const newNodes = core$2.findChildren(newState.doc, node => node.type.name === name);
2648
+ const oldNodes = core$1.findChildren(oldState.doc, node => node.type.name === name);
2649
+ const newNodes = core$1.findChildren(newState.doc, node => node.type.name === name);
2935
2650
  if (transaction.docChanged
2936
2651
  // Apply decorations if:
2937
2652
  && (
@@ -2954,40 +2669,51 @@
2954
2669
  && node.pos + node.node.nodeSize <= step.to;
2955
2670
  });
2956
2671
  }))) {
2957
- return getDecorations({ doc: transaction.doc, name, lowlight });
2672
+ return getDecorations({
2673
+ doc: transaction.doc,
2674
+ name,
2675
+ lowlight,
2676
+ defaultLanguage,
2677
+ });
2958
2678
  }
2959
2679
  return decorationSet.map(transaction.mapping, transaction.doc);
2960
2680
  },
2961
2681
  },
2962
2682
  props: {
2963
2683
  decorations(state) {
2964
- return this.getState(state);
2684
+ return lowlightPlugin.getState(state);
2965
2685
  },
2966
2686
  },
2967
2687
  });
2688
+ return lowlightPlugin;
2968
2689
  }
2969
2690
 
2970
- const CodeBlockLowlight = CodeBlock__default['default'].extend({
2971
- defaultOptions: {
2972
- ...CodeBlock__default['default'].options,
2973
- lowlight: core,
2691
+ const CodeBlockLowlight = CodeBlock__default["default"].extend({
2692
+ addOptions() {
2693
+ var _a;
2694
+ return {
2695
+ ...(_a = this.parent) === null || _a === void 0 ? void 0 : _a.call(this),
2696
+ lowlight: {},
2697
+ defaultLanguage: null,
2698
+ };
2974
2699
  },
2975
2700
  addProseMirrorPlugins() {
2976
2701
  var _a;
2977
2702
  return [
2978
2703
  ...((_a = this.parent) === null || _a === void 0 ? void 0 : _a.call(this)) || [],
2979
2704
  LowlightPlugin({
2980
- name: 'codeBlock',
2705
+ name: this.name,
2981
2706
  lowlight: this.options.lowlight,
2707
+ defaultLanguage: this.options.defaultLanguage,
2982
2708
  }),
2983
2709
  ];
2984
2710
  },
2985
2711
  });
2986
2712
 
2987
2713
  exports.CodeBlockLowlight = CodeBlockLowlight;
2988
- exports.default = CodeBlockLowlight;
2714
+ exports["default"] = CodeBlockLowlight;
2989
2715
 
2990
2716
  Object.defineProperty(exports, '__esModule', { value: true });
2991
2717
 
2992
- })));
2718
+ }));
2993
2719
  //# sourceMappingURL=tiptap-extension-code-block-lowlight.umd.js.map