lexgui 0.1.26 → 0.1.28

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.
@@ -313,7 +313,7 @@ class CodeEditor {
313
313
  }
314
314
 
315
315
  this.base_area = area;
316
- this.area = new LX.Area( { className: "lexcodeeditor", height: "auto", no_append: true } );
316
+ this.area = new LX.Area( { className: "lexcodeeditor", height: "100%", no_append: true } );
317
317
 
318
318
  this.tabs = this.area.addTabs( { onclose: (name) => {
319
319
  delete this.openedTabs[ name ];
@@ -377,17 +377,7 @@ class CodeEditor {
377
377
  this.xPadding = "48px";
378
378
 
379
379
  // Add main cursor
380
- {
381
- this._addCursor( 0, 0, true, true );
382
-
383
- Object.defineProperty( this, 'line', {
384
- get: (v) => { return this._getCurrentCursor().line }
385
- } );
386
-
387
- Object.defineProperty( this, 'position', {
388
- get: (v) => { return this._getCurrentCursor().position }
389
- } );
390
- }
380
+ this._addCursor( 0, 0, true, true );
391
381
 
392
382
  // Scroll stuff
393
383
  {
@@ -600,12 +590,17 @@ class CodeEditor {
600
590
 
601
591
  // Convert reserved word arrays to maps so we can search tokens faster
602
592
 
603
- for( let lang in CodeEditor.keywords ) CodeEditor.keywords[lang] = CodeEditor.keywords[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
604
- for( let lang in CodeEditor.utils ) CodeEditor.utils[lang] = CodeEditor.utils[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
605
- for( let lang in CodeEditor.types ) CodeEditor.types[lang] = CodeEditor.types[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
606
- for( let lang in CodeEditor.builtin ) CodeEditor.builtin[lang] = CodeEditor.builtin[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
607
- for( let lang in CodeEditor.statementsAndDeclarations ) CodeEditor.statementsAndDeclarations[lang] = CodeEditor.statementsAndDeclarations[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
608
- for( let lang in CodeEditor.symbols ) CodeEditor.symbols[lang] = CodeEditor.symbols[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
593
+ if( !CodeEditor._staticReady )
594
+ {
595
+ for( let lang in CodeEditor.keywords ) CodeEditor.keywords[lang] = CodeEditor.keywords[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
596
+ for( let lang in CodeEditor.utils ) CodeEditor.utils[lang] = CodeEditor.utils[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
597
+ for( let lang in CodeEditor.types ) CodeEditor.types[lang] = CodeEditor.types[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
598
+ for( let lang in CodeEditor.builtin ) CodeEditor.builtin[lang] = CodeEditor.builtin[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
599
+ for( let lang in CodeEditor.statementsAndDeclarations ) CodeEditor.statementsAndDeclarations[lang] = CodeEditor.statementsAndDeclarations[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
600
+ for( let lang in CodeEditor.symbols ) CodeEditor.symbols[lang] = CodeEditor.symbols[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
601
+
602
+ CodeEditor._staticReady = true;
603
+ }
609
604
 
610
605
  // Action keys
611
606
 
@@ -698,14 +693,31 @@ class CodeEditor {
698
693
 
699
694
  this.action( 'Tab', true, ( ln, cursor, e ) => {
700
695
 
701
- if( this.isAutoCompleteActive )
696
+ if( this._skipTabs )
702
697
  {
703
- this.autoCompleteWord( cursor );
704
- } else
698
+ this._skipTabs--;
699
+ if( !this._skipTabs )
700
+ delete this._skipTabs;
701
+ }
702
+ else if( this.isAutoCompleteActive )
705
703
  {
706
- this.addSpaces( this.tabSpaces );
704
+ this.autoCompleteWord();
707
705
  }
708
- });
706
+ else
707
+ {
708
+ this._addUndoStep( cursor );
709
+
710
+ if( e && e.shiftKey )
711
+ {
712
+ this._removeSpaces( cursor );
713
+ }
714
+ else
715
+ {
716
+ const indentSpaces = this.tabSpaces - (cursor.position % this.tabSpaces);
717
+ this._addSpaces( indentSpaces );
718
+ }
719
+ }
720
+ }, "shiftKey");
709
721
 
710
722
  this.action( 'Home', false, ( ln, cursor, e ) => {
711
723
 
@@ -718,7 +730,8 @@ class CodeEditor {
718
730
  let lastX = cursor.position;
719
731
 
720
732
  this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
721
- if(idx > 0) this.cursorToString( cursor, prestring );
733
+ if(idx > 0)
734
+ this.cursorToString( cursor, prestring );
722
735
  this.setScrollLeft( 0 );
723
736
 
724
737
  // Merge cursors
@@ -777,7 +790,7 @@ class CodeEditor {
777
790
  // Add word
778
791
  if( this.isAutoCompleteActive )
779
792
  {
780
- this.autoCompleteWord( cursor );
793
+ this.autoCompleteWord();
781
794
  return;
782
795
  }
783
796
 
@@ -804,10 +817,10 @@ class CodeEditor {
804
817
 
805
818
  if( _c0 == '{' && _c1 == '}' ) {
806
819
  this.code.lines.splice( cursor.line, 0, "" );
807
- this.addSpaceTabs( tabs + 1 );
820
+ this._addSpaceTabs( cursor, tabs + 1 );
808
821
  this.code.lines[ cursor.line + 1 ] = " ".repeat(spaces) + this.code.lines[ cursor.line + 1 ];
809
822
  } else {
810
- this.addSpaceTabs( tabs );
823
+ this._addSpaceTabs( cursor, tabs );
811
824
  }
812
825
 
813
826
  this.processLines();
@@ -892,19 +905,28 @@ class CodeEditor {
892
905
  const [word, from, to] = this.getWordAtPos( cursor, -1 );
893
906
  // If no length, we change line..
894
907
  if( !word.length && this.lineUp( cursor, true ) ) {
908
+ const cS = e.cancelShift, kS = e.keepSelection;
895
909
  e.cancelShift = true;
896
910
  e.keepSelection = true;
897
911
  this.actions[ 'End' ].callback( cursor.line, cursor, e );
898
- delete e.cancelShift;
899
- delete e.keepSelection;
912
+ e.cancelShift = cS;
913
+ e.keepSelection = kS;
900
914
  }
901
915
  var diff = Math.max( cursor.position - from, 1 );
902
916
  var substr = word.substr( 0, diff );
917
+
903
918
  // Selections...
904
- if( e.shiftKey ) { if( !cursor.selection ) this.startSelection( cursor ); }
905
- else this.endSelection();
919
+ if( e.shiftKey ) {
920
+ if( !cursor.selection )
921
+ this.startSelection( cursor );
922
+ }
923
+ else
924
+ this.endSelection();
925
+
906
926
  this.cursorToString( cursor, substr, true );
907
- if( e.shiftKey ) this._processSelection( cursor, e, false, true );
927
+
928
+ if( e.shiftKey )
929
+ this._processSelection( cursor, e );
908
930
  }
909
931
  else {
910
932
  var letter = this.getCharAtPos( cursor, -1 );
@@ -1462,14 +1484,25 @@ class CodeEditor {
1462
1484
 
1463
1485
  panel.sameLine();
1464
1486
  panel.addLabel( this.code.title, { float: 'right', signal: "@tab-name" });
1465
- panel.addLabel( "Ln " + 1, { width: "64px", signal: "@cursor-line" });
1466
- panel.addLabel( "Col " + 1, { width: "64px", signal: "@cursor-pos" });
1487
+ panel.addLabel( "Ln " + 1, { maxWidth: "48px", signal: "@cursor-line" });
1488
+ panel.addLabel( "Col " + 1, { maxWidth: "48px", signal: "@cursor-pos" });
1489
+ panel.addButton( null, "Spaces: " + this.tabSpaces, ( value, event ) => {
1490
+ LX.addContextMenu( "Spaces", event, m => {
1491
+ const options = [ 2, 4, 8 ];
1492
+ for( const n of options )
1493
+ m.add( n, (v) => {
1494
+ this.tabSpaces = v;
1495
+ this.processLines();
1496
+ this._updateDataInfoPanel( "@tab-spaces", "Spaces: " + this.tabSpaces );
1497
+ } );
1498
+ });
1499
+ }, { width: "10%", nameWidth: "15%", signal: "@tab-spaces" });
1467
1500
  panel.addButton( "<b>{ }</b>", this.highlight, ( value, event ) => {
1468
1501
  LX.addContextMenu( "Language", event, m => {
1469
1502
  for( const lang of Object.keys(this.languages) )
1470
1503
  m.add( lang, this._changeLanguage.bind(this) );
1471
1504
  });
1472
- }, { width: "25%", nameWidth: "15%", signal: "@highlight" });
1505
+ }, { width: "17.5%", nameWidth: "15%", signal: "@highlight" });
1473
1506
  panel.endLine();
1474
1507
 
1475
1508
  return panel;
@@ -1878,8 +1911,8 @@ class CodeEditor {
1878
1911
  let ccw = true;
1879
1912
 
1880
1913
  // Check if we must change ccw or not ... (not with mouse)
1881
- if( !isMouseEvent && this.line >= cursor.selection.fromY &&
1882
- (this.line == cursor.selection.fromY ? this.position >= cursor.selection.fromX : true) )
1914
+ if( !isMouseEvent && cursor.line >= cursor.selection.fromY &&
1915
+ (cursor.line == cursor.selection.fromY ? cursor.position >= cursor.selection.fromX : true) )
1883
1916
  {
1884
1917
  ccw = ( e && this._lastSelectionKeyDir && ( e.key == 'ArrowRight' || e.key == 'ArrowDown' || e.key == 'End' ) );
1885
1918
  }
@@ -2188,12 +2221,13 @@ class CodeEditor {
2188
2221
  this.hideAutoCompleteBox();
2189
2222
  return;
2190
2223
  case 'arrowdown': // add cursor below only for the main cursor..
2191
- if( isLastCursor && this.code.lines[ lidx + 1 ] != undefined )
2224
+ // Make sure shift is not pressed..
2225
+ if( !e.shiftKey && isLastCursor && this.code.lines[ lidx + 1 ] != undefined )
2192
2226
  {
2193
2227
  var new_cursor = this._addCursor( cursor.line, cursor.position, true );
2194
2228
  this.lineDown( new_cursor );
2229
+ return;
2195
2230
  }
2196
- return;
2197
2231
  }
2198
2232
  }
2199
2233
 
@@ -2231,10 +2265,12 @@ class CodeEditor {
2231
2265
 
2232
2266
  for( const actKey in this.actions ) {
2233
2267
 
2234
- if( key != actKey ) continue;
2268
+ if( key != actKey )
2269
+ continue;
2270
+
2235
2271
  e.preventDefault();
2236
2272
 
2237
- if( this.actions[ key ].deleteSelection && cursor.selection )
2273
+ if( this._actionMustDelete( cursor, this.actions[ key ], e ) )
2238
2274
  this.actions['Backspace'].callback( lidx, cursor, e );
2239
2275
 
2240
2276
  return this.actions[ key ].callback( lidx, cursor, e );
@@ -2529,14 +2565,22 @@ class CodeEditor {
2529
2565
  }
2530
2566
  }
2531
2567
 
2532
- action( key, deleteSelection, fn ) {
2568
+ action( key, deleteSelection, fn, eventSkipDelete ) {
2533
2569
 
2534
2570
  this.actions[ key ] = {
2571
+ "key": key,
2535
2572
  "callback": fn,
2536
- "deleteSelection": deleteSelection
2573
+ "deleteSelection": deleteSelection,
2574
+ "eventSkipDelete": eventSkipDelete
2537
2575
  };
2538
2576
  }
2539
2577
 
2578
+ _actionMustDelete( cursor, action, e ) {
2579
+
2580
+ return cursor.selection && action.deleteSelection &&
2581
+ ( action.eventSkipDelete ? !e[ action.eventSkipDelete ] : true );
2582
+ }
2583
+
2540
2584
  scanWordSuggestions() {
2541
2585
 
2542
2586
  this.code.tokens = {};
@@ -2861,111 +2905,98 @@ class CodeEditor {
2861
2905
 
2862
2906
  const usesBlockComments = lang.blockComments ?? true;
2863
2907
  const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
2908
+ const singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
2909
+
2910
+ let token_classname = "";
2911
+ let discardToken = false;
2864
2912
 
2865
- if( !usePreviousTokenToCheckString && token == ' ' )
2866
- {
2867
- if( this._buildingString != undefined )
2868
- {
2869
- this._appendStringToken( token );
2870
- return "";
2871
- }
2872
- return token;
2873
- }
2874
- else
2875
- {
2876
- const singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
2877
-
2878
- let token_classname = "";
2879
- let discardToken = false;
2880
-
2881
- if( this._buildingBlockComment != undefined )
2882
- token_classname = "cm-com";
2883
-
2884
- else if( this._buildingString != undefined )
2885
- discardToken = this._appendStringToken( token );
2886
-
2887
- else if( this._mustHightlightWord( token, CodeEditor.keywords ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2888
- token_classname = "cm-kwd";
2913
+ if( this._buildingBlockComment != undefined )
2914
+ token_classname = "cm-com";
2915
+
2916
+ else if( this._buildingString != undefined )
2917
+ discardToken = this._appendStringToken( token );
2918
+
2919
+ else if( this._mustHightlightWord( token, CodeEditor.keywords ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2920
+ token_classname = "cm-kwd";
2889
2921
 
2890
- else if( this._mustHightlightWord( token, CodeEditor.builtin ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2891
- token_classname = "cm-bln";
2922
+ else if( this._mustHightlightWord( token, CodeEditor.builtin ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2923
+ token_classname = "cm-bln";
2892
2924
 
2893
- else if( this._mustHightlightWord( token, CodeEditor.statementsAndDeclarations ) )
2894
- token_classname = "cm-std";
2925
+ else if( this._mustHightlightWord( token, CodeEditor.statementsAndDeclarations ) )
2926
+ token_classname = "cm-std";
2895
2927
 
2896
- else if( this._mustHightlightWord( token, CodeEditor.symbols ) )
2897
- token_classname = "cm-sym";
2928
+ else if( this._mustHightlightWord( token, CodeEditor.symbols ) )
2929
+ token_classname = "cm-sym";
2898
2930
 
2899
- else if( token.substr( 0, singleLineCommentToken.length ) == singleLineCommentToken )
2900
- token_classname = "cm-com";
2931
+ else if( token.substr( 0, singleLineCommentToken.length ) == singleLineCommentToken )
2932
+ token_classname = "cm-com";
2901
2933
 
2902
- else if( this._isNumber( token ) || this._isNumber( token.replace(/[px]|[em]|%/g,'') ) )
2903
- token_classname = "cm-dec";
2934
+ else if( this._isNumber( token ) || this._isNumber( token.replace(/[px]|[em]|%/g,'') ) )
2935
+ token_classname = "cm-dec";
2904
2936
 
2905
- else if( this._isCSSClass( token, prev, next ) )
2906
- token_classname = "cm-kwd";
2937
+ else if( this._isCSSClass( token, prev, next ) )
2938
+ token_classname = "cm-kwd";
2907
2939
 
2908
- else if ( this._isType( token, prev, next ) )
2909
- token_classname = "cm-typ";
2940
+ else if ( this._isType( token, prev, next ) )
2941
+ token_classname = "cm-typ";
2910
2942
 
2911
- else if ( highlight == 'batch' && ( token == '@' || prev == ':' || prev == '@' ) )
2912
- token_classname = "cm-kwd";
2943
+ else if ( highlight == 'batch' && ( token == '@' || prev == ':' || prev == '@' ) )
2944
+ token_classname = "cm-kwd";
2913
2945
 
2914
- else if ( [ 'cpp', 'wgsl', 'glsl' ].indexOf( highlight ) > -1 && token.includes( '#' ) ) // C++ preprocessor
2915
- token_classname = "cm-ppc";
2946
+ else if ( [ 'cpp', 'wgsl', 'glsl' ].indexOf( highlight ) > -1 && token.includes( '#' ) ) // C++ preprocessor
2947
+ token_classname = "cm-ppc";
2916
2948
 
2917
- else if ( highlight == 'cpp' && prev == '<' && (next == '>' || next == '*') ) // Defining template type in C++
2918
- token_classname = "cm-typ";
2949
+ else if ( highlight == 'cpp' && prev == '<' && (next == '>' || next == '*') ) // Defining template type in C++
2950
+ token_classname = "cm-typ";
2919
2951
 
2920
- else if ( highlight == 'cpp' && (next == '::' || prev == '::' && next != '(' )) // C++ Class
2921
- token_classname = "cm-typ";
2952
+ else if ( highlight == 'cpp' && (next == '::' || prev == '::' && next != '(' )) // C++ Class
2953
+ token_classname = "cm-typ";
2922
2954
 
2923
- else if ( highlight == 'css' && prev == ':' && (next == ';' || next == '!important') ) // CSS value
2924
- token_classname = "cm-str";
2955
+ else if ( highlight == 'css' && prev == ':' && (next == ';' || next == '!important') ) // CSS value
2956
+ token_classname = "cm-str";
2925
2957
 
2926
- else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
2927
- token_classname = "cm-typ";
2928
-
2929
- else if ( this._markdownHeader || ( highlight == 'markdown' && isFirstToken && token.replaceAll('#', '').length != token.length ) ) // Header
2930
- {
2931
- token_classname = "cm-kwd";
2932
- this._markdownHeader = true;
2933
- }
2958
+ else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
2959
+ token_classname = "cm-typ";
2960
+
2961
+ else if ( this._markdownHeader || ( highlight == 'markdown' && isFirstToken && token.replaceAll('#', '').length != token.length ) ) // Header
2962
+ {
2963
+ token_classname = "cm-kwd";
2964
+ this._markdownHeader = true;
2965
+ }
2934
2966
 
2935
- else if ( token[ 0 ] != '@' && token[ 0 ] != ',' && next == '(' )
2936
- token_classname = "cm-mtd";
2967
+ else if ( token[ 0 ] != '@' && token[ 0 ] != ',' && next == '(' )
2968
+ token_classname = "cm-mtd";
2937
2969
 
2938
2970
 
2939
- if( usesBlockComments && this._buildingBlockComment != undefined
2940
- && token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
2941
- {
2942
- this._blockCommentCache.push( new LX.vec2( this._buildingBlockComment, this._currentLineNumber ) );
2943
- delete this._buildingBlockComment;
2944
- }
2971
+ if( usesBlockComments && this._buildingBlockComment != undefined
2972
+ && token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
2973
+ {
2974
+ this._blockCommentCache.push( new LX.vec2( this._buildingBlockComment, this._currentLineNumber ) );
2975
+ delete this._buildingBlockComment;
2976
+ }
2945
2977
 
2946
- // We finished constructing a string
2947
- if( this._buildingString && ( this._stringEnded || isLastToken ) )
2948
- {
2949
- token = this._getCurrentString();
2950
- token_classname = "cm-str";
2951
- discardToken = false;
2952
- }
2978
+ // We finished constructing a string
2979
+ if( this._buildingString && ( this._stringEnded || isLastToken ) )
2980
+ {
2981
+ token = this._getCurrentString();
2982
+ token_classname = "cm-str";
2983
+ discardToken = false;
2984
+ }
2953
2985
 
2954
- // Update state
2955
- this._buildingString = this._stringEnded ? undefined : this._buildingString;
2986
+ // Update state
2987
+ this._buildingString = this._stringEnded ? undefined : this._buildingString;
2956
2988
 
2957
- if( discardToken )
2958
- return "";
2989
+ if( discardToken )
2990
+ return "";
2959
2991
 
2960
- token = token.replace( "<", "&lt;" );
2961
- token = token.replace( ">", "&gt;" );
2992
+ token = token.replace( "<", "&lt;" );
2993
+ token = token.replace( ">", "&gt;" );
2962
2994
 
2963
- // No highlighting, no need to put it inside another span..
2964
- if( !token_classname.length )
2965
- return token;
2995
+ // No highlighting, no need to put it inside another span..
2996
+ if( !token_classname.length )
2997
+ return token;
2966
2998
 
2967
- return "<span class='" + highlight + " " + token_classname + "'>" + token + "</span>";
2968
- }
2999
+ return "<span class='" + highlight + " " + token_classname + "'>" + token + "</span>";
2969
3000
  }
2970
3001
 
2971
3002
  _appendStringToken( token ) {
@@ -3204,6 +3235,7 @@ class CodeEditor {
3204
3235
  delete this._tripleClickSelection;
3205
3236
  delete this._lastSelectionKeyDir;
3206
3237
  delete this._currentOcurrences;
3238
+ delete this._lastResult;
3207
3239
 
3208
3240
  if( cursor )
3209
3241
  {
@@ -3428,14 +3460,14 @@ class CodeEditor {
3428
3460
  }
3429
3461
  }
3430
3462
 
3431
- addSpaceTabs( n ) {
3463
+ _addSpaceTabs( cursor, n ) {
3432
3464
 
3433
3465
  for( var i = 0; i < n; ++i ) {
3434
- this.actions[ 'Tab' ].callback();
3466
+ this.actions[ 'Tab' ].callback( cursor.line, cursor, null );
3435
3467
  }
3436
3468
  }
3437
3469
 
3438
- addSpaces( n ) {
3470
+ _addSpaces( n ) {
3439
3471
 
3440
3472
  for( var i = 0; i < n; ++i ) {
3441
3473
  this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
@@ -3446,6 +3478,42 @@ class CodeEditor {
3446
3478
  }
3447
3479
  }
3448
3480
 
3481
+ _removeSpaces( cursor ) {
3482
+
3483
+ // Remove indentation
3484
+ const lidx = cursor.line;
3485
+ let lineStart = firstNonspaceIndex( this.code.lines[ lidx ] );
3486
+
3487
+ // Nothing to remove... we are at the start of the line
3488
+ if( lineStart == 0 )
3489
+ return;
3490
+
3491
+ // Only tabs/spaces in the line...
3492
+ if( lineStart == -1 ) {
3493
+ lineStart = this.code.lines[ lidx ].length;
3494
+ }
3495
+
3496
+ let indentSpaces = lineStart % this.tabSpaces;
3497
+ indentSpaces = indentSpaces == 0 ? this.tabSpaces : indentSpaces;
3498
+ const newStart = Math.max( lineStart - indentSpaces, 0 );
3499
+
3500
+ this.code.lines[ lidx ] = [
3501
+ this.code.lines[ lidx ].slice( 0, newStart ),
3502
+ this.code.lines[ lidx ].slice( lineStart )
3503
+ ].join('');
3504
+
3505
+ this.processLine( lidx );
3506
+
3507
+ this.cursorToString( cursor, " ".repeat( indentSpaces ), true );
3508
+
3509
+ if( cursor.selection )
3510
+ {
3511
+ cursor.selection.invertIfNecessary();
3512
+ cursor.selection.fromX = Math.max( cursor.selection.fromX - indentSpaces, 0 );
3513
+ this._processSelection( cursor );
3514
+ }
3515
+ }
3516
+
3449
3517
  getScrollLeft() {
3450
3518
 
3451
3519
  if( !this.codeScroller ) return 0;
@@ -3715,8 +3783,11 @@ class CodeEditor {
3715
3783
 
3716
3784
  showAutoCompleteBox( key, cursor ) {
3717
3785
 
3786
+ if( !cursor.isMain )
3787
+ return;
3788
+
3718
3789
  const [word, start, end] = this.getWordAtPos( cursor, -1 );
3719
- if(key == ' ' || !word.length) {
3790
+ if( key == ' ' || !word.length ) {
3720
3791
  this.hideAutoCompleteBox();
3721
3792
  return;
3722
3793
  }
@@ -3763,7 +3834,7 @@ class CodeEditor {
3763
3834
  pre.appendChild( icon );
3764
3835
 
3765
3836
  pre.addEventListener( 'click', () => {
3766
- this.autoCompleteWord( cursor, s );
3837
+ this.autoCompleteWord( s );
3767
3838
  } );
3768
3839
 
3769
3840
  // Highlight the written part
@@ -3795,8 +3866,8 @@ class CodeEditor {
3795
3866
  // Show box
3796
3867
  this.autocomplete.classList.toggle('show', true);
3797
3868
  this.autocomplete.classList.toggle('no-scrollbar', !(this.autocomplete.scrollHeight > this.autocomplete.offsetHeight));
3798
- this.autocomplete.style.left = (cursor._left + 36 - this.getScrollLeft()) + "px";
3799
- this.autocomplete.style.top = (cursor._top + 48 - this.getScrollTop()) + "px";
3869
+ this.autocomplete.style.left = (cursor._left + 48 - this.getScrollLeft()) + "px";
3870
+ this.autocomplete.style.top = (cursor._top + 28 + this.lineHeight - this.getScrollTop()) + "px";
3800
3871
 
3801
3872
  this.isAutoCompleteActive = true;
3802
3873
  }
@@ -3809,23 +3880,30 @@ class CodeEditor {
3809
3880
  return isActive != this.isAutoCompleteActive;
3810
3881
  }
3811
3882
 
3812
- autoCompleteWord( cursor, suggestion ) {
3883
+ autoCompleteWord( suggestion ) {
3813
3884
 
3814
3885
  if( !this.isAutoCompleteActive )
3815
- return;
3886
+ return;
3816
3887
 
3817
3888
  let [suggestedWord, idx] = this._getSelectedAutoComplete();
3818
3889
  suggestedWord = suggestion ?? suggestedWord;
3819
3890
 
3820
- const [word, start, end] = this.getWordAtPos( cursor, -1 );
3891
+ for( let cursor of this.cursors.children )
3892
+ {
3893
+ const [word, start, end] = this.getWordAtPos( cursor, -1 );
3894
+
3895
+ const lineString = this.code.lines[ cursor.line ];
3896
+ this.code.lines[ cursor.line ] =
3897
+ lineString.slice(0, start) + suggestedWord + lineString.slice( end );
3898
+
3899
+ // Process lines and remove suggestion box
3900
+ this.cursorToPosition( cursor, start + suggestedWord.length );
3901
+ this.processLine( cursor.line );
3902
+ }
3821
3903
 
3822
- const lineString = this.code.lines[ cursor.line ];
3823
- this.code.lines[ cursor.line ] =
3824
- lineString.slice(0, start) + suggestedWord + lineString.slice(end);
3904
+ // Only the main cursor autocompletes, skip the "Tab" event for the rest
3905
+ this._skipTabs = this.cursors.childElementCount - 1;
3825
3906
 
3826
- // Process lines and remove suggestion box
3827
- this.cursorToPosition(cursor, start + suggestedWord.length);
3828
- this.processLine(cursor.line);
3829
3907
  this.hideAutoCompleteBox();
3830
3908
  }
3831
3909
 
@@ -4210,7 +4288,7 @@ class CodeEditor {
4210
4288
  CodeEditor.keywords = {
4211
4289
 
4212
4290
  'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
4213
- 'arguments', 'extends', 'instanceof'],
4291
+ 'arguments', 'extends', 'instanceof', 'Infinity'],
4214
4292
  'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
4215
4293
  'NULL', 'unsigned', 'namespace'],
4216
4294
  'JSON': ['true', 'false'],
@@ -4243,7 +4321,8 @@ CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to
4243
4321
 
4244
4322
  CodeEditor.types = {
4245
4323
 
4246
- 'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder'],
4324
+ 'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent',
4325
+ 'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element'],
4247
4326
  'Rust': ['u128'],
4248
4327
  'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
4249
4328
  'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',