lexgui 0.7.8 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
1
  import { LX } from 'lexgui';
2
2
 
3
- if(!LX) {
4
- throw("lexgui.js missing!");
3
+ if( !LX )
4
+ {
5
+ throw( "lexgui.js missing!" );
5
6
  }
6
7
 
7
8
  LX.extensions.push( 'CodeEditor' );
@@ -14,8 +15,8 @@ function strReverse( str ) { return str.split( "" ).reverse().join( "" ); }
14
15
  function isLetter( c ){ return /[a-zA-Z]/.test( c ); };
15
16
  function isSymbol( c ){ return /[^\w\s]/.test( c ); };
16
17
 
17
- function indexOfFrom( str, reg, from, reverse ) {
18
-
18
+ function indexOfFrom( str, reg, from, reverse )
19
+ {
19
20
  from = from ?? 0;
20
21
 
21
22
  if( reverse )
@@ -33,7 +34,8 @@ function indexOfFrom( str, reg, from, reverse ) {
33
34
  }
34
35
  }
35
36
 
36
- function codeScopesEqual( a, b ) {
37
+ function codeScopesEqual( a, b )
38
+ {
37
39
  if( a.length !== b.length ) return false;
38
40
  for( let i = 0; i < a.length; i++ )
39
41
  {
@@ -42,10 +44,10 @@ function codeScopesEqual( a, b ) {
42
44
  return true;
43
45
  }
44
46
 
45
- class CodeSelection {
46
-
47
- constructor( editor, cursor, className = "lexcodeselection" ) {
48
-
47
+ class CodeSelection
48
+ {
49
+ constructor( editor, cursor, className = "lexcodeselection" )
50
+ {
49
51
  this.editor = editor;
50
52
  this.cursor = cursor;
51
53
  this.className = className;
@@ -57,20 +59,23 @@ class CodeSelection {
57
59
  this.toY = cursor.line;
58
60
  }
59
61
 
60
- sameLine() {
62
+ sameLine()
63
+ {
61
64
  return this.fromY === this.toY;
62
65
  }
63
66
 
64
- samePosition() {
67
+ samePosition()
68
+ {
65
69
  return this.fromX === this.toX;
66
70
  }
67
71
 
68
- isEmpty() {
72
+ isEmpty()
73
+ {
69
74
  return this.sameLine() && this.samePosition();
70
75
  }
71
76
 
72
- invertIfNecessary() {
73
- // if( this.fromX > this.toX )
77
+ invertIfNecessary()
78
+ {
74
79
  if( this.fromY > this.toY )
75
80
  {
76
81
  swapElements( this, 'fromX', 'toX' );
@@ -82,8 +87,8 @@ class CodeSelection {
82
87
  }
83
88
  }
84
89
 
85
- selectInline( cursor, x, y, width, isSearchResult ) {
86
-
90
+ selectInline( cursor, x, y, width, isSearchResult )
91
+ {
87
92
  this.chars = width / this.editor.charWidth;
88
93
  this.fromX = x;
89
94
  this.toX = x + this.chars;
@@ -110,8 +115,8 @@ class CodeSelection {
110
115
  this.editor._hideActiveLine();
111
116
  }
112
117
 
113
- save() {
114
-
118
+ save()
119
+ {
115
120
  return {
116
121
  fromX: this.fromX,
117
122
  fromY: this.fromY,
@@ -120,16 +125,16 @@ class CodeSelection {
120
125
  }
121
126
  }
122
127
 
123
- load( data ) {
124
-
128
+ load( data )
129
+ {
125
130
  this.fromX = data.fromX;
126
131
  this.fromY = data.fromY;
127
132
  this.toX = data.toX;
128
133
  this.toY = data.toY;
129
134
  }
130
135
 
131
- getText() {
132
-
136
+ getText()
137
+ {
133
138
  if( !this.editor.code || !this.sameLine() )
134
139
  return null;
135
140
 
@@ -137,16 +142,16 @@ class CodeSelection {
137
142
  }
138
143
  };
139
144
 
140
- class ScrollBar {
141
-
145
+ class ScrollBar
146
+ {
142
147
  static SCROLLBAR_VERTICAL = 1;
143
148
  static SCROLLBAR_HORIZONTAL = 2;
144
149
 
145
150
  static SCROLLBAR_VERTICAL_WIDTH = 10;
146
151
  static SCROLLBAR_HORIZONTAL_HEIGHT = 10;
147
152
 
148
- constructor( editor, type ) {
149
-
153
+ constructor( editor, type )
154
+ {
150
155
  this.editor = editor;
151
156
  this.type = type;
152
157
 
@@ -199,7 +204,6 @@ class ScrollBar {
199
204
  doc.removeEventListener( "mouseup", inner_mouseup );
200
205
  }
201
206
  }
202
-
203
207
  }
204
208
 
205
209
  /* Highlight rules
@@ -210,8 +214,8 @@ class ScrollBar {
210
214
  to "ctx.discardToken" and no class is applied
211
215
  */
212
216
 
213
- const HighlightRules = {
214
-
217
+ const HighlightRules =
218
+ {
215
219
  common: [
216
220
  { test: ctx => ctx.inBlockComment, className: "cm-com" },
217
221
  { test: ctx => ctx.inString, action: (ctx, editor) => editor._appendStringToken( ctx.token ), discard: true },
@@ -280,9 +284,9 @@ const HighlightRules = {
280
284
  * @class CodeEditor
281
285
  */
282
286
 
283
- class CodeEditor {
284
-
285
- static __instances = [];
287
+ class CodeEditor
288
+ {
289
+ static __instances = [];
286
290
 
287
291
  static CURSOR_LEFT = 1;
288
292
  static CURSOR_TOP = 2;
@@ -295,17 +299,17 @@ class CodeEditor {
295
299
  static KEEP_VISIBLE_LINES = 1;
296
300
  static UPDATE_VISIBLE_LINES = 2;
297
301
 
298
- static WORD_TYPE_METHOD = 0;
299
- static WORD_TYPE_CLASS = 1;
302
+ static WORD_TYPE_METHOD = 0;
303
+ static WORD_TYPE_CLASS = 1;
300
304
 
301
- static CODE_MIN_FONT_SIZE = 9;
302
- static CODE_MAX_FONT_SIZE = 22;
305
+ static CODE_MIN_FONT_SIZE = 9;
306
+ static CODE_MAX_FONT_SIZE = 22;
303
307
 
304
- static LINE_GUTTER_WIDTH = 48;
305
- static LINE_GUTTER_WIDTH = 48;
308
+ static LINE_GUTTER_WIDTH = 48;
309
+ static LINE_GUTTER_WIDTH = 48;
306
310
 
307
- static RESIZE_SCROLLBAR_H = 1;
308
- static RESIZE_SCROLLBAR_V = 2;
311
+ static RESIZE_SCROLLBAR_H = 1;
312
+ static RESIZE_SCROLLBAR_V = 2;
309
313
  static RESIZE_SCROLLBAR_H_V = CodeEditor.RESIZE_SCROLLBAR_H | CodeEditor.RESIZE_SCROLLBAR_V;
310
314
 
311
315
  /**
@@ -317,8 +321,8 @@ class CodeEditor {
317
321
  * disableEdition:
318
322
  */
319
323
 
320
- constructor( area, options = {} ) {
321
-
324
+ constructor( area, options = {} )
325
+ {
322
326
  if( options.filesAsync )
323
327
  {
324
328
  options.files = [ ...options.filesAsync ];
@@ -336,8 +340,8 @@ class CodeEditor {
336
340
  }
337
341
  }
338
342
 
339
- async _init( area, options ) {
340
-
343
+ async _init( area, options )
344
+ {
341
345
  window.editor = this;
342
346
 
343
347
  CodeEditor.__instances.push( this );
@@ -1197,8 +1201,8 @@ class CodeEditor {
1197
1201
  else // Next char
1198
1202
  {
1199
1203
  var letter = this.getCharAtPos( cursor );
1200
- if( letter ) {
1201
-
1204
+ if( letter )
1205
+ {
1202
1206
  // Selecting chars
1203
1207
  if( e.shiftKey )
1204
1208
  {
@@ -1210,7 +1214,8 @@ class CodeEditor {
1210
1214
  }
1211
1215
  else
1212
1216
  {
1213
- if( !cursor.selection ) {
1217
+ if( !cursor.selection )
1218
+ {
1214
1219
  this.cursorToRight( letter, cursor );
1215
1220
  if( this.useAutoComplete && this.isAutoCompleteActive )
1216
1221
  this.showAutoCompleteBox( 'foo', cursor );
@@ -1221,16 +1226,20 @@ class CodeEditor {
1221
1226
  this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, cursor );
1222
1227
  this.cursorToLine( cursor, cursor.selection.toY );
1223
1228
  this.cursorToPosition( cursor, cursor.selection.toX );
1224
- this.endSelection();
1229
+ this.endSelection( cursor );
1225
1230
  }
1226
1231
  }
1227
1232
  }
1228
- else if( this.code.lines[ cursor.line + 1 ] !== undefined ) {
1229
-
1230
- if( e.shiftKey ) {
1233
+ else if( this.code.lines[ cursor.line + 1 ] !== undefined )
1234
+ {
1235
+ if( e.shiftKey )
1236
+ {
1231
1237
  if( !cursor.selection ) this.startSelection( cursor );
1232
1238
  }
1233
- else this.endSelection();
1239
+ else
1240
+ {
1241
+ this.endSelection();
1242
+ }
1234
1243
 
1235
1244
  this.lineDown( cursor, true );
1236
1245
 
@@ -1338,7 +1347,8 @@ class CodeEditor {
1338
1347
  }
1339
1348
 
1340
1349
  // Clear signals
1341
- clear() {
1350
+ clear()
1351
+ {
1342
1352
  console.assert( this.rightStatusPanel && this.leftStatusPanel, "No panels to clear." );
1343
1353
  this.rightStatusPanel.clear();
1344
1354
  this.leftStatusPanel.clear();
@@ -1350,8 +1360,8 @@ class CodeEditor {
1350
1360
  }
1351
1361
 
1352
1362
  // This received key inputs from the entire document...
1353
- onKeyPressed( e ) {
1354
-
1363
+ onKeyPressed( e )
1364
+ {
1355
1365
  // Toggle visibility of the file explorer
1356
1366
  if( e.key == 'b' && e.ctrlKey && this.useFileExplorer )
1357
1367
  {
@@ -1369,13 +1379,14 @@ class CodeEditor {
1369
1379
  }
1370
1380
  }
1371
1381
 
1372
- getText( min ) {
1382
+ getText( min )
1383
+ {
1373
1384
  return this.code.lines.join( min ? ' ' : '\n' );
1374
1385
  }
1375
1386
 
1376
1387
  // This can be used to empty all text...
1377
- setText( text = "", lang ) {
1378
-
1388
+ setText( text = "", lang )
1389
+ {
1379
1390
  let newLines = text.split( '\n' );
1380
1391
  this.code.lines = [].concat( newLines );
1381
1392
 
@@ -1397,8 +1408,8 @@ class CodeEditor {
1397
1408
  this._processLinesIfNecessary();
1398
1409
  }
1399
1410
 
1400
- appendText( text, cursor ) {
1401
-
1411
+ appendText( text, cursor )
1412
+ {
1402
1413
  let lidx = cursor.line;
1403
1414
 
1404
1415
  if( cursor.selection )
@@ -1468,8 +1479,18 @@ class CodeEditor {
1468
1479
  } );
1469
1480
  }
1470
1481
 
1471
- async loadFile( file, options = {} ) {
1482
+ setCustomSuggestions( suggestions )
1483
+ {
1484
+ if( !suggestions || suggestions.constructor !== Array )
1485
+ {
1486
+ return;
1487
+ }
1488
+
1489
+ this.customSuggestions = suggestions;
1490
+ }
1472
1491
 
1492
+ async loadFile( file, options = {} )
1493
+ {
1473
1494
  const _innerAddTab = ( text, name, title ) => {
1474
1495
 
1475
1496
  // Remove Carriage Return in some cases and sub tabs using spaces
@@ -1540,8 +1561,8 @@ class CodeEditor {
1540
1561
  }
1541
1562
  }
1542
1563
 
1543
- _addUndoStep( cursor, force, deleteRedo = true ) {
1544
-
1564
+ _addUndoStep( cursor, force, deleteRedo = true )
1565
+ {
1545
1566
  // Only the mainc cursor stores undo steps
1546
1567
  if( !cursor.isMain )
1547
1568
  return;
@@ -1582,10 +1603,12 @@ class CodeEditor {
1582
1603
  } );
1583
1604
  }
1584
1605
 
1585
- _doUndo( cursor ) {
1586
-
1606
+ _doUndo( cursor )
1607
+ {
1587
1608
  if( !this.code.undoSteps.length )
1609
+ {
1588
1610
  return;
1611
+ }
1589
1612
 
1590
1613
  this._addRedoStep( cursor );
1591
1614
 
@@ -1612,8 +1635,8 @@ class CodeEditor {
1612
1635
  this._hideActiveLine();
1613
1636
  }
1614
1637
 
1615
- _addRedoStep( cursor ) {
1616
-
1638
+ _addRedoStep( cursor )
1639
+ {
1617
1640
  // Only the mainc cursor stores redo steps
1618
1641
  if( !cursor.isMain )
1619
1642
  {
@@ -1626,10 +1649,12 @@ class CodeEditor {
1626
1649
  } );
1627
1650
  }
1628
1651
 
1629
- _doRedo( cursor ) {
1630
-
1652
+ _doRedo( cursor )
1653
+ {
1631
1654
  if( !this.code.redoSteps.length )
1655
+ {
1632
1656
  return;
1657
+ }
1633
1658
 
1634
1659
  this._addUndoStep( cursor, true, false);
1635
1660
 
@@ -1656,8 +1681,8 @@ class CodeEditor {
1656
1681
  }
1657
1682
  }
1658
1683
 
1659
- _changeLanguage( lang, langExtension, override = false ) {
1660
-
1684
+ _changeLanguage( lang, langExtension, override = false )
1685
+ {
1661
1686
  this.code.language = lang;
1662
1687
  this.highlight = lang;
1663
1688
 
@@ -1702,8 +1727,8 @@ class CodeEditor {
1702
1727
  }
1703
1728
  }
1704
1729
 
1705
- _changeLanguageFromExtension( ext ) {
1706
-
1730
+ _changeLanguageFromExtension( ext )
1731
+ {
1707
1732
  if( !ext )
1708
1733
  {
1709
1734
  return this._changeLanguage( this.code.language );
@@ -1733,8 +1758,8 @@ class CodeEditor {
1733
1758
  this._changeLanguage( 'Plain Text' );
1734
1759
  }
1735
1760
 
1736
- _createStatusPanel( options ) {
1737
-
1761
+ _createStatusPanel( options )
1762
+ {
1738
1763
  if( this.skipInfo )
1739
1764
  {
1740
1765
  return;
@@ -1833,8 +1858,8 @@ class CodeEditor {
1833
1858
  return panel;
1834
1859
  }
1835
1860
 
1836
- _getFileIcon( name, extension, lang ) {
1837
-
1861
+ _getFileIcon( name, extension, lang )
1862
+ {
1838
1863
  const isNewTabButton = name ? ( name === '+' ) : false;
1839
1864
  if( isNewTabButton )
1840
1865
  {
@@ -1888,8 +1913,8 @@ class CodeEditor {
1888
1913
  return "AlignLeft gray";
1889
1914
  }
1890
1915
 
1891
- _onNewTab( e ) {
1892
-
1916
+ _onNewTab( e )
1917
+ {
1893
1918
  this.processFocus( false );
1894
1919
 
1895
1920
  if( this.onNewTab )
@@ -1906,8 +1931,8 @@ class CodeEditor {
1906
1931
  new LX.DropdownMenu( e.target, dmOptions, { side: "bottom", align: "start" });
1907
1932
  }
1908
1933
 
1909
- _onCreateNewFile() {
1910
-
1934
+ _onCreateNewFile()
1935
+ {
1911
1936
  let options = {};
1912
1937
 
1913
1938
  if( this.onCreateFile )
@@ -1923,8 +1948,8 @@ class CodeEditor {
1923
1948
  this.addTab( name, true, name, { indexOffset: options.indexOffset, language: options.language ?? "JavaScript" } );
1924
1949
  }
1925
1950
 
1926
- _onSelectTab( isNewTabButton, event, name ) {
1927
-
1951
+ _onSelectTab( isNewTabButton, event, name )
1952
+ {
1928
1953
  if( this.disableEdition )
1929
1954
  {
1930
1955
  return;
@@ -1973,8 +1998,8 @@ class CodeEditor {
1973
1998
  }
1974
1999
  }
1975
2000
 
1976
- _onContextMenuTab( isNewTabButton, event, name, ) {
1977
-
2001
+ _onContextMenuTab( isNewTabButton, event, name, )
2002
+ {
1978
2003
  if( isNewTabButton )
1979
2004
  {
1980
2005
  return;
@@ -2003,8 +2028,8 @@ class CodeEditor {
2003
2028
  ], { side: "bottom", align: "start", event });
2004
2029
  }
2005
2030
 
2006
- addTab( name, selected, title, options = {} ) {
2007
-
2031
+ addTab( name, selected, title, options = {} )
2032
+ {
2008
2033
  // If already loaded, set new name...
2009
2034
  const repeats = Object.keys( editor.loadedTabs ).slice( 1 ).reduce( ( v, key ) => {
2010
2035
  const noRepeatName = key.replace( /[_\d+]/g, '');
@@ -2120,8 +2145,8 @@ class CodeEditor {
2120
2145
  return name;
2121
2146
  }
2122
2147
 
2123
- loadCode( name ) {
2124
-
2148
+ loadCode( name )
2149
+ {
2125
2150
  // Hide all others
2126
2151
  this.codeSizer.querySelectorAll( ".code" ).forEach( c => c.classList.add( "hidden" ) );
2127
2152
 
@@ -2179,8 +2204,8 @@ class CodeEditor {
2179
2204
  this._updateDataInfoPanel( "@tab-name", code.tabName );
2180
2205
  }
2181
2206
 
2182
- loadTab( name ) {
2183
-
2207
+ loadTab( name )
2208
+ {
2184
2209
  // Already open...
2185
2210
  if( this.openedTabs[ name ] )
2186
2211
  {
@@ -2245,8 +2270,8 @@ class CodeEditor {
2245
2270
  this._updateDataInfoPanel( "@tab-name", code.tabName );
2246
2271
  }
2247
2272
 
2248
- closeTab( name, eraseAll ) {
2249
-
2273
+ closeTab( name, eraseAll )
2274
+ {
2250
2275
  if( !this.allowClosingTabs )
2251
2276
  {
2252
2277
  return;
@@ -2262,12 +2287,13 @@ class CodeEditor {
2262
2287
  }
2263
2288
  }
2264
2289
 
2265
- getSelectedTabName() {
2290
+ getSelectedTabName()
2291
+ {
2266
2292
  return this.tabs.selected;
2267
2293
  }
2268
2294
 
2269
- loadTabFromFile() {
2270
-
2295
+ loadTabFromFile()
2296
+ {
2271
2297
  const input = document.createElement( 'input' );
2272
2298
  input.type = 'file';
2273
2299
  document.body.appendChild( input );
@@ -2281,8 +2307,8 @@ class CodeEditor {
2281
2307
  });
2282
2308
  }
2283
2309
 
2284
- processFocus( active ) {
2285
-
2310
+ processFocus( active )
2311
+ {
2286
2312
  if( active )
2287
2313
  {
2288
2314
  this.restartBlink();
@@ -2294,8 +2320,8 @@ class CodeEditor {
2294
2320
  }
2295
2321
  }
2296
2322
 
2297
- processMouse( e ) {
2298
-
2323
+ processMouse( e )
2324
+ {
2299
2325
  if( !e.target.classList.contains('code') && !e.target.classList.contains('lexcodearea') ) return;
2300
2326
  if( !this.code ) return;
2301
2327
 
@@ -2435,8 +2461,8 @@ class CodeEditor {
2435
2461
  }
2436
2462
  }
2437
2463
 
2438
- _onMouseUp( e ) {
2439
-
2464
+ _onMouseUp( e )
2465
+ {
2440
2466
  if( ( LX.getTime() - this.lastMouseDown ) < 120 )
2441
2467
  {
2442
2468
  this.state.selectingText = false;
@@ -2453,8 +2479,8 @@ class CodeEditor {
2453
2479
  delete this._lastSelectionKeyDir;
2454
2480
  }
2455
2481
 
2456
- processClick( e ) {
2457
-
2482
+ processClick( e )
2483
+ {
2458
2484
  var cursor = this.getCurrentCursor();
2459
2485
  var code_rect = this.codeScroller.getBoundingClientRect();
2460
2486
  var position = [( e.clientX - code_rect.x ) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
@@ -2485,8 +2511,8 @@ class CodeEditor {
2485
2511
  this.hideAutoCompleteBox();
2486
2512
  }
2487
2513
 
2488
- updateSelections( e, keepRange, flags = CodeEditor.SELECTION_X_Y ) {
2489
-
2514
+ updateSelections( e, keepRange, flags = CodeEditor.SELECTION_X_Y )
2515
+ {
2490
2516
  for( let cursor of this.cursors.children )
2491
2517
  {
2492
2518
  if( !cursor.selection )
@@ -2498,16 +2524,16 @@ class CodeEditor {
2498
2524
  }
2499
2525
  }
2500
2526
 
2501
- processSelections( e, keepRange, flags = CodeEditor.SELECTION_X_Y ) {
2502
-
2527
+ processSelections( e, keepRange, flags = CodeEditor.SELECTION_X_Y )
2528
+ {
2503
2529
  for( let cursor of this.cursors.children )
2504
2530
  {
2505
2531
  this._processSelection( cursor, e, keepRange, flags );
2506
2532
  }
2507
2533
  }
2508
2534
 
2509
- _processSelection( cursor, e, keepRange, flags = CodeEditor.SELECTION_X_Y ) {
2510
-
2535
+ _processSelection( cursor, e, keepRange, flags = CodeEditor.SELECTION_X_Y )
2536
+ {
2511
2537
  const isMouseEvent = e && ( e.constructor == MouseEvent );
2512
2538
 
2513
2539
  if( isMouseEvent )
@@ -2668,8 +2694,8 @@ class CodeEditor {
2668
2694
  }
2669
2695
  }
2670
2696
 
2671
- async processKey( e ) {
2672
-
2697
+ async processKey( e )
2698
+ {
2673
2699
  const numCursors = this.cursors.childElementCount;
2674
2700
 
2675
2701
  if( !this.code || e.srcElement.constructor != HTMLDivElement )
@@ -2737,8 +2763,8 @@ class CodeEditor {
2737
2763
  delete this._lastProcessedCursorIndex;
2738
2764
  }
2739
2765
 
2740
- async processKeyAtTargetCursor( e, key, targetIdx ) {
2741
-
2766
+ async processKeyAtTargetCursor( e, key, targetIdx )
2767
+ {
2742
2768
  let cursor = this.cursors.children[ targetIdx ];
2743
2769
 
2744
2770
  // We could delete secondary cursor while iterating..
@@ -2749,13 +2775,14 @@ class CodeEditor {
2749
2775
  this._processGlobalKeys( e, key );
2750
2776
  }
2751
2777
 
2752
- _processGlobalKeys( e, key ) {
2753
-
2778
+ _processGlobalKeys( e, key )
2779
+ {
2754
2780
  let cursor = this.getCurrentCursor();
2755
2781
 
2756
2782
  if( e.ctrlKey || e.metaKey )
2757
2783
  {
2758
- switch( key.toLowerCase() ) {
2784
+ switch( key.toLowerCase() )
2785
+ {
2759
2786
  case 'a': // select all
2760
2787
  e.preventDefault();
2761
2788
  this.selectAll();
@@ -2831,8 +2858,8 @@ class CodeEditor {
2831
2858
  return false;
2832
2859
  }
2833
2860
 
2834
- async _processKeyAtCursor( e, key, cursor ) {
2835
-
2861
+ async _processKeyAtCursor( e, key, cursor )
2862
+ {
2836
2863
  const skipUndo = e.detail.skipUndo ?? false;
2837
2864
 
2838
2865
  // keys with length > 1 are probably special keys
@@ -2994,8 +3021,8 @@ class CodeEditor {
2994
3021
  }
2995
3022
  }
2996
3023
 
2997
- async _pasteContent( cursor ) {
2998
-
3024
+ async _pasteContent( cursor )
3025
+ {
2999
3026
  const mustDetectLanguage = ( !this.getText().length );
3000
3027
 
3001
3028
  let text = await navigator.clipboard.readText();
@@ -3025,8 +3052,8 @@ class CodeEditor {
3025
3052
  }
3026
3053
  }
3027
3054
 
3028
- async _copyContent( cursor ) {
3029
-
3055
+ async _copyContent( cursor )
3056
+ {
3030
3057
  let textToCopy = "";
3031
3058
 
3032
3059
  if( !cursor.selection )
@@ -3060,8 +3087,8 @@ class CodeEditor {
3060
3087
  // .then(() => console.log("Successfully copied"), (err) => console.error("Error"));
3061
3088
  }
3062
3089
 
3063
- async _cutContent( cursor ) {
3064
-
3090
+ async _cutContent( cursor )
3091
+ {
3065
3092
  let lidx = cursor.line;
3066
3093
  let textToCut = "";
3067
3094
 
@@ -3107,8 +3134,8 @@ class CodeEditor {
3107
3134
  // .then(() => console.log("Successfully cut"), (err) => console.error("Error"));
3108
3135
  }
3109
3136
 
3110
- _duplicateLine( lidx, cursor ) {
3111
-
3137
+ _duplicateLine( lidx, cursor )
3138
+ {
3112
3139
  this.endSelection();
3113
3140
  this._addUndoStep( cursor, true );
3114
3141
  this.code.lines.splice( lidx, 0, this.code.lines[ lidx ] );
@@ -3117,8 +3144,8 @@ class CodeEditor {
3117
3144
  this.hideAutoCompleteBox();
3118
3145
  }
3119
3146
 
3120
- _commentLines( cursor, useCommentBlock ) {
3121
-
3147
+ _commentLines( cursor, useCommentBlock )
3148
+ {
3122
3149
  const lang = CodeEditor.languages[ this.highlight ];
3123
3150
 
3124
3151
  this.state.keyChain = null;
@@ -3195,8 +3222,8 @@ class CodeEditor {
3195
3222
  this._hideActiveLine();
3196
3223
  }
3197
3224
 
3198
- _commentLine( cursor, line, minNonspaceIdx, updateCursor = true ) {
3199
-
3225
+ _commentLine( cursor, line, minNonspaceIdx, updateCursor = true )
3226
+ {
3200
3227
  const lang = CodeEditor.languages[ this.highlight ];
3201
3228
  if( !( lang.singleLineComments ?? true ) )
3202
3229
  return;
@@ -3225,8 +3252,8 @@ class CodeEditor {
3225
3252
  }
3226
3253
  }
3227
3254
 
3228
- _uncommentLines( cursor ) {
3229
-
3255
+ _uncommentLines( cursor )
3256
+ {
3230
3257
  this.state.keyChain = null;
3231
3258
 
3232
3259
  if( cursor.selection )
@@ -3252,8 +3279,8 @@ class CodeEditor {
3252
3279
  this._hideActiveLine();
3253
3280
  }
3254
3281
 
3255
- _uncommentLine( cursor, line ) {
3256
-
3282
+ _uncommentLine( cursor, line )
3283
+ {
3257
3284
  const lang = CodeEditor.languages[ this.highlight ];
3258
3285
 
3259
3286
  if( !( lang.singleLineComments ?? true ))
@@ -3274,8 +3301,8 @@ class CodeEditor {
3274
3301
  }
3275
3302
  }
3276
3303
 
3277
- action( key, deleteSelection, fn, eventSkipDelete ) {
3278
-
3304
+ action( key, deleteSelection, fn, eventSkipDelete )
3305
+ {
3279
3306
  this.actions[ key ] = {
3280
3307
  "key": key,
3281
3308
  "callback": fn,
@@ -3284,13 +3311,14 @@ class CodeEditor {
3284
3311
  };
3285
3312
  }
3286
3313
 
3287
- _actionMustDelete( cursor, action, e ) {
3314
+ _actionMustDelete( cursor, action, e )
3315
+ {
3288
3316
  return cursor.selection && action.deleteSelection &&
3289
3317
  ( action.eventSkipDelete ? !e[ action.eventSkipDelete ] : true );
3290
3318
  }
3291
3319
 
3292
- scanWordSuggestions() {
3293
-
3320
+ scanWordSuggestions()
3321
+ {
3294
3322
  this.code.tokens = {};
3295
3323
 
3296
3324
  for( let i = 0; i < this.code.lines.length; ++i )
@@ -3301,16 +3329,19 @@ class CodeEditor {
3301
3329
  }
3302
3330
  }
3303
3331
 
3304
- toLocalLine( line ) {
3332
+ toLocalLine( line )
3333
+ {
3305
3334
  const d = Math.max( this.firstLineInViewport - this.lineScrollMargin.x, 0 );
3306
3335
  return Math.min( Math.max( line - d, 0 ), this.code.lines.length - 1 );
3307
3336
  }
3308
3337
 
3309
- getMaxLineLength() {
3338
+ getMaxLineLength()
3339
+ {
3310
3340
  return Math.max(...this.code.lines.map( v => v.length ));
3311
3341
  }
3312
3342
 
3313
- _processLinesIfNecessary() {
3343
+ _processLinesIfNecessary()
3344
+ {
3314
3345
  if( this.mustProcessLines )
3315
3346
  {
3316
3347
  this.mustProcessLines = false;
@@ -3318,8 +3349,8 @@ class CodeEditor {
3318
3349
  }
3319
3350
  }
3320
3351
 
3321
- processLines( mode ) {
3322
-
3352
+ processLines( mode )
3353
+ {
3323
3354
  if( !this.code )
3324
3355
  {
3325
3356
  return;
@@ -3373,8 +3404,8 @@ class CodeEditor {
3373
3404
  this.resize();
3374
3405
  }
3375
3406
 
3376
- processLine( lineNumber, force, skipPropagation ) {
3377
-
3407
+ processLine( lineNumber, force, skipPropagation )
3408
+ {
3378
3409
  if( this._scopeStack )
3379
3410
  {
3380
3411
  this.code.lineScopes[ lineNumber ] = [ ...this._scopeStack ];
@@ -3596,14 +3627,15 @@ class CodeEditor {
3596
3627
  return this._updateLine( force, lineNumber, lineInnerHtml, skipPropagation, symbols, tokensToEvaluate );
3597
3628
  }
3598
3629
 
3599
- _getLineSignatureFromTokens( tokens ) {
3630
+ _getLineSignatureFromTokens( tokens )
3631
+ {
3600
3632
  const structuralChars = new Set( [ '{', '}'] );
3601
3633
  const sign = tokens.filter( t => structuralChars.has( t ) );
3602
3634
  return sign.join( "_" );
3603
3635
  }
3604
3636
 
3605
- _updateBlockComments( section, lineNumber, tokens ) {
3606
-
3637
+ _updateBlockComments( section, lineNumber, tokens )
3638
+ {
3607
3639
  const lang = CodeEditor.languages[ this.highlight ];
3608
3640
  const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
3609
3641
  const lineOpensBlock = ( section[ 0 ].x === lineNumber );
@@ -3682,8 +3714,8 @@ class CodeEditor {
3682
3714
  }
3683
3715
  }
3684
3716
 
3685
- _processExtraLineIfNecessary( lineNumber, tokens, oldSymbols, skipPropagation ) {
3686
-
3717
+ _processExtraLineIfNecessary( lineNumber, tokens, oldSymbols, skipPropagation )
3718
+ {
3687
3719
  if( !this._scopeStack )
3688
3720
  {
3689
3721
  console.warn( "CodeEditor: No scope available" );
@@ -3750,8 +3782,8 @@ class CodeEditor {
3750
3782
  }
3751
3783
  }
3752
3784
 
3753
- _updateLine( force, lineNumber, html, skipPropagation, symbols = [], tokens = [] ) {
3754
-
3785
+ _updateLine( force, lineNumber, html, skipPropagation, symbols = [], tokens = [] )
3786
+ {
3755
3787
  const gutterLineHtml = `<span class='line-gutter'>${ lineNumber + 1 }</span>`;
3756
3788
  const oldSymbols = this._updateLineSymbols( lineNumber, symbols );
3757
3789
  const lineScope = CodeEditor.debugScopes && this.code.lineScopes[ lineNumber ] ? this.code.lineScopes[ lineNumber ].map( s => `${ s.type }` ).join( ", " ) : "";
@@ -3809,8 +3841,8 @@ class CodeEditor {
3809
3841
  /**
3810
3842
  * Parses a single line of code and extract declared symbols
3811
3843
  */
3812
- _parseLineForSymbols( lineNumber, lineString, tokens, pushedScope ) {
3813
-
3844
+ _parseLineForSymbols( lineNumber, lineString, tokens, pushedScope )
3845
+ {
3814
3846
  const scope = this._scopeStack.at( pushedScope ? -2 : -1 );
3815
3847
 
3816
3848
  if( !scope || this._inBlockCommentSection( lineNumber ) )
@@ -3946,8 +3978,8 @@ class CodeEditor {
3946
3978
  * - Removes old symbols from that line
3947
3979
  * - Inserts the new symbols
3948
3980
  */
3949
- _updateLineSymbols( lineNumber, newSymbols ) {
3950
-
3981
+ _updateLineSymbols( lineNumber, newSymbols )
3982
+ {
3951
3983
  this.code.lineSymbols[ lineNumber ] = this.code.lineSymbols[ lineNumber ] ?? [];
3952
3984
  const oldSymbols = LX.deepCopy( this.code.lineSymbols[ lineNumber ] );
3953
3985
 
@@ -3986,8 +4018,8 @@ class CodeEditor {
3986
4018
  return oldSymbols;
3987
4019
  }
3988
4020
 
3989
- _lineHasComment( lineString ) {
3990
-
4021
+ _lineHasComment( lineString )
4022
+ {
3991
4023
  const lang = CodeEditor.languages[ this.highlight ];
3992
4024
 
3993
4025
  if( !(lang.singleLineComments ?? true) )
@@ -4012,8 +4044,8 @@ class CodeEditor {
4012
4044
  }
4013
4045
  }
4014
4046
 
4015
- _getTokensFromLine( lineString, skipNonWords ) {
4016
-
4047
+ _getTokensFromLine( lineString, skipNonWords )
4048
+ {
4017
4049
  if( !lineString || !lineString.length )
4018
4050
  {
4019
4051
  return [];
@@ -4072,8 +4104,8 @@ class CodeEditor {
4072
4104
  return this._processTokens( tokensToEvaluate );
4073
4105
  }
4074
4106
 
4075
- _processTokens( tokens, offset = 0 ) {
4076
-
4107
+ _processTokens( tokens, offset = 0 )
4108
+ {
4077
4109
  if( this.highlight == 'C++' || this.highlight == 'CSS' )
4078
4110
  {
4079
4111
  var idx = tokens.slice( offset ).findIndex( ( value, index ) => this._isNumber( value ) );
@@ -4142,8 +4174,8 @@ class CodeEditor {
4142
4174
  return tokens;
4143
4175
  }
4144
4176
 
4145
- _mustHightlightWord( token, wordCategory, lang ) {
4146
-
4177
+ _mustHightlightWord( token, wordCategory, lang )
4178
+ {
4147
4179
  if( !lang )
4148
4180
  {
4149
4181
  lang = CodeEditor.languages[ this.highlight ];
@@ -4159,8 +4191,8 @@ class CodeEditor {
4159
4191
  return wordCategory[ this.highlight ] && wordCategory[ this.highlight ].has( t );
4160
4192
  }
4161
4193
 
4162
- _getTokenHighlighting( ctx, highlight ) {
4163
-
4194
+ _getTokenHighlighting( ctx, highlight )
4195
+ {
4164
4196
  const rules = [ ...HighlightRules.common, ...( HighlightRules[ highlight ] || [] ), ...HighlightRules.post_common ];
4165
4197
 
4166
4198
  for( const rule of rules )
@@ -4178,8 +4210,8 @@ class CodeEditor {
4178
4210
  return null;
4179
4211
  }
4180
4212
 
4181
- _evaluateToken( ctxData ) {
4182
-
4213
+ _evaluateToken( ctxData )
4214
+ {
4183
4215
  let { token, prev, next, tokenIndex, isFirstToken, isLastToken } = ctxData;
4184
4216
 
4185
4217
  const lang = CodeEditor.languages[ this.highlight ];
@@ -4276,8 +4308,8 @@ class CodeEditor {
4276
4308
  return `<span class="${ highlight } ${ tokenClass }">${ token }</span>`;
4277
4309
  }
4278
4310
 
4279
- _appendStringToken( token ) {
4280
-
4311
+ _appendStringToken( token )
4312
+ {
4281
4313
  if( !this._pendingString )
4282
4314
  {
4283
4315
  this._pendingString = "";
@@ -4288,14 +4320,15 @@ class CodeEditor {
4288
4320
  return true;
4289
4321
  }
4290
4322
 
4291
- _getCurrentString() {
4323
+ _getCurrentString()
4324
+ {
4292
4325
  const chars = this._pendingString;
4293
4326
  delete this._pendingString;
4294
4327
  return chars;
4295
4328
  }
4296
4329
 
4297
- _enclosedByTokens( token, tokenIndex, tagStart, tagEnd ) {
4298
-
4330
+ _enclosedByTokens( token, tokenIndex, tagStart, tagEnd )
4331
+ {
4299
4332
  const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];
4300
4333
  const tagStartIndex = indexOfFrom( this._currentLineString, tagStart, tokenStartIndex, true );
4301
4334
  if( tagStartIndex < 0 ) // Not found..
@@ -4313,8 +4346,8 @@ class CodeEditor {
4313
4346
  }
4314
4347
  }
4315
4348
 
4316
- _inBlockCommentSection( lineNumber, tokenPosition, tokenLength ) {
4317
-
4349
+ _inBlockCommentSection( lineNumber, tokenPosition, tokenLength )
4350
+ {
4318
4351
  const lang = CodeEditor.languages[ this.highlight ];
4319
4352
  const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
4320
4353
 
@@ -4341,8 +4374,8 @@ class CodeEditor {
4341
4374
  }
4342
4375
  }
4343
4376
 
4344
- _isKeyword( ctxData ) {
4345
-
4377
+ _isKeyword( ctxData )
4378
+ {
4346
4379
  const { token, tokenIndex, tokens, lang } = ctxData;
4347
4380
 
4348
4381
  let isKwd = this._mustHightlightWord( token, CodeEditor.keywords ) || this.highlight == 'XML';
@@ -4373,8 +4406,8 @@ class CodeEditor {
4373
4406
  return isKwd;
4374
4407
  }
4375
4408
 
4376
- _isNumber( token ) {
4377
-
4409
+ _isNumber( token )
4410
+ {
4378
4411
  const lang = CodeEditor.languages[ this.highlight ];
4379
4412
  if( !( lang.numbers ?? true ) )
4380
4413
  {
@@ -4412,8 +4445,8 @@ class CodeEditor {
4412
4445
  return token.length && token != ' ' && !Number.isNaN( +token );
4413
4446
  }
4414
4447
 
4415
- _encloseSelectedWordWithKey( key, lidx, cursor ) {
4416
-
4448
+ _encloseSelectedWordWithKey( key, lidx, cursor )
4449
+ {
4417
4450
  if( !cursor.selection || ( cursor.selection.fromY != cursor.selection.toY ) )
4418
4451
  {
4419
4452
  return false;
@@ -4460,8 +4493,8 @@ class CodeEditor {
4460
4493
  return true;
4461
4494
  }
4462
4495
 
4463
- _detectLanguage( text ) {
4464
-
4496
+ _detectLanguage( text )
4497
+ {
4465
4498
  const tokenSet = new Set( this._getTokensFromLine( text, true ) );
4466
4499
  const scores = {};
4467
4500
 
@@ -4500,8 +4533,8 @@ class CodeEditor {
4500
4533
  return sorted[0][1] > 0 ? sorted[0][0] : undefined;
4501
4534
  }
4502
4535
 
4503
- lineUp( cursor, resetLeft ) {
4504
-
4536
+ lineUp( cursor, resetLeft )
4537
+ {
4505
4538
  if( this.code.lines[ cursor.line - 1 ] == undefined )
4506
4539
  return false;
4507
4540
 
@@ -4512,8 +4545,8 @@ class CodeEditor {
4512
4545
  return true;
4513
4546
  }
4514
4547
 
4515
- lineDown( cursor, resetLeft ) {
4516
-
4548
+ lineDown( cursor, resetLeft )
4549
+ {
4517
4550
  if( this.code.lines[ cursor.line + 1 ] == undefined )
4518
4551
  return false;
4519
4552
 
@@ -4524,8 +4557,8 @@ class CodeEditor {
4524
4557
  return true;
4525
4558
  }
4526
4559
 
4527
- restartBlink() {
4528
-
4560
+ restartBlink()
4561
+ {
4529
4562
  if( !this.code ) return;
4530
4563
 
4531
4564
  clearInterval( this.blinker );
@@ -4539,8 +4572,8 @@ class CodeEditor {
4539
4572
  this.cursors.classList.remove( 'show' );
4540
4573
  }
4541
4574
 
4542
- startSelection( cursor ) {
4543
-
4575
+ startSelection( cursor )
4576
+ {
4544
4577
  // Show elements
4545
4578
  let selectionContainer = document.createElement( 'div' );
4546
4579
  selectionContainer.className = 'selections';
@@ -4553,8 +4586,8 @@ class CodeEditor {
4553
4586
  cursor.selection = new CodeSelection( this, cursor );
4554
4587
  }
4555
4588
 
4556
- deleteSelection( cursor ) {
4557
-
4589
+ deleteSelection( cursor )
4590
+ {
4558
4591
  // I think it's not necessary but...
4559
4592
  if( this.disableEdition )
4560
4593
  {
@@ -4586,8 +4619,8 @@ class CodeEditor {
4586
4619
  this.processLines();
4587
4620
  }
4588
4621
 
4589
- endSelection( cursor ) {
4590
-
4622
+ endSelection( cursor )
4623
+ {
4591
4624
  delete this._tripleClickSelection;
4592
4625
  delete this._lastSelectionKeyDir;
4593
4626
  delete this._currentOcurrences;
@@ -4610,7 +4643,9 @@ class CodeEditor {
4610
4643
  }
4611
4644
  }
4612
4645
 
4613
- selectAll() {
4646
+ selectAll()
4647
+ {
4648
+ this.endSelection();
4614
4649
 
4615
4650
  // Use main cursor
4616
4651
  this._removeSecondaryCursors();
@@ -4632,8 +4667,8 @@ class CodeEditor {
4632
4667
  this.hideAutoCompleteBox();
4633
4668
  }
4634
4669
 
4635
- cursorToRight( key, cursor ) {
4636
-
4670
+ cursorToRight( key, cursor )
4671
+ {
4637
4672
  if( !key ) return;
4638
4673
 
4639
4674
  cursor._left += this.charWidth;
@@ -4651,8 +4686,8 @@ class CodeEditor {
4651
4686
  }
4652
4687
  }
4653
4688
 
4654
- cursorToLeft( key, cursor ) {
4655
-
4689
+ cursorToLeft( key, cursor )
4690
+ {
4656
4691
  if( !key ) return;
4657
4692
 
4658
4693
  cursor._left -= this.charWidth;
@@ -4672,8 +4707,8 @@ class CodeEditor {
4672
4707
  }
4673
4708
  }
4674
4709
 
4675
- cursorToTop( cursor, resetLeft = false ) {
4676
-
4710
+ cursorToTop( cursor, resetLeft = false )
4711
+ {
4677
4712
  cursor._top -= this.lineHeight;
4678
4713
  cursor._top = Math.max( cursor._top, 0 );
4679
4714
  cursor.style.top = `calc(${ cursor._top }px)`;
@@ -4692,8 +4727,8 @@ class CodeEditor {
4692
4727
  }
4693
4728
  }
4694
4729
 
4695
- cursorToBottom( cursor, resetLeft = false ) {
4696
-
4730
+ cursorToBottom( cursor, resetLeft = false )
4731
+ {
4697
4732
  cursor._top += this.lineHeight;
4698
4733
  cursor.style.top = `calc(${ cursor._top }px)`;
4699
4734
 
@@ -4713,8 +4748,8 @@ class CodeEditor {
4713
4748
  }
4714
4749
  }
4715
4750
 
4716
- cursorToString( cursor, text, reverse ) {
4717
-
4751
+ cursorToString( cursor, text, reverse )
4752
+ {
4718
4753
  if( !text.length )
4719
4754
  {
4720
4755
  return;
@@ -4726,23 +4761,23 @@ class CodeEditor {
4726
4761
  }
4727
4762
  }
4728
4763
 
4729
- cursorToPosition( cursor, position ) {
4730
-
4764
+ cursorToPosition( cursor, position )
4765
+ {
4731
4766
  cursor.position = position;
4732
4767
  cursor._left = position * this.charWidth;
4733
4768
  cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
4734
4769
  }
4735
4770
 
4736
- cursorToLine( cursor, line, resetLeft = false ) {
4737
-
4771
+ cursorToLine( cursor, line, resetLeft = false )
4772
+ {
4738
4773
  cursor.line = line;
4739
4774
  cursor._top = this.lineHeight * line;
4740
4775
  cursor.style.top = cursor._top + "px";
4741
4776
  if( resetLeft ) this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
4742
4777
  }
4743
4778
 
4744
- saveCursor( cursor, state = {} ) {
4745
-
4779
+ saveCursor( cursor, state = {} )
4780
+ {
4746
4781
  state.position = cursor.position;
4747
4782
  state.line = cursor.line;
4748
4783
  state.selection = cursor.selection ? cursor.selection.save() : undefined;
@@ -4750,8 +4785,8 @@ class CodeEditor {
4750
4785
  return state;
4751
4786
  }
4752
4787
 
4753
- saveCursors() {
4754
-
4788
+ saveCursors()
4789
+ {
4755
4790
  var cursors = [];
4756
4791
 
4757
4792
  for( let cursor of this.cursors.children )
@@ -4762,8 +4797,8 @@ class CodeEditor {
4762
4797
  return cursors;
4763
4798
  }
4764
4799
 
4765
- getCurrentCursor( removeOthers ) {
4766
-
4800
+ getCurrentCursor( removeOthers )
4801
+ {
4767
4802
  if( removeOthers )
4768
4803
  {
4769
4804
  this._removeSecondaryCursors();
@@ -4772,8 +4807,8 @@ class CodeEditor {
4772
4807
  return this.cursors.children[ 0 ];
4773
4808
  }
4774
4809
 
4775
- relocateCursors() {
4776
-
4810
+ relocateCursors()
4811
+ {
4777
4812
  for( let cursor of this.cursors.children )
4778
4813
  {
4779
4814
  cursor._left = cursor.position * this.charWidth;
@@ -4783,8 +4818,8 @@ class CodeEditor {
4783
4818
  }
4784
4819
  }
4785
4820
 
4786
- mergeCursors( line ) {
4787
-
4821
+ mergeCursors( line )
4822
+ {
4788
4823
  console.assert( line >= 0 );
4789
4824
  const cursorsInLine = Array.from( this.cursors.children ).filter( v => v.line == line );
4790
4825
 
@@ -4792,8 +4827,8 @@ class CodeEditor {
4792
4827
  this.removeCursor( cursorsInLine.pop() );
4793
4828
  }
4794
4829
 
4795
- restoreCursor( cursor, state ) {
4796
-
4830
+ restoreCursor( cursor, state )
4831
+ {
4797
4832
  cursor.position = state.position ?? 0;
4798
4833
  cursor.line = state.line ?? 0;
4799
4834
 
@@ -4814,15 +4849,15 @@ class CodeEditor {
4814
4849
  }
4815
4850
  }
4816
4851
 
4817
- removeCursor( cursor ) {
4818
-
4852
+ removeCursor( cursor )
4853
+ {
4819
4854
  LX.deleteElement( this.selections[ cursor.name ] );
4820
4855
  delete this.selections[ cursor.name ];
4821
4856
  LX.deleteElement( cursor );
4822
4857
  }
4823
4858
 
4824
- resetCursorPos( flag, cursor ) {
4825
-
4859
+ resetCursorPos( flag, cursor )
4860
+ {
4826
4861
  cursor = cursor ?? this.getCurrentCursor();
4827
4862
 
4828
4863
  if( flag & CodeEditor.CURSOR_LEFT )
@@ -4840,8 +4875,8 @@ class CodeEditor {
4840
4875
  }
4841
4876
  }
4842
4877
 
4843
- _addCursor( line = 0, position = 0, force, isMain = false ) {
4844
-
4878
+ _addCursor( line = 0, position = 0, force, isMain = false )
4879
+ {
4845
4880
  // If cursor in that position exists, remove it instead..
4846
4881
  const exists = Array.from( this.cursors.children ).find( v => v.position == position && v.line == line );
4847
4882
  if( exists && !force )
@@ -4891,30 +4926,42 @@ class CodeEditor {
4891
4926
  return cursor;
4892
4927
  }
4893
4928
 
4894
- _removeSecondaryCursors() {
4895
-
4929
+ _removeSecondaryCursors()
4930
+ {
4896
4931
  while( this.cursors.childElementCount > 1 )
4932
+ {
4897
4933
  this.cursors.lastChild.remove();
4934
+ }
4935
+ }
4936
+
4937
+ _getLastCursor()
4938
+ {
4939
+ return this.cursors.lastChild;
4898
4940
  }
4899
4941
 
4900
- _logCursors() {
4942
+ _isLastCursor( cursor )
4943
+ {
4944
+ return cursor === this._getLastCursor();
4945
+ }
4901
4946
 
4947
+ _logCursors()
4948
+ {
4902
4949
  for( let cursor of this.cursors.children )
4903
4950
  {
4904
4951
  cursor.print();
4905
4952
  }
4906
4953
  }
4907
4954
 
4908
- _addSpaceTabs( cursor, n ) {
4909
-
4955
+ _addSpaceTabs( cursor, n )
4956
+ {
4910
4957
  for( var i = 0; i < n; ++i )
4911
4958
  {
4912
4959
  this.actions[ 'Tab' ].callback( cursor.line, cursor, null );
4913
4960
  }
4914
4961
  }
4915
4962
 
4916
- _addSpaces( n ) {
4917
-
4963
+ _addSpaces( n )
4964
+ {
4918
4965
  for( var i = 0; i < n; ++i )
4919
4966
  {
4920
4967
  this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
@@ -4925,8 +4972,8 @@ class CodeEditor {
4925
4972
  }
4926
4973
  }
4927
4974
 
4928
- _removeSpaces( cursor ) {
4929
-
4975
+ _removeSpaces( cursor )
4976
+ {
4930
4977
  const lidx = cursor.line;
4931
4978
 
4932
4979
  // Remove indentation
@@ -4965,17 +5012,20 @@ class CodeEditor {
4965
5012
  }
4966
5013
  }
4967
5014
 
4968
- getScrollLeft() {
5015
+ getScrollLeft()
5016
+ {
4969
5017
  if( !this.codeScroller ) return 0;
4970
5018
  return this.codeScroller.scrollLeft;
4971
5019
  }
4972
5020
 
4973
- getScrollTop() {
5021
+ getScrollTop()
5022
+ {
4974
5023
  if( !this.codeScroller ) return 0;
4975
5024
  return this.codeScroller.scrollTop;
4976
5025
  }
4977
5026
 
4978
- setScrollLeft( value ) {
5027
+ setScrollLeft( value )
5028
+ {
4979
5029
  if( !this.codeScroller ) return;
4980
5030
  LX.doAsync( () => {
4981
5031
  this.codeScroller.scrollLeft = value;
@@ -4983,7 +5033,8 @@ class CodeEditor {
4983
5033
  }, 20 );
4984
5034
  }
4985
5035
 
4986
- setScrollTop( value ) {
5036
+ setScrollTop( value )
5037
+ {
4987
5038
  if( !this.codeScroller ) return;
4988
5039
  LX.doAsync( () => {
4989
5040
  this.codeScroller.scrollTop = value;
@@ -4991,8 +5042,8 @@ class CodeEditor {
4991
5042
  }, 20 );
4992
5043
  }
4993
5044
 
4994
- resize( flag = CodeEditor.RESIZE_SCROLLBAR_H_V, pMaxLength, onResize ) {
4995
-
5045
+ resize( flag = CodeEditor.RESIZE_SCROLLBAR_H_V, pMaxLength, onResize )
5046
+ {
4996
5047
  setTimeout( () => {
4997
5048
 
4998
5049
  let scrollWidth, scrollHeight;
@@ -5022,8 +5073,8 @@ class CodeEditor {
5022
5073
  }, 10 );
5023
5074
  }
5024
5075
 
5025
- resizeIfNecessary( cursor, force ) {
5026
-
5076
+ resizeIfNecessary( cursor, force )
5077
+ {
5027
5078
  const maxLineLength = this.getMaxLineLength();
5028
5079
  const numViewportChars = Math.floor( ( this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH ) / this.charWidth );
5029
5080
  if( force || ( maxLineLength >= numViewportChars && maxLineLength != this._lastMaxLineLength ) )
@@ -5037,8 +5088,8 @@ class CodeEditor {
5037
5088
  }
5038
5089
  }
5039
5090
 
5040
- resizeScrollBars( flag = CodeEditor.RESIZE_SCROLLBAR_H_V ) {
5041
-
5091
+ resizeScrollBars( flag = CodeEditor.RESIZE_SCROLLBAR_H_V )
5092
+ {
5042
5093
  if( flag & CodeEditor.RESIZE_SCROLLBAR_V )
5043
5094
  {
5044
5095
  const totalLinesInViewport = (( this.codeScroller.offsetHeight ) / this.lineHeight)|0;
@@ -5071,8 +5122,8 @@ class CodeEditor {
5071
5122
  }
5072
5123
  }
5073
5124
 
5074
- setScrollBarValue( type = 'vertical', value ) {
5075
-
5125
+ setScrollBarValue( type = 'vertical', value )
5126
+ {
5076
5127
  if( type == 'vertical' )
5077
5128
  {
5078
5129
  const scrollBarHeight = this.vScrollbar.thumb.parentElement.offsetHeight;
@@ -5102,8 +5153,8 @@ class CodeEditor {
5102
5153
  }
5103
5154
  }
5104
5155
 
5105
- updateHorizontalScrollFromScrollBar( value ) {
5106
-
5156
+ updateHorizontalScrollFromScrollBar( value )
5157
+ {
5107
5158
  value = this.hScrollbar.thumb._left - value;
5108
5159
 
5109
5160
  // Move scrollbar thumb
@@ -5124,8 +5175,8 @@ class CodeEditor {
5124
5175
  this._discardScroll = true;
5125
5176
  }
5126
5177
 
5127
- updateVerticalScrollFromScrollBar( value ) {
5128
-
5178
+ updateVerticalScrollFromScrollBar( value )
5179
+ {
5129
5180
  value = this.vScrollbar.thumb._top - value;
5130
5181
 
5131
5182
  // Move scrollbar thumb
@@ -5143,12 +5194,13 @@ class CodeEditor {
5143
5194
  this.codeScroller.scrollTop = currentScroll;
5144
5195
  }
5145
5196
 
5146
- getCharAtPos( cursor, offset = 0 ) {
5197
+ getCharAtPos( cursor, offset = 0 )
5198
+ {
5147
5199
  return this.code.lines[ cursor.line ][ cursor.position + offset ];
5148
5200
  }
5149
5201
 
5150
- getWordAtPos( cursor, offset = 0 ) {
5151
-
5202
+ getWordAtPos( cursor, offset = 0 )
5203
+ {
5152
5204
  const col = cursor.line;
5153
5205
  const words = this.code.lines[ col ];
5154
5206
 
@@ -5197,7 +5249,8 @@ class CodeEditor {
5197
5249
  return [ word, from, to ];
5198
5250
  }
5199
5251
 
5200
- _measureChar( char = "a", useFloating = false, getBB = false ) {
5252
+ _measureChar( char = "a", useFloating = false, getBB = false )
5253
+ {
5201
5254
  const parentContainer = LX.makeContainer( null, "lexcodeeditor", "", document.body );
5202
5255
  const container = LX.makeContainer( null, "code", "", parentContainer );
5203
5256
  const line = document.createElement( "pre" );
@@ -5211,12 +5264,13 @@ class CodeEditor {
5211
5264
  return getBB ? bb : bb[ 0 ];
5212
5265
  }
5213
5266
 
5214
- measureString( str ) {
5267
+ measureString( str )
5268
+ {
5215
5269
  return str.length * this.charWidth;
5216
5270
  }
5217
5271
 
5218
- runScript( code ) {
5219
-
5272
+ runScript( code )
5273
+ {
5220
5274
  const script = document.createElement( 'script' );
5221
5275
  script.type = 'module';
5222
5276
  script.innerHTML = code;
@@ -5226,8 +5280,8 @@ class CodeEditor {
5226
5280
  document.getElementsByTagName( 'head' )[ 0 ].appendChild( script );
5227
5281
  }
5228
5282
 
5229
- toJSONFormat( text ) {
5230
-
5283
+ toJSONFormat( text )
5284
+ {
5231
5285
  let params = text.split( ':' );
5232
5286
 
5233
5287
  for(let i = 0; i < params.length; i++) {
@@ -5261,8 +5315,8 @@ class CodeEditor {
5261
5315
  }
5262
5316
  }
5263
5317
 
5264
- showAutoCompleteBox( key, cursor ) {
5265
-
5318
+ showAutoCompleteBox( key, cursor )
5319
+ {
5266
5320
  if( !cursor.isMain )
5267
5321
  {
5268
5322
  return;
@@ -5355,7 +5409,6 @@ class CodeEditor {
5355
5409
  }
5356
5410
 
5357
5411
  pre.appendChild( LX.makeIcon( iconName, { iconClass: "mr-1", svgClass: "sm " + iconClass } ) );
5358
- s
5359
5412
  pre.addEventListener( 'click', () => {
5360
5413
  this.autoCompleteWord( s );
5361
5414
  } );
@@ -5395,8 +5448,8 @@ s
5395
5448
  this.isAutoCompleteActive = true;
5396
5449
  }
5397
5450
 
5398
- hideAutoCompleteBox() {
5399
-
5451
+ hideAutoCompleteBox()
5452
+ {
5400
5453
  if( !this.autocomplete )
5401
5454
  {
5402
5455
  return;
@@ -5410,8 +5463,8 @@ s
5410
5463
  return isActive != this.isAutoCompleteActive;
5411
5464
  }
5412
5465
 
5413
- autoCompleteWord( suggestion ) {
5414
-
5466
+ autoCompleteWord( suggestion )
5467
+ {
5415
5468
  if( !this.isAutoCompleteActive )
5416
5469
  return;
5417
5470
 
@@ -5437,8 +5490,8 @@ s
5437
5490
  this.hideAutoCompleteBox();
5438
5491
  }
5439
5492
 
5440
- _getSelectedAutoComplete() {
5441
-
5493
+ _getSelectedAutoComplete()
5494
+ {
5442
5495
  if( !this.isAutoCompleteActive )
5443
5496
  {
5444
5497
  return;
@@ -5464,18 +5517,21 @@ s
5464
5517
  }
5465
5518
  }
5466
5519
 
5467
- _moveArrowSelectedAutoComplete( dir ) {
5468
-
5520
+ _moveArrowSelectedAutoComplete( dir )
5521
+ {
5469
5522
  if( !this.isAutoCompleteActive )
5470
- return;
5523
+ return;
5471
5524
 
5472
- const [word, idx] = this._getSelectedAutoComplete();
5525
+ const [ word, idx ] = this._getSelectedAutoComplete();
5473
5526
  const offset = dir == 'down' ? 1 : -1;
5474
5527
 
5475
- if( dir == 'down' ) {
5476
- if( (idx + offset) >= this.autocomplete.childElementCount ) return;
5477
- } else if( dir == 'up') {
5478
- if( (idx + offset) < 0 ) return;
5528
+ if( dir == 'down' )
5529
+ {
5530
+ if( ( idx + offset ) >= this.autocomplete.childElementCount ) return;
5531
+ }
5532
+ else if( dir == 'up')
5533
+ {
5534
+ if( ( idx + offset ) < 0 ) return;
5479
5535
  }
5480
5536
 
5481
5537
  this.autocomplete.scrollTop += offset * 20;
@@ -5485,8 +5541,8 @@ s
5485
5541
  this.autocomplete.childNodes[ idx + offset ].classList.add('selected');
5486
5542
  }
5487
5543
 
5488
- showSearchBox( clear ) {
5489
-
5544
+ showSearchBox( clear )
5545
+ {
5490
5546
  this.hideSearchLineBox();
5491
5547
 
5492
5548
  this.searchbox.classList.add( 'opened' );
@@ -5514,8 +5570,8 @@ s
5514
5570
  input.focus();
5515
5571
  }
5516
5572
 
5517
- hideSearchBox() {
5518
-
5573
+ hideSearchBox()
5574
+ {
5519
5575
  const active = this.searchboxActive;
5520
5576
 
5521
5577
  if( this.searchboxActive )
@@ -5532,11 +5588,11 @@ s
5532
5588
 
5533
5589
  this.searchResultSelections.classList.remove( 'show' );
5534
5590
 
5535
- return active != this.searchboxActive;
5591
+ return ( active != this.searchboxActive );
5536
5592
  }
5537
5593
 
5538
- search( text, reverse, callback, skipAlert ) {
5539
-
5594
+ search( text, reverse, callback, skipAlert, forceFocus = true )
5595
+ {
5540
5596
  text = text ?? this._lastTextFound;
5541
5597
 
5542
5598
  if( !text )
@@ -5658,12 +5714,15 @@ s
5658
5714
  };
5659
5715
 
5660
5716
  // Force focus back to search box
5661
- const input = this.searchbox.querySelector( 'input' );
5662
- input.focus();
5717
+ if( forceFocus )
5718
+ {
5719
+ const input = this.searchbox.querySelector( 'input' );
5720
+ input.focus();
5721
+ }
5663
5722
  }
5664
5723
 
5665
- showSearchLineBox() {
5666
-
5724
+ showSearchLineBox()
5725
+ {
5667
5726
  this.hideSearchBox();
5668
5727
 
5669
5728
  this.searchlinebox.classList.add( 'opened' );
@@ -5674,8 +5733,8 @@ s
5674
5733
  input.focus();
5675
5734
  }
5676
5735
 
5677
- hideSearchLineBox() {
5678
-
5736
+ hideSearchLineBox()
5737
+ {
5679
5738
  if( this.searchlineboxActive )
5680
5739
  {
5681
5740
  this.searchlinebox.classList.remove( 'opened' );
@@ -5683,8 +5742,8 @@ s
5683
5742
  }
5684
5743
  }
5685
5744
 
5686
- goToLine( line ) {
5687
-
5745
+ goToLine( line )
5746
+ {
5688
5747
  if( !this._isNumber( line ) )
5689
5748
  return;
5690
5749
 
@@ -5695,8 +5754,8 @@ s
5695
5754
  this.cursorToLine( cursor, line - 1, true );
5696
5755
  }
5697
5756
 
5698
- selectNextOcurrence( cursor ) {
5699
-
5757
+ selectNextOcurrence( cursor )
5758
+ {
5700
5759
  if( !cursor.selection )
5701
5760
  return;
5702
5761
 
@@ -5711,11 +5770,12 @@ s
5711
5770
  this._currentOcurrences[ currentKey ] = true;
5712
5771
  }
5713
5772
 
5714
- this.search( text, false, (col, ln) => {
5773
+ this.search( text, false, ( col, ln ) => {
5715
5774
 
5716
5775
  const key = [ col, ln ].join( '_' );
5717
5776
 
5718
- if( this._currentOcurrences[ key ] ) {
5777
+ if( this._currentOcurrences[ key ] )
5778
+ {
5719
5779
  return;
5720
5780
  }
5721
5781
 
@@ -5726,11 +5786,11 @@ s
5726
5786
 
5727
5787
  this._currentOcurrences[ key ] = true;
5728
5788
 
5729
- }, true );
5789
+ }, true, false );
5730
5790
  }
5731
5791
 
5732
- _updateDataInfoPanel( signal, value ) {
5733
-
5792
+ _updateDataInfoPanel( signal, value )
5793
+ {
5734
5794
  if( !this.skipInfo )
5735
5795
  {
5736
5796
  if( this.cursors.childElementCount > 1 )
@@ -5742,8 +5802,8 @@ s
5742
5802
  }
5743
5803
  }
5744
5804
 
5745
- _setActiveLine( number ) {
5746
-
5805
+ _setActiveLine( number )
5806
+ {
5747
5807
  number = number ?? this.state.activeLine;
5748
5808
 
5749
5809
  const cursor = this.getCurrentCursor();
@@ -5769,11 +5829,13 @@ s
5769
5829
  }
5770
5830
  }
5771
5831
 
5772
- _hideActiveLine() {
5832
+ _hideActiveLine()
5833
+ {
5773
5834
  this.code.querySelectorAll( '.active-line' ).forEach( e => e.classList.remove( 'active-line' ) );
5774
5835
  }
5775
5836
 
5776
- _setFontSize( size ) {
5837
+ _setFontSize( size )
5838
+ {
5777
5839
  // Change font size
5778
5840
  this.fontSize = size;
5779
5841
  const r = document.querySelector( ':root' );
@@ -5797,20 +5859,24 @@ s
5797
5859
  LX.emit( "@font-size", this.fontSize );
5798
5860
  }
5799
5861
 
5800
- _applyFontSizeOffset( offset = 0 ) {
5862
+ _applyFontSizeOffset( offset = 0 )
5863
+ {
5801
5864
  const newFontSize = LX.clamp( this.fontSize + offset, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE );
5802
5865
  this._setFontSize( newFontSize );
5803
5866
  }
5804
5867
 
5805
- _increaseFontSize() {
5868
+ _increaseFontSize()
5869
+ {
5806
5870
  this._applyFontSizeOffset( 1 );
5807
5871
  }
5808
5872
 
5809
- _decreaseFontSize() {
5873
+ _decreaseFontSize()
5874
+ {
5810
5875
  this._applyFontSizeOffset( -1 );
5811
5876
  }
5812
5877
 
5813
- _clearTmpVariables() {
5878
+ _clearTmpVariables()
5879
+ {
5814
5880
  delete this._currentLineString;
5815
5881
  delete this._currentLineNumber;
5816
5882
  delete this._buildingString;
@@ -5821,7 +5887,8 @@ s
5821
5887
  delete this._scopeStack;
5822
5888
  }
5823
5889
 
5824
- async _requestFileAsync( url, dataType, nocache ) {
5890
+ async _requestFileAsync( url, dataType, nocache )
5891
+ {
5825
5892
  return new Promise( (resolve, reject) => {
5826
5893
  dataType = dataType ?? "arraybuffer";
5827
5894
  const mimeType = dataType === "arraybuffer" ? "application/octet-stream" : undefined;
@@ -5852,7 +5919,8 @@ s
5852
5919
  }
5853
5920
  }
5854
5921
 
5855
- CodeEditor.languages = {
5922
+ CodeEditor.languages =
5923
+ {
5856
5924
  'Plain Text': { ext: "txt", blockComments: false, singleLineComments: false, numbers: false, icon: "AlignLeft gray" },
5857
5925
  'JavaScript': { ext: "js", icon: "Js goldenrod" },
5858
5926
  'TypeScript': { ext: "ts", icon: "Ts pipelineblue" },
@@ -5872,17 +5940,20 @@ CodeEditor.languages = {
5872
5940
  'PHP': { ext: "php", icon: "Php blueviolet" },
5873
5941
  };
5874
5942
 
5875
- CodeEditor.nativeTypes = {
5943
+ CodeEditor.nativeTypes =
5944
+ {
5876
5945
  'C++': ['int', 'float', 'double', 'bool', 'long', 'short', 'char', 'wchar_t', 'void'],
5877
5946
  'WGSL': ['bool', 'u32', 'i32', 'f16', 'f32', 'vec2', 'vec3', 'vec4', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'vec2u', 'vec3u', 'vec4u', 'ptr', 'sampler']
5878
5947
  };
5879
5948
 
5880
- CodeEditor.declarationKeywords = {
5949
+ CodeEditor.declarationKeywords =
5950
+ {
5881
5951
  'JavaScript': ['var', 'let', 'const', 'this', 'static', 'class'],
5882
5952
  'C++': [...CodeEditor.nativeTypes["C++"], 'const', 'auto', 'class', 'struct', 'namespace', 'enum', 'extern']
5883
5953
  };
5884
5954
 
5885
- CodeEditor.keywords = {
5955
+ CodeEditor.keywords =
5956
+ {
5886
5957
  'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
5887
5958
  'arguments', 'extends', 'instanceof', 'Infinity'],
5888
5959
  'TypeScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'class', 'extends', 'instanceof', 'Infinity', 'private', 'public', 'protected', 'interface',
@@ -5911,7 +5982,9 @@ CodeEditor.keywords = {
5911
5982
  'enum'],
5912
5983
  };
5913
5984
 
5914
- CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
5985
+ // These ones don't have hightlight, used as suggestions to autocomplete only...
5986
+ CodeEditor.utils =
5987
+ {
5915
5988
  'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
5916
5989
  'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement', 'prompt',
5917
5990
  'alert'],
@@ -5929,7 +6002,8 @@ CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to
5929
6002
  'lime', 'teal', 'navy', 'transparent', 'currentcolor', 'inherit', 'initial', 'unset', 'revert', 'none', 'auto', 'fit-content', 'min-content', 'max-content']
5930
6003
  };
5931
6004
 
5932
- CodeEditor.types = {
6005
+ CodeEditor.types =
6006
+ {
5933
6007
  'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent',
5934
6008
  'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element'],
5935
6009
  'TypeScript': ['arguments', 'constructor', 'null', 'typeof', 'debugger', 'abstract', 'Object', 'string', 'String', 'Function', 'Boolean', 'boolean', 'Error', 'Number', 'number', 'TextEncoder',
@@ -5943,7 +6017,8 @@ CodeEditor.types = {
5943
6017
  'PHP': ['Exception', 'DateTime', 'JsonSerializable'],
5944
6018
  };
5945
6019
 
5946
- CodeEditor.builtIn = {
6020
+ CodeEditor.builtIn =
6021
+ {
5947
6022
  'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
5948
6023
  'CSS': ['*', '!important'],
5949
6024
  'C++': ['vector', 'list', 'map'],
@@ -5953,7 +6028,8 @@ CodeEditor.builtIn = {
5953
6028
  'PHP': ['echo', 'print'],
5954
6029
  };
5955
6030
 
5956
- CodeEditor.statements = {
6031
+ CodeEditor.statements =
6032
+ {
5957
6033
  'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
5958
6034
  'TypeScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
5959
6035
  'CSS': ['@', 'import'],
@@ -5969,7 +6045,8 @@ CodeEditor.statements = {
5969
6045
  'try', 'catch', 'die', 'do', 'exit', 'finally'],
5970
6046
  };
5971
6047
 
5972
- CodeEditor.symbols = {
6048
+ CodeEditor.symbols =
6049
+ {
5973
6050
  'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
5974
6051
  'TypeScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
5975
6052
  'C': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '*', '-', '+'],