lexgui 8.1.2 → 8.2.1

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.
Files changed (65) hide show
  1. package/build/components/AlertDialog.d.ts +7 -7
  2. package/build/components/Avatar.d.ts +15 -0
  3. package/build/components/Counter.d.ts +9 -9
  4. package/build/components/Dialog.d.ts +20 -20
  5. package/build/components/Footer.d.ts +14 -14
  6. package/build/components/Menubar.d.ts +59 -59
  7. package/build/components/NodeTree.d.ts +26 -1
  8. package/build/components/Vector.d.ts +1 -0
  9. package/build/core/Area.d.ts +143 -143
  10. package/build/core/Event.d.ts +0 -20
  11. package/build/core/Namespace.js +1 -1
  12. package/build/core/Namespace.js.map +1 -1
  13. package/build/core/Panel.d.ts +538 -538
  14. package/build/extensions/AssetView.d.ts +137 -136
  15. package/build/extensions/AssetView.js +193 -155
  16. package/build/extensions/AssetView.js.map +1 -1
  17. package/build/extensions/Audio.js +163 -163
  18. package/build/extensions/Audio.js.map +1 -1
  19. package/build/extensions/CodeEditor.d.ts +358 -350
  20. package/build/extensions/CodeEditor.js +302 -270
  21. package/build/extensions/CodeEditor.js.map +1 -1
  22. package/build/extensions/DocMaker.d.ts +27 -27
  23. package/build/extensions/DocMaker.js +15 -11
  24. package/build/extensions/DocMaker.js.map +1 -1
  25. package/build/extensions/GraphEditor.js +2754 -2760
  26. package/build/extensions/GraphEditor.js.map +1 -1
  27. package/build/extensions/ImUi.js +227 -227
  28. package/build/extensions/Timeline.d.ts +668 -670
  29. package/build/extensions/Timeline.js +71 -79
  30. package/build/extensions/Timeline.js.map +1 -1
  31. package/build/extensions/VideoEditor.d.ts +38 -16
  32. package/build/extensions/VideoEditor.js +294 -180
  33. package/build/extensions/VideoEditor.js.map +1 -1
  34. package/build/extensions/index.d.ts +8 -8
  35. package/build/extensions/index.js +10 -10
  36. package/build/index.all.d.ts +2 -2
  37. package/build/index.css.d.ts +3 -4
  38. package/build/index.d.ts +57 -56
  39. package/build/lexgui.all.js +1877 -1520
  40. package/build/lexgui.all.js.map +1 -1
  41. package/build/lexgui.all.min.js +1 -1
  42. package/build/lexgui.all.module.js +1875 -1516
  43. package/build/lexgui.all.module.js.map +1 -1
  44. package/build/lexgui.all.module.min.js +1 -1
  45. package/build/lexgui.css +6123 -5556
  46. package/build/lexgui.js +997 -814
  47. package/build/lexgui.js.map +1 -1
  48. package/build/lexgui.min.css +2 -3
  49. package/build/lexgui.min.js +1 -1
  50. package/build/lexgui.module.js +995 -810
  51. package/build/lexgui.module.js.map +1 -1
  52. package/build/lexgui.module.min.js +1 -1
  53. package/changelog.md +65 -2
  54. package/demo.js +167 -65
  55. package/examples/all-components.html +40 -55
  56. package/examples/asset-view.html +27 -0
  57. package/examples/code-editor.html +12 -1
  58. package/examples/dialogs.html +13 -2
  59. package/examples/editor.html +9 -49
  60. package/examples/index.html +2 -2
  61. package/examples/side-bar.html +1 -1
  62. package/examples/timeline.html +2 -2
  63. package/examples/video-editor.html +1 -1
  64. package/examples/video-editor2.html +2 -2
  65. package/package.json +7 -4
@@ -114,6 +114,9 @@ class Cursor {
114
114
  print() {
115
115
  console.log(this.line, this.position);
116
116
  }
117
+ destroy() {
118
+ LX.deleteElement(this.root);
119
+ }
117
120
  isLast() {
118
121
  return (this.editor._getLastCursor() == this);
119
122
  }
@@ -261,10 +264,8 @@ class ScrollBar {
261
264
  const HighlightRules = {
262
265
  common: [
263
266
  { test: (ctx) => ctx.inBlockComment, className: 'cm-com' },
264
- { test: (ctx) => ctx.inString,
265
- action: (ctx, editor) => editor._appendStringToken(ctx.token), discard: true },
266
- { test: (ctx) => ctx.token.substr(0, ctx.singleLineCommentToken.length) == ctx.singleLineCommentToken,
267
- className: 'cm-com' },
267
+ { test: (ctx) => ctx.inString, action: (ctx, editor) => editor._appendStringToken(ctx.token), discard: true },
268
+ { test: (ctx) => ctx.token.substr(0, ctx.singleLineCommentToken.length) == ctx.singleLineCommentToken, className: 'cm-com' },
268
269
  { test: (ctx, editor) => editor._isKeyword(ctx), className: 'cm-kwd' },
269
270
  {
270
271
  test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.builtIn, ctx.lang) && (ctx.lang.tags ?? false
@@ -272,12 +273,9 @@ const HighlightRules = {
272
273
  : true),
273
274
  className: 'cm-bln'
274
275
  },
275
- { test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.statements, ctx.lang),
276
- className: 'cm-std' },
277
- { test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.symbols, ctx.lang),
278
- className: 'cm-sym' },
279
- { test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.types, ctx.lang),
280
- className: 'cm-typ' },
276
+ { test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.statements, ctx.lang), className: 'cm-std' },
277
+ { test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.symbols, ctx.lang), className: 'cm-sym' },
278
+ { test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.types, ctx.lang), className: 'cm-typ' },
281
279
  {
282
280
  test: (ctx, editor) => editor._isNumber(ctx.token) || editor._isNumber(ctx.token.replace(/[px]|[em]|%/g, '')),
283
281
  className: 'cm-dec'
@@ -332,12 +330,9 @@ const HighlightRules = {
332
330
  || (ctx.token[0] == '#' && ctx.prev != ':')),
333
331
  className: 'cm-kwd'
334
332
  },
335
- { test: (ctx) => ctx.prev === ':' && (ctx.next === ';' || ctx.next === '!important'),
336
- className: 'cm-str' }, // CSS value
337
- { test: (ctx) => (ctx.prev === undefined || ctx.prev === '{' || ctx.prev === ';') && ctx.next === ':',
338
- className: 'cm-typ' }, // CSS attribute
339
- { test: (ctx) => ctx.prev === '(' && ctx.next === ')' && ctx.token.startsWith('--'),
340
- className: 'cm-typ' } // CSS vars
333
+ { test: (ctx) => ctx.prev === ':' && (ctx.next === ';' || ctx.next === '!important'), className: 'cm-str' }, // CSS value
334
+ { test: (ctx) => (ctx.prev === undefined || ctx.prev === '{' || ctx.prev === ';') && ctx.next === ':', className: 'cm-typ' }, // CSS attribute
335
+ { test: (ctx) => ctx.prev === '(' && ctx.next === ')' && ctx.token.startsWith('--'), className: 'cm-typ' } // CSS vars
341
336
  ],
342
337
  batch: [
343
338
  { test: (ctx) => ctx.token === '@' || ctx.prev === ':' || ctx.prev === '@', className: 'cm-kwd' }
@@ -462,17 +457,29 @@ class CodeEditor {
462
457
  newTabOptions;
463
458
  customSuggestions = [];
464
459
  // Editor callbacks
465
- onSave;
466
- onRun;
467
- onCtrlSpace;
468
- onCreateStatusPanel;
469
460
  onContextMenu;
461
+ onCreateFile;
462
+ onCreateStatusPanel;
463
+ onCtrlSpace;
470
464
  onNewTab;
465
+ onSave;
471
466
  onSelectTab;
472
- onCreateFile;
467
+ onReady;
468
+ onRun;
473
469
  // Inner functions
474
470
  addExplorerItem;
475
- // Temp variables
471
+ // Internal variables
472
+ _blockCommentCache = [];
473
+ _buildingBlockComment = undefined;
474
+ _buildingString = undefined;
475
+ _currentOcurrences = undefined;
476
+ _currentLineNumber = undefined;
477
+ _currentLineString = undefined;
478
+ _currentTokenPositions = undefined;
479
+ _discardScroll = false;
480
+ _displayObserver = null;
481
+ _fullVerticalOffset = -1;
482
+ _isReady = false;
476
483
  _lastTime = null;
477
484
  _lastProcessedLine = -1;
478
485
  _lastResult = undefined;
@@ -482,28 +489,21 @@ class CodeEditor {
482
489
  _lastMouseDown = 0;
483
490
  _lastTextFound = '';
484
491
  _lastBaseareaWidth = undefined;
485
- _blockCommentCache = [];
486
- _pendingString = undefined;
487
- _skipTabs = undefined;
488
- _discardScroll = false;
489
492
  _markdownHeader = undefined;
490
- _tabStorage = {};
491
- _tripleClickSelection = undefined;
492
- _currentOcurrences = undefined;
493
- _currentLineNumber = undefined;
494
- _currentLineString = undefined;
495
- _currentTokenPositions = undefined;
496
- _buildingBlockComment = undefined;
497
- _buildingString = undefined;
498
- _verticalTopOffset = -1;
499
- _verticalBottomOffset = -1;
500
- _fullVerticalOffset = -1;
501
- _scopeStack = null;
502
493
  _mouseDown = undefined;
494
+ _nextCursorPositionOffset = undefined;
495
+ _pendingString = undefined;
496
+ _preparedAt = undefined;
497
+ _scopeStack = null;
503
498
  _scopesUpdated = undefined;
499
+ _skipTabs = undefined;
504
500
  _stringEnded = false;
505
501
  _stringInterpolation = undefined;
506
502
  _stringInterpolationOpened = undefined;
503
+ _tabStorage = {};
504
+ _tripleClickSelection = undefined;
505
+ _verticalBottomOffset = -1;
506
+ _verticalTopOffset = -1;
507
507
  constructor(area, options = {}) {
508
508
  if (options.filesAsync) {
509
509
  options.files = [...options.filesAsync];
@@ -541,6 +541,7 @@ class CodeEditor {
541
541
  this.onContextMenu = options.onContextMenu;
542
542
  this.onNewTab = options.onNewTab;
543
543
  this.onSelectTab = options.onSelectTab;
544
+ this.onReady = options.onReady;
544
545
  // File explorer
545
546
  if (this.useFileExplorer) {
546
547
  let [explorerArea, editorArea] = area.split({ sizes: ['15%', '85%'] });
@@ -552,27 +553,15 @@ class CodeEditor {
552
553
  this.explorer = panel.addTree(null, sceneData, {
553
554
  filter: false,
554
555
  rename: false,
555
- skipDefaultIcon: true,
556
- onevent: (event) => {
557
- switch (event.type) {
558
- // case LX.TreeEvent.NODE_SELECTED:
559
- // if( !this.tabs.tabDOMs[ event.node.id ] ) break;
560
- case LX.TreeEvent.NODE_DBLCLICKED:
561
- this.loadTab(event.node.id);
562
- break;
563
- case LX.TreeEvent.NODE_DELETED:
564
- this.closeTab(event.node.id);
565
- break;
566
- // case LX.TreeEvent.NODE_CONTEXTMENU:
567
- // LX.addContextMenu( event.multiple ? "Selected Nodes" : event.node.id, event.value, m => {
568
- //
569
- // });
570
- // break;
571
- // case LX.TreeEvent.NODE_DRAGGED:
572
- // console.log(event.node.id + " is now child of " + event.value.id);
573
- // break;
574
- }
575
- }
556
+ skipDefaultIcon: true
557
+ });
558
+ this.explorer.on('dblClick', (event) => {
559
+ const node = event.items[0];
560
+ this.loadTab(node.id);
561
+ });
562
+ this.explorer.on('delete', (event) => {
563
+ const node = event.items[0];
564
+ this.closeTab(node.id);
576
565
  });
577
566
  this.addExplorerItem = function (item) {
578
567
  if (!this.explorer.innerTree.data.find((value, index) => value.id === item.id)) {
@@ -584,13 +573,13 @@ class CodeEditor {
584
573
  area = editorArea;
585
574
  }
586
575
  this.baseArea = area;
587
- this.area = new LX.Area({ className: 'lexcodeeditor', height: '100%', skipAppend: true });
576
+ this.area = new LX.Area({ className: 'lexcodeeditor outline-none overflow-hidden size-full select-none bg-inherit', skipAppend: true });
588
577
  if (!this.skipTabs) {
589
578
  this.tabs = this.area.addTabs({ onclose: (name) => {
590
579
  delete this.openedTabs[name];
591
580
  if (Object.keys(this.openedTabs).length < 2) {
592
581
  clearInterval(this.blinker);
593
- LX.removeClas(this.cursorsDOM, 'show');
582
+ LX.removeClass(this.cursorsDOM, 'show');
594
583
  }
595
584
  } });
596
585
  LX.addClass(this.tabs.root.parentElement, 'rounded-t-lg');
@@ -607,13 +596,12 @@ class CodeEditor {
607
596
  else {
608
597
  this.codeArea = new LX.Area({ skipAppend: true });
609
598
  this.area.attach(this.codeArea);
610
- const loadFileButton = LX.makeElement('button', 'grid absolute self-center z-100 p-3 rounded-full bg-secondary hover:bg-tertiary cursor-pointer border', LX.makeIcon('FolderOpen').innerHTML, this.area, {
599
+ const loadFileButton = LX.makeElement('button', 'grid absolute place-self-center z-100 p-3 rounded-full bg-secondary hover:bg-accent cursor-pointer border-color', LX.makeIcon('FolderOpen').innerHTML, this.area, {
611
600
  bottom: '8px'
612
601
  });
613
602
  loadFileButton.addEventListener('click', (e) => {
614
603
  const dropdownOptions = [];
615
- for (const [key, value] of [...Object.entries(this.loadedTabs).slice(1),
616
- ...Object.entries(this._tabStorage)]) {
604
+ for (const [key, value] of [...Object.entries(this.loadedTabs).slice(1), ...Object.entries(this._tabStorage)]) {
617
605
  const icon = this._getFileIcon(key);
618
606
  const classes = icon ? icon.split(' ') : [];
619
607
  dropdownOptions.push({
@@ -628,7 +616,7 @@ class CodeEditor {
628
616
  new LX.DropdownMenu(loadFileButton, dropdownOptions, { side: 'top', align: 'center' });
629
617
  });
630
618
  }
631
- this.codeArea.root.classList.add('lexcodearea');
619
+ this.codeArea.root.classList.add('lexcodearea', 'scrollbar-hidden');
632
620
  const codeResizeObserver = new ResizeObserver((entries) => {
633
621
  if (!this.code) {
634
622
  return;
@@ -637,7 +625,7 @@ class CodeEditor {
637
625
  });
638
626
  codeResizeObserver.observe(this.codeArea.root);
639
627
  // Full editor
640
- area.root.classList.add('codebasearea');
628
+ area.root.className = LX.mergeClass(area.root.className, 'codebasearea flex relative bg-card');
641
629
  const observer = new MutationObserver((e) => {
642
630
  if (e[0].attributeName == 'style') {
643
631
  this.resize();
@@ -766,11 +754,11 @@ class CodeEditor {
766
754
  box.appendChild(searchPanel.root);
767
755
  searchPanel.sameLine(4);
768
756
  searchPanel.addText(null, '', null, { placeholder: 'Find', inputClass: 'bg-secondary' });
769
- searchPanel.addButton(null, 'up', () => this.search(null, true), { icon: 'ArrowUp',
770
- title: 'Previous Match', tooltip: true });
771
- searchPanel.addButton(null, 'down', () => this.search(), { icon: 'ArrowDown', title: 'Next Match',
757
+ searchPanel.addButton(null, 'up', () => this.search(null, true), { icon: 'ArrowUp', buttonClass: 'ghost', title: 'Previous Match',
758
+ tooltip: true });
759
+ searchPanel.addButton(null, 'down', () => this.search(), { icon: 'ArrowDown', buttonClass: 'ghost', title: 'Next Match',
772
760
  tooltip: true });
773
- searchPanel.addButton(null, 'x', this.hideSearchBox.bind(this), { icon: 'X', title: 'Close',
761
+ searchPanel.addButton(null, 'x', this.hideSearchBox.bind(this), { icon: 'X', buttonClass: 'ghost', title: 'Close',
774
762
  tooltip: true });
775
763
  const searchInput = box.querySelector('input');
776
764
  searchInput?.addEventListener('keyup', (e) => {
@@ -793,7 +781,7 @@ class CodeEditor {
793
781
  input.value = ':' + value.replaceAll(':', '');
794
782
  this.goToLine(input.value.slice(1));
795
783
  }, { placeholder: 'Go to line', trigger: 'input' });
796
- searchPanel.addButton(null, 'x', this.hideSearchLineBox.bind(this), { icon: 'X', title: 'Close',
784
+ searchPanel.addButton(null, 'x', this.hideSearchLineBox.bind(this), { icon: 'X', title: 'Close', buttonClass: 'ghost',
797
785
  tooltip: true });
798
786
  let input = box.querySelector('input');
799
787
  input.addEventListener('keyup', (e) => {
@@ -858,9 +846,8 @@ class CodeEditor {
858
846
  CodeEditor.types[lang] = new Set(CodeEditor.types[lang]);
859
847
  for (let lang in CodeEditor.builtIn)
860
848
  CodeEditor.builtIn[lang] = new Set(CodeEditor.builtIn[lang]);
861
- for (let lang in CodeEditor.statements) {
849
+ for (let lang in CodeEditor.statements)
862
850
  CodeEditor.statements[lang] = new Set(CodeEditor.statements[lang]);
863
- }
864
851
  for (let lang in CodeEditor.symbols)
865
852
  CodeEditor.symbols[lang] = new Set(CodeEditor.symbols[lang]);
866
853
  CodeEditor._staticReady = true;
@@ -931,6 +918,9 @@ class CodeEditor {
931
918
  if (letter) {
932
919
  this.code.lines[ln] = sliceChars(this.code.lines[ln], cursor.position);
933
920
  this.processLine(ln);
921
+ // "Delete" removes the char at the right, so cursor position does not change
922
+ // but line length does and next cursor position must be updated 1 position to the left
923
+ this._nextCursorPositionOffset = -1;
934
924
  }
935
925
  else if (this.code.lines[ln + 1] != undefined) {
936
926
  this.code.lines[ln] += this.code.lines[ln + 1];
@@ -1159,18 +1149,18 @@ class CodeEditor {
1159
1149
  this._processSelection(cursor, e, false, CodeEditor.SELECTION_X);
1160
1150
  }
1161
1151
  else {
1162
- if (!cursor.selection) {
1163
- this.cursorToLeft(letter, cursor);
1164
- if (this.useAutoComplete && this.isAutoCompleteActive) {
1165
- this.showAutoCompleteBox('foo', cursor);
1166
- }
1167
- }
1168
- else {
1152
+ if (cursor.selection) {
1169
1153
  cursor.selection.invertIfNecessary();
1170
1154
  this.resetCursorPos(CodeEditor.CURSOR_LEFT_TOP, cursor);
1171
1155
  this.cursorToLine(cursor, cursor.selection.fromY);
1172
1156
  this.cursorToPosition(cursor, cursor.selection.fromX, true);
1173
- this.endSelection();
1157
+ this.endSelection(cursor);
1158
+ }
1159
+ else {
1160
+ this.cursorToLeft(letter, cursor);
1161
+ if (this.useAutoComplete && this.isAutoCompleteActive) {
1162
+ this.showAutoCompleteBox('foo', cursor);
1163
+ }
1174
1164
  }
1175
1165
  }
1176
1166
  }
@@ -1271,39 +1261,7 @@ class CodeEditor {
1271
1261
  if (this.statusPanel) {
1272
1262
  area.attach(this.statusPanel);
1273
1263
  }
1274
- if (document.fonts.status == 'loading') {
1275
- await document.fonts.ready;
1276
- }
1277
- // Load any font size from local storage
1278
- const savedFontSize = window.localStorage.getItem('lexcodeeditor-font-size');
1279
- if (savedFontSize) {
1280
- this._setFontSize(parseInt(savedFontSize));
1281
- }
1282
- // Use default size
1283
- else {
1284
- const r = document.querySelector(':root');
1285
- const s = getComputedStyle(r);
1286
- this.fontSize = parseInt(s.getPropertyValue('--code-editor-font-size'));
1287
- this.charWidth = this._measureChar();
1288
- }
1289
- LX.emitSignal('@font-size', this.fontSize);
1290
- // Get final sizes for editor elements based on Tabs and status bar offsets
1291
- LX.doAsync(() => {
1292
- this._verticalTopOffset = this.tabs?.root.getBoundingClientRect().height ?? 0;
1293
- this._verticalBottomOffset = this.statusPanel?.root.getBoundingClientRect().height ?? 0;
1294
- this._fullVerticalOffset = this._verticalTopOffset + this._verticalBottomOffset;
1295
- this.gutter.style.marginTop = `${this._verticalTopOffset}px`;
1296
- this.gutter.style.height = `calc(100% - ${this._fullVerticalOffset}px)`;
1297
- this.vScrollbar.root.style.marginTop = `${this._verticalTopOffset}px`;
1298
- this.vScrollbar.root.style.height = `calc(100% - ${this._fullVerticalOffset}px)`;
1299
- this.hScrollbar.root.style.bottom = `${this._verticalBottomOffset}px`;
1300
- this.codeArea.root.style.height = `calc(100% - ${this._fullVerticalOffset}px)`;
1301
- // Process lines on finish computing final sizes
1302
- this.processLines();
1303
- }, 50);
1304
- if (options.callback) {
1305
- options.callback.call(this, this);
1306
- }
1264
+ this._setupDisplayObserver();
1307
1265
  g.editor = this;
1308
1266
  };
1309
1267
  if (options.allowAddScripts ?? true) {
@@ -1318,8 +1276,7 @@ class CodeEditor {
1318
1276
  for (let url of options.files) {
1319
1277
  const finalUrl = url.constructor === Array ? url[0] : url;
1320
1278
  const finalFileName = url.constructor === Array ? url[1] : undefined;
1321
- await this.loadFile(finalUrl, { filename: finalFileName, async: loadAsync,
1322
- callback: (name, text) => {
1279
+ await this.loadFile(finalUrl, { filename: finalFileName, async: loadAsync, callback: (name, text) => {
1323
1280
  filesLoaded++;
1324
1281
  if (filesLoaded == numFiles) {
1325
1282
  onLoadAll();
@@ -1339,6 +1296,92 @@ class CodeEditor {
1339
1296
  onLoadAll();
1340
1297
  }
1341
1298
  }
1299
+ _setupDisplayObserver() {
1300
+ if (this._displayObserver)
1301
+ return;
1302
+ this._isReady = false;
1303
+ const root = this.root;
1304
+ const _isVisible = () => {
1305
+ return (root.offsetParent !== null
1306
+ && root.clientWidth > 0
1307
+ && root.clientHeight > 0);
1308
+ };
1309
+ const _tryPrepare = async () => {
1310
+ if (this._isReady)
1311
+ return;
1312
+ if (!_isVisible())
1313
+ return;
1314
+ this._isReady = true;
1315
+ // Stop observing once prepared
1316
+ intersectionObserver.disconnect();
1317
+ resizeObserver.disconnect();
1318
+ await this._setupEditorWhenVisible();
1319
+ };
1320
+ // IntersectionObserver (for viewport)
1321
+ const intersectionObserver = new IntersectionObserver((entries) => {
1322
+ for (const entry of entries) {
1323
+ if (entry.isIntersecting) {
1324
+ _tryPrepare();
1325
+ }
1326
+ }
1327
+ });
1328
+ intersectionObserver.observe(root);
1329
+ // ResizeObserver (for display property changes)
1330
+ const resizeObserver = new ResizeObserver(() => {
1331
+ _tryPrepare();
1332
+ });
1333
+ resizeObserver.observe(root);
1334
+ // Fallback polling (don't use it for now)
1335
+ // const interval = setInterval( () => {
1336
+ // if ( this._isReady ) {
1337
+ // clearInterval( interval );
1338
+ // return;
1339
+ // }
1340
+ // _tryPrepare();
1341
+ // }, 250 );
1342
+ this._displayObserver = {
1343
+ intersectionObserver,
1344
+ resizeObserver
1345
+ // interval,
1346
+ };
1347
+ }
1348
+ async _setupEditorWhenVisible() {
1349
+ if (document.fonts.status == 'loading') {
1350
+ await document.fonts.ready;
1351
+ }
1352
+ // Load any font size from local storage
1353
+ const savedFontSize = window.localStorage.getItem('lexcodeeditor-font-size');
1354
+ if (savedFontSize) {
1355
+ this._setFontSize(parseInt(savedFontSize));
1356
+ }
1357
+ // Use default size
1358
+ else {
1359
+ const r = document.querySelector(':root');
1360
+ const s = getComputedStyle(r);
1361
+ this.fontSize = parseInt(s.getPropertyValue('--code-editor-font-size'));
1362
+ this.charWidth = this._measureChar();
1363
+ }
1364
+ LX.emitSignal('@font-size', this.fontSize);
1365
+ // Get final sizes for editor elements based on Tabs and status bar offsets
1366
+ LX.doAsync(() => {
1367
+ this._verticalTopOffset = this.tabs?.root.getBoundingClientRect().height ?? 0;
1368
+ this._verticalBottomOffset = this.statusPanel?.root.getBoundingClientRect().height ?? 0;
1369
+ this._fullVerticalOffset = this._verticalTopOffset + this._verticalBottomOffset;
1370
+ this.gutter.style.marginTop = `${this._verticalTopOffset}px`;
1371
+ this.gutter.style.height = `calc(100% - ${this._fullVerticalOffset}px)`;
1372
+ this.vScrollbar.root.style.marginTop = `${this._verticalTopOffset}px`;
1373
+ this.vScrollbar.root.style.height = `calc(100% - ${this._fullVerticalOffset}px)`;
1374
+ this.hScrollbar.root.style.bottom = `${this._verticalBottomOffset}px`;
1375
+ this.codeArea.root.style.height = `calc(100% - ${this._fullVerticalOffset}px)`;
1376
+ // Process lines on finish computing final sizes
1377
+ this.processLines();
1378
+ this._preparedAt = performance.now();
1379
+ if (this.onReady) {
1380
+ this.onReady(this);
1381
+ }
1382
+ console.log(`[LX.CodeEditor] Ready! (font size: ${this.fontSize}px)`);
1383
+ }, 50);
1384
+ }
1342
1385
  // Clear signals
1343
1386
  clear() {
1344
1387
  console.assert(this.rightStatusPanel && this.leftStatusPanel, 'No panels to clear.');
@@ -1367,7 +1410,7 @@ class CodeEditor {
1367
1410
  return this.code.lines.join(min ? ' ' : '\n');
1368
1411
  }
1369
1412
  // This can be used to empty all text...
1370
- setText(text = '', langString) {
1413
+ setText(text = '', langString, detectLanguage = false) {
1371
1414
  let newLines = text.split('\n');
1372
1415
  this.code.lines = [].concat(newLines);
1373
1416
  this._removeSecondaryCursors();
@@ -1376,6 +1419,9 @@ class CodeEditor {
1376
1419
  this.cursorToLine(cursor, newLines.length); // Already substracted 1
1377
1420
  this.cursorToPosition(cursor, lastLine?.length ?? 0, true);
1378
1421
  this.mustProcessLines = true;
1422
+ if (detectLanguage) {
1423
+ langString = this._detectLanguage(text);
1424
+ }
1379
1425
  if (langString) {
1380
1426
  this._changeLanguage(langString);
1381
1427
  }
@@ -1638,25 +1684,22 @@ class CodeEditor {
1638
1684
  this.onCreateStatusPanel(panel, this);
1639
1685
  }
1640
1686
  let leftStatusPanel = this.leftStatusPanel = new LX.Panel({ id: 'FontSizeZoomStatusComponent',
1641
- height: 'auto' });
1687
+ className: 'pad-xs content-center items-center flex-auto-keep', width: 'auto', height: 'auto' });
1642
1688
  leftStatusPanel.sameLine();
1643
- if (this.skipTabs) {
1644
- leftStatusPanel.addButton(null, 'ZoomOutButton', this._decreaseFontSize.bind(this), { icon: 'ZoomOut',
1645
- width: '32px', title: 'Zoom Out', tooltip: true });
1646
- }
1647
- leftStatusPanel.addButton(null, 'ZoomOutButton', this._decreaseFontSize.bind(this), { icon: 'ZoomOut',
1648
- width: '32px', title: 'Zoom Out', tooltip: true });
1689
+ leftStatusPanel.addButton(null, 'ZoomOutButton', this._decreaseFontSize.bind(this), { icon: 'ZoomOut', buttonClass: 'ghost sm',
1690
+ title: 'Zoom Out', tooltip: true });
1649
1691
  leftStatusPanel.addLabel(this.fontSize, { fit: true, signal: '@font-size' });
1650
- leftStatusPanel.addButton(null, 'ZoomInButton', this._increaseFontSize.bind(this), { icon: 'ZoomIn',
1651
- width: '32px', title: 'Zoom In', tooltip: true });
1692
+ leftStatusPanel.addButton(null, 'ZoomInButton', this._increaseFontSize.bind(this), { icon: 'ZoomIn', buttonClass: 'ghost sm',
1693
+ title: 'Zoom In', tooltip: true });
1652
1694
  leftStatusPanel.endLine('justify-start');
1653
1695
  panel.attach(leftStatusPanel.root);
1654
- let rightStatusPanel = this.rightStatusPanel = new LX.Panel({ height: 'auto' });
1696
+ let rightStatusPanel = this.rightStatusPanel = new LX.Panel({ className: 'pad-xs content-center items-center', height: 'auto' });
1655
1697
  rightStatusPanel.sameLine();
1656
- rightStatusPanel.addLabel(this.code?.title ?? '', { id: 'EditorFilenameStatusComponent', fit: true,
1698
+ rightStatusPanel.addLabel(this.code?.title ?? '', { id: 'EditorFilenameStatusComponent', fit: true, inputClass: 'text-xs',
1657
1699
  signal: '@tab-name' });
1658
1700
  rightStatusPanel.addButton(null, 'Ln 1, Col 1', this.showSearchLineBox.bind(this), {
1659
1701
  id: 'EditorSelectionStatusComponent',
1702
+ buttonClass: 'outline xs',
1660
1703
  fit: true,
1661
1704
  signal: '@cursor-data'
1662
1705
  });
@@ -1671,7 +1714,7 @@ class CodeEditor {
1671
1714
  });
1672
1715
  }
1673
1716
  });
1674
- }, { id: 'EditorIndentationStatusComponent', nameWidth: '15%', signal: '@tab-spaces' });
1717
+ }, { id: 'EditorIndentationStatusComponent', buttonClass: 'outline xs', signal: '@tab-spaces' });
1675
1718
  rightStatusPanel.addButton('<b>{ }</b>', this.highlight, (value, event) => {
1676
1719
  LX.addContextMenu('Language', event, (m) => {
1677
1720
  for (const lang of Object.keys(CodeEditor.languages)) {
@@ -1681,7 +1724,7 @@ class CodeEditor {
1681
1724
  });
1682
1725
  }
1683
1726
  });
1684
- }, { id: 'EditorLanguageStatusComponent', nameWidth: '15%', signal: '@highlight' });
1727
+ }, { id: 'EditorLanguageStatusComponent', nameWidth: 'auto', buttonClass: 'outline xs', signal: '@highlight', title: '' });
1685
1728
  rightStatusPanel.endLine('justify-end');
1686
1729
  panel.attach(rightStatusPanel.root);
1687
1730
  const itemVisibilityMap = {
@@ -1753,13 +1796,13 @@ class CodeEditor {
1753
1796
  }
1754
1797
  }
1755
1798
  if (langString === undefined) {
1756
- return 'AlignLeft fg-neutral-500';
1799
+ return 'AlignLeft text-neutral-500';
1757
1800
  }
1758
1801
  const iconPlusClasses = CodeEditor.languages[langString]?.icon;
1759
1802
  if (iconPlusClasses) {
1760
1803
  return iconPlusClasses[extension] ?? iconPlusClasses;
1761
1804
  }
1762
- return 'AlignLeft fg-neutral-500';
1805
+ return 'AlignLeft text-neutral-500';
1763
1806
  }
1764
1807
  _onNewTab(e) {
1765
1808
  this.processFocus(false);
@@ -1769,8 +1812,7 @@ class CodeEditor {
1769
1812
  }
1770
1813
  const dmOptions = this.newTabOptions ?? [
1771
1814
  { name: 'Create file', icon: 'FilePlus', callback: this._onCreateNewFile.bind(this) },
1772
- { name: 'Load file', icon: 'FileUp', disabled: !this.allowLoadingFiles,
1773
- callback: this.loadTabFromFile.bind(this) }
1815
+ { name: 'Load file', icon: 'FileUp', disabled: !this.allowLoadingFiles, callback: this.loadTabFromFile.bind(this) }
1774
1816
  ];
1775
1817
  new LX.DropdownMenu(e.target, dmOptions, { side: 'bottom', align: 'start' });
1776
1818
  }
@@ -1783,8 +1825,7 @@ class CodeEditor {
1783
1825
  }
1784
1826
  }
1785
1827
  const name = options.name ?? 'unnamed.js';
1786
- this.addTab(name, true, name, { indexOffset: options.indexOffset,
1787
- language: options.language ?? 'JavaScript' });
1828
+ this.addTab(name, true, name, { indexOffset: options.indexOffset, language: options.language ?? 'JavaScript' });
1788
1829
  }
1789
1830
  _onSelectTab(isNewTabButton, event, name) {
1790
1831
  if (this.disableEdition) {
@@ -2196,8 +2237,7 @@ class CodeEditor {
2196
2237
  processClick(e) {
2197
2238
  var cursor = this.getCurrentCursor();
2198
2239
  var code_rect = this.codeScroller.getBoundingClientRect();
2199
- var position = [(e.clientX - code_rect.x) + this.getScrollLeft(),
2200
- (e.clientY - code_rect.y) + this.getScrollTop()];
2240
+ var position = [(e.clientX - code_rect.x) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
2201
2241
  var ln = (position[1] / this.lineHeight) | 0;
2202
2242
  // Check out of range line
2203
2243
  const outOfRange = ln > this.code.lines.length - 1;
@@ -2362,12 +2402,12 @@ class CodeEditor {
2362
2402
  }
2363
2403
  async processKey(e) {
2364
2404
  const numCursors = this.cursors.length;
2365
- if (!this.code || e.srcElement?.constructor != HTMLDivElement) {
2405
+ if (!this.code || e.target?.constructor != HTMLDivElement) {
2366
2406
  return;
2367
2407
  }
2368
2408
  const detail = e.detail ?? {};
2369
2409
  const key = e.key ?? detail.key;
2370
- // Do not propagate "space to scroll" event
2410
+ // Don't propagate "space to scroll" event
2371
2411
  if (key == ' ') {
2372
2412
  e.preventDefault();
2373
2413
  e.stopPropagation();
@@ -2384,6 +2424,7 @@ class CodeEditor {
2384
2424
  return;
2385
2425
  }
2386
2426
  this._lastProcessedCursorIndex = null;
2427
+ this._nextCursorPositionOffset = 0;
2387
2428
  var lastProcessedCursor = null;
2388
2429
  var cursorOffset = new LX.vec2(0, 0);
2389
2430
  for (var i = 0; i < numCursors; i++) {
@@ -2397,18 +2438,35 @@ class CodeEditor {
2397
2438
  cursor.position += cursorOffset.x;
2398
2439
  cursor.line += cursorOffset.y;
2399
2440
  this.relocateCursors();
2441
+ // Apart from relocation based on offsets, its selection (if any) must be relocated too
2442
+ if (cursor.selection) {
2443
+ cursor.selection.fromX += cursorOffset.x;
2444
+ cursor.selection.toX += cursorOffset.x;
2445
+ cursor.selection.fromY += cursorOffset.y;
2446
+ cursor.selection.toY += cursorOffset.y;
2447
+ this._processSelection(cursor);
2448
+ }
2449
+ }
2450
+ else if (lastProcessedCursor && lastProcessedCursor.line != cursor.line) {
2451
+ // Reset offset X in case we changed line
2452
+ cursorOffset.x = 0;
2400
2453
  }
2401
2454
  lastProcessedCursor = this.saveCursor(cursor);
2402
2455
  this._lastProcessedCursorIndex = i;
2403
2456
  this._processKeyAtCursor(e, key, cursor);
2404
- cursorOffset.x += cursor.position - lastProcessedCursor.position;
2405
- cursorOffset.y += cursor.line - lastProcessedCursor.line;
2457
+ // Apply difference offset between last processed cursor and current positions plus any offset generated
2458
+ // during processing the key pressed
2459
+ const totalCursorOffsetX = (cursor.position - lastProcessedCursor.position) + this._nextCursorPositionOffset;
2460
+ const totalCursorOffsetY = cursor.line - lastProcessedCursor.line;
2461
+ cursorOffset.x += totalCursorOffsetX;
2462
+ cursorOffset.y += totalCursorOffsetY;
2406
2463
  // Set active line in case it's blurred
2407
2464
  if (!cursor.selection) {
2408
2465
  cursor.line = cursor.line;
2409
2466
  }
2410
2467
  }
2411
2468
  // Clear tmp
2469
+ delete this._nextCursorPositionOffset;
2412
2470
  delete this._lastProcessedCursorIndex;
2413
2471
  }
2414
2472
  async processKeyAtTargetCursor(e, key, targetIdx) {
@@ -2580,8 +2638,9 @@ class CodeEditor {
2580
2638
  lidx = cursor.line;
2581
2639
  }
2582
2640
  // Append key
2641
+ const nextChar = this.getCharAtPos(cursor); // Only pair keys if no next char or its a space
2583
2642
  const isPairKey = (Object.values(this.pairKeys).indexOf(key) > -1) && !this.wasKeyPaired;
2584
- const sameKeyNext = isPairKey && (this.code.lines[lidx][cursor.position] === key);
2643
+ const sameKeyNext = isPairKey && (nextChar === key);
2585
2644
  if (!sameKeyNext) {
2586
2645
  this.code.lines[lidx] = [
2587
2646
  this.code.lines[lidx].slice(0, cursor.position),
@@ -2591,7 +2650,7 @@ class CodeEditor {
2591
2650
  }
2592
2651
  this.cursorToRight(key, cursor);
2593
2652
  // Some custom cases for auto key pair (), {}, "", '', ...
2594
- const keyMustPair = this.pairKeys[key] !== undefined;
2653
+ const keyMustPair = (this.pairKeys[key] !== undefined) && (!nextChar || /\s/.test(nextChar));
2595
2654
  if (keyMustPair && !this.wasKeyPaired) {
2596
2655
  // Make sure to detect later that the key is paired automatically to avoid loops...
2597
2656
  this.wasKeyPaired = true;
@@ -2967,8 +3026,7 @@ class CodeEditor {
2967
3026
  if (blockComments && this._buildingBlockComment != undefined
2968
3027
  && token.substr(0, blockCommentsTokens[1].length) == blockCommentsTokens[1]) {
2969
3028
  const [commentLineNumber, tokenPos] = this._buildingBlockComment;
2970
- this._blockCommentCache.push([new LX.vec2(commentLineNumber, lineNumber),
2971
- new LX.vec2(tokenPos, tokenStartIndex)]);
3029
+ this._blockCommentCache.push([new LX.vec2(commentLineNumber, lineNumber), new LX.vec2(tokenPos, tokenStartIndex)]);
2972
3030
  delete this._buildingBlockComment;
2973
3031
  }
2974
3032
  if (token !== '{') {
@@ -3229,16 +3287,13 @@ class CodeEditor {
3229
3287
  // Add regexes to detect methods, variables ( including "id : nativeType" )
3230
3288
  {
3231
3289
  if (nativeTypes) {
3232
- topLevelRegexes.push([new RegExp(`^(?:${nativeTypes.join('|')})\\s+([A-Za-z0-9_]+)\s*[\(]+`),
3233
- 'method']);
3290
+ topLevelRegexes.push([new RegExp(`^(?:${nativeTypes.join('|')})\\s+([A-Za-z0-9_]+)\s*[\(]+`), 'method']);
3234
3291
  if (this.highlight === 'WGSL') {
3235
- topLevelRegexes.push([new RegExp(`[A-Za-z0-9]+(\\s*)+:(\\s*)+(${nativeTypes.join('|')})`),
3236
- 'variable', (m) => m[0].split(':')[0].trim()]);
3292
+ topLevelRegexes.push([new RegExp(`[A-Za-z0-9]+(\\s*)+:(\\s*)+(${nativeTypes.join('|')})`), 'variable', (m) => m[0].split(':')[0].trim()]);
3237
3293
  }
3238
3294
  }
3239
3295
  const declarationKeywords = CodeEditor.declarationKeywords[this.highlight] ?? ['const', 'let', 'var'];
3240
- topLevelRegexes.push([new RegExp(`^(?:${declarationKeywords.join('|')})\\s+([A-Za-z0-9_]+)`),
3241
- 'variable']);
3296
+ topLevelRegexes.push([new RegExp(`^(?:${declarationKeywords.join('|')})\\s+([A-Za-z0-9_]+)`), 'variable']);
3242
3297
  }
3243
3298
  for (let [regex, kind, fn] of topLevelRegexes) {
3244
3299
  const m = text.match(regex);
@@ -3455,8 +3510,7 @@ class CodeEditor {
3455
3510
  return wordCategory[this.highlight] && wordCategory[this.highlight].has(t);
3456
3511
  }
3457
3512
  _getTokenHighlighting(ctx, highlight) {
3458
- const rules = [...HighlightRules.common, ...(HighlightRules[highlight] || []),
3459
- ...HighlightRules.post_common];
3513
+ const rules = [...HighlightRules.common, ...(HighlightRules[highlight] || []), ...HighlightRules.post_common];
3460
3514
  for (const rule of rules) {
3461
3515
  if (!rule.test(ctx, this)) {
3462
3516
  continue;
@@ -3809,12 +3863,11 @@ class CodeEditor {
3809
3863
  }
3810
3864
  deleteSelection(cursor) {
3811
3865
  // I think it's not necessary but...
3812
- if (this.disableEdition) {
3866
+ if (this.disableEdition || !cursor.selection) {
3813
3867
  return;
3814
3868
  }
3815
3869
  // Some selections don't depend on mouse up..
3816
- if (cursor.selection)
3817
- cursor.selection.invertIfNecessary();
3870
+ cursor.selection.invertIfNecessary();
3818
3871
  const selection = cursor.selection;
3819
3872
  const separator = '_NEWLINE_';
3820
3873
  let code = this.code.lines.join(separator);
@@ -4002,7 +4055,7 @@ class CodeEditor {
4002
4055
  }
4003
4056
  LX.deleteElement(this.selections[cursor.name]);
4004
4057
  delete this.selections[cursor.name];
4005
- LX.deleteElement(cursor);
4058
+ cursor.destroy();
4006
4059
  }
4007
4060
  resetCursorPos(flag, cursor, resetScroll = false) {
4008
4061
  cursor = cursor ?? this.getCurrentCursor();
@@ -4341,8 +4394,7 @@ class CodeEditor {
4341
4394
  text.innerText = char;
4342
4395
  var rect = text.getBoundingClientRect();
4343
4396
  LX.deleteElement(parentContainer);
4344
- const bb = [useFloating ? rect.width : Math.floor(rect.width),
4345
- useFloating ? rect.height : Math.floor(rect.height)];
4397
+ const bb = [useFloating ? rect.width : Math.floor(rect.width), useFloating ? rect.height : Math.floor(rect.height)];
4346
4398
  return getBB ? bb : bb[0];
4347
4399
  }
4348
4400
  measureString(str) {
@@ -4442,15 +4494,15 @@ class CodeEditor {
4442
4494
  {
4443
4495
  case 'variable':
4444
4496
  iconName = 'Cuboid';
4445
- iconClass = 'fg-blue-400';
4497
+ iconClass = 'text-blue-400';
4446
4498
  break;
4447
4499
  case 'method':
4448
4500
  iconName = 'Box';
4449
- iconClass = 'fg-fuchsia-500';
4501
+ iconClass = 'text-fuchsia-500';
4450
4502
  break;
4451
4503
  case 'class':
4452
4504
  iconName = 'CircleNodes';
4453
- iconClass = 'fg-orange-500';
4505
+ iconClass = 'text-orange-500';
4454
4506
  break;
4455
4507
  }
4456
4508
  }
@@ -4460,7 +4512,7 @@ class CodeEditor {
4460
4512
  }
4461
4513
  else if (this._mustHightlightWord(currSuggestion, CodeEditor.types)) {
4462
4514
  iconName = 'Type';
4463
- iconClass = 'fg-blue-400';
4515
+ iconClass = 'text-blue-400';
4464
4516
  }
4465
4517
  }
4466
4518
  pre.appendChild(LX.makeIcon(iconName, { iconClass: 'mr-1', svgClass: 'sm ' + iconClass }));
@@ -4491,8 +4543,7 @@ class CodeEditor {
4491
4543
  this.autocomplete.classList.toggle('show', true);
4492
4544
  this.autocomplete.classList.toggle('no-scrollbar', !(this.autocomplete.scrollHeight > this.autocomplete.offsetHeight));
4493
4545
  this.autocomplete.style.left = `${Math.min(cursor.left + CodeEditor.LINE_GUTTER_WIDTH - this.getScrollLeft(), maxX)}px`;
4494
- this.autocomplete.style.top =
4495
- `${(cursor.top + this._verticalTopOffset + this.lineHeight - this.getScrollTop())}px`;
4546
+ this.autocomplete.style.top = `${(cursor.top + this._verticalTopOffset + this.lineHeight - this.getScrollTop())}px`;
4496
4547
  this.isAutoCompleteActive = true;
4497
4548
  }
4498
4549
  hideAutoCompleteBox() {
@@ -4729,8 +4780,10 @@ class CodeEditor {
4729
4780
  var newCursor = this._addCursor(ln, col, true);
4730
4781
  if (newCursor) {
4731
4782
  this.startSelection(newCursor);
4732
- newCursor?.root.selection.selectInline(newCursor, col, ln, this.measureString(text));
4733
- this.cursorToString(newCursor, text);
4783
+ if (newCursor.selection) {
4784
+ newCursor.selection.selectInline(newCursor, col, ln, this.measureString(text));
4785
+ this.cursorToString(newCursor, text);
4786
+ }
4734
4787
  }
4735
4788
  this._currentOcurrences[key] = true;
4736
4789
  }, true, false);
@@ -4838,117 +4891,99 @@ class CodeEditor {
4838
4891
  }
4839
4892
  const CE = CodeEditor;
4840
4893
  CE.languages = {
4841
- 'Plain Text': { ext: 'txt', blockComments: false, singleLineComments: false, numbers: false,
4842
- icon: 'AlignLeft fg-neutral-500' },
4843
- 'JavaScript': { ext: 'js', icon: 'Js fg-yellow-500' },
4844
- 'TypeScript': { ext: 'ts', icon: 'Ts fg-blue-600' },
4845
- 'C': { ext: ['c', 'h'], usePreprocessor: true, icon: { 'c': 'C fg-sky-400', 'h': 'C fg-fuchsia-500' } },
4846
- 'C++': { ext: ['cpp', 'hpp'], usePreprocessor: true,
4847
- icon: { 'cpp': 'CPlusPlus fg-sky-400', 'hpp': 'CPlusPlus fg-fuchsia-500' } },
4848
- 'CSS': { ext: 'css', icon: 'Hash fg-blue-700' },
4894
+ 'Plain Text': { ext: 'txt', blockComments: false, singleLineComments: false, numbers: false, icon: 'AlignLeft text-neutral-500' },
4895
+ 'JavaScript': { ext: 'js', icon: 'Js text-yellow-500' },
4896
+ 'TypeScript': { ext: 'ts', icon: 'Ts text-blue-600' },
4897
+ 'C': { ext: ['c', 'h'], usePreprocessor: true, icon: { 'c': 'C text-sky-400', 'h': 'C text-fuchsia-500' } },
4898
+ 'C++': { ext: ['cpp', 'hpp'], usePreprocessor: true, icon: { 'cpp': 'CPlusPlus text-sky-400', 'hpp': 'CPlusPlus text-fuchsia-500' } },
4899
+ 'CSS': { ext: 'css', icon: 'Hash text-blue-700' },
4849
4900
  'CMake': { ext: 'cmake', singleLineCommentToken: '#', blockComments: false, ignoreCase: true },
4850
4901
  'GLSL': { ext: 'glsl', usePreprocessor: true },
4851
4902
  'WGSL': { ext: 'wgsl', usePreprocessor: true },
4852
- 'JSON': { ext: 'json', blockComments: false, singleLineComments: false, icon: 'Json fg-yellow-400' },
4853
- 'XML': { ext: 'xml', tags: true, icon: 'Rss fg-orange-500' },
4854
- 'Rust': { ext: 'rs', icon: 'Rust fg-primary' },
4855
- 'Python': { ext: 'py', singleLineCommentToken: '#', icon: 'Python fg-cyan-600' },
4856
- 'HTML': { ext: 'html', tags: true, singleLineComments: false, blockCommentsTokens: ['<!--', '-->'],
4857
- numbers: false, icon: 'Code fg-orange-500' },
4858
- 'Batch': { ext: 'bat', blockComments: false, singleLineCommentToken: '::', ignoreCase: true,
4859
- icon: 'Windows fg-blue-400' },
4860
- 'Markdown': { ext: 'md', blockComments: false, singleLineCommentToken: '::', tags: true, numbers: false,
4861
- icon: 'Markdown fg-primary' },
4862
- 'PHP': { ext: 'php', icon: 'Php fg-purple-700' }
4903
+ 'JSON': { ext: 'json', blockComments: false, singleLineComments: false, icon: 'Json text-yellow-400' },
4904
+ 'XML': { ext: 'xml', tags: true, icon: 'Rss text-orange-500' },
4905
+ 'Rust': { ext: 'rs', icon: 'Rust text-foreground' },
4906
+ 'Python': { ext: 'py', singleLineCommentToken: '#', icon: 'Python text-cyan-600' },
4907
+ 'HTML': { ext: 'html', tags: true, singleLineComments: false, blockCommentsTokens: ['<!--', '-->'], numbers: false,
4908
+ icon: 'Code text-orange-500' },
4909
+ 'Batch': { ext: 'bat', blockComments: false, singleLineCommentToken: '::', ignoreCase: true, icon: 'Windows text-blue-400' },
4910
+ 'Markdown': { ext: 'md', blockComments: false, singleLineCommentToken: '::', tags: true, numbers: false, icon: 'Markdown text-foreground' },
4911
+ 'PHP': { ext: 'php', icon: 'Php text-purple-700' }
4863
4912
  };
4864
4913
  CE.nativeTypes = {
4865
4914
  'C++': ['int', 'float', 'double', 'bool', 'long', 'short', 'char', 'wchar_t', 'void'],
4866
- 'WGSL': ['bool', 'u32', 'i32', 'f16', 'f32', 'vec2', 'vec3', 'vec4', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f',
4867
- 'mat3x3f', 'mat4x4f', 'array', 'vec2u', 'vec3u', 'vec4u', 'ptr', 'sampler']
4915
+ 'WGSL': ['bool', 'u32', 'i32', 'f16', 'f32', 'vec2', 'vec3', 'vec4', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array',
4916
+ 'vec2u', 'vec3u', 'vec4u', 'ptr', 'sampler']
4868
4917
  };
4869
4918
  CE.declarationKeywords = {
4870
4919
  'JavaScript': ['var', 'let', 'const', 'this', 'static', 'class'],
4871
4920
  'C++': [...CE.nativeTypes['C++'], 'const', 'auto', 'class', 'struct', 'namespace', 'enum', 'extern']
4872
4921
  };
4873
4922
  CE.keywords = {
4874
- 'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static',
4875
- 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract', 'arguments', 'extends', 'instanceof',
4876
- 'Infinity', 'get'],
4877
- 'TypeScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'class', 'extends',
4878
- 'instanceof', 'Infinity', 'private', 'public', 'protected', 'interface', 'enum', 'type', 'get'],
4879
- 'C': ['int', 'float', 'double', 'long', 'short', 'char', 'const', 'void', 'true', 'false', 'auto', 'struct',
4880
- 'typedef', 'signed', 'volatile', 'unsigned', 'static', 'extern', 'enum', 'register', 'union'],
4881
- 'C++': [...CE.nativeTypes['C++'], 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'true', 'false', 'auto',
4882
- 'class', 'struct', 'typedef', 'nullptr', 'NULL', 'signed', 'unsigned', 'namespace', 'enum', 'extern', 'union',
4883
- 'sizeof', 'static', 'private', 'public'],
4884
- 'CMake': ['cmake_minimum_required', 'set', 'not', 'if', 'endif', 'exists', 'string', 'strequal', 'add_definitions',
4885
- 'macro', 'endmacro', 'file', 'list', 'source_group', 'add_executable', 'target_include_directories',
4886
- 'set_target_properties', 'set_property', 'add_compile_options', 'add_link_options', 'include_directories',
4887
- 'add_library', 'target_link_libraries', 'target_link_options', 'add_subdirectory', 'add_compile_definitions',
4888
- 'project', 'cache'],
4923
+ 'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null',
4924
+ 'typeof', 'debugger', 'abstract', 'arguments', 'extends', 'instanceof', 'Infinity', 'get'],
4925
+ 'TypeScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'class', 'extends', 'instanceof', 'Infinity',
4926
+ 'private', 'public', 'protected', 'interface', 'enum', 'type', 'get'],
4927
+ 'C': ['int', 'float', 'double', 'long', 'short', 'char', 'const', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'signed', 'volatile',
4928
+ 'unsigned', 'static', 'extern', 'enum', 'register', 'union'],
4929
+ 'C++': [...CE.nativeTypes['C++'], 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'true', 'false', 'auto', 'class', 'struct', 'typedef',
4930
+ 'nullptr', 'NULL', 'signed', 'unsigned', 'namespace', 'enum', 'extern', 'union', 'sizeof', 'static', 'private', 'public'],
4931
+ 'CMake': ['cmake_minimum_required', 'set', 'not', 'if', 'endif', 'exists', 'string', 'strequal', 'add_definitions', 'macro', 'endmacro', 'file',
4932
+ 'list', 'source_group', 'add_executable', 'target_include_directories', 'set_target_properties', 'set_property', 'add_compile_options',
4933
+ 'add_link_options', 'include_directories', 'add_library', 'target_link_libraries', 'target_link_options', 'add_subdirectory',
4934
+ 'add_compile_definitions', 'project', 'cache'],
4889
4935
  'JSON': ['true', 'false'],
4890
- 'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4',
4891
- 'struct'],
4892
- 'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.', 'table', 'tr', 'td', 'th', 'label', 'video', 'img',
4893
- 'code', 'button', 'select', 'option', 'svg', 'media', 'all', 'i', 'a', 'li', 'h1', 'h2', 'h3', 'h4', 'h5',
4894
- 'last-child', 'tbody', 'pre', 'monospace', 'font-face'],
4895
- 'WGSL': [...CE.nativeTypes['WGSL'], 'var', 'let', 'true', 'false', 'fn', 'atomic', 'struct', 'sampler_comparison',
4896
- 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array',
4897
- 'texture_depth_multisampled_2d', 'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array',
4898
- 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
4936
+ 'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
4937
+ 'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.', 'table', 'tr', 'td', 'th', 'label', 'video', 'img', 'code', 'button', 'select',
4938
+ 'option', 'svg', 'media', 'all', 'i', 'a', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'last-child', 'tbody', 'pre', 'monospace', 'font-face'],
4939
+ 'WGSL': [...CE.nativeTypes['WGSL'], 'var', 'let', 'true', 'false', 'fn', 'atomic', 'struct', 'sampler_comparison', 'texture_depth_2d',
4940
+ 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d', 'texture_external', 'texture_1d',
4941
+ 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
4899
4942
  'texture_storage_2d_array', 'texture_storage_3d'],
4900
- 'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub',
4901
- 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where',
4902
- 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
4943
+ 'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self',
4944
+ 'static', 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override',
4945
+ 'priv', 'typeof', 'unsized', 'virtual'],
4903
4946
  'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
4904
4947
  'Batch': ['set', 'echo', 'off', 'del', 'defined', 'setlocal', 'enabledelayedexpansion', 'driverquery', 'print'],
4905
- 'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr',
4906
- 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
4948
+ 'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr', 'td', 'h1', 'h2', 'h3', 'h4',
4949
+ 'h5'],
4907
4950
  'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
4908
- 'PHP': ['const', 'function', 'array', 'new', 'int', 'string', '$this', 'public', 'null', 'private', 'protected',
4909
- 'implements', 'class', 'use', 'namespace', 'abstract', 'clone', 'final', 'enum']
4951
+ 'PHP': ['const', 'function', 'array', 'new', 'int', 'string', '$this', 'public', 'null', 'private', 'protected', 'implements', 'class', 'use',
4952
+ 'namespace', 'abstract', 'clone', 'final', 'enum']
4910
4953
  };
4911
4954
  // These ones don't have hightlight, used as suggestions to autocomplete only...
4912
4955
  CE.utils = {
4913
- 'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys',
4914
- 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite', 'bind', 'prototype', 'length', 'assign',
4915
- 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild',
4916
- 'createElement', 'prompt', 'alert'],
4956
+ 'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat',
4957
+ 'parseInt', 'EPSILON', 'isFinite', 'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice',
4958
+ 'slice', 'buffer', 'appendChild', 'createElement', 'prompt', 'alert'],
4917
4959
  'WGSL': ['textureSample'],
4918
- 'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod',
4919
- 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'filter', 'float',
4920
- 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int',
4921
- 'isinstance', 'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next',
4922
- 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set',
4923
- 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'],
4924
- 'CSS': [...Object.keys(document.body.style).map(LX.toKebabCase), 'block', 'inline', 'inline-block', 'flex',
4925
- 'grid', 'none', 'inherit', 'initial', 'unset', 'revert', 'sticky', 'relative', 'absolute', 'fixed', 'static',
4926
- 'auto', 'visible', 'hidden', 'scroll', 'clip', 'ellipsis', 'nowrap', 'wrap', 'break-word', 'solid', 'dashed',
4927
- 'dotted', 'double', 'groove', 'ridge', 'inset', 'outset', 'left', 'right', 'center', 'top', 'bottom', 'start',
4928
- 'end', 'justify', 'stretch', 'space-between', 'space-around', 'space-evenly', 'baseline', 'middle', 'normal',
4929
- 'bold', 'lighter', 'bolder', 'italic', 'blur', 'uppercase', 'lowercase', 'capitalize', 'transparent',
4930
- 'currentColor', 'pointer', 'default', 'move', 'grab', 'grabbing', 'not-allowed', 'none', 'cover', 'contain',
4931
- 'repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'round', 'space', 'linear-gradient', 'radial-gradient',
4932
- 'conic-gradient', 'url', 'calc', 'min', 'max', 'clamp', 'red', 'blue', 'green', 'black', 'white', 'gray',
4933
- 'silver', 'yellow', 'orange', 'purple', 'pink', 'cyan', 'magenta', 'lime', 'teal', 'navy', 'transparent',
4934
- 'currentcolor', 'inherit', 'initial', 'unset', 'revert', 'none', 'auto', 'fit-content', 'min-content',
4935
- 'max-content']
4960
+ 'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr',
4961
+ 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash',
4962
+ 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next',
4963
+ 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted',
4964
+ 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'],
4965
+ 'CSS': [...Object.keys(document.body.style).map(LX.toKebabCase), 'block', 'inline', 'inline-block', 'flex', 'grid', 'none', 'inherit',
4966
+ 'initial', 'unset', 'revert', 'sticky', 'relative', 'absolute', 'fixed', 'static', 'auto', 'visible', 'hidden', 'scroll', 'clip', 'ellipsis',
4967
+ 'nowrap', 'wrap', 'break-word', 'solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'inset', 'outset', 'left', 'right', 'center', 'top',
4968
+ 'bottom', 'start', 'end', 'justify', 'stretch', 'space-between', 'space-around', 'space-evenly', 'baseline', 'middle', 'normal', 'bold',
4969
+ 'lighter', 'bolder', 'italic', 'blur', 'uppercase', 'lowercase', 'capitalize', 'transparent', 'currentColor', 'pointer', 'default', 'move',
4970
+ 'grab', 'grabbing', 'not-allowed', 'none', 'cover', 'contain', 'repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'round', 'space',
4971
+ 'linear-gradient', 'radial-gradient', 'conic-gradient', 'url', 'calc', 'min', 'max', 'clamp', 'red', 'blue', 'green', 'black', 'white',
4972
+ 'gray', 'silver', 'yellow', 'orange', 'purple', 'pink', 'cyan', 'magenta', 'lime', 'teal', 'navy', 'transparent', 'currentcolor', 'inherit',
4973
+ 'initial', 'unset', 'revert', 'none', 'auto', 'fit-content', 'min-content', 'max-content']
4936
4974
  };
4937
4975
  CE.types = {
4938
- 'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder',
4939
- 'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent', 'Int8Array', 'Int16Array', 'Int32Array',
4940
- 'Float32Array', 'Float64Array', 'Element'],
4941
- 'TypeScript': ['arguments', 'constructor', 'null', 'typeof', 'debugger', 'abstract', 'Object', 'string', 'String',
4942
- 'Function', 'Boolean', 'boolean', 'Error', 'Number', 'number', 'TextEncoder', 'TextDecoder', 'Array',
4943
- 'ArrayBuffer', 'InputEvent', 'MouseEvent', 'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array',
4944
- 'Float64Array', 'Element', 'bigint', 'unknown', 'any', 'Record'],
4976
+ 'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder', 'Array', 'ArrayBuffer',
4977
+ 'InputEvent', 'MouseEvent', 'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element'],
4978
+ 'TypeScript': ['arguments', 'constructor', 'null', 'typeof', 'debugger', 'abstract', 'Object', 'string', 'String', 'Function', 'Boolean',
4979
+ 'boolean', 'Error', 'Number', 'number', 'TextEncoder', 'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent', 'Int8Array',
4980
+ 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element', 'bigint', 'unknown', 'any', 'Record'],
4945
4981
  'Rust': ['u128'],
4946
- 'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError',
4947
- 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit', 'ImportError', 'IndentationError', 'IndexError',
4948
- 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
4949
- 'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError',
4950
- 'SystemExit', 'TypeError', 'UnboundLocalError', 'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError',
4951
- 'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError'],
4982
+ 'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError',
4983
+ 'FloatingPointError', 'GeneratorExit', 'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
4984
+ 'MemoryError', 'NameError', 'NotImplementedError', 'OSError', 'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration',
4985
+ 'SyntaxError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError', 'UnicodeError', 'UnicodeEncodeError',
4986
+ 'UnicodeDecodeError', 'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError'],
4952
4987
  'C++': ['uint8_t', 'uint16_t', 'uint32_t'],
4953
4988
  'PHP': ['Exception', 'DateTime', 'JsonSerializable']
4954
4989
  };
@@ -4962,25 +4997,22 @@ CE.builtIn = {
4962
4997
  'PHP': ['echo', 'print']
4963
4998
  };
4964
4999
  CE.statements = {
4965
- 'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import',
4966
- 'default', 'export', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
4967
- 'TypeScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import',
4968
- 'default', 'export', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
5000
+ 'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'default', 'export', 'from',
5001
+ 'throw', 'async', 'try', 'catch', 'await', 'as'],
5002
+ 'TypeScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'default', 'export', 'from',
5003
+ 'throw', 'async', 'try', 'catch', 'await', 'as'],
4969
5004
  'CSS': ['@', 'import'],
4970
- 'C': ['for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'default', 'goto',
4971
- 'do'],
4972
- 'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm',
4973
- 'spdlog', 'default'],
5005
+ 'C': ['for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'default', 'goto', 'do'],
5006
+ 'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog', 'default'],
4974
5007
  'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
4975
- 'WGSL': ['const', 'for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'read_write', 'uniform',
4976
- 'function', 'workgroup', 'bitcast'],
5008
+ 'WGSL': ['const', 'for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'read_write', 'uniform', 'function', 'workgroup',
5009
+ 'bitcast'],
4977
5010
  'Rust': ['break', 'else', 'continue', 'for', 'if', 'loop', 'match', 'return', 'while', 'do', 'yield'],
4978
- 'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with',
4979
- 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue', 'global', 'pass', 'from'],
5011
+ 'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield',
5012
+ 'break', 'for', 'class', 'continue', 'global', 'pass', 'from'],
4980
5013
  'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT'],
4981
- 'PHP': ['declare', 'enddeclare', 'foreach', 'endforeach', 'if', 'else', 'elseif', 'endif', 'for', 'endfor',
4982
- 'while', 'endwhile', 'switch', 'case', 'default', 'endswitch', 'return', 'break', 'continue', 'try', 'catch',
4983
- 'die', 'do', 'exit', 'finally']
5014
+ 'PHP': ['declare', 'enddeclare', 'foreach', 'endforeach', 'if', 'else', 'elseif', 'endif', 'for', 'endfor', 'while', 'endwhile', 'switch',
5015
+ 'case', 'default', 'endswitch', 'return', 'break', 'continue', 'try', 'catch', 'die', 'do', 'exit', 'finally']
4984
5016
  };
4985
5017
  CE.symbols = {
4986
5018
  'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],