lexgui 0.1.19 → 0.1.20
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/README.md +10 -13
- package/build/components/codeeditor.js +428 -295
- package/build/lexgui.css +27 -16
- package/build/lexgui.js +22 -4
- package/build/lexgui.module.js +22 -4
- package/changelog.md +15 -0
- package/examples/code_editor.html +43 -34
- package/examples/index.html +1 -2
- package/package.json +2 -1
- package/examples/overlay_area.html +0 -61
- package/examples/previews/overlay_area.png +0 -0
|
@@ -30,7 +30,7 @@ function firstNonspaceIndex( str ) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function deleteElement( el ) {
|
|
33
|
-
if(el) el.remove();
|
|
33
|
+
if( el ) el.remove();
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
let ASYNC_ENABLED = true;
|
|
@@ -59,11 +59,25 @@ class CodeSelection {
|
|
|
59
59
|
return this.fromY === this.toY;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
samePosition() {
|
|
63
|
+
return this.fromX === this.toX;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
isEmpty() {
|
|
67
|
+
return this.sameLine() && this.samePosition();
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
invertIfNecessary() {
|
|
63
|
-
if(this.fromX > this.toX)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
swapElements(this, '
|
|
71
|
+
// if( this.fromX > this.toX )
|
|
72
|
+
if( this.fromY > this.toY )
|
|
73
|
+
{
|
|
74
|
+
swapElements( this, 'fromX', 'toX' );
|
|
75
|
+
swapElements( this, 'fromY', 'toY' );
|
|
76
|
+
}
|
|
77
|
+
else if( this.sameLine() && this.fromX > this.toX )
|
|
78
|
+
{
|
|
79
|
+
swapElements( this, 'fromX', 'toX' );
|
|
80
|
+
}
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
selectInline( x, y, width ) {
|
|
@@ -82,6 +96,9 @@ class CodeSelection {
|
|
|
82
96
|
domEl.style.left = "calc(" + domEl._left + "px + " + this.editor.xPadding + ")";
|
|
83
97
|
domEl.style.width = width + "px";
|
|
84
98
|
this.editor.selections.appendChild(domEl);
|
|
99
|
+
|
|
100
|
+
// Hide active line background
|
|
101
|
+
this.editor.code.childNodes.forEach( e => e.classList.remove( 'active-line' ) );
|
|
85
102
|
}
|
|
86
103
|
};
|
|
87
104
|
|
|
@@ -154,6 +171,10 @@ class CodeEditor {
|
|
|
154
171
|
static CURSOR_LEFT = 1;
|
|
155
172
|
static CURSOR_TOP = 2;
|
|
156
173
|
|
|
174
|
+
static SELECTION_X = 1;
|
|
175
|
+
static SELECTION_Y = 2
|
|
176
|
+
static SELECTION_X_Y = CodeEditor.SELECTION_X | CodeEditor.SELECTION_Y;
|
|
177
|
+
|
|
157
178
|
static KEEP_VISIBLE_LINES = 1;
|
|
158
179
|
static UPDATE_VISIBLE_LINES = 2;
|
|
159
180
|
|
|
@@ -235,7 +256,7 @@ class CodeEditor {
|
|
|
235
256
|
if( Object.keys( this.openedTabs ).length < 2 )
|
|
236
257
|
{
|
|
237
258
|
clearInterval( this.blinker );
|
|
238
|
-
this.cursors.classList.remove('show');
|
|
259
|
+
this.cursors.classList.remove( 'show' );
|
|
239
260
|
}
|
|
240
261
|
} } );
|
|
241
262
|
this.tabs.root.addEventListener( 'dblclick', (e) => {
|
|
@@ -271,6 +292,9 @@ class CodeEditor {
|
|
|
271
292
|
this.root.addEventListener( 'click', this.processMouse.bind(this) );
|
|
272
293
|
this.root.addEventListener( 'contextmenu', this.processMouse.bind(this) );
|
|
273
294
|
|
|
295
|
+
// Add mouseup event to document as well to detect when selections end
|
|
296
|
+
document.body.addEventListener( 'mouseup', this._onMouseUp.bind(this) );
|
|
297
|
+
|
|
274
298
|
// Cursors and selection
|
|
275
299
|
|
|
276
300
|
this.cursors = document.createElement( 'div' );
|
|
@@ -293,13 +317,32 @@ class CodeEditor {
|
|
|
293
317
|
cursor.style.left = this.xPadding;
|
|
294
318
|
cursor._top = 0;
|
|
295
319
|
cursor.style.top = cursor._top + "px";
|
|
296
|
-
cursor.
|
|
320
|
+
cursor._position = 0;
|
|
297
321
|
cursor._line = 0;
|
|
298
322
|
cursor.print = (function() { console.log( this.line, this.position ) }).bind( cursor );
|
|
299
323
|
|
|
324
|
+
Object.defineProperty( this, 'line', {
|
|
325
|
+
get: (v) => { return cursor.line }
|
|
326
|
+
} );
|
|
327
|
+
|
|
328
|
+
Object.defineProperty( this, 'position', {
|
|
329
|
+
get: (v) => { return cursor.position }
|
|
330
|
+
} );
|
|
331
|
+
|
|
300
332
|
Object.defineProperty( cursor, 'line', {
|
|
301
333
|
get: (v) => { return this._line },
|
|
302
|
-
set: (v) => {
|
|
334
|
+
set: (v) => {
|
|
335
|
+
this._line = v;
|
|
336
|
+
this._setActiveLine( v );
|
|
337
|
+
}
|
|
338
|
+
} );
|
|
339
|
+
|
|
340
|
+
Object.defineProperty( cursor, 'position', {
|
|
341
|
+
get: (v) => { return this._position },
|
|
342
|
+
set: (v) => {
|
|
343
|
+
this._position = v;
|
|
344
|
+
this._updateDataInfoPanel( "@cursor-pos", "Col " + v );
|
|
345
|
+
}
|
|
303
346
|
} );
|
|
304
347
|
|
|
305
348
|
this.cursors.appendChild( cursor );
|
|
@@ -400,7 +443,8 @@ class CodeEditor {
|
|
|
400
443
|
|
|
401
444
|
this.state = {
|
|
402
445
|
focused: false,
|
|
403
|
-
selectingText: false
|
|
446
|
+
selectingText: false,
|
|
447
|
+
activeLine: null
|
|
404
448
|
}
|
|
405
449
|
|
|
406
450
|
// Code
|
|
@@ -415,7 +459,7 @@ class CodeEditor {
|
|
|
415
459
|
this.maxUndoSteps = 16;
|
|
416
460
|
this.lineHeight = 20;
|
|
417
461
|
this.defaultSingleLineCommentToken = "//";
|
|
418
|
-
this.charWidth =
|
|
462
|
+
this.charWidth = 7; //this._measureChar();
|
|
419
463
|
this._lastTime = null;
|
|
420
464
|
|
|
421
465
|
this.pairKeys = {
|
|
@@ -443,6 +487,7 @@ class CodeEditor {
|
|
|
443
487
|
'WGSL': { ext: 'wgsl' },
|
|
444
488
|
'JSON': { ext: 'json' },
|
|
445
489
|
'XML': { ext: 'xml' },
|
|
490
|
+
'Rust': { ext: 'rs' },
|
|
446
491
|
'Python': { ext: 'py', singleLineCommentToken: '#' },
|
|
447
492
|
'HTML': { ext: 'html' },
|
|
448
493
|
'Batch': { ext: 'bat', blockComments: false, singleLineCommentToken: '::' }
|
|
@@ -458,7 +503,7 @@ class CodeEditor {
|
|
|
458
503
|
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
|
|
459
504
|
'arguments', 'extends', 'instanceof'],
|
|
460
505
|
'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
|
|
461
|
-
'NULL', 'unsigned'],
|
|
506
|
+
'NULL', 'unsigned', 'namespace'],
|
|
462
507
|
'JSON': ['true', 'false'],
|
|
463
508
|
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
464
509
|
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
|
|
@@ -466,6 +511,8 @@ class CodeEditor {
|
|
|
466
511
|
'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
|
|
467
512
|
'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
|
|
468
513
|
'texture_storage_2d_array', 'texture_storage_3d'],
|
|
514
|
+
'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true',
|
|
515
|
+
'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
|
|
469
516
|
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
|
|
470
517
|
'Batch': ['set', 'SET', 'echo', 'ECHO', 'off', 'OFF', 'del', 'DEL', 'defined', 'DEFINED', 'setlocal', 'SETLOCAL', 'enabledelayedexpansion', 'ENABLEDELAYEDEXPANSION', 'driverquery',
|
|
471
518
|
'DRIVERQUERY', 'print', 'PRINT'],
|
|
@@ -483,6 +530,7 @@ class CodeEditor {
|
|
|
483
530
|
};
|
|
484
531
|
this.types = {
|
|
485
532
|
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder'],
|
|
533
|
+
'Rust': ['u128'],
|
|
486
534
|
'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
|
|
487
535
|
'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
|
|
488
536
|
'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError',
|
|
@@ -497,9 +545,10 @@ class CodeEditor {
|
|
|
497
545
|
};
|
|
498
546
|
this.statementsAndDeclarations = {
|
|
499
547
|
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
|
|
500
|
-
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'glm', 'spdlog'],
|
|
548
|
+
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog'],
|
|
501
549
|
'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
|
|
502
550
|
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
|
|
551
|
+
'Rust': ['break', 'else', 'continue', 'for', 'if', 'loop', 'match', 'return', 'while', 'do', 'yield'],
|
|
503
552
|
'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue',
|
|
504
553
|
'global', 'pass'],
|
|
505
554
|
'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT']
|
|
@@ -511,6 +560,7 @@ class CodeEditor {
|
|
|
511
560
|
'GLSL': ['[', ']', '{', '}', '(', ')'],
|
|
512
561
|
'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
|
|
513
562
|
'CSS': ['{', '}', '(', ')', '*'],
|
|
563
|
+
'Rust': ['<', '>', '[', ']', '(', ')', '='],
|
|
514
564
|
'Python': ['<', '>', '[', ']', '(', ')', '='],
|
|
515
565
|
'Batch': ['[', ']', '(', ')', '%'],
|
|
516
566
|
'HTML': ['<', '>', '/']
|
|
@@ -610,7 +660,6 @@ class CodeEditor {
|
|
|
610
660
|
|
|
611
661
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
612
662
|
if(idx > 0) this.cursorToString( cursor, prestring );
|
|
613
|
-
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
614
663
|
this.setScrollLeft( 0 );
|
|
615
664
|
|
|
616
665
|
if( e.shiftKey && !e.cancelShift )
|
|
@@ -626,7 +675,7 @@ class CodeEditor {
|
|
|
626
675
|
this.selection.selectInline( idx, cursor.line, this.measureString( string ) );
|
|
627
676
|
else
|
|
628
677
|
{
|
|
629
|
-
this.processSelection();
|
|
678
|
+
this.processSelection( e );
|
|
630
679
|
}
|
|
631
680
|
} else if( !e.keepSelection )
|
|
632
681
|
this.endSelection();
|
|
@@ -634,7 +683,7 @@ class CodeEditor {
|
|
|
634
683
|
|
|
635
684
|
this.action( 'End', false, ( ln, cursor, e ) => {
|
|
636
685
|
|
|
637
|
-
if( e.shiftKey || e._shiftKey ) {
|
|
686
|
+
if( ( e.shiftKey || e._shiftKey ) && !e.cancelShift ) {
|
|
638
687
|
|
|
639
688
|
var string = this.code.lines[ ln ].substring( cursor.position );
|
|
640
689
|
if( !this.selection )
|
|
@@ -645,9 +694,9 @@ class CodeEditor {
|
|
|
645
694
|
{
|
|
646
695
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
647
696
|
this.cursorToString( cursor, this.code.lines[ ln ] );
|
|
648
|
-
this.processSelection();
|
|
697
|
+
this.processSelection( e );
|
|
649
698
|
}
|
|
650
|
-
} else
|
|
699
|
+
} else if( !e.keepSelection )
|
|
651
700
|
this.endSelection();
|
|
652
701
|
|
|
653
702
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
@@ -706,16 +755,14 @@ class CodeEditor {
|
|
|
706
755
|
if( !this.selection )
|
|
707
756
|
this.startSelection( cursor );
|
|
708
757
|
|
|
709
|
-
this.
|
|
710
|
-
this.cursorToLine( cursor, this.selection.toY );
|
|
758
|
+
this.lineUp();
|
|
711
759
|
|
|
712
760
|
var letter = this.getCharAtPos( cursor );
|
|
713
761
|
if( !letter ) {
|
|
714
|
-
this.
|
|
715
|
-
this.cursorToPosition( cursor, this.selection.toX );
|
|
762
|
+
this.cursorToPosition( cursor, this.code.lines[ cursor.line ].length );
|
|
716
763
|
}
|
|
717
764
|
|
|
718
|
-
this.processSelection(
|
|
765
|
+
this.processSelection( e, false );
|
|
719
766
|
|
|
720
767
|
} else {
|
|
721
768
|
this.endSelection();
|
|
@@ -728,7 +775,7 @@ class CodeEditor {
|
|
|
728
775
|
// Move up autocomplete selection
|
|
729
776
|
else
|
|
730
777
|
{
|
|
731
|
-
this.
|
|
778
|
+
this._moveArrowSelectedAutoComplete('up');
|
|
732
779
|
}
|
|
733
780
|
});
|
|
734
781
|
|
|
@@ -741,16 +788,14 @@ class CodeEditor {
|
|
|
741
788
|
if( !this.selection )
|
|
742
789
|
this.startSelection( cursor );
|
|
743
790
|
|
|
744
|
-
this.
|
|
745
|
-
this.cursorToLine( cursor, this.selection.toY );
|
|
791
|
+
this.lineDown( cursor );
|
|
746
792
|
|
|
747
793
|
var letter = this.getCharAtPos( cursor );
|
|
748
794
|
if( !letter ) {
|
|
749
|
-
this.
|
|
750
|
-
this.cursorToPosition(cursor, this.selection.toX);
|
|
795
|
+
this.cursorToPosition( cursor, Math.max(this.code.lines[ cursor.line ].length - 1, 0) );
|
|
751
796
|
}
|
|
752
797
|
|
|
753
|
-
this.processSelection(
|
|
798
|
+
this.processSelection( e );
|
|
754
799
|
} else {
|
|
755
800
|
|
|
756
801
|
if( this.code.lines[ ln + 1 ] == undefined )
|
|
@@ -765,12 +810,16 @@ class CodeEditor {
|
|
|
765
810
|
// Move down autocomplete selection
|
|
766
811
|
else
|
|
767
812
|
{
|
|
768
|
-
this.
|
|
813
|
+
this._moveArrowSelectedAutoComplete('down');
|
|
769
814
|
}
|
|
770
815
|
});
|
|
771
816
|
|
|
772
817
|
this.action( 'ArrowLeft', false, ( ln, cursor, e ) => {
|
|
773
818
|
|
|
819
|
+
// Nothing to do..
|
|
820
|
+
if( cursor.line == 0 && cursor.position == 0 )
|
|
821
|
+
return;
|
|
822
|
+
|
|
774
823
|
if( e.metaKey ) { // Apple devices (Command)
|
|
775
824
|
e.preventDefault();
|
|
776
825
|
this.actions[ 'Home' ].callback( ln, cursor, e );
|
|
@@ -778,29 +827,29 @@ class CodeEditor {
|
|
|
778
827
|
else if( e.ctrlKey ) {
|
|
779
828
|
// Get next word
|
|
780
829
|
const [word, from, to] = this.getWordAtPos( cursor, -1 );
|
|
781
|
-
|
|
782
|
-
|
|
830
|
+
// If no length, we change line..
|
|
831
|
+
if( !word.length && this.lineUp( cursor, true ) ) {
|
|
832
|
+
e.cancelShift = true;
|
|
833
|
+
e.keepSelection = true;
|
|
834
|
+
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
835
|
+
delete e.cancelShift;
|
|
836
|
+
delete e.keepSelection;
|
|
837
|
+
}
|
|
838
|
+
var diff = Math.max( cursor.position - from, 1 );
|
|
839
|
+
var substr = word.substr( 0, diff );
|
|
783
840
|
// Selections...
|
|
784
841
|
if( e.shiftKey ) { if( !this.selection ) this.startSelection( cursor ); }
|
|
785
842
|
else this.endSelection();
|
|
786
|
-
this.cursorToString(cursor, substr, true);
|
|
787
|
-
if( e.shiftKey ) this.processSelection();
|
|
843
|
+
this.cursorToString( cursor, substr, true );
|
|
844
|
+
if( e.shiftKey ) this.processSelection( e, false, true );
|
|
788
845
|
}
|
|
789
846
|
else {
|
|
790
847
|
var letter = this.getCharAtPos( cursor, -1 );
|
|
791
848
|
if( letter ) {
|
|
792
849
|
if( e.shiftKey ) {
|
|
793
850
|
if( !this.selection ) this.startSelection( cursor );
|
|
794
|
-
if( ( ( cursor.position - 1 ) < this.selection.fromX ) && this.selection.sameLine() )
|
|
795
|
-
this.selection.fromX--;
|
|
796
|
-
else if( ( cursor.position - 1 ) == this.selection.fromX && this.selection.sameLine() ) {
|
|
797
|
-
this.cursorToLeft( letter, cursor );
|
|
798
|
-
this.endSelection();
|
|
799
|
-
return;
|
|
800
|
-
}
|
|
801
|
-
else this.selection.toX--;
|
|
802
851
|
this.cursorToLeft( letter, cursor );
|
|
803
|
-
this.processSelection(
|
|
852
|
+
this.processSelection( e, false, CodeEditor.SELECTION_X );
|
|
804
853
|
}
|
|
805
854
|
else {
|
|
806
855
|
if( !this.selection ) {
|
|
@@ -819,55 +868,48 @@ class CodeEditor {
|
|
|
819
868
|
}
|
|
820
869
|
else if( cursor.line > 0 ) {
|
|
821
870
|
|
|
822
|
-
if( e.shiftKey )
|
|
823
|
-
if( !this.selection ) this.startSelection( cursor );
|
|
824
|
-
}
|
|
871
|
+
if( e.shiftKey && !this.selection ) this.startSelection( cursor );
|
|
825
872
|
|
|
826
873
|
this.lineUp( cursor );
|
|
874
|
+
|
|
875
|
+
e.cancelShift = e.keepSelection = true;
|
|
827
876
|
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
877
|
+
delete e.cancelShift; delete e.keepSelection;
|
|
828
878
|
|
|
829
|
-
if( e.shiftKey )
|
|
830
|
-
this.selection.toX = cursor.position;
|
|
831
|
-
this.selection.toY--;
|
|
832
|
-
this.processSelection( null, true );
|
|
833
|
-
}
|
|
879
|
+
if( e.shiftKey ) this.processSelection( e, false );
|
|
834
880
|
}
|
|
835
881
|
}
|
|
836
882
|
});
|
|
837
883
|
|
|
838
884
|
this.action( 'ArrowRight', false, ( ln, cursor, e ) => {
|
|
839
885
|
|
|
886
|
+
// Nothing to do..
|
|
887
|
+
if( cursor.line == this.code.lines.length - 1 &&
|
|
888
|
+
cursor.position == this.code.lines[ cursor.line - 1 ].length )
|
|
889
|
+
return;
|
|
890
|
+
|
|
840
891
|
if( e.metaKey ) { // Apple devices (Command)
|
|
841
892
|
e.preventDefault();
|
|
842
893
|
this.actions[ 'End' ].callback( ln, cursor );
|
|
843
894
|
} else if( e.ctrlKey ) {
|
|
844
895
|
// Get next word
|
|
845
896
|
const [ word, from, to ] = this.getWordAtPos( cursor );
|
|
897
|
+
// If no length, we change line..
|
|
898
|
+
if( !word.length ) this.lineDown( cursor, true );
|
|
846
899
|
var diff = cursor.position - from;
|
|
847
900
|
var substr = word.substr( diff );
|
|
848
901
|
// Selections...
|
|
849
902
|
if( e.shiftKey ) { if( !this.selection ) this.startSelection( cursor ); }
|
|
850
903
|
else this.endSelection();
|
|
851
904
|
this.cursorToString( cursor, substr);
|
|
852
|
-
if( e.shiftKey ) this.processSelection();
|
|
905
|
+
if( e.shiftKey ) this.processSelection( e );
|
|
853
906
|
} else {
|
|
854
907
|
var letter = this.getCharAtPos( cursor );
|
|
855
908
|
if( letter ) {
|
|
856
909
|
if( e.shiftKey ) {
|
|
857
910
|
if( !this.selection ) this.startSelection( cursor );
|
|
858
|
-
var keep_range = false;
|
|
859
|
-
if( cursor.position == this.selection.fromX ) {
|
|
860
|
-
if( ( cursor.position + 1 ) == this.selection.toX && this.selection.sameLine() ) {
|
|
861
|
-
this.cursorToRight( letter, cursor );
|
|
862
|
-
this.endSelection();
|
|
863
|
-
return;
|
|
864
|
-
} else if( cursor.position < this.selection.toX ) {
|
|
865
|
-
this.selection.fromX++;
|
|
866
|
-
keep_range = true;
|
|
867
|
-
} else this.selection.toX++;
|
|
868
|
-
}
|
|
869
911
|
this.cursorToRight( letter, cursor );
|
|
870
|
-
this.processSelection(
|
|
912
|
+
this.processSelection( e, false, CodeEditor.SELECTION_X );
|
|
871
913
|
}else{
|
|
872
914
|
if( !this.selection ) {
|
|
873
915
|
this.cursorToRight( letter, cursor );
|
|
@@ -888,18 +930,11 @@ class CodeEditor {
|
|
|
888
930
|
|
|
889
931
|
if( e.shiftKey ) {
|
|
890
932
|
if( !this.selection ) this.startSelection( cursor );
|
|
891
|
-
e.cancelShift = true;
|
|
892
|
-
e.keepSelection = true;
|
|
893
933
|
}
|
|
894
934
|
|
|
895
|
-
this.lineDown( cursor );
|
|
896
|
-
this.actions['Home'].callback( cursor.line, cursor, e );
|
|
935
|
+
this.lineDown( cursor, true );
|
|
897
936
|
|
|
898
|
-
if( e.shiftKey )
|
|
899
|
-
this.selection.toX = cursor.position;
|
|
900
|
-
this.selection.toY++;
|
|
901
|
-
this.processSelection( null, true );
|
|
902
|
-
}
|
|
937
|
+
if( e.shiftKey ) this.processSelection( e, false );
|
|
903
938
|
|
|
904
939
|
this.hideAutoCompleteBox();
|
|
905
940
|
}
|
|
@@ -919,6 +954,28 @@ class CodeEditor {
|
|
|
919
954
|
// Create inspector panel
|
|
920
955
|
let panel = this._createPanelInfo();
|
|
921
956
|
if( panel ) area.attach( panel );
|
|
957
|
+
|
|
958
|
+
const fontUrl = "https://raw.githubusercontent.com/jxarco/lexgui.js/master/" + "/data/CommitMono-400-Regular.otf";
|
|
959
|
+
const commitMono = new FontFace(
|
|
960
|
+
"CommitMono",
|
|
961
|
+
`url(${ fontUrl })`,
|
|
962
|
+
{
|
|
963
|
+
style: "normal",
|
|
964
|
+
weight: "400",
|
|
965
|
+
display: "swap"
|
|
966
|
+
}
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
// Add to the document.fonts (FontFaceSet)
|
|
970
|
+
document.fonts.add(commitMono);
|
|
971
|
+
|
|
972
|
+
// Load the font
|
|
973
|
+
commitMono.load();
|
|
974
|
+
|
|
975
|
+
// Wait until the fonts are all loaded
|
|
976
|
+
document.fonts.ready.then(() => {
|
|
977
|
+
console.log("commitMono loaded")
|
|
978
|
+
});
|
|
922
979
|
}
|
|
923
980
|
|
|
924
981
|
static getInstances()
|
|
@@ -1106,7 +1163,7 @@ class CodeEditor {
|
|
|
1106
1163
|
|
|
1107
1164
|
this.code.language = lang;
|
|
1108
1165
|
this.highlight = lang;
|
|
1109
|
-
this.
|
|
1166
|
+
this._updateDataInfoPanel( "@highlight", lang );
|
|
1110
1167
|
this.processLines();
|
|
1111
1168
|
|
|
1112
1169
|
const ext = this.languages[ lang ].ext;
|
|
@@ -1144,23 +1201,13 @@ class CodeEditor {
|
|
|
1144
1201
|
if( !ext )
|
|
1145
1202
|
return this._changeLanguage( this.code.language );
|
|
1146
1203
|
|
|
1147
|
-
|
|
1204
|
+
for( let l in this.languages )
|
|
1148
1205
|
{
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
case 'h': return this._changeLanguage( 'C++' );
|
|
1152
|
-
case 'glsl': return this._changeLanguage( 'GLSL' );
|
|
1153
|
-
case 'css': return this._changeLanguage( 'CSS' );
|
|
1154
|
-
case 'json': return this._changeLanguage( 'JSON' );
|
|
1155
|
-
case 'xml': return this._changeLanguage( 'XML' );
|
|
1156
|
-
case 'wgsl': return this._changeLanguage( 'WGSL' );
|
|
1157
|
-
case 'py': return this._changeLanguage( 'Python' );
|
|
1158
|
-
case 'bat': return this._changeLanguage( 'Batch' );
|
|
1159
|
-
case 'html': return this._changeLanguage( 'HTML' );
|
|
1160
|
-
case 'txt':
|
|
1161
|
-
default:
|
|
1162
|
-
this._changeLanguage( 'Plain Text' );
|
|
1206
|
+
if( this.languages[l].ext == ext )
|
|
1207
|
+
return this._changeLanguage( l );
|
|
1163
1208
|
}
|
|
1209
|
+
|
|
1210
|
+
this._changeLanguage( 'Plain Text' );
|
|
1164
1211
|
}
|
|
1165
1212
|
|
|
1166
1213
|
_createPanelInfo() {
|
|
@@ -1168,34 +1215,23 @@ class CodeEditor {
|
|
|
1168
1215
|
if( !this.skipCodeInfo )
|
|
1169
1216
|
{
|
|
1170
1217
|
let panel = new LX.Panel({ className: "lexcodetabinfo", width: "calc(100%)", height: "auto" });
|
|
1171
|
-
panel.ln = 0;
|
|
1172
|
-
panel.col = 0;
|
|
1173
|
-
|
|
1174
|
-
this._refreshCodeInfo = ( ln = panel.ln, col = panel.col ) => {
|
|
1175
|
-
panel.ln = ln + 1;
|
|
1176
|
-
panel.col = col + 1;
|
|
1177
|
-
panel.clear();
|
|
1178
|
-
panel.sameLine();
|
|
1179
|
-
panel.addLabel( this.code.title, { float: 'right' });
|
|
1180
|
-
panel.addLabel( "Ln " + panel.ln, { width: "64px" });
|
|
1181
|
-
panel.addLabel( "Col " + panel.col, { width: "64px" });
|
|
1182
|
-
panel.addButton( "<b>{ }</b>", this.highlight, ( value, event ) => {
|
|
1183
|
-
LX.addContextMenu( "Language", event, m => {
|
|
1184
|
-
for( const lang of Object.keys(this.languages) )
|
|
1185
|
-
m.add( lang, this._changeLanguage.bind(this) );
|
|
1186
|
-
});
|
|
1187
|
-
}, { width: "25%", nameWidth: "15%" });
|
|
1188
|
-
panel.endLine();
|
|
1189
|
-
};
|
|
1190
1218
|
|
|
1191
|
-
|
|
1219
|
+
panel.sameLine();
|
|
1220
|
+
panel.addLabel( this.code.title, { float: 'right', signal: "@tab-name" });
|
|
1221
|
+
panel.addLabel( "Ln " + 1, { width: "64px", signal: "@cursor-line" });
|
|
1222
|
+
panel.addLabel( "Col " + 1, { width: "64px", signal: "@cursor-pos" });
|
|
1223
|
+
panel.addButton( "<b>{ }</b>", this.highlight, ( value, event ) => {
|
|
1224
|
+
LX.addContextMenu( "Language", event, m => {
|
|
1225
|
+
for( const lang of Object.keys(this.languages) )
|
|
1226
|
+
m.add( lang, this._changeLanguage.bind(this) );
|
|
1227
|
+
});
|
|
1228
|
+
}, { width: "25%", nameWidth: "15%", signal: "@highlight" });
|
|
1229
|
+
panel.endLine();
|
|
1192
1230
|
|
|
1193
1231
|
return panel;
|
|
1194
1232
|
}
|
|
1195
1233
|
else
|
|
1196
1234
|
{
|
|
1197
|
-
this._refreshCodeInfo = () => {};
|
|
1198
|
-
|
|
1199
1235
|
doAsync( () => {
|
|
1200
1236
|
|
|
1201
1237
|
// Change css a little bit...
|
|
@@ -1212,10 +1248,10 @@ class CodeEditor {
|
|
|
1212
1248
|
const isNewTabButton = name ? ( name === '+' ) : false;
|
|
1213
1249
|
const ext = extension ?? LX.getExtension( name );
|
|
1214
1250
|
return ext == 'html' ? "fa-solid fa-code orange" :
|
|
1215
|
-
ext ==
|
|
1216
|
-
ext ==
|
|
1217
|
-
ext ==
|
|
1218
|
-
[
|
|
1251
|
+
ext == 'css' ? "fa-solid fa-hashtag dodgerblue" :
|
|
1252
|
+
ext == 'xml' ? "fa-solid fa-rss orange" :
|
|
1253
|
+
ext == 'bat' ? "fa-brands fa-windows lightblue" :
|
|
1254
|
+
[ 'js', 'py', 'json', 'cpp', 'rs' ].indexOf( ext ) > -1 ? "images/" + ext + ".png" :
|
|
1219
1255
|
!isNewTabButton ? "fa-solid fa-align-left gray" : undefined;
|
|
1220
1256
|
}
|
|
1221
1257
|
|
|
@@ -1229,6 +1265,37 @@ class CodeEditor {
|
|
|
1229
1265
|
});
|
|
1230
1266
|
}
|
|
1231
1267
|
|
|
1268
|
+
_onSelectTab( isNewTabButton, event, name, ) {
|
|
1269
|
+
|
|
1270
|
+
if( isNewTabButton )
|
|
1271
|
+
{
|
|
1272
|
+
this._onNewTab( event );
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
var cursor = cursor ?? this.cursors.children[ 0 ];
|
|
1277
|
+
this.saveCursor( cursor, this.code.cursorState );
|
|
1278
|
+
|
|
1279
|
+
this.code = this.loadedTabs[ name ];
|
|
1280
|
+
this.restoreCursor( cursor, this.code.cursorState );
|
|
1281
|
+
|
|
1282
|
+
this.endSelection();
|
|
1283
|
+
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
1284
|
+
this._updateDataInfoPanel( "@tab-name", name );
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
_onContextMenuTab( isNewTabButton, event, name, ) {
|
|
1288
|
+
|
|
1289
|
+
if( isNewTabButton )
|
|
1290
|
+
return;
|
|
1291
|
+
|
|
1292
|
+
LX.addContextMenu( null, event, m => {
|
|
1293
|
+
m.add( "Close", () => { this.tabs.delete( name ) } );
|
|
1294
|
+
m.add( "" );
|
|
1295
|
+
m.add( "Rename", () => { console.warn( "TODO" )} );
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1232
1299
|
addTab( name, selected, title ) {
|
|
1233
1300
|
|
|
1234
1301
|
// If already loaded, set new name...
|
|
@@ -1281,28 +1348,14 @@ class CodeEditor {
|
|
|
1281
1348
|
this.explorer.frefresh( name );
|
|
1282
1349
|
}
|
|
1283
1350
|
|
|
1284
|
-
this.tabs.add(name, code, {
|
|
1351
|
+
this.tabs.add( name, code, {
|
|
1285
1352
|
selected: selected,
|
|
1286
1353
|
fixed: isNewTabButton,
|
|
1287
1354
|
title: code.title,
|
|
1288
1355
|
icon: tabIcon,
|
|
1289
|
-
onSelect: (
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
{
|
|
1293
|
-
this._onNewTab( e );
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
var cursor = cursor ?? this.cursors.children[ 0 ];
|
|
1298
|
-
this.saveCursor( cursor, this.code.cursorState );
|
|
1299
|
-
this.code = this.loadedTabs[ tabname ];
|
|
1300
|
-
this.restoreCursor( cursor, this.code.cursorState );
|
|
1301
|
-
this.endSelection();
|
|
1302
|
-
this._changeLanguageFromExtension( LX.getExtension( tabname ) );
|
|
1303
|
-
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1304
|
-
}
|
|
1305
|
-
});
|
|
1356
|
+
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
1357
|
+
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton )
|
|
1358
|
+
} );
|
|
1306
1359
|
|
|
1307
1360
|
// Move into the sizer..
|
|
1308
1361
|
this.codeSizer.appendChild( code );
|
|
@@ -1314,9 +1367,10 @@ class CodeEditor {
|
|
|
1314
1367
|
this.code = code;
|
|
1315
1368
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
|
|
1316
1369
|
this.processLines();
|
|
1317
|
-
doAsync( () => this._refreshCodeInfo( 0, 0 ), 50 );
|
|
1318
1370
|
}
|
|
1319
1371
|
|
|
1372
|
+
this._updateDataInfoPanel( "@tab-name", name );
|
|
1373
|
+
|
|
1320
1374
|
// Bc it could be overrided..
|
|
1321
1375
|
return name;
|
|
1322
1376
|
}
|
|
@@ -1355,22 +1409,8 @@ class CodeEditor {
|
|
|
1355
1409
|
fixed: isNewTabButton,
|
|
1356
1410
|
title: code.title,
|
|
1357
1411
|
icon: tabIcon,
|
|
1358
|
-
onSelect: (
|
|
1359
|
-
|
|
1360
|
-
if( isNewTabButton )
|
|
1361
|
-
{
|
|
1362
|
-
this._onNewTab( e );
|
|
1363
|
-
return;
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
var cursor = cursor ?? this.cursors.children[ 0 ];
|
|
1367
|
-
this.saveCursor( cursor, this.code.cursorState );
|
|
1368
|
-
this.code = this.loadedTabs[ tabname ];
|
|
1369
|
-
this.restoreCursor( cursor, this.code.cursorState );
|
|
1370
|
-
this.endSelection();
|
|
1371
|
-
this._changeLanguageFromExtension( LX.getExtension( tabname ) );
|
|
1372
|
-
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1373
|
-
}
|
|
1412
|
+
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
1413
|
+
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton )
|
|
1374
1414
|
});
|
|
1375
1415
|
|
|
1376
1416
|
// Move into the sizer..
|
|
@@ -1383,7 +1423,7 @@ class CodeEditor {
|
|
|
1383
1423
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
|
|
1384
1424
|
this.processLines();
|
|
1385
1425
|
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
1386
|
-
|
|
1426
|
+
this._updateDataInfoPanel( "@tab-name", tabname );
|
|
1387
1427
|
}
|
|
1388
1428
|
|
|
1389
1429
|
loadTabFromFile() {
|
|
@@ -1405,7 +1445,7 @@ class CodeEditor {
|
|
|
1405
1445
|
this.restartBlink();
|
|
1406
1446
|
else {
|
|
1407
1447
|
clearInterval( this.blinker );
|
|
1408
|
-
this.cursors.classList.remove('show');
|
|
1448
|
+
this.cursors.classList.remove( 'show' );
|
|
1409
1449
|
}
|
|
1410
1450
|
}
|
|
1411
1451
|
|
|
@@ -1430,7 +1470,7 @@ class CodeEditor {
|
|
|
1430
1470
|
// Left click only...
|
|
1431
1471
|
if( e.button === 2 )
|
|
1432
1472
|
{
|
|
1433
|
-
this.processClick(e);
|
|
1473
|
+
this.processClick( e );
|
|
1434
1474
|
|
|
1435
1475
|
this.canOpenContextMenu = !this.selection;
|
|
1436
1476
|
|
|
@@ -1450,21 +1490,13 @@ class CodeEditor {
|
|
|
1450
1490
|
|
|
1451
1491
|
else if( e.type == 'mouseup' )
|
|
1452
1492
|
{
|
|
1453
|
-
|
|
1454
|
-
this.state.selectingText = false;
|
|
1455
|
-
this.processClick(e);
|
|
1456
|
-
this.endSelection();
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
if(this.selection) this.selection.invertIfNecessary();
|
|
1460
|
-
|
|
1461
|
-
this.state.selectingText = false;
|
|
1493
|
+
this._onMouseUp( e );
|
|
1462
1494
|
}
|
|
1463
1495
|
|
|
1464
1496
|
else if( e.type == 'mousemove' )
|
|
1465
1497
|
{
|
|
1466
1498
|
if( this.state.selectingText )
|
|
1467
|
-
this.processSelection(e);
|
|
1499
|
+
this.processSelection( e );
|
|
1468
1500
|
}
|
|
1469
1501
|
|
|
1470
1502
|
else if ( e.type == 'click' ) // trip
|
|
@@ -1515,7 +1547,24 @@ class CodeEditor {
|
|
|
1515
1547
|
}
|
|
1516
1548
|
}
|
|
1517
1549
|
|
|
1518
|
-
|
|
1550
|
+
_onMouseUp( e ) {
|
|
1551
|
+
|
|
1552
|
+
if( (LX.getTime() - this.lastMouseDown) < 300 ) {
|
|
1553
|
+
this.state.selectingText = false;
|
|
1554
|
+
this.processClick( e );
|
|
1555
|
+
this.endSelection();
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
if( this.selection )
|
|
1559
|
+
{
|
|
1560
|
+
this.selection.invertIfNecessary();
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
this.state.selectingText = false;
|
|
1564
|
+
delete this._lastSelectionKeyDir;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
processClick( e ) {
|
|
1519
1568
|
|
|
1520
1569
|
var cursor = this.cursors.children[ 0 ];
|
|
1521
1570
|
var code_rect = this.codeScroller.getBoundingClientRect();
|
|
@@ -1532,24 +1581,51 @@ class CodeEditor {
|
|
|
1532
1581
|
this.cursorToPosition( cursor, string.length );
|
|
1533
1582
|
|
|
1534
1583
|
this.hideAutoCompleteBox();
|
|
1535
|
-
|
|
1536
|
-
if( !skip_refresh )
|
|
1537
|
-
this._refreshCodeInfo( ln, cursor.position );
|
|
1538
1584
|
}
|
|
1539
1585
|
|
|
1540
|
-
processSelection( e, keep_range ) {
|
|
1586
|
+
processSelection( e, keep_range, flags = CodeEditor.SELECTION_X_Y ) {
|
|
1541
1587
|
|
|
1542
1588
|
var cursor = this.cursors.children[ 0 ];
|
|
1589
|
+
const isMouseEvent = e && ( e.constructor == MouseEvent );
|
|
1543
1590
|
|
|
1544
|
-
if(
|
|
1591
|
+
if( isMouseEvent ) this.processClick( e );
|
|
1545
1592
|
if( !this.selection )
|
|
1546
1593
|
this.startSelection( cursor );
|
|
1547
1594
|
|
|
1595
|
+
// Hide active line background
|
|
1596
|
+
this.code.childNodes.forEach( e => e.classList.remove( 'active-line' ) );
|
|
1597
|
+
|
|
1548
1598
|
// Update selection
|
|
1549
|
-
if(!keep_range)
|
|
1599
|
+
if( !keep_range )
|
|
1550
1600
|
{
|
|
1551
|
-
|
|
1552
|
-
|
|
1601
|
+
let ccw = true;
|
|
1602
|
+
|
|
1603
|
+
// Check if we must change ccw or not ... (not with mouse)
|
|
1604
|
+
if( !isMouseEvent && this.line >= this.selection.fromY &&
|
|
1605
|
+
(this.line == this.selection.fromY ? this.position >= this.selection.fromX : true) )
|
|
1606
|
+
{
|
|
1607
|
+
ccw = ( e && this._lastSelectionKeyDir && ( e.key == 'ArrowRight' || e.key == 'ArrowDown' || e.key == 'End' ) );
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
if( ccw )
|
|
1611
|
+
{
|
|
1612
|
+
if( flags & CodeEditor.SELECTION_X ) this.selection.fromX = cursor.position;
|
|
1613
|
+
if( flags & CodeEditor.SELECTION_Y ) this.selection.fromY = cursor.line;
|
|
1614
|
+
}
|
|
1615
|
+
else
|
|
1616
|
+
{
|
|
1617
|
+
if( flags & CodeEditor.SELECTION_X ) this.selection.toX = cursor.position;
|
|
1618
|
+
if( flags & CodeEditor.SELECTION_Y ) this.selection.toY = cursor.line;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
this._lastSelectionKeyDir = ccw;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// Only leave if not a mouse selection...
|
|
1625
|
+
if( !isMouseEvent && this.selection.isEmpty() )
|
|
1626
|
+
{
|
|
1627
|
+
this.endSelection();
|
|
1628
|
+
return;
|
|
1553
1629
|
}
|
|
1554
1630
|
|
|
1555
1631
|
this.selection.chars = 0;
|
|
@@ -1692,9 +1768,10 @@ class CodeEditor {
|
|
|
1692
1768
|
const nlines = this.code.lines.length - 1;
|
|
1693
1769
|
this.selection.toX = this.code.lines[ nlines ].length;
|
|
1694
1770
|
this.selection.toY = nlines;
|
|
1695
|
-
this.processSelection( null, true );
|
|
1696
1771
|
this.cursorToPosition( cursor, this.selection.toX );
|
|
1697
1772
|
this.cursorToLine( cursor, this.selection.toY );
|
|
1773
|
+
this.processSelection( null, true );
|
|
1774
|
+
this.hideAutoCompleteBox();
|
|
1698
1775
|
break;
|
|
1699
1776
|
case 'c': // copy
|
|
1700
1777
|
this._copyContent();
|
|
@@ -1704,6 +1781,7 @@ class CodeEditor {
|
|
|
1704
1781
|
this.code.lines.splice( lidx, 0, this.code.lines[ lidx ] );
|
|
1705
1782
|
this.lineDown( cursor );
|
|
1706
1783
|
this.processLines();
|
|
1784
|
+
this.hideAutoCompleteBox();
|
|
1707
1785
|
return;
|
|
1708
1786
|
case 's': // save
|
|
1709
1787
|
e.preventDefault();
|
|
@@ -1714,14 +1792,15 @@ class CodeEditor {
|
|
|
1714
1792
|
return;
|
|
1715
1793
|
case 'x': // cut line
|
|
1716
1794
|
this._cutContent();
|
|
1795
|
+
this.hideAutoCompleteBox();
|
|
1717
1796
|
return;
|
|
1718
1797
|
case 'z': // undo
|
|
1719
1798
|
if(!this.code.undoSteps.length)
|
|
1720
1799
|
return;
|
|
1721
1800
|
const step = this.code.undoSteps.pop();
|
|
1722
1801
|
this.code.lines = step.lines;
|
|
1723
|
-
this.restoreCursor( cursor, step.cursor );
|
|
1724
1802
|
this.processLines();
|
|
1803
|
+
this.restoreCursor( cursor, step.cursor );
|
|
1725
1804
|
return;
|
|
1726
1805
|
}
|
|
1727
1806
|
}
|
|
@@ -1736,6 +1815,7 @@ class CodeEditor {
|
|
|
1736
1815
|
this.lineUp();
|
|
1737
1816
|
this.processLine( lidx - 1 );
|
|
1738
1817
|
this.processLine( lidx );
|
|
1818
|
+
this.hideAutoCompleteBox();
|
|
1739
1819
|
return;
|
|
1740
1820
|
case 'ArrowDown':
|
|
1741
1821
|
if(this.code.lines[ lidx + 1 ] == undefined)
|
|
@@ -1744,6 +1824,7 @@ class CodeEditor {
|
|
|
1744
1824
|
this.lineDown();
|
|
1745
1825
|
this.processLine( lidx );
|
|
1746
1826
|
this.processLine( lidx + 1 );
|
|
1827
|
+
this.hideAutoCompleteBox();
|
|
1747
1828
|
return;
|
|
1748
1829
|
}
|
|
1749
1830
|
}
|
|
@@ -1776,7 +1857,7 @@ class CodeEditor {
|
|
|
1776
1857
|
const enclosableKeys = ["\"", "'", "(", "{"];
|
|
1777
1858
|
if( enclosableKeys.indexOf( key ) > -1 )
|
|
1778
1859
|
{
|
|
1779
|
-
if( this._encloseSelectedWordWithKey(key, lidx, cursor) )
|
|
1860
|
+
if( this._encloseSelectedWordWithKey( key, lidx, cursor ) )
|
|
1780
1861
|
return;
|
|
1781
1862
|
}
|
|
1782
1863
|
|
|
@@ -1785,7 +1866,8 @@ class CodeEditor {
|
|
|
1785
1866
|
|
|
1786
1867
|
if( this.selection )
|
|
1787
1868
|
{
|
|
1788
|
-
this.actions['Backspace'].callback(lidx, cursor, e);
|
|
1869
|
+
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
1870
|
+
lidx = cursor.line;
|
|
1789
1871
|
}
|
|
1790
1872
|
|
|
1791
1873
|
// Append key
|
|
@@ -1882,11 +1964,15 @@ class CodeEditor {
|
|
|
1882
1964
|
let lidx = cursor.line;
|
|
1883
1965
|
let text_to_cut = "";
|
|
1884
1966
|
|
|
1967
|
+
this._addUndoStep( cursor );
|
|
1968
|
+
|
|
1885
1969
|
if( !this.selection ) {
|
|
1886
1970
|
text_to_cut = "\n" + this.code.lines[ cursor.line ];
|
|
1887
1971
|
this.code.lines.splice( lidx, 1 );
|
|
1888
1972
|
this.processLines();
|
|
1889
1973
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
1974
|
+
if( this.code.lines[ lidx ] == undefined )
|
|
1975
|
+
this.lineUp();
|
|
1890
1976
|
}
|
|
1891
1977
|
else {
|
|
1892
1978
|
|
|
@@ -1949,7 +2035,6 @@ class CodeEditor {
|
|
|
1949
2035
|
|
|
1950
2036
|
const start = performance.now();
|
|
1951
2037
|
|
|
1952
|
-
var gutter_html = "";
|
|
1953
2038
|
var code_html = "";
|
|
1954
2039
|
|
|
1955
2040
|
// Reset all lines content
|
|
@@ -1975,16 +2060,11 @@ class CodeEditor {
|
|
|
1975
2060
|
// Process visible lines
|
|
1976
2061
|
for( let i = this.visibleLinesViewport.x; i < this.visibleLinesViewport.y; ++i )
|
|
1977
2062
|
{
|
|
1978
|
-
gutter_html += "<span>" + (i + 1) + "</span>";
|
|
1979
2063
|
code_html += this.processLine( i, true );
|
|
1980
2064
|
}
|
|
1981
2065
|
|
|
1982
2066
|
this.code.innerHTML = code_html;
|
|
1983
2067
|
|
|
1984
|
-
// console.log("RANGE:", this.visibleLinesViewport);
|
|
1985
|
-
// console.log( "Num lines processed:", (this.visibleLinesViewport.y - this.visibleLinesViewport.x), performance.now() - start );
|
|
1986
|
-
// console.log("--------------------------------------------");
|
|
1987
|
-
|
|
1988
2068
|
// Update scroll data
|
|
1989
2069
|
this.codeScroller.scrollTop = lastScrollTop;
|
|
1990
2070
|
this.code.style.top = ( this.visibleLinesViewport.x * this.lineHeight ) + "px";
|
|
@@ -1993,10 +2073,8 @@ class CodeEditor {
|
|
|
1993
2073
|
if( this.selection )
|
|
1994
2074
|
this.processSelection( null, true );
|
|
1995
2075
|
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
delete this._pendingString;
|
|
1999
|
-
|
|
2076
|
+
this._clearTmpVariables();
|
|
2077
|
+
this._setActiveLine();
|
|
2000
2078
|
this.resize();
|
|
2001
2079
|
}
|
|
2002
2080
|
|
|
@@ -2007,7 +2085,10 @@ class CodeEditor {
|
|
|
2007
2085
|
|
|
2008
2086
|
const UPDATE_LINE = ( html ) => {
|
|
2009
2087
|
if( !force ) // Single line update
|
|
2088
|
+
{
|
|
2010
2089
|
this.code.childNodes[ local_line_num ].innerHTML = gutter_line + html;
|
|
2090
|
+
this._clearTmpVariables();
|
|
2091
|
+
}
|
|
2011
2092
|
else // Update all lines at once
|
|
2012
2093
|
return "<pre>" + ( gutter_line + html ) + "</pre>";
|
|
2013
2094
|
}
|
|
@@ -2061,8 +2142,6 @@ class CodeEditor {
|
|
|
2061
2142
|
{
|
|
2062
2143
|
if( token.substr( 0, 2 ) == '/*' )
|
|
2063
2144
|
this._buildingBlockComment = true;
|
|
2064
|
-
if( token.substr( token.length - 2 ) == '*/' )
|
|
2065
|
-
delete this._buildingBlockComment;
|
|
2066
2145
|
}
|
|
2067
2146
|
|
|
2068
2147
|
line_inner_html += this._evaluateToken( token, prev, next, (i == tokensToEvaluate.length - 1) );
|
|
@@ -2073,7 +2152,7 @@ class CodeEditor {
|
|
|
2073
2152
|
|
|
2074
2153
|
_processTokens( tokens, offset = 0 ) {
|
|
2075
2154
|
|
|
2076
|
-
if( this.highlight == 'C++' )
|
|
2155
|
+
if( this.highlight == 'C++' || this.highlight == 'CSS' )
|
|
2077
2156
|
{
|
|
2078
2157
|
var idx = tokens.slice( offset ).findIndex( ( value, index ) => this.isNumber( value ) );
|
|
2079
2158
|
if( idx > -1 )
|
|
@@ -2129,8 +2208,6 @@ class CodeEditor {
|
|
|
2129
2208
|
linestring = ogLine.substring( 0, hasCommentIdx );
|
|
2130
2209
|
}
|
|
2131
2210
|
|
|
2132
|
-
// const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
|
|
2133
|
-
|
|
2134
2211
|
let tokensToEvaluate = []; // store in a temp array so we know prev and next tokens...
|
|
2135
2212
|
|
|
2136
2213
|
const pushToken = function( t ) {
|
|
@@ -2139,7 +2216,7 @@ class CodeEditor {
|
|
|
2139
2216
|
tokensToEvaluate.push( t );
|
|
2140
2217
|
};
|
|
2141
2218
|
|
|
2142
|
-
let iter = linestring.matchAll(/(
|
|
2219
|
+
let iter = linestring.matchAll(/(\*\/|\/\*|::|[\[\](){}<>.,;:*"'%@!/= ])/g);
|
|
2143
2220
|
let subtokens = iter.next();
|
|
2144
2221
|
if( subtokens.value )
|
|
2145
2222
|
{
|
|
@@ -2159,26 +2236,6 @@ class CodeEditor {
|
|
|
2159
2236
|
}
|
|
2160
2237
|
else tokensToEvaluate.push( linestring );
|
|
2161
2238
|
|
|
2162
|
-
// if( usesBlockComments )
|
|
2163
|
-
// {
|
|
2164
|
-
// var block = false;
|
|
2165
|
-
|
|
2166
|
-
// if( t.includes('/*') )
|
|
2167
|
-
// {
|
|
2168
|
-
// const idx = t.indexOf( '/*' );
|
|
2169
|
-
// tokensToEvaluate.push( t.substring( 0, idx ), '/*', t.substring( idx + 2 ) );
|
|
2170
|
-
// block |= true;
|
|
2171
|
-
// }
|
|
2172
|
-
// else if( t.includes('*/') )
|
|
2173
|
-
// {
|
|
2174
|
-
// const idx = t.indexOf( '*/' );
|
|
2175
|
-
// tokensToEvaluate.push( t.substring( 0, idx ), '*/', t.substring( idx + 2 ) );
|
|
2176
|
-
// block |= true;
|
|
2177
|
-
// }
|
|
2178
|
-
|
|
2179
|
-
// if( block ) continue;
|
|
2180
|
-
// }
|
|
2181
|
-
|
|
2182
2239
|
if( hasCommentIdx != undefined )
|
|
2183
2240
|
{
|
|
2184
2241
|
pushToken( ogLine.substring( hasCommentIdx ) );
|
|
@@ -2215,6 +2272,8 @@ class CodeEditor {
|
|
|
2215
2272
|
this._buildingString = token;
|
|
2216
2273
|
}
|
|
2217
2274
|
|
|
2275
|
+
const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
|
|
2276
|
+
|
|
2218
2277
|
if(token == ' ')
|
|
2219
2278
|
{
|
|
2220
2279
|
if( this._buildingString != undefined )
|
|
@@ -2227,7 +2286,6 @@ class CodeEditor {
|
|
|
2227
2286
|
else
|
|
2228
2287
|
{
|
|
2229
2288
|
const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
2230
|
-
const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
|
|
2231
2289
|
|
|
2232
2290
|
let token_classname = "";
|
|
2233
2291
|
let discardToken = false;
|
|
@@ -2253,12 +2311,6 @@ class CodeEditor {
|
|
|
2253
2311
|
else if( token.substr( 0, singleLineCommentToken.length ) == singleLineCommentToken )
|
|
2254
2312
|
token_classname = "cm-com";
|
|
2255
2313
|
|
|
2256
|
-
else if( usesBlockComments && token.substr( 0, 2 ) == '/*' )
|
|
2257
|
-
token_classname = "cm-com";
|
|
2258
|
-
|
|
2259
|
-
else if( usesBlockComments && token.substr( token.length - 2 ) == '*/' )
|
|
2260
|
-
token_classname = "cm-com";
|
|
2261
|
-
|
|
2262
2314
|
else if( this.isNumber( token ) || this.isNumber( token.replace(/[px]|[em]|%/g,'') ) )
|
|
2263
2315
|
token_classname = "cm-dec";
|
|
2264
2316
|
|
|
@@ -2290,6 +2342,11 @@ class CodeEditor {
|
|
|
2290
2342
|
token_classname = "cm-mtd";
|
|
2291
2343
|
|
|
2292
2344
|
|
|
2345
|
+
if( usesBlockComments && this._buildingBlockComment && token.substr( 0, 2 ) == '*/' )
|
|
2346
|
+
{
|
|
2347
|
+
delete this._buildingBlockComment;
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2293
2350
|
// We finished constructing a string
|
|
2294
2351
|
if( this._buildingString && ( this._stringEnded || isLastToken ) )
|
|
2295
2352
|
{
|
|
@@ -2345,6 +2402,12 @@ class CodeEditor {
|
|
|
2345
2402
|
else if( token.lastChar == 'u' )
|
|
2346
2403
|
return !(token.includes('.')) && this.isNumber( token.substring(0, token.length - 1) );
|
|
2347
2404
|
}
|
|
2405
|
+
|
|
2406
|
+
else if(this.highlight == 'CSS')
|
|
2407
|
+
{
|
|
2408
|
+
if( token.lastChar == '%' )
|
|
2409
|
+
return this.isNumber( token.substring(0, token.length - 1) )
|
|
2410
|
+
}
|
|
2348
2411
|
|
|
2349
2412
|
return token.length && token != ' ' && !Number.isNaN(+token);
|
|
2350
2413
|
}
|
|
@@ -2422,31 +2485,43 @@ class CodeEditor {
|
|
|
2422
2485
|
lineUp( cursor, resetLeft ) {
|
|
2423
2486
|
|
|
2424
2487
|
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2488
|
+
|
|
2489
|
+
if( this.code.lines[ cursor.line - 1 ] == undefined )
|
|
2490
|
+
return false;
|
|
2491
|
+
|
|
2425
2492
|
cursor.line--;
|
|
2426
2493
|
cursor.line = Math.max( 0, cursor.line );
|
|
2427
2494
|
this.cursorToTop( cursor, resetLeft );
|
|
2495
|
+
|
|
2496
|
+
return true;
|
|
2428
2497
|
}
|
|
2429
2498
|
|
|
2430
2499
|
lineDown( cursor, resetLeft ) {
|
|
2431
2500
|
|
|
2432
2501
|
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2502
|
+
|
|
2503
|
+
if( this.code.lines[ cursor.line + 1 ] == undefined )
|
|
2504
|
+
return false;
|
|
2505
|
+
|
|
2433
2506
|
cursor.line++;
|
|
2434
2507
|
this.cursorToBottom( cursor, resetLeft );
|
|
2508
|
+
|
|
2509
|
+
return true;
|
|
2435
2510
|
}
|
|
2436
2511
|
|
|
2437
2512
|
restartBlink() {
|
|
2438
2513
|
|
|
2439
2514
|
if( !this.code ) return;
|
|
2440
2515
|
|
|
2441
|
-
clearInterval(this.blinker);
|
|
2442
|
-
this.cursors.classList.add('show');
|
|
2516
|
+
clearInterval( this.blinker );
|
|
2517
|
+
this.cursors.classList.add( 'show' );
|
|
2443
2518
|
|
|
2444
|
-
if
|
|
2519
|
+
if( this.cursorBlinkRate > 0 )
|
|
2445
2520
|
this.blinker = setInterval(() => {
|
|
2446
|
-
this.cursors.classList.toggle('show');
|
|
2521
|
+
this.cursors.classList.toggle( 'show' );
|
|
2447
2522
|
}, this.cursorBlinkRate);
|
|
2448
|
-
else if
|
|
2449
|
-
this.cursors.classList.remove('show');
|
|
2523
|
+
else if( this.cursorBlinkRate < 0 )
|
|
2524
|
+
this.cursors.classList.remove( 'show' );
|
|
2450
2525
|
}
|
|
2451
2526
|
|
|
2452
2527
|
startSelection( cursor ) {
|
|
@@ -2455,10 +2530,10 @@ class CodeEditor {
|
|
|
2455
2530
|
this.selections.innerHTML = "";
|
|
2456
2531
|
|
|
2457
2532
|
// Show elements
|
|
2458
|
-
this.selections.classList.add('show');
|
|
2533
|
+
this.selections.classList.add( 'show' );
|
|
2459
2534
|
|
|
2460
2535
|
// Create new selection instance
|
|
2461
|
-
this.selection = new CodeSelection(this, cursor.position, cursor.line);
|
|
2536
|
+
this.selection = new CodeSelection( this, cursor.position, cursor.line );
|
|
2462
2537
|
}
|
|
2463
2538
|
|
|
2464
2539
|
deleteSelection( cursor ) {
|
|
@@ -2475,7 +2550,7 @@ class CodeEditor {
|
|
|
2475
2550
|
|
|
2476
2551
|
// Get linear start index
|
|
2477
2552
|
let index = 0;
|
|
2478
|
-
for(let i = 0; i <= this.selection.fromY; i++)
|
|
2553
|
+
for( let i = 0; i <= this.selection.fromY; i++ )
|
|
2479
2554
|
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[ i ].length);
|
|
2480
2555
|
|
|
2481
2556
|
index += this.selection.fromY * separator.length;
|
|
@@ -2488,19 +2563,17 @@ class CodeEditor {
|
|
|
2488
2563
|
|
|
2489
2564
|
this.cursorToLine( cursor, this.selection.fromY, true );
|
|
2490
2565
|
this.cursorToPosition( cursor, this.selection.fromX );
|
|
2491
|
-
|
|
2492
2566
|
this.endSelection();
|
|
2493
|
-
|
|
2494
2567
|
this.processLines();
|
|
2495
|
-
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
2496
2568
|
}
|
|
2497
2569
|
|
|
2498
2570
|
endSelection() {
|
|
2499
2571
|
|
|
2500
|
-
this.selections.classList.remove('show');
|
|
2572
|
+
this.selections.classList.remove( 'show' );
|
|
2501
2573
|
this.selections.innerHTML = "";
|
|
2502
2574
|
delete this.selection;
|
|
2503
2575
|
delete this._tripleClickSelection;
|
|
2576
|
+
delete this._lastSelectionKeyDir;
|
|
2504
2577
|
}
|
|
2505
2578
|
|
|
2506
2579
|
cursorToRight( key, cursor ) {
|
|
@@ -2512,7 +2585,6 @@ class CodeEditor {
|
|
|
2512
2585
|
cursor.position++;
|
|
2513
2586
|
|
|
2514
2587
|
this.restartBlink();
|
|
2515
|
-
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
2516
2588
|
|
|
2517
2589
|
// Add horizontal scroll
|
|
2518
2590
|
|
|
@@ -2525,7 +2597,7 @@ class CodeEditor {
|
|
|
2525
2597
|
|
|
2526
2598
|
cursorToLeft( key, cursor ) {
|
|
2527
2599
|
|
|
2528
|
-
if(!key) return;
|
|
2600
|
+
if( !key ) return;
|
|
2529
2601
|
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2530
2602
|
cursor._left -= this.charWidth;
|
|
2531
2603
|
cursor._left = Math.max( cursor._left, 0 );
|
|
@@ -2533,7 +2605,6 @@ class CodeEditor {
|
|
|
2533
2605
|
cursor.position--;
|
|
2534
2606
|
cursor.position = Math.max( cursor.position, 0 );
|
|
2535
2607
|
this.restartBlink();
|
|
2536
|
-
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
2537
2608
|
|
|
2538
2609
|
// Add horizontal scroll
|
|
2539
2610
|
|
|
@@ -2555,8 +2626,6 @@ class CodeEditor {
|
|
|
2555
2626
|
if(resetLeft)
|
|
2556
2627
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
2557
2628
|
|
|
2558
|
-
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
2559
|
-
|
|
2560
2629
|
doAsync(() => {
|
|
2561
2630
|
var first_line = ( this.getScrollTop() / this.lineHeight )|0;
|
|
2562
2631
|
if( (cursor.line - 1) < first_line )
|
|
@@ -2574,8 +2643,6 @@ class CodeEditor {
|
|
|
2574
2643
|
if(resetLeft)
|
|
2575
2644
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
2576
2645
|
|
|
2577
|
-
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
2578
|
-
|
|
2579
2646
|
doAsync(() => {
|
|
2580
2647
|
var last_line = ( ( this.codeScroller.offsetHeight + this.getScrollTop() ) / this.lineHeight )|0;
|
|
2581
2648
|
if( cursor.line >= last_line )
|
|
@@ -2585,9 +2652,10 @@ class CodeEditor {
|
|
|
2585
2652
|
|
|
2586
2653
|
cursorToString( cursor, text, reverse ) {
|
|
2587
2654
|
|
|
2655
|
+
if( !text.length ) return;
|
|
2588
2656
|
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2589
2657
|
for( let char of text )
|
|
2590
|
-
reverse ? this.cursorToLeft(char) : this.cursorToRight(char);
|
|
2658
|
+
reverse ? this.cursorToLeft( char ) : this.cursorToRight( char );
|
|
2591
2659
|
}
|
|
2592
2660
|
|
|
2593
2661
|
cursorToPosition( cursor, position ) {
|
|
@@ -2602,7 +2670,7 @@ class CodeEditor {
|
|
|
2602
2670
|
cursor.line = line;
|
|
2603
2671
|
cursor._top = this.lineHeight * line;
|
|
2604
2672
|
cursor.style.top = cursor._top + "px";
|
|
2605
|
-
if(resetLeft) this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
2673
|
+
if( resetLeft ) this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
2606
2674
|
}
|
|
2607
2675
|
|
|
2608
2676
|
saveCursor( cursor, state = {} ) {
|
|
@@ -2611,7 +2679,7 @@ class CodeEditor {
|
|
|
2611
2679
|
state.top = cursor._top;
|
|
2612
2680
|
state.left = cursor._left;
|
|
2613
2681
|
state.line = cursor.line;
|
|
2614
|
-
state.
|
|
2682
|
+
state.position = cursor.position;
|
|
2615
2683
|
return state;
|
|
2616
2684
|
}
|
|
2617
2685
|
|
|
@@ -2622,9 +2690,9 @@ class CodeEditor {
|
|
|
2622
2690
|
cursor.position = state.position ?? 0;
|
|
2623
2691
|
|
|
2624
2692
|
cursor._left = state.left ?? 0;
|
|
2625
|
-
cursor.style.left = "calc(" + (cursor._left - this.getScrollLeft()) + "px + " + this.xPadding + ")";
|
|
2693
|
+
cursor.style.left = "calc(" + ( cursor._left - this.getScrollLeft() ) + "px + " + this.xPadding + ")";
|
|
2626
2694
|
cursor._top = state.top ?? 0;
|
|
2627
|
-
cursor.style.top = "calc(" + (cursor._top - this.getScrollTop()) + "px)";
|
|
2695
|
+
cursor.style.top = "calc(" + ( cursor._top - this.getScrollTop() ) + "px)";
|
|
2628
2696
|
}
|
|
2629
2697
|
|
|
2630
2698
|
resetCursorPos( flag, cursor ) {
|
|
@@ -2634,14 +2702,14 @@ class CodeEditor {
|
|
|
2634
2702
|
if( flag & CodeEditor.CURSOR_LEFT )
|
|
2635
2703
|
{
|
|
2636
2704
|
cursor._left = 0;
|
|
2637
|
-
cursor.style.left = "calc(" + (-this.getScrollLeft()) + "px + " + this.xPadding + ")";
|
|
2705
|
+
cursor.style.left = "calc(" + ( -this.getScrollLeft() ) + "px + " + this.xPadding + ")";
|
|
2638
2706
|
cursor.position = 0;
|
|
2639
2707
|
}
|
|
2640
2708
|
|
|
2641
2709
|
if( flag & CodeEditor.CURSOR_TOP )
|
|
2642
2710
|
{
|
|
2643
2711
|
cursor._top = 0;
|
|
2644
|
-
cursor.style.top = (
|
|
2712
|
+
cursor.style.top = ( -this.getScrollTop() ) + "px";
|
|
2645
2713
|
cursor.line = 0;
|
|
2646
2714
|
}
|
|
2647
2715
|
}
|
|
@@ -2649,14 +2717,14 @@ class CodeEditor {
|
|
|
2649
2717
|
addSpaceTabs(n) {
|
|
2650
2718
|
|
|
2651
2719
|
for( var i = 0; i < n; ++i ) {
|
|
2652
|
-
this.actions['Tab'].callback();
|
|
2720
|
+
this.actions[ 'Tab' ].callback();
|
|
2653
2721
|
}
|
|
2654
2722
|
}
|
|
2655
2723
|
|
|
2656
2724
|
addSpaces(n) {
|
|
2657
2725
|
|
|
2658
2726
|
for( var i = 0; i < n; ++i ) {
|
|
2659
|
-
this.root.dispatchEvent(new CustomEvent('keydown', {'detail': {
|
|
2727
|
+
this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
|
|
2660
2728
|
skip_undo: true,
|
|
2661
2729
|
key: ' '
|
|
2662
2730
|
}}));
|
|
@@ -2665,26 +2733,26 @@ class CodeEditor {
|
|
|
2665
2733
|
|
|
2666
2734
|
getScrollLeft() {
|
|
2667
2735
|
|
|
2668
|
-
if(!this.codeScroller) return 0;
|
|
2736
|
+
if( !this.codeScroller ) return 0;
|
|
2669
2737
|
return this.codeScroller.scrollLeft;
|
|
2670
2738
|
}
|
|
2671
2739
|
|
|
2672
2740
|
getScrollTop() {
|
|
2673
2741
|
|
|
2674
|
-
if(!this.codeScroller) return 0;
|
|
2742
|
+
if( !this.codeScroller ) return 0;
|
|
2675
2743
|
return this.codeScroller.scrollTop;
|
|
2676
2744
|
}
|
|
2677
2745
|
|
|
2678
2746
|
setScrollLeft( value ) {
|
|
2679
2747
|
|
|
2680
|
-
if(!this.codeScroller) return;
|
|
2748
|
+
if( !this.codeScroller ) return;
|
|
2681
2749
|
this.codeScroller.scrollLeft = value;
|
|
2682
2750
|
this.setScrollBarValue( 'horizontal', 0 );
|
|
2683
2751
|
}
|
|
2684
2752
|
|
|
2685
2753
|
setScrollTop( value ) {
|
|
2686
2754
|
|
|
2687
|
-
if(!this.codeScroller) return;
|
|
2755
|
+
if( !this.codeScroller ) return;
|
|
2688
2756
|
this.codeScroller.scrollTop = value;
|
|
2689
2757
|
this.setScrollBarValue( 'vertical' );
|
|
2690
2758
|
}
|
|
@@ -2816,46 +2884,70 @@ class CodeEditor {
|
|
|
2816
2884
|
getCharAtPos( cursor, offset = 0 ) {
|
|
2817
2885
|
|
|
2818
2886
|
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2819
|
-
return this.code.lines[ cursor.line ][cursor.position + offset];
|
|
2887
|
+
return this.code.lines[ cursor.line ][ cursor.position + offset ];
|
|
2820
2888
|
}
|
|
2821
2889
|
|
|
2822
|
-
getWordAtPos( cursor,
|
|
2890
|
+
getWordAtPos( cursor, loffset = 0, roffset ) {
|
|
2823
2891
|
|
|
2892
|
+
roffset = roffset ?? loffset;
|
|
2824
2893
|
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2825
2894
|
const col = cursor.line;
|
|
2826
|
-
const words = this.code.lines[col];
|
|
2895
|
+
const words = this.code.lines[ col ];
|
|
2827
2896
|
|
|
2828
|
-
const is_char =
|
|
2829
|
-
const exceptions = ['_', '#', '!'];
|
|
2830
|
-
const code = char.charCodeAt(0);
|
|
2831
|
-
return (exceptions.indexOf(char) > - 1) || (code > 47 && code < 58) || (code > 64 && code < 91) || (code > 96 && code < 123);
|
|
2897
|
+
const is_char = char => {
|
|
2898
|
+
const exceptions = [ '_', '#', '!' ];
|
|
2899
|
+
const code = char.charCodeAt( 0 );
|
|
2900
|
+
return (exceptions.indexOf( char ) > - 1) || (code > 47 && code < 58) || (code > 64 && code < 91) || (code > 96 && code < 123);
|
|
2832
2901
|
}
|
|
2833
2902
|
|
|
2834
|
-
let
|
|
2903
|
+
let from = cursor.position + roffset;
|
|
2904
|
+
let to = cursor.position + loffset;
|
|
2905
|
+
|
|
2906
|
+
// Check left ...
|
|
2907
|
+
|
|
2908
|
+
while( words[ from ] && is_char( words[ from ] ) )
|
|
2909
|
+
from--;
|
|
2835
2910
|
|
|
2836
|
-
|
|
2837
|
-
it--;
|
|
2911
|
+
from++;
|
|
2838
2912
|
|
|
2839
|
-
|
|
2840
|
-
it = cursor.position + offset;
|
|
2913
|
+
// Check right ...
|
|
2841
2914
|
|
|
2842
|
-
while( words[
|
|
2843
|
-
|
|
2915
|
+
while( words[ to ] && is_char( words[ to ] ) )
|
|
2916
|
+
to++;
|
|
2844
2917
|
|
|
2845
|
-
|
|
2918
|
+
// Skip spaces ...
|
|
2846
2919
|
|
|
2847
|
-
|
|
2920
|
+
let word = words.substring( from, to );
|
|
2921
|
+
if( word == ' ' )
|
|
2922
|
+
{
|
|
2923
|
+
if( loffset < 0 )
|
|
2924
|
+
{
|
|
2925
|
+
while( words[ from - 1 ] != undefined && words[ from - 1 ] == ' ' )
|
|
2926
|
+
from--;
|
|
2927
|
+
to++;
|
|
2928
|
+
word = words.substring( from, to + 1 );
|
|
2929
|
+
}
|
|
2930
|
+
else
|
|
2931
|
+
{
|
|
2932
|
+
while( words[ to ] != undefined && words[ to ] == ' ' )
|
|
2933
|
+
to++;
|
|
2934
|
+
from--;
|
|
2935
|
+
word = words.substring( from, to );
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
return [ word, from, to ];
|
|
2848
2940
|
}
|
|
2849
2941
|
|
|
2850
|
-
|
|
2942
|
+
_measureChar( char = "a", get_bb = false ) {
|
|
2851
2943
|
|
|
2852
|
-
var test = document.createElement("pre");
|
|
2944
|
+
var test = document.createElement( "pre" );
|
|
2853
2945
|
test.className = "codechar";
|
|
2854
2946
|
test.innerHTML = char;
|
|
2855
|
-
document.body.appendChild(test);
|
|
2947
|
+
document.body.appendChild( test );
|
|
2856
2948
|
var rect = test.getBoundingClientRect();
|
|
2857
2949
|
deleteElement( test );
|
|
2858
|
-
const bb = [Math.floor(rect.width), Math.floor(rect.height)];
|
|
2950
|
+
const bb = [ Math.floor( rect.width ), Math.floor( rect.height ) ];
|
|
2859
2951
|
return get_bb ? bb : bb[ 0 ];
|
|
2860
2952
|
}
|
|
2861
2953
|
|
|
@@ -2866,44 +2958,46 @@ class CodeEditor {
|
|
|
2866
2958
|
|
|
2867
2959
|
runScript( code ) {
|
|
2868
2960
|
|
|
2869
|
-
var script = document.createElement('script');
|
|
2961
|
+
var script = document.createElement( 'script' );
|
|
2870
2962
|
script.type = 'module';
|
|
2871
2963
|
script.innerHTML = code;
|
|
2872
2964
|
// script.src = url[ i ] + ( version ? "?version=" + version : "" );
|
|
2873
2965
|
script.async = false;
|
|
2874
2966
|
// script.onload = function(e) { };
|
|
2875
|
-
document.getElementsByTagName('head')[ 0 ].appendChild(script);
|
|
2967
|
+
document.getElementsByTagName( 'head' )[ 0 ].appendChild( script );
|
|
2876
2968
|
}
|
|
2877
2969
|
|
|
2878
2970
|
toJSONFormat( text ) {
|
|
2879
2971
|
|
|
2880
|
-
let params = text.split(
|
|
2972
|
+
let params = text.split( ':' );
|
|
2881
2973
|
|
|
2882
2974
|
for(let i = 0; i < params.length; i++) {
|
|
2883
|
-
let key = params[ i ].split(',');
|
|
2884
|
-
if(key.length > 1)
|
|
2885
|
-
|
|
2975
|
+
let key = params[ i ].split( ',' );
|
|
2976
|
+
if( key.length > 1 )
|
|
2977
|
+
{
|
|
2978
|
+
if( key[ key.length - 1 ].includes( ']' ))
|
|
2886
2979
|
continue;
|
|
2887
|
-
key = key[key.length-1];
|
|
2980
|
+
key = key[ key.length - 1 ];
|
|
2888
2981
|
}
|
|
2889
|
-
else if(key[ 0 ].includes(
|
|
2982
|
+
else if( key[ 0 ].includes( '}' ) )
|
|
2890
2983
|
continue;
|
|
2891
2984
|
else
|
|
2892
2985
|
key = key[ 0 ];
|
|
2893
|
-
key = key.replaceAll(/[{}\n\r]/g,
|
|
2894
|
-
if(key[ 0 ] != '"' && key[key.length - 1] != '"')
|
|
2895
|
-
|
|
2986
|
+
key = key.replaceAll( /[{}\n\r]/g, '' ).replaceAll( ' ', '' )
|
|
2987
|
+
if( key[ 0 ] != '"' && key[ key.length - 1 ] != '"')
|
|
2988
|
+
{
|
|
2989
|
+
params[ i ] = params[ i ].replace( key, '"' + key + '"' );
|
|
2896
2990
|
}
|
|
2897
2991
|
}
|
|
2898
2992
|
|
|
2899
|
-
text = params.join(':');
|
|
2993
|
+
text = params.join( ':' );
|
|
2900
2994
|
|
|
2901
2995
|
try {
|
|
2902
|
-
let json = JSON.parse(text);
|
|
2903
|
-
return JSON.stringify(json, undefined, 4);
|
|
2996
|
+
let json = JSON.parse( text );
|
|
2997
|
+
return JSON.stringify( json, undefined, 4 );
|
|
2904
2998
|
}
|
|
2905
|
-
catch(e) {
|
|
2906
|
-
alert("Invalid JSON format");
|
|
2999
|
+
catch( e ) {
|
|
3000
|
+
alert( "Invalid JSON format" );
|
|
2907
3001
|
return;
|
|
2908
3002
|
}
|
|
2909
3003
|
}
|
|
@@ -2943,10 +3037,10 @@ class CodeEditor {
|
|
|
2943
3037
|
if( !s.toLowerCase().includes(word.toLowerCase()) )
|
|
2944
3038
|
continue;
|
|
2945
3039
|
|
|
2946
|
-
var pre = document.createElement('pre');
|
|
3040
|
+
var pre = document.createElement( 'pre' );
|
|
2947
3041
|
this.autocomplete.appendChild(pre);
|
|
2948
3042
|
|
|
2949
|
-
var icon = document.createElement('a');
|
|
3043
|
+
var icon = document.createElement( 'a' );
|
|
2950
3044
|
|
|
2951
3045
|
if( this._mustHightlightWord( s, this.utils ) )
|
|
2952
3046
|
icon.className = "fa fa-cube";
|
|
@@ -2999,7 +3093,7 @@ class CodeEditor {
|
|
|
2999
3093
|
hideAutoCompleteBox() {
|
|
3000
3094
|
|
|
3001
3095
|
this.isAutoCompleteActive = false;
|
|
3002
|
-
this.autocomplete.classList.remove('show');
|
|
3096
|
+
this.autocomplete.classList.remove( 'show' );
|
|
3003
3097
|
}
|
|
3004
3098
|
|
|
3005
3099
|
autoCompleteWord( cursor, suggestion ) {
|
|
@@ -3007,7 +3101,7 @@ class CodeEditor {
|
|
|
3007
3101
|
if( !this.isAutoCompleteActive )
|
|
3008
3102
|
return;
|
|
3009
3103
|
|
|
3010
|
-
let [suggestedWord, idx] = this.
|
|
3104
|
+
let [suggestedWord, idx] = this._getSelectedAutoComplete();
|
|
3011
3105
|
suggestedWord = suggestion ?? suggestedWord;
|
|
3012
3106
|
|
|
3013
3107
|
const [word, start, end] = this.getWordAtPos( cursor, -1 );
|
|
@@ -3022,7 +3116,7 @@ class CodeEditor {
|
|
|
3022
3116
|
this.hideAutoCompleteBox();
|
|
3023
3117
|
}
|
|
3024
3118
|
|
|
3025
|
-
|
|
3119
|
+
_getSelectedAutoComplete() {
|
|
3026
3120
|
|
|
3027
3121
|
if( !this.isAutoCompleteActive )
|
|
3028
3122
|
return;
|
|
@@ -3041,12 +3135,12 @@ class CodeEditor {
|
|
|
3041
3135
|
}
|
|
3042
3136
|
}
|
|
3043
3137
|
|
|
3044
|
-
|
|
3138
|
+
_moveArrowSelectedAutoComplete( dir ) {
|
|
3045
3139
|
|
|
3046
3140
|
if( !this.isAutoCompleteActive )
|
|
3047
3141
|
return;
|
|
3048
3142
|
|
|
3049
|
-
const [word, idx] = this.
|
|
3143
|
+
const [word, idx] = this._getSelectedAutoComplete();
|
|
3050
3144
|
const offset = dir == 'down' ? 1 : -1;
|
|
3051
3145
|
|
|
3052
3146
|
if( dir == 'down' ) {
|
|
@@ -3055,12 +3149,51 @@ class CodeEditor {
|
|
|
3055
3149
|
if( (idx + offset) < 0 ) return;
|
|
3056
3150
|
}
|
|
3057
3151
|
|
|
3058
|
-
this.autocomplete.scrollTop += offset *
|
|
3152
|
+
this.autocomplete.scrollTop += offset * 20;
|
|
3059
3153
|
|
|
3060
3154
|
// Remove selected from the current word and add it to the next one
|
|
3061
3155
|
this.autocomplete.childNodes[ idx ].classList.remove('selected');
|
|
3062
3156
|
this.autocomplete.childNodes[ idx + offset ].classList.add('selected');
|
|
3063
3157
|
}
|
|
3158
|
+
|
|
3159
|
+
_updateDataInfoPanel( signal, value ) {
|
|
3160
|
+
|
|
3161
|
+
if( !this.skipCodeInfo )
|
|
3162
|
+
{
|
|
3163
|
+
LX.emit( signal, value );
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
_setActiveLine( number ) {
|
|
3168
|
+
|
|
3169
|
+
number = number ?? this.state.activeLine;
|
|
3170
|
+
|
|
3171
|
+
this._updateDataInfoPanel( "@cursor-line", "Ln " + ( number + 1 ) );
|
|
3172
|
+
|
|
3173
|
+
const old_local = this.toLocalLine( this.state.activeLine );
|
|
3174
|
+
let line = this.code.childNodes[ old_local ];
|
|
3175
|
+
|
|
3176
|
+
if( !line )
|
|
3177
|
+
return;
|
|
3178
|
+
|
|
3179
|
+
line.classList.remove( 'active-line' );
|
|
3180
|
+
|
|
3181
|
+
// Set new active
|
|
3182
|
+
{
|
|
3183
|
+
this.state.activeLine = number;
|
|
3184
|
+
|
|
3185
|
+
const new_local = this.toLocalLine( number );
|
|
3186
|
+
line = this.code.childNodes[ new_local ];
|
|
3187
|
+
if( line ) line.classList.add( 'active-line' );
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3191
|
+
_clearTmpVariables() {
|
|
3192
|
+
|
|
3193
|
+
delete this._buildingString;
|
|
3194
|
+
delete this._pendingString;
|
|
3195
|
+
delete this._buildingBlockComment;
|
|
3196
|
+
}
|
|
3064
3197
|
}
|
|
3065
3198
|
|
|
3066
3199
|
LX.CodeEditor = CodeEditor;
|