lexgui 0.1.25 → 0.1.27
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 +612 -306
- package/build/lexgui.js +5 -2
- package/build/lexgui.module.js +5 -2
- package/changelog.md +21 -0
- package/package.json +1 -1
|
@@ -30,6 +30,10 @@ function firstNonspaceIndex( str ) {
|
|
|
30
30
|
return index < str.length ? index : -1;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function strReverse( str ) {
|
|
34
|
+
return str.split( "" ).reverse().join( "" );
|
|
35
|
+
}
|
|
36
|
+
|
|
33
37
|
function indexOfFrom( str, reg, from, reverse ) {
|
|
34
38
|
|
|
35
39
|
from = from ?? 0;
|
|
@@ -64,16 +68,17 @@ function doAsync( fn, ms ) {
|
|
|
64
68
|
|
|
65
69
|
class CodeSelection {
|
|
66
70
|
|
|
67
|
-
constructor( editor,
|
|
71
|
+
constructor( editor, cursor, className = "lexcodeselection" ) {
|
|
68
72
|
|
|
69
73
|
this.editor = editor;
|
|
70
|
-
this.
|
|
74
|
+
this.cursor = cursor;
|
|
71
75
|
this.className = className;
|
|
76
|
+
this.chars = 0;
|
|
72
77
|
|
|
73
|
-
this.fromX =
|
|
74
|
-
this.toX =
|
|
75
|
-
this.fromY =
|
|
76
|
-
this.toY =
|
|
78
|
+
this.fromX = cursor.position;
|
|
79
|
+
this.toX = cursor.position;
|
|
80
|
+
this.fromY = cursor.line;
|
|
81
|
+
this.toY = cursor.line;
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
sameLine() {
|
|
@@ -101,7 +106,7 @@ class CodeSelection {
|
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
selectInline( x, y, width ) {
|
|
109
|
+
selectInline( cursor, x, y, width, isSearchResult ) {
|
|
105
110
|
|
|
106
111
|
this.chars = width / this.editor.charWidth;
|
|
107
112
|
this.fromX = x;
|
|
@@ -116,11 +121,45 @@ class CodeSelection {
|
|
|
116
121
|
domEl._left = x * this.editor.charWidth;
|
|
117
122
|
domEl.style.left = "calc(" + domEl._left + "px + " + this.editor.xPadding + ")";
|
|
118
123
|
domEl.style.width = width + "px";
|
|
119
|
-
|
|
124
|
+
|
|
125
|
+
if( isSearchResult )
|
|
126
|
+
{
|
|
127
|
+
this.editor.searchResultSelections.appendChild( domEl );
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
{
|
|
131
|
+
this.editor.selections[ cursor.name ].appendChild( domEl );
|
|
132
|
+
}
|
|
120
133
|
|
|
121
134
|
// Hide active line background
|
|
122
135
|
this.editor._hideActiveLine();
|
|
123
136
|
}
|
|
137
|
+
|
|
138
|
+
save() {
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
fromX: this.fromX,
|
|
142
|
+
fromY: this.fromY,
|
|
143
|
+
toX: this.toX,
|
|
144
|
+
toY: this.toY
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
load( data ) {
|
|
149
|
+
|
|
150
|
+
this.fromX = data.fromX;
|
|
151
|
+
this.fromY = data.fromY;
|
|
152
|
+
this.toX = data.toX;
|
|
153
|
+
this.toY = data.toY;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
getText() {
|
|
157
|
+
|
|
158
|
+
if( !this.editor.code || !this.sameLine() )
|
|
159
|
+
return null;
|
|
160
|
+
|
|
161
|
+
return this.editor.code.lines[ this.fromY ].substring( this.fromX, this.toX );
|
|
162
|
+
}
|
|
124
163
|
};
|
|
125
164
|
|
|
126
165
|
class ScrollBar {
|
|
@@ -141,6 +180,7 @@ class ScrollBar {
|
|
|
141
180
|
this.thumb = document.createElement( 'div' );
|
|
142
181
|
this.thumb._top = 0;
|
|
143
182
|
this.thumb._left = 0;
|
|
183
|
+
|
|
144
184
|
this.root.appendChild( this.thumb );
|
|
145
185
|
|
|
146
186
|
this.thumb.addEventListener( "mousedown", inner_mousedown );
|
|
@@ -152,8 +192,8 @@ class ScrollBar {
|
|
|
152
192
|
function inner_mousedown( e )
|
|
153
193
|
{
|
|
154
194
|
var doc = editor.root.ownerDocument;
|
|
155
|
-
doc.addEventListener( "mousemove",inner_mousemove );
|
|
156
|
-
doc.addEventListener( "mouseup",inner_mouseup );
|
|
195
|
+
doc.addEventListener( "mousemove", inner_mousemove );
|
|
196
|
+
doc.addEventListener( "mouseup", inner_mouseup );
|
|
157
197
|
that.lastPosition.set( e.x, e.y );
|
|
158
198
|
e.stopPropagation();
|
|
159
199
|
e.preventDefault();
|
|
@@ -325,25 +365,19 @@ class CodeEditor {
|
|
|
325
365
|
this.cursors.className = 'cursors';
|
|
326
366
|
this.tabs.area.attach( this.cursors );
|
|
327
367
|
|
|
328
|
-
this.
|
|
329
|
-
this.
|
|
330
|
-
this.
|
|
368
|
+
this.searchResultSelections = document.createElement( 'div' );
|
|
369
|
+
this.searchResultSelections.id = 'search-selections';
|
|
370
|
+
this.searchResultSelections.className = 'selections';
|
|
371
|
+
this.tabs.area.attach( this.searchResultSelections );
|
|
372
|
+
|
|
373
|
+
// Store here selections per cursor
|
|
374
|
+
this.selections = {};
|
|
331
375
|
|
|
332
376
|
// Css char synchronization
|
|
333
377
|
this.xPadding = "48px";
|
|
334
378
|
|
|
335
379
|
// Add main cursor
|
|
336
|
-
|
|
337
|
-
this._addCursor( 0, 0, true, true );
|
|
338
|
-
|
|
339
|
-
Object.defineProperty( this, 'line', {
|
|
340
|
-
get: (v) => { return this._getCurrentCursor().line }
|
|
341
|
-
} );
|
|
342
|
-
|
|
343
|
-
Object.defineProperty( this, 'position', {
|
|
344
|
-
get: (v) => { return this._getCurrentCursor().position }
|
|
345
|
-
} );
|
|
346
|
-
}
|
|
380
|
+
this._addCursor( 0, 0, true, true );
|
|
347
381
|
|
|
348
382
|
// Scroll stuff
|
|
349
383
|
{
|
|
@@ -556,18 +590,27 @@ class CodeEditor {
|
|
|
556
590
|
|
|
557
591
|
// Convert reserved word arrays to maps so we can search tokens faster
|
|
558
592
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
593
|
+
if( !CodeEditor._staticReady )
|
|
594
|
+
{
|
|
595
|
+
for( let lang in CodeEditor.keywords ) CodeEditor.keywords[lang] = CodeEditor.keywords[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
596
|
+
for( let lang in CodeEditor.utils ) CodeEditor.utils[lang] = CodeEditor.utils[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
597
|
+
for( let lang in CodeEditor.types ) CodeEditor.types[lang] = CodeEditor.types[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
598
|
+
for( let lang in CodeEditor.builtin ) CodeEditor.builtin[lang] = CodeEditor.builtin[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
599
|
+
for( let lang in CodeEditor.statementsAndDeclarations ) CodeEditor.statementsAndDeclarations[lang] = CodeEditor.statementsAndDeclarations[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
600
|
+
for( let lang in CodeEditor.symbols ) CodeEditor.symbols[lang] = CodeEditor.symbols[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
601
|
+
|
|
602
|
+
CodeEditor._staticReady = true;
|
|
603
|
+
}
|
|
565
604
|
|
|
566
605
|
// Action keys
|
|
567
606
|
|
|
568
607
|
this.action( 'Escape', false, ( ln, cursor, e ) => {
|
|
569
|
-
this.hideAutoCompleteBox()
|
|
570
|
-
|
|
608
|
+
if( this.hideAutoCompleteBox() )
|
|
609
|
+
return;
|
|
610
|
+
if( this.hideSearchBox() )
|
|
611
|
+
return;
|
|
612
|
+
// Remove selections and cursors
|
|
613
|
+
this.endSelection();
|
|
571
614
|
this._removeSecondaryCursors();
|
|
572
615
|
});
|
|
573
616
|
|
|
@@ -575,7 +618,7 @@ class CodeEditor {
|
|
|
575
618
|
|
|
576
619
|
this._addUndoStep( cursor );
|
|
577
620
|
|
|
578
|
-
if(
|
|
621
|
+
if( cursor.selection ) {
|
|
579
622
|
this.deleteSelection( cursor );
|
|
580
623
|
// Remove entire line when selecting with triple click
|
|
581
624
|
if( this._tripleClickSelection )
|
|
@@ -629,7 +672,7 @@ class CodeEditor {
|
|
|
629
672
|
|
|
630
673
|
this._addUndoStep( cursor );
|
|
631
674
|
|
|
632
|
-
if(
|
|
675
|
+
if( cursor.selection ) {
|
|
633
676
|
// Use 'Backspace' as it's the same callback...
|
|
634
677
|
this.actions['Backspace'].callback( ln, cursor, e );
|
|
635
678
|
}
|
|
@@ -640,7 +683,7 @@ class CodeEditor {
|
|
|
640
683
|
this.code.lines[ ln ] = sliceChars( this.code.lines[ ln ], cursor.position );
|
|
641
684
|
this.processLine( ln );
|
|
642
685
|
}
|
|
643
|
-
else if(this.code.lines[ ln + 1 ] != undefined) {
|
|
686
|
+
else if( this.code.lines[ ln + 1 ] != undefined ) {
|
|
644
687
|
this.code.lines[ ln ] += this.code.lines[ ln + 1 ];
|
|
645
688
|
this.code.lines.splice( ln + 1, 1 );
|
|
646
689
|
this.processLines();
|
|
@@ -650,14 +693,31 @@ class CodeEditor {
|
|
|
650
693
|
|
|
651
694
|
this.action( 'Tab', true, ( ln, cursor, e ) => {
|
|
652
695
|
|
|
653
|
-
if( this.
|
|
696
|
+
if( this._skipTabs )
|
|
654
697
|
{
|
|
655
|
-
this.
|
|
656
|
-
|
|
698
|
+
this._skipTabs--;
|
|
699
|
+
if( !this._skipTabs )
|
|
700
|
+
delete this._skipTabs;
|
|
701
|
+
}
|
|
702
|
+
else if( this.isAutoCompleteActive )
|
|
657
703
|
{
|
|
658
|
-
this.
|
|
704
|
+
this.autoCompleteWord();
|
|
659
705
|
}
|
|
660
|
-
|
|
706
|
+
else
|
|
707
|
+
{
|
|
708
|
+
this._addUndoStep( cursor );
|
|
709
|
+
|
|
710
|
+
if( e && e.shiftKey )
|
|
711
|
+
{
|
|
712
|
+
this._removeSpaces( cursor );
|
|
713
|
+
}
|
|
714
|
+
else
|
|
715
|
+
{
|
|
716
|
+
const indentSpaces = this.tabSpaces - (cursor.position % this.tabSpaces);
|
|
717
|
+
this._addSpaces( indentSpaces );
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}, "shiftKey");
|
|
661
721
|
|
|
662
722
|
this.action( 'Home', false, ( ln, cursor, e ) => {
|
|
663
723
|
|
|
@@ -670,7 +730,8 @@ class CodeEditor {
|
|
|
670
730
|
let lastX = cursor.position;
|
|
671
731
|
|
|
672
732
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
673
|
-
if(idx > 0)
|
|
733
|
+
if(idx > 0)
|
|
734
|
+
this.cursorToString( cursor, prestring );
|
|
674
735
|
this.setScrollLeft( 0 );
|
|
675
736
|
|
|
676
737
|
// Merge cursors
|
|
@@ -679,18 +740,18 @@ class CodeEditor {
|
|
|
679
740
|
if( e.shiftKey && !e.cancelShift )
|
|
680
741
|
{
|
|
681
742
|
// Get last selection range
|
|
682
|
-
if(
|
|
683
|
-
lastX +=
|
|
743
|
+
if( cursor.selection )
|
|
744
|
+
lastX += cursor.selection.chars;
|
|
684
745
|
|
|
685
|
-
if( !
|
|
746
|
+
if( !cursor.selection )
|
|
686
747
|
this.startSelection( cursor );
|
|
687
748
|
|
|
688
749
|
var string = this.code.lines[ ln ].substring( idx, lastX );
|
|
689
|
-
if(
|
|
690
|
-
|
|
750
|
+
if( cursor.selection.sameLine() )
|
|
751
|
+
cursor.selection.selectInline( cursor, idx, cursor.line, this.measureString( string ) );
|
|
691
752
|
else
|
|
692
753
|
{
|
|
693
|
-
this.
|
|
754
|
+
this._processSelection( cursor, e );
|
|
694
755
|
}
|
|
695
756
|
} else if( !e.keepSelection )
|
|
696
757
|
this.endSelection();
|
|
@@ -701,15 +762,15 @@ class CodeEditor {
|
|
|
701
762
|
if( ( e.shiftKey || e._shiftKey ) && !e.cancelShift ) {
|
|
702
763
|
|
|
703
764
|
var string = this.code.lines[ ln ].substring( cursor.position );
|
|
704
|
-
if( !
|
|
765
|
+
if( !cursor.selection )
|
|
705
766
|
this.startSelection( cursor );
|
|
706
|
-
if(
|
|
707
|
-
|
|
767
|
+
if( cursor.selection.sameLine() )
|
|
768
|
+
cursor.selection.selectInline( cursor, cursor.position, cursor.line, this.measureString( string ));
|
|
708
769
|
else
|
|
709
770
|
{
|
|
710
771
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
711
772
|
this.cursorToString( cursor, this.code.lines[ ln ] );
|
|
712
|
-
this.
|
|
773
|
+
this._processSelection( cursor, e );
|
|
713
774
|
}
|
|
714
775
|
} else if( !e.keepSelection )
|
|
715
776
|
this.endSelection();
|
|
@@ -729,7 +790,7 @@ class CodeEditor {
|
|
|
729
790
|
// Add word
|
|
730
791
|
if( this.isAutoCompleteActive )
|
|
731
792
|
{
|
|
732
|
-
this.autoCompleteWord(
|
|
793
|
+
this.autoCompleteWord();
|
|
733
794
|
return;
|
|
734
795
|
}
|
|
735
796
|
|
|
@@ -756,10 +817,10 @@ class CodeEditor {
|
|
|
756
817
|
|
|
757
818
|
if( _c0 == '{' && _c1 == '}' ) {
|
|
758
819
|
this.code.lines.splice( cursor.line, 0, "" );
|
|
759
|
-
this.
|
|
820
|
+
this._addSpaceTabs( cursor, tabs + 1 );
|
|
760
821
|
this.code.lines[ cursor.line + 1 ] = " ".repeat(spaces) + this.code.lines[ cursor.line + 1 ];
|
|
761
822
|
} else {
|
|
762
|
-
this.
|
|
823
|
+
this._addSpaceTabs( cursor, tabs );
|
|
763
824
|
}
|
|
764
825
|
|
|
765
826
|
this.processLines();
|
|
@@ -771,7 +832,7 @@ class CodeEditor {
|
|
|
771
832
|
if( !this.isAutoCompleteActive )
|
|
772
833
|
{
|
|
773
834
|
if( e.shiftKey ) {
|
|
774
|
-
if( !
|
|
835
|
+
if( !cursor.selection )
|
|
775
836
|
this.startSelection( cursor );
|
|
776
837
|
|
|
777
838
|
this.lineUp( cursor );
|
|
@@ -781,7 +842,7 @@ class CodeEditor {
|
|
|
781
842
|
this.cursorToPosition( cursor, this.code.lines[ cursor.line ].length );
|
|
782
843
|
}
|
|
783
844
|
|
|
784
|
-
this.
|
|
845
|
+
this._processSelection( cursor, e, false );
|
|
785
846
|
|
|
786
847
|
} else {
|
|
787
848
|
this.endSelection();
|
|
@@ -804,7 +865,7 @@ class CodeEditor {
|
|
|
804
865
|
if( !this.isAutoCompleteActive )
|
|
805
866
|
{
|
|
806
867
|
if( e.shiftKey ) {
|
|
807
|
-
if( !
|
|
868
|
+
if( !cursor.selection )
|
|
808
869
|
this.startSelection( cursor );
|
|
809
870
|
} else {
|
|
810
871
|
this.endSelection();
|
|
@@ -819,7 +880,7 @@ class CodeEditor {
|
|
|
819
880
|
}
|
|
820
881
|
|
|
821
882
|
if( e.shiftKey ) {
|
|
822
|
-
this.
|
|
883
|
+
this._processSelection( cursor, e );
|
|
823
884
|
}
|
|
824
885
|
}
|
|
825
886
|
// Move down autocomplete selection
|
|
@@ -853,37 +914,37 @@ class CodeEditor {
|
|
|
853
914
|
var diff = Math.max( cursor.position - from, 1 );
|
|
854
915
|
var substr = word.substr( 0, diff );
|
|
855
916
|
// Selections...
|
|
856
|
-
if( e.shiftKey ) { if( !
|
|
917
|
+
if( e.shiftKey ) { if( !cursor.selection ) this.startSelection( cursor ); }
|
|
857
918
|
else this.endSelection();
|
|
858
919
|
this.cursorToString( cursor, substr, true );
|
|
859
|
-
if( e.shiftKey ) this.
|
|
920
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false, true );
|
|
860
921
|
}
|
|
861
922
|
else {
|
|
862
923
|
var letter = this.getCharAtPos( cursor, -1 );
|
|
863
924
|
if( letter ) {
|
|
864
925
|
if( e.shiftKey ) {
|
|
865
|
-
if( !
|
|
926
|
+
if( !cursor.selection ) this.startSelection( cursor );
|
|
866
927
|
this.cursorToLeft( letter, cursor );
|
|
867
|
-
this.
|
|
928
|
+
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
868
929
|
}
|
|
869
930
|
else {
|
|
870
|
-
if( !
|
|
931
|
+
if( !cursor.selection ) {
|
|
871
932
|
this.cursorToLeft( letter, cursor );
|
|
872
933
|
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
873
934
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
874
935
|
}
|
|
875
936
|
else {
|
|
876
|
-
|
|
937
|
+
cursor.selection.invertIfNecessary();
|
|
877
938
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
|
|
878
|
-
this.cursorToLine( cursor,
|
|
879
|
-
this.cursorToPosition( cursor,
|
|
939
|
+
this.cursorToLine( cursor, cursor.selection.fromY, true );
|
|
940
|
+
this.cursorToPosition( cursor, cursor.selection.fromX );
|
|
880
941
|
this.endSelection();
|
|
881
942
|
}
|
|
882
943
|
}
|
|
883
944
|
}
|
|
884
945
|
else if( cursor.line > 0 ) {
|
|
885
946
|
|
|
886
|
-
if( e.shiftKey && !
|
|
947
|
+
if( e.shiftKey && !cursor.selection ) this.startSelection( cursor );
|
|
887
948
|
|
|
888
949
|
this.lineUp( cursor );
|
|
889
950
|
|
|
@@ -891,7 +952,7 @@ class CodeEditor {
|
|
|
891
952
|
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
892
953
|
delete e.cancelShift; delete e.keepSelection;
|
|
893
954
|
|
|
894
|
-
if( e.shiftKey ) this.
|
|
955
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false );
|
|
895
956
|
}
|
|
896
957
|
}
|
|
897
958
|
});
|
|
@@ -903,40 +964,61 @@ class CodeEditor {
|
|
|
903
964
|
cursor.position == this.code.lines[ cursor.line ].length )
|
|
904
965
|
return;
|
|
905
966
|
|
|
906
|
-
if( e.metaKey )
|
|
967
|
+
if( e.metaKey ) // Apple devices (Command)
|
|
968
|
+
{
|
|
907
969
|
e.preventDefault();
|
|
908
970
|
this.actions[ 'End' ].callback( ln, cursor );
|
|
909
|
-
}
|
|
971
|
+
}
|
|
972
|
+
else if( e.ctrlKey ) // Next word
|
|
973
|
+
{
|
|
910
974
|
// Get next word
|
|
911
975
|
const [ word, from, to ] = this.getWordAtPos( cursor );
|
|
976
|
+
|
|
912
977
|
// If no length, we change line..
|
|
913
978
|
if( !word.length ) this.lineDown( cursor, true );
|
|
914
979
|
var diff = cursor.position - from;
|
|
915
980
|
var substr = word.substr( diff );
|
|
981
|
+
|
|
916
982
|
// Selections...
|
|
917
|
-
if( e.shiftKey ) {
|
|
918
|
-
|
|
983
|
+
if( e.shiftKey ) {
|
|
984
|
+
if( !cursor.selection )
|
|
985
|
+
this.startSelection( cursor );
|
|
986
|
+
}
|
|
987
|
+
else
|
|
988
|
+
this.endSelection();
|
|
989
|
+
|
|
919
990
|
this.cursorToString( cursor, substr );
|
|
920
|
-
|
|
921
|
-
|
|
991
|
+
|
|
992
|
+
if( e.shiftKey )
|
|
993
|
+
this._processSelection( cursor, e );
|
|
994
|
+
}
|
|
995
|
+
else // Next char
|
|
996
|
+
{
|
|
922
997
|
var letter = this.getCharAtPos( cursor );
|
|
923
998
|
if( letter ) {
|
|
924
|
-
|
|
925
|
-
|
|
999
|
+
|
|
1000
|
+
// Selecting chars
|
|
1001
|
+
if( e.shiftKey )
|
|
1002
|
+
{
|
|
1003
|
+
if( !cursor.selection )
|
|
1004
|
+
this.startSelection( cursor );
|
|
1005
|
+
|
|
926
1006
|
this.cursorToRight( letter, cursor );
|
|
927
|
-
this.
|
|
928
|
-
}
|
|
929
|
-
|
|
1007
|
+
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
1008
|
+
}
|
|
1009
|
+
else
|
|
1010
|
+
{
|
|
1011
|
+
if( !cursor.selection ) {
|
|
930
1012
|
this.cursorToRight( letter, cursor );
|
|
931
1013
|
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
932
1014
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
933
1015
|
}
|
|
934
|
-
else
|
|
1016
|
+
else
|
|
935
1017
|
{
|
|
936
|
-
|
|
1018
|
+
cursor.selection.invertIfNecessary();
|
|
937
1019
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
|
|
938
|
-
this.cursorToLine( cursor,
|
|
939
|
-
this.cursorToPosition( cursor,
|
|
1020
|
+
this.cursorToLine( cursor, cursor.selection.toY );
|
|
1021
|
+
this.cursorToPosition( cursor, cursor.selection.toX );
|
|
940
1022
|
this.endSelection();
|
|
941
1023
|
}
|
|
942
1024
|
}
|
|
@@ -944,12 +1026,13 @@ class CodeEditor {
|
|
|
944
1026
|
else if( this.code.lines[ cursor.line + 1 ] !== undefined ) {
|
|
945
1027
|
|
|
946
1028
|
if( e.shiftKey ) {
|
|
947
|
-
if( !
|
|
1029
|
+
if( !cursor.selection ) this.startSelection( cursor );
|
|
948
1030
|
}
|
|
1031
|
+
else this.endSelection();
|
|
949
1032
|
|
|
950
1033
|
this.lineDown( cursor, true );
|
|
951
1034
|
|
|
952
|
-
if( e.shiftKey ) this.
|
|
1035
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false );
|
|
953
1036
|
|
|
954
1037
|
this.hideAutoCompleteBox();
|
|
955
1038
|
}
|
|
@@ -1049,14 +1132,14 @@ class CodeEditor {
|
|
|
1049
1132
|
|
|
1050
1133
|
let lidx = cursor.line;
|
|
1051
1134
|
|
|
1052
|
-
if(
|
|
1135
|
+
if( cursor.selection ) {
|
|
1053
1136
|
this.deleteSelection( cursor );
|
|
1054
1137
|
lidx = cursor.line;
|
|
1055
1138
|
}
|
|
1056
1139
|
|
|
1057
1140
|
this.endSelection();
|
|
1058
1141
|
|
|
1059
|
-
const new_lines = text.split( '\n' );
|
|
1142
|
+
const new_lines = text.replaceAll( '\r', '' ).split( '\n' );
|
|
1060
1143
|
|
|
1061
1144
|
// Pasting Multiline...
|
|
1062
1145
|
if( new_lines.length != 1 )
|
|
@@ -1111,10 +1194,16 @@ class CodeEditor {
|
|
|
1111
1194
|
|
|
1112
1195
|
const inner_add_tab = ( text, name, title ) => {
|
|
1113
1196
|
|
|
1197
|
+
// Remove Carriage Return in some cases and sub tabs using spaces
|
|
1198
|
+
text = text.replaceAll( '\r', '' );
|
|
1199
|
+
text = text.replaceAll( /\t|\\t/g, ' '.repeat( this.tabSpaces ) );
|
|
1200
|
+
|
|
1114
1201
|
// Set current text and language
|
|
1115
|
-
|
|
1202
|
+
|
|
1203
|
+
const lines = text.split( '\n' );
|
|
1116
1204
|
|
|
1117
1205
|
// Add item in the explorer if used
|
|
1206
|
+
|
|
1118
1207
|
if( this.explorer )
|
|
1119
1208
|
{
|
|
1120
1209
|
this._storedLines = this._storedLines ?? {};
|
|
@@ -1163,6 +1252,7 @@ class CodeEditor {
|
|
|
1163
1252
|
}
|
|
1164
1253
|
|
|
1165
1254
|
let cursor = document.createElement( 'div' );
|
|
1255
|
+
cursor.name = "cursor" + this.cursors.childElementCount;
|
|
1166
1256
|
cursor.className = "cursor";
|
|
1167
1257
|
cursor.innerHTML = " ";
|
|
1168
1258
|
cursor.isMain = isMain;
|
|
@@ -1385,14 +1475,25 @@ class CodeEditor {
|
|
|
1385
1475
|
|
|
1386
1476
|
panel.sameLine();
|
|
1387
1477
|
panel.addLabel( this.code.title, { float: 'right', signal: "@tab-name" });
|
|
1388
|
-
panel.addLabel( "Ln " + 1, {
|
|
1389
|
-
panel.addLabel( "Col " + 1, {
|
|
1478
|
+
panel.addLabel( "Ln " + 1, { maxWidth: "48px", signal: "@cursor-line" });
|
|
1479
|
+
panel.addLabel( "Col " + 1, { maxWidth: "48px", signal: "@cursor-pos" });
|
|
1480
|
+
panel.addButton( null, "Spaces: " + this.tabSpaces, ( value, event ) => {
|
|
1481
|
+
LX.addContextMenu( "Spaces", event, m => {
|
|
1482
|
+
const options = [ 2, 4, 8 ];
|
|
1483
|
+
for( const n of options )
|
|
1484
|
+
m.add( n, (v) => {
|
|
1485
|
+
this.tabSpaces = v;
|
|
1486
|
+
this.processLines();
|
|
1487
|
+
this._updateDataInfoPanel( "@tab-spaces", "Spaces: " + this.tabSpaces );
|
|
1488
|
+
} );
|
|
1489
|
+
});
|
|
1490
|
+
}, { width: "10%", nameWidth: "15%", signal: "@tab-spaces" });
|
|
1390
1491
|
panel.addButton( "<b>{ }</b>", this.highlight, ( value, event ) => {
|
|
1391
1492
|
LX.addContextMenu( "Language", event, m => {
|
|
1392
1493
|
for( const lang of Object.keys(this.languages) )
|
|
1393
1494
|
m.add( lang, this._changeLanguage.bind(this) );
|
|
1394
1495
|
});
|
|
1395
|
-
}, { width: "
|
|
1496
|
+
}, { width: "17.5%", nameWidth: "15%", signal: "@highlight" });
|
|
1396
1497
|
panel.endLine();
|
|
1397
1498
|
|
|
1398
1499
|
return panel;
|
|
@@ -1639,12 +1740,12 @@ class CodeEditor {
|
|
|
1639
1740
|
{
|
|
1640
1741
|
this.processClick( e );
|
|
1641
1742
|
|
|
1642
|
-
this.canOpenContextMenu = !
|
|
1743
|
+
this.canOpenContextMenu = !cursor.selection;
|
|
1643
1744
|
|
|
1644
|
-
if(
|
|
1745
|
+
if( cursor.selection )
|
|
1645
1746
|
{
|
|
1646
|
-
this.canOpenContextMenu |= (cursor.line >=
|
|
1647
|
-
&& cursor.position >=
|
|
1747
|
+
this.canOpenContextMenu |= (cursor.line >= cursor.selection.fromY && cursor.line <= cursor.selection.toY
|
|
1748
|
+
&& cursor.position >= cursor.selection.fromX && cursor.position <= cursor.selection.toX);
|
|
1648
1749
|
if( this.canOpenContextMenu )
|
|
1649
1750
|
return;
|
|
1650
1751
|
}
|
|
@@ -1664,7 +1765,7 @@ class CodeEditor {
|
|
|
1664
1765
|
else if( e.type == 'mousemove' )
|
|
1665
1766
|
{
|
|
1666
1767
|
if( this.state.selectingText )
|
|
1667
|
-
this.
|
|
1768
|
+
this.processSelections( e );
|
|
1668
1769
|
}
|
|
1669
1770
|
|
|
1670
1771
|
else if ( e.type == 'click' ) // trip
|
|
@@ -1676,7 +1777,7 @@ class CodeEditor {
|
|
|
1676
1777
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
1677
1778
|
this.cursorToPosition( cursor, from );
|
|
1678
1779
|
this.startSelection( cursor );
|
|
1679
|
-
|
|
1780
|
+
cursor.selection.selectInline( cursor, from, cursor.line, this.measureString( word ) );
|
|
1680
1781
|
this.cursorToString( cursor, word ); // Go to the end of the word
|
|
1681
1782
|
break;
|
|
1682
1783
|
// Select entire line
|
|
@@ -1705,6 +1806,8 @@ class CodeEditor {
|
|
|
1705
1806
|
m.add( "" );
|
|
1706
1807
|
m.add( "Format/JSON", () => {
|
|
1707
1808
|
let json = this.toJSONFormat( this.getText() );
|
|
1809
|
+
if( !json )
|
|
1810
|
+
return;
|
|
1708
1811
|
this.code.lines = json.split( "\n" );
|
|
1709
1812
|
this.processLines();
|
|
1710
1813
|
} );
|
|
@@ -1722,10 +1825,9 @@ class CodeEditor {
|
|
|
1722
1825
|
this.endSelection();
|
|
1723
1826
|
}
|
|
1724
1827
|
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
}
|
|
1828
|
+
const cursor = this._getCurrentCursor();
|
|
1829
|
+
if( cursor.selection )
|
|
1830
|
+
cursor.selection.invertIfNecessary();
|
|
1729
1831
|
|
|
1730
1832
|
this.state.selectingText = false;
|
|
1731
1833
|
delete this._lastSelectionKeyDir;
|
|
@@ -1764,13 +1866,32 @@ class CodeEditor {
|
|
|
1764
1866
|
this.hideAutoCompleteBox();
|
|
1765
1867
|
}
|
|
1766
1868
|
|
|
1767
|
-
|
|
1869
|
+
updateSelections( e, keep_range, flags = CodeEditor.SELECTION_X_Y ) {
|
|
1870
|
+
|
|
1871
|
+
for( let cursor of this.cursors.children )
|
|
1872
|
+
{
|
|
1873
|
+
if( !cursor.selection )
|
|
1874
|
+
continue;
|
|
1875
|
+
|
|
1876
|
+
this._processSelection( cursor, e, keep_range, flags );
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
processSelections( e, keep_range, flags = CodeEditor.SELECTION_X_Y ) {
|
|
1881
|
+
|
|
1882
|
+
for( let cursor of this.cursors.children )
|
|
1883
|
+
{
|
|
1884
|
+
this._processSelection( cursor, e, keep_range, flags );
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
_processSelection( cursor, e, keep_range, flags = CodeEditor.SELECTION_X_Y ) {
|
|
1768
1889
|
|
|
1769
|
-
var cursor = this._getCurrentCursor();
|
|
1770
1890
|
const isMouseEvent = e && ( e.constructor == MouseEvent );
|
|
1771
1891
|
|
|
1772
1892
|
if( isMouseEvent ) this.processClick( e );
|
|
1773
|
-
|
|
1893
|
+
|
|
1894
|
+
if( !cursor.selection )
|
|
1774
1895
|
this.startSelection( cursor );
|
|
1775
1896
|
|
|
1776
1897
|
this._hideActiveLine();
|
|
@@ -1781,46 +1902,48 @@ class CodeEditor {
|
|
|
1781
1902
|
let ccw = true;
|
|
1782
1903
|
|
|
1783
1904
|
// Check if we must change ccw or not ... (not with mouse)
|
|
1784
|
-
if( !isMouseEvent &&
|
|
1785
|
-
(
|
|
1905
|
+
if( !isMouseEvent && cursor.line >= cursor.selection.fromY &&
|
|
1906
|
+
(cursor.line == cursor.selection.fromY ? cursor.position >= cursor.selection.fromX : true) )
|
|
1786
1907
|
{
|
|
1787
1908
|
ccw = ( e && this._lastSelectionKeyDir && ( e.key == 'ArrowRight' || e.key == 'ArrowDown' || e.key == 'End' ) );
|
|
1788
1909
|
}
|
|
1789
1910
|
|
|
1790
1911
|
if( ccw )
|
|
1791
1912
|
{
|
|
1792
|
-
if( flags & CodeEditor.SELECTION_X )
|
|
1793
|
-
if( flags & CodeEditor.SELECTION_Y )
|
|
1913
|
+
if( flags & CodeEditor.SELECTION_X ) cursor.selection.fromX = cursor.position;
|
|
1914
|
+
if( flags & CodeEditor.SELECTION_Y ) cursor.selection.fromY = cursor.line;
|
|
1794
1915
|
}
|
|
1795
1916
|
else
|
|
1796
1917
|
{
|
|
1797
|
-
if( flags & CodeEditor.SELECTION_X )
|
|
1798
|
-
if( flags & CodeEditor.SELECTION_Y )
|
|
1918
|
+
if( flags & CodeEditor.SELECTION_X ) cursor.selection.toX = cursor.position;
|
|
1919
|
+
if( flags & CodeEditor.SELECTION_Y ) cursor.selection.toY = cursor.line;
|
|
1799
1920
|
}
|
|
1800
1921
|
|
|
1801
1922
|
this._lastSelectionKeyDir = ccw;
|
|
1802
1923
|
}
|
|
1803
1924
|
|
|
1804
1925
|
// Only leave if not a mouse selection...
|
|
1805
|
-
if( !isMouseEvent &&
|
|
1926
|
+
if( !isMouseEvent && cursor.selection.isEmpty() )
|
|
1806
1927
|
{
|
|
1807
1928
|
this.endSelection();
|
|
1808
1929
|
return;
|
|
1809
1930
|
}
|
|
1810
1931
|
|
|
1811
|
-
|
|
1932
|
+
cursor.selection.chars = 0;
|
|
1812
1933
|
|
|
1813
|
-
const fromX =
|
|
1814
|
-
fromY =
|
|
1815
|
-
toX =
|
|
1816
|
-
toY =
|
|
1934
|
+
const fromX = cursor.selection.fromX,
|
|
1935
|
+
fromY = cursor.selection.fromY,
|
|
1936
|
+
toX = cursor.selection.toX,
|
|
1937
|
+
toY = cursor.selection.toY;
|
|
1817
1938
|
const deltaY = toY - fromY;
|
|
1818
1939
|
|
|
1940
|
+
let cursorSelections = this.selections[ cursor.name ];
|
|
1941
|
+
|
|
1819
1942
|
// Selection goes down...
|
|
1820
1943
|
if( deltaY >= 0 )
|
|
1821
1944
|
{
|
|
1822
|
-
while( deltaY < (
|
|
1823
|
-
deleteElement(
|
|
1945
|
+
while( deltaY < ( cursorSelections.childElementCount - 1 ) )
|
|
1946
|
+
deleteElement( cursorSelections.lastChild );
|
|
1824
1947
|
|
|
1825
1948
|
for(let i = fromY; i <= toY; i++){
|
|
1826
1949
|
|
|
@@ -1831,12 +1954,12 @@ class CodeEditor {
|
|
|
1831
1954
|
if( isVisible )
|
|
1832
1955
|
{
|
|
1833
1956
|
// Make sure that the line selection is generated...
|
|
1834
|
-
domEl =
|
|
1957
|
+
domEl = cursorSelections.childNodes[ sId ];
|
|
1835
1958
|
if(!domEl)
|
|
1836
1959
|
{
|
|
1837
1960
|
domEl = document.createElement( 'div' );
|
|
1838
1961
|
domEl.className = "lexcodeselection";
|
|
1839
|
-
|
|
1962
|
+
cursorSelections.appendChild( domEl );
|
|
1840
1963
|
}
|
|
1841
1964
|
}
|
|
1842
1965
|
|
|
@@ -1858,7 +1981,7 @@ class CodeEditor {
|
|
|
1858
1981
|
}
|
|
1859
1982
|
|
|
1860
1983
|
const stringWidth = this.measureString( string );
|
|
1861
|
-
|
|
1984
|
+
cursor.selection.chars += stringWidth / this.charWidth;
|
|
1862
1985
|
|
|
1863
1986
|
if( isVisible )
|
|
1864
1987
|
{
|
|
@@ -1870,8 +1993,8 @@ class CodeEditor {
|
|
|
1870
1993
|
}
|
|
1871
1994
|
else // Selection goes up...
|
|
1872
1995
|
{
|
|
1873
|
-
while( Math.abs( deltaY ) < (
|
|
1874
|
-
deleteElement(
|
|
1996
|
+
while( Math.abs( deltaY ) < ( cursorSelections.childElementCount - 1 ) )
|
|
1997
|
+
deleteElement( cursorSelections.firstChild );
|
|
1875
1998
|
|
|
1876
1999
|
for( let i = toY; i <= fromY; i++ ){
|
|
1877
2000
|
|
|
@@ -1882,12 +2005,12 @@ class CodeEditor {
|
|
|
1882
2005
|
if( isVisible )
|
|
1883
2006
|
{
|
|
1884
2007
|
// Make sure that the line selection is generated...
|
|
1885
|
-
domEl =
|
|
2008
|
+
domEl = cursorSelections.childNodes[ sId ];
|
|
1886
2009
|
if(!domEl)
|
|
1887
2010
|
{
|
|
1888
2011
|
domEl = document.createElement( 'div' );
|
|
1889
2012
|
domEl.className = "lexcodeselection";
|
|
1890
|
-
|
|
2013
|
+
cursorSelections.appendChild( domEl );
|
|
1891
2014
|
}
|
|
1892
2015
|
}
|
|
1893
2016
|
|
|
@@ -1907,7 +2030,7 @@ class CodeEditor {
|
|
|
1907
2030
|
}
|
|
1908
2031
|
|
|
1909
2032
|
const stringWidth = this.measureString( string );
|
|
1910
|
-
|
|
2033
|
+
cursor.selection.chars += stringWidth / this.charWidth;
|
|
1911
2034
|
|
|
1912
2035
|
if( isVisible )
|
|
1913
2036
|
{
|
|
@@ -1998,46 +2121,59 @@ class CodeEditor {
|
|
|
1998
2121
|
|
|
1999
2122
|
if( e.ctrlKey || e.metaKey )
|
|
2000
2123
|
{
|
|
2001
|
-
e.preventDefault();
|
|
2002
|
-
|
|
2003
2124
|
switch( key.toLowerCase() ) {
|
|
2004
2125
|
case 'a': // select all
|
|
2126
|
+
e.preventDefault();
|
|
2005
2127
|
this.selectAll();
|
|
2006
2128
|
return true;
|
|
2007
2129
|
case 'c': // k+c, comment line
|
|
2130
|
+
e.preventDefault();
|
|
2008
2131
|
if( this.state.keyChain == 'k' ) {
|
|
2009
2132
|
this._commentLines();
|
|
2010
2133
|
return true;
|
|
2011
2134
|
}
|
|
2012
2135
|
return false;
|
|
2136
|
+
case 'd': // next ocurrence
|
|
2137
|
+
e.preventDefault();
|
|
2138
|
+
this.selectNextOcurrence( cursor );
|
|
2139
|
+
return true;
|
|
2013
2140
|
case 'f': // find/search
|
|
2141
|
+
e.preventDefault();
|
|
2014
2142
|
this.showSearchBox();
|
|
2015
2143
|
return true;
|
|
2016
2144
|
case 'g': // find line
|
|
2145
|
+
e.preventDefault();
|
|
2017
2146
|
this.showSearchLineBox();
|
|
2018
2147
|
return true;
|
|
2019
2148
|
case 'k': // shortcut chain
|
|
2149
|
+
e.preventDefault();
|
|
2020
2150
|
this.state.keyChain = 'k';
|
|
2021
2151
|
return true;
|
|
2022
2152
|
case 's': // save
|
|
2153
|
+
e.preventDefault();
|
|
2023
2154
|
this.onsave( this.getText() );
|
|
2024
2155
|
return true;
|
|
2025
2156
|
case 'u': // k+u, uncomment line
|
|
2157
|
+
e.preventDefault();
|
|
2026
2158
|
if( this.state.keyChain == 'k' ) {
|
|
2027
2159
|
this._uncommentLines();
|
|
2028
2160
|
return true;
|
|
2029
2161
|
}
|
|
2030
2162
|
return false;
|
|
2031
2163
|
case 'y': // redo
|
|
2164
|
+
e.preventDefault();
|
|
2032
2165
|
this._doRedo( cursor );
|
|
2033
2166
|
return true;
|
|
2034
2167
|
case 'z': // undo
|
|
2168
|
+
e.preventDefault();
|
|
2035
2169
|
this._doUndo( cursor );
|
|
2036
2170
|
return true;
|
|
2037
2171
|
case '+': // increase size
|
|
2172
|
+
e.preventDefault();
|
|
2038
2173
|
this._increaseFontSize();
|
|
2039
2174
|
return true;
|
|
2040
2175
|
case '-': // decrease size
|
|
2176
|
+
e.preventDefault();
|
|
2041
2177
|
this._decreaseFontSize();
|
|
2042
2178
|
return true;
|
|
2043
2179
|
}
|
|
@@ -2066,14 +2202,8 @@ class CodeEditor {
|
|
|
2066
2202
|
{
|
|
2067
2203
|
switch( key.toLowerCase() ) {
|
|
2068
2204
|
case 'c': // copy
|
|
2069
|
-
// TODO: COPY TEXT FROM EVERY CURSOR
|
|
2070
2205
|
this._copyContent( cursor );
|
|
2071
2206
|
return;
|
|
2072
|
-
case 'd': // duplicate line
|
|
2073
|
-
e.preventDefault();
|
|
2074
|
-
// TODO: UPDATE NEXT CURSOR ON MODIFY STATE
|
|
2075
|
-
this._duplicateLine( lidx, cursor );
|
|
2076
|
-
return;
|
|
2077
2207
|
case 'v': // paste
|
|
2078
2208
|
this._pasteContent( cursor );
|
|
2079
2209
|
return;
|
|
@@ -2094,6 +2224,10 @@ class CodeEditor {
|
|
|
2094
2224
|
else if( e.altKey )
|
|
2095
2225
|
{
|
|
2096
2226
|
switch( key ) {
|
|
2227
|
+
case 'd': // duplicate line
|
|
2228
|
+
e.preventDefault();
|
|
2229
|
+
this._duplicateLine( lidx, cursor );
|
|
2230
|
+
return;
|
|
2097
2231
|
case 'ArrowUp':
|
|
2098
2232
|
if(this.code.lines[ lidx - 1 ] == undefined)
|
|
2099
2233
|
return;
|
|
@@ -2121,10 +2255,12 @@ class CodeEditor {
|
|
|
2121
2255
|
|
|
2122
2256
|
for( const actKey in this.actions ) {
|
|
2123
2257
|
|
|
2124
|
-
if( key != actKey )
|
|
2258
|
+
if( key != actKey )
|
|
2259
|
+
continue;
|
|
2260
|
+
|
|
2125
2261
|
e.preventDefault();
|
|
2126
2262
|
|
|
2127
|
-
if( this.actions[ key ]
|
|
2263
|
+
if( this._actionMustDelete( cursor, this.actions[ key ], e ) )
|
|
2128
2264
|
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
2129
2265
|
|
|
2130
2266
|
return this.actions[ key ].callback( lidx, cursor, e );
|
|
@@ -2153,7 +2289,7 @@ class CodeEditor {
|
|
|
2153
2289
|
// Until this point, if there was a selection, we need
|
|
2154
2290
|
// to delete the content..
|
|
2155
2291
|
|
|
2156
|
-
if(
|
|
2292
|
+
if( cursor.selection )
|
|
2157
2293
|
{
|
|
2158
2294
|
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
2159
2295
|
lidx = cursor.line;
|
|
@@ -2214,22 +2350,32 @@ class CodeEditor {
|
|
|
2214
2350
|
|
|
2215
2351
|
let text = await navigator.clipboard.readText();
|
|
2216
2352
|
|
|
2353
|
+
// Remove any possible tabs (\t) and add spaces
|
|
2354
|
+
text = text.replaceAll( /\t|\\t/g, ' '.repeat( this.tabSpaces ) );
|
|
2355
|
+
|
|
2217
2356
|
this._addUndoStep( cursor, true );
|
|
2218
2357
|
|
|
2219
2358
|
this.appendText( text, cursor );
|
|
2359
|
+
|
|
2360
|
+
const currentScroll = this.getScrollTop();
|
|
2361
|
+
const scroll = Math.max( cursor.line * this.lineHeight - this.codeScroller.offsetWidth, 0 );
|
|
2362
|
+
|
|
2363
|
+
if( currentScroll < scroll ) {
|
|
2364
|
+
this.codeScroller.scrollTo( 0, cursor.line * this.lineHeight );
|
|
2365
|
+
}
|
|
2220
2366
|
}
|
|
2221
2367
|
|
|
2222
2368
|
async _copyContent( cursor ) {
|
|
2223
2369
|
|
|
2224
2370
|
let text_to_copy = "";
|
|
2225
2371
|
|
|
2226
|
-
if( !
|
|
2372
|
+
if( !cursor.selection ) {
|
|
2227
2373
|
text_to_copy = "\n" + this.code.lines[ cursor.line ];
|
|
2228
2374
|
}
|
|
2229
2375
|
else {
|
|
2230
2376
|
|
|
2231
2377
|
// Some selections don't depend on mouse up..
|
|
2232
|
-
if(
|
|
2378
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2233
2379
|
|
|
2234
2380
|
const separator = "_NEWLINE_";
|
|
2235
2381
|
let code = this.code.lines.join( separator );
|
|
@@ -2237,11 +2383,11 @@ class CodeEditor {
|
|
|
2237
2383
|
// Get linear start index
|
|
2238
2384
|
let index = 0;
|
|
2239
2385
|
|
|
2240
|
-
for( let i = 0; i <=
|
|
2241
|
-
index += ( i ==
|
|
2386
|
+
for( let i = 0; i <= cursor.selection.fromY; i++ )
|
|
2387
|
+
index += ( i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length );
|
|
2242
2388
|
|
|
2243
|
-
index +=
|
|
2244
|
-
const num_chars =
|
|
2389
|
+
index += cursor.selection.fromY * separator.length;
|
|
2390
|
+
const num_chars = cursor.selection.chars + ( cursor.selection.toY - cursor.selection.fromY ) * separator.length;
|
|
2245
2391
|
const text = code.substr( index, num_chars );
|
|
2246
2392
|
const lines = text.split( separator );
|
|
2247
2393
|
text_to_copy = lines.join('\n');
|
|
@@ -2257,7 +2403,7 @@ class CodeEditor {
|
|
|
2257
2403
|
|
|
2258
2404
|
this._addUndoStep( cursor, true );
|
|
2259
2405
|
|
|
2260
|
-
if( !
|
|
2406
|
+
if( !cursor.selection ) {
|
|
2261
2407
|
text_to_cut = "\n" + this.code.lines[ cursor.line ];
|
|
2262
2408
|
this.code.lines.splice( lidx, 1 );
|
|
2263
2409
|
this.processLines();
|
|
@@ -2268,7 +2414,7 @@ class CodeEditor {
|
|
|
2268
2414
|
else {
|
|
2269
2415
|
|
|
2270
2416
|
// Some selections don't depend on mouse up..
|
|
2271
|
-
if(
|
|
2417
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2272
2418
|
|
|
2273
2419
|
const separator = "_NEWLINE_";
|
|
2274
2420
|
let code = this.code.lines.join(separator);
|
|
@@ -2276,11 +2422,11 @@ class CodeEditor {
|
|
|
2276
2422
|
// Get linear start index
|
|
2277
2423
|
let index = 0;
|
|
2278
2424
|
|
|
2279
|
-
for(let i = 0; i <=
|
|
2280
|
-
index += (i ==
|
|
2425
|
+
for(let i = 0; i <= cursor.selection.fromY; i++)
|
|
2426
|
+
index += (i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length);
|
|
2281
2427
|
|
|
2282
|
-
index +=
|
|
2283
|
-
const num_chars =
|
|
2428
|
+
index += cursor.selection.fromY * separator.length;
|
|
2429
|
+
const num_chars = cursor.selection.chars + (cursor.selection.toY - cursor.selection.fromY) * separator.length;
|
|
2284
2430
|
const text = code.substr(index, num_chars);
|
|
2285
2431
|
const lines = text.split(separator);
|
|
2286
2432
|
text_to_cut = lines.join('\n');
|
|
@@ -2305,18 +2451,18 @@ class CodeEditor {
|
|
|
2305
2451
|
|
|
2306
2452
|
this.state.keyChain = null;
|
|
2307
2453
|
|
|
2308
|
-
if(
|
|
2454
|
+
if( cursor.selection )
|
|
2309
2455
|
{
|
|
2310
2456
|
var cursor = this._getCurrentCursor();
|
|
2311
2457
|
this._addUndoStep( cursor, true );
|
|
2312
2458
|
|
|
2313
|
-
const selectedLines = this.code.lines.slice(
|
|
2459
|
+
const selectedLines = this.code.lines.slice( cursor.selection.fromY, cursor.selection.toY );
|
|
2314
2460
|
const minIdx = Math.min(...selectedLines.map( v => {
|
|
2315
2461
|
var idx = firstNonspaceIndex( v );
|
|
2316
2462
|
return idx < 0 ? 1e10 : idx;
|
|
2317
2463
|
} ));
|
|
2318
2464
|
|
|
2319
|
-
for( var i =
|
|
2465
|
+
for( var i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
2320
2466
|
{
|
|
2321
2467
|
this._commentLine( cursor, i, minIdx );
|
|
2322
2468
|
}
|
|
@@ -2364,12 +2510,12 @@ class CodeEditor {
|
|
|
2364
2510
|
|
|
2365
2511
|
this.state.keyChain = null;
|
|
2366
2512
|
|
|
2367
|
-
if(
|
|
2513
|
+
if( cursor.selection )
|
|
2368
2514
|
{
|
|
2369
2515
|
var cursor = this._getCurrentCursor();
|
|
2370
2516
|
this._addUndoStep( cursor, true );
|
|
2371
2517
|
|
|
2372
|
-
for( var i =
|
|
2518
|
+
for( var i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
2373
2519
|
{
|
|
2374
2520
|
this._uncommentLine( cursor, i );
|
|
2375
2521
|
}
|
|
@@ -2409,14 +2555,22 @@ class CodeEditor {
|
|
|
2409
2555
|
}
|
|
2410
2556
|
}
|
|
2411
2557
|
|
|
2412
|
-
action( key, deleteSelection, fn ) {
|
|
2558
|
+
action( key, deleteSelection, fn, eventSkipDelete ) {
|
|
2413
2559
|
|
|
2414
2560
|
this.actions[ key ] = {
|
|
2561
|
+
"key": key,
|
|
2415
2562
|
"callback": fn,
|
|
2416
|
-
"deleteSelection": deleteSelection
|
|
2563
|
+
"deleteSelection": deleteSelection,
|
|
2564
|
+
"eventSkipDelete": eventSkipDelete
|
|
2417
2565
|
};
|
|
2418
2566
|
}
|
|
2419
2567
|
|
|
2568
|
+
_actionMustDelete( cursor, action, e ) {
|
|
2569
|
+
|
|
2570
|
+
return cursor.selection && action.deleteSelection &&
|
|
2571
|
+
( action.eventSkipDelete ? !e[ action.eventSkipDelete ] : true );
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2420
2574
|
scanWordSuggestions() {
|
|
2421
2575
|
|
|
2422
2576
|
this.code.tokens = {};
|
|
@@ -2478,8 +2632,7 @@ class CodeEditor {
|
|
|
2478
2632
|
this.code.style.top = ( this.visibleLinesViewport.x * this.lineHeight ) + "px";
|
|
2479
2633
|
|
|
2480
2634
|
// Update selections
|
|
2481
|
-
|
|
2482
|
-
this.processSelection( null, true );
|
|
2635
|
+
this.updateSelections( null, true );
|
|
2483
2636
|
|
|
2484
2637
|
this._clearTmpVariables();
|
|
2485
2638
|
this._setActiveLine();
|
|
@@ -2742,111 +2895,98 @@ class CodeEditor {
|
|
|
2742
2895
|
|
|
2743
2896
|
const usesBlockComments = lang.blockComments ?? true;
|
|
2744
2897
|
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
2898
|
+
const singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
2899
|
+
|
|
2900
|
+
let token_classname = "";
|
|
2901
|
+
let discardToken = false;
|
|
2745
2902
|
|
|
2746
|
-
if(
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
}
|
|
2755
|
-
else
|
|
2756
|
-
{
|
|
2757
|
-
const singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
2758
|
-
|
|
2759
|
-
let token_classname = "";
|
|
2760
|
-
let discardToken = false;
|
|
2761
|
-
|
|
2762
|
-
if( this._buildingBlockComment != undefined )
|
|
2763
|
-
token_classname = "cm-com";
|
|
2764
|
-
|
|
2765
|
-
else if( this._buildingString != undefined )
|
|
2766
|
-
discardToken = this._appendStringToken( token );
|
|
2767
|
-
|
|
2768
|
-
else if( this._mustHightlightWord( token, CodeEditor.keywords ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
|
|
2769
|
-
token_classname = "cm-kwd";
|
|
2903
|
+
if( this._buildingBlockComment != undefined )
|
|
2904
|
+
token_classname = "cm-com";
|
|
2905
|
+
|
|
2906
|
+
else if( this._buildingString != undefined )
|
|
2907
|
+
discardToken = this._appendStringToken( token );
|
|
2908
|
+
|
|
2909
|
+
else if( this._mustHightlightWord( token, CodeEditor.keywords ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
|
|
2910
|
+
token_classname = "cm-kwd";
|
|
2770
2911
|
|
|
2771
|
-
|
|
2772
|
-
|
|
2912
|
+
else if( this._mustHightlightWord( token, CodeEditor.builtin ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
|
|
2913
|
+
token_classname = "cm-bln";
|
|
2773
2914
|
|
|
2774
|
-
|
|
2775
|
-
|
|
2915
|
+
else if( this._mustHightlightWord( token, CodeEditor.statementsAndDeclarations ) )
|
|
2916
|
+
token_classname = "cm-std";
|
|
2776
2917
|
|
|
2777
|
-
|
|
2778
|
-
|
|
2918
|
+
else if( this._mustHightlightWord( token, CodeEditor.symbols ) )
|
|
2919
|
+
token_classname = "cm-sym";
|
|
2779
2920
|
|
|
2780
|
-
|
|
2781
|
-
|
|
2921
|
+
else if( token.substr( 0, singleLineCommentToken.length ) == singleLineCommentToken )
|
|
2922
|
+
token_classname = "cm-com";
|
|
2782
2923
|
|
|
2783
|
-
|
|
2784
|
-
|
|
2924
|
+
else if( this._isNumber( token ) || this._isNumber( token.replace(/[px]|[em]|%/g,'') ) )
|
|
2925
|
+
token_classname = "cm-dec";
|
|
2785
2926
|
|
|
2786
|
-
|
|
2787
|
-
|
|
2927
|
+
else if( this._isCSSClass( token, prev, next ) )
|
|
2928
|
+
token_classname = "cm-kwd";
|
|
2788
2929
|
|
|
2789
|
-
|
|
2790
|
-
|
|
2930
|
+
else if ( this._isType( token, prev, next ) )
|
|
2931
|
+
token_classname = "cm-typ";
|
|
2791
2932
|
|
|
2792
|
-
|
|
2793
|
-
|
|
2933
|
+
else if ( highlight == 'batch' && ( token == '@' || prev == ':' || prev == '@' ) )
|
|
2934
|
+
token_classname = "cm-kwd";
|
|
2794
2935
|
|
|
2795
|
-
|
|
2796
|
-
|
|
2936
|
+
else if ( [ 'cpp', 'wgsl', 'glsl' ].indexOf( highlight ) > -1 && token.includes( '#' ) ) // C++ preprocessor
|
|
2937
|
+
token_classname = "cm-ppc";
|
|
2797
2938
|
|
|
2798
|
-
|
|
2799
|
-
|
|
2939
|
+
else if ( highlight == 'cpp' && prev == '<' && (next == '>' || next == '*') ) // Defining template type in C++
|
|
2940
|
+
token_classname = "cm-typ";
|
|
2800
2941
|
|
|
2801
|
-
|
|
2802
|
-
|
|
2942
|
+
else if ( highlight == 'cpp' && (next == '::' || prev == '::' && next != '(' )) // C++ Class
|
|
2943
|
+
token_classname = "cm-typ";
|
|
2803
2944
|
|
|
2804
|
-
|
|
2805
|
-
|
|
2945
|
+
else if ( highlight == 'css' && prev == ':' && (next == ';' || next == '!important') ) // CSS value
|
|
2946
|
+
token_classname = "cm-str";
|
|
2806
2947
|
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2948
|
+
else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
|
|
2949
|
+
token_classname = "cm-typ";
|
|
2950
|
+
|
|
2951
|
+
else if ( this._markdownHeader || ( highlight == 'markdown' && isFirstToken && token.replaceAll('#', '').length != token.length ) ) // Header
|
|
2952
|
+
{
|
|
2953
|
+
token_classname = "cm-kwd";
|
|
2954
|
+
this._markdownHeader = true;
|
|
2955
|
+
}
|
|
2815
2956
|
|
|
2816
|
-
|
|
2817
|
-
|
|
2957
|
+
else if ( token[ 0 ] != '@' && token[ 0 ] != ',' && next == '(' )
|
|
2958
|
+
token_classname = "cm-mtd";
|
|
2818
2959
|
|
|
2819
2960
|
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2961
|
+
if( usesBlockComments && this._buildingBlockComment != undefined
|
|
2962
|
+
&& token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
|
|
2963
|
+
{
|
|
2964
|
+
this._blockCommentCache.push( new LX.vec2( this._buildingBlockComment, this._currentLineNumber ) );
|
|
2965
|
+
delete this._buildingBlockComment;
|
|
2966
|
+
}
|
|
2826
2967
|
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2968
|
+
// We finished constructing a string
|
|
2969
|
+
if( this._buildingString && ( this._stringEnded || isLastToken ) )
|
|
2970
|
+
{
|
|
2971
|
+
token = this._getCurrentString();
|
|
2972
|
+
token_classname = "cm-str";
|
|
2973
|
+
discardToken = false;
|
|
2974
|
+
}
|
|
2834
2975
|
|
|
2835
|
-
|
|
2836
|
-
|
|
2976
|
+
// Update state
|
|
2977
|
+
this._buildingString = this._stringEnded ? undefined : this._buildingString;
|
|
2837
2978
|
|
|
2838
|
-
|
|
2839
|
-
|
|
2979
|
+
if( discardToken )
|
|
2980
|
+
return "";
|
|
2840
2981
|
|
|
2841
|
-
|
|
2842
|
-
|
|
2982
|
+
token = token.replace( "<", "<" );
|
|
2983
|
+
token = token.replace( ">", ">" );
|
|
2843
2984
|
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2985
|
+
// No highlighting, no need to put it inside another span..
|
|
2986
|
+
if( !token_classname.length )
|
|
2987
|
+
return token;
|
|
2847
2988
|
|
|
2848
|
-
|
|
2849
|
-
}
|
|
2989
|
+
return "<span class='" + highlight + " " + token_classname + "'>" + token + "</span>";
|
|
2850
2990
|
}
|
|
2851
2991
|
|
|
2852
2992
|
_appendStringToken( token ) {
|
|
@@ -2952,20 +3092,20 @@ class CodeEditor {
|
|
|
2952
3092
|
|
|
2953
3093
|
_encloseSelectedWordWithKey( key, lidx, cursor ) {
|
|
2954
3094
|
|
|
2955
|
-
if( !
|
|
3095
|
+
if( !cursor.selection || (cursor.selection.fromY != cursor.selection.toY) )
|
|
2956
3096
|
return false;
|
|
2957
3097
|
|
|
2958
|
-
|
|
3098
|
+
cursor.selection.invertIfNecessary();
|
|
2959
3099
|
|
|
2960
3100
|
// Insert first..
|
|
2961
3101
|
this.code.lines[ lidx ] = [
|
|
2962
|
-
this.code.lines[ lidx ].slice(0,
|
|
3102
|
+
this.code.lines[ lidx ].slice(0, cursor.selection.fromX),
|
|
2963
3103
|
key,
|
|
2964
|
-
this.code.lines[ lidx ].slice(
|
|
3104
|
+
this.code.lines[ lidx ].slice(cursor.selection.fromX)
|
|
2965
3105
|
].join('');
|
|
2966
3106
|
|
|
2967
3107
|
// Go to the end of the word
|
|
2968
|
-
this.cursorToPosition(cursor,
|
|
3108
|
+
this.cursorToPosition(cursor, cursor.selection.toX + 1);
|
|
2969
3109
|
|
|
2970
3110
|
// Change next key?
|
|
2971
3111
|
switch(key)
|
|
@@ -2986,10 +3126,10 @@ class CodeEditor {
|
|
|
2986
3126
|
|
|
2987
3127
|
// Recompute and reposition current selection
|
|
2988
3128
|
|
|
2989
|
-
|
|
2990
|
-
|
|
3129
|
+
cursor.selection.fromX++;
|
|
3130
|
+
cursor.selection.toX++;
|
|
2991
3131
|
|
|
2992
|
-
this.
|
|
3132
|
+
this._processSelection( cursor );
|
|
2993
3133
|
this.processLine( lidx );
|
|
2994
3134
|
|
|
2995
3135
|
// Stop propagation
|
|
@@ -3037,14 +3177,16 @@ class CodeEditor {
|
|
|
3037
3177
|
|
|
3038
3178
|
startSelection( cursor ) {
|
|
3039
3179
|
|
|
3040
|
-
// Clear other selections...
|
|
3041
|
-
this.selections.innerHTML = "";
|
|
3042
|
-
|
|
3043
3180
|
// Show elements
|
|
3044
|
-
|
|
3181
|
+
let selectionContainer = document.createElement( 'div' );
|
|
3182
|
+
selectionContainer.className = 'selections';
|
|
3183
|
+
selectionContainer.classList.add( 'show' );
|
|
3184
|
+
|
|
3185
|
+
this.codeSizer.insertChildAtIndex( selectionContainer, 2 );
|
|
3186
|
+
this.selections[ cursor.name ] = selectionContainer;
|
|
3045
3187
|
|
|
3046
3188
|
// Create new selection instance
|
|
3047
|
-
|
|
3189
|
+
cursor.selection = new CodeSelection( this, cursor );
|
|
3048
3190
|
}
|
|
3049
3191
|
|
|
3050
3192
|
deleteSelection( cursor ) {
|
|
@@ -3054,37 +3196,52 @@ class CodeEditor {
|
|
|
3054
3196
|
return;
|
|
3055
3197
|
|
|
3056
3198
|
// Some selections don't depend on mouse up..
|
|
3057
|
-
if(
|
|
3199
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
3058
3200
|
|
|
3059
3201
|
const separator = "_NEWLINE_";
|
|
3060
3202
|
let code = this.code.lines.join( separator );
|
|
3061
3203
|
|
|
3062
3204
|
// Get linear start index
|
|
3063
3205
|
let index = 0;
|
|
3064
|
-
for( let i = 0; i <=
|
|
3065
|
-
index += (i ==
|
|
3206
|
+
for( let i = 0; i <= cursor.selection.fromY; i++ )
|
|
3207
|
+
index += (i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length);
|
|
3066
3208
|
|
|
3067
|
-
index +=
|
|
3209
|
+
index += cursor.selection.fromY * separator.length;
|
|
3068
3210
|
|
|
3069
|
-
const num_chars =
|
|
3211
|
+
const num_chars = cursor.selection.chars + (cursor.selection.toY - cursor.selection.fromY) * separator.length;
|
|
3070
3212
|
const pre = code.slice( 0, index );
|
|
3071
3213
|
const post = code.slice( index + num_chars );
|
|
3072
3214
|
|
|
3073
3215
|
this.code.lines = ( pre + post ).split( separator );
|
|
3074
3216
|
|
|
3075
|
-
this.cursorToLine( cursor,
|
|
3076
|
-
this.cursorToPosition( cursor,
|
|
3077
|
-
this.endSelection();
|
|
3217
|
+
this.cursorToLine( cursor, cursor.selection.fromY, true );
|
|
3218
|
+
this.cursorToPosition( cursor, cursor.selection.fromX );
|
|
3219
|
+
this.endSelection( cursor );
|
|
3078
3220
|
this.processLines();
|
|
3079
3221
|
}
|
|
3080
3222
|
|
|
3081
|
-
endSelection() {
|
|
3223
|
+
endSelection( cursor ) {
|
|
3082
3224
|
|
|
3083
|
-
this.selections.classList.remove( 'show' );
|
|
3084
|
-
this.selections.innerHTML = "";
|
|
3085
|
-
delete this.selection;
|
|
3086
3225
|
delete this._tripleClickSelection;
|
|
3087
3226
|
delete this._lastSelectionKeyDir;
|
|
3227
|
+
delete this._currentOcurrences;
|
|
3228
|
+
delete this._lastResult;
|
|
3229
|
+
|
|
3230
|
+
if( cursor )
|
|
3231
|
+
{
|
|
3232
|
+
deleteElement( this.selections[ cursor.name ] );
|
|
3233
|
+
delete this.selections[ cursor.name ];
|
|
3234
|
+
delete cursor.selection;
|
|
3235
|
+
}
|
|
3236
|
+
else
|
|
3237
|
+
{
|
|
3238
|
+
for( let cursor of this.cursors.children )
|
|
3239
|
+
{
|
|
3240
|
+
deleteElement( this.selections[ cursor.name ] );
|
|
3241
|
+
delete this.selections[ cursor.name ];
|
|
3242
|
+
delete cursor.selection;
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3088
3245
|
}
|
|
3089
3246
|
|
|
3090
3247
|
selectAll() {
|
|
@@ -3098,13 +3255,13 @@ class CodeEditor {
|
|
|
3098
3255
|
this.startSelection( cursor );
|
|
3099
3256
|
|
|
3100
3257
|
const nlines = this.code.lines.length - 1;
|
|
3101
|
-
|
|
3102
|
-
|
|
3258
|
+
cursor.selection.toX = this.code.lines[ nlines ].length;
|
|
3259
|
+
cursor.selection.toY = nlines;
|
|
3103
3260
|
|
|
3104
|
-
this.cursorToPosition( cursor,
|
|
3105
|
-
this.cursorToLine( cursor,
|
|
3261
|
+
this.cursorToPosition( cursor, cursor.selection.toX );
|
|
3262
|
+
this.cursorToLine( cursor, cursor.selection.toY );
|
|
3106
3263
|
|
|
3107
|
-
this.
|
|
3264
|
+
this._processSelection( cursor, null, true );
|
|
3108
3265
|
|
|
3109
3266
|
this.hideAutoCompleteBox();
|
|
3110
3267
|
}
|
|
@@ -3210,6 +3367,8 @@ class CodeEditor {
|
|
|
3210
3367
|
|
|
3211
3368
|
state.position = cursor.position;
|
|
3212
3369
|
state.line = cursor.line;
|
|
3370
|
+
state.selection = cursor.selection ? cursor.selection.save() : undefined;
|
|
3371
|
+
|
|
3213
3372
|
return state;
|
|
3214
3373
|
}
|
|
3215
3374
|
|
|
@@ -3242,7 +3401,7 @@ class CodeEditor {
|
|
|
3242
3401
|
const cursorsInLine = Array.from( this.cursors.children ).filter( v => v.line == line );
|
|
3243
3402
|
|
|
3244
3403
|
while( cursorsInLine.length > 1 )
|
|
3245
|
-
cursorsInLine.pop()
|
|
3404
|
+
this.removeCursor( cursorsInLine.pop() );
|
|
3246
3405
|
}
|
|
3247
3406
|
|
|
3248
3407
|
restoreCursor( cursor, state ) {
|
|
@@ -3254,6 +3413,22 @@ class CodeEditor {
|
|
|
3254
3413
|
cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
|
|
3255
3414
|
cursor._top = cursor.line * this.lineHeight;
|
|
3256
3415
|
cursor.style.top = "calc(" + cursor._top + "px)";
|
|
3416
|
+
|
|
3417
|
+
if( state.selection )
|
|
3418
|
+
{
|
|
3419
|
+
this.startSelection( cursor );
|
|
3420
|
+
|
|
3421
|
+
cursor.selection.load( state.selection );
|
|
3422
|
+
|
|
3423
|
+
this._processSelection( cursor, null, true );
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
|
|
3427
|
+
removeCursor( cursor ) {
|
|
3428
|
+
|
|
3429
|
+
deleteElement( this.selections[ cursor.name ] );
|
|
3430
|
+
delete this.selections[ cursor.name ];
|
|
3431
|
+
deleteElement( cursor );
|
|
3257
3432
|
}
|
|
3258
3433
|
|
|
3259
3434
|
resetCursorPos( flag, cursor ) {
|
|
@@ -3275,14 +3450,14 @@ class CodeEditor {
|
|
|
3275
3450
|
}
|
|
3276
3451
|
}
|
|
3277
3452
|
|
|
3278
|
-
|
|
3453
|
+
_addSpaceTabs( cursor, n ) {
|
|
3279
3454
|
|
|
3280
3455
|
for( var i = 0; i < n; ++i ) {
|
|
3281
|
-
this.actions[ 'Tab' ].callback();
|
|
3456
|
+
this.actions[ 'Tab' ].callback( cursor.line, cursor, null );
|
|
3282
3457
|
}
|
|
3283
3458
|
}
|
|
3284
3459
|
|
|
3285
|
-
|
|
3460
|
+
_addSpaces( n ) {
|
|
3286
3461
|
|
|
3287
3462
|
for( var i = 0; i < n; ++i ) {
|
|
3288
3463
|
this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
|
|
@@ -3293,6 +3468,37 @@ class CodeEditor {
|
|
|
3293
3468
|
}
|
|
3294
3469
|
}
|
|
3295
3470
|
|
|
3471
|
+
_removeSpaces( cursor ) {
|
|
3472
|
+
|
|
3473
|
+
// Remove indentation
|
|
3474
|
+
const lidx = cursor.line;
|
|
3475
|
+
const lineStart = firstNonspaceIndex( this.code.lines[ lidx ] );
|
|
3476
|
+
|
|
3477
|
+
// Nothing to remove...
|
|
3478
|
+
if( lineStart == 0 )
|
|
3479
|
+
return;
|
|
3480
|
+
|
|
3481
|
+
let indentSpaces = lineStart % this.tabSpaces;
|
|
3482
|
+
indentSpaces = indentSpaces == 0 ? this.tabSpaces : indentSpaces;
|
|
3483
|
+
const newStart = Math.max( lineStart - indentSpaces, 0 );
|
|
3484
|
+
|
|
3485
|
+
this.code.lines[ lidx ] = [
|
|
3486
|
+
this.code.lines[ lidx ].slice( 0, newStart ),
|
|
3487
|
+
this.code.lines[ lidx ].slice( lineStart )
|
|
3488
|
+
].join('');
|
|
3489
|
+
|
|
3490
|
+
this.processLine( lidx );
|
|
3491
|
+
|
|
3492
|
+
this.cursorToString( cursor, " ".repeat( indentSpaces ), true );
|
|
3493
|
+
|
|
3494
|
+
if( cursor.selection )
|
|
3495
|
+
{
|
|
3496
|
+
cursor.selection.invertIfNecessary();
|
|
3497
|
+
cursor.selection.fromX = Math.max( cursor.selection.fromX - indentSpaces, 0 );
|
|
3498
|
+
this._processSelection( cursor );
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3296
3502
|
getScrollLeft() {
|
|
3297
3503
|
|
|
3298
3504
|
if( !this.codeScroller ) return 0;
|
|
@@ -3562,8 +3768,11 @@ class CodeEditor {
|
|
|
3562
3768
|
|
|
3563
3769
|
showAutoCompleteBox( key, cursor ) {
|
|
3564
3770
|
|
|
3771
|
+
if( !cursor.isMain )
|
|
3772
|
+
return;
|
|
3773
|
+
|
|
3565
3774
|
const [word, start, end] = this.getWordAtPos( cursor, -1 );
|
|
3566
|
-
if(key == ' ' || !word.length) {
|
|
3775
|
+
if( key == ' ' || !word.length ) {
|
|
3567
3776
|
this.hideAutoCompleteBox();
|
|
3568
3777
|
return;
|
|
3569
3778
|
}
|
|
@@ -3610,7 +3819,7 @@ class CodeEditor {
|
|
|
3610
3819
|
pre.appendChild( icon );
|
|
3611
3820
|
|
|
3612
3821
|
pre.addEventListener( 'click', () => {
|
|
3613
|
-
this.autoCompleteWord(
|
|
3822
|
+
this.autoCompleteWord( s );
|
|
3614
3823
|
} );
|
|
3615
3824
|
|
|
3616
3825
|
// Highlight the written part
|
|
@@ -3642,8 +3851,8 @@ class CodeEditor {
|
|
|
3642
3851
|
// Show box
|
|
3643
3852
|
this.autocomplete.classList.toggle('show', true);
|
|
3644
3853
|
this.autocomplete.classList.toggle('no-scrollbar', !(this.autocomplete.scrollHeight > this.autocomplete.offsetHeight));
|
|
3645
|
-
this.autocomplete.style.left = (cursor._left +
|
|
3646
|
-
this.autocomplete.style.top = (cursor._top +
|
|
3854
|
+
this.autocomplete.style.left = (cursor._left + 48 - this.getScrollLeft()) + "px";
|
|
3855
|
+
this.autocomplete.style.top = (cursor._top + 28 + this.lineHeight - this.getScrollTop()) + "px";
|
|
3647
3856
|
|
|
3648
3857
|
this.isAutoCompleteActive = true;
|
|
3649
3858
|
}
|
|
@@ -3656,23 +3865,30 @@ class CodeEditor {
|
|
|
3656
3865
|
return isActive != this.isAutoCompleteActive;
|
|
3657
3866
|
}
|
|
3658
3867
|
|
|
3659
|
-
autoCompleteWord(
|
|
3868
|
+
autoCompleteWord( suggestion ) {
|
|
3660
3869
|
|
|
3661
3870
|
if( !this.isAutoCompleteActive )
|
|
3662
|
-
|
|
3871
|
+
return;
|
|
3663
3872
|
|
|
3664
3873
|
let [suggestedWord, idx] = this._getSelectedAutoComplete();
|
|
3665
3874
|
suggestedWord = suggestion ?? suggestedWord;
|
|
3666
3875
|
|
|
3667
|
-
|
|
3876
|
+
for( let cursor of this.cursors.children )
|
|
3877
|
+
{
|
|
3878
|
+
const [word, start, end] = this.getWordAtPos( cursor, -1 );
|
|
3879
|
+
|
|
3880
|
+
const lineString = this.code.lines[ cursor.line ];
|
|
3881
|
+
this.code.lines[ cursor.line ] =
|
|
3882
|
+
lineString.slice(0, start) + suggestedWord + lineString.slice( end );
|
|
3883
|
+
|
|
3884
|
+
// Process lines and remove suggestion box
|
|
3885
|
+
this.cursorToPosition( cursor, start + suggestedWord.length );
|
|
3886
|
+
this.processLine( cursor.line );
|
|
3887
|
+
}
|
|
3668
3888
|
|
|
3669
|
-
|
|
3670
|
-
this.
|
|
3671
|
-
lineString.slice(0, start) + suggestedWord + lineString.slice(end);
|
|
3889
|
+
// Only the main cursor autocompletes, skip the "Tab" event for the rest
|
|
3890
|
+
this._skipTabs = this.cursors.childElementCount - 1;
|
|
3672
3891
|
|
|
3673
|
-
// Process lines and remove suggestion box
|
|
3674
|
-
this.cursorToPosition(cursor, start + suggestedWord.length);
|
|
3675
|
-
this.processLine(cursor.line);
|
|
3676
3892
|
this.hideAutoCompleteBox();
|
|
3677
3893
|
}
|
|
3678
3894
|
|
|
@@ -3718,6 +3934,8 @@ class CodeEditor {
|
|
|
3718
3934
|
|
|
3719
3935
|
showSearchBox( clear ) {
|
|
3720
3936
|
|
|
3937
|
+
this.hideSearchLineBox();
|
|
3938
|
+
|
|
3721
3939
|
this.searchbox.classList.add( 'opened' );
|
|
3722
3940
|
this.searchboxActive = true;
|
|
3723
3941
|
|
|
@@ -3727,12 +3945,26 @@ class CodeEditor {
|
|
|
3727
3945
|
{
|
|
3728
3946
|
input.value = "";
|
|
3729
3947
|
}
|
|
3948
|
+
else
|
|
3949
|
+
{
|
|
3950
|
+
const cursor = this._getCurrentCursor();
|
|
3951
|
+
|
|
3952
|
+
if( cursor.selection )
|
|
3953
|
+
{
|
|
3954
|
+
input.value = cursor.selection.getText() ?? input.value;
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
input.selectionStart = 0;
|
|
3959
|
+
input.selectionEnd = input.value.length;
|
|
3730
3960
|
|
|
3731
3961
|
input.focus();
|
|
3732
3962
|
}
|
|
3733
3963
|
|
|
3734
3964
|
hideSearchBox() {
|
|
3735
3965
|
|
|
3966
|
+
const active = this.searchboxActive;
|
|
3967
|
+
|
|
3736
3968
|
if( this.searchboxActive )
|
|
3737
3969
|
{
|
|
3738
3970
|
this.searchbox.classList.remove( 'opened' );
|
|
@@ -3741,31 +3973,49 @@ class CodeEditor {
|
|
|
3741
3973
|
|
|
3742
3974
|
else if( this._lastResult )
|
|
3743
3975
|
{
|
|
3744
|
-
this._lastResult.dom
|
|
3976
|
+
deleteElement( this._lastResult.dom );
|
|
3745
3977
|
delete this._lastResult;
|
|
3746
3978
|
}
|
|
3979
|
+
|
|
3980
|
+
this.searchResultSelections.classList.remove( 'show' );
|
|
3981
|
+
|
|
3982
|
+
return active != this.searchboxActive;
|
|
3747
3983
|
}
|
|
3748
3984
|
|
|
3749
|
-
search( text, reverse ) {
|
|
3985
|
+
search( text, reverse, callback, skipAlert ) {
|
|
3750
3986
|
|
|
3751
3987
|
text = text ?? this._lastTextFound;
|
|
3752
3988
|
|
|
3753
3989
|
if( !text )
|
|
3754
3990
|
return;
|
|
3755
3991
|
|
|
3756
|
-
let
|
|
3992
|
+
let cursor = this._getCurrentCursor();
|
|
3993
|
+
let cursorData = new LX.vec2( cursor.position, cursor.line );
|
|
3757
3994
|
let line = null;
|
|
3758
3995
|
let char = -1;
|
|
3759
3996
|
|
|
3760
3997
|
if( this._lastResult )
|
|
3761
3998
|
{
|
|
3762
|
-
this._lastResult.dom
|
|
3999
|
+
deleteElement( this._lastResult.dom );
|
|
3763
4000
|
cursorData = this._lastResult.pos;
|
|
3764
4001
|
delete this._lastResult;
|
|
3765
4002
|
}
|
|
3766
4003
|
|
|
3767
4004
|
const getIndex = l => {
|
|
3768
|
-
|
|
4005
|
+
|
|
4006
|
+
var string = this.code.lines[ l ];
|
|
4007
|
+
|
|
4008
|
+
if( reverse )
|
|
4009
|
+
{
|
|
4010
|
+
string = string.substr( 0, l == cursorData.y ? cursorData.x : string.length );
|
|
4011
|
+
var reversed = strReverse( string );
|
|
4012
|
+
var reversedIdx = reversed.indexOf( strReverse( text ) );
|
|
4013
|
+
return reversedIdx == -1 ? -1 : string.length - reversedIdx - text.length;
|
|
4014
|
+
}
|
|
4015
|
+
else
|
|
4016
|
+
{
|
|
4017
|
+
return string.substr( l == cursorData.y ? cursorData.x : 0 ).indexOf( text );
|
|
4018
|
+
}
|
|
3769
4019
|
};
|
|
3770
4020
|
|
|
3771
4021
|
if( reverse )
|
|
@@ -3795,7 +4045,15 @@ class CodeEditor {
|
|
|
3795
4045
|
|
|
3796
4046
|
if( line == null)
|
|
3797
4047
|
{
|
|
3798
|
-
|
|
4048
|
+
if( !skipAlert )
|
|
4049
|
+
alert( "No results!" );
|
|
4050
|
+
|
|
4051
|
+
const lastLine = this.code.lines.length - 1;
|
|
4052
|
+
|
|
4053
|
+
this._lastResult = {
|
|
4054
|
+
'dom': this.searchResultSelections.lastChild,
|
|
4055
|
+
'pos': reverse ? new LX.vec2( this.code.lines[ lastLine ].length, lastLine ) : new LX.vec2( 0, 0 )
|
|
4056
|
+
};
|
|
3799
4057
|
return;
|
|
3800
4058
|
}
|
|
3801
4059
|
|
|
@@ -3805,32 +4063,44 @@ class CodeEditor {
|
|
|
3805
4063
|
have to add the length of the substring (0, first_ocurrence)
|
|
3806
4064
|
*/
|
|
3807
4065
|
|
|
3808
|
-
|
|
4066
|
+
|
|
4067
|
+
if( !reverse )
|
|
4068
|
+
char += ( line == cursorData.y ? cursorData.x : 0 );
|
|
4069
|
+
|
|
3809
4070
|
|
|
3810
4071
|
// Text found..
|
|
3811
4072
|
|
|
3812
4073
|
this._lastTextFound = text;
|
|
3813
4074
|
|
|
3814
4075
|
this.codeScroller.scrollTo(
|
|
3815
|
-
Math.max( char * this.charWidth - this.codeScroller.clientWidth ),
|
|
3816
|
-
Math.max( line - 10 ) * this.lineHeight
|
|
4076
|
+
Math.max( char * this.charWidth - this.codeScroller.clientWidth, 0 ),
|
|
4077
|
+
Math.max( line - 10, 0 ) * this.lineHeight
|
|
3817
4078
|
);
|
|
3818
4079
|
|
|
3819
|
-
|
|
3820
|
-
|
|
4080
|
+
if( callback )
|
|
4081
|
+
{
|
|
4082
|
+
callback( char, line );
|
|
4083
|
+
}
|
|
4084
|
+
else
|
|
4085
|
+
{
|
|
4086
|
+
// Show elements
|
|
4087
|
+
this.searchResultSelections.classList.add( 'show' );
|
|
4088
|
+
|
|
4089
|
+
// Create new selection instance
|
|
4090
|
+
cursor.selection = new CodeSelection( this, cursor, "lexcodesearchresult" );
|
|
4091
|
+
cursor.selection.selectInline( cursor, char, line, this.measureString( text ), true );
|
|
4092
|
+
}
|
|
3821
4093
|
|
|
3822
|
-
// Create new selection instance
|
|
3823
|
-
this.selection = new CodeSelection( this, 0, 0, "lexcodesearchresult" );
|
|
3824
|
-
this.selection.selectInline( char, line, this.measureString( text ) );
|
|
3825
4094
|
this._lastResult = {
|
|
3826
|
-
'dom': this.
|
|
3827
|
-
'pos': new LX.vec2( char + text.length, line )
|
|
4095
|
+
'dom': this.searchResultSelections.lastChild,
|
|
4096
|
+
'pos': new LX.vec2( char + text.length * ( reverse ? -1 : 1 ) , line )
|
|
3828
4097
|
};
|
|
3829
|
-
|
|
3830
4098
|
}
|
|
3831
4099
|
|
|
3832
4100
|
showSearchLineBox() {
|
|
3833
4101
|
|
|
4102
|
+
this.hideSearchBox();
|
|
4103
|
+
|
|
3834
4104
|
this.searchlinebox.classList.add( 'opened' );
|
|
3835
4105
|
this.searchlineboxActive = true;
|
|
3836
4106
|
|
|
@@ -3860,6 +4130,40 @@ class CodeEditor {
|
|
|
3860
4130
|
this.cursorToLine( cursor, line - 1, true );
|
|
3861
4131
|
}
|
|
3862
4132
|
|
|
4133
|
+
selectNextOcurrence( cursor ) {
|
|
4134
|
+
|
|
4135
|
+
if( !cursor.selection )
|
|
4136
|
+
return;
|
|
4137
|
+
|
|
4138
|
+
const text = cursor.selection.getText();
|
|
4139
|
+
if( !text )
|
|
4140
|
+
return;
|
|
4141
|
+
|
|
4142
|
+
if( !this._currentOcurrences )
|
|
4143
|
+
{
|
|
4144
|
+
const currentKey = [ cursor.position - text.length, cursor.line ].join( '_' );
|
|
4145
|
+
this._currentOcurrences = { };
|
|
4146
|
+
this._currentOcurrences[ currentKey ] = true;
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
this.search( text, false, (col, ln) => {
|
|
4150
|
+
|
|
4151
|
+
const key = [ col, ln ].join( '_' );
|
|
4152
|
+
|
|
4153
|
+
if( this._currentOcurrences[ key ] ) {
|
|
4154
|
+
return;
|
|
4155
|
+
}
|
|
4156
|
+
|
|
4157
|
+
var newCursor = this._addCursor( ln, col, true );
|
|
4158
|
+
this.startSelection( newCursor );
|
|
4159
|
+
newCursor.selection.selectInline( newCursor, col, ln, this.measureString( text ) );
|
|
4160
|
+
this.cursorToString( newCursor, text );
|
|
4161
|
+
|
|
4162
|
+
this._currentOcurrences[ key ] = true;
|
|
4163
|
+
|
|
4164
|
+
}, true );
|
|
4165
|
+
}
|
|
4166
|
+
|
|
3863
4167
|
_updateDataInfoPanel( signal, value ) {
|
|
3864
4168
|
|
|
3865
4169
|
if( !this.skipCodeInfo )
|
|
@@ -3962,13 +4266,14 @@ class CodeEditor {
|
|
|
3962
4266
|
delete this._pendingString;
|
|
3963
4267
|
delete this._buildingBlockComment;
|
|
3964
4268
|
delete this._markdownHeader;
|
|
4269
|
+
delete this._lastResult;
|
|
3965
4270
|
}
|
|
3966
4271
|
}
|
|
3967
4272
|
|
|
3968
4273
|
CodeEditor.keywords = {
|
|
3969
4274
|
|
|
3970
4275
|
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
|
|
3971
|
-
'arguments', 'extends', 'instanceof'],
|
|
4276
|
+
'arguments', 'extends', 'instanceof', 'Infinity'],
|
|
3972
4277
|
'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
|
|
3973
4278
|
'NULL', 'unsigned', 'namespace'],
|
|
3974
4279
|
'JSON': ['true', 'false'],
|
|
@@ -4001,7 +4306,8 @@ CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to
|
|
|
4001
4306
|
|
|
4002
4307
|
CodeEditor.types = {
|
|
4003
4308
|
|
|
4004
|
-
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder'
|
|
4309
|
+
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent',
|
|
4310
|
+
'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element'],
|
|
4005
4311
|
'Rust': ['u128'],
|
|
4006
4312
|
'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
|
|
4007
4313
|
'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
|