lexgui 0.1.24 → 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 +721 -304
- package/build/lexgui.css +4 -4
- package/build/lexgui.js +64 -38
- package/build/lexgui.module.js +54 -28
- package/changelog.md +22 -0
- package/package.json +1 -1
|
@@ -26,7 +26,12 @@ function sliceChars( str, idx, n = 1 ) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function firstNonspaceIndex( str ) {
|
|
29
|
-
|
|
29
|
+
const index = str.search(/\S|$/)
|
|
30
|
+
return index < str.length ? index : -1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function strReverse( str ) {
|
|
34
|
+
return str.split( "" ).reverse().join( "" );
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
function indexOfFrom( str, reg, from, reverse ) {
|
|
@@ -63,16 +68,17 @@ function doAsync( fn, ms ) {
|
|
|
63
68
|
|
|
64
69
|
class CodeSelection {
|
|
65
70
|
|
|
66
|
-
constructor( editor,
|
|
71
|
+
constructor( editor, cursor, className = "lexcodeselection" ) {
|
|
67
72
|
|
|
68
73
|
this.editor = editor;
|
|
69
|
-
this.
|
|
74
|
+
this.cursor = cursor;
|
|
70
75
|
this.className = className;
|
|
76
|
+
this.chars = 0;
|
|
71
77
|
|
|
72
|
-
this.fromX =
|
|
73
|
-
this.toX =
|
|
74
|
-
this.fromY =
|
|
75
|
-
this.toY =
|
|
78
|
+
this.fromX = cursor.position;
|
|
79
|
+
this.toX = cursor.position;
|
|
80
|
+
this.fromY = cursor.line;
|
|
81
|
+
this.toY = cursor.line;
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
sameLine() {
|
|
@@ -100,7 +106,7 @@ class CodeSelection {
|
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
108
|
|
|
103
|
-
selectInline( x, y, width ) {
|
|
109
|
+
selectInline( cursor, x, y, width, isSearchResult ) {
|
|
104
110
|
|
|
105
111
|
this.chars = width / this.editor.charWidth;
|
|
106
112
|
this.fromX = x;
|
|
@@ -115,10 +121,44 @@ class CodeSelection {
|
|
|
115
121
|
domEl._left = x * this.editor.charWidth;
|
|
116
122
|
domEl.style.left = "calc(" + domEl._left + "px + " + this.editor.xPadding + ")";
|
|
117
123
|
domEl.style.width = width + "px";
|
|
118
|
-
|
|
124
|
+
|
|
125
|
+
if( isSearchResult )
|
|
126
|
+
{
|
|
127
|
+
this.editor.searchResultSelections.appendChild( domEl );
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
{
|
|
131
|
+
this.editor.selections[ cursor.name ].appendChild( domEl );
|
|
132
|
+
}
|
|
119
133
|
|
|
120
134
|
// Hide active line background
|
|
121
|
-
this.editor.
|
|
135
|
+
this.editor._hideActiveLine();
|
|
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 );
|
|
122
162
|
}
|
|
123
163
|
};
|
|
124
164
|
|
|
@@ -140,6 +180,7 @@ class ScrollBar {
|
|
|
140
180
|
this.thumb = document.createElement( 'div' );
|
|
141
181
|
this.thumb._top = 0;
|
|
142
182
|
this.thumb._left = 0;
|
|
183
|
+
|
|
143
184
|
this.root.appendChild( this.thumb );
|
|
144
185
|
|
|
145
186
|
this.thumb.addEventListener( "mousedown", inner_mousedown );
|
|
@@ -151,8 +192,8 @@ class ScrollBar {
|
|
|
151
192
|
function inner_mousedown( e )
|
|
152
193
|
{
|
|
153
194
|
var doc = editor.root.ownerDocument;
|
|
154
|
-
doc.addEventListener( "mousemove",inner_mousemove );
|
|
155
|
-
doc.addEventListener( "mouseup",inner_mouseup );
|
|
195
|
+
doc.addEventListener( "mousemove", inner_mousemove );
|
|
196
|
+
doc.addEventListener( "mouseup", inner_mouseup );
|
|
156
197
|
that.lastPosition.set( e.x, e.y );
|
|
157
198
|
e.stopPropagation();
|
|
158
199
|
e.preventDefault();
|
|
@@ -201,8 +242,8 @@ class CodeEditor {
|
|
|
201
242
|
static WORD_TYPE_METHOD = 0;
|
|
202
243
|
static WORD_TYPE_CLASS = 1;
|
|
203
244
|
|
|
204
|
-
static CODE_MAX_FONT_SIZE = 22;
|
|
205
245
|
static CODE_MIN_FONT_SIZE = 9;
|
|
246
|
+
static CODE_MAX_FONT_SIZE = 22;
|
|
206
247
|
|
|
207
248
|
/**
|
|
208
249
|
* @param {*} options
|
|
@@ -324,9 +365,13 @@ class CodeEditor {
|
|
|
324
365
|
this.cursors.className = 'cursors';
|
|
325
366
|
this.tabs.area.attach( this.cursors );
|
|
326
367
|
|
|
327
|
-
this.
|
|
328
|
-
this.
|
|
329
|
-
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 = {};
|
|
330
375
|
|
|
331
376
|
// Css char synchronization
|
|
332
377
|
this.xPadding = "48px";
|
|
@@ -493,7 +538,8 @@ class CodeEditor {
|
|
|
493
538
|
this.state = {
|
|
494
539
|
focused: false,
|
|
495
540
|
selectingText: false,
|
|
496
|
-
activeLine: null
|
|
541
|
+
activeLine: null,
|
|
542
|
+
keyChain: null
|
|
497
543
|
}
|
|
498
544
|
|
|
499
545
|
// Code
|
|
@@ -550,90 +596,26 @@ class CodeEditor {
|
|
|
550
596
|
'End', 'Tab', 'Escape'
|
|
551
597
|
];
|
|
552
598
|
|
|
553
|
-
this.
|
|
554
|
-
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
|
|
555
|
-
'arguments', 'extends', 'instanceof'],
|
|
556
|
-
'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
|
|
557
|
-
'NULL', 'unsigned', 'namespace'],
|
|
558
|
-
'JSON': ['true', 'false'],
|
|
559
|
-
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
560
|
-
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
|
|
561
|
-
'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
|
|
562
|
-
'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
|
|
563
|
-
'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
|
|
564
|
-
'texture_storage_2d_array', 'texture_storage_3d'],
|
|
565
|
-
'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true',
|
|
566
|
-
'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
|
|
567
|
-
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
|
|
568
|
-
'Batch': ['set', 'SET', 'echo', 'ECHO', 'off', 'OFF', 'del', 'DEL', 'defined', 'DEFINED', 'setlocal', 'SETLOCAL', 'enabledelayedexpansion', 'ENABLEDELAYEDEXPANSION', 'driverquery',
|
|
569
|
-
'DRIVERQUERY', 'print', 'PRINT'],
|
|
570
|
-
'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
571
|
-
'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
572
|
-
};
|
|
573
|
-
this.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
|
|
574
|
-
'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
|
|
575
|
-
'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement', 'prompt',
|
|
576
|
-
'alert'],
|
|
577
|
-
'WGSL': ['textureSample'],
|
|
578
|
-
'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
|
|
579
|
-
'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
|
|
580
|
-
'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr',
|
|
581
|
-
'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
|
|
582
|
-
};
|
|
583
|
-
this.types = {
|
|
584
|
-
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder'],
|
|
585
|
-
'Rust': ['u128'],
|
|
586
|
-
'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
|
|
587
|
-
'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
|
|
588
|
-
'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError',
|
|
589
|
-
'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError', 'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError'],
|
|
590
|
-
'C++': ['uint8_t', 'uint16_t', 'uint32_t']
|
|
591
|
-
};
|
|
592
|
-
this.builtin = {
|
|
593
|
-
'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
|
|
594
|
-
'CSS': ['*', '!important'],
|
|
595
|
-
'C++': ['vector', 'list', 'map'],
|
|
596
|
-
'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
|
|
597
|
-
'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
|
|
598
|
-
};
|
|
599
|
-
this.statementsAndDeclarations = {
|
|
600
|
-
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
|
|
601
|
-
'CSS': ['@', 'import'],
|
|
602
|
-
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog'],
|
|
603
|
-
'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
|
|
604
|
-
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
|
|
605
|
-
'Rust': ['break', 'else', 'continue', 'for', 'if', 'loop', 'match', 'return', 'while', 'do', 'yield'],
|
|
606
|
-
'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue',
|
|
607
|
-
'global', 'pass'],
|
|
608
|
-
'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT']
|
|
609
|
-
};
|
|
610
|
-
this.symbols = {
|
|
611
|
-
'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
612
|
-
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
|
|
613
|
-
'JSON': ['[', ']', '{', '}', '(', ')'],
|
|
614
|
-
'GLSL': ['[', ']', '{', '}', '(', ')'],
|
|
615
|
-
'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
|
|
616
|
-
'CSS': ['{', '}', '(', ')', '*'],
|
|
617
|
-
'Rust': ['<', '>', '[', ']', '(', ')', '='],
|
|
618
|
-
'Python': ['<', '>', '[', ']', '(', ')', '='],
|
|
619
|
-
'Batch': ['[', ']', '(', ')', '%'],
|
|
620
|
-
'HTML': ['<', '>', '/']
|
|
621
|
-
};
|
|
599
|
+
this._blockCommentCache = [];
|
|
622
600
|
|
|
623
601
|
// Convert reserved word arrays to maps so we can search tokens faster
|
|
624
602
|
|
|
625
|
-
for( let lang in
|
|
626
|
-
for( let lang in
|
|
627
|
-
for( let lang in
|
|
628
|
-
for( let lang in
|
|
629
|
-
for( let lang in
|
|
630
|
-
for( let lang in
|
|
603
|
+
for( let lang in CodeEditor.keywords ) CodeEditor.keywords[lang] = CodeEditor.keywords[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
604
|
+
for( let lang in CodeEditor.utils ) CodeEditor.utils[lang] = CodeEditor.utils[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
605
|
+
for( let lang in CodeEditor.types ) CodeEditor.types[lang] = CodeEditor.types[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
606
|
+
for( let lang in CodeEditor.builtin ) CodeEditor.builtin[lang] = CodeEditor.builtin[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
607
|
+
for( let lang in CodeEditor.statementsAndDeclarations ) CodeEditor.statementsAndDeclarations[lang] = CodeEditor.statementsAndDeclarations[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
608
|
+
for( let lang in CodeEditor.symbols ) CodeEditor.symbols[lang] = CodeEditor.symbols[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
631
609
|
|
|
632
610
|
// Action keys
|
|
633
611
|
|
|
634
612
|
this.action( 'Escape', false, ( ln, cursor, e ) => {
|
|
635
|
-
this.hideAutoCompleteBox()
|
|
636
|
-
|
|
613
|
+
if( this.hideAutoCompleteBox() )
|
|
614
|
+
return;
|
|
615
|
+
if( this.hideSearchBox() )
|
|
616
|
+
return;
|
|
617
|
+
// Remove selections and cursors
|
|
618
|
+
this.endSelection();
|
|
637
619
|
this._removeSecondaryCursors();
|
|
638
620
|
});
|
|
639
621
|
|
|
@@ -641,7 +623,7 @@ class CodeEditor {
|
|
|
641
623
|
|
|
642
624
|
this._addUndoStep( cursor );
|
|
643
625
|
|
|
644
|
-
if(
|
|
626
|
+
if( cursor.selection ) {
|
|
645
627
|
this.deleteSelection( cursor );
|
|
646
628
|
// Remove entire line when selecting with triple click
|
|
647
629
|
if( this._tripleClickSelection )
|
|
@@ -695,7 +677,7 @@ class CodeEditor {
|
|
|
695
677
|
|
|
696
678
|
this._addUndoStep( cursor );
|
|
697
679
|
|
|
698
|
-
if(
|
|
680
|
+
if( cursor.selection ) {
|
|
699
681
|
// Use 'Backspace' as it's the same callback...
|
|
700
682
|
this.actions['Backspace'].callback( ln, cursor, e );
|
|
701
683
|
}
|
|
@@ -706,7 +688,7 @@ class CodeEditor {
|
|
|
706
688
|
this.code.lines[ ln ] = sliceChars( this.code.lines[ ln ], cursor.position );
|
|
707
689
|
this.processLine( ln );
|
|
708
690
|
}
|
|
709
|
-
else if(this.code.lines[ ln + 1 ] != undefined) {
|
|
691
|
+
else if( this.code.lines[ ln + 1 ] != undefined ) {
|
|
710
692
|
this.code.lines[ ln ] += this.code.lines[ ln + 1 ];
|
|
711
693
|
this.code.lines.splice( ln + 1, 1 );
|
|
712
694
|
this.processLines();
|
|
@@ -745,18 +727,18 @@ class CodeEditor {
|
|
|
745
727
|
if( e.shiftKey && !e.cancelShift )
|
|
746
728
|
{
|
|
747
729
|
// Get last selection range
|
|
748
|
-
if(
|
|
749
|
-
lastX +=
|
|
730
|
+
if( cursor.selection )
|
|
731
|
+
lastX += cursor.selection.chars;
|
|
750
732
|
|
|
751
|
-
if( !
|
|
733
|
+
if( !cursor.selection )
|
|
752
734
|
this.startSelection( cursor );
|
|
753
735
|
|
|
754
736
|
var string = this.code.lines[ ln ].substring( idx, lastX );
|
|
755
|
-
if(
|
|
756
|
-
|
|
737
|
+
if( cursor.selection.sameLine() )
|
|
738
|
+
cursor.selection.selectInline( cursor, idx, cursor.line, this.measureString( string ) );
|
|
757
739
|
else
|
|
758
740
|
{
|
|
759
|
-
this.
|
|
741
|
+
this._processSelection( cursor, e );
|
|
760
742
|
}
|
|
761
743
|
} else if( !e.keepSelection )
|
|
762
744
|
this.endSelection();
|
|
@@ -767,15 +749,15 @@ class CodeEditor {
|
|
|
767
749
|
if( ( e.shiftKey || e._shiftKey ) && !e.cancelShift ) {
|
|
768
750
|
|
|
769
751
|
var string = this.code.lines[ ln ].substring( cursor.position );
|
|
770
|
-
if( !
|
|
752
|
+
if( !cursor.selection )
|
|
771
753
|
this.startSelection( cursor );
|
|
772
|
-
if(
|
|
773
|
-
|
|
754
|
+
if( cursor.selection.sameLine() )
|
|
755
|
+
cursor.selection.selectInline( cursor, cursor.position, cursor.line, this.measureString( string ));
|
|
774
756
|
else
|
|
775
757
|
{
|
|
776
758
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
777
759
|
this.cursorToString( cursor, this.code.lines[ ln ] );
|
|
778
|
-
this.
|
|
760
|
+
this._processSelection( cursor, e );
|
|
779
761
|
}
|
|
780
762
|
} else if( !e.keepSelection )
|
|
781
763
|
this.endSelection();
|
|
@@ -837,7 +819,7 @@ class CodeEditor {
|
|
|
837
819
|
if( !this.isAutoCompleteActive )
|
|
838
820
|
{
|
|
839
821
|
if( e.shiftKey ) {
|
|
840
|
-
if( !
|
|
822
|
+
if( !cursor.selection )
|
|
841
823
|
this.startSelection( cursor );
|
|
842
824
|
|
|
843
825
|
this.lineUp( cursor );
|
|
@@ -847,7 +829,7 @@ class CodeEditor {
|
|
|
847
829
|
this.cursorToPosition( cursor, this.code.lines[ cursor.line ].length );
|
|
848
830
|
}
|
|
849
831
|
|
|
850
|
-
this.
|
|
832
|
+
this._processSelection( cursor, e, false );
|
|
851
833
|
|
|
852
834
|
} else {
|
|
853
835
|
this.endSelection();
|
|
@@ -870,7 +852,7 @@ class CodeEditor {
|
|
|
870
852
|
if( !this.isAutoCompleteActive )
|
|
871
853
|
{
|
|
872
854
|
if( e.shiftKey ) {
|
|
873
|
-
if( !
|
|
855
|
+
if( !cursor.selection )
|
|
874
856
|
this.startSelection( cursor );
|
|
875
857
|
} else {
|
|
876
858
|
this.endSelection();
|
|
@@ -881,11 +863,11 @@ class CodeEditor {
|
|
|
881
863
|
|
|
882
864
|
// Go to end of line if out of range
|
|
883
865
|
if( !letter || !canGoDown ) {
|
|
884
|
-
this.
|
|
866
|
+
this.cursorToPosition( cursor, Math.max(this.code.lines[ cursor.line ].length, 0) );
|
|
885
867
|
}
|
|
886
868
|
|
|
887
869
|
if( e.shiftKey ) {
|
|
888
|
-
this.
|
|
870
|
+
this._processSelection( cursor, e );
|
|
889
871
|
}
|
|
890
872
|
}
|
|
891
873
|
// Move down autocomplete selection
|
|
@@ -919,37 +901,37 @@ class CodeEditor {
|
|
|
919
901
|
var diff = Math.max( cursor.position - from, 1 );
|
|
920
902
|
var substr = word.substr( 0, diff );
|
|
921
903
|
// Selections...
|
|
922
|
-
if( e.shiftKey ) { if( !
|
|
904
|
+
if( e.shiftKey ) { if( !cursor.selection ) this.startSelection( cursor ); }
|
|
923
905
|
else this.endSelection();
|
|
924
906
|
this.cursorToString( cursor, substr, true );
|
|
925
|
-
if( e.shiftKey ) this.
|
|
907
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false, true );
|
|
926
908
|
}
|
|
927
909
|
else {
|
|
928
910
|
var letter = this.getCharAtPos( cursor, -1 );
|
|
929
911
|
if( letter ) {
|
|
930
912
|
if( e.shiftKey ) {
|
|
931
|
-
if( !
|
|
913
|
+
if( !cursor.selection ) this.startSelection( cursor );
|
|
932
914
|
this.cursorToLeft( letter, cursor );
|
|
933
|
-
this.
|
|
915
|
+
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
934
916
|
}
|
|
935
917
|
else {
|
|
936
|
-
if( !
|
|
918
|
+
if( !cursor.selection ) {
|
|
937
919
|
this.cursorToLeft( letter, cursor );
|
|
938
920
|
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
939
921
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
940
922
|
}
|
|
941
923
|
else {
|
|
942
|
-
|
|
924
|
+
cursor.selection.invertIfNecessary();
|
|
943
925
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
|
|
944
|
-
this.cursorToLine( cursor,
|
|
945
|
-
this.cursorToPosition( cursor,
|
|
926
|
+
this.cursorToLine( cursor, cursor.selection.fromY, true );
|
|
927
|
+
this.cursorToPosition( cursor, cursor.selection.fromX );
|
|
946
928
|
this.endSelection();
|
|
947
929
|
}
|
|
948
930
|
}
|
|
949
931
|
}
|
|
950
932
|
else if( cursor.line > 0 ) {
|
|
951
933
|
|
|
952
|
-
if( e.shiftKey && !
|
|
934
|
+
if( e.shiftKey && !cursor.selection ) this.startSelection( cursor );
|
|
953
935
|
|
|
954
936
|
this.lineUp( cursor );
|
|
955
937
|
|
|
@@ -957,7 +939,7 @@ class CodeEditor {
|
|
|
957
939
|
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
958
940
|
delete e.cancelShift; delete e.keepSelection;
|
|
959
941
|
|
|
960
|
-
if( e.shiftKey ) this.
|
|
942
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false );
|
|
961
943
|
}
|
|
962
944
|
}
|
|
963
945
|
});
|
|
@@ -969,40 +951,61 @@ class CodeEditor {
|
|
|
969
951
|
cursor.position == this.code.lines[ cursor.line ].length )
|
|
970
952
|
return;
|
|
971
953
|
|
|
972
|
-
if( e.metaKey )
|
|
954
|
+
if( e.metaKey ) // Apple devices (Command)
|
|
955
|
+
{
|
|
973
956
|
e.preventDefault();
|
|
974
957
|
this.actions[ 'End' ].callback( ln, cursor );
|
|
975
|
-
}
|
|
958
|
+
}
|
|
959
|
+
else if( e.ctrlKey ) // Next word
|
|
960
|
+
{
|
|
976
961
|
// Get next word
|
|
977
962
|
const [ word, from, to ] = this.getWordAtPos( cursor );
|
|
963
|
+
|
|
978
964
|
// If no length, we change line..
|
|
979
965
|
if( !word.length ) this.lineDown( cursor, true );
|
|
980
966
|
var diff = cursor.position - from;
|
|
981
967
|
var substr = word.substr( diff );
|
|
968
|
+
|
|
982
969
|
// Selections...
|
|
983
|
-
if( e.shiftKey ) {
|
|
984
|
-
|
|
970
|
+
if( e.shiftKey ) {
|
|
971
|
+
if( !cursor.selection )
|
|
972
|
+
this.startSelection( cursor );
|
|
973
|
+
}
|
|
974
|
+
else
|
|
975
|
+
this.endSelection();
|
|
976
|
+
|
|
985
977
|
this.cursorToString( cursor, substr );
|
|
986
|
-
|
|
987
|
-
|
|
978
|
+
|
|
979
|
+
if( e.shiftKey )
|
|
980
|
+
this._processSelection( cursor, e );
|
|
981
|
+
}
|
|
982
|
+
else // Next char
|
|
983
|
+
{
|
|
988
984
|
var letter = this.getCharAtPos( cursor );
|
|
989
985
|
if( letter ) {
|
|
990
|
-
|
|
991
|
-
|
|
986
|
+
|
|
987
|
+
// Selecting chars
|
|
988
|
+
if( e.shiftKey )
|
|
989
|
+
{
|
|
990
|
+
if( !cursor.selection )
|
|
991
|
+
this.startSelection( cursor );
|
|
992
|
+
|
|
992
993
|
this.cursorToRight( letter, cursor );
|
|
993
|
-
this.
|
|
994
|
-
}
|
|
995
|
-
|
|
994
|
+
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
995
|
+
}
|
|
996
|
+
else
|
|
997
|
+
{
|
|
998
|
+
if( !cursor.selection ) {
|
|
996
999
|
this.cursorToRight( letter, cursor );
|
|
997
1000
|
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
998
1001
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
999
1002
|
}
|
|
1000
|
-
else
|
|
1003
|
+
else
|
|
1001
1004
|
{
|
|
1002
|
-
|
|
1005
|
+
cursor.selection.invertIfNecessary();
|
|
1003
1006
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
|
|
1004
|
-
this.cursorToLine( cursor,
|
|
1005
|
-
this.cursorToPosition( cursor,
|
|
1007
|
+
this.cursorToLine( cursor, cursor.selection.toY );
|
|
1008
|
+
this.cursorToPosition( cursor, cursor.selection.toX );
|
|
1006
1009
|
this.endSelection();
|
|
1007
1010
|
}
|
|
1008
1011
|
}
|
|
@@ -1010,12 +1013,13 @@ class CodeEditor {
|
|
|
1010
1013
|
else if( this.code.lines[ cursor.line + 1 ] !== undefined ) {
|
|
1011
1014
|
|
|
1012
1015
|
if( e.shiftKey ) {
|
|
1013
|
-
if( !
|
|
1016
|
+
if( !cursor.selection ) this.startSelection( cursor );
|
|
1014
1017
|
}
|
|
1018
|
+
else this.endSelection();
|
|
1015
1019
|
|
|
1016
1020
|
this.lineDown( cursor, true );
|
|
1017
1021
|
|
|
1018
|
-
if( e.shiftKey ) this.
|
|
1022
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false );
|
|
1019
1023
|
|
|
1020
1024
|
this.hideAutoCompleteBox();
|
|
1021
1025
|
}
|
|
@@ -1115,14 +1119,14 @@ class CodeEditor {
|
|
|
1115
1119
|
|
|
1116
1120
|
let lidx = cursor.line;
|
|
1117
1121
|
|
|
1118
|
-
if(
|
|
1122
|
+
if( cursor.selection ) {
|
|
1119
1123
|
this.deleteSelection( cursor );
|
|
1120
1124
|
lidx = cursor.line;
|
|
1121
1125
|
}
|
|
1122
1126
|
|
|
1123
1127
|
this.endSelection();
|
|
1124
1128
|
|
|
1125
|
-
const new_lines = text.split( '\n' );
|
|
1129
|
+
const new_lines = text.replaceAll( '\r', '' ).split( '\n' );
|
|
1126
1130
|
|
|
1127
1131
|
// Pasting Multiline...
|
|
1128
1132
|
if( new_lines.length != 1 )
|
|
@@ -1177,10 +1181,16 @@ class CodeEditor {
|
|
|
1177
1181
|
|
|
1178
1182
|
const inner_add_tab = ( text, name, title ) => {
|
|
1179
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
|
+
|
|
1180
1188
|
// Set current text and language
|
|
1181
|
-
|
|
1189
|
+
|
|
1190
|
+
const lines = text.split( '\n' );
|
|
1182
1191
|
|
|
1183
1192
|
// Add item in the explorer if used
|
|
1193
|
+
|
|
1184
1194
|
if( this.explorer )
|
|
1185
1195
|
{
|
|
1186
1196
|
this._storedLines = this._storedLines ?? {};
|
|
@@ -1227,8 +1237,9 @@ class CodeEditor {
|
|
|
1227
1237
|
|
|
1228
1238
|
return;
|
|
1229
1239
|
}
|
|
1230
|
-
|
|
1240
|
+
|
|
1231
1241
|
let cursor = document.createElement( 'div' );
|
|
1242
|
+
cursor.name = "cursor" + this.cursors.childElementCount;
|
|
1232
1243
|
cursor.className = "cursor";
|
|
1233
1244
|
cursor.innerHTML = " ";
|
|
1234
1245
|
cursor.isMain = isMain;
|
|
@@ -1348,6 +1359,8 @@ class CodeEditor {
|
|
|
1348
1359
|
|
|
1349
1360
|
this.restoreCursor( currentCursor, step.cursors[ i ] );
|
|
1350
1361
|
}
|
|
1362
|
+
|
|
1363
|
+
this._hideActiveLine();
|
|
1351
1364
|
}
|
|
1352
1365
|
|
|
1353
1366
|
_addRedoStep( cursor ) {
|
|
@@ -1703,12 +1716,12 @@ class CodeEditor {
|
|
|
1703
1716
|
{
|
|
1704
1717
|
this.processClick( e );
|
|
1705
1718
|
|
|
1706
|
-
this.canOpenContextMenu = !
|
|
1719
|
+
this.canOpenContextMenu = !cursor.selection;
|
|
1707
1720
|
|
|
1708
|
-
if(
|
|
1721
|
+
if( cursor.selection )
|
|
1709
1722
|
{
|
|
1710
|
-
this.canOpenContextMenu |= (cursor.line >=
|
|
1711
|
-
&& 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);
|
|
1712
1725
|
if( this.canOpenContextMenu )
|
|
1713
1726
|
return;
|
|
1714
1727
|
}
|
|
@@ -1728,7 +1741,7 @@ class CodeEditor {
|
|
|
1728
1741
|
else if( e.type == 'mousemove' )
|
|
1729
1742
|
{
|
|
1730
1743
|
if( this.state.selectingText )
|
|
1731
|
-
this.
|
|
1744
|
+
this.processSelections( e );
|
|
1732
1745
|
}
|
|
1733
1746
|
|
|
1734
1747
|
else if ( e.type == 'click' ) // trip
|
|
@@ -1740,7 +1753,7 @@ class CodeEditor {
|
|
|
1740
1753
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
1741
1754
|
this.cursorToPosition( cursor, from );
|
|
1742
1755
|
this.startSelection( cursor );
|
|
1743
|
-
|
|
1756
|
+
cursor.selection.selectInline( cursor, from, cursor.line, this.measureString( word ) );
|
|
1744
1757
|
this.cursorToString( cursor, word ); // Go to the end of the word
|
|
1745
1758
|
break;
|
|
1746
1759
|
// Select entire line
|
|
@@ -1769,6 +1782,8 @@ class CodeEditor {
|
|
|
1769
1782
|
m.add( "" );
|
|
1770
1783
|
m.add( "Format/JSON", () => {
|
|
1771
1784
|
let json = this.toJSONFormat( this.getText() );
|
|
1785
|
+
if( !json )
|
|
1786
|
+
return;
|
|
1772
1787
|
this.code.lines = json.split( "\n" );
|
|
1773
1788
|
this.processLines();
|
|
1774
1789
|
} );
|
|
@@ -1786,10 +1801,9 @@ class CodeEditor {
|
|
|
1786
1801
|
this.endSelection();
|
|
1787
1802
|
}
|
|
1788
1803
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
}
|
|
1804
|
+
const cursor = this._getCurrentCursor();
|
|
1805
|
+
if( cursor.selection )
|
|
1806
|
+
cursor.selection.invertIfNecessary();
|
|
1793
1807
|
|
|
1794
1808
|
this.state.selectingText = false;
|
|
1795
1809
|
delete this._lastSelectionKeyDir;
|
|
@@ -1828,17 +1842,35 @@ class CodeEditor {
|
|
|
1828
1842
|
this.hideAutoCompleteBox();
|
|
1829
1843
|
}
|
|
1830
1844
|
|
|
1831
|
-
|
|
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 ) {
|
|
1832
1865
|
|
|
1833
|
-
var cursor = this._getCurrentCursor();
|
|
1834
1866
|
const isMouseEvent = e && ( e.constructor == MouseEvent );
|
|
1835
1867
|
|
|
1836
1868
|
if( isMouseEvent ) this.processClick( e );
|
|
1837
|
-
|
|
1869
|
+
|
|
1870
|
+
if( !cursor.selection )
|
|
1838
1871
|
this.startSelection( cursor );
|
|
1839
1872
|
|
|
1840
|
-
|
|
1841
|
-
this.code.childNodes.forEach( e => e.classList.remove( 'active-line' ) );
|
|
1873
|
+
this._hideActiveLine();
|
|
1842
1874
|
|
|
1843
1875
|
// Update selection
|
|
1844
1876
|
if( !keep_range )
|
|
@@ -1846,46 +1878,48 @@ class CodeEditor {
|
|
|
1846
1878
|
let ccw = true;
|
|
1847
1879
|
|
|
1848
1880
|
// Check if we must change ccw or not ... (not with mouse)
|
|
1849
|
-
if( !isMouseEvent && this.line >=
|
|
1850
|
-
(this.line ==
|
|
1881
|
+
if( !isMouseEvent && this.line >= cursor.selection.fromY &&
|
|
1882
|
+
(this.line == cursor.selection.fromY ? this.position >= cursor.selection.fromX : true) )
|
|
1851
1883
|
{
|
|
1852
1884
|
ccw = ( e && this._lastSelectionKeyDir && ( e.key == 'ArrowRight' || e.key == 'ArrowDown' || e.key == 'End' ) );
|
|
1853
1885
|
}
|
|
1854
1886
|
|
|
1855
1887
|
if( ccw )
|
|
1856
1888
|
{
|
|
1857
|
-
if( flags & CodeEditor.SELECTION_X )
|
|
1858
|
-
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;
|
|
1859
1891
|
}
|
|
1860
1892
|
else
|
|
1861
1893
|
{
|
|
1862
|
-
if( flags & CodeEditor.SELECTION_X )
|
|
1863
|
-
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;
|
|
1864
1896
|
}
|
|
1865
1897
|
|
|
1866
1898
|
this._lastSelectionKeyDir = ccw;
|
|
1867
1899
|
}
|
|
1868
1900
|
|
|
1869
1901
|
// Only leave if not a mouse selection...
|
|
1870
|
-
if( !isMouseEvent &&
|
|
1902
|
+
if( !isMouseEvent && cursor.selection.isEmpty() )
|
|
1871
1903
|
{
|
|
1872
1904
|
this.endSelection();
|
|
1873
1905
|
return;
|
|
1874
1906
|
}
|
|
1875
1907
|
|
|
1876
|
-
|
|
1908
|
+
cursor.selection.chars = 0;
|
|
1877
1909
|
|
|
1878
|
-
const fromX =
|
|
1879
|
-
fromY =
|
|
1880
|
-
toX =
|
|
1881
|
-
toY =
|
|
1910
|
+
const fromX = cursor.selection.fromX,
|
|
1911
|
+
fromY = cursor.selection.fromY,
|
|
1912
|
+
toX = cursor.selection.toX,
|
|
1913
|
+
toY = cursor.selection.toY;
|
|
1882
1914
|
const deltaY = toY - fromY;
|
|
1883
1915
|
|
|
1916
|
+
let cursorSelections = this.selections[ cursor.name ];
|
|
1917
|
+
|
|
1884
1918
|
// Selection goes down...
|
|
1885
1919
|
if( deltaY >= 0 )
|
|
1886
1920
|
{
|
|
1887
|
-
while( deltaY < (
|
|
1888
|
-
deleteElement(
|
|
1921
|
+
while( deltaY < ( cursorSelections.childElementCount - 1 ) )
|
|
1922
|
+
deleteElement( cursorSelections.lastChild );
|
|
1889
1923
|
|
|
1890
1924
|
for(let i = fromY; i <= toY; i++){
|
|
1891
1925
|
|
|
@@ -1896,12 +1930,12 @@ class CodeEditor {
|
|
|
1896
1930
|
if( isVisible )
|
|
1897
1931
|
{
|
|
1898
1932
|
// Make sure that the line selection is generated...
|
|
1899
|
-
domEl =
|
|
1933
|
+
domEl = cursorSelections.childNodes[ sId ];
|
|
1900
1934
|
if(!domEl)
|
|
1901
1935
|
{
|
|
1902
1936
|
domEl = document.createElement( 'div' );
|
|
1903
1937
|
domEl.className = "lexcodeselection";
|
|
1904
|
-
|
|
1938
|
+
cursorSelections.appendChild( domEl );
|
|
1905
1939
|
}
|
|
1906
1940
|
}
|
|
1907
1941
|
|
|
@@ -1923,7 +1957,7 @@ class CodeEditor {
|
|
|
1923
1957
|
}
|
|
1924
1958
|
|
|
1925
1959
|
const stringWidth = this.measureString( string );
|
|
1926
|
-
|
|
1960
|
+
cursor.selection.chars += stringWidth / this.charWidth;
|
|
1927
1961
|
|
|
1928
1962
|
if( isVisible )
|
|
1929
1963
|
{
|
|
@@ -1935,8 +1969,8 @@ class CodeEditor {
|
|
|
1935
1969
|
}
|
|
1936
1970
|
else // Selection goes up...
|
|
1937
1971
|
{
|
|
1938
|
-
while( Math.abs( deltaY ) < (
|
|
1939
|
-
deleteElement(
|
|
1972
|
+
while( Math.abs( deltaY ) < ( cursorSelections.childElementCount - 1 ) )
|
|
1973
|
+
deleteElement( cursorSelections.firstChild );
|
|
1940
1974
|
|
|
1941
1975
|
for( let i = toY; i <= fromY; i++ ){
|
|
1942
1976
|
|
|
@@ -1947,12 +1981,12 @@ class CodeEditor {
|
|
|
1947
1981
|
if( isVisible )
|
|
1948
1982
|
{
|
|
1949
1983
|
// Make sure that the line selection is generated...
|
|
1950
|
-
domEl =
|
|
1984
|
+
domEl = cursorSelections.childNodes[ sId ];
|
|
1951
1985
|
if(!domEl)
|
|
1952
1986
|
{
|
|
1953
1987
|
domEl = document.createElement( 'div' );
|
|
1954
1988
|
domEl.className = "lexcodeselection";
|
|
1955
|
-
|
|
1989
|
+
cursorSelections.appendChild( domEl );
|
|
1956
1990
|
}
|
|
1957
1991
|
}
|
|
1958
1992
|
|
|
@@ -1972,7 +2006,7 @@ class CodeEditor {
|
|
|
1972
2006
|
}
|
|
1973
2007
|
|
|
1974
2008
|
const stringWidth = this.measureString( string );
|
|
1975
|
-
|
|
2009
|
+
cursor.selection.chars += stringWidth / this.charWidth;
|
|
1976
2010
|
|
|
1977
2011
|
if( isVisible )
|
|
1978
2012
|
{
|
|
@@ -1994,14 +2028,21 @@ class CodeEditor {
|
|
|
1994
2028
|
const key = e.key ?? e.detail.key;
|
|
1995
2029
|
const target = e.detail.targetCursor;
|
|
1996
2030
|
|
|
2031
|
+
// Global keys
|
|
2032
|
+
|
|
2033
|
+
if( this._processGlobalKeys( e, key ) ) {
|
|
2034
|
+
// Stop propagation..
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
// By cursor keys
|
|
2039
|
+
|
|
1997
2040
|
if( target !== undefined )
|
|
1998
2041
|
{
|
|
1999
2042
|
this.processKeyAtTargetCursor( e, key, target );
|
|
2000
2043
|
return;
|
|
2001
2044
|
}
|
|
2002
2045
|
|
|
2003
|
-
// By cursor keys
|
|
2004
|
-
|
|
2005
2046
|
this._lastProcessedCursorIndex = null;
|
|
2006
2047
|
|
|
2007
2048
|
var lastProcessedCursor = null;
|
|
@@ -2033,10 +2074,6 @@ class CodeEditor {
|
|
|
2033
2074
|
cursorOffset.y += ( cursor.line - lastProcessedCursor.line );
|
|
2034
2075
|
}
|
|
2035
2076
|
|
|
2036
|
-
// Global keys
|
|
2037
|
-
|
|
2038
|
-
this._processGlobalKeys( e, key );
|
|
2039
|
-
|
|
2040
2077
|
// Clear tmp
|
|
2041
2078
|
|
|
2042
2079
|
delete this._lastProcessedCursorIndex;
|
|
@@ -2054,41 +2091,72 @@ class CodeEditor {
|
|
|
2054
2091
|
this._processGlobalKeys( e, key );
|
|
2055
2092
|
}
|
|
2056
2093
|
|
|
2057
|
-
|
|
2094
|
+
_processGlobalKeys( e, key ) {
|
|
2058
2095
|
|
|
2059
2096
|
let cursor = this._getCurrentCursor();
|
|
2060
2097
|
|
|
2061
2098
|
if( e.ctrlKey || e.metaKey )
|
|
2062
2099
|
{
|
|
2063
|
-
e.preventDefault();
|
|
2064
|
-
|
|
2065
2100
|
switch( key.toLowerCase() ) {
|
|
2066
2101
|
case 'a': // select all
|
|
2102
|
+
e.preventDefault();
|
|
2067
2103
|
this.selectAll();
|
|
2068
|
-
|
|
2104
|
+
return true;
|
|
2105
|
+
case 'c': // k+c, comment line
|
|
2106
|
+
e.preventDefault();
|
|
2107
|
+
if( this.state.keyChain == 'k' ) {
|
|
2108
|
+
this._commentLines();
|
|
2109
|
+
return true;
|
|
2110
|
+
}
|
|
2111
|
+
return false;
|
|
2112
|
+
case 'd': // next ocurrence
|
|
2113
|
+
e.preventDefault();
|
|
2114
|
+
this.selectNextOcurrence( cursor );
|
|
2115
|
+
return true;
|
|
2069
2116
|
case 'f': // find/search
|
|
2117
|
+
e.preventDefault();
|
|
2070
2118
|
this.showSearchBox();
|
|
2071
|
-
|
|
2119
|
+
return true;
|
|
2072
2120
|
case 'g': // find line
|
|
2121
|
+
e.preventDefault();
|
|
2073
2122
|
this.showSearchLineBox();
|
|
2074
|
-
|
|
2123
|
+
return true;
|
|
2124
|
+
case 'k': // shortcut chain
|
|
2125
|
+
e.preventDefault();
|
|
2126
|
+
this.state.keyChain = 'k';
|
|
2127
|
+
return true;
|
|
2075
2128
|
case 's': // save
|
|
2129
|
+
e.preventDefault();
|
|
2076
2130
|
this.onsave( this.getText() );
|
|
2077
|
-
|
|
2131
|
+
return true;
|
|
2132
|
+
case 'u': // k+u, uncomment line
|
|
2133
|
+
e.preventDefault();
|
|
2134
|
+
if( this.state.keyChain == 'k' ) {
|
|
2135
|
+
this._uncommentLines();
|
|
2136
|
+
return true;
|
|
2137
|
+
}
|
|
2138
|
+
return false;
|
|
2078
2139
|
case 'y': // redo
|
|
2140
|
+
e.preventDefault();
|
|
2079
2141
|
this._doRedo( cursor );
|
|
2080
|
-
|
|
2142
|
+
return true;
|
|
2081
2143
|
case 'z': // undo
|
|
2144
|
+
e.preventDefault();
|
|
2082
2145
|
this._doUndo( cursor );
|
|
2083
|
-
|
|
2146
|
+
return true;
|
|
2084
2147
|
case '+': // increase size
|
|
2148
|
+
e.preventDefault();
|
|
2085
2149
|
this._increaseFontSize();
|
|
2086
|
-
|
|
2150
|
+
return true;
|
|
2087
2151
|
case '-': // decrease size
|
|
2152
|
+
e.preventDefault();
|
|
2088
2153
|
this._decreaseFontSize();
|
|
2089
|
-
|
|
2154
|
+
return true;
|
|
2090
2155
|
}
|
|
2091
2156
|
}
|
|
2157
|
+
|
|
2158
|
+
this.state.keyChain = null;
|
|
2159
|
+
return false;
|
|
2092
2160
|
}
|
|
2093
2161
|
|
|
2094
2162
|
async _processKeyAtCursor( e, key, cursor ) {
|
|
@@ -2110,14 +2178,8 @@ class CodeEditor {
|
|
|
2110
2178
|
{
|
|
2111
2179
|
switch( key.toLowerCase() ) {
|
|
2112
2180
|
case 'c': // copy
|
|
2113
|
-
// TODO: COPY TEXT FROM EVERY CURSOR
|
|
2114
2181
|
this._copyContent( cursor );
|
|
2115
2182
|
return;
|
|
2116
|
-
case 'd': // duplicate line
|
|
2117
|
-
e.preventDefault();
|
|
2118
|
-
// TODO: UPDATE NEXT CURSOR ON MODIFY STATE
|
|
2119
|
-
this._duplicateLine( lidx, cursor );
|
|
2120
|
-
return;
|
|
2121
2183
|
case 'v': // paste
|
|
2122
2184
|
this._pasteContent( cursor );
|
|
2123
2185
|
return;
|
|
@@ -2138,6 +2200,10 @@ class CodeEditor {
|
|
|
2138
2200
|
else if( e.altKey )
|
|
2139
2201
|
{
|
|
2140
2202
|
switch( key ) {
|
|
2203
|
+
case 'd': // duplicate line
|
|
2204
|
+
e.preventDefault();
|
|
2205
|
+
this._duplicateLine( lidx, cursor );
|
|
2206
|
+
return;
|
|
2141
2207
|
case 'ArrowUp':
|
|
2142
2208
|
if(this.code.lines[ lidx - 1 ] == undefined)
|
|
2143
2209
|
return;
|
|
@@ -2168,7 +2234,7 @@ class CodeEditor {
|
|
|
2168
2234
|
if( key != actKey ) continue;
|
|
2169
2235
|
e.preventDefault();
|
|
2170
2236
|
|
|
2171
|
-
if( this.actions[ key ].deleteSelection &&
|
|
2237
|
+
if( this.actions[ key ].deleteSelection && cursor.selection )
|
|
2172
2238
|
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
2173
2239
|
|
|
2174
2240
|
return this.actions[ key ].callback( lidx, cursor, e );
|
|
@@ -2197,7 +2263,7 @@ class CodeEditor {
|
|
|
2197
2263
|
// Until this point, if there was a selection, we need
|
|
2198
2264
|
// to delete the content..
|
|
2199
2265
|
|
|
2200
|
-
if(
|
|
2266
|
+
if( cursor.selection )
|
|
2201
2267
|
{
|
|
2202
2268
|
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
2203
2269
|
lidx = cursor.line;
|
|
@@ -2258,22 +2324,32 @@ class CodeEditor {
|
|
|
2258
2324
|
|
|
2259
2325
|
let text = await navigator.clipboard.readText();
|
|
2260
2326
|
|
|
2327
|
+
// Remove any possible tabs (\t) and add spaces
|
|
2328
|
+
text = text.replaceAll( /\t|\\t/g, ' '.repeat( this.tabSpaces ) );
|
|
2329
|
+
|
|
2261
2330
|
this._addUndoStep( cursor, true );
|
|
2262
2331
|
|
|
2263
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
|
+
}
|
|
2264
2340
|
}
|
|
2265
2341
|
|
|
2266
2342
|
async _copyContent( cursor ) {
|
|
2267
2343
|
|
|
2268
2344
|
let text_to_copy = "";
|
|
2269
2345
|
|
|
2270
|
-
if( !
|
|
2346
|
+
if( !cursor.selection ) {
|
|
2271
2347
|
text_to_copy = "\n" + this.code.lines[ cursor.line ];
|
|
2272
2348
|
}
|
|
2273
2349
|
else {
|
|
2274
2350
|
|
|
2275
2351
|
// Some selections don't depend on mouse up..
|
|
2276
|
-
if(
|
|
2352
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2277
2353
|
|
|
2278
2354
|
const separator = "_NEWLINE_";
|
|
2279
2355
|
let code = this.code.lines.join( separator );
|
|
@@ -2281,11 +2357,11 @@ class CodeEditor {
|
|
|
2281
2357
|
// Get linear start index
|
|
2282
2358
|
let index = 0;
|
|
2283
2359
|
|
|
2284
|
-
for( let i = 0; i <=
|
|
2285
|
-
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 );
|
|
2286
2362
|
|
|
2287
|
-
index +=
|
|
2288
|
-
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;
|
|
2289
2365
|
const text = code.substr( index, num_chars );
|
|
2290
2366
|
const lines = text.split( separator );
|
|
2291
2367
|
text_to_copy = lines.join('\n');
|
|
@@ -2301,7 +2377,7 @@ class CodeEditor {
|
|
|
2301
2377
|
|
|
2302
2378
|
this._addUndoStep( cursor, true );
|
|
2303
2379
|
|
|
2304
|
-
if( !
|
|
2380
|
+
if( !cursor.selection ) {
|
|
2305
2381
|
text_to_cut = "\n" + this.code.lines[ cursor.line ];
|
|
2306
2382
|
this.code.lines.splice( lidx, 1 );
|
|
2307
2383
|
this.processLines();
|
|
@@ -2312,7 +2388,7 @@ class CodeEditor {
|
|
|
2312
2388
|
else {
|
|
2313
2389
|
|
|
2314
2390
|
// Some selections don't depend on mouse up..
|
|
2315
|
-
if(
|
|
2391
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2316
2392
|
|
|
2317
2393
|
const separator = "_NEWLINE_";
|
|
2318
2394
|
let code = this.code.lines.join(separator);
|
|
@@ -2320,11 +2396,11 @@ class CodeEditor {
|
|
|
2320
2396
|
// Get linear start index
|
|
2321
2397
|
let index = 0;
|
|
2322
2398
|
|
|
2323
|
-
for(let i = 0; i <=
|
|
2324
|
-
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);
|
|
2325
2401
|
|
|
2326
|
-
index +=
|
|
2327
|
-
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;
|
|
2328
2404
|
const text = code.substr(index, num_chars);
|
|
2329
2405
|
const lines = text.split(separator);
|
|
2330
2406
|
text_to_cut = lines.join('\n');
|
|
@@ -2345,6 +2421,114 @@ class CodeEditor {
|
|
|
2345
2421
|
this.hideAutoCompleteBox();
|
|
2346
2422
|
}
|
|
2347
2423
|
|
|
2424
|
+
_commentLines() {
|
|
2425
|
+
|
|
2426
|
+
this.state.keyChain = null;
|
|
2427
|
+
|
|
2428
|
+
if( cursor.selection )
|
|
2429
|
+
{
|
|
2430
|
+
var cursor = this._getCurrentCursor();
|
|
2431
|
+
this._addUndoStep( cursor, true );
|
|
2432
|
+
|
|
2433
|
+
const selectedLines = this.code.lines.slice( cursor.selection.fromY, cursor.selection.toY );
|
|
2434
|
+
const minIdx = Math.min(...selectedLines.map( v => {
|
|
2435
|
+
var idx = firstNonspaceIndex( v );
|
|
2436
|
+
return idx < 0 ? 1e10 : idx;
|
|
2437
|
+
} ));
|
|
2438
|
+
|
|
2439
|
+
for( var i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
2440
|
+
{
|
|
2441
|
+
this._commentLine( cursor, i, minIdx );
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
else
|
|
2445
|
+
{
|
|
2446
|
+
for( let cursor of this.cursors.children )
|
|
2447
|
+
{
|
|
2448
|
+
this._addUndoStep( cursor, true );
|
|
2449
|
+
this._commentLine( cursor, cursor.line );
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
this.processLines();
|
|
2454
|
+
this._hideActiveLine();
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
_commentLine( cursor, line, minNonspaceIdx ) {
|
|
2458
|
+
|
|
2459
|
+
const lang = this.languages[ this.highlight ];
|
|
2460
|
+
|
|
2461
|
+
if( !( lang.singleLineComments ?? true ))
|
|
2462
|
+
return;
|
|
2463
|
+
|
|
2464
|
+
const token = ( lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken ) + ' ';
|
|
2465
|
+
const string = this.code.lines[ line ];
|
|
2466
|
+
|
|
2467
|
+
let idx = firstNonspaceIndex( string );
|
|
2468
|
+
if( idx > -1 )
|
|
2469
|
+
{
|
|
2470
|
+
// Update idx using min of the selected lines (if necessary..)
|
|
2471
|
+
idx = minNonspaceIdx ?? idx;
|
|
2472
|
+
|
|
2473
|
+
this.code.lines[ line ] = [
|
|
2474
|
+
string.substring( 0, idx ),
|
|
2475
|
+
token,
|
|
2476
|
+
string.substring( idx )
|
|
2477
|
+
].join( '' );
|
|
2478
|
+
|
|
2479
|
+
this.cursorToString( cursor, token );
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
_uncommentLines() {
|
|
2484
|
+
|
|
2485
|
+
this.state.keyChain = null;
|
|
2486
|
+
|
|
2487
|
+
if( cursor.selection )
|
|
2488
|
+
{
|
|
2489
|
+
var cursor = this._getCurrentCursor();
|
|
2490
|
+
this._addUndoStep( cursor, true );
|
|
2491
|
+
|
|
2492
|
+
for( var i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
2493
|
+
{
|
|
2494
|
+
this._uncommentLine( cursor, i );
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
else
|
|
2498
|
+
{
|
|
2499
|
+
for( let cursor of this.cursors.children )
|
|
2500
|
+
{
|
|
2501
|
+
this._addUndoStep( cursor, true );
|
|
2502
|
+
this._uncommentLine( cursor, cursor.line );
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
this.processLines();
|
|
2507
|
+
this._hideActiveLine();
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
_uncommentLine( cursor, line ) {
|
|
2511
|
+
|
|
2512
|
+
const lang = this.languages[ this.highlight ];
|
|
2513
|
+
|
|
2514
|
+
if( !( lang.singleLineComments ?? true ))
|
|
2515
|
+
return;
|
|
2516
|
+
|
|
2517
|
+
const token = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
2518
|
+
const string = this.code.lines[ line ];
|
|
2519
|
+
|
|
2520
|
+
if( string.includes( token ) )
|
|
2521
|
+
{
|
|
2522
|
+
this.code.lines[ line ] = string.replace( token + ' ', '' );
|
|
2523
|
+
|
|
2524
|
+
// try deleting token + space, and then if not, delete only the token
|
|
2525
|
+
if( string.length == this.code.lines[ line ].length )
|
|
2526
|
+
this.code.lines[ line ] = string.replace( token, '' );
|
|
2527
|
+
|
|
2528
|
+
this.cursorToString( cursor, 'X'.repeat( Math.abs( string.length - this.code.lines[ line ].length ) ), true );
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2348
2532
|
action( key, deleteSelection, fn ) {
|
|
2349
2533
|
|
|
2350
2534
|
this.actions[ key ] = {
|
|
@@ -2379,6 +2563,7 @@ class CodeEditor {
|
|
|
2379
2563
|
processLines( mode ) {
|
|
2380
2564
|
|
|
2381
2565
|
var code_html = "";
|
|
2566
|
+
this._blockCommentCache.length = 0;
|
|
2382
2567
|
|
|
2383
2568
|
// Reset all lines content
|
|
2384
2569
|
this.code.innerHTML = "";
|
|
@@ -2413,8 +2598,7 @@ class CodeEditor {
|
|
|
2413
2598
|
this.code.style.top = ( this.visibleLinesViewport.x * this.lineHeight ) + "px";
|
|
2414
2599
|
|
|
2415
2600
|
// Update selections
|
|
2416
|
-
|
|
2417
|
-
this.processSelection( null, true );
|
|
2601
|
+
this.updateSelections( null, true );
|
|
2418
2602
|
|
|
2419
2603
|
this._clearTmpVariables();
|
|
2420
2604
|
this._setActiveLine();
|
|
@@ -2423,6 +2607,13 @@ class CodeEditor {
|
|
|
2423
2607
|
|
|
2424
2608
|
processLine( linenum, force ) {
|
|
2425
2609
|
|
|
2610
|
+
// Check if we are in block comment sections..
|
|
2611
|
+
if( !force && this._inBlockCommentSection( linenum ) )
|
|
2612
|
+
{
|
|
2613
|
+
this.processLines();
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2426
2617
|
const lang = this.languages[ this.highlight ];
|
|
2427
2618
|
const local_line_num = this.toLocalLine( linenum );
|
|
2428
2619
|
const gutter_line = "<span class='line-gutter'>" + (linenum + 1) + "</span>";
|
|
@@ -2431,6 +2622,7 @@ class CodeEditor {
|
|
|
2431
2622
|
if( !force ) // Single line update
|
|
2432
2623
|
{
|
|
2433
2624
|
this.code.childNodes[ local_line_num ].innerHTML = gutter_line + html;
|
|
2625
|
+
this._setActiveLine( linenum );
|
|
2434
2626
|
this._clearTmpVariables();
|
|
2435
2627
|
}
|
|
2436
2628
|
else // Update all lines at once
|
|
@@ -2457,6 +2649,9 @@ class CodeEditor {
|
|
|
2457
2649
|
return UPDATE_LINE( linestring );
|
|
2458
2650
|
}
|
|
2459
2651
|
|
|
2652
|
+
this._currentLineNumber = linenum;
|
|
2653
|
+
this._currentLineString = linestring;
|
|
2654
|
+
|
|
2460
2655
|
const tokensToEvaluate = this._getTokensFromLine( linestring );
|
|
2461
2656
|
|
|
2462
2657
|
if( !tokensToEvaluate.length )
|
|
@@ -2487,7 +2682,7 @@ class CodeEditor {
|
|
|
2487
2682
|
{
|
|
2488
2683
|
const blockCommentsToken = ( lang.blockCommentsTokens ?? this.defaultBlockCommentTokens )[ 0 ];
|
|
2489
2684
|
if( token.substr( 0, blockCommentsToken.length ) == blockCommentsToken )
|
|
2490
|
-
this._buildingBlockComment =
|
|
2685
|
+
this._buildingBlockComment = linenum;
|
|
2491
2686
|
}
|
|
2492
2687
|
|
|
2493
2688
|
line_inner_html += this._evaluateToken( {
|
|
@@ -2525,19 +2720,12 @@ class CodeEditor {
|
|
|
2525
2720
|
var matches = (linestring.substring( 0, idx ).match( re ) || []);
|
|
2526
2721
|
return (matches.length % 2) !== 0;
|
|
2527
2722
|
} );
|
|
2528
|
-
err |= stringKeys.some( function(v) {
|
|
2529
|
-
var re = new RegExp( v, "g" );
|
|
2530
|
-
var matches = (linestring.substring( idx ).match( re ) || []);
|
|
2531
|
-
return (matches.length % 2) !== 0;
|
|
2532
|
-
} );
|
|
2533
2723
|
return err ? undefined : idx;
|
|
2534
2724
|
}
|
|
2535
2725
|
}
|
|
2536
2726
|
|
|
2537
2727
|
_getTokensFromLine( linestring, skipNonWords ) {
|
|
2538
2728
|
|
|
2539
|
-
this._currentLineString = linestring;
|
|
2540
|
-
|
|
2541
2729
|
// Check if line comment
|
|
2542
2730
|
const ogLine = linestring;
|
|
2543
2731
|
const hasCommentIdx = this._lineHasComment( linestring );
|
|
@@ -2594,12 +2782,12 @@ class CodeEditor {
|
|
|
2594
2782
|
|
|
2595
2783
|
if( this.highlight == 'C++' || this.highlight == 'CSS' )
|
|
2596
2784
|
{
|
|
2597
|
-
var idx = tokens.slice( offset ).findIndex( ( value, index ) => this.
|
|
2785
|
+
var idx = tokens.slice( offset ).findIndex( ( value, index ) => this._isNumber( value ) );
|
|
2598
2786
|
if( idx > -1 )
|
|
2599
2787
|
{
|
|
2600
2788
|
idx += offset; // Add offset to compute within the whole array of tokens
|
|
2601
2789
|
let data = tokens[ idx ] + tokens[ ++idx ];
|
|
2602
|
-
while( this.
|
|
2790
|
+
while( this._isNumber( data ) )
|
|
2603
2791
|
{
|
|
2604
2792
|
tokens[ idx - 1 ] += tokens[ idx ];
|
|
2605
2793
|
tokens.splice( idx, 1 );
|
|
@@ -2647,7 +2835,7 @@ class CodeEditor {
|
|
|
2647
2835
|
// Manage strings
|
|
2648
2836
|
this._stringEnded = false;
|
|
2649
2837
|
|
|
2650
|
-
if( usePreviousTokenToCheckString || (
|
|
2838
|
+
if( usePreviousTokenToCheckString || ( this._buildingBlockComment === undefined && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) ) )
|
|
2651
2839
|
{
|
|
2652
2840
|
const checkIfStringEnded = t => {
|
|
2653
2841
|
const idx = Object.values( customStringKeys ).indexOf( t );
|
|
@@ -2696,22 +2884,22 @@ class CodeEditor {
|
|
|
2696
2884
|
else if( this._buildingString != undefined )
|
|
2697
2885
|
discardToken = this._appendStringToken( token );
|
|
2698
2886
|
|
|
2699
|
-
else if( this._mustHightlightWord( token,
|
|
2887
|
+
else if( this._mustHightlightWord( token, CodeEditor.keywords ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
|
|
2700
2888
|
token_classname = "cm-kwd";
|
|
2701
2889
|
|
|
2702
|
-
else if( this._mustHightlightWord( token,
|
|
2890
|
+
else if( this._mustHightlightWord( token, CodeEditor.builtin ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
|
|
2703
2891
|
token_classname = "cm-bln";
|
|
2704
2892
|
|
|
2705
|
-
else if( this._mustHightlightWord( token,
|
|
2893
|
+
else if( this._mustHightlightWord( token, CodeEditor.statementsAndDeclarations ) )
|
|
2706
2894
|
token_classname = "cm-std";
|
|
2707
2895
|
|
|
2708
|
-
else if( this._mustHightlightWord( token,
|
|
2896
|
+
else if( this._mustHightlightWord( token, CodeEditor.symbols ) )
|
|
2709
2897
|
token_classname = "cm-sym";
|
|
2710
2898
|
|
|
2711
2899
|
else if( token.substr( 0, singleLineCommentToken.length ) == singleLineCommentToken )
|
|
2712
2900
|
token_classname = "cm-com";
|
|
2713
2901
|
|
|
2714
|
-
else if( this.
|
|
2902
|
+
else if( this._isNumber( token ) || this._isNumber( token.replace(/[px]|[em]|%/g,'') ) )
|
|
2715
2903
|
token_classname = "cm-dec";
|
|
2716
2904
|
|
|
2717
2905
|
else if( this._isCSSClass( token, prev, next ) )
|
|
@@ -2723,7 +2911,7 @@ class CodeEditor {
|
|
|
2723
2911
|
else if ( highlight == 'batch' && ( token == '@' || prev == ':' || prev == '@' ) )
|
|
2724
2912
|
token_classname = "cm-kwd";
|
|
2725
2913
|
|
|
2726
|
-
else if (
|
|
2914
|
+
else if ( [ 'cpp', 'wgsl', 'glsl' ].indexOf( highlight ) > -1 && token.includes( '#' ) ) // C++ preprocessor
|
|
2727
2915
|
token_classname = "cm-ppc";
|
|
2728
2916
|
|
|
2729
2917
|
else if ( highlight == 'cpp' && prev == '<' && (next == '>' || next == '*') ) // Defining template type in C++
|
|
@@ -2748,9 +2936,10 @@ class CodeEditor {
|
|
|
2748
2936
|
token_classname = "cm-mtd";
|
|
2749
2937
|
|
|
2750
2938
|
|
|
2751
|
-
if( usesBlockComments && this._buildingBlockComment
|
|
2939
|
+
if( usesBlockComments && this._buildingBlockComment != undefined
|
|
2752
2940
|
&& token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
|
|
2753
2941
|
{
|
|
2942
|
+
this._blockCommentCache.push( new LX.vec2( this._buildingBlockComment, this._currentLineNumber ) );
|
|
2754
2943
|
delete this._buildingBlockComment;
|
|
2755
2944
|
}
|
|
2756
2945
|
|
|
@@ -2799,16 +2988,27 @@ class CodeEditor {
|
|
|
2799
2988
|
_enclosedByTokens( token, tokenIndex, tagStart, tagEnd ) {
|
|
2800
2989
|
|
|
2801
2990
|
const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];
|
|
2802
|
-
const tagStartIndex = indexOfFrom(this._currentLineString, tagStart, tokenStartIndex, true );
|
|
2991
|
+
const tagStartIndex = indexOfFrom( this._currentLineString, tagStart, tokenStartIndex, true );
|
|
2803
2992
|
if( tagStartIndex < 0 ) // Not found..
|
|
2804
2993
|
return;
|
|
2805
|
-
const tagEndIndex = indexOfFrom(this._currentLineString, tagEnd, tokenStartIndex );
|
|
2994
|
+
const tagEndIndex = indexOfFrom( this._currentLineString, tagEnd, tokenStartIndex );
|
|
2806
2995
|
if( tagEndIndex < 0 ) // Not found..
|
|
2807
2996
|
return;
|
|
2808
2997
|
|
|
2809
2998
|
return ( tagStartIndex < tokenStartIndex ) && ( tagEndIndex >= ( tokenStartIndex + token.length ) );
|
|
2810
2999
|
}
|
|
2811
3000
|
|
|
3001
|
+
_inBlockCommentSection( line ) {
|
|
3002
|
+
|
|
3003
|
+
for( var section of this._blockCommentCache )
|
|
3004
|
+
{
|
|
3005
|
+
if( line >= section.x && line <= section.y )
|
|
3006
|
+
return true;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
return false;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
2812
3012
|
_isCSSClass( token, prev, next ) {
|
|
2813
3013
|
|
|
2814
3014
|
if( this.highlight != 'CSS' )
|
|
@@ -2819,20 +3019,26 @@ class CodeEditor {
|
|
|
2819
3019
|
|| ( token[ 0 ] == '#' && prev != ':' ) );
|
|
2820
3020
|
}
|
|
2821
3021
|
|
|
2822
|
-
|
|
3022
|
+
_isNumber( token ) {
|
|
2823
3023
|
|
|
2824
3024
|
if(this.highlight == 'C++')
|
|
2825
3025
|
{
|
|
2826
3026
|
if( token.lastChar == 'f' )
|
|
2827
|
-
return this.
|
|
3027
|
+
return this._isNumber( token.substring(0, token.length - 1) )
|
|
2828
3028
|
else if( token.lastChar == 'u' )
|
|
2829
|
-
return !(token.includes('.')) && this.
|
|
3029
|
+
return !(token.includes('.')) && this._isNumber( token.substring(0, token.length - 1) );
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3032
|
+
else if(this.highlight == 'WGSL')
|
|
3033
|
+
{
|
|
3034
|
+
if( token.lastChar == 'u' )
|
|
3035
|
+
return !(token.includes('.')) && this._isNumber( token.substring(0, token.length - 1) );
|
|
2830
3036
|
}
|
|
2831
3037
|
|
|
2832
3038
|
else if(this.highlight == 'CSS')
|
|
2833
3039
|
{
|
|
2834
3040
|
if( token.lastChar == '%' )
|
|
2835
|
-
return this.
|
|
3041
|
+
return this._isNumber( token.substring(0, token.length - 1) )
|
|
2836
3042
|
}
|
|
2837
3043
|
|
|
2838
3044
|
return token.length && token != ' ' && !Number.isNaN(+token);
|
|
@@ -2841,7 +3047,7 @@ class CodeEditor {
|
|
|
2841
3047
|
_isType( token, prev, next ) {
|
|
2842
3048
|
|
|
2843
3049
|
// Common case
|
|
2844
|
-
if( this._mustHightlightWord( token,
|
|
3050
|
+
if( this._mustHightlightWord( token, CodeEditor.types ) )
|
|
2845
3051
|
return true;
|
|
2846
3052
|
|
|
2847
3053
|
if( this.highlight == 'JavaScript' )
|
|
@@ -2854,30 +3060,31 @@ class CodeEditor {
|
|
|
2854
3060
|
}
|
|
2855
3061
|
else if ( this.highlight == 'WGSL' )
|
|
2856
3062
|
{
|
|
2857
|
-
const
|
|
2858
|
-
return
|
|
2859
|
-
|
|
3063
|
+
const not_kwd = !this._mustHightlightWord( token, CodeEditor.keywords );
|
|
3064
|
+
return (prev == 'struct' && next == '{') ||
|
|
3065
|
+
(not_kwd && prev == ':' && next == ';') ||
|
|
3066
|
+
( not_kwd &&
|
|
2860
3067
|
( prev == ':' && next == ')' || prev == ':' && next == ',' || prev == '>' && next == '{'
|
|
2861
|
-
|| prev == '<' && next == ',' || prev == '<' && next == '>' || prev == '>' && !next ));
|
|
3068
|
+
|| prev == '<' && next == ',' || prev == '<' && next == '>' || prev == '>' && token != ';' && !next ));
|
|
2862
3069
|
}
|
|
2863
3070
|
}
|
|
2864
3071
|
|
|
2865
3072
|
_encloseSelectedWordWithKey( key, lidx, cursor ) {
|
|
2866
3073
|
|
|
2867
|
-
if( !
|
|
3074
|
+
if( !cursor.selection || (cursor.selection.fromY != cursor.selection.toY) )
|
|
2868
3075
|
return false;
|
|
2869
3076
|
|
|
2870
|
-
|
|
3077
|
+
cursor.selection.invertIfNecessary();
|
|
2871
3078
|
|
|
2872
3079
|
// Insert first..
|
|
2873
3080
|
this.code.lines[ lidx ] = [
|
|
2874
|
-
this.code.lines[ lidx ].slice(0,
|
|
3081
|
+
this.code.lines[ lidx ].slice(0, cursor.selection.fromX),
|
|
2875
3082
|
key,
|
|
2876
|
-
this.code.lines[ lidx ].slice(
|
|
3083
|
+
this.code.lines[ lidx ].slice(cursor.selection.fromX)
|
|
2877
3084
|
].join('');
|
|
2878
3085
|
|
|
2879
3086
|
// Go to the end of the word
|
|
2880
|
-
this.cursorToPosition(cursor,
|
|
3087
|
+
this.cursorToPosition(cursor, cursor.selection.toX + 1);
|
|
2881
3088
|
|
|
2882
3089
|
// Change next key?
|
|
2883
3090
|
switch(key)
|
|
@@ -2898,10 +3105,10 @@ class CodeEditor {
|
|
|
2898
3105
|
|
|
2899
3106
|
// Recompute and reposition current selection
|
|
2900
3107
|
|
|
2901
|
-
|
|
2902
|
-
|
|
3108
|
+
cursor.selection.fromX++;
|
|
3109
|
+
cursor.selection.toX++;
|
|
2903
3110
|
|
|
2904
|
-
this.
|
|
3111
|
+
this._processSelection( cursor );
|
|
2905
3112
|
this.processLine( lidx );
|
|
2906
3113
|
|
|
2907
3114
|
// Stop propagation
|
|
@@ -2949,14 +3156,16 @@ class CodeEditor {
|
|
|
2949
3156
|
|
|
2950
3157
|
startSelection( cursor ) {
|
|
2951
3158
|
|
|
2952
|
-
// Clear other selections...
|
|
2953
|
-
this.selections.innerHTML = "";
|
|
2954
|
-
|
|
2955
3159
|
// Show elements
|
|
2956
|
-
|
|
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;
|
|
2957
3166
|
|
|
2958
3167
|
// Create new selection instance
|
|
2959
|
-
|
|
3168
|
+
cursor.selection = new CodeSelection( this, cursor );
|
|
2960
3169
|
}
|
|
2961
3170
|
|
|
2962
3171
|
deleteSelection( cursor ) {
|
|
@@ -2966,37 +3175,51 @@ class CodeEditor {
|
|
|
2966
3175
|
return;
|
|
2967
3176
|
|
|
2968
3177
|
// Some selections don't depend on mouse up..
|
|
2969
|
-
if(
|
|
3178
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2970
3179
|
|
|
2971
3180
|
const separator = "_NEWLINE_";
|
|
2972
3181
|
let code = this.code.lines.join( separator );
|
|
2973
3182
|
|
|
2974
3183
|
// Get linear start index
|
|
2975
3184
|
let index = 0;
|
|
2976
|
-
for( let i = 0; i <=
|
|
2977
|
-
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);
|
|
2978
3187
|
|
|
2979
|
-
index +=
|
|
3188
|
+
index += cursor.selection.fromY * separator.length;
|
|
2980
3189
|
|
|
2981
|
-
const num_chars =
|
|
3190
|
+
const num_chars = cursor.selection.chars + (cursor.selection.toY - cursor.selection.fromY) * separator.length;
|
|
2982
3191
|
const pre = code.slice( 0, index );
|
|
2983
3192
|
const post = code.slice( index + num_chars );
|
|
2984
3193
|
|
|
2985
3194
|
this.code.lines = ( pre + post ).split( separator );
|
|
2986
3195
|
|
|
2987
|
-
this.cursorToLine( cursor,
|
|
2988
|
-
this.cursorToPosition( cursor,
|
|
2989
|
-
this.endSelection();
|
|
3196
|
+
this.cursorToLine( cursor, cursor.selection.fromY, true );
|
|
3197
|
+
this.cursorToPosition( cursor, cursor.selection.fromX );
|
|
3198
|
+
this.endSelection( cursor );
|
|
2990
3199
|
this.processLines();
|
|
2991
3200
|
}
|
|
2992
3201
|
|
|
2993
|
-
endSelection() {
|
|
3202
|
+
endSelection( cursor ) {
|
|
2994
3203
|
|
|
2995
|
-
this.selections.classList.remove( 'show' );
|
|
2996
|
-
this.selections.innerHTML = "";
|
|
2997
|
-
delete this.selection;
|
|
2998
3204
|
delete this._tripleClickSelection;
|
|
2999
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
|
+
}
|
|
3000
3223
|
}
|
|
3001
3224
|
|
|
3002
3225
|
selectAll() {
|
|
@@ -3010,13 +3233,13 @@ class CodeEditor {
|
|
|
3010
3233
|
this.startSelection( cursor );
|
|
3011
3234
|
|
|
3012
3235
|
const nlines = this.code.lines.length - 1;
|
|
3013
|
-
|
|
3014
|
-
|
|
3236
|
+
cursor.selection.toX = this.code.lines[ nlines ].length;
|
|
3237
|
+
cursor.selection.toY = nlines;
|
|
3015
3238
|
|
|
3016
|
-
this.cursorToPosition( cursor,
|
|
3017
|
-
this.cursorToLine( cursor,
|
|
3239
|
+
this.cursorToPosition( cursor, cursor.selection.toX );
|
|
3240
|
+
this.cursorToLine( cursor, cursor.selection.toY );
|
|
3018
3241
|
|
|
3019
|
-
this.
|
|
3242
|
+
this._processSelection( cursor, null, true );
|
|
3020
3243
|
|
|
3021
3244
|
this.hideAutoCompleteBox();
|
|
3022
3245
|
}
|
|
@@ -3122,6 +3345,8 @@ class CodeEditor {
|
|
|
3122
3345
|
|
|
3123
3346
|
state.position = cursor.position;
|
|
3124
3347
|
state.line = cursor.line;
|
|
3348
|
+
state.selection = cursor.selection ? cursor.selection.save() : undefined;
|
|
3349
|
+
|
|
3125
3350
|
return state;
|
|
3126
3351
|
}
|
|
3127
3352
|
|
|
@@ -3154,7 +3379,7 @@ class CodeEditor {
|
|
|
3154
3379
|
const cursorsInLine = Array.from( this.cursors.children ).filter( v => v.line == line );
|
|
3155
3380
|
|
|
3156
3381
|
while( cursorsInLine.length > 1 )
|
|
3157
|
-
cursorsInLine.pop()
|
|
3382
|
+
this.removeCursor( cursorsInLine.pop() );
|
|
3158
3383
|
}
|
|
3159
3384
|
|
|
3160
3385
|
restoreCursor( cursor, state ) {
|
|
@@ -3166,6 +3391,22 @@ class CodeEditor {
|
|
|
3166
3391
|
cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
|
|
3167
3392
|
cursor._top = cursor.line * this.lineHeight;
|
|
3168
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 );
|
|
3169
3410
|
}
|
|
3170
3411
|
|
|
3171
3412
|
resetCursorPos( flag, cursor ) {
|
|
@@ -3486,11 +3727,11 @@ class CodeEditor {
|
|
|
3486
3727
|
|
|
3487
3728
|
// Add language special keys...
|
|
3488
3729
|
suggestions = suggestions.concat(
|
|
3489
|
-
Object.keys(
|
|
3490
|
-
Object.keys(
|
|
3491
|
-
Object.keys(
|
|
3492
|
-
Object.keys(
|
|
3493
|
-
Object.keys(
|
|
3730
|
+
Object.keys( CodeEditor.builtin[ this.highlight ] ?? {} ),
|
|
3731
|
+
Object.keys( CodeEditor.keywords[ this.highlight ] ?? {} ),
|
|
3732
|
+
Object.keys( CodeEditor.statementsAndDeclarations[ this.highlight ] ?? {} ),
|
|
3733
|
+
Object.keys( CodeEditor.types[ this.highlight ] ?? {} ),
|
|
3734
|
+
Object.keys( CodeEditor.utils[ this.highlight ] ?? {} )
|
|
3494
3735
|
);
|
|
3495
3736
|
|
|
3496
3737
|
// Add words in current tab plus remove current word
|
|
@@ -3512,9 +3753,9 @@ class CodeEditor {
|
|
|
3512
3753
|
|
|
3513
3754
|
var icon = document.createElement( 'a' );
|
|
3514
3755
|
|
|
3515
|
-
if( this._mustHightlightWord( s,
|
|
3756
|
+
if( this._mustHightlightWord( s, CodeEditor.utils ) )
|
|
3516
3757
|
icon.className = "fa fa-cube";
|
|
3517
|
-
else if( this._mustHightlightWord( s,
|
|
3758
|
+
else if( this._mustHightlightWord( s, CodeEditor.types ) )
|
|
3518
3759
|
icon.className = "fa fa-code";
|
|
3519
3760
|
else
|
|
3520
3761
|
icon.className = "fa fa-font";
|
|
@@ -3630,6 +3871,8 @@ class CodeEditor {
|
|
|
3630
3871
|
|
|
3631
3872
|
showSearchBox( clear ) {
|
|
3632
3873
|
|
|
3874
|
+
this.hideSearchLineBox();
|
|
3875
|
+
|
|
3633
3876
|
this.searchbox.classList.add( 'opened' );
|
|
3634
3877
|
this.searchboxActive = true;
|
|
3635
3878
|
|
|
@@ -3639,12 +3882,26 @@ class CodeEditor {
|
|
|
3639
3882
|
{
|
|
3640
3883
|
input.value = "";
|
|
3641
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;
|
|
3642
3897
|
|
|
3643
3898
|
input.focus();
|
|
3644
3899
|
}
|
|
3645
3900
|
|
|
3646
3901
|
hideSearchBox() {
|
|
3647
3902
|
|
|
3903
|
+
const active = this.searchboxActive;
|
|
3904
|
+
|
|
3648
3905
|
if( this.searchboxActive )
|
|
3649
3906
|
{
|
|
3650
3907
|
this.searchbox.classList.remove( 'opened' );
|
|
@@ -3653,31 +3910,49 @@ class CodeEditor {
|
|
|
3653
3910
|
|
|
3654
3911
|
else if( this._lastResult )
|
|
3655
3912
|
{
|
|
3656
|
-
this._lastResult.dom
|
|
3913
|
+
deleteElement( this._lastResult.dom );
|
|
3657
3914
|
delete this._lastResult;
|
|
3658
3915
|
}
|
|
3916
|
+
|
|
3917
|
+
this.searchResultSelections.classList.remove( 'show' );
|
|
3918
|
+
|
|
3919
|
+
return active != this.searchboxActive;
|
|
3659
3920
|
}
|
|
3660
3921
|
|
|
3661
|
-
search( text, reverse ) {
|
|
3922
|
+
search( text, reverse, callback, skipAlert ) {
|
|
3662
3923
|
|
|
3663
3924
|
text = text ?? this._lastTextFound;
|
|
3664
3925
|
|
|
3665
3926
|
if( !text )
|
|
3666
3927
|
return;
|
|
3667
3928
|
|
|
3668
|
-
let
|
|
3929
|
+
let cursor = this._getCurrentCursor();
|
|
3930
|
+
let cursorData = new LX.vec2( cursor.position, cursor.line );
|
|
3669
3931
|
let line = null;
|
|
3670
3932
|
let char = -1;
|
|
3671
3933
|
|
|
3672
3934
|
if( this._lastResult )
|
|
3673
3935
|
{
|
|
3674
|
-
this._lastResult.dom
|
|
3936
|
+
deleteElement( this._lastResult.dom );
|
|
3675
3937
|
cursorData = this._lastResult.pos;
|
|
3676
3938
|
delete this._lastResult;
|
|
3677
3939
|
}
|
|
3678
3940
|
|
|
3679
3941
|
const getIndex = l => {
|
|
3680
|
-
|
|
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
|
+
}
|
|
3681
3956
|
};
|
|
3682
3957
|
|
|
3683
3958
|
if( reverse )
|
|
@@ -3707,7 +3982,15 @@ class CodeEditor {
|
|
|
3707
3982
|
|
|
3708
3983
|
if( line == null)
|
|
3709
3984
|
{
|
|
3710
|
-
|
|
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
|
+
};
|
|
3711
3994
|
return;
|
|
3712
3995
|
}
|
|
3713
3996
|
|
|
@@ -3717,32 +4000,44 @@ class CodeEditor {
|
|
|
3717
4000
|
have to add the length of the substring (0, first_ocurrence)
|
|
3718
4001
|
*/
|
|
3719
4002
|
|
|
3720
|
-
|
|
4003
|
+
|
|
4004
|
+
if( !reverse )
|
|
4005
|
+
char += ( line == cursorData.y ? cursorData.x : 0 );
|
|
4006
|
+
|
|
3721
4007
|
|
|
3722
4008
|
// Text found..
|
|
3723
4009
|
|
|
3724
4010
|
this._lastTextFound = text;
|
|
3725
4011
|
|
|
3726
4012
|
this.codeScroller.scrollTo(
|
|
3727
|
-
Math.max( char * this.charWidth - this.codeScroller.clientWidth ),
|
|
3728
|
-
Math.max( line - 10 ) * this.lineHeight
|
|
4013
|
+
Math.max( char * this.charWidth - this.codeScroller.clientWidth, 0 ),
|
|
4014
|
+
Math.max( line - 10, 0 ) * this.lineHeight
|
|
3729
4015
|
);
|
|
3730
4016
|
|
|
3731
|
-
|
|
3732
|
-
|
|
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
|
+
}
|
|
3733
4030
|
|
|
3734
|
-
// Create new selection instance
|
|
3735
|
-
this.selection = new CodeSelection( this, 0, 0, "lexcodesearchresult" );
|
|
3736
|
-
this.selection.selectInline( char, line, this.measureString( text ) );
|
|
3737
4031
|
this._lastResult = {
|
|
3738
|
-
'dom': this.
|
|
3739
|
-
'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 )
|
|
3740
4034
|
};
|
|
3741
|
-
|
|
3742
4035
|
}
|
|
3743
4036
|
|
|
3744
4037
|
showSearchLineBox() {
|
|
3745
4038
|
|
|
4039
|
+
this.hideSearchBox();
|
|
4040
|
+
|
|
3746
4041
|
this.searchlinebox.classList.add( 'opened' );
|
|
3747
4042
|
this.searchlineboxActive = true;
|
|
3748
4043
|
|
|
@@ -3762,7 +4057,7 @@ class CodeEditor {
|
|
|
3762
4057
|
|
|
3763
4058
|
goToLine( line ) {
|
|
3764
4059
|
|
|
3765
|
-
if( !this.
|
|
4060
|
+
if( !this._isNumber( line ) )
|
|
3766
4061
|
return;
|
|
3767
4062
|
|
|
3768
4063
|
this.codeScroller.scrollTo( 0, Math.max( line - 15 ) * this.lineHeight );
|
|
@@ -3772,6 +4067,40 @@ class CodeEditor {
|
|
|
3772
4067
|
this.cursorToLine( cursor, line - 1, true );
|
|
3773
4068
|
}
|
|
3774
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
|
+
|
|
3775
4104
|
_updateDataInfoPanel( signal, value ) {
|
|
3776
4105
|
|
|
3777
4106
|
if( !this.skipCodeInfo )
|
|
@@ -3809,6 +4138,11 @@ class CodeEditor {
|
|
|
3809
4138
|
}
|
|
3810
4139
|
}
|
|
3811
4140
|
|
|
4141
|
+
_hideActiveLine() {
|
|
4142
|
+
|
|
4143
|
+
this.code.querySelectorAll( '.active-line' ).forEach( e => e.classList.remove( 'active-line' ) );
|
|
4144
|
+
}
|
|
4145
|
+
|
|
3812
4146
|
_increaseFontSize() {
|
|
3813
4147
|
|
|
3814
4148
|
// Change font size
|
|
@@ -3864,13 +4198,96 @@ class CodeEditor {
|
|
|
3864
4198
|
_clearTmpVariables() {
|
|
3865
4199
|
|
|
3866
4200
|
delete this._currentLineString;
|
|
4201
|
+
delete this._currentLineNumber;
|
|
3867
4202
|
delete this._buildingString;
|
|
3868
4203
|
delete this._pendingString;
|
|
3869
4204
|
delete this._buildingBlockComment;
|
|
3870
4205
|
delete this._markdownHeader;
|
|
4206
|
+
delete this._lastResult;
|
|
3871
4207
|
}
|
|
3872
4208
|
}
|
|
3873
4209
|
|
|
4210
|
+
CodeEditor.keywords = {
|
|
4211
|
+
|
|
4212
|
+
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
|
|
4213
|
+
'arguments', 'extends', 'instanceof'],
|
|
4214
|
+
'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
|
|
4215
|
+
'NULL', 'unsigned', 'namespace'],
|
|
4216
|
+
'JSON': ['true', 'false'],
|
|
4217
|
+
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
4218
|
+
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
|
|
4219
|
+
'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
|
|
4220
|
+
'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
|
|
4221
|
+
'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
|
|
4222
|
+
'texture_storage_2d_array', 'texture_storage_3d', 'vec2u', 'vec3u', 'vec4u'],
|
|
4223
|
+
'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true',
|
|
4224
|
+
'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
|
|
4225
|
+
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
|
|
4226
|
+
'Batch': ['set', 'SET', 'echo', 'ECHO', 'off', 'OFF', 'del', 'DEL', 'defined', 'DEFINED', 'setlocal', 'SETLOCAL', 'enabledelayedexpansion', 'ENABLEDELAYEDEXPANSION', 'driverquery',
|
|
4227
|
+
'DRIVERQUERY', 'print', 'PRINT'],
|
|
4228
|
+
'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
4229
|
+
'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
4230
|
+
};
|
|
4231
|
+
|
|
4232
|
+
CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
|
|
4233
|
+
|
|
4234
|
+
'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
|
|
4235
|
+
'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement', 'prompt',
|
|
4236
|
+
'alert'],
|
|
4237
|
+
'WGSL': ['textureSample'],
|
|
4238
|
+
'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
|
|
4239
|
+
'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
|
|
4240
|
+
'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr',
|
|
4241
|
+
'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
|
|
4242
|
+
};
|
|
4243
|
+
|
|
4244
|
+
CodeEditor.types = {
|
|
4245
|
+
|
|
4246
|
+
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder'],
|
|
4247
|
+
'Rust': ['u128'],
|
|
4248
|
+
'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
|
|
4249
|
+
'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
|
|
4250
|
+
'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError',
|
|
4251
|
+
'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError', 'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError'],
|
|
4252
|
+
'C++': ['uint8_t', 'uint16_t', 'uint32_t']
|
|
4253
|
+
};
|
|
4254
|
+
|
|
4255
|
+
CodeEditor.builtin = {
|
|
4256
|
+
|
|
4257
|
+
'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
|
|
4258
|
+
'CSS': ['*', '!important'],
|
|
4259
|
+
'C++': ['vector', 'list', 'map'],
|
|
4260
|
+
'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
|
|
4261
|
+
'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
|
|
4262
|
+
};
|
|
4263
|
+
|
|
4264
|
+
CodeEditor.statementsAndDeclarations = {
|
|
4265
|
+
|
|
4266
|
+
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
|
|
4267
|
+
'CSS': ['@', 'import'],
|
|
4268
|
+
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog'],
|
|
4269
|
+
'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
|
|
4270
|
+
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'read_write', 'uniform', 'function', 'workgroup'],
|
|
4271
|
+
'Rust': ['break', 'else', 'continue', 'for', 'if', 'loop', 'match', 'return', 'while', 'do', 'yield'],
|
|
4272
|
+
'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue',
|
|
4273
|
+
'global', 'pass'],
|
|
4274
|
+
'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT']
|
|
4275
|
+
};
|
|
4276
|
+
|
|
4277
|
+
CodeEditor.symbols = {
|
|
4278
|
+
|
|
4279
|
+
'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
4280
|
+
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
|
|
4281
|
+
'JSON': ['[', ']', '{', '}', '(', ')'],
|
|
4282
|
+
'GLSL': ['[', ']', '{', '}', '(', ')'],
|
|
4283
|
+
'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
|
|
4284
|
+
'CSS': ['{', '}', '(', ')', '*'],
|
|
4285
|
+
'Rust': ['<', '>', '[', ']', '(', ')', '='],
|
|
4286
|
+
'Python': ['<', '>', '[', ']', '(', ')', '='],
|
|
4287
|
+
'Batch': ['[', ']', '(', ')', '%'],
|
|
4288
|
+
'HTML': ['<', '>', '/']
|
|
4289
|
+
};
|
|
4290
|
+
|
|
3874
4291
|
LX.CodeEditor = CodeEditor;
|
|
3875
4292
|
|
|
3876
4293
|
export { CodeEditor };
|