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