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.
- package/build/components/codeeditor.js +423 -181
- package/build/lexgui.css +44 -12
- package/build/lexgui.js +1 -1
- package/build/lexgui.module.js +1 -1
- package/changelog.md +16 -0
- package/examples/code_editor.html +5 -2
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
|
339
|
+
get: (v) => { return this._getCurrentCursor().line }
|
|
346
340
|
} );
|
|
347
|
-
|
|
341
|
+
|
|
348
342
|
Object.defineProperty( this, 'position', {
|
|
349
|
-
get: (v) => { return
|
|
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
|
-
|
|
417
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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( !
|
|
1210
|
-
|
|
1211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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) <
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1874
|
-
this.code.
|
|
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,
|
|
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
|
-
|
|
1888
|
-
this.
|
|
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
|
-
|
|
1897
|
-
this.
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 )
|
|
2806
|
-
|
|
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(" +
|
|
3005
|
+
cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
|
|
2844
3006
|
cursor._top = state.top ?? 0;
|
|
2845
|
-
cursor.style.top = "calc(" +
|
|
3007
|
+
cursor.style.top = "calc(" + cursor._top + "px)";
|
|
2846
3008
|
}
|
|
2847
3009
|
|
|
2848
3010
|
resetCursorPos( flag, cursor ) {
|
|
2849
3011
|
|
|
2850
|
-
cursor = cursor ?? this.
|
|
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,
|
|
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 +
|
|
3054
|
-
let to = cursor.position +
|
|
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(
|
|
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
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
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
|
|
3348
|
-
let
|
|
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 =
|
|
3358
|
-
return this.code.lines[
|
|
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
|
-
|
|
3366
|
-
if(
|
|
3525
|
+
char = getIndex( j );
|
|
3526
|
+
if( char > -1 )
|
|
3367
3527
|
{
|
|
3368
|
-
|
|
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
|
-
|
|
3378
|
-
if(
|
|
3537
|
+
char = getIndex( j );
|
|
3538
|
+
if( char > -1 )
|
|
3379
3539
|
{
|
|
3380
|
-
|
|
3540
|
+
line = j;
|
|
3381
3541
|
break;
|
|
3382
3542
|
}
|
|
3383
3543
|
}
|
|
3384
3544
|
}
|
|
3385
3545
|
|
|
3386
|
-
if(
|
|
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(
|
|
3400
|
-
Math.max(
|
|
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(
|
|
3574
|
+
this.selection.selectInline( char, line, this.measureString( text ) );
|
|
3409
3575
|
this._lastResult = {
|
|
3410
3576
|
'dom': this.selections.lastChild,
|
|
3411
|
-
'pos': new LX.vec2(
|
|
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
|
-
|
|
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;
|