lexgui 0.1.21 → 0.1.23

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.
@@ -201,6 +201,9 @@ class CodeEditor {
201
201
  static WORD_TYPE_METHOD = 0;
202
202
  static WORD_TYPE_CLASS = 1;
203
203
 
204
+ static CODE_MAX_FONT_SIZE = 22;
205
+ static CODE_MIN_FONT_SIZE = 9;
206
+
204
207
  /**
205
208
  * @param {*} options
206
209
  * skip_info, allow_add_scripts, name
@@ -330,42 +333,15 @@ class CodeEditor {
330
333
 
331
334
  // Add main cursor
332
335
  {
333
- var cursor = document.createElement( 'div' );
334
- cursor.className = "cursor";
335
- cursor.innerHTML = " ";
336
- cursor._left = 0;
337
- cursor.style.left = this.xPadding;
338
- cursor._top = 0;
339
- cursor.style.top = cursor._top + "px";
340
- cursor._position = 0;
341
- cursor._line = 0;
342
- cursor.print = (function() { console.log( this.line, this.position ) }).bind( cursor );
336
+ this._addCursor( 0, 0, true );
343
337
 
344
338
  Object.defineProperty( this, 'line', {
345
- get: (v) => { return cursor.line }
339
+ get: (v) => { return this._getCurrentCursor().line }
346
340
  } );
347
-
341
+
348
342
  Object.defineProperty( this, 'position', {
349
- get: (v) => { return cursor.position }
350
- } );
351
-
352
- Object.defineProperty( cursor, 'line', {
353
- get: (v) => { return this._line },
354
- set: (v) => {
355
- this._line = v;
356
- this._setActiveLine( v );
357
- }
358
- } );
359
-
360
- Object.defineProperty( cursor, 'position', {
361
- get: (v) => { return this._position },
362
- set: (v) => {
363
- this._position = v;
364
- this._updateDataInfoPanel( "@cursor-pos", "Col " + v );
365
- }
343
+ get: (v) => { return this._getCurrentCursor().position }
366
344
  } );
367
-
368
- this.cursors.appendChild( cursor );
369
345
  }
370
346
 
371
347
  // Scroll stuff
@@ -413,8 +389,16 @@ class CodeEditor {
413
389
  });
414
390
 
415
391
  this.codeScroller.addEventListener( 'wheel', e => {
416
- const dX = ( e.deltaY > 0.0 ? 10.0 : -10.0 ) * ( e.shiftKey ? 1.0 : 0.0 );
417
- if( dX != 0.0 ) this.setScrollBarValue( 'horizontal', dX );
392
+ if( e.ctrlKey )
393
+ {
394
+ e.preventDefault();
395
+ ( e.deltaY > 0.0 ? this._decreaseFontSize() : this._increaseFontSize() );
396
+ }
397
+ else
398
+ {
399
+ const dX = ( e.deltaY > 0.0 ? 10.0 : -10.0 ) * ( e.shiftKey ? 1.0 : 0.0 );
400
+ if( dX != 0.0 ) this.setScrollBarValue( 'horizontal', dX );
401
+ }
418
402
  });
419
403
  }
420
404
 
@@ -470,6 +454,28 @@ class CodeEditor {
470
454
  this.tabs.area.attach( box );
471
455
  }
472
456
 
457
+ // Add search LINE box
458
+ {
459
+ var box = document.createElement( 'div' );
460
+ box.className = "searchbox gotoline";
461
+
462
+ var searchPanel = new LX.Panel();
463
+ box.appendChild( searchPanel.root );
464
+
465
+ searchPanel.addText( null, "", ( value, event ) => {
466
+ input.value = ":" + value.replaceAll( ':', '' );
467
+ this.goToLine( input.value.slice( 1 ) );
468
+ }, { placeholder: "Go to line", trigger: "input" } );
469
+
470
+ let input = box.querySelector( 'input' );
471
+ input.addEventListener( 'keyup', e => {
472
+ if( e.key == 'Escape' ) this.hideSearchLineBox();
473
+ } );
474
+
475
+ this.searchlinebox = box;
476
+ this.tabs.area.attach( box );
477
+ }
478
+
473
479
  // Add code-sizer
474
480
  {
475
481
  this.codeSizer = document.createElement( 'div' );
@@ -501,9 +507,9 @@ class CodeEditor {
501
507
  this.tabSpaces = 4;
502
508
  this.maxUndoSteps = 16;
503
509
  this.lineHeight = 20;
510
+ this.charWidth = 7; // To update later depending on size..
504
511
  this.defaultSingleLineCommentToken = '//';
505
512
  this.defaultBlockCommentTokens = [ '/*', '*/' ];
506
- this.charWidth = 7; //this._measureChar();
507
513
  this._lastTime = null;
508
514
 
509
515
  this.pairKeys = {
@@ -592,6 +598,7 @@ class CodeEditor {
592
598
  };
593
599
  this.statementsAndDeclarations = {
594
600
  'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
601
+ 'CSS': ['@', 'import'],
595
602
  'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog'],
596
603
  'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
597
604
  'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
@@ -627,6 +634,7 @@ class CodeEditor {
627
634
  this.action( 'Escape', false, ( ln, cursor, e ) => {
628
635
  this.hideAutoCompleteBox();
629
636
  this.hideSearchBox();
637
+ this._removeSecondaryCursors();
630
638
  });
631
639
 
632
640
  this.action( 'Backspace', false, ( ln, cursor, e ) => {
@@ -672,7 +680,7 @@ class CodeEditor {
672
680
  }
673
681
  else if( this.code.lines[ ln - 1 ] != undefined ) {
674
682
 
675
- this.lineUp();
683
+ this.lineUp( cursor );
676
684
  e.cancelShift = true;
677
685
  this.actions[ 'End' ].callback( cursor.line, cursor, e );
678
686
  // Move line on top
@@ -727,7 +735,7 @@ class CodeEditor {
727
735
  const prestring = this.code.lines[ ln ].substring( 0, idx );
728
736
  let lastX = cursor.position;
729
737
 
730
- this.resetCursorPos( CodeEditor.CURSOR_LEFT );
738
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
731
739
  if(idx > 0) this.cursorToString( cursor, prestring );
732
740
  this.setScrollLeft( 0 );
733
741
 
@@ -761,14 +769,14 @@ class CodeEditor {
761
769
  this.selection.selectInline(cursor.position, cursor.line, this.measureString( string ));
762
770
  else
763
771
  {
764
- this.resetCursorPos( CodeEditor.CURSOR_LEFT );
772
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
765
773
  this.cursorToString( cursor, this.code.lines[ ln ] );
766
774
  this.processSelection( e );
767
775
  }
768
776
  } else if( !e.keepSelection )
769
777
  this.endSelection();
770
778
 
771
- this.resetCursorPos( CodeEditor.CURSOR_LEFT );
779
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
772
780
  this.cursorToString( cursor, this.code.lines[ ln ] );
773
781
 
774
782
  const last_char = ( this.code.clientWidth / this.charWidth )|0;
@@ -790,7 +798,7 @@ class CodeEditor {
790
798
  return;
791
799
  }
792
800
 
793
- this._addUndoStep( cursor );
801
+ this._addUndoStep( cursor, true );
794
802
 
795
803
  var _c0 = this.getCharAtPos( cursor, -1 );
796
804
  var _c1 = this.getCharAtPos( cursor );
@@ -824,7 +832,7 @@ class CodeEditor {
824
832
  if( !this.selection )
825
833
  this.startSelection( cursor );
826
834
 
827
- this.lineUp();
835
+ this.lineUp( cursor );
828
836
 
829
837
  var letter = this.getCharAtPos( cursor );
830
838
  if( !letter ) {
@@ -835,7 +843,7 @@ class CodeEditor {
835
843
 
836
844
  } else {
837
845
  this.endSelection();
838
- this.lineUp();
846
+ this.lineUp( cursor );
839
847
  // Go to end of line if out of line
840
848
  var letter = this.getCharAtPos( cursor );
841
849
  if( !letter ) this.actions['End'].callback( cursor.line, cursor, e );
@@ -928,7 +936,7 @@ class CodeEditor {
928
936
  }
929
937
  else {
930
938
  this.selection.invertIfNecessary();
931
- this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
939
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
932
940
  this.cursorToLine( cursor, this.selection.fromY, true );
933
941
  this.cursorToPosition( cursor, this.selection.fromX );
934
942
  this.endSelection();
@@ -954,7 +962,7 @@ class CodeEditor {
954
962
 
955
963
  // Nothing to do..
956
964
  if( cursor.line == this.code.lines.length - 1 &&
957
- cursor.position == this.code.lines[ cursor.line - 1 ].length )
965
+ cursor.position == this.code.lines[ cursor.line ].length )
958
966
  return;
959
967
 
960
968
  if( e.metaKey ) { // Apple devices (Command)
@@ -970,7 +978,7 @@ class CodeEditor {
970
978
  // Selections...
971
979
  if( e.shiftKey ) { if( !this.selection ) this.startSelection( cursor ); }
972
980
  else this.endSelection();
973
- this.cursorToString( cursor, substr);
981
+ this.cursorToString( cursor, substr );
974
982
  if( e.shiftKey ) this.processSelection( e );
975
983
  } else {
976
984
  var letter = this.getCharAtPos( cursor );
@@ -988,7 +996,7 @@ class CodeEditor {
988
996
  else
989
997
  {
990
998
  this.selection.invertIfNecessary();
991
- this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
999
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
992
1000
  this.cursorToLine( cursor, this.selection.toY );
993
1001
  this.cursorToPosition( cursor, this.selection.toX );
994
1002
  this.endSelection();
@@ -1018,7 +1026,7 @@ class CodeEditor {
1018
1026
  if( options.allow_add_scripts ?? true )
1019
1027
  this.addTab("+", false, "New File");
1020
1028
 
1021
- this.addTab(options.name || "untitled", true, options.title);
1029
+ this.addTab( options.name || "untitled", true, options.title );
1022
1030
 
1023
1031
  // Create inspector panel
1024
1032
  let panel = this._createPanelInfo();
@@ -1036,14 +1044,15 @@ class CodeEditor {
1036
1044
  );
1037
1045
 
1038
1046
  // Add to the document.fonts (FontFaceSet)
1039
- document.fonts.add(commitMono);
1047
+ document.fonts.add( commitMono );
1040
1048
 
1041
1049
  // Load the font
1042
1050
  commitMono.load();
1043
1051
 
1044
1052
  // Wait until the fonts are all loaded
1045
1053
  document.fonts.ready.then(() => {
1046
- console.log("commitMono loaded")
1054
+ // console.log("commitMono loaded")
1055
+ this.charWidth = this._measureChar( "a", true );
1047
1056
  });
1048
1057
  }
1049
1058
 
@@ -1073,7 +1082,8 @@ class CodeEditor {
1073
1082
  }
1074
1083
 
1075
1084
  getText( min ) {
1076
- return this.code.lines.join(min ? ' ' : '\n');
1085
+
1086
+ return this.code.lines.join( min ? ' ' : '\n' );
1077
1087
  }
1078
1088
 
1079
1089
  // This can be used to empty all text...
@@ -1082,7 +1092,9 @@ class CodeEditor {
1082
1092
  let new_lines = text.split( '\n' );
1083
1093
  this.code.lines = [].concat( new_lines );
1084
1094
 
1085
- let cursor = this.cursors.children[ 0 ];
1095
+ this._removeSecondaryCursors();
1096
+
1097
+ let cursor = this._getCurrentCursor( true );
1086
1098
  let lastLine = new_lines.pop();
1087
1099
 
1088
1100
  this.cursorToLine( cursor, new_lines.length ); // Already substracted 1
@@ -1095,9 +1107,8 @@ class CodeEditor {
1095
1107
  }
1096
1108
  }
1097
1109
 
1098
- appendText( text ) {
1110
+ appendText( text, cursor ) {
1099
1111
 
1100
- let cursor = this.cursors.children[ 0 ];
1101
1112
  let lidx = cursor.line;
1102
1113
 
1103
1114
  if( this.selection ) {
@@ -1201,24 +1212,90 @@ class CodeEditor {
1201
1212
  }
1202
1213
  }
1203
1214
 
1204
- _addUndoStep( cursor ) {
1215
+ _addCursor( line = 0, position = 0, isMain = false ) {
1216
+
1217
+ let cursor = document.createElement( 'div' );
1218
+ cursor.className = "cursor";
1219
+ cursor.innerHTML = " ";
1220
+ cursor.isMainCursor = isMain;
1221
+ cursor._left = position * this.charWidth;
1222
+ cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
1223
+ cursor._top = line * this.lineHeight;
1224
+ cursor.style.top = cursor._top + "px";
1225
+ cursor._position = position;
1226
+ cursor._line = line;
1227
+ cursor.print = (function() { console.log( this, this._line, this._position ) }).bind( cursor );
1228
+
1229
+ Object.defineProperty( cursor, 'line', {
1230
+ get: (v) => { return cursor._line },
1231
+ set: (v) => {
1232
+ cursor._line = v;
1233
+ if( cursor.isMainCursor ) this._setActiveLine( v );
1234
+ }
1235
+ } );
1236
+
1237
+ Object.defineProperty( cursor, 'position', {
1238
+ get: (v) => { return cursor._position },
1239
+ set: (v) => {
1240
+ cursor._position = v;
1241
+ if( cursor.isMainCursor ) this._updateDataInfoPanel( "@cursor-pos", "Col " + v );
1242
+ }
1243
+ } );
1244
+
1245
+ this.cursors.appendChild( cursor );
1246
+
1247
+ return cursor;
1248
+ }
1249
+
1250
+ _getCurrentCursor( removeOthers ) {
1251
+
1252
+ if( removeOthers )
1253
+ {
1254
+ this._removeSecondaryCursors();
1255
+ }
1256
+
1257
+ return this.cursors.children[ 0 ];
1258
+ }
1259
+
1260
+ _removeSecondaryCursors() {
1261
+
1262
+ while( this.cursors.childElementCount > 1 )
1263
+ this.cursors.lastChild.remove();
1264
+ }
1265
+
1266
+ _logCursors() {
1267
+
1268
+ for( let cursor of this.cursors.children )
1269
+ {
1270
+ cursor.print();
1271
+ }
1272
+ }
1273
+
1274
+ _addUndoStep( cursor, force, deleteRedo = true ) {
1205
1275
 
1206
1276
  const d = new Date();
1207
1277
  const current = d.getTime();
1208
1278
 
1209
- if( !this._lastTime ) {
1210
- this._lastTime = current;
1211
- } else {
1212
- if( ( current - this._lastTime ) > 3000 ){
1213
- this._lastTime = null;
1214
- } else {
1215
- // If time not enough, reset timer
1279
+ if( !force )
1280
+ {
1281
+ if( !this._lastTime ) {
1216
1282
  this._lastTime = current;
1217
- return;
1283
+ } else {
1284
+ if( ( current - this._lastTime ) > 3000 ){
1285
+ this._lastTime = null;
1286
+ } else {
1287
+ // If time not enough, reset timer
1288
+ this._lastTime = current;
1289
+ return;
1290
+ }
1218
1291
  }
1219
1292
  }
1220
1293
 
1221
- var cursor = cursor ?? this.cursors.children[ 0 ];
1294
+ if( deleteRedo )
1295
+ {
1296
+ // Remove all redo steps
1297
+ this.code.redoSteps.length = 0;
1298
+ }
1222
1299
 
1223
1300
  this.code.undoSteps.push( {
1224
1301
  lines: LX.deepCopy( this.code.lines ),
@@ -1228,6 +1305,16 @@ class CodeEditor {
1228
1305
  } );
1229
1306
  }
1230
1307
 
1308
+ _addRedoStep( cursor ) {
1309
+
1310
+ this.code.redoSteps.push( {
1311
+ lines: LX.deepCopy( this.code.lines ),
1312
+ cursor: this.saveCursor( cursor ),
1313
+ line: cursor.line,
1314
+ position: cursor.position
1315
+ } );
1316
+ }
1317
+
1231
1318
  _changeLanguage( lang ) {
1232
1319
 
1233
1320
  this.code.language = lang;
@@ -1320,7 +1407,7 @@ class CodeEditor {
1320
1407
  ext == 'css' ? "fa-solid fa-hashtag dodgerblue" :
1321
1408
  ext == 'xml' ? "fa-solid fa-rss orange" :
1322
1409
  ext == 'bat' ? "fa-brands fa-windows lightblue" :
1323
- [ 'js', 'py', 'json', 'cpp', 'rs' ].indexOf( ext ) > -1 ? "images/" + ext + ".png" :
1410
+ [ 'js', 'py', 'json', 'cpp', 'rs', 'md' ].indexOf( ext ) > -1 ? "images/" + ext + ".png" :
1324
1411
  !isNewTabButton ? "fa-solid fa-align-left gray" : undefined;
1325
1412
  }
1326
1413
 
@@ -1334,7 +1421,7 @@ class CodeEditor {
1334
1421
  });
1335
1422
  }
1336
1423
 
1337
- _onSelectTab( isNewTabButton, event, name, ) {
1424
+ _onSelectTab( isNewTabButton, event, name ) {
1338
1425
 
1339
1426
  if( isNewTabButton )
1340
1427
  {
@@ -1342,7 +1429,9 @@ class CodeEditor {
1342
1429
  return;
1343
1430
  }
1344
1431
 
1345
- var cursor = cursor ?? this.cursors.children[ 0 ];
1432
+ this._removeSecondaryCursors();
1433
+ var cursor = this._getCurrentCursor( true );
1434
+
1346
1435
  this.saveCursor( cursor, this.code.cursorState );
1347
1436
 
1348
1437
  this.code = this.loadedTabs[ name ];
@@ -1385,6 +1474,7 @@ class CodeEditor {
1385
1474
  code.language = "Plain Text";
1386
1475
  code.cursorState = {};
1387
1476
  code.undoSteps = [];
1477
+ code.redoSteps = [];
1388
1478
  code.tabName = name;
1389
1479
  code.title = title ?? name;
1390
1480
  code.tokens = {};
@@ -1518,12 +1608,12 @@ class CodeEditor {
1518
1608
  }
1519
1609
  }
1520
1610
 
1521
- processMouse(e) {
1611
+ processMouse( e ) {
1522
1612
 
1523
1613
  if( !e.target.classList.contains('code') ) return;
1524
1614
  if( !this.code ) return;
1525
1615
 
1526
- var cursor = this.cursors.children[ 0 ];
1616
+ var cursor = this._getCurrentCursor();
1527
1617
  var code_rect = this.code.getBoundingClientRect();
1528
1618
  var mouse_pos = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
1529
1619
 
@@ -1555,6 +1645,7 @@ class CodeEditor {
1555
1645
  this.lastMouseDown = LX.getTime();
1556
1646
  this.state.selectingText = true;
1557
1647
  this.endSelection();
1648
+ this.processClick( e );
1558
1649
  }
1559
1650
 
1560
1651
  else if( e.type == 'mouseup' )
@@ -1574,7 +1665,7 @@ class CodeEditor {
1574
1665
  {
1575
1666
  case LX.MOUSE_DOUBLE_CLICK:
1576
1667
  const [word, from, to] = this.getWordAtPos( cursor );
1577
- this.resetCursorPos( CodeEditor.CURSOR_LEFT );
1668
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
1578
1669
  this.cursorToPosition( cursor, from );
1579
1670
  this.startSelection( cursor );
1580
1671
  this.selection.selectInline( from, cursor.line, this.measureString( word ) );
@@ -1582,7 +1673,7 @@ class CodeEditor {
1582
1673
  break;
1583
1674
  // Select entire line
1584
1675
  case LX.MOUSE_TRIPLE_CLICK:
1585
- this.resetCursorPos( CodeEditor.CURSOR_LEFT );
1676
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
1586
1677
  e._shiftKey = true;
1587
1678
  this.actions['End'].callback(cursor.line, cursor, e);
1588
1679
  this._tripleClickSelection = true;
@@ -1598,11 +1689,11 @@ class CodeEditor {
1598
1689
  return;
1599
1690
 
1600
1691
  LX.addContextMenu( null, e, m => {
1601
- m.add( "Copy", () => { this._copyContent(); } );
1692
+ m.add( "Copy", () => { this._copyContent( cursor ); } );
1602
1693
  if( !this.disableEdition )
1603
1694
  {
1604
- m.add( "Cut", () => { this._cutContent(); } );
1605
- m.add( "Paste", () => { this._pasteContent(); } );
1695
+ m.add( "Cut", () => { this._cutContent( cursor ); } );
1696
+ m.add( "Paste", () => { this._pasteContent( cursor ); } );
1606
1697
  m.add( "" );
1607
1698
  m.add( "Format/JSON", () => {
1608
1699
  let json = this.toJSONFormat( this.getText() );
@@ -1618,9 +1709,8 @@ class CodeEditor {
1618
1709
 
1619
1710
  _onMouseUp( e ) {
1620
1711
 
1621
- if( (LX.getTime() - this.lastMouseDown) < 300 ) {
1712
+ if( (LX.getTime() - this.lastMouseDown) < 120 ) {
1622
1713
  this.state.selectingText = false;
1623
- this.processClick( e );
1624
1714
  this.endSelection();
1625
1715
  }
1626
1716
 
@@ -1635,26 +1725,39 @@ class CodeEditor {
1635
1725
 
1636
1726
  processClick( e ) {
1637
1727
 
1638
- var cursor = this.cursors.children[ 0 ];
1728
+ var cursor = this._getCurrentCursor();
1639
1729
  var code_rect = this.codeScroller.getBoundingClientRect();
1640
1730
  var position = [( e.clientX - code_rect.x ) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
1641
1731
  var ln = (position[ 1 ] / this.lineHeight)|0;
1642
1732
 
1643
1733
  if( this.code.lines[ ln ] == undefined )
1644
1734
  return;
1645
-
1646
- this.cursorToLine( cursor, ln, true );
1647
-
1735
+
1648
1736
  var ch = ( ( position[ 0 ] - parseInt( this.xPadding ) + 3) / this.charWidth )|0;
1649
1737
  var string = this.code.lines[ ln ].slice( 0, ch );
1650
- this.cursorToPosition( cursor, string.length );
1738
+
1739
+ // Move main cursor there...
1740
+ if( !e.altKey )
1741
+ {
1742
+ // Make sure we only keep the main cursor..
1743
+ this._removeSecondaryCursors();
1744
+
1745
+ this.cursorToLine( cursor, ln, true );
1746
+ this.cursorToPosition( cursor, string.length );
1747
+ }
1748
+
1749
+ // Add new cursor
1750
+ else
1751
+ {
1752
+ this._addCursor( ln, string.length );
1753
+ }
1651
1754
 
1652
1755
  this.hideAutoCompleteBox();
1653
1756
  }
1654
1757
 
1655
1758
  processSelection( e, keep_range, flags = CodeEditor.SELECTION_X_Y ) {
1656
1759
 
1657
- var cursor = this.cursors.children[ 0 ];
1760
+ var cursor = this._getCurrentCursor();
1658
1761
  const isMouseEvent = e && ( e.constructor == MouseEvent );
1659
1762
 
1660
1763
  if( isMouseEvent ) this.processClick( e );
@@ -1808,7 +1911,23 @@ class CodeEditor {
1808
1911
  }
1809
1912
  }
1810
1913
 
1811
- async processKey( e ) {
1914
+ async processKey( event ) {
1915
+
1916
+ const numCursors = this.cursors.childElementCount;
1917
+
1918
+ for( var i = 0; i < numCursors; i++ )
1919
+ {
1920
+ let cursor = this.cursors.children[ i ];
1921
+
1922
+ // We could delete secondary cursor while iterating..
1923
+ if( !cursor )
1924
+ break;
1925
+
1926
+ this.processKeyAtCursor( event, cursor );
1927
+ }
1928
+ }
1929
+
1930
+ async processKeyAtCursor( e, cursor ) {
1812
1931
 
1813
1932
  if( !this.code || e.srcElement.constructor != HTMLDivElement )
1814
1933
  return;
@@ -1821,7 +1940,6 @@ class CodeEditor {
1821
1940
  if( key.length > 1 && this.specialKeys.indexOf( key ) == -1 )
1822
1941
  return;
1823
1942
 
1824
- let cursor = this.cursors.children[ 0 ];
1825
1943
  let lidx = cursor.line;
1826
1944
  this.code.lines[ lidx ] = this.code.lines[ lidx ] ?? "";
1827
1945
 
@@ -1832,49 +1950,67 @@ class CodeEditor {
1832
1950
  switch( key.toLowerCase() ) {
1833
1951
  case 'a': // select all
1834
1952
  e.preventDefault();
1835
- this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
1836
- this.startSelection( cursor );
1837
- const nlines = this.code.lines.length - 1;
1838
- this.selection.toX = this.code.lines[ nlines ].length;
1839
- this.selection.toY = nlines;
1840
- this.cursorToPosition( cursor, this.selection.toX );
1841
- this.cursorToLine( cursor, this.selection.toY );
1842
- this.processSelection( null, true );
1843
- this.hideAutoCompleteBox();
1953
+ this.selectAll( cursor );
1844
1954
  break;
1845
1955
  case 'c': // copy
1846
- this._copyContent();
1956
+ this._copyContent( cursor );
1847
1957
  return;
1848
1958
  case 'd': // duplicate line
1849
1959
  e.preventDefault();
1850
- this.code.lines.splice( lidx, 0, this.code.lines[ lidx ] );
1851
- this.lineDown( cursor );
1852
- this.processLines();
1853
- this.hideAutoCompleteBox();
1960
+ this._duplicateLine( lidx, cursor );
1854
1961
  return;
1855
1962
  case 'f': // find/search
1856
1963
  e.preventDefault();
1857
1964
  this.showSearchBox();
1858
1965
  return;
1966
+ case 'g': // find line
1967
+ e.preventDefault();
1968
+ this.showSearchLineBox();
1969
+ return;
1859
1970
  case 's': // save
1860
1971
  e.preventDefault();
1861
1972
  this.onsave( this.getText() );
1862
1973
  return;
1863
1974
  case 'v': // paste
1864
- this._pasteContent();
1975
+ this._pasteContent( cursor );
1865
1976
  return;
1866
1977
  case 'x': // cut line
1867
- this._cutContent();
1978
+ this._cutContent( cursor );
1868
1979
  this.hideAutoCompleteBox();
1869
1980
  return;
1981
+ case 'y': // redo
1982
+ if( !this.code.redoSteps.length )
1983
+ return;
1984
+ this._addUndoStep( cursor, true, false);
1985
+ const redo_step = this.code.redoSteps.pop();
1986
+ this.code.lines = redo_step.lines;
1987
+ this.processLines();
1988
+ this.restoreCursor( cursor, redo_step.cursor );
1989
+ return;
1870
1990
  case 'z': // undo
1871
- if(!this.code.undoSteps.length)
1991
+ if( !this.code.undoSteps.length )
1872
1992
  return;
1873
- const step = this.code.undoSteps.pop();
1874
- this.code.lines = step.lines;
1993
+ this._addRedoStep( cursor );
1994
+ const undo_step = this.code.undoSteps.pop();
1995
+ this.code.lines = undo_step.lines;
1875
1996
  this.processLines();
1876
- this.restoreCursor( cursor, step.cursor );
1997
+ this.restoreCursor( cursor, undo_step.cursor );
1998
+ return;
1999
+ case '+': // increase size
2000
+ e.preventDefault();
2001
+ this._increaseFontSize();
1877
2002
  return;
2003
+ case '-': // decrease size
2004
+ e.preventDefault();
2005
+ this._decreaseFontSize();
2006
+ return;
2007
+ case 'arrowdown': // add cursor below only for the main cursor..
2008
+ if( cursor.isMainCursor && this.code.lines[ lidx + 1 ] != undefined )
2009
+ {
2010
+ var new_cursor = this._addCursor( cursor.line, cursor.position );
2011
+ this.lineDown( new_cursor );
2012
+ return;
2013
+ }
1878
2014
  }
1879
2015
  }
1880
2016
 
@@ -1884,8 +2020,9 @@ class CodeEditor {
1884
2020
  case 'ArrowUp':
1885
2021
  if(this.code.lines[ lidx - 1 ] == undefined)
1886
2022
  return;
1887
- swapArrayElements(this.code.lines, lidx - 1, lidx);
1888
- this.lineUp();
2023
+ this._addUndoStep( cursor, true );
2024
+ swapArrayElements( this.code.lines, lidx - 1, lidx );
2025
+ this.lineUp( cursor );
1889
2026
  this.processLine( lidx - 1 );
1890
2027
  this.processLine( lidx );
1891
2028
  this.hideAutoCompleteBox();
@@ -1893,8 +2030,9 @@ class CodeEditor {
1893
2030
  case 'ArrowDown':
1894
2031
  if(this.code.lines[ lidx + 1 ] == undefined)
1895
2032
  return;
1896
- swapArrayElements(this.code.lines, lidx, lidx + 1);
1897
- this.lineDown();
2033
+ this._addUndoStep( cursor, true );
2034
+ swapArrayElements( this.code.lines, lidx, lidx + 1 );
2035
+ this.lineDown( cursor );
1898
2036
  this.processLine( lidx );
1899
2037
  this.processLine( lidx + 1 );
1900
2038
  this.hideAutoCompleteBox();
@@ -1905,17 +2043,18 @@ class CodeEditor {
1905
2043
  // Apply binded actions...
1906
2044
 
1907
2045
  for( const actKey in this.actions ) {
2046
+
1908
2047
  if( key != actKey ) continue;
1909
2048
  e.preventDefault();
1910
2049
 
1911
- if(this.actions[ key ].deleteSelection && this.selection)
1912
- this.actions['Backspace'].callback(lidx, cursor, e);
2050
+ if( this.actions[ key ].deleteSelection && this.selection )
2051
+ this.actions['Backspace'].callback( lidx, cursor, e );
1913
2052
 
1914
2053
  return this.actions[ key ].callback( lidx, cursor, e );
1915
2054
  }
1916
2055
 
1917
2056
  // From now on, don't allow ctrl, shift or meta (mac) combinations
1918
- if( (e.ctrlKey || e.metaKey) )
2057
+ if( e.ctrlKey || e.metaKey )
1919
2058
  return;
1920
2059
 
1921
2060
  // Add undo steps
@@ -1927,7 +2066,7 @@ class CodeEditor {
1927
2066
 
1928
2067
  // Some custom cases for word enclosing (), {}, "", '', ...
1929
2068
 
1930
- const enclosableKeys = ["\"", "'", "(", "{"];
2069
+ const enclosableKeys = [ "\"", "'", "(", "{" ];
1931
2070
  if( enclosableKeys.indexOf( key ) > -1 )
1932
2071
  {
1933
2072
  if( this._encloseSelectedWordWithKey( key, lidx, cursor ) )
@@ -1945,19 +2084,19 @@ class CodeEditor {
1945
2084
 
1946
2085
  // Append key
1947
2086
 
1948
- const isPairKey = (Object.values( this.pairKeys ).indexOf( key ) > -1) && !this.wasKeyPaired;
1949
- const sameKeyNext = isPairKey && (this.code.lines[ lidx ][cursor.position] === key);
2087
+ const isPairKey = ( Object.values( this.pairKeys ).indexOf( key ) > -1 ) && !this.wasKeyPaired;
2088
+ const sameKeyNext = isPairKey && ( this.code.lines[ lidx ][ cursor.position ] === key );
1950
2089
 
1951
2090
  if( !sameKeyNext )
1952
2091
  {
1953
2092
  this.code.lines[ lidx ] = [
1954
- this.code.lines[ lidx ].slice(0, cursor.position),
2093
+ this.code.lines[ lidx ].slice( 0, cursor.position ),
1955
2094
  key,
1956
- this.code.lines[ lidx ].slice(cursor.position)
2095
+ this.code.lines[ lidx ].slice( cursor.position )
1957
2096
  ].join('');
1958
2097
  }
1959
2098
 
1960
- this.cursorToRight( key );
2099
+ this.cursorToRight( key, cursor );
1961
2100
 
1962
2101
  // Some custom cases for auto key pair (), {}, "", '', ...
1963
2102
 
@@ -1967,7 +2106,7 @@ class CodeEditor {
1967
2106
  // Make sure to detect later that the key is paired automatically to avoid loops...
1968
2107
  this.wasKeyPaired = true;
1969
2108
 
1970
- if(sameKeyNext) return;
2109
+ if( sameKeyNext ) return;
1971
2110
 
1972
2111
  this.root.dispatchEvent(new KeyboardEvent('keydown', { 'key': this.pairKeys[ key ] }));
1973
2112
  this.cursorToLeft( key, cursor );
@@ -1994,14 +2133,14 @@ class CodeEditor {
1994
2133
  this.showAutoCompleteBox( key, cursor );
1995
2134
  }
1996
2135
 
1997
- async _pasteContent() {
2136
+ async _pasteContent( cursor ) {
2137
+
1998
2138
  let text = await navigator.clipboard.readText();
1999
- this.appendText(text);
2139
+ this.appendText( text, cursor );
2000
2140
  }
2001
2141
 
2002
- async _copyContent() {
2142
+ async _copyContent( cursor ) {
2003
2143
 
2004
- let cursor = this.cursors.children[ 0 ];
2005
2144
  let text_to_copy = "";
2006
2145
 
2007
2146
  if( !this.selection ) {
@@ -2031,21 +2170,20 @@ class CodeEditor {
2031
2170
  navigator.clipboard.writeText( text_to_copy ).then(() => console.log("Successfully copied"), (err) => console.error("Error"));
2032
2171
  }
2033
2172
 
2034
- async _cutContent() {
2173
+ async _cutContent( cursor ) {
2035
2174
 
2036
- let cursor = this.cursors.children[ 0 ];
2037
2175
  let lidx = cursor.line;
2038
2176
  let text_to_cut = "";
2039
2177
 
2040
- this._addUndoStep( cursor );
2178
+ this._addUndoStep( cursor, true );
2041
2179
 
2042
2180
  if( !this.selection ) {
2043
2181
  text_to_cut = "\n" + this.code.lines[ cursor.line ];
2044
2182
  this.code.lines.splice( lidx, 1 );
2045
2183
  this.processLines();
2046
- this.resetCursorPos( CodeEditor.CURSOR_LEFT );
2184
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
2047
2185
  if( this.code.lines[ lidx ] == undefined )
2048
- this.lineUp();
2186
+ this.lineUp( cursor );
2049
2187
  }
2050
2188
  else {
2051
2189
 
@@ -2073,6 +2211,16 @@ class CodeEditor {
2073
2211
  navigator.clipboard.writeText( text_to_cut ).then(() => console.log("Successfully cut"), (err) => console.error("Error"));
2074
2212
  }
2075
2213
 
2214
+ _duplicateLine( lidx, cursor ) {
2215
+
2216
+ this.endSelection();
2217
+ this._addUndoStep( cursor, true );
2218
+ this.code.lines.splice( lidx, 0, this.code.lines[ lidx ] );
2219
+ this.lineDown( cursor );
2220
+ this.processLines();
2221
+ this.hideAutoCompleteBox();
2222
+ }
2223
+
2076
2224
  action( key, deleteSelection, fn ) {
2077
2225
 
2078
2226
  this.actions[ key ] = {
@@ -2540,7 +2688,13 @@ class CodeEditor {
2540
2688
  }
2541
2689
 
2542
2690
  _isCSSClass( token, prev, next ) {
2543
- return this.highlight == 'CSS' && prev == '.';
2691
+
2692
+ if( this.highlight != 'CSS' )
2693
+ return false;
2694
+
2695
+ return ( prev == '.' || prev == '::'
2696
+ || ( prev == ':' && next == '{' )
2697
+ || ( token[ 0 ] == '#' && prev != ':' ) );
2544
2698
  }
2545
2699
 
2546
2700
  isNumber( token ) {
@@ -2634,8 +2788,6 @@ class CodeEditor {
2634
2788
 
2635
2789
  lineUp( cursor, resetLeft ) {
2636
2790
 
2637
- cursor = cursor ?? this.cursors.children[ 0 ];
2638
-
2639
2791
  if( this.code.lines[ cursor.line - 1 ] == undefined )
2640
2792
  return false;
2641
2793
 
@@ -2647,8 +2799,6 @@ class CodeEditor {
2647
2799
  }
2648
2800
 
2649
2801
  lineDown( cursor, resetLeft ) {
2650
-
2651
- cursor = cursor ?? this.cursors.children[ 0 ];
2652
2802
 
2653
2803
  if( this.code.lines[ cursor.line + 1 ] == undefined )
2654
2804
  return false;
@@ -2726,10 +2876,23 @@ class CodeEditor {
2726
2876
  delete this._lastSelectionKeyDir;
2727
2877
  }
2728
2878
 
2879
+ selectAll( cursor ) {
2880
+
2881
+ this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
2882
+ this.startSelection( cursor );
2883
+ const nlines = this.code.lines.length - 1;
2884
+ this.selection.toX = this.code.lines[ nlines ].length;
2885
+ this.selection.toY = nlines;
2886
+ this.cursorToPosition( cursor, this.selection.toX );
2887
+ this.cursorToLine( cursor, this.selection.toY );
2888
+ this.processSelection( null, true );
2889
+ this.hideAutoCompleteBox();
2890
+ }
2891
+
2729
2892
  cursorToRight( key, cursor ) {
2730
2893
 
2731
2894
  if( !key ) return;
2732
- cursor = cursor ?? this.cursors.children[ 0 ];
2895
+
2733
2896
  cursor._left += this.charWidth;
2734
2897
  cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
2735
2898
  cursor.position++;
@@ -2748,7 +2911,7 @@ class CodeEditor {
2748
2911
  cursorToLeft( key, cursor ) {
2749
2912
 
2750
2913
  if( !key ) return;
2751
- cursor = cursor ?? this.cursors.children[ 0 ];
2914
+
2752
2915
  cursor._left -= this.charWidth;
2753
2916
  cursor._left = Math.max( cursor._left, 0 );
2754
2917
  cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
@@ -2767,13 +2930,12 @@ class CodeEditor {
2767
2930
 
2768
2931
  cursorToTop( cursor, resetLeft = false ) {
2769
2932
 
2770
- cursor = cursor ?? this.cursors.children[ 0 ];
2771
2933
  cursor._top -= this.lineHeight;
2772
2934
  cursor._top = Math.max(cursor._top, 0);
2773
2935
  cursor.style.top = "calc(" + cursor._top + "px)";
2774
2936
  this.restartBlink();
2775
2937
 
2776
- if(resetLeft)
2938
+ if( resetLeft )
2777
2939
  this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
2778
2940
 
2779
2941
  doAsync(() => {
@@ -2785,12 +2947,12 @@ class CodeEditor {
2785
2947
 
2786
2948
  cursorToBottom( cursor, resetLeft = false ) {
2787
2949
 
2788
- cursor = cursor ?? this.cursors.children[ 0 ];
2789
2950
  cursor._top += this.lineHeight;
2790
2951
  cursor.style.top = "calc(" + cursor._top + "px)";
2952
+
2791
2953
  this.restartBlink();
2792
2954
 
2793
- if(resetLeft)
2955
+ if( resetLeft )
2794
2956
  this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
2795
2957
 
2796
2958
  doAsync(() => {
@@ -2802,10 +2964,11 @@ class CodeEditor {
2802
2964
 
2803
2965
  cursorToString( cursor, text, reverse ) {
2804
2966
 
2805
- if( !text.length ) return;
2806
- cursor = cursor ?? this.cursors.children[ 0 ];
2967
+ if( !text.length )
2968
+ return;
2969
+
2807
2970
  for( let char of text )
2808
- reverse ? this.cursorToLeft( char ) : this.cursorToRight( char );
2971
+ reverse ? this.cursorToLeft( char, cursor ) : this.cursorToRight( char, cursor );
2809
2972
  }
2810
2973
 
2811
2974
  cursorToPosition( cursor, position ) {
@@ -2825,29 +2988,28 @@ class CodeEditor {
2825
2988
 
2826
2989
  saveCursor( cursor, state = {} ) {
2827
2990
 
2828
- var cursor = cursor ?? this.cursors.children[ 0 ];
2829
2991
  state.top = cursor._top;
2830
2992
  state.left = cursor._left;
2831
2993
  state.line = cursor.line;
2832
2994
  state.position = cursor.position;
2995
+
2833
2996
  return state;
2834
2997
  }
2835
2998
 
2836
2999
  restoreCursor( cursor, state ) {
2837
3000
 
2838
- cursor = cursor ?? this.cursors.children[ 0 ];
2839
3001
  cursor.line = state.line ?? 0;
2840
3002
  cursor.position = state.position ?? 0;
2841
3003
 
2842
3004
  cursor._left = state.left ?? 0;
2843
- cursor.style.left = "calc(" + ( cursor._left - this.getScrollLeft() ) + "px + " + this.xPadding + ")";
3005
+ cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
2844
3006
  cursor._top = state.top ?? 0;
2845
- cursor.style.top = "calc(" + ( cursor._top - this.getScrollTop() ) + "px)";
3007
+ cursor.style.top = "calc(" + cursor._top + "px)";
2846
3008
  }
2847
3009
 
2848
3010
  resetCursorPos( flag, cursor ) {
2849
3011
 
2850
- cursor = cursor ?? this.cursors.children[ 0 ];
3012
+ cursor = cursor ?? this._getCurrentCursor();
2851
3013
 
2852
3014
  if( flag & CodeEditor.CURSOR_LEFT )
2853
3015
  {
@@ -2864,14 +3026,14 @@ class CodeEditor {
2864
3026
  }
2865
3027
  }
2866
3028
 
2867
- addSpaceTabs(n) {
3029
+ addSpaceTabs( n ) {
2868
3030
 
2869
3031
  for( var i = 0; i < n; ++i ) {
2870
3032
  this.actions[ 'Tab' ].callback();
2871
3033
  }
2872
3034
  }
2873
3035
 
2874
- addSpaces(n) {
3036
+ addSpaces( n ) {
2875
3037
 
2876
3038
  for( var i = 0; i < n; ++i ) {
2877
3039
  this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
@@ -2923,8 +3085,6 @@ class CodeEditor {
2923
3085
 
2924
3086
  this.resizeScrollBars();
2925
3087
 
2926
- // console.warn("Resize editor viewport");
2927
-
2928
3088
  }, 10 );
2929
3089
  }
2930
3090
 
@@ -3033,14 +3193,11 @@ class CodeEditor {
3033
3193
 
3034
3194
  getCharAtPos( cursor, offset = 0 ) {
3035
3195
 
3036
- cursor = cursor ?? this.cursors.children[ 0 ];
3037
3196
  return this.code.lines[ cursor.line ][ cursor.position + offset ];
3038
3197
  }
3039
3198
 
3040
- getWordAtPos( cursor, loffset = 0, roffset ) {
3199
+ getWordAtPos( cursor, offset = 0 ) {
3041
3200
 
3042
- roffset = roffset ?? loffset;
3043
- cursor = cursor ?? this.cursors.children[ 0 ];
3044
3201
  const col = cursor.line;
3045
3202
  const words = this.code.lines[ col ];
3046
3203
 
@@ -3050,8 +3207,8 @@ class CodeEditor {
3050
3207
  return (exceptions.indexOf( char ) > - 1) || (code > 47 && code < 58) || (code > 64 && code < 91) || (code > 96 && code < 123);
3051
3208
  }
3052
3209
 
3053
- let from = cursor.position + roffset;
3054
- let to = cursor.position + loffset;
3210
+ let from = cursor.position + offset;
3211
+ let to = cursor.position + offset;
3055
3212
 
3056
3213
  // Check left ...
3057
3214
 
@@ -3070,7 +3227,7 @@ class CodeEditor {
3070
3227
  let word = words.substring( from, to );
3071
3228
  if( word == ' ' )
3072
3229
  {
3073
- if( loffset < 0 )
3230
+ if( offset < 0 )
3074
3231
  {
3075
3232
  while( words[ from - 1 ] != undefined && words[ from - 1 ] == ' ' )
3076
3233
  from--;
@@ -3089,15 +3246,16 @@ class CodeEditor {
3089
3246
  return [ word, from, to ];
3090
3247
  }
3091
3248
 
3092
- _measureChar( char = "a", get_bb = false ) {
3249
+ _measureChar( char = "a", use_floating = false, get_bb = false ) {
3093
3250
 
3094
- var test = document.createElement( "pre" );
3095
- test.className = "codechar";
3096
- test.innerHTML = char;
3097
- document.body.appendChild( test );
3098
- var rect = test.getBoundingClientRect();
3099
- deleteElement( test );
3100
- const bb = [ Math.floor( rect.width ), Math.floor( rect.height ) ];
3251
+ var line = document.createElement( "pre" );
3252
+ var text = document.createElement( "span" );
3253
+ line.appendChild( text );
3254
+ text.innerText = char;
3255
+ this.code.appendChild( line );
3256
+ var rect = text.getBoundingClientRect();
3257
+ deleteElement( line );
3258
+ const bb = [ use_floating ? rect.width : Math.floor( rect.width ), use_floating ? rect.height : Math.floor( rect.height ) ];
3101
3259
  return get_bb ? bb : bb[ 0 ];
3102
3260
  }
3103
3261
 
@@ -3242,8 +3400,10 @@ class CodeEditor {
3242
3400
 
3243
3401
  hideAutoCompleteBox() {
3244
3402
 
3403
+ const isActive = this.isAutoCompleteActive;
3245
3404
  this.isAutoCompleteActive = false;
3246
3405
  this.autocomplete.classList.remove( 'show' );
3406
+ return isActive != this.isAutoCompleteActive;
3247
3407
  }
3248
3408
 
3249
3409
  autoCompleteWord( cursor, suggestion ) {
@@ -3344,8 +3504,8 @@ class CodeEditor {
3344
3504
  return;
3345
3505
 
3346
3506
  let cursorData = new LX.vec2( this.position, this.line );
3347
- let found = null;
3348
- let idx = -1;
3507
+ let line = null;
3508
+ let char = -1;
3349
3509
 
3350
3510
  if( this._lastResult )
3351
3511
  {
@@ -3354,18 +3514,18 @@ class CodeEditor {
3354
3514
  delete this._lastResult;
3355
3515
  }
3356
3516
 
3357
- const getIndex = line => {
3358
- return this.code.lines[ line ].substr( line == cursorData.y ? cursorData.x : 0 ).indexOf( text );
3517
+ const getIndex = l => {
3518
+ return this.code.lines[ l ].substr( l == cursorData.y ? cursorData.x : 0 ).indexOf( text );
3359
3519
  };
3360
3520
 
3361
3521
  if( reverse )
3362
3522
  {
3363
3523
  for( var j = cursorData.y; j >= 0; --j )
3364
3524
  {
3365
- idx = getIndex( j );
3366
- if( idx > -1 )
3525
+ char = getIndex( j );
3526
+ if( char > -1 )
3367
3527
  {
3368
- found = j;
3528
+ line = j;
3369
3529
  break;
3370
3530
  }
3371
3531
  }
@@ -3374,30 +3534,36 @@ class CodeEditor {
3374
3534
  {
3375
3535
  for( var j = cursorData.y; j < this.code.lines.length; ++j )
3376
3536
  {
3377
- idx = getIndex( j );
3378
- if( idx > -1 )
3537
+ char = getIndex( j );
3538
+ if( char > -1 )
3379
3539
  {
3380
- found = j;
3540
+ line = j;
3381
3541
  break;
3382
3542
  }
3383
3543
  }
3384
3544
  }
3385
3545
 
3386
- if( found == null)
3546
+ if( line == null)
3387
3547
  {
3388
3548
  alert("No results!")
3389
3549
  return;
3390
3550
  }
3391
3551
 
3552
+ /*
3553
+ Position idx is computed from last pos, which could be in same line,
3554
+ so we search in the substring (first_ocurrence, end). That's why we
3555
+ have to add the length of the substring (0, first_ocurrence)
3556
+ */
3557
+
3558
+ char += ( line == cursorData.y ? cursorData.x : 0 );
3559
+
3392
3560
  // Text found..
3393
3561
 
3394
3562
  this._lastTextFound = text;
3395
3563
 
3396
- // console.warn("FOUND!! --", text, "-- at", "[" + idx + ", " + j + "]")
3397
-
3398
3564
  this.codeScroller.scrollTo(
3399
- Math.max( idx * this.charWidth - this.codeScroller.clientWidth ),
3400
- Math.max( found - 10 ) * this.lineHeight
3565
+ Math.max( char * this.charWidth - this.codeScroller.clientWidth ),
3566
+ Math.max( line - 10 ) * this.lineHeight
3401
3567
  );
3402
3568
 
3403
3569
  // Show elements
@@ -3405,18 +3571,54 @@ class CodeEditor {
3405
3571
 
3406
3572
  // Create new selection instance
3407
3573
  this.selection = new CodeSelection( this, 0, 0, "lexcodesearchresult" );
3408
- this.selection.selectInline( idx, found, this.measureString( text ) );
3574
+ this.selection.selectInline( char, line, this.measureString( text ) );
3409
3575
  this._lastResult = {
3410
3576
  'dom': this.selections.lastChild,
3411
- 'pos': new LX.vec2( idx + text.length, found )
3577
+ 'pos': new LX.vec2( char + text.length, line )
3412
3578
  };
3413
3579
 
3414
3580
  }
3415
3581
 
3582
+ showSearchLineBox() {
3583
+
3584
+ this.searchlinebox.classList.add( 'opened' );
3585
+ this.searchlineboxActive = true;
3586
+
3587
+ const input = this.searchlinebox.querySelector( 'input' );
3588
+ input.value = ":";
3589
+ input.focus();
3590
+ }
3591
+
3592
+ hideSearchLineBox() {
3593
+
3594
+ if( this.searchlineboxActive )
3595
+ {
3596
+ this.searchlinebox.classList.remove( 'opened' );
3597
+ this.searchlineboxActive = false;
3598
+ }
3599
+ }
3600
+
3601
+ goToLine( line ) {
3602
+
3603
+ if( !this.isNumber( line ) )
3604
+ return;
3605
+
3606
+ this.codeScroller.scrollTo( 0, Math.max( line - 15 ) * this.lineHeight );
3607
+
3608
+ // Select line ?
3609
+ var cursor = this._getCurrentCursor( true );
3610
+ this.cursorToLine( cursor, line - 1, true );
3611
+ }
3612
+
3416
3613
  _updateDataInfoPanel( signal, value ) {
3417
3614
 
3418
3615
  if( !this.skipCodeInfo )
3419
3616
  {
3617
+ if( this.cursors.childElementCount > 1 )
3618
+ {
3619
+ value = "";
3620
+ }
3621
+
3420
3622
  LX.emit( signal, value );
3421
3623
  }
3422
3624
  }
@@ -3431,7 +3633,7 @@ class CodeEditor {
3431
3633
  let line = this.code.childNodes[ old_local ];
3432
3634
 
3433
3635
  if( !line )
3434
- return;
3636
+ return;
3435
3637
 
3436
3638
  line.classList.remove( 'active-line' );
3437
3639
 
@@ -3445,6 +3647,46 @@ class CodeEditor {
3445
3647
  }
3446
3648
  }
3447
3649
 
3650
+ _increaseFontSize() {
3651
+
3652
+ // Change font size
3653
+
3654
+ var r = document.querySelector( ':root' );
3655
+ var s = getComputedStyle( r );
3656
+ var pixels = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
3657
+ pixels = LX.UTILS.clamp( pixels + 1, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE );
3658
+ r.style.setProperty( "--code-editor-font-size", pixels + "px" );
3659
+ this.charWidth = this._measureChar( "a", true );
3660
+
3661
+ // Change row size
3662
+
3663
+ var row_pixels = pixels + 6;
3664
+ r.style.setProperty( "--code-editor-row-height", row_pixels + "px" );
3665
+ this.lineHeight = row_pixels;
3666
+
3667
+ this.processLines(); // ... it's necessary?
3668
+ }
3669
+
3670
+ _decreaseFontSize() {
3671
+
3672
+ // Change font size
3673
+
3674
+ var r = document.querySelector( ':root' );
3675
+ var s = getComputedStyle( r );
3676
+ var pixels = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
3677
+ pixels = LX.UTILS.clamp( pixels - 1, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE );
3678
+ r.style.setProperty( "--code-editor-font-size", pixels + "px" );
3679
+ this.charWidth = this._measureChar( "a", true );
3680
+
3681
+ // Change row size
3682
+
3683
+ var row_pixels = pixels + 6;
3684
+ r.style.setProperty( "--code-editor-row-height", row_pixels + "px" );
3685
+ this.lineHeight = row_pixels;
3686
+
3687
+ this.processLines(); // ... it's necessary?
3688
+ }
3689
+
3448
3690
  _clearTmpVariables() {
3449
3691
 
3450
3692
  delete this._currentLineString;