@lexical/code 0.12.1 → 0.12.3

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.
@@ -31,60 +31,50 @@ var lexical = require('lexical');
31
31
  * LICENSE file in the root directory of this source tree.
32
32
  *
33
33
  */
34
-
35
34
  const mapToPrismLanguage = language => {
36
35
  // eslint-disable-next-line no-prototype-builtins
37
36
  return language != null && Prism.languages.hasOwnProperty(language) ? language : undefined;
38
37
  };
39
-
40
38
  function hasChildDOMNodeTag(node, tagName) {
41
39
  for (const child of node.childNodes) {
42
40
  if (utils.isHTMLElement(child) && child.tagName === tagName) {
43
41
  return true;
44
42
  }
45
-
46
43
  hasChildDOMNodeTag(child, tagName);
47
44
  }
48
-
49
45
  return false;
50
46
  }
51
-
52
47
  const LANGUAGE_DATA_ATTRIBUTE = 'data-highlight-language';
53
- /** @noInheritDoc */
54
48
 
49
+ /** @noInheritDoc */
55
50
  class CodeNode extends lexical.ElementNode {
56
51
  /** @internal */
52
+
57
53
  static getType() {
58
54
  return 'code';
59
55
  }
60
-
61
56
  static clone(node) {
62
57
  return new CodeNode(node.__language, node.__key);
63
58
  }
64
-
65
59
  constructor(language, key) {
66
60
  super(key);
67
61
  this.__language = mapToPrismLanguage(language);
68
- } // View
69
-
62
+ }
70
63
 
64
+ // View
71
65
  createDOM(config) {
72
66
  const element = document.createElement('code');
73
67
  utils.addClassNamesToElement(element, config.theme.code);
74
68
  element.setAttribute('spellcheck', 'false');
75
69
  const language = this.getLanguage();
76
-
77
70
  if (language) {
78
71
  element.setAttribute(LANGUAGE_DATA_ATTRIBUTE, language);
79
72
  }
80
-
81
73
  return element;
82
74
  }
83
-
84
75
  updateDOM(prevNode, dom, config) {
85
76
  const language = this.__language;
86
77
  const prevLanguage = prevNode.__language;
87
-
88
78
  if (language) {
89
79
  if (language !== prevLanguage) {
90
80
  dom.setAttribute(LANGUAGE_DATA_ATTRIBUTE, language);
@@ -92,24 +82,19 @@ class CodeNode extends lexical.ElementNode {
92
82
  } else if (prevLanguage) {
93
83
  dom.removeAttribute(LANGUAGE_DATA_ATTRIBUTE);
94
84
  }
95
-
96
85
  return false;
97
86
  }
98
-
99
87
  exportDOM() {
100
88
  const element = document.createElement('pre');
101
89
  element.setAttribute('spellcheck', 'false');
102
90
  const language = this.getLanguage();
103
-
104
91
  if (language) {
105
92
  element.setAttribute(LANGUAGE_DATA_ATTRIBUTE, language);
106
93
  }
107
-
108
94
  return {
109
95
  element
110
96
  };
111
97
  }
112
-
113
98
  static importDOM() {
114
99
  return {
115
100
  // Typically <pre> is used for code blocks, and <code> for inline code styles
@@ -131,29 +116,26 @@ class CodeNode extends lexical.ElementNode {
131
116
  priority: 0
132
117
  }),
133
118
  table: node => {
134
- const table = node; // domNode is a <table> since we matched it by nodeName
135
-
119
+ const table = node;
120
+ // domNode is a <table> since we matched it by nodeName
136
121
  if (isGitHubCodeTable(table)) {
137
122
  return {
138
123
  conversion: convertTableElement,
139
124
  priority: 3
140
125
  };
141
126
  }
142
-
143
127
  return null;
144
128
  },
145
129
  td: node => {
146
130
  // element is a <td> since we matched it by nodeName
147
131
  const td = node;
148
132
  const table = td.closest('table');
149
-
150
133
  if (isGitHubCodeCell(td)) {
151
134
  return {
152
135
  conversion: convertTableCellElement,
153
136
  priority: 3
154
137
  };
155
138
  }
156
-
157
139
  if (table && isGitHubCodeTable(table)) {
158
140
  // Return a no-op if it's a table cell in a code table, but not a code line.
159
141
  // Otherwise it'll fall back to the T
@@ -162,26 +144,22 @@ class CodeNode extends lexical.ElementNode {
162
144
  priority: 3
163
145
  };
164
146
  }
165
-
166
147
  return null;
167
148
  },
168
149
  tr: node => {
169
150
  // element is a <tr> since we matched it by nodeName
170
151
  const tr = node;
171
152
  const table = tr.closest('table');
172
-
173
153
  if (table && isGitHubCodeTable(table)) {
174
154
  return {
175
155
  conversion: convertCodeNoop,
176
156
  priority: 3
177
157
  };
178
158
  }
179
-
180
159
  return null;
181
160
  }
182
161
  };
183
162
  }
184
-
185
163
  static importJSON(serializedNode) {
186
164
  const node = $createCodeNode(serializedNode.language);
187
165
  node.setFormat(serializedNode.format);
@@ -189,40 +167,40 @@ class CodeNode extends lexical.ElementNode {
189
167
  node.setDirection(serializedNode.direction);
190
168
  return node;
191
169
  }
192
-
193
170
  exportJSON() {
194
- return { ...super.exportJSON(),
171
+ return {
172
+ ...super.exportJSON(),
195
173
  language: this.getLanguage(),
196
174
  type: 'code',
197
175
  version: 1
198
176
  };
199
- } // Mutation
200
-
177
+ }
201
178
 
179
+ // Mutation
202
180
  insertNewAfter(selection, restoreSelection = true) {
203
181
  const children = this.getChildren();
204
182
  const childrenLength = children.length;
205
-
206
183
  if (childrenLength >= 2 && children[childrenLength - 1].getTextContent() === '\n' && children[childrenLength - 2].getTextContent() === '\n' && selection.isCollapsed() && selection.anchor.key === this.__key && selection.anchor.offset === childrenLength) {
207
184
  children[childrenLength - 1].remove();
208
185
  children[childrenLength - 2].remove();
209
186
  const newElement = lexical.$createParagraphNode();
210
187
  this.insertAfter(newElement, restoreSelection);
211
188
  return newElement;
212
- } // If the selection is within the codeblock, find all leading tabs and
189
+ }
190
+
191
+ // If the selection is within the codeblock, find all leading tabs and
213
192
  // spaces of the current line. Create a new line that has all those
214
193
  // tabs and spaces, such that leading indentation is preserved.
215
-
216
-
217
- const anchor = selection.anchor;
218
- const focus = selection.focus;
194
+ const {
195
+ anchor,
196
+ focus
197
+ } = selection;
219
198
  const firstPoint = anchor.isBefore(focus) ? anchor : focus;
220
199
  const firstSelectionNode = firstPoint.getNode();
221
-
222
- if ($isCodeHighlightNode(firstSelectionNode) || lexical.$isTabNode(firstSelectionNode)) {
200
+ if (lexical.$isTextNode(firstSelectionNode)) {
223
201
  let node = getFirstCodeNodeOfLine(firstSelectionNode);
224
- const insertNodes = []; // eslint-disable-next-line no-constant-condition
225
-
202
+ const insertNodes = [];
203
+ // eslint-disable-next-line no-constant-condition
226
204
  while (true) {
227
205
  if (lexical.$isTabNode(node)) {
228
206
  insertNodes.push(lexical.$createTabNode());
@@ -231,36 +209,45 @@ class CodeNode extends lexical.ElementNode {
231
209
  let spaces = 0;
232
210
  const text = node.getTextContent();
233
211
  const textSize = node.getTextContentSize();
234
-
235
212
  for (; spaces < textSize && text[spaces] === ' '; spaces++);
236
-
237
213
  if (spaces !== 0) {
238
214
  insertNodes.push($createCodeHighlightNode(' '.repeat(spaces)));
239
215
  }
240
-
241
216
  if (spaces !== textSize) {
242
217
  break;
243
218
  }
244
-
245
219
  node = node.getNextSibling();
246
220
  } else {
247
221
  break;
248
222
  }
249
223
  }
250
-
251
- if (insertNodes.length > 0) {
252
- selection.insertNodes([lexical.$createLineBreakNode(), ...insertNodes]);
253
- return insertNodes[insertNodes.length - 1];
224
+ const split = firstSelectionNode.splitText(anchor.offset)[0];
225
+ const x = anchor.offset === 0 ? 0 : 1;
226
+ const index = split.getIndexWithinParent() + x;
227
+ const codeNode = firstSelectionNode.getParentOrThrow();
228
+ const nodesToInsert = [lexical.$createLineBreakNode(), ...insertNodes];
229
+ codeNode.splice(index, 0, nodesToInsert);
230
+ const last = insertNodes[insertNodes.length - 1];
231
+ if (last) {
232
+ last.select();
233
+ } else if (anchor.offset === 0) {
234
+ split.selectPrevious();
235
+ } else {
236
+ split.getNextSibling().selectNext(0, 0);
254
237
  }
255
238
  }
256
-
239
+ if ($isCodeNode(firstSelectionNode)) {
240
+ const {
241
+ offset
242
+ } = selection.anchor;
243
+ firstSelectionNode.splice(offset, 0, [lexical.$createLineBreakNode()]);
244
+ firstSelectionNode.select(offset + 1, offset + 1);
245
+ }
257
246
  return null;
258
247
  }
259
-
260
248
  canIndent() {
261
249
  return false;
262
250
  }
263
-
264
251
  collapseAtStart() {
265
252
  const paragraph = lexical.$createParagraphNode();
266
253
  const children = this.getChildren();
@@ -268,16 +255,13 @@ class CodeNode extends lexical.ElementNode {
268
255
  this.replace(paragraph);
269
256
  return true;
270
257
  }
271
-
272
258
  setLanguage(language) {
273
259
  const writable = this.getWritable();
274
260
  writable.__language = mapToPrismLanguage(language);
275
261
  }
276
-
277
262
  getLanguage() {
278
263
  return this.getLatest().__language;
279
264
  }
280
-
281
265
  }
282
266
  function $createCodeNode(language) {
283
267
  return lexical.$applyNodeReplacement(new CodeNode(language));
@@ -285,56 +269,45 @@ function $createCodeNode(language) {
285
269
  function $isCodeNode(node) {
286
270
  return node instanceof CodeNode;
287
271
  }
288
-
289
272
  function convertPreElement(domNode) {
290
273
  let language;
291
-
292
274
  if (utils.isHTMLElement(domNode)) {
293
275
  language = domNode.getAttribute(LANGUAGE_DATA_ATTRIBUTE);
294
276
  }
295
-
296
277
  return {
297
278
  node: $createCodeNode(language)
298
279
  };
299
280
  }
300
-
301
281
  function convertDivElement(domNode) {
302
282
  // domNode is a <div> since we matched it by nodeName
303
283
  const div = domNode;
304
284
  const isCode = isCodeElement(div);
305
-
306
285
  if (!isCode && !isCodeChildElement(div)) {
307
286
  return {
308
287
  node: null
309
288
  };
310
289
  }
311
-
312
290
  return {
313
291
  after: childLexicalNodes => {
314
292
  const domParent = domNode.parentNode;
315
-
316
293
  if (domParent != null && domNode !== domParent.lastChild) {
317
294
  childLexicalNodes.push(lexical.$createLineBreakNode());
318
295
  }
319
-
320
296
  return childLexicalNodes;
321
297
  },
322
298
  node: isCode ? $createCodeNode() : null
323
299
  };
324
300
  }
325
-
326
301
  function convertTableElement() {
327
302
  return {
328
303
  node: $createCodeNode()
329
304
  };
330
305
  }
331
-
332
306
  function convertCodeNoop() {
333
307
  return {
334
308
  node: null
335
309
  };
336
310
  }
337
-
338
311
  function convertTableCellElement(domNode) {
339
312
  // domNode is a <td> since we matched it by nodeName
340
313
  const cell = domNode;
@@ -344,35 +317,27 @@ function convertTableCellElement(domNode) {
344
317
  // Append newline between code lines
345
318
  childLexicalNodes.push(lexical.$createLineBreakNode());
346
319
  }
347
-
348
320
  return childLexicalNodes;
349
321
  },
350
322
  node: null
351
323
  };
352
324
  }
353
-
354
325
  function isCodeElement(div) {
355
326
  return div.style.fontFamily.match('monospace') !== null;
356
327
  }
357
-
358
328
  function isCodeChildElement(node) {
359
329
  let parent = node.parentElement;
360
-
361
330
  while (parent !== null) {
362
331
  if (isCodeElement(parent)) {
363
332
  return true;
364
333
  }
365
-
366
334
  parent = parent.parentElement;
367
335
  }
368
-
369
336
  return false;
370
337
  }
371
-
372
338
  function isGitHubCodeCell(cell) {
373
339
  return cell.classList.contains('js-file-line');
374
340
  }
375
-
376
341
  function isGitHubCodeTable(table) {
377
342
  return table.classList.contains('js-file-line-container');
378
343
  }
@@ -418,60 +383,52 @@ function normalizeCodeLang(lang) {
418
383
  }
419
384
  function getLanguageFriendlyName(lang) {
420
385
  const _lang = normalizeCodeLang(lang);
421
-
422
386
  return CODE_LANGUAGE_FRIENDLY_NAME_MAP[_lang] || _lang;
423
387
  }
424
388
  const getDefaultCodeLanguage = () => DEFAULT_CODE_LANGUAGE;
425
- const getCodeLanguages = () => Object.keys(Prism.languages).filter( // Prism has several language helpers mixed into languages object
389
+ const getCodeLanguages = () => Object.keys(Prism.languages).filter(
390
+ // Prism has several language helpers mixed into languages object
426
391
  // so filtering them out here to get langs list
427
392
  language => typeof Prism.languages[language] !== 'function').sort();
428
- /** @noInheritDoc */
429
393
 
394
+ /** @noInheritDoc */
430
395
  class CodeHighlightNode extends lexical.TextNode {
431
396
  /** @internal */
397
+
432
398
  constructor(text, highlightType, key) {
433
399
  super(text, key);
434
400
  this.__highlightType = highlightType;
435
401
  }
436
-
437
402
  static getType() {
438
403
  return 'code-highlight';
439
404
  }
440
-
441
405
  static clone(node) {
442
406
  return new CodeHighlightNode(node.__text, node.__highlightType || undefined, node.__key);
443
407
  }
444
-
445
408
  getHighlightType() {
446
409
  const self = this.getLatest();
447
410
  return self.__highlightType;
448
411
  }
449
-
450
412
  createDOM(config) {
451
413
  const element = super.createDOM(config);
452
414
  const className = getHighlightThemeClass(config.theme, this.__highlightType);
453
415
  utils.addClassNamesToElement(element, className);
454
416
  return element;
455
417
  }
456
-
457
418
  updateDOM(prevNode, dom, config) {
458
419
  const update = super.updateDOM(prevNode, dom, config);
459
420
  const prevClassName = getHighlightThemeClass(config.theme, prevNode.__highlightType);
460
421
  const nextClassName = getHighlightThemeClass(config.theme, this.__highlightType);
461
-
462
422
  if (prevClassName !== nextClassName) {
463
423
  if (prevClassName) {
464
424
  utils.removeClassNamesFromElement(dom, prevClassName);
465
425
  }
466
-
467
426
  if (nextClassName) {
468
427
  utils.addClassNamesToElement(dom, nextClassName);
469
428
  }
470
429
  }
471
-
472
430
  return update;
473
431
  }
474
-
475
432
  static importJSON(serializedNode) {
476
433
  const node = $createCodeHighlightNode(serializedNode.text, serializedNode.highlightType);
477
434
  node.setFormat(serializedNode.format);
@@ -480,34 +437,29 @@ class CodeHighlightNode extends lexical.TextNode {
480
437
  node.setStyle(serializedNode.style);
481
438
  return node;
482
439
  }
483
-
484
440
  exportJSON() {
485
- return { ...super.exportJSON(),
441
+ return {
442
+ ...super.exportJSON(),
486
443
  highlightType: this.getHighlightType(),
487
444
  type: 'code-highlight',
488
445
  version: 1
489
446
  };
490
- } // Prevent formatting (bold, underline, etc)
491
-
447
+ }
492
448
 
449
+ // Prevent formatting (bold, underline, etc)
493
450
  setFormat(format) {
494
451
  return this;
495
452
  }
496
-
497
453
  isParentRequired() {
498
454
  return true;
499
455
  }
500
-
501
456
  createParentElementNode() {
502
457
  return $createCodeNode();
503
458
  }
504
-
505
459
  }
506
-
507
460
  function getHighlightThemeClass(theme, highlightType) {
508
461
  return highlightType && theme && theme.codeHighlight && theme.codeHighlight[highlightType];
509
462
  }
510
-
511
463
  function $createCodeHighlightNode(text, highlightType) {
512
464
  return lexical.$applyNodeReplacement(new CodeHighlightNode(text, highlightType));
513
465
  }
@@ -517,23 +469,19 @@ function $isCodeHighlightNode(node) {
517
469
  function getFirstCodeNodeOfLine(anchor) {
518
470
  let previousNode = anchor;
519
471
  let node = anchor;
520
-
521
472
  while ($isCodeHighlightNode(node) || lexical.$isTabNode(node)) {
522
473
  previousNode = node;
523
474
  node = node.getPreviousSibling();
524
475
  }
525
-
526
476
  return previousNode;
527
477
  }
528
478
  function getLastCodeNodeOfLine(anchor) {
529
479
  let nextNode = anchor;
530
480
  let node = anchor;
531
-
532
481
  while ($isCodeHighlightNode(node) || lexical.$isTabNode(node)) {
533
482
  nextNode = node;
534
483
  node = node.getNextSibling();
535
484
  }
536
-
537
485
  return nextNode;
538
486
  }
539
487
 
@@ -546,31 +494,26 @@ function getLastCodeNodeOfLine(anchor) {
546
494
  */
547
495
  const PrismTokenizer = {
548
496
  defaultLanguage: DEFAULT_CODE_LANGUAGE,
549
-
550
497
  tokenize(code, language) {
551
498
  return Prism.tokenize(code, Prism.languages[language || ''] || Prism.languages[this.defaultLanguage]);
552
499
  }
553
-
554
500
  };
555
501
  function getStartOfCodeInLine(anchor, offset) {
556
502
  let last = null;
557
503
  let lastNonBlank = null;
558
504
  let node = anchor;
559
505
  let nodeOffset = offset;
560
- let nodeTextContent = anchor.getTextContent(); // eslint-disable-next-line no-constant-condition
561
-
506
+ let nodeTextContent = anchor.getTextContent();
507
+ // eslint-disable-next-line no-constant-condition
562
508
  while (true) {
563
509
  if (nodeOffset === 0) {
564
510
  node = node.getPreviousSibling();
565
-
566
511
  if (node === null) {
567
512
  break;
568
513
  }
569
-
570
514
  if (!($isCodeHighlightNode(node) || lexical.$isTabNode(node) || lexical.$isLineBreakNode(node))) {
571
515
  throw Error(`Expected a valid Code Node: CodeHighlightNode, TabNode, LineBreakNode`);
572
516
  }
573
-
574
517
  if (lexical.$isLineBreakNode(node)) {
575
518
  last = {
576
519
  node,
@@ -578,49 +521,40 @@ function getStartOfCodeInLine(anchor, offset) {
578
521
  };
579
522
  break;
580
523
  }
581
-
582
524
  nodeOffset = Math.max(0, node.getTextContentSize() - 1);
583
525
  nodeTextContent = node.getTextContent();
584
526
  } else {
585
527
  nodeOffset--;
586
528
  }
587
-
588
529
  const character = nodeTextContent[nodeOffset];
589
-
590
530
  if ($isCodeHighlightNode(node) && character !== ' ') {
591
531
  lastNonBlank = {
592
532
  node,
593
533
  offset: nodeOffset
594
534
  };
595
535
  }
596
- } // lastNonBlank !== null: anchor in the middle of code; move to line beginning
597
-
598
-
536
+ }
537
+ // lastNonBlank !== null: anchor in the middle of code; move to line beginning
599
538
  if (lastNonBlank !== null) {
600
539
  return lastNonBlank;
601
- } // Spaces, tabs or nothing ahead of anchor
602
-
603
-
540
+ }
541
+ // Spaces, tabs or nothing ahead of anchor
604
542
  let codeCharacterAtAnchorOffset = null;
605
-
606
543
  if (offset < anchor.getTextContentSize()) {
607
544
  if ($isCodeHighlightNode(anchor)) {
608
545
  codeCharacterAtAnchorOffset = anchor.getTextContent()[offset];
609
546
  }
610
547
  } else {
611
548
  const nextSibling = anchor.getNextSibling();
612
-
613
549
  if ($isCodeHighlightNode(nextSibling)) {
614
550
  codeCharacterAtAnchorOffset = nextSibling.getTextContent()[0];
615
551
  }
616
552
  }
617
-
618
553
  if (codeCharacterAtAnchorOffset !== null && codeCharacterAtAnchorOffset !== ' ') {
619
554
  // Borderline whitespace and code, move to line beginning
620
555
  return last;
621
556
  } else {
622
557
  const nextNonBlank = findNextNonBlankInLine(anchor, offset);
623
-
624
558
  if (nextNonBlank !== null) {
625
559
  return nextNonBlank;
626
560
  } else {
@@ -628,28 +562,24 @@ function getStartOfCodeInLine(anchor, offset) {
628
562
  }
629
563
  }
630
564
  }
631
-
632
565
  function findNextNonBlankInLine(anchor, offset) {
633
566
  let node = anchor;
634
567
  let nodeOffset = offset;
635
568
  let nodeTextContent = anchor.getTextContent();
636
- let nodeTextContentSize = anchor.getTextContentSize(); // eslint-disable-next-line no-constant-condition
637
-
569
+ let nodeTextContentSize = anchor.getTextContentSize();
570
+ // eslint-disable-next-line no-constant-condition
638
571
  while (true) {
639
572
  if (!$isCodeHighlightNode(node) || nodeOffset === nodeTextContentSize) {
640
573
  node = node.getNextSibling();
641
-
642
574
  if (node === null || lexical.$isLineBreakNode(node)) {
643
575
  return null;
644
576
  }
645
-
646
577
  if ($isCodeHighlightNode(node)) {
647
578
  nodeOffset = 0;
648
579
  nodeTextContent = node.getTextContent();
649
580
  nodeTextContentSize = node.getTextContentSize();
650
581
  }
651
582
  }
652
-
653
583
  if ($isCodeHighlightNode(node)) {
654
584
  if (nodeTextContent[nodeOffset] !== ' ') {
655
585
  return {
@@ -657,27 +587,21 @@ function findNextNonBlankInLine(anchor, offset) {
657
587
  offset: nodeOffset
658
588
  };
659
589
  }
660
-
661
590
  nodeOffset++;
662
591
  }
663
592
  }
664
593
  }
665
-
666
594
  function getEndOfCodeInLine(anchor) {
667
595
  const lastNode = getLastCodeNodeOfLine(anchor);
668
-
669
596
  if (!!lexical.$isLineBreakNode(lastNode)) {
670
597
  throw Error(`Unexpected lineBreakNode in getEndOfCodeInLine`);
671
598
  }
672
-
673
599
  return lastNode;
674
600
  }
675
-
676
601
  function textNodeTransform(node, editor, tokenizer) {
677
602
  // Since CodeNode has flat children structure we only need to check
678
603
  // if node's parent is a code node and run highlighting if so
679
604
  const parentNode = node.getParent();
680
-
681
605
  if ($isCodeNode(parentNode)) {
682
606
  codeNodeTransform(parentNode, editor, tokenizer);
683
607
  } else if ($isCodeHighlightNode(node)) {
@@ -686,35 +610,31 @@ function textNodeTransform(node, editor, tokenizer) {
686
610
  node.replace(lexical.$createTextNode(node.__text));
687
611
  }
688
612
  }
689
-
690
613
  function updateCodeGutter(node, editor) {
691
614
  const codeElement = editor.getElementByKey(node.getKey());
692
-
693
615
  if (codeElement === null) {
694
616
  return;
695
617
  }
696
-
697
618
  const children = node.getChildren();
698
- const childrenLength = children.length; // @ts-ignore: internal field
699
-
619
+ const childrenLength = children.length;
620
+ // @ts-ignore: internal field
700
621
  if (childrenLength === codeElement.__cachedChildrenLength) {
701
622
  // Avoid updating the attribute if the children length hasn't changed.
702
623
  return;
703
- } // @ts-ignore:: internal field
704
-
705
-
624
+ }
625
+ // @ts-ignore:: internal field
706
626
  codeElement.__cachedChildrenLength = childrenLength;
707
627
  let gutter = '1';
708
628
  let count = 1;
709
-
710
629
  for (let i = 0; i < childrenLength; i++) {
711
630
  if (lexical.$isLineBreakNode(children[i])) {
712
631
  gutter += '\n' + ++count;
713
632
  }
714
633
  }
715
-
716
634
  codeElement.setAttribute('data-gutter', gutter);
717
- } // Using `skipTransforms` to prevent extra transforms since reformatting the code
635
+ }
636
+
637
+ // Using `skipTransforms` to prevent extra transforms since reformatting the code
718
638
  // will not affect code block content itself.
719
639
  //
720
640
  // Using extra cache (`nodesCurrentlyHighlighting`) since both CodeNode and CodeHighlightNode
@@ -722,33 +642,28 @@ function updateCodeGutter(node, editor) {
722
642
  // in both cases we'll rerun whole reformatting over CodeNode, which is redundant.
723
643
  // Especially when pasting code into CodeBlock.
724
644
 
725
-
726
645
  const nodesCurrentlyHighlighting = new Set();
727
-
728
646
  function codeNodeTransform(node, editor, tokenizer) {
729
647
  const nodeKey = node.getKey();
730
-
731
648
  if (nodesCurrentlyHighlighting.has(nodeKey)) {
732
649
  return;
733
650
  }
651
+ nodesCurrentlyHighlighting.add(nodeKey);
734
652
 
735
- nodesCurrentlyHighlighting.add(nodeKey); // When new code block inserted it might not have language selected
736
-
653
+ // When new code block inserted it might not have language selected
737
654
  if (node.getLanguage() === undefined) {
738
655
  node.setLanguage(tokenizer.defaultLanguage);
739
- } // Using nested update call to pass `skipTransforms` since we don't want
656
+ }
657
+
658
+ // Using nested update call to pass `skipTransforms` since we don't want
740
659
  // each individual codehighlight node to be transformed again as it's already
741
660
  // in its final state
742
-
743
-
744
661
  editor.update(() => {
745
662
  updateAndRetainSelection(nodeKey, () => {
746
663
  const currentNode = lexical.$getNodeByKey(nodeKey);
747
-
748
664
  if (!$isCodeNode(currentNode) || !currentNode.isAttached()) {
749
665
  return false;
750
666
  }
751
-
752
667
  const code = currentNode.getTextContent();
753
668
  const tokens = tokenizer.tokenize(code, currentNode.getLanguage() || tokenizer.defaultLanguage);
754
669
  const highlightNodes = getHighlightNodes(tokens);
@@ -758,12 +673,10 @@ function codeNodeTransform(node, editor, tokenizer) {
758
673
  to,
759
674
  nodesForReplacement
760
675
  } = diffRange;
761
-
762
676
  if (from !== to || nodesForReplacement.length) {
763
677
  node.splice(from, to - from, nodesForReplacement);
764
678
  return true;
765
679
  }
766
-
767
680
  return false;
768
681
  });
769
682
  }, {
@@ -773,18 +686,14 @@ function codeNodeTransform(node, editor, tokenizer) {
773
686
  skipTransforms: true
774
687
  });
775
688
  }
776
-
777
689
  function getHighlightNodes(tokens, type) {
778
690
  const nodes = [];
779
-
780
691
  for (const token of tokens) {
781
692
  if (typeof token === 'string') {
782
693
  const partials = token.split(/(\n|\t)/);
783
694
  const partialsLength = partials.length;
784
-
785
695
  for (let i = 0; i < partialsLength; i++) {
786
696
  const part = partials[i];
787
-
788
697
  if (part === '\n' || part === '\r\n') {
789
698
  nodes.push(lexical.$createLineBreakNode());
790
699
  } else if (part === '\t') {
@@ -797,7 +706,6 @@ function getHighlightNodes(tokens, type) {
797
706
  const {
798
707
  content
799
708
  } = token;
800
-
801
709
  if (typeof content === 'string') {
802
710
  nodes.push(...getHighlightNodes([content], token.type));
803
711
  } else if (Array.isArray(content)) {
@@ -805,100 +713,84 @@ function getHighlightNodes(tokens, type) {
805
713
  }
806
714
  }
807
715
  }
808
-
809
716
  return nodes;
810
- } // Wrapping update function into selection retainer, that tries to keep cursor at the same
811
- // position as before.
812
-
717
+ }
813
718
 
719
+ // Wrapping update function into selection retainer, that tries to keep cursor at the same
720
+ // position as before.
814
721
  function updateAndRetainSelection(nodeKey, updateFn) {
815
722
  const node = lexical.$getNodeByKey(nodeKey);
816
-
817
723
  if (!$isCodeNode(node) || !node.isAttached()) {
818
724
  return;
819
725
  }
820
-
821
- const selection = lexical.$getSelection(); // If it's not range selection (or null selection) there's no need to change it,
726
+ const selection = lexical.$getSelection();
727
+ // If it's not range selection (or null selection) there's no need to change it,
822
728
  // but we can still run highlighting logic
823
-
824
729
  if (!lexical.$isRangeSelection(selection)) {
825
730
  updateFn();
826
731
  return;
827
732
  }
828
-
829
733
  const anchor = selection.anchor;
830
734
  const anchorOffset = anchor.offset;
831
735
  const isNewLineAnchor = anchor.type === 'element' && lexical.$isLineBreakNode(node.getChildAtIndex(anchor.offset - 1));
832
- let textOffset = 0; // Calculating previous text offset (all text node prior to anchor + anchor own text offset)
736
+ let textOffset = 0;
833
737
 
738
+ // Calculating previous text offset (all text node prior to anchor + anchor own text offset)
834
739
  if (!isNewLineAnchor) {
835
740
  const anchorNode = anchor.getNode();
836
741
  textOffset = anchorOffset + anchorNode.getPreviousSiblings().reduce((offset, _node) => {
837
742
  return offset + _node.getTextContentSize();
838
743
  }, 0);
839
744
  }
840
-
841
745
  const hasChanges = updateFn();
842
-
843
746
  if (!hasChanges) {
844
747
  return;
845
- } // Non-text anchors only happen for line breaks, otherwise
846
- // selection will be within text node (code highlight node)
847
-
748
+ }
848
749
 
750
+ // Non-text anchors only happen for line breaks, otherwise
751
+ // selection will be within text node (code highlight node)
849
752
  if (isNewLineAnchor) {
850
753
  anchor.getNode().select(anchorOffset, anchorOffset);
851
754
  return;
852
- } // If it was non-element anchor then we walk through child nodes
853
- // and looking for a position of original text offset
854
-
755
+ }
855
756
 
757
+ // If it was non-element anchor then we walk through child nodes
758
+ // and looking for a position of original text offset
856
759
  node.getChildren().some(_node => {
857
760
  const isText = lexical.$isTextNode(_node);
858
-
859
761
  if (isText || lexical.$isLineBreakNode(_node)) {
860
762
  const textContentSize = _node.getTextContentSize();
861
-
862
763
  if (isText && textContentSize >= textOffset) {
863
764
  _node.select(textOffset, textOffset);
864
-
865
765
  return true;
866
766
  }
867
-
868
767
  textOffset -= textContentSize;
869
768
  }
870
-
871
769
  return false;
872
770
  });
873
- } // Finds minimal diff range between two nodes lists. It returns from/to range boundaries of prevNodes
874
- // that needs to be replaced with `nodes` (subset of nextNodes) to make prevNodes equal to nextNodes.
875
-
771
+ }
876
772
 
773
+ // Finds minimal diff range between two nodes lists. It returns from/to range boundaries of prevNodes
774
+ // that needs to be replaced with `nodes` (subset of nextNodes) to make prevNodes equal to nextNodes.
877
775
  function getDiffRange(prevNodes, nextNodes) {
878
776
  let leadingMatch = 0;
879
-
880
777
  while (leadingMatch < prevNodes.length) {
881
778
  if (!isEqual(prevNodes[leadingMatch], nextNodes[leadingMatch])) {
882
779
  break;
883
780
  }
884
-
885
781
  leadingMatch++;
886
782
  }
887
-
888
783
  const prevNodesLength = prevNodes.length;
889
784
  const nextNodesLength = nextNodes.length;
890
785
  const maxTrailingMatch = Math.min(prevNodesLength, nextNodesLength) - leadingMatch;
891
786
  let trailingMatch = 0;
892
-
893
787
  while (trailingMatch < maxTrailingMatch) {
894
788
  trailingMatch++;
895
-
896
789
  if (!isEqual(prevNodes[prevNodesLength - trailingMatch], nextNodes[nextNodesLength - trailingMatch])) {
897
790
  trailingMatch--;
898
791
  break;
899
792
  }
900
793
  }
901
-
902
794
  const from = leadingMatch;
903
795
  const to = prevNodesLength - trailingMatch;
904
796
  const nodesForReplacement = nextNodes.slice(leadingMatch, nextNodesLength - trailingMatch);
@@ -908,46 +800,35 @@ function getDiffRange(prevNodes, nextNodes) {
908
800
  to
909
801
  };
910
802
  }
911
-
912
803
  function isEqual(nodeA, nodeB) {
913
804
  // Only checking for code higlight nodes, tabs and linebreaks. If it's regular text node
914
805
  // returning false so that it's transformed into code highlight node
915
806
  return $isCodeHighlightNode(nodeA) && $isCodeHighlightNode(nodeB) && nodeA.__text === nodeB.__text && nodeA.__highlightType === nodeB.__highlightType || lexical.$isTabNode(nodeA) && lexical.$isTabNode(nodeB) || lexical.$isLineBreakNode(nodeA) && lexical.$isLineBreakNode(nodeB);
916
807
  }
917
-
918
808
  function $isSelectionInCode(selection) {
919
809
  if (!lexical.$isRangeSelection(selection)) {
920
810
  return false;
921
811
  }
922
-
923
812
  const anchorNode = selection.anchor.getNode();
924
813
  const focusNode = selection.focus.getNode();
925
-
926
814
  if (anchorNode.is(focusNode) && $isCodeNode(anchorNode)) {
927
815
  return true;
928
816
  }
929
-
930
817
  const anchorParent = anchorNode.getParent();
931
818
  return $isCodeNode(anchorParent) && anchorParent.is(focusNode.getParent());
932
819
  }
933
-
934
820
  function $getCodeLines(selection) {
935
821
  const nodes = selection.getNodes();
936
822
  const lines = [[]];
937
-
938
823
  if (nodes.length === 1 && $isCodeNode(nodes[0])) {
939
824
  return lines;
940
825
  }
941
-
942
826
  let lastLine = lines[0];
943
-
944
827
  for (let i = 0; i < nodes.length; i++) {
945
828
  const node = nodes[i];
946
-
947
829
  if (!($isCodeHighlightNode(node) || lexical.$isTabNode(node) || lexical.$isLineBreakNode(node))) {
948
830
  throw Error(`Expected selection to be inside CodeBlock and consisting of CodeHighlightNode, TabNode and LineBreakNode`);
949
831
  }
950
-
951
832
  if (lexical.$isLineBreakNode(node)) {
952
833
  if (i !== 0 && lastLine.length > 0) {
953
834
  lastLine = [];
@@ -957,45 +838,35 @@ function $getCodeLines(selection) {
957
838
  lastLine.push(node);
958
839
  }
959
840
  }
960
-
961
841
  return lines;
962
842
  }
963
-
964
843
  function handleTab(shiftKey) {
965
844
  const selection = lexical.$getSelection();
966
-
967
845
  if (!lexical.$isRangeSelection(selection) || !$isSelectionInCode(selection)) {
968
846
  return null;
969
847
  }
970
-
971
848
  const indentOrOutdent = !shiftKey ? lexical.INDENT_CONTENT_COMMAND : lexical.OUTDENT_CONTENT_COMMAND;
972
- const tabOrOutdent = !shiftKey ? lexical.INSERT_TAB_COMMAND : lexical.OUTDENT_CONTENT_COMMAND; // 1. If multiple lines selected: indent/outdent
973
-
849
+ const tabOrOutdent = !shiftKey ? lexical.INSERT_TAB_COMMAND : lexical.OUTDENT_CONTENT_COMMAND;
850
+ // 1. If multiple lines selected: indent/outdent
974
851
  const codeLines = $getCodeLines(selection);
975
-
976
852
  if (codeLines.length > 1) {
977
853
  return indentOrOutdent;
978
- } // 2. If entire line selected: indent/outdent
979
-
980
-
854
+ }
855
+ // 2. If entire line selected: indent/outdent
981
856
  const selectionNodes = selection.getNodes();
982
857
  const firstNode = selectionNodes[0];
983
-
984
858
  if (!($isCodeNode(firstNode) || $isCodeHighlightNode(firstNode) || lexical.$isTabNode(firstNode) || lexical.$isLineBreakNode(firstNode))) {
985
859
  throw Error(`Expected selection firstNode to be CodeHighlightNode or TabNode`);
986
860
  }
987
-
988
861
  if ($isCodeNode(firstNode)) {
989
862
  return indentOrOutdent;
990
863
  }
991
-
992
864
  const firstOfLine = getFirstCodeNodeOfLine(firstNode);
993
865
  const lastOfLine = getLastCodeNodeOfLine(firstNode);
994
866
  const anchor = selection.anchor;
995
867
  const focus = selection.focus;
996
868
  let selectionFirst;
997
869
  let selectionLast;
998
-
999
870
  if (focus.isBefore(anchor)) {
1000
871
  selectionFirst = focus;
1001
872
  selectionLast = anchor;
@@ -1003,36 +874,29 @@ function handleTab(shiftKey) {
1003
874
  selectionFirst = anchor;
1004
875
  selectionLast = focus;
1005
876
  }
1006
-
1007
877
  if (firstOfLine !== null && lastOfLine !== null && selectionFirst.key === firstOfLine.getKey() && selectionFirst.offset === 0 && selectionLast.key === lastOfLine.getKey() && selectionLast.offset === lastOfLine.getTextContentSize()) {
1008
878
  return indentOrOutdent;
1009
- } // 3. Else: tab/outdent
1010
-
1011
-
879
+ }
880
+ // 3. Else: tab/outdent
1012
881
  return tabOrOutdent;
1013
882
  }
1014
-
1015
883
  function handleMultilineIndent(type) {
1016
884
  const selection = lexical.$getSelection();
1017
-
1018
885
  if (!lexical.$isRangeSelection(selection) || !$isSelectionInCode(selection)) {
1019
886
  return false;
1020
887
  }
1021
-
1022
888
  const codeLines = $getCodeLines(selection);
1023
- const codeLinesLength = codeLines.length; // Multiple lines selection
1024
-
889
+ const codeLinesLength = codeLines.length;
890
+ // Multiple lines selection
1025
891
  if (codeLines.length > 1) {
1026
892
  for (let i = 0; i < codeLinesLength; i++) {
1027
893
  const line = codeLines[i];
1028
-
1029
894
  if (line.length > 0) {
1030
- let firstOfLine = line[0]; // First and last lines might not be complete
1031
-
895
+ let firstOfLine = line[0];
896
+ // First and last lines might not be complete
1032
897
  if (i === 0) {
1033
898
  firstOfLine = getFirstCodeNodeOfLine(firstOfLine);
1034
899
  }
1035
-
1036
900
  if (firstOfLine !== null) {
1037
901
  if (type === lexical.INDENT_CONTENT_COMMAND) {
1038
902
  firstOfLine.insertBefore(lexical.$createTabNode());
@@ -1042,33 +906,25 @@ function handleMultilineIndent(type) {
1042
906
  }
1043
907
  }
1044
908
  }
1045
-
1046
909
  return true;
1047
- } // Just one line
1048
-
1049
-
910
+ }
911
+ // Just one line
1050
912
  const selectionNodes = selection.getNodes();
1051
913
  const firstNode = selectionNodes[0];
1052
-
1053
914
  if (!($isCodeNode(firstNode) || $isCodeHighlightNode(firstNode) || lexical.$isTabNode(firstNode) || lexical.$isLineBreakNode(firstNode))) {
1054
915
  throw Error(`Expected selection firstNode to be CodeHighlightNode or CodeTabNode`);
1055
916
  }
1056
-
1057
917
  if ($isCodeNode(firstNode)) {
1058
918
  // CodeNode is empty
1059
919
  if (type === lexical.INDENT_CONTENT_COMMAND) {
1060
920
  selection.insertNodes([lexical.$createTabNode()]);
1061
921
  }
1062
-
1063
922
  return true;
1064
923
  }
1065
-
1066
924
  const firstOfLine = getFirstCodeNodeOfLine(firstNode);
1067
-
1068
925
  if (!(firstOfLine !== null)) {
1069
926
  throw Error(`Expected getFirstCodeNodeOfLine to return a valid Code Node`);
1070
927
  }
1071
-
1072
928
  if (type === lexical.INDENT_CONTENT_COMMAND) {
1073
929
  if (lexical.$isLineBreakNode(firstOfLine)) {
1074
930
  firstOfLine.insertAfter(lexical.$createTabNode());
@@ -1078,20 +934,17 @@ function handleMultilineIndent(type) {
1078
934
  } else if (lexical.$isTabNode(firstOfLine)) {
1079
935
  firstOfLine.remove();
1080
936
  }
1081
-
1082
937
  return true;
1083
938
  }
1084
-
1085
939
  function handleShiftLines(type, event) {
1086
940
  // We only care about the alt+arrow keys
1087
941
  const selection = lexical.$getSelection();
1088
-
1089
942
  if (!lexical.$isRangeSelection(selection)) {
1090
943
  return false;
1091
- } // I'm not quite sure why, but it seems like calling anchor.getNode() collapses the selection here
1092
- // So first, get the anchor and the focus, then get their nodes
1093
-
944
+ }
1094
945
 
946
+ // I'm not quite sure why, but it seems like calling anchor.getNode() collapses the selection here
947
+ // So first, get the anchor and the focus, then get their nodes
1095
948
  const {
1096
949
  anchor,
1097
950
  focus
@@ -1100,21 +953,19 @@ function handleShiftLines(type, event) {
1100
953
  const focusOffset = focus.offset;
1101
954
  const anchorNode = anchor.getNode();
1102
955
  const focusNode = focus.getNode();
1103
- const arrowIsUp = type === lexical.KEY_ARROW_UP_COMMAND; // Ensure the selection is within the codeblock
956
+ const arrowIsUp = type === lexical.KEY_ARROW_UP_COMMAND;
1104
957
 
958
+ // Ensure the selection is within the codeblock
1105
959
  if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || lexical.$isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || lexical.$isTabNode(focusNode))) {
1106
960
  return false;
1107
961
  }
1108
-
1109
962
  if (!event.altKey) {
1110
963
  // Handle moving selection out of the code block, given there are no
1111
964
  // sibling thats can natively take the selection.
1112
965
  if (selection.isCollapsed()) {
1113
966
  const codeNode = anchorNode.getParentOrThrow();
1114
-
1115
967
  if (arrowIsUp && anchorOffset === 0 && anchorNode.getPreviousSibling() === null) {
1116
968
  const codeNodeSibling = codeNode.getPreviousSibling();
1117
-
1118
969
  if (codeNodeSibling === null) {
1119
970
  codeNode.selectPrevious();
1120
971
  event.preventDefault();
@@ -1122,7 +973,6 @@ function handleShiftLines(type, event) {
1122
973
  }
1123
974
  } else if (!arrowIsUp && anchorOffset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null) {
1124
975
  const codeNodeSibling = codeNode.getNextSibling();
1125
-
1126
976
  if (codeNodeSibling === null) {
1127
977
  codeNode.selectNext();
1128
978
  event.preventDefault();
@@ -1130,13 +980,10 @@ function handleShiftLines(type, event) {
1130
980
  }
1131
981
  }
1132
982
  }
1133
-
1134
983
  return false;
1135
984
  }
1136
-
1137
985
  let start;
1138
986
  let end;
1139
-
1140
987
  if (anchorNode.isBefore(focusNode)) {
1141
988
  start = getFirstCodeNodeOfLine(anchorNode);
1142
989
  end = getLastCodeNodeOfLine(focusNode);
@@ -1144,44 +991,35 @@ function handleShiftLines(type, event) {
1144
991
  start = getFirstCodeNodeOfLine(focusNode);
1145
992
  end = getLastCodeNodeOfLine(anchorNode);
1146
993
  }
1147
-
1148
994
  if (start == null || end == null) {
1149
995
  return false;
1150
996
  }
1151
-
1152
997
  const range = start.getNodesBetween(end);
1153
-
1154
998
  for (let i = 0; i < range.length; i++) {
1155
999
  const node = range[i];
1156
-
1157
1000
  if (!$isCodeHighlightNode(node) && !lexical.$isTabNode(node) && !lexical.$isLineBreakNode(node)) {
1158
1001
  return false;
1159
1002
  }
1160
- } // After this point, we know the selection is within the codeblock. We may not be able to
1003
+ }
1004
+
1005
+ // After this point, we know the selection is within the codeblock. We may not be able to
1161
1006
  // actually move the lines around, but we want to return true either way to prevent
1162
1007
  // the event's default behavior
1163
-
1164
-
1165
1008
  event.preventDefault();
1166
1009
  event.stopPropagation(); // required to stop cursor movement under Firefox
1167
1010
 
1168
1011
  const linebreak = arrowIsUp ? start.getPreviousSibling() : end.getNextSibling();
1169
-
1170
1012
  if (!lexical.$isLineBreakNode(linebreak)) {
1171
1013
  return true;
1172
1014
  }
1173
-
1174
1015
  const sibling = arrowIsUp ? linebreak.getPreviousSibling() : linebreak.getNextSibling();
1175
-
1176
1016
  if (sibling == null) {
1177
1017
  return true;
1178
1018
  }
1179
-
1180
1019
  const maybeInsertionPoint = $isCodeHighlightNode(sibling) || lexical.$isTabNode(sibling) || lexical.$isLineBreakNode(sibling) ? arrowIsUp ? getFirstCodeNodeOfLine(sibling) : getLastCodeNodeOfLine(sibling) : null;
1181
1020
  let insertionPoint = maybeInsertionPoint != null ? maybeInsertionPoint : sibling;
1182
1021
  linebreak.remove();
1183
1022
  range.forEach(node => node.remove());
1184
-
1185
1023
  if (type === lexical.KEY_ARROW_UP_COMMAND) {
1186
1024
  range.forEach(node => insertionPoint.insertBefore(node));
1187
1025
  insertionPoint.insertBefore(linebreak);
@@ -1193,18 +1031,14 @@ function handleShiftLines(type, event) {
1193
1031
  insertionPoint = node;
1194
1032
  });
1195
1033
  }
1196
-
1197
1034
  selection.setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset);
1198
1035
  return true;
1199
1036
  }
1200
-
1201
1037
  function handleMoveTo(type, event) {
1202
1038
  const selection = lexical.$getSelection();
1203
-
1204
1039
  if (!lexical.$isRangeSelection(selection)) {
1205
1040
  return false;
1206
1041
  }
1207
-
1208
1042
  const {
1209
1043
  anchor,
1210
1044
  focus
@@ -1212,20 +1046,16 @@ function handleMoveTo(type, event) {
1212
1046
  const anchorNode = anchor.getNode();
1213
1047
  const focusNode = focus.getNode();
1214
1048
  const isMoveToStart = type === lexical.MOVE_TO_START;
1215
-
1216
1049
  if (!($isCodeHighlightNode(anchorNode) || lexical.$isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || lexical.$isTabNode(focusNode))) {
1217
1050
  return false;
1218
1051
  }
1219
-
1220
1052
  if (isMoveToStart) {
1221
1053
  const start = getStartOfCodeInLine(focusNode, focus.offset);
1222
-
1223
1054
  if (start !== null) {
1224
1055
  const {
1225
1056
  node,
1226
1057
  offset
1227
1058
  } = start;
1228
-
1229
1059
  if (lexical.$isLineBreakNode(node)) {
1230
1060
  node.selectNext(0, 0);
1231
1061
  } else {
@@ -1238,27 +1068,22 @@ function handleMoveTo(type, event) {
1238
1068
  const node = getEndOfCodeInLine(focusNode);
1239
1069
  node.select();
1240
1070
  }
1241
-
1242
1071
  event.preventDefault();
1243
1072
  event.stopPropagation();
1244
1073
  return true;
1245
1074
  }
1246
-
1247
1075
  function registerCodeHighlighting(editor, tokenizer) {
1248
1076
  if (!editor.hasNodes([CodeNode, CodeHighlightNode])) {
1249
1077
  throw new Error('CodeHighlightPlugin: CodeNode or CodeHighlightNode not registered on editor');
1250
1078
  }
1251
-
1252
1079
  if (tokenizer == null) {
1253
1080
  tokenizer = PrismTokenizer;
1254
1081
  }
1255
-
1256
1082
  return utils.mergeRegister(editor.registerMutationListener(CodeNode, mutations => {
1257
1083
  editor.update(() => {
1258
1084
  for (const [key, type] of mutations) {
1259
1085
  if (type !== 'destroyed') {
1260
1086
  const node = lexical.$getNodeByKey(key);
1261
-
1262
1087
  if (node !== null) {
1263
1088
  updateCodeGutter(node, editor);
1264
1089
  }
@@ -1267,21 +1092,17 @@ function registerCodeHighlighting(editor, tokenizer) {
1267
1092
  });
1268
1093
  }), editor.registerNodeTransform(CodeNode, node => codeNodeTransform(node, editor, tokenizer)), editor.registerNodeTransform(lexical.TextNode, node => textNodeTransform(node, editor, tokenizer)), editor.registerNodeTransform(CodeHighlightNode, node => textNodeTransform(node, editor, tokenizer)), editor.registerCommand(lexical.KEY_TAB_COMMAND, event => {
1269
1094
  const command = handleTab(event.shiftKey);
1270
-
1271
1095
  if (command === null) {
1272
1096
  return false;
1273
1097
  }
1274
-
1275
1098
  event.preventDefault();
1276
1099
  editor.dispatchCommand(command, undefined);
1277
1100
  return true;
1278
1101
  }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.INSERT_TAB_COMMAND, () => {
1279
1102
  const selection = lexical.$getSelection();
1280
-
1281
1103
  if (!$isSelectionInCode(selection)) {
1282
1104
  return false;
1283
1105
  }
1284
-
1285
1106
  lexical.$insertNodes([lexical.$createTabNode()]);
1286
1107
  return true;
1287
1108
  }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.INDENT_CONTENT_COMMAND, payload => handleMultilineIndent(lexical.INDENT_CONTENT_COMMAND), lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.OUTDENT_CONTENT_COMMAND, payload => handleMultilineIndent(lexical.OUTDENT_CONTENT_COMMAND), lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ARROW_UP_COMMAND, payload => handleShiftLines(lexical.KEY_ARROW_UP_COMMAND, payload), lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ARROW_DOWN_COMMAND, payload => handleShiftLines(lexical.KEY_ARROW_DOWN_COMMAND, payload), lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.MOVE_TO_END, payload => handleMoveTo(lexical.MOVE_TO_END, payload), lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.MOVE_TO_START, payload => handleMoveTo(lexical.MOVE_TO_START, payload), lexical.COMMAND_PRIORITY_LOW));