lexgui 0.1.25 → 0.1.26
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 +416 -174
- package/build/lexgui.js +2 -2
- package/build/lexgui.module.js +2 -2
- package/changelog.md +11 -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,9 +365,13 @@ 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";
|
|
@@ -566,8 +610,12 @@ class CodeEditor {
|
|
|
566
610
|
// Action keys
|
|
567
611
|
|
|
568
612
|
this.action( 'Escape', false, ( ln, cursor, e ) => {
|
|
569
|
-
this.hideAutoCompleteBox()
|
|
570
|
-
|
|
613
|
+
if( this.hideAutoCompleteBox() )
|
|
614
|
+
return;
|
|
615
|
+
if( this.hideSearchBox() )
|
|
616
|
+
return;
|
|
617
|
+
// Remove selections and cursors
|
|
618
|
+
this.endSelection();
|
|
571
619
|
this._removeSecondaryCursors();
|
|
572
620
|
});
|
|
573
621
|
|
|
@@ -575,7 +623,7 @@ class CodeEditor {
|
|
|
575
623
|
|
|
576
624
|
this._addUndoStep( cursor );
|
|
577
625
|
|
|
578
|
-
if(
|
|
626
|
+
if( cursor.selection ) {
|
|
579
627
|
this.deleteSelection( cursor );
|
|
580
628
|
// Remove entire line when selecting with triple click
|
|
581
629
|
if( this._tripleClickSelection )
|
|
@@ -629,7 +677,7 @@ class CodeEditor {
|
|
|
629
677
|
|
|
630
678
|
this._addUndoStep( cursor );
|
|
631
679
|
|
|
632
|
-
if(
|
|
680
|
+
if( cursor.selection ) {
|
|
633
681
|
// Use 'Backspace' as it's the same callback...
|
|
634
682
|
this.actions['Backspace'].callback( ln, cursor, e );
|
|
635
683
|
}
|
|
@@ -640,7 +688,7 @@ class CodeEditor {
|
|
|
640
688
|
this.code.lines[ ln ] = sliceChars( this.code.lines[ ln ], cursor.position );
|
|
641
689
|
this.processLine( ln );
|
|
642
690
|
}
|
|
643
|
-
else if(this.code.lines[ ln + 1 ] != undefined) {
|
|
691
|
+
else if( this.code.lines[ ln + 1 ] != undefined ) {
|
|
644
692
|
this.code.lines[ ln ] += this.code.lines[ ln + 1 ];
|
|
645
693
|
this.code.lines.splice( ln + 1, 1 );
|
|
646
694
|
this.processLines();
|
|
@@ -679,18 +727,18 @@ class CodeEditor {
|
|
|
679
727
|
if( e.shiftKey && !e.cancelShift )
|
|
680
728
|
{
|
|
681
729
|
// Get last selection range
|
|
682
|
-
if(
|
|
683
|
-
lastX +=
|
|
730
|
+
if( cursor.selection )
|
|
731
|
+
lastX += cursor.selection.chars;
|
|
684
732
|
|
|
685
|
-
if( !
|
|
733
|
+
if( !cursor.selection )
|
|
686
734
|
this.startSelection( cursor );
|
|
687
735
|
|
|
688
736
|
var string = this.code.lines[ ln ].substring( idx, lastX );
|
|
689
|
-
if(
|
|
690
|
-
|
|
737
|
+
if( cursor.selection.sameLine() )
|
|
738
|
+
cursor.selection.selectInline( cursor, idx, cursor.line, this.measureString( string ) );
|
|
691
739
|
else
|
|
692
740
|
{
|
|
693
|
-
this.
|
|
741
|
+
this._processSelection( cursor, e );
|
|
694
742
|
}
|
|
695
743
|
} else if( !e.keepSelection )
|
|
696
744
|
this.endSelection();
|
|
@@ -701,15 +749,15 @@ class CodeEditor {
|
|
|
701
749
|
if( ( e.shiftKey || e._shiftKey ) && !e.cancelShift ) {
|
|
702
750
|
|
|
703
751
|
var string = this.code.lines[ ln ].substring( cursor.position );
|
|
704
|
-
if( !
|
|
752
|
+
if( !cursor.selection )
|
|
705
753
|
this.startSelection( cursor );
|
|
706
|
-
if(
|
|
707
|
-
|
|
754
|
+
if( cursor.selection.sameLine() )
|
|
755
|
+
cursor.selection.selectInline( cursor, cursor.position, cursor.line, this.measureString( string ));
|
|
708
756
|
else
|
|
709
757
|
{
|
|
710
758
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
711
759
|
this.cursorToString( cursor, this.code.lines[ ln ] );
|
|
712
|
-
this.
|
|
760
|
+
this._processSelection( cursor, e );
|
|
713
761
|
}
|
|
714
762
|
} else if( !e.keepSelection )
|
|
715
763
|
this.endSelection();
|
|
@@ -771,7 +819,7 @@ class CodeEditor {
|
|
|
771
819
|
if( !this.isAutoCompleteActive )
|
|
772
820
|
{
|
|
773
821
|
if( e.shiftKey ) {
|
|
774
|
-
if( !
|
|
822
|
+
if( !cursor.selection )
|
|
775
823
|
this.startSelection( cursor );
|
|
776
824
|
|
|
777
825
|
this.lineUp( cursor );
|
|
@@ -781,7 +829,7 @@ class CodeEditor {
|
|
|
781
829
|
this.cursorToPosition( cursor, this.code.lines[ cursor.line ].length );
|
|
782
830
|
}
|
|
783
831
|
|
|
784
|
-
this.
|
|
832
|
+
this._processSelection( cursor, e, false );
|
|
785
833
|
|
|
786
834
|
} else {
|
|
787
835
|
this.endSelection();
|
|
@@ -804,7 +852,7 @@ class CodeEditor {
|
|
|
804
852
|
if( !this.isAutoCompleteActive )
|
|
805
853
|
{
|
|
806
854
|
if( e.shiftKey ) {
|
|
807
|
-
if( !
|
|
855
|
+
if( !cursor.selection )
|
|
808
856
|
this.startSelection( cursor );
|
|
809
857
|
} else {
|
|
810
858
|
this.endSelection();
|
|
@@ -819,7 +867,7 @@ class CodeEditor {
|
|
|
819
867
|
}
|
|
820
868
|
|
|
821
869
|
if( e.shiftKey ) {
|
|
822
|
-
this.
|
|
870
|
+
this._processSelection( cursor, e );
|
|
823
871
|
}
|
|
824
872
|
}
|
|
825
873
|
// Move down autocomplete selection
|
|
@@ -853,37 +901,37 @@ class CodeEditor {
|
|
|
853
901
|
var diff = Math.max( cursor.position - from, 1 );
|
|
854
902
|
var substr = word.substr( 0, diff );
|
|
855
903
|
// Selections...
|
|
856
|
-
if( e.shiftKey ) { if( !
|
|
904
|
+
if( e.shiftKey ) { if( !cursor.selection ) this.startSelection( cursor ); }
|
|
857
905
|
else this.endSelection();
|
|
858
906
|
this.cursorToString( cursor, substr, true );
|
|
859
|
-
if( e.shiftKey ) this.
|
|
907
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false, true );
|
|
860
908
|
}
|
|
861
909
|
else {
|
|
862
910
|
var letter = this.getCharAtPos( cursor, -1 );
|
|
863
911
|
if( letter ) {
|
|
864
912
|
if( e.shiftKey ) {
|
|
865
|
-
if( !
|
|
913
|
+
if( !cursor.selection ) this.startSelection( cursor );
|
|
866
914
|
this.cursorToLeft( letter, cursor );
|
|
867
|
-
this.
|
|
915
|
+
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
868
916
|
}
|
|
869
917
|
else {
|
|
870
|
-
if( !
|
|
918
|
+
if( !cursor.selection ) {
|
|
871
919
|
this.cursorToLeft( letter, cursor );
|
|
872
920
|
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
873
921
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
874
922
|
}
|
|
875
923
|
else {
|
|
876
|
-
|
|
924
|
+
cursor.selection.invertIfNecessary();
|
|
877
925
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
|
|
878
|
-
this.cursorToLine( cursor,
|
|
879
|
-
this.cursorToPosition( cursor,
|
|
926
|
+
this.cursorToLine( cursor, cursor.selection.fromY, true );
|
|
927
|
+
this.cursorToPosition( cursor, cursor.selection.fromX );
|
|
880
928
|
this.endSelection();
|
|
881
929
|
}
|
|
882
930
|
}
|
|
883
931
|
}
|
|
884
932
|
else if( cursor.line > 0 ) {
|
|
885
933
|
|
|
886
|
-
if( e.shiftKey && !
|
|
934
|
+
if( e.shiftKey && !cursor.selection ) this.startSelection( cursor );
|
|
887
935
|
|
|
888
936
|
this.lineUp( cursor );
|
|
889
937
|
|
|
@@ -891,7 +939,7 @@ class CodeEditor {
|
|
|
891
939
|
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
892
940
|
delete e.cancelShift; delete e.keepSelection;
|
|
893
941
|
|
|
894
|
-
if( e.shiftKey ) this.
|
|
942
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false );
|
|
895
943
|
}
|
|
896
944
|
}
|
|
897
945
|
});
|
|
@@ -903,40 +951,61 @@ class CodeEditor {
|
|
|
903
951
|
cursor.position == this.code.lines[ cursor.line ].length )
|
|
904
952
|
return;
|
|
905
953
|
|
|
906
|
-
if( e.metaKey )
|
|
954
|
+
if( e.metaKey ) // Apple devices (Command)
|
|
955
|
+
{
|
|
907
956
|
e.preventDefault();
|
|
908
957
|
this.actions[ 'End' ].callback( ln, cursor );
|
|
909
|
-
}
|
|
958
|
+
}
|
|
959
|
+
else if( e.ctrlKey ) // Next word
|
|
960
|
+
{
|
|
910
961
|
// Get next word
|
|
911
962
|
const [ word, from, to ] = this.getWordAtPos( cursor );
|
|
963
|
+
|
|
912
964
|
// If no length, we change line..
|
|
913
965
|
if( !word.length ) this.lineDown( cursor, true );
|
|
914
966
|
var diff = cursor.position - from;
|
|
915
967
|
var substr = word.substr( diff );
|
|
968
|
+
|
|
916
969
|
// Selections...
|
|
917
|
-
if( e.shiftKey ) {
|
|
918
|
-
|
|
970
|
+
if( e.shiftKey ) {
|
|
971
|
+
if( !cursor.selection )
|
|
972
|
+
this.startSelection( cursor );
|
|
973
|
+
}
|
|
974
|
+
else
|
|
975
|
+
this.endSelection();
|
|
976
|
+
|
|
919
977
|
this.cursorToString( cursor, substr );
|
|
920
|
-
|
|
921
|
-
|
|
978
|
+
|
|
979
|
+
if( e.shiftKey )
|
|
980
|
+
this._processSelection( cursor, e );
|
|
981
|
+
}
|
|
982
|
+
else // Next char
|
|
983
|
+
{
|
|
922
984
|
var letter = this.getCharAtPos( cursor );
|
|
923
985
|
if( letter ) {
|
|
924
|
-
|
|
925
|
-
|
|
986
|
+
|
|
987
|
+
// Selecting chars
|
|
988
|
+
if( e.shiftKey )
|
|
989
|
+
{
|
|
990
|
+
if( !cursor.selection )
|
|
991
|
+
this.startSelection( cursor );
|
|
992
|
+
|
|
926
993
|
this.cursorToRight( letter, cursor );
|
|
927
|
-
this.
|
|
928
|
-
}
|
|
929
|
-
|
|
994
|
+
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
995
|
+
}
|
|
996
|
+
else
|
|
997
|
+
{
|
|
998
|
+
if( !cursor.selection ) {
|
|
930
999
|
this.cursorToRight( letter, cursor );
|
|
931
1000
|
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
932
1001
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
933
1002
|
}
|
|
934
|
-
else
|
|
1003
|
+
else
|
|
935
1004
|
{
|
|
936
|
-
|
|
1005
|
+
cursor.selection.invertIfNecessary();
|
|
937
1006
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
|
|
938
|
-
this.cursorToLine( cursor,
|
|
939
|
-
this.cursorToPosition( cursor,
|
|
1007
|
+
this.cursorToLine( cursor, cursor.selection.toY );
|
|
1008
|
+
this.cursorToPosition( cursor, cursor.selection.toX );
|
|
940
1009
|
this.endSelection();
|
|
941
1010
|
}
|
|
942
1011
|
}
|
|
@@ -944,12 +1013,13 @@ class CodeEditor {
|
|
|
944
1013
|
else if( this.code.lines[ cursor.line + 1 ] !== undefined ) {
|
|
945
1014
|
|
|
946
1015
|
if( e.shiftKey ) {
|
|
947
|
-
if( !
|
|
1016
|
+
if( !cursor.selection ) this.startSelection( cursor );
|
|
948
1017
|
}
|
|
1018
|
+
else this.endSelection();
|
|
949
1019
|
|
|
950
1020
|
this.lineDown( cursor, true );
|
|
951
1021
|
|
|
952
|
-
if( e.shiftKey ) this.
|
|
1022
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false );
|
|
953
1023
|
|
|
954
1024
|
this.hideAutoCompleteBox();
|
|
955
1025
|
}
|
|
@@ -1049,14 +1119,14 @@ class CodeEditor {
|
|
|
1049
1119
|
|
|
1050
1120
|
let lidx = cursor.line;
|
|
1051
1121
|
|
|
1052
|
-
if(
|
|
1122
|
+
if( cursor.selection ) {
|
|
1053
1123
|
this.deleteSelection( cursor );
|
|
1054
1124
|
lidx = cursor.line;
|
|
1055
1125
|
}
|
|
1056
1126
|
|
|
1057
1127
|
this.endSelection();
|
|
1058
1128
|
|
|
1059
|
-
const new_lines = text.split( '\n' );
|
|
1129
|
+
const new_lines = text.replaceAll( '\r', '' ).split( '\n' );
|
|
1060
1130
|
|
|
1061
1131
|
// Pasting Multiline...
|
|
1062
1132
|
if( new_lines.length != 1 )
|
|
@@ -1111,10 +1181,16 @@ class CodeEditor {
|
|
|
1111
1181
|
|
|
1112
1182
|
const inner_add_tab = ( text, name, title ) => {
|
|
1113
1183
|
|
|
1184
|
+
// Remove Carriage Return in some cases and sub tabs using spaces
|
|
1185
|
+
text = text.replaceAll( '\r', '' );
|
|
1186
|
+
text = text.replaceAll( /\t|\\t/g, ' '.repeat( this.tabSpaces ) );
|
|
1187
|
+
|
|
1114
1188
|
// Set current text and language
|
|
1115
|
-
|
|
1189
|
+
|
|
1190
|
+
const lines = text.split( '\n' );
|
|
1116
1191
|
|
|
1117
1192
|
// Add item in the explorer if used
|
|
1193
|
+
|
|
1118
1194
|
if( this.explorer )
|
|
1119
1195
|
{
|
|
1120
1196
|
this._storedLines = this._storedLines ?? {};
|
|
@@ -1163,6 +1239,7 @@ class CodeEditor {
|
|
|
1163
1239
|
}
|
|
1164
1240
|
|
|
1165
1241
|
let cursor = document.createElement( 'div' );
|
|
1242
|
+
cursor.name = "cursor" + this.cursors.childElementCount;
|
|
1166
1243
|
cursor.className = "cursor";
|
|
1167
1244
|
cursor.innerHTML = " ";
|
|
1168
1245
|
cursor.isMain = isMain;
|
|
@@ -1639,12 +1716,12 @@ class CodeEditor {
|
|
|
1639
1716
|
{
|
|
1640
1717
|
this.processClick( e );
|
|
1641
1718
|
|
|
1642
|
-
this.canOpenContextMenu = !
|
|
1719
|
+
this.canOpenContextMenu = !cursor.selection;
|
|
1643
1720
|
|
|
1644
|
-
if(
|
|
1721
|
+
if( cursor.selection )
|
|
1645
1722
|
{
|
|
1646
|
-
this.canOpenContextMenu |= (cursor.line >=
|
|
1647
|
-
&& cursor.position >=
|
|
1723
|
+
this.canOpenContextMenu |= (cursor.line >= cursor.selection.fromY && cursor.line <= cursor.selection.toY
|
|
1724
|
+
&& cursor.position >= cursor.selection.fromX && cursor.position <= cursor.selection.toX);
|
|
1648
1725
|
if( this.canOpenContextMenu )
|
|
1649
1726
|
return;
|
|
1650
1727
|
}
|
|
@@ -1664,7 +1741,7 @@ class CodeEditor {
|
|
|
1664
1741
|
else if( e.type == 'mousemove' )
|
|
1665
1742
|
{
|
|
1666
1743
|
if( this.state.selectingText )
|
|
1667
|
-
this.
|
|
1744
|
+
this.processSelections( e );
|
|
1668
1745
|
}
|
|
1669
1746
|
|
|
1670
1747
|
else if ( e.type == 'click' ) // trip
|
|
@@ -1676,7 +1753,7 @@ class CodeEditor {
|
|
|
1676
1753
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
1677
1754
|
this.cursorToPosition( cursor, from );
|
|
1678
1755
|
this.startSelection( cursor );
|
|
1679
|
-
|
|
1756
|
+
cursor.selection.selectInline( cursor, from, cursor.line, this.measureString( word ) );
|
|
1680
1757
|
this.cursorToString( cursor, word ); // Go to the end of the word
|
|
1681
1758
|
break;
|
|
1682
1759
|
// Select entire line
|
|
@@ -1705,6 +1782,8 @@ class CodeEditor {
|
|
|
1705
1782
|
m.add( "" );
|
|
1706
1783
|
m.add( "Format/JSON", () => {
|
|
1707
1784
|
let json = this.toJSONFormat( this.getText() );
|
|
1785
|
+
if( !json )
|
|
1786
|
+
return;
|
|
1708
1787
|
this.code.lines = json.split( "\n" );
|
|
1709
1788
|
this.processLines();
|
|
1710
1789
|
} );
|
|
@@ -1722,10 +1801,9 @@ class CodeEditor {
|
|
|
1722
1801
|
this.endSelection();
|
|
1723
1802
|
}
|
|
1724
1803
|
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
}
|
|
1804
|
+
const cursor = this._getCurrentCursor();
|
|
1805
|
+
if( cursor.selection )
|
|
1806
|
+
cursor.selection.invertIfNecessary();
|
|
1729
1807
|
|
|
1730
1808
|
this.state.selectingText = false;
|
|
1731
1809
|
delete this._lastSelectionKeyDir;
|
|
@@ -1764,13 +1842,32 @@ class CodeEditor {
|
|
|
1764
1842
|
this.hideAutoCompleteBox();
|
|
1765
1843
|
}
|
|
1766
1844
|
|
|
1767
|
-
|
|
1845
|
+
updateSelections( e, keep_range, flags = CodeEditor.SELECTION_X_Y ) {
|
|
1846
|
+
|
|
1847
|
+
for( let cursor of this.cursors.children )
|
|
1848
|
+
{
|
|
1849
|
+
if( !cursor.selection )
|
|
1850
|
+
continue;
|
|
1851
|
+
|
|
1852
|
+
this._processSelection( cursor, e, keep_range, flags );
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
processSelections( e, keep_range, flags = CodeEditor.SELECTION_X_Y ) {
|
|
1857
|
+
|
|
1858
|
+
for( let cursor of this.cursors.children )
|
|
1859
|
+
{
|
|
1860
|
+
this._processSelection( cursor, e, keep_range, flags );
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
_processSelection( cursor, e, keep_range, flags = CodeEditor.SELECTION_X_Y ) {
|
|
1768
1865
|
|
|
1769
|
-
var cursor = this._getCurrentCursor();
|
|
1770
1866
|
const isMouseEvent = e && ( e.constructor == MouseEvent );
|
|
1771
1867
|
|
|
1772
1868
|
if( isMouseEvent ) this.processClick( e );
|
|
1773
|
-
|
|
1869
|
+
|
|
1870
|
+
if( !cursor.selection )
|
|
1774
1871
|
this.startSelection( cursor );
|
|
1775
1872
|
|
|
1776
1873
|
this._hideActiveLine();
|
|
@@ -1781,46 +1878,48 @@ class CodeEditor {
|
|
|
1781
1878
|
let ccw = true;
|
|
1782
1879
|
|
|
1783
1880
|
// Check if we must change ccw or not ... (not with mouse)
|
|
1784
|
-
if( !isMouseEvent && this.line >=
|
|
1785
|
-
(this.line ==
|
|
1881
|
+
if( !isMouseEvent && this.line >= cursor.selection.fromY &&
|
|
1882
|
+
(this.line == cursor.selection.fromY ? this.position >= cursor.selection.fromX : true) )
|
|
1786
1883
|
{
|
|
1787
1884
|
ccw = ( e && this._lastSelectionKeyDir && ( e.key == 'ArrowRight' || e.key == 'ArrowDown' || e.key == 'End' ) );
|
|
1788
1885
|
}
|
|
1789
1886
|
|
|
1790
1887
|
if( ccw )
|
|
1791
1888
|
{
|
|
1792
|
-
if( flags & CodeEditor.SELECTION_X )
|
|
1793
|
-
if( flags & CodeEditor.SELECTION_Y )
|
|
1889
|
+
if( flags & CodeEditor.SELECTION_X ) cursor.selection.fromX = cursor.position;
|
|
1890
|
+
if( flags & CodeEditor.SELECTION_Y ) cursor.selection.fromY = cursor.line;
|
|
1794
1891
|
}
|
|
1795
1892
|
else
|
|
1796
1893
|
{
|
|
1797
|
-
if( flags & CodeEditor.SELECTION_X )
|
|
1798
|
-
if( flags & CodeEditor.SELECTION_Y )
|
|
1894
|
+
if( flags & CodeEditor.SELECTION_X ) cursor.selection.toX = cursor.position;
|
|
1895
|
+
if( flags & CodeEditor.SELECTION_Y ) cursor.selection.toY = cursor.line;
|
|
1799
1896
|
}
|
|
1800
1897
|
|
|
1801
1898
|
this._lastSelectionKeyDir = ccw;
|
|
1802
1899
|
}
|
|
1803
1900
|
|
|
1804
1901
|
// Only leave if not a mouse selection...
|
|
1805
|
-
if( !isMouseEvent &&
|
|
1902
|
+
if( !isMouseEvent && cursor.selection.isEmpty() )
|
|
1806
1903
|
{
|
|
1807
1904
|
this.endSelection();
|
|
1808
1905
|
return;
|
|
1809
1906
|
}
|
|
1810
1907
|
|
|
1811
|
-
|
|
1908
|
+
cursor.selection.chars = 0;
|
|
1812
1909
|
|
|
1813
|
-
const fromX =
|
|
1814
|
-
fromY =
|
|
1815
|
-
toX =
|
|
1816
|
-
toY =
|
|
1910
|
+
const fromX = cursor.selection.fromX,
|
|
1911
|
+
fromY = cursor.selection.fromY,
|
|
1912
|
+
toX = cursor.selection.toX,
|
|
1913
|
+
toY = cursor.selection.toY;
|
|
1817
1914
|
const deltaY = toY - fromY;
|
|
1818
1915
|
|
|
1916
|
+
let cursorSelections = this.selections[ cursor.name ];
|
|
1917
|
+
|
|
1819
1918
|
// Selection goes down...
|
|
1820
1919
|
if( deltaY >= 0 )
|
|
1821
1920
|
{
|
|
1822
|
-
while( deltaY < (
|
|
1823
|
-
deleteElement(
|
|
1921
|
+
while( deltaY < ( cursorSelections.childElementCount - 1 ) )
|
|
1922
|
+
deleteElement( cursorSelections.lastChild );
|
|
1824
1923
|
|
|
1825
1924
|
for(let i = fromY; i <= toY; i++){
|
|
1826
1925
|
|
|
@@ -1831,12 +1930,12 @@ class CodeEditor {
|
|
|
1831
1930
|
if( isVisible )
|
|
1832
1931
|
{
|
|
1833
1932
|
// Make sure that the line selection is generated...
|
|
1834
|
-
domEl =
|
|
1933
|
+
domEl = cursorSelections.childNodes[ sId ];
|
|
1835
1934
|
if(!domEl)
|
|
1836
1935
|
{
|
|
1837
1936
|
domEl = document.createElement( 'div' );
|
|
1838
1937
|
domEl.className = "lexcodeselection";
|
|
1839
|
-
|
|
1938
|
+
cursorSelections.appendChild( domEl );
|
|
1840
1939
|
}
|
|
1841
1940
|
}
|
|
1842
1941
|
|
|
@@ -1858,7 +1957,7 @@ class CodeEditor {
|
|
|
1858
1957
|
}
|
|
1859
1958
|
|
|
1860
1959
|
const stringWidth = this.measureString( string );
|
|
1861
|
-
|
|
1960
|
+
cursor.selection.chars += stringWidth / this.charWidth;
|
|
1862
1961
|
|
|
1863
1962
|
if( isVisible )
|
|
1864
1963
|
{
|
|
@@ -1870,8 +1969,8 @@ class CodeEditor {
|
|
|
1870
1969
|
}
|
|
1871
1970
|
else // Selection goes up...
|
|
1872
1971
|
{
|
|
1873
|
-
while( Math.abs( deltaY ) < (
|
|
1874
|
-
deleteElement(
|
|
1972
|
+
while( Math.abs( deltaY ) < ( cursorSelections.childElementCount - 1 ) )
|
|
1973
|
+
deleteElement( cursorSelections.firstChild );
|
|
1875
1974
|
|
|
1876
1975
|
for( let i = toY; i <= fromY; i++ ){
|
|
1877
1976
|
|
|
@@ -1882,12 +1981,12 @@ class CodeEditor {
|
|
|
1882
1981
|
if( isVisible )
|
|
1883
1982
|
{
|
|
1884
1983
|
// Make sure that the line selection is generated...
|
|
1885
|
-
domEl =
|
|
1984
|
+
domEl = cursorSelections.childNodes[ sId ];
|
|
1886
1985
|
if(!domEl)
|
|
1887
1986
|
{
|
|
1888
1987
|
domEl = document.createElement( 'div' );
|
|
1889
1988
|
domEl.className = "lexcodeselection";
|
|
1890
|
-
|
|
1989
|
+
cursorSelections.appendChild( domEl );
|
|
1891
1990
|
}
|
|
1892
1991
|
}
|
|
1893
1992
|
|
|
@@ -1907,7 +2006,7 @@ class CodeEditor {
|
|
|
1907
2006
|
}
|
|
1908
2007
|
|
|
1909
2008
|
const stringWidth = this.measureString( string );
|
|
1910
|
-
|
|
2009
|
+
cursor.selection.chars += stringWidth / this.charWidth;
|
|
1911
2010
|
|
|
1912
2011
|
if( isVisible )
|
|
1913
2012
|
{
|
|
@@ -1998,46 +2097,59 @@ class CodeEditor {
|
|
|
1998
2097
|
|
|
1999
2098
|
if( e.ctrlKey || e.metaKey )
|
|
2000
2099
|
{
|
|
2001
|
-
e.preventDefault();
|
|
2002
|
-
|
|
2003
2100
|
switch( key.toLowerCase() ) {
|
|
2004
2101
|
case 'a': // select all
|
|
2102
|
+
e.preventDefault();
|
|
2005
2103
|
this.selectAll();
|
|
2006
2104
|
return true;
|
|
2007
2105
|
case 'c': // k+c, comment line
|
|
2106
|
+
e.preventDefault();
|
|
2008
2107
|
if( this.state.keyChain == 'k' ) {
|
|
2009
2108
|
this._commentLines();
|
|
2010
2109
|
return true;
|
|
2011
2110
|
}
|
|
2012
2111
|
return false;
|
|
2112
|
+
case 'd': // next ocurrence
|
|
2113
|
+
e.preventDefault();
|
|
2114
|
+
this.selectNextOcurrence( cursor );
|
|
2115
|
+
return true;
|
|
2013
2116
|
case 'f': // find/search
|
|
2117
|
+
e.preventDefault();
|
|
2014
2118
|
this.showSearchBox();
|
|
2015
2119
|
return true;
|
|
2016
2120
|
case 'g': // find line
|
|
2121
|
+
e.preventDefault();
|
|
2017
2122
|
this.showSearchLineBox();
|
|
2018
2123
|
return true;
|
|
2019
2124
|
case 'k': // shortcut chain
|
|
2125
|
+
e.preventDefault();
|
|
2020
2126
|
this.state.keyChain = 'k';
|
|
2021
2127
|
return true;
|
|
2022
2128
|
case 's': // save
|
|
2129
|
+
e.preventDefault();
|
|
2023
2130
|
this.onsave( this.getText() );
|
|
2024
2131
|
return true;
|
|
2025
2132
|
case 'u': // k+u, uncomment line
|
|
2133
|
+
e.preventDefault();
|
|
2026
2134
|
if( this.state.keyChain == 'k' ) {
|
|
2027
2135
|
this._uncommentLines();
|
|
2028
2136
|
return true;
|
|
2029
2137
|
}
|
|
2030
2138
|
return false;
|
|
2031
2139
|
case 'y': // redo
|
|
2140
|
+
e.preventDefault();
|
|
2032
2141
|
this._doRedo( cursor );
|
|
2033
2142
|
return true;
|
|
2034
2143
|
case 'z': // undo
|
|
2144
|
+
e.preventDefault();
|
|
2035
2145
|
this._doUndo( cursor );
|
|
2036
2146
|
return true;
|
|
2037
2147
|
case '+': // increase size
|
|
2148
|
+
e.preventDefault();
|
|
2038
2149
|
this._increaseFontSize();
|
|
2039
2150
|
return true;
|
|
2040
2151
|
case '-': // decrease size
|
|
2152
|
+
e.preventDefault();
|
|
2041
2153
|
this._decreaseFontSize();
|
|
2042
2154
|
return true;
|
|
2043
2155
|
}
|
|
@@ -2066,14 +2178,8 @@ class CodeEditor {
|
|
|
2066
2178
|
{
|
|
2067
2179
|
switch( key.toLowerCase() ) {
|
|
2068
2180
|
case 'c': // copy
|
|
2069
|
-
// TODO: COPY TEXT FROM EVERY CURSOR
|
|
2070
2181
|
this._copyContent( cursor );
|
|
2071
2182
|
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
2183
|
case 'v': // paste
|
|
2078
2184
|
this._pasteContent( cursor );
|
|
2079
2185
|
return;
|
|
@@ -2094,6 +2200,10 @@ class CodeEditor {
|
|
|
2094
2200
|
else if( e.altKey )
|
|
2095
2201
|
{
|
|
2096
2202
|
switch( key ) {
|
|
2203
|
+
case 'd': // duplicate line
|
|
2204
|
+
e.preventDefault();
|
|
2205
|
+
this._duplicateLine( lidx, cursor );
|
|
2206
|
+
return;
|
|
2097
2207
|
case 'ArrowUp':
|
|
2098
2208
|
if(this.code.lines[ lidx - 1 ] == undefined)
|
|
2099
2209
|
return;
|
|
@@ -2124,7 +2234,7 @@ class CodeEditor {
|
|
|
2124
2234
|
if( key != actKey ) continue;
|
|
2125
2235
|
e.preventDefault();
|
|
2126
2236
|
|
|
2127
|
-
if( this.actions[ key ].deleteSelection &&
|
|
2237
|
+
if( this.actions[ key ].deleteSelection && cursor.selection )
|
|
2128
2238
|
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
2129
2239
|
|
|
2130
2240
|
return this.actions[ key ].callback( lidx, cursor, e );
|
|
@@ -2153,7 +2263,7 @@ class CodeEditor {
|
|
|
2153
2263
|
// Until this point, if there was a selection, we need
|
|
2154
2264
|
// to delete the content..
|
|
2155
2265
|
|
|
2156
|
-
if(
|
|
2266
|
+
if( cursor.selection )
|
|
2157
2267
|
{
|
|
2158
2268
|
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
2159
2269
|
lidx = cursor.line;
|
|
@@ -2214,22 +2324,32 @@ class CodeEditor {
|
|
|
2214
2324
|
|
|
2215
2325
|
let text = await navigator.clipboard.readText();
|
|
2216
2326
|
|
|
2327
|
+
// Remove any possible tabs (\t) and add spaces
|
|
2328
|
+
text = text.replaceAll( /\t|\\t/g, ' '.repeat( this.tabSpaces ) );
|
|
2329
|
+
|
|
2217
2330
|
this._addUndoStep( cursor, true );
|
|
2218
2331
|
|
|
2219
2332
|
this.appendText( text, cursor );
|
|
2333
|
+
|
|
2334
|
+
const currentScroll = this.getScrollTop();
|
|
2335
|
+
const scroll = Math.max( cursor.line * this.lineHeight - this.codeScroller.offsetWidth, 0 );
|
|
2336
|
+
|
|
2337
|
+
if( currentScroll < scroll ) {
|
|
2338
|
+
this.codeScroller.scrollTo( 0, cursor.line * this.lineHeight );
|
|
2339
|
+
}
|
|
2220
2340
|
}
|
|
2221
2341
|
|
|
2222
2342
|
async _copyContent( cursor ) {
|
|
2223
2343
|
|
|
2224
2344
|
let text_to_copy = "";
|
|
2225
2345
|
|
|
2226
|
-
if( !
|
|
2346
|
+
if( !cursor.selection ) {
|
|
2227
2347
|
text_to_copy = "\n" + this.code.lines[ cursor.line ];
|
|
2228
2348
|
}
|
|
2229
2349
|
else {
|
|
2230
2350
|
|
|
2231
2351
|
// Some selections don't depend on mouse up..
|
|
2232
|
-
if(
|
|
2352
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2233
2353
|
|
|
2234
2354
|
const separator = "_NEWLINE_";
|
|
2235
2355
|
let code = this.code.lines.join( separator );
|
|
@@ -2237,11 +2357,11 @@ class CodeEditor {
|
|
|
2237
2357
|
// Get linear start index
|
|
2238
2358
|
let index = 0;
|
|
2239
2359
|
|
|
2240
|
-
for( let i = 0; i <=
|
|
2241
|
-
index += ( i ==
|
|
2360
|
+
for( let i = 0; i <= cursor.selection.fromY; i++ )
|
|
2361
|
+
index += ( i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length );
|
|
2242
2362
|
|
|
2243
|
-
index +=
|
|
2244
|
-
const num_chars =
|
|
2363
|
+
index += cursor.selection.fromY * separator.length;
|
|
2364
|
+
const num_chars = cursor.selection.chars + ( cursor.selection.toY - cursor.selection.fromY ) * separator.length;
|
|
2245
2365
|
const text = code.substr( index, num_chars );
|
|
2246
2366
|
const lines = text.split( separator );
|
|
2247
2367
|
text_to_copy = lines.join('\n');
|
|
@@ -2257,7 +2377,7 @@ class CodeEditor {
|
|
|
2257
2377
|
|
|
2258
2378
|
this._addUndoStep( cursor, true );
|
|
2259
2379
|
|
|
2260
|
-
if( !
|
|
2380
|
+
if( !cursor.selection ) {
|
|
2261
2381
|
text_to_cut = "\n" + this.code.lines[ cursor.line ];
|
|
2262
2382
|
this.code.lines.splice( lidx, 1 );
|
|
2263
2383
|
this.processLines();
|
|
@@ -2268,7 +2388,7 @@ class CodeEditor {
|
|
|
2268
2388
|
else {
|
|
2269
2389
|
|
|
2270
2390
|
// Some selections don't depend on mouse up..
|
|
2271
|
-
if(
|
|
2391
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2272
2392
|
|
|
2273
2393
|
const separator = "_NEWLINE_";
|
|
2274
2394
|
let code = this.code.lines.join(separator);
|
|
@@ -2276,11 +2396,11 @@ class CodeEditor {
|
|
|
2276
2396
|
// Get linear start index
|
|
2277
2397
|
let index = 0;
|
|
2278
2398
|
|
|
2279
|
-
for(let i = 0; i <=
|
|
2280
|
-
index += (i ==
|
|
2399
|
+
for(let i = 0; i <= cursor.selection.fromY; i++)
|
|
2400
|
+
index += (i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length);
|
|
2281
2401
|
|
|
2282
|
-
index +=
|
|
2283
|
-
const num_chars =
|
|
2402
|
+
index += cursor.selection.fromY * separator.length;
|
|
2403
|
+
const num_chars = cursor.selection.chars + (cursor.selection.toY - cursor.selection.fromY) * separator.length;
|
|
2284
2404
|
const text = code.substr(index, num_chars);
|
|
2285
2405
|
const lines = text.split(separator);
|
|
2286
2406
|
text_to_cut = lines.join('\n');
|
|
@@ -2305,18 +2425,18 @@ class CodeEditor {
|
|
|
2305
2425
|
|
|
2306
2426
|
this.state.keyChain = null;
|
|
2307
2427
|
|
|
2308
|
-
if(
|
|
2428
|
+
if( cursor.selection )
|
|
2309
2429
|
{
|
|
2310
2430
|
var cursor = this._getCurrentCursor();
|
|
2311
2431
|
this._addUndoStep( cursor, true );
|
|
2312
2432
|
|
|
2313
|
-
const selectedLines = this.code.lines.slice(
|
|
2433
|
+
const selectedLines = this.code.lines.slice( cursor.selection.fromY, cursor.selection.toY );
|
|
2314
2434
|
const minIdx = Math.min(...selectedLines.map( v => {
|
|
2315
2435
|
var idx = firstNonspaceIndex( v );
|
|
2316
2436
|
return idx < 0 ? 1e10 : idx;
|
|
2317
2437
|
} ));
|
|
2318
2438
|
|
|
2319
|
-
for( var i =
|
|
2439
|
+
for( var i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
2320
2440
|
{
|
|
2321
2441
|
this._commentLine( cursor, i, minIdx );
|
|
2322
2442
|
}
|
|
@@ -2364,12 +2484,12 @@ class CodeEditor {
|
|
|
2364
2484
|
|
|
2365
2485
|
this.state.keyChain = null;
|
|
2366
2486
|
|
|
2367
|
-
if(
|
|
2487
|
+
if( cursor.selection )
|
|
2368
2488
|
{
|
|
2369
2489
|
var cursor = this._getCurrentCursor();
|
|
2370
2490
|
this._addUndoStep( cursor, true );
|
|
2371
2491
|
|
|
2372
|
-
for( var i =
|
|
2492
|
+
for( var i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
2373
2493
|
{
|
|
2374
2494
|
this._uncommentLine( cursor, i );
|
|
2375
2495
|
}
|
|
@@ -2478,8 +2598,7 @@ class CodeEditor {
|
|
|
2478
2598
|
this.code.style.top = ( this.visibleLinesViewport.x * this.lineHeight ) + "px";
|
|
2479
2599
|
|
|
2480
2600
|
// Update selections
|
|
2481
|
-
|
|
2482
|
-
this.processSelection( null, true );
|
|
2601
|
+
this.updateSelections( null, true );
|
|
2483
2602
|
|
|
2484
2603
|
this._clearTmpVariables();
|
|
2485
2604
|
this._setActiveLine();
|
|
@@ -2952,20 +3071,20 @@ class CodeEditor {
|
|
|
2952
3071
|
|
|
2953
3072
|
_encloseSelectedWordWithKey( key, lidx, cursor ) {
|
|
2954
3073
|
|
|
2955
|
-
if( !
|
|
3074
|
+
if( !cursor.selection || (cursor.selection.fromY != cursor.selection.toY) )
|
|
2956
3075
|
return false;
|
|
2957
3076
|
|
|
2958
|
-
|
|
3077
|
+
cursor.selection.invertIfNecessary();
|
|
2959
3078
|
|
|
2960
3079
|
// Insert first..
|
|
2961
3080
|
this.code.lines[ lidx ] = [
|
|
2962
|
-
this.code.lines[ lidx ].slice(0,
|
|
3081
|
+
this.code.lines[ lidx ].slice(0, cursor.selection.fromX),
|
|
2963
3082
|
key,
|
|
2964
|
-
this.code.lines[ lidx ].slice(
|
|
3083
|
+
this.code.lines[ lidx ].slice(cursor.selection.fromX)
|
|
2965
3084
|
].join('');
|
|
2966
3085
|
|
|
2967
3086
|
// Go to the end of the word
|
|
2968
|
-
this.cursorToPosition(cursor,
|
|
3087
|
+
this.cursorToPosition(cursor, cursor.selection.toX + 1);
|
|
2969
3088
|
|
|
2970
3089
|
// Change next key?
|
|
2971
3090
|
switch(key)
|
|
@@ -2986,10 +3105,10 @@ class CodeEditor {
|
|
|
2986
3105
|
|
|
2987
3106
|
// Recompute and reposition current selection
|
|
2988
3107
|
|
|
2989
|
-
|
|
2990
|
-
|
|
3108
|
+
cursor.selection.fromX++;
|
|
3109
|
+
cursor.selection.toX++;
|
|
2991
3110
|
|
|
2992
|
-
this.
|
|
3111
|
+
this._processSelection( cursor );
|
|
2993
3112
|
this.processLine( lidx );
|
|
2994
3113
|
|
|
2995
3114
|
// Stop propagation
|
|
@@ -3037,14 +3156,16 @@ class CodeEditor {
|
|
|
3037
3156
|
|
|
3038
3157
|
startSelection( cursor ) {
|
|
3039
3158
|
|
|
3040
|
-
// Clear other selections...
|
|
3041
|
-
this.selections.innerHTML = "";
|
|
3042
|
-
|
|
3043
3159
|
// Show elements
|
|
3044
|
-
|
|
3160
|
+
let selectionContainer = document.createElement( 'div' );
|
|
3161
|
+
selectionContainer.className = 'selections';
|
|
3162
|
+
selectionContainer.classList.add( 'show' );
|
|
3163
|
+
|
|
3164
|
+
this.codeSizer.insertChildAtIndex( selectionContainer, 2 );
|
|
3165
|
+
this.selections[ cursor.name ] = selectionContainer;
|
|
3045
3166
|
|
|
3046
3167
|
// Create new selection instance
|
|
3047
|
-
|
|
3168
|
+
cursor.selection = new CodeSelection( this, cursor );
|
|
3048
3169
|
}
|
|
3049
3170
|
|
|
3050
3171
|
deleteSelection( cursor ) {
|
|
@@ -3054,37 +3175,51 @@ class CodeEditor {
|
|
|
3054
3175
|
return;
|
|
3055
3176
|
|
|
3056
3177
|
// Some selections don't depend on mouse up..
|
|
3057
|
-
if(
|
|
3178
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
3058
3179
|
|
|
3059
3180
|
const separator = "_NEWLINE_";
|
|
3060
3181
|
let code = this.code.lines.join( separator );
|
|
3061
3182
|
|
|
3062
3183
|
// Get linear start index
|
|
3063
3184
|
let index = 0;
|
|
3064
|
-
for( let i = 0; i <=
|
|
3065
|
-
index += (i ==
|
|
3185
|
+
for( let i = 0; i <= cursor.selection.fromY; i++ )
|
|
3186
|
+
index += (i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length);
|
|
3066
3187
|
|
|
3067
|
-
index +=
|
|
3188
|
+
index += cursor.selection.fromY * separator.length;
|
|
3068
3189
|
|
|
3069
|
-
const num_chars =
|
|
3190
|
+
const num_chars = cursor.selection.chars + (cursor.selection.toY - cursor.selection.fromY) * separator.length;
|
|
3070
3191
|
const pre = code.slice( 0, index );
|
|
3071
3192
|
const post = code.slice( index + num_chars );
|
|
3072
3193
|
|
|
3073
3194
|
this.code.lines = ( pre + post ).split( separator );
|
|
3074
3195
|
|
|
3075
|
-
this.cursorToLine( cursor,
|
|
3076
|
-
this.cursorToPosition( cursor,
|
|
3077
|
-
this.endSelection();
|
|
3196
|
+
this.cursorToLine( cursor, cursor.selection.fromY, true );
|
|
3197
|
+
this.cursorToPosition( cursor, cursor.selection.fromX );
|
|
3198
|
+
this.endSelection( cursor );
|
|
3078
3199
|
this.processLines();
|
|
3079
3200
|
}
|
|
3080
3201
|
|
|
3081
|
-
endSelection() {
|
|
3202
|
+
endSelection( cursor ) {
|
|
3082
3203
|
|
|
3083
|
-
this.selections.classList.remove( 'show' );
|
|
3084
|
-
this.selections.innerHTML = "";
|
|
3085
|
-
delete this.selection;
|
|
3086
3204
|
delete this._tripleClickSelection;
|
|
3087
3205
|
delete this._lastSelectionKeyDir;
|
|
3206
|
+
delete this._currentOcurrences;
|
|
3207
|
+
|
|
3208
|
+
if( cursor )
|
|
3209
|
+
{
|
|
3210
|
+
deleteElement( this.selections[ cursor.name ] );
|
|
3211
|
+
delete this.selections[ cursor.name ];
|
|
3212
|
+
delete cursor.selection;
|
|
3213
|
+
}
|
|
3214
|
+
else
|
|
3215
|
+
{
|
|
3216
|
+
for( let cursor of this.cursors.children )
|
|
3217
|
+
{
|
|
3218
|
+
deleteElement( this.selections[ cursor.name ] );
|
|
3219
|
+
delete this.selections[ cursor.name ];
|
|
3220
|
+
delete cursor.selection;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3088
3223
|
}
|
|
3089
3224
|
|
|
3090
3225
|
selectAll() {
|
|
@@ -3098,13 +3233,13 @@ class CodeEditor {
|
|
|
3098
3233
|
this.startSelection( cursor );
|
|
3099
3234
|
|
|
3100
3235
|
const nlines = this.code.lines.length - 1;
|
|
3101
|
-
|
|
3102
|
-
|
|
3236
|
+
cursor.selection.toX = this.code.lines[ nlines ].length;
|
|
3237
|
+
cursor.selection.toY = nlines;
|
|
3103
3238
|
|
|
3104
|
-
this.cursorToPosition( cursor,
|
|
3105
|
-
this.cursorToLine( cursor,
|
|
3239
|
+
this.cursorToPosition( cursor, cursor.selection.toX );
|
|
3240
|
+
this.cursorToLine( cursor, cursor.selection.toY );
|
|
3106
3241
|
|
|
3107
|
-
this.
|
|
3242
|
+
this._processSelection( cursor, null, true );
|
|
3108
3243
|
|
|
3109
3244
|
this.hideAutoCompleteBox();
|
|
3110
3245
|
}
|
|
@@ -3210,6 +3345,8 @@ class CodeEditor {
|
|
|
3210
3345
|
|
|
3211
3346
|
state.position = cursor.position;
|
|
3212
3347
|
state.line = cursor.line;
|
|
3348
|
+
state.selection = cursor.selection ? cursor.selection.save() : undefined;
|
|
3349
|
+
|
|
3213
3350
|
return state;
|
|
3214
3351
|
}
|
|
3215
3352
|
|
|
@@ -3242,7 +3379,7 @@ class CodeEditor {
|
|
|
3242
3379
|
const cursorsInLine = Array.from( this.cursors.children ).filter( v => v.line == line );
|
|
3243
3380
|
|
|
3244
3381
|
while( cursorsInLine.length > 1 )
|
|
3245
|
-
cursorsInLine.pop()
|
|
3382
|
+
this.removeCursor( cursorsInLine.pop() );
|
|
3246
3383
|
}
|
|
3247
3384
|
|
|
3248
3385
|
restoreCursor( cursor, state ) {
|
|
@@ -3254,6 +3391,22 @@ class CodeEditor {
|
|
|
3254
3391
|
cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
|
|
3255
3392
|
cursor._top = cursor.line * this.lineHeight;
|
|
3256
3393
|
cursor.style.top = "calc(" + cursor._top + "px)";
|
|
3394
|
+
|
|
3395
|
+
if( state.selection )
|
|
3396
|
+
{
|
|
3397
|
+
this.startSelection( cursor );
|
|
3398
|
+
|
|
3399
|
+
cursor.selection.load( state.selection );
|
|
3400
|
+
|
|
3401
|
+
this._processSelection( cursor, null, true );
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
removeCursor( cursor ) {
|
|
3406
|
+
|
|
3407
|
+
deleteElement( this.selections[ cursor.name ] );
|
|
3408
|
+
delete this.selections[ cursor.name ];
|
|
3409
|
+
deleteElement( cursor );
|
|
3257
3410
|
}
|
|
3258
3411
|
|
|
3259
3412
|
resetCursorPos( flag, cursor ) {
|
|
@@ -3718,6 +3871,8 @@ class CodeEditor {
|
|
|
3718
3871
|
|
|
3719
3872
|
showSearchBox( clear ) {
|
|
3720
3873
|
|
|
3874
|
+
this.hideSearchLineBox();
|
|
3875
|
+
|
|
3721
3876
|
this.searchbox.classList.add( 'opened' );
|
|
3722
3877
|
this.searchboxActive = true;
|
|
3723
3878
|
|
|
@@ -3727,12 +3882,26 @@ class CodeEditor {
|
|
|
3727
3882
|
{
|
|
3728
3883
|
input.value = "";
|
|
3729
3884
|
}
|
|
3885
|
+
else
|
|
3886
|
+
{
|
|
3887
|
+
const cursor = this._getCurrentCursor();
|
|
3888
|
+
|
|
3889
|
+
if( cursor.selection )
|
|
3890
|
+
{
|
|
3891
|
+
input.value = cursor.selection.getText() ?? input.value;
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
input.selectionStart = 0;
|
|
3896
|
+
input.selectionEnd = input.value.length;
|
|
3730
3897
|
|
|
3731
3898
|
input.focus();
|
|
3732
3899
|
}
|
|
3733
3900
|
|
|
3734
3901
|
hideSearchBox() {
|
|
3735
3902
|
|
|
3903
|
+
const active = this.searchboxActive;
|
|
3904
|
+
|
|
3736
3905
|
if( this.searchboxActive )
|
|
3737
3906
|
{
|
|
3738
3907
|
this.searchbox.classList.remove( 'opened' );
|
|
@@ -3741,31 +3910,49 @@ class CodeEditor {
|
|
|
3741
3910
|
|
|
3742
3911
|
else if( this._lastResult )
|
|
3743
3912
|
{
|
|
3744
|
-
this._lastResult.dom
|
|
3913
|
+
deleteElement( this._lastResult.dom );
|
|
3745
3914
|
delete this._lastResult;
|
|
3746
3915
|
}
|
|
3916
|
+
|
|
3917
|
+
this.searchResultSelections.classList.remove( 'show' );
|
|
3918
|
+
|
|
3919
|
+
return active != this.searchboxActive;
|
|
3747
3920
|
}
|
|
3748
3921
|
|
|
3749
|
-
search( text, reverse ) {
|
|
3922
|
+
search( text, reverse, callback, skipAlert ) {
|
|
3750
3923
|
|
|
3751
3924
|
text = text ?? this._lastTextFound;
|
|
3752
3925
|
|
|
3753
3926
|
if( !text )
|
|
3754
3927
|
return;
|
|
3755
3928
|
|
|
3756
|
-
let
|
|
3929
|
+
let cursor = this._getCurrentCursor();
|
|
3930
|
+
let cursorData = new LX.vec2( cursor.position, cursor.line );
|
|
3757
3931
|
let line = null;
|
|
3758
3932
|
let char = -1;
|
|
3759
3933
|
|
|
3760
3934
|
if( this._lastResult )
|
|
3761
3935
|
{
|
|
3762
|
-
this._lastResult.dom
|
|
3936
|
+
deleteElement( this._lastResult.dom );
|
|
3763
3937
|
cursorData = this._lastResult.pos;
|
|
3764
3938
|
delete this._lastResult;
|
|
3765
3939
|
}
|
|
3766
3940
|
|
|
3767
3941
|
const getIndex = l => {
|
|
3768
|
-
|
|
3942
|
+
|
|
3943
|
+
var string = this.code.lines[ l ];
|
|
3944
|
+
|
|
3945
|
+
if( reverse )
|
|
3946
|
+
{
|
|
3947
|
+
string = string.substr( 0, l == cursorData.y ? cursorData.x : string.length );
|
|
3948
|
+
var reversed = strReverse( string );
|
|
3949
|
+
var reversedIdx = reversed.indexOf( strReverse( text ) );
|
|
3950
|
+
return reversedIdx == -1 ? -1 : string.length - reversedIdx - text.length;
|
|
3951
|
+
}
|
|
3952
|
+
else
|
|
3953
|
+
{
|
|
3954
|
+
return string.substr( l == cursorData.y ? cursorData.x : 0 ).indexOf( text );
|
|
3955
|
+
}
|
|
3769
3956
|
};
|
|
3770
3957
|
|
|
3771
3958
|
if( reverse )
|
|
@@ -3795,7 +3982,15 @@ class CodeEditor {
|
|
|
3795
3982
|
|
|
3796
3983
|
if( line == null)
|
|
3797
3984
|
{
|
|
3798
|
-
|
|
3985
|
+
if( !skipAlert )
|
|
3986
|
+
alert( "No results!" );
|
|
3987
|
+
|
|
3988
|
+
const lastLine = this.code.lines.length - 1;
|
|
3989
|
+
|
|
3990
|
+
this._lastResult = {
|
|
3991
|
+
'dom': this.searchResultSelections.lastChild,
|
|
3992
|
+
'pos': reverse ? new LX.vec2( this.code.lines[ lastLine ].length, lastLine ) : new LX.vec2( 0, 0 )
|
|
3993
|
+
};
|
|
3799
3994
|
return;
|
|
3800
3995
|
}
|
|
3801
3996
|
|
|
@@ -3805,32 +4000,44 @@ class CodeEditor {
|
|
|
3805
4000
|
have to add the length of the substring (0, first_ocurrence)
|
|
3806
4001
|
*/
|
|
3807
4002
|
|
|
3808
|
-
|
|
4003
|
+
|
|
4004
|
+
if( !reverse )
|
|
4005
|
+
char += ( line == cursorData.y ? cursorData.x : 0 );
|
|
4006
|
+
|
|
3809
4007
|
|
|
3810
4008
|
// Text found..
|
|
3811
4009
|
|
|
3812
4010
|
this._lastTextFound = text;
|
|
3813
4011
|
|
|
3814
4012
|
this.codeScroller.scrollTo(
|
|
3815
|
-
Math.max( char * this.charWidth - this.codeScroller.clientWidth ),
|
|
3816
|
-
Math.max( line - 10 ) * this.lineHeight
|
|
4013
|
+
Math.max( char * this.charWidth - this.codeScroller.clientWidth, 0 ),
|
|
4014
|
+
Math.max( line - 10, 0 ) * this.lineHeight
|
|
3817
4015
|
);
|
|
3818
4016
|
|
|
3819
|
-
|
|
3820
|
-
|
|
4017
|
+
if( callback )
|
|
4018
|
+
{
|
|
4019
|
+
callback( char, line );
|
|
4020
|
+
}
|
|
4021
|
+
else
|
|
4022
|
+
{
|
|
4023
|
+
// Show elements
|
|
4024
|
+
this.searchResultSelections.classList.add( 'show' );
|
|
4025
|
+
|
|
4026
|
+
// Create new selection instance
|
|
4027
|
+
cursor.selection = new CodeSelection( this, cursor, "lexcodesearchresult" );
|
|
4028
|
+
cursor.selection.selectInline( cursor, char, line, this.measureString( text ), true );
|
|
4029
|
+
}
|
|
3821
4030
|
|
|
3822
|
-
// Create new selection instance
|
|
3823
|
-
this.selection = new CodeSelection( this, 0, 0, "lexcodesearchresult" );
|
|
3824
|
-
this.selection.selectInline( char, line, this.measureString( text ) );
|
|
3825
4031
|
this._lastResult = {
|
|
3826
|
-
'dom': this.
|
|
3827
|
-
'pos': new LX.vec2( char + text.length, line )
|
|
4032
|
+
'dom': this.searchResultSelections.lastChild,
|
|
4033
|
+
'pos': new LX.vec2( char + text.length * ( reverse ? -1 : 1 ) , line )
|
|
3828
4034
|
};
|
|
3829
|
-
|
|
3830
4035
|
}
|
|
3831
4036
|
|
|
3832
4037
|
showSearchLineBox() {
|
|
3833
4038
|
|
|
4039
|
+
this.hideSearchBox();
|
|
4040
|
+
|
|
3834
4041
|
this.searchlinebox.classList.add( 'opened' );
|
|
3835
4042
|
this.searchlineboxActive = true;
|
|
3836
4043
|
|
|
@@ -3860,6 +4067,40 @@ class CodeEditor {
|
|
|
3860
4067
|
this.cursorToLine( cursor, line - 1, true );
|
|
3861
4068
|
}
|
|
3862
4069
|
|
|
4070
|
+
selectNextOcurrence( cursor ) {
|
|
4071
|
+
|
|
4072
|
+
if( !cursor.selection )
|
|
4073
|
+
return;
|
|
4074
|
+
|
|
4075
|
+
const text = cursor.selection.getText();
|
|
4076
|
+
if( !text )
|
|
4077
|
+
return;
|
|
4078
|
+
|
|
4079
|
+
if( !this._currentOcurrences )
|
|
4080
|
+
{
|
|
4081
|
+
const currentKey = [ cursor.position - text.length, cursor.line ].join( '_' );
|
|
4082
|
+
this._currentOcurrences = { };
|
|
4083
|
+
this._currentOcurrences[ currentKey ] = true;
|
|
4084
|
+
}
|
|
4085
|
+
|
|
4086
|
+
this.search( text, false, (col, ln) => {
|
|
4087
|
+
|
|
4088
|
+
const key = [ col, ln ].join( '_' );
|
|
4089
|
+
|
|
4090
|
+
if( this._currentOcurrences[ key ] ) {
|
|
4091
|
+
return;
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
var newCursor = this._addCursor( ln, col, true );
|
|
4095
|
+
this.startSelection( newCursor );
|
|
4096
|
+
newCursor.selection.selectInline( newCursor, col, ln, this.measureString( text ) );
|
|
4097
|
+
this.cursorToString( newCursor, text );
|
|
4098
|
+
|
|
4099
|
+
this._currentOcurrences[ key ] = true;
|
|
4100
|
+
|
|
4101
|
+
}, true );
|
|
4102
|
+
}
|
|
4103
|
+
|
|
3863
4104
|
_updateDataInfoPanel( signal, value ) {
|
|
3864
4105
|
|
|
3865
4106
|
if( !this.skipCodeInfo )
|
|
@@ -3962,6 +4203,7 @@ class CodeEditor {
|
|
|
3962
4203
|
delete this._pendingString;
|
|
3963
4204
|
delete this._buildingBlockComment;
|
|
3964
4205
|
delete this._markdownHeader;
|
|
4206
|
+
delete this._lastResult;
|
|
3965
4207
|
}
|
|
3966
4208
|
}
|
|
3967
4209
|
|
package/build/lexgui.js
CHANGED
|
@@ -12,7 +12,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
var LX = global.LX = {
|
|
15
|
-
version: "0.1.
|
|
15
|
+
version: "0.1.26",
|
|
16
16
|
ready: false,
|
|
17
17
|
components: [], // specific pre-build components
|
|
18
18
|
signals: {} // events and triggers
|
|
@@ -2178,7 +2178,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
|
|
|
2178
2178
|
set( value ) {
|
|
2179
2179
|
|
|
2180
2180
|
if( this.onSetValue )
|
|
2181
|
-
this.onSetValue( value );
|
|
2181
|
+
return this.onSetValue( value );
|
|
2182
2182
|
|
|
2183
2183
|
console.warn("Can't set value of " + this.typeName());
|
|
2184
2184
|
}
|
package/build/lexgui.module.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
var LX = {
|
|
11
|
-
version: "0.1.
|
|
11
|
+
version: "0.1.26",
|
|
12
12
|
ready: false,
|
|
13
13
|
components: [], // specific pre-build components
|
|
14
14
|
signals: {} // events and triggers
|
|
@@ -2174,7 +2174,7 @@ class Widget {
|
|
|
2174
2174
|
set( value ) {
|
|
2175
2175
|
|
|
2176
2176
|
if( this.onSetValue )
|
|
2177
|
-
this.onSetValue( value );
|
|
2177
|
+
return this.onSetValue( value );
|
|
2178
2178
|
|
|
2179
2179
|
console.warn("Can't set value of " + this.typeName());
|
|
2180
2180
|
}
|
package/changelog.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# lexgui.js changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.26
|
|
4
|
+
|
|
5
|
+
Code Editor:
|
|
6
|
+
- Search Next Ocurrence using "Ctrl+D" (Duplicate line moved to "Alt+D")
|
|
7
|
+
- Previous selection is restored on undo.
|
|
8
|
+
- Get text to search from selection.
|
|
9
|
+
- Reverse search bugfixes.
|
|
10
|
+
- Pasting content now scrolls to follow cursor.
|
|
11
|
+
|
|
12
|
+
Minor fixes.
|
|
13
|
+
|
|
3
14
|
## 0.1.25
|
|
4
15
|
|
|
5
16
|
Code Editor:
|