lexgui 0.1.15 → 0.1.16
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 +473 -170
- package/build/lexgui.css +72 -11
- package/build/lexgui.js +22 -27
- package/build/lexgui.module.js +12 -17
- package/examples/code_editor.html +9 -1
- package/package.json +1 -1
|
@@ -106,10 +106,31 @@ class CodeEditor {
|
|
|
106
106
|
|
|
107
107
|
constructor( area, options = {} ) {
|
|
108
108
|
|
|
109
|
+
// var a = [];
|
|
110
|
+
|
|
111
|
+
// var map = {};
|
|
112
|
+
|
|
113
|
+
// for( var i = 0; i < 1000000; ++i )
|
|
114
|
+
// map[i] = 1;
|
|
115
|
+
|
|
116
|
+
// const start = performance.now();
|
|
117
|
+
|
|
118
|
+
// for( var i = 0; i < 3000; ++i ) {
|
|
119
|
+
// const b = map[Math.floor( Math.random() * 1000000 )];
|
|
120
|
+
// }
|
|
121
|
+
|
|
122
|
+
// console.log( performance.now() - start );
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
// debugger;
|
|
126
|
+
|
|
127
|
+
|
|
109
128
|
window.editor = this;
|
|
110
129
|
|
|
111
130
|
CodeEditor.__instances.push( this );
|
|
112
131
|
|
|
132
|
+
var that = this;
|
|
133
|
+
|
|
113
134
|
this.base_area = area;
|
|
114
135
|
this.area = new LX.Area( { className: "lexcodeeditor", height: "auto", no_append: true } );
|
|
115
136
|
|
|
@@ -146,6 +167,9 @@ class CodeEditor {
|
|
|
146
167
|
this.root.addEventListener( 'click', this.processMouse.bind(this) );
|
|
147
168
|
this.root.addEventListener( 'contextmenu', this.processMouse.bind(this) );
|
|
148
169
|
|
|
170
|
+
// Take into account the scrollbar..
|
|
171
|
+
this.tabs.area.root.classList.add( 'codetabsarea' );
|
|
172
|
+
|
|
149
173
|
// Cursors and selection
|
|
150
174
|
|
|
151
175
|
this.cursors = document.createElement('div');
|
|
@@ -174,6 +198,96 @@ class CodeEditor {
|
|
|
174
198
|
this.cursors.appendChild(cursor);
|
|
175
199
|
}
|
|
176
200
|
|
|
201
|
+
// Add custom vertical scroll bar
|
|
202
|
+
{
|
|
203
|
+
var scrollbar = document.createElement('div');
|
|
204
|
+
scrollbar.className = "lexcodescrollbar";
|
|
205
|
+
this.scrollbar = scrollbar;
|
|
206
|
+
area.attach(this.scrollbar);
|
|
207
|
+
|
|
208
|
+
var scrollbarThumb = document.createElement('div');
|
|
209
|
+
this.scrollbarThumb = scrollbarThumb;
|
|
210
|
+
this.scrollbarThumb._top = 0;
|
|
211
|
+
scrollbar.appendChild(scrollbarThumb);
|
|
212
|
+
|
|
213
|
+
this.scrollbarThumb.addEventListener("mousedown", inner_mousedown);
|
|
214
|
+
|
|
215
|
+
var last_pos = 0;
|
|
216
|
+
|
|
217
|
+
function inner_mousedown(e)
|
|
218
|
+
{
|
|
219
|
+
var doc = that.root.ownerDocument;
|
|
220
|
+
doc.addEventListener("mousemove",inner_mousemove);
|
|
221
|
+
doc.addEventListener("mouseup",inner_mouseup);
|
|
222
|
+
last_pos = e.y;
|
|
223
|
+
e.stopPropagation();
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function inner_mousemove(e)
|
|
228
|
+
{
|
|
229
|
+
var dt = (last_pos - e.y);
|
|
230
|
+
|
|
231
|
+
that.applyVerticalScrollFromScrollBar( that.scrollbarThumb._top - dt )
|
|
232
|
+
|
|
233
|
+
last_pos = e.y;
|
|
234
|
+
e.stopPropagation();
|
|
235
|
+
e.preventDefault();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function inner_mouseup(e)
|
|
239
|
+
{
|
|
240
|
+
var doc = that.root.ownerDocument;
|
|
241
|
+
doc.removeEventListener("mousemove", inner_mousemove);
|
|
242
|
+
doc.removeEventListener("mouseup", inner_mouseup);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Add custom horizontal scroll bar
|
|
247
|
+
{
|
|
248
|
+
var hScrollbar = document.createElement('div');
|
|
249
|
+
hScrollbar.className = "lexcodescrollbar horizontal";
|
|
250
|
+
this.hScrollbar = hScrollbar;
|
|
251
|
+
area.attach(this.hScrollbar);
|
|
252
|
+
|
|
253
|
+
var hScrollbarThumb = document.createElement('div');
|
|
254
|
+
this.hScrollbarThumb = hScrollbarThumb;
|
|
255
|
+
this.hScrollbarThumb._left = 0;
|
|
256
|
+
hScrollbar.appendChild(hScrollbarThumb);
|
|
257
|
+
|
|
258
|
+
this.hScrollbarThumb.addEventListener("mousedown", inner_mousedown);
|
|
259
|
+
|
|
260
|
+
var last_pos = 0;
|
|
261
|
+
|
|
262
|
+
function inner_mousedown(e)
|
|
263
|
+
{
|
|
264
|
+
var doc = that.root.ownerDocument;
|
|
265
|
+
doc.addEventListener("mousemove",inner_mousemove);
|
|
266
|
+
doc.addEventListener("mouseup",inner_mouseup);
|
|
267
|
+
last_pos = e.x;
|
|
268
|
+
e.stopPropagation();
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function inner_mousemove(e)
|
|
273
|
+
{
|
|
274
|
+
var dt = (last_pos - e.x);
|
|
275
|
+
|
|
276
|
+
that.applyHorizontalScrollFromScrollBar( that.hScrollbarThumb._left - dt )
|
|
277
|
+
|
|
278
|
+
last_pos = e.x;
|
|
279
|
+
e.stopPropagation();
|
|
280
|
+
e.preventDefault();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function inner_mouseup(e)
|
|
284
|
+
{
|
|
285
|
+
var doc = that.root.ownerDocument;
|
|
286
|
+
doc.removeEventListener("mousemove", inner_mousemove);
|
|
287
|
+
doc.removeEventListener("mouseup", inner_mouseup);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
177
291
|
// Add autocomplete box
|
|
178
292
|
{
|
|
179
293
|
var box = document.createElement('div');
|
|
@@ -241,11 +355,13 @@ class CodeEditor {
|
|
|
241
355
|
'ArrowRight', 'ArrowLeft', 'Delete', 'Home',
|
|
242
356
|
'End', 'Tab', 'Escape'
|
|
243
357
|
];
|
|
358
|
+
|
|
244
359
|
this.keywords = {
|
|
245
360
|
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
|
|
246
361
|
'arguments', 'extends', 'instanceof'],
|
|
247
362
|
'C++': ['int', 'float', 'double', 'bool', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'nullptr',
|
|
248
363
|
'NULL', 'unsigned'],
|
|
364
|
+
'JSON': ['true', 'false'],
|
|
249
365
|
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
250
366
|
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
|
|
251
367
|
'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
|
|
@@ -281,7 +397,7 @@ class CodeEditor {
|
|
|
281
397
|
};
|
|
282
398
|
this.statementsAndDeclarations = {
|
|
283
399
|
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
|
|
284
|
-
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'glm'],
|
|
400
|
+
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'glm', 'spdlog'],
|
|
285
401
|
'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
|
|
286
402
|
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
|
|
287
403
|
'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue',
|
|
@@ -290,7 +406,7 @@ class CodeEditor {
|
|
|
290
406
|
};
|
|
291
407
|
this.symbols = {
|
|
292
408
|
'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
293
|
-
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::'],
|
|
409
|
+
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
|
|
294
410
|
'JSON': ['[', ']', '{', '}', '(', ')'],
|
|
295
411
|
'GLSL': ['[', ']', '{', '}', '(', ')'],
|
|
296
412
|
'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
|
|
@@ -299,6 +415,15 @@ class CodeEditor {
|
|
|
299
415
|
'Batch': ['[', ']', '(', ')', '%'],
|
|
300
416
|
};
|
|
301
417
|
|
|
418
|
+
// Convert reserved word arrays to maps so we can search tokens faster
|
|
419
|
+
|
|
420
|
+
for( let lang in this.keywords ) this.keywords[lang] = this.keywords[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
421
|
+
for( let lang in this.utils ) this.utils[lang] = this.utils[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
422
|
+
for( let lang in this.types ) this.types[lang] = this.types[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
423
|
+
for( let lang in this.builtin ) this.builtin[lang] = this.builtin[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
424
|
+
for( let lang in this.statementsAndDeclarations ) this.statementsAndDeclarations[lang] = this.statementsAndDeclarations[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
425
|
+
for( let lang in this.symbols ) this.symbols[lang] = this.symbols[lang].reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
426
|
+
|
|
302
427
|
// Action keys
|
|
303
428
|
|
|
304
429
|
this.action('Escape', false, ( ln, cursor, e ) => {
|
|
@@ -382,7 +507,7 @@ class CodeEditor {
|
|
|
382
507
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
383
508
|
if(idx > 0) this.cursorToString(cursor, prestring);
|
|
384
509
|
this._refreshCodeInfo(cursor.line, cursor.position);
|
|
385
|
-
this.
|
|
510
|
+
this.setScrollLeft( 0 );
|
|
386
511
|
|
|
387
512
|
if( e.shiftKey && !e.cancelShift )
|
|
388
513
|
{
|
|
@@ -411,8 +536,8 @@ class CodeEditor {
|
|
|
411
536
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
412
537
|
this.cursorToString( cursor, this.code.lines[ln] );
|
|
413
538
|
|
|
414
|
-
const last_char = (
|
|
415
|
-
this.
|
|
539
|
+
const last_char = (this.code.clientWidth / this.charWidth)|0;
|
|
540
|
+
this.setScrollLeft( cursor.position >= last_char ? (cursor.position - last_char) * this.charWidth : 0 );
|
|
416
541
|
});
|
|
417
542
|
|
|
418
543
|
this.action('Enter', true, ( ln, cursor, e ) => {
|
|
@@ -531,7 +656,7 @@ class CodeEditor {
|
|
|
531
656
|
|
|
532
657
|
if(e.metaKey) { // Apple devices (Command)
|
|
533
658
|
e.preventDefault();
|
|
534
|
-
this.actions[ 'Home' ].callback( ln, cursor );
|
|
659
|
+
this.actions[ 'Home' ].callback( ln, cursor, e );
|
|
535
660
|
}
|
|
536
661
|
else if(e.ctrlKey) {
|
|
537
662
|
// Get next word
|
|
@@ -581,8 +706,7 @@ class CodeEditor {
|
|
|
581
706
|
}
|
|
582
707
|
|
|
583
708
|
this.lineUp( cursor );
|
|
584
|
-
this.
|
|
585
|
-
this.cursorToPosition( cursor, this.code.lines[cursor.line].length );
|
|
709
|
+
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
586
710
|
|
|
587
711
|
if( e.shiftKey ) {
|
|
588
712
|
this.selection.toX = cursor.position;
|
|
@@ -771,12 +895,13 @@ class CodeEditor {
|
|
|
771
895
|
loadFile( file ) {
|
|
772
896
|
|
|
773
897
|
const inner_add_tab = ( text, name, title ) => {
|
|
774
|
-
this.addTab(name, true, title);
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
898
|
+
const existing = this.addTab(name, true, title);
|
|
899
|
+
if( !existing )
|
|
900
|
+
{
|
|
901
|
+
text = text.replaceAll('\r', '');
|
|
902
|
+
this.code.lines = text.split('\n');
|
|
903
|
+
this._changeLanguageFromExtension( LX.getExtension(name) );
|
|
904
|
+
}
|
|
780
905
|
};
|
|
781
906
|
|
|
782
907
|
if(file.constructor == String)
|
|
@@ -797,7 +922,6 @@ class CodeEditor {
|
|
|
797
922
|
inner_add_tab( text, file.name );
|
|
798
923
|
};
|
|
799
924
|
}
|
|
800
|
-
|
|
801
925
|
}
|
|
802
926
|
|
|
803
927
|
_addUndoStep( cursor ) {
|
|
@@ -901,7 +1025,7 @@ class CodeEditor {
|
|
|
901
1025
|
if(this.openedTabs[name])
|
|
902
1026
|
{
|
|
903
1027
|
this.tabs.select( this.code.tabName );
|
|
904
|
-
return;
|
|
1028
|
+
return true;
|
|
905
1029
|
}
|
|
906
1030
|
|
|
907
1031
|
// Create code content
|
|
@@ -913,37 +1037,36 @@ class CodeEditor {
|
|
|
913
1037
|
code.undoSteps = [];
|
|
914
1038
|
code.tabName = name;
|
|
915
1039
|
code.title = title ?? name;
|
|
916
|
-
code.tokens = {};
|
|
1040
|
+
code.tokens = {};
|
|
1041
|
+
code.customScroll = new LX.vec2;
|
|
917
1042
|
|
|
918
1043
|
code.addEventListener('dragenter', function(e) {
|
|
919
1044
|
e.preventDefault();
|
|
920
|
-
this.classList.add('dragging');
|
|
1045
|
+
this.parentElement.classList.add('dragging');
|
|
921
1046
|
});
|
|
922
1047
|
code.addEventListener('dragleave', function(e) {
|
|
923
1048
|
e.preventDefault();
|
|
924
|
-
this.
|
|
1049
|
+
this.parentElement.remove('dragging');
|
|
925
1050
|
});
|
|
926
1051
|
code.addEventListener('drop', (e) => {
|
|
927
1052
|
e.preventDefault();
|
|
928
|
-
code.classList.remove('dragging');
|
|
1053
|
+
code.parentElement.classList.remove('dragging');
|
|
929
1054
|
for( let i = 0; i < e.dataTransfer.files.length; ++i )
|
|
930
1055
|
this.loadFile( e.dataTransfer.files[i] );
|
|
931
1056
|
});
|
|
1057
|
+
code.addEventListener('wheel', (e) => {
|
|
932
1058
|
|
|
933
|
-
|
|
934
|
-
this.gutter.scrollTop = code.scrollTop;
|
|
935
|
-
this.gutter.scrollLeft = code.scrollLeft;
|
|
1059
|
+
// Get scroll data
|
|
936
1060
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
cursor.style.top = (cursor._top - code.scrollTop) + "px";
|
|
940
|
-
cursor.style.left = "calc( " + (cursor._left - code.scrollLeft) + "px + " + this.xPadding + ")";
|
|
1061
|
+
const dX = (e.deltaY > 0.0 ? 1.0 : -1.0) * 20.0 * ( e.shiftKey ? 1.0 : 0.0 );
|
|
1062
|
+
const dY = (e.deltaY > 0.0 ? 1.0 : -1.0) * 40.0 * ( e.shiftKey ? 0.0 : 1.0 );
|
|
941
1063
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1064
|
+
var new_scroll = code.customScroll.add( new LX.vec2( dX, dY ), new LX.vec2() );
|
|
1065
|
+
|
|
1066
|
+
// Update state
|
|
1067
|
+
|
|
1068
|
+
if( new_scroll.x != this.getScrollLeft()) this.setScrollLeft( new_scroll.x );
|
|
1069
|
+
if( new_scroll.y != this.getScrollTop()) this.setScrollTop( new_scroll.y );
|
|
947
1070
|
});
|
|
948
1071
|
|
|
949
1072
|
this.openedTabs[name] = code;
|
|
@@ -965,13 +1088,14 @@ class CodeEditor {
|
|
|
965
1088
|
this._refreshCodeInfo(cursor.line, cursor.position);
|
|
966
1089
|
|
|
967
1090
|
// Restore scroll
|
|
968
|
-
this.gutter.
|
|
969
|
-
this.gutter.
|
|
1091
|
+
this.gutter.scrollLeft = this.getScrollLeft();
|
|
1092
|
+
this.gutter.scrollTop = this.getScrollTop();
|
|
970
1093
|
}});
|
|
971
1094
|
|
|
972
1095
|
this.endSelection();
|
|
973
1096
|
|
|
974
|
-
if(selected)
|
|
1097
|
+
if( selected )
|
|
1098
|
+
{
|
|
975
1099
|
this.code = code;
|
|
976
1100
|
this.resetCursorPos(CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP);
|
|
977
1101
|
this.processLines();
|
|
@@ -1009,7 +1133,7 @@ class CodeEditor {
|
|
|
1009
1133
|
|
|
1010
1134
|
var cursor = this.cursors.children[0];
|
|
1011
1135
|
var code_rect = this.code.getBoundingClientRect();
|
|
1012
|
-
var mouse_pos = [(e.clientX - code_rect.x)
|
|
1136
|
+
var mouse_pos = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
|
|
1013
1137
|
|
|
1014
1138
|
// Discard out of lines click...
|
|
1015
1139
|
if( e.type != 'contextmenu' )
|
|
@@ -1020,9 +1144,6 @@ class CodeEditor {
|
|
|
1020
1144
|
|
|
1021
1145
|
if( e.type == 'mousedown' )
|
|
1022
1146
|
{
|
|
1023
|
-
if( mouse_pos[0] > this.code.scrollWidth || mouse_pos[1] > this.code.scrollHeight )
|
|
1024
|
-
return; // Scrollbar click
|
|
1025
|
-
|
|
1026
1147
|
// Left click only...
|
|
1027
1148
|
if( e.button === 2 )
|
|
1028
1149
|
{
|
|
@@ -1113,7 +1234,7 @@ class CodeEditor {
|
|
|
1113
1234
|
processClick(e, skip_refresh = false) {
|
|
1114
1235
|
|
|
1115
1236
|
var code_rect = this.code.getBoundingClientRect();
|
|
1116
|
-
var position = [(e.clientX - code_rect.x)
|
|
1237
|
+
var position = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
|
|
1117
1238
|
var ln = (position[1] / this.lineHeight)|0;
|
|
1118
1239
|
|
|
1119
1240
|
if(this.code.lines[ln] == undefined) return;
|
|
@@ -1125,7 +1246,6 @@ class CodeEditor {
|
|
|
1125
1246
|
|
|
1126
1247
|
var ch = (position[0] / this.charWidth)|0;
|
|
1127
1248
|
var string = this.code.lines[ln].slice(0, ch);
|
|
1128
|
-
// this.cursorToString(cursor, string);
|
|
1129
1249
|
this.cursorToPosition(cursor, string.length);
|
|
1130
1250
|
|
|
1131
1251
|
this.hideAutoCompleteBox();
|
|
@@ -1516,19 +1636,37 @@ class CodeEditor {
|
|
|
1516
1636
|
}
|
|
1517
1637
|
}
|
|
1518
1638
|
|
|
1519
|
-
processLines(
|
|
1639
|
+
processLines( from__legacy ) {
|
|
1520
1640
|
|
|
1521
|
-
|
|
1641
|
+
const start = performance.now();
|
|
1642
|
+
|
|
1643
|
+
var gutter_html = "";
|
|
1644
|
+
var code_html = "";
|
|
1645
|
+
|
|
1646
|
+
this.resizeScrollBars();
|
|
1522
1647
|
|
|
1523
|
-
this.gutter.innerHTML = "";
|
|
1524
1648
|
this.code.innerHTML = "";
|
|
1649
|
+
this.gutter.innerHTML = "";
|
|
1525
1650
|
|
|
1526
|
-
|
|
1651
|
+
// Get info about lines in viewport
|
|
1652
|
+
const margin = 20;
|
|
1653
|
+
const firstLineInViewport = (this.getScrollTop() / this.lineHeight)|0;
|
|
1654
|
+
const totalLinesInViewport = ((this.code.parentElement.offsetHeight - 36) / this.lineHeight)|0;
|
|
1655
|
+
const viewportRange = new LX.vec2(
|
|
1656
|
+
Math.max( firstLineInViewport - margin, 0 ),
|
|
1657
|
+
Math.min( firstLineInViewport + totalLinesInViewport + margin, this.code.lines.length )
|
|
1658
|
+
);
|
|
1659
|
+
|
|
1660
|
+
for( let i = viewportRange.x; i < viewportRange.y; ++i )
|
|
1527
1661
|
{
|
|
1528
|
-
|
|
1662
|
+
gutter_html += "<span>" + (i + 1) + "</span>";
|
|
1663
|
+
code_html += this.processLine( i, true );
|
|
1529
1664
|
}
|
|
1530
1665
|
|
|
1531
|
-
|
|
1666
|
+
this.code.innerHTML = code_html;
|
|
1667
|
+
this.gutter.innerHTML = gutter_html;
|
|
1668
|
+
|
|
1669
|
+
console.log( "Num lines processed: " + (viewportRange.y - viewportRange.x), performance.now() - start );
|
|
1532
1670
|
}
|
|
1533
1671
|
|
|
1534
1672
|
processLine( linenum, force ) {
|
|
@@ -1538,31 +1676,27 @@ class CodeEditor {
|
|
|
1538
1676
|
// It's allowed to process only 1 line to optimize
|
|
1539
1677
|
let linestring = this.code.lines[ linenum ];
|
|
1540
1678
|
|
|
1541
|
-
|
|
1542
|
-
pre.dataset['linenum'] = linenum;
|
|
1543
|
-
|
|
1544
|
-
// Line gutter
|
|
1545
|
-
var linenumspan = document.createElement('span');
|
|
1546
|
-
linenumspan.innerHTML = (linenum + 1);
|
|
1547
|
-
|
|
1548
|
-
if( force )
|
|
1679
|
+
if( !force )
|
|
1549
1680
|
{
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
{
|
|
1554
|
-
deleteElement( this.gutter.childNodes[ linenum ] );
|
|
1681
|
+
var pre = document.createElement('pre');
|
|
1682
|
+
|
|
1683
|
+
// Single code line
|
|
1555
1684
|
deleteElement( this.code.childNodes[ linenum ] );
|
|
1556
|
-
this.gutter.insertChildAtIndex( linenumspan, linenum );
|
|
1557
1685
|
this.code.insertChildAtIndex( pre, linenum );
|
|
1686
|
+
|
|
1687
|
+
// Gutter
|
|
1688
|
+
deleteElement( this.gutter.childNodes[ linenum ] );
|
|
1689
|
+
var linenumspan = document.createElement('span');
|
|
1690
|
+
linenumspan.innerHTML = (linenum + 1);
|
|
1691
|
+
this.gutter.insertChildAtIndex( linenumspan, linenum );
|
|
1558
1692
|
}
|
|
1559
1693
|
|
|
1560
|
-
var linespan = document.createElement('span');
|
|
1561
|
-
pre.appendChild(linespan);
|
|
1562
|
-
|
|
1563
1694
|
const tokensToEvaluate = this._getTokensFromString( linestring );
|
|
1564
1695
|
|
|
1565
|
-
|
|
1696
|
+
if( !tokensToEvaluate.length )
|
|
1697
|
+
return "<pre></pre>";
|
|
1698
|
+
|
|
1699
|
+
var line_inner_html = "";
|
|
1566
1700
|
|
|
1567
1701
|
// Process all tokens
|
|
1568
1702
|
for( var i = 0; i < tokensToEvaluate.length; ++i )
|
|
@@ -1576,12 +1710,12 @@ class CodeEditor {
|
|
|
1576
1710
|
|
|
1577
1711
|
it = i + 1;
|
|
1578
1712
|
let next = tokensToEvaluate[it];
|
|
1579
|
-
while( next == ' ' || next == '"') {
|
|
1713
|
+
while( next == ' ' || next == '"' ) {
|
|
1580
1714
|
it++;
|
|
1581
1715
|
next = tokensToEvaluate[it];
|
|
1582
1716
|
}
|
|
1583
1717
|
|
|
1584
|
-
|
|
1718
|
+
const token = tokensToEvaluate[i];
|
|
1585
1719
|
|
|
1586
1720
|
if( this.languages[ this.highlight ].blockComments ?? true )
|
|
1587
1721
|
{
|
|
@@ -1594,7 +1728,16 @@ class CodeEditor {
|
|
|
1594
1728
|
line_inner_html += this.evaluateToken(token, prev, next);
|
|
1595
1729
|
}
|
|
1596
1730
|
|
|
1597
|
-
|
|
1731
|
+
// Single line update
|
|
1732
|
+
if( !force )
|
|
1733
|
+
{
|
|
1734
|
+
this.code.childNodes[ linenum ].innerHTML = line_inner_html;
|
|
1735
|
+
}
|
|
1736
|
+
// Update all lines at once
|
|
1737
|
+
else
|
|
1738
|
+
{
|
|
1739
|
+
return "<pre>" + line_inner_html + "</pre>";
|
|
1740
|
+
}
|
|
1598
1741
|
}
|
|
1599
1742
|
|
|
1600
1743
|
_processTokens( tokens, offset = 0 ) {
|
|
@@ -1624,145 +1767,168 @@ class CodeEditor {
|
|
|
1624
1767
|
|
|
1625
1768
|
// Check if line comment
|
|
1626
1769
|
const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
1627
|
-
const
|
|
1628
|
-
|
|
1770
|
+
const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
|
|
1771
|
+
const has_comment = linestring.split(singleLineCommentToken);
|
|
1772
|
+
linestring = ( has_comment.length > 1 ) ? has_comment[0] : linestring;
|
|
1629
1773
|
|
|
1630
|
-
|
|
1774
|
+
// const tokens = linestring.split(' ').join('¬ ¬').split('¬'); // trick to split without losing spaces
|
|
1775
|
+
|
|
1631
1776
|
let tokensToEvaluate = []; // store in a temp array so we know prev and next tokens...
|
|
1632
1777
|
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
if( t == ' ' ) {
|
|
1639
|
-
tokensToEvaluate.push( t );
|
|
1640
|
-
continue;
|
|
1641
|
-
}
|
|
1778
|
+
const pushToken = function( t ) {
|
|
1779
|
+
if( (skipNonWords && ( t.includes('"') || t.length < 3 )) )
|
|
1780
|
+
return;
|
|
1781
|
+
tokensToEvaluate.push( t );
|
|
1782
|
+
};
|
|
1642
1783
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1784
|
+
let iter = linestring.matchAll(/(::|[\[\](){}<>.,;:*"'%@ ])/g);
|
|
1785
|
+
let subtokens = iter.next();
|
|
1786
|
+
if( subtokens.value )
|
|
1787
|
+
{
|
|
1788
|
+
let idx = 0;
|
|
1789
|
+
while( subtokens.value != undefined )
|
|
1646
1790
|
{
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
if(!subtokens.value) {
|
|
1656
|
-
const _at = t.substring(idx);
|
|
1657
|
-
if( _at.length ) tokensToEvaluate.push( _at );
|
|
1658
|
-
}
|
|
1791
|
+
const _pt = linestring.substring(idx, subtokens.value.index);
|
|
1792
|
+
if( _pt.length ) pushToken( _pt );
|
|
1793
|
+
pushToken( subtokens.value[0] );
|
|
1794
|
+
idx = subtokens.value.index + subtokens.value[0].length;
|
|
1795
|
+
subtokens = iter.next();
|
|
1796
|
+
if(!subtokens.value) {
|
|
1797
|
+
const _at = linestring.substring(idx);
|
|
1798
|
+
if( _at.length ) pushToken( _at );
|
|
1659
1799
|
}
|
|
1660
1800
|
}
|
|
1661
|
-
else tokensToEvaluate.push( t );
|
|
1662
1801
|
}
|
|
1802
|
+
else tokensToEvaluate.push( linestring );
|
|
1803
|
+
|
|
1804
|
+
// if( usesBlockComments )
|
|
1805
|
+
// {
|
|
1806
|
+
// var block = false;
|
|
1807
|
+
|
|
1808
|
+
// if( t.includes('/*') )
|
|
1809
|
+
// {
|
|
1810
|
+
// const idx = t.indexOf( '/*' );
|
|
1811
|
+
// tokensToEvaluate.push( t.substring( 0, idx ), '/*', t.substring( idx + 2 ) );
|
|
1812
|
+
// block |= true;
|
|
1813
|
+
// }
|
|
1814
|
+
// else if( t.includes('*/') )
|
|
1815
|
+
// {
|
|
1816
|
+
// const idx = t.indexOf( '*/' );
|
|
1817
|
+
// tokensToEvaluate.push( t.substring( 0, idx ), '*/', t.substring( idx + 2 ) );
|
|
1818
|
+
// block |= true;
|
|
1819
|
+
// }
|
|
1820
|
+
|
|
1821
|
+
// if( block ) continue;
|
|
1822
|
+
// }
|
|
1663
1823
|
|
|
1664
|
-
if(
|
|
1665
|
-
|
|
1824
|
+
if( has_comment.length > 1 && !skipNonWords )
|
|
1825
|
+
pushToken( singleLineCommentToken + has_comment[1] );
|
|
1826
|
+
|
|
1827
|
+
// console.log( tokensToEvaluate );
|
|
1666
1828
|
|
|
1667
1829
|
return this._processTokens( tokensToEvaluate );
|
|
1668
1830
|
}
|
|
1669
1831
|
|
|
1670
1832
|
_mustHightlightWord( token, kindArray ) {
|
|
1671
1833
|
|
|
1672
|
-
return kindArray[this.highlight] && kindArray[this.highlight]
|
|
1834
|
+
return kindArray[this.highlight] && kindArray[this.highlight][token] != undefined;
|
|
1673
1835
|
}
|
|
1674
1836
|
|
|
1675
1837
|
evaluateToken( token, prev, next ) {
|
|
1676
1838
|
|
|
1677
|
-
let stringEnded = false;
|
|
1678
|
-
let highlight = this.highlight.replace(/\s/g, '').replaceAll("+", "p").toLowerCase();
|
|
1679
|
-
let inner_html = "", token_classname = "";
|
|
1680
|
-
|
|
1681
|
-
const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
1682
|
-
const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
|
|
1683
|
-
const customStringKeys = Object.assign( {}, this.stringKeys );
|
|
1684
|
-
|
|
1685
|
-
if( highlight == 'cpp' && prev && prev.includes('#') ) // preprocessor code..
|
|
1686
|
-
{
|
|
1687
|
-
customStringKeys['@<'] = '>';
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
// Manage strings
|
|
1691
|
-
if( this._buildingString != undefined )
|
|
1692
|
-
{
|
|
1693
|
-
const idx = Object.values(customStringKeys).indexOf( token );
|
|
1694
|
-
stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
|
|
1695
|
-
}
|
|
1696
|
-
else if( customStringKeys[ '@' + token ] )
|
|
1697
|
-
{
|
|
1698
|
-
// Start new string
|
|
1699
|
-
this._buildingString = token;
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
1839
|
if(token == ' ')
|
|
1703
1840
|
{
|
|
1704
|
-
|
|
1841
|
+
return token;
|
|
1705
1842
|
}
|
|
1706
1843
|
else
|
|
1707
1844
|
{
|
|
1845
|
+
let stringEnded = false;
|
|
1846
|
+
let highlight = this.highlight.replace(/\s/g, '').replaceAll("+", "p").toLowerCase();
|
|
1847
|
+
|
|
1848
|
+
const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
1849
|
+
const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
|
|
1850
|
+
const customStringKeys = Object.assign( {}, this.stringKeys );
|
|
1851
|
+
|
|
1852
|
+
if( highlight == 'cpp' && prev && prev.includes('#') ) // preprocessor code..
|
|
1853
|
+
{
|
|
1854
|
+
customStringKeys['@<'] = '>';
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
// Manage strings
|
|
1858
|
+
if( this._buildingString != undefined )
|
|
1859
|
+
{
|
|
1860
|
+
const idx = Object.values(customStringKeys).indexOf( token );
|
|
1861
|
+
stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
|
|
1862
|
+
}
|
|
1863
|
+
else if( customStringKeys[ '@' + token ] )
|
|
1864
|
+
{
|
|
1865
|
+
// Start new string
|
|
1866
|
+
this._buildingString = token;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
let token_classname = "";
|
|
1870
|
+
|
|
1708
1871
|
if( this._buildingBlockComment != undefined )
|
|
1709
|
-
token_classname
|
|
1872
|
+
token_classname = "cm-com";
|
|
1710
1873
|
|
|
1711
1874
|
else if( this._buildingString != undefined )
|
|
1712
|
-
token_classname
|
|
1875
|
+
token_classname = "cm-str";
|
|
1713
1876
|
|
|
1714
1877
|
else if( this._mustHightlightWord( token, this.keywords ) )
|
|
1715
|
-
token_classname
|
|
1878
|
+
token_classname = "cm-kwd";
|
|
1716
1879
|
|
|
1717
1880
|
else if( this._mustHightlightWord( token, this.builtin ) )
|
|
1718
|
-
token_classname
|
|
1881
|
+
token_classname = "cm-bln";
|
|
1719
1882
|
|
|
1720
1883
|
else if( this._mustHightlightWord( token, this.statementsAndDeclarations ) )
|
|
1721
|
-
token_classname
|
|
1884
|
+
token_classname = "cm-std";
|
|
1722
1885
|
|
|
1723
1886
|
else if( this._mustHightlightWord( token, this.symbols ) )
|
|
1724
|
-
token_classname
|
|
1887
|
+
token_classname = "cm-sym";
|
|
1725
1888
|
|
|
1726
1889
|
else if( token.substr(0, 2) == singleLineCommentToken )
|
|
1727
|
-
token_classname
|
|
1890
|
+
token_classname = "cm-com";
|
|
1728
1891
|
|
|
1729
1892
|
else if( usesBlockComments && token.substr(0, 2) == '/*' )
|
|
1730
|
-
token_classname
|
|
1893
|
+
token_classname = "cm-com";
|
|
1731
1894
|
|
|
1732
1895
|
else if( usesBlockComments && token.substr(token.length - 2) == '*/' )
|
|
1733
|
-
token_classname
|
|
1896
|
+
token_classname = "cm-com";
|
|
1734
1897
|
|
|
1735
|
-
else if(
|
|
1736
|
-
token_classname
|
|
1898
|
+
else if( this.isNumber(token) || this.isNumber( token.replace(/[px]|[em]|%/g,'') ) )
|
|
1899
|
+
token_classname = "cm-dec";
|
|
1737
1900
|
|
|
1738
1901
|
else if( this.isCSSClass(token, prev, next) )
|
|
1739
|
-
token_classname
|
|
1902
|
+
token_classname = "cm-kwd";
|
|
1740
1903
|
|
|
1741
1904
|
else if ( this.isType(token, prev, next) )
|
|
1742
|
-
token_classname
|
|
1905
|
+
token_classname = "cm-typ";
|
|
1743
1906
|
|
|
1744
|
-
else if ( token
|
|
1745
|
-
token_classname
|
|
1907
|
+
else if ( highlight == 'batch' && (token == '@' || prev == ':' || prev == '@') )
|
|
1908
|
+
token_classname = "cm-kwd";
|
|
1746
1909
|
|
|
1747
1910
|
else if ( highlight == 'cpp' && token.includes('#') ) // C++ preprocessor
|
|
1748
|
-
token_classname
|
|
1911
|
+
token_classname = "cm-ppc";
|
|
1912
|
+
|
|
1913
|
+
else if ( highlight == 'cpp' && prev == '<' && (next == '>' || next == '*') ) // Defining template type in C++
|
|
1914
|
+
token_classname = "cm-typ";
|
|
1749
1915
|
|
|
1750
|
-
else if ( highlight == 'cpp' && prev
|
|
1751
|
-
token_classname
|
|
1916
|
+
else if ( highlight == 'cpp' && (next == '::' || prev == '::' && next != '(' )) // C++ Class
|
|
1917
|
+
token_classname = "cm-typ";
|
|
1752
1918
|
|
|
1753
1919
|
else if ( highlight == 'css' && prev == ':' && (next == ';' || next == '!important') ) // CSS value
|
|
1754
|
-
token_classname
|
|
1920
|
+
token_classname = "cm-str";
|
|
1755
1921
|
|
|
1756
1922
|
else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
|
|
1757
|
-
token_classname
|
|
1923
|
+
token_classname = "cm-typ";
|
|
1758
1924
|
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
}
|
|
1925
|
+
else if ( token[0] != '@' && next == '(' )
|
|
1926
|
+
token_classname = "cm-mtd";
|
|
1762
1927
|
|
|
1763
|
-
|
|
1928
|
+
this._buildingString = stringEnded ? undefined : this._buildingString;
|
|
1764
1929
|
|
|
1765
|
-
|
|
1930
|
+
return "<span class='" + highlight + " " + token_classname + "'>" + token + "</span>";
|
|
1931
|
+
}
|
|
1766
1932
|
}
|
|
1767
1933
|
|
|
1768
1934
|
isCSSClass( token, prev, next ) {
|
|
@@ -1798,7 +1964,7 @@ class CodeEditor {
|
|
|
1798
1964
|
}
|
|
1799
1965
|
else if ( this.highlight == 'WGSL' )
|
|
1800
1966
|
{
|
|
1801
|
-
const is_kwd =
|
|
1967
|
+
const is_kwd = !this._mustHightlightWord( token, this.keywords );
|
|
1802
1968
|
return (prev == 'struct' && next == '{') ||
|
|
1803
1969
|
( is_kwd &&
|
|
1804
1970
|
( prev == ':' && next == ')' || prev == ':' && next == ',' || prev == '>' && next == '{'
|
|
@@ -1947,9 +2113,9 @@ class CodeEditor {
|
|
|
1947
2113
|
// Add horizontal scroll
|
|
1948
2114
|
|
|
1949
2115
|
doAsync(() => {
|
|
1950
|
-
var last_char = ((this.code.
|
|
2116
|
+
var last_char = ((this.code.clientWidth) / this.charWidth)|0;
|
|
1951
2117
|
if( cursor.position >= last_char )
|
|
1952
|
-
this.
|
|
2118
|
+
this.setScrollLeft( this.getScrollLeft() + this.charWidth );
|
|
1953
2119
|
});
|
|
1954
2120
|
}
|
|
1955
2121
|
|
|
@@ -1965,10 +2131,12 @@ class CodeEditor {
|
|
|
1965
2131
|
this.restartBlink();
|
|
1966
2132
|
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1967
2133
|
|
|
2134
|
+
// Add horizontal scroll
|
|
2135
|
+
|
|
1968
2136
|
doAsync(() => {
|
|
1969
|
-
var first_char = (this.
|
|
2137
|
+
var first_char = (this.getScrollLeft() / this.charWidth)|0;
|
|
1970
2138
|
if( (cursor.position - 1) < first_char )
|
|
1971
|
-
this.
|
|
2139
|
+
this.setScrollLeft( this.getScrollLeft() - this.charWidth );
|
|
1972
2140
|
});
|
|
1973
2141
|
}
|
|
1974
2142
|
|
|
@@ -1986,9 +2154,9 @@ class CodeEditor {
|
|
|
1986
2154
|
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1987
2155
|
|
|
1988
2156
|
doAsync(() => {
|
|
1989
|
-
var first_line = (this.
|
|
2157
|
+
var first_line = (this.getScrollTop() / this.lineHeight)|0;
|
|
1990
2158
|
if( (cursor.line - 1) < first_line )
|
|
1991
|
-
this.
|
|
2159
|
+
this.setScrollTop( this.getScrollTop() - this.lineHeight );
|
|
1992
2160
|
});
|
|
1993
2161
|
}
|
|
1994
2162
|
|
|
@@ -2005,9 +2173,9 @@ class CodeEditor {
|
|
|
2005
2173
|
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
2006
2174
|
|
|
2007
2175
|
doAsync(() => {
|
|
2008
|
-
var last_line = ((this.code.
|
|
2176
|
+
var last_line = ((this.code.parentElement.offsetHeight - 32) / this.lineHeight)|0;
|
|
2009
2177
|
if( cursor.line >= last_line )
|
|
2010
|
-
this.
|
|
2178
|
+
this.setScrollTop( this.getScrollTop() + this.lineHeight );
|
|
2011
2179
|
});
|
|
2012
2180
|
}
|
|
2013
2181
|
|
|
@@ -2094,13 +2262,148 @@ class CodeEditor {
|
|
|
2094
2262
|
getScrollLeft() {
|
|
2095
2263
|
|
|
2096
2264
|
if(!this.code) return 0;
|
|
2097
|
-
return this.code.
|
|
2265
|
+
return this.code.customScroll.x;
|
|
2098
2266
|
}
|
|
2099
2267
|
|
|
2100
2268
|
getScrollTop() {
|
|
2101
2269
|
|
|
2102
2270
|
if(!this.code) return 0;
|
|
2103
|
-
return this.code.
|
|
2271
|
+
return this.code.customScroll.y;
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
setScrollLeft( value, keepScrollBar ) {
|
|
2275
|
+
|
|
2276
|
+
if(!this.code) return;
|
|
2277
|
+
|
|
2278
|
+
const realClientWidth = (this.code.clientWidth - this.code.customScroll.x);
|
|
2279
|
+
const maxWidth = Math.max( this.code.scrollWidth - realClientWidth, 0 );
|
|
2280
|
+
|
|
2281
|
+
value = LX.UTILS.clamp( value, 0, maxWidth );
|
|
2282
|
+
|
|
2283
|
+
this.code.style.marginLeft = (-value) + "px";
|
|
2284
|
+
|
|
2285
|
+
if( !keepScrollBar )
|
|
2286
|
+
{
|
|
2287
|
+
const scrollWidth = this.hScrollbarThumb.parentElement.offsetWidth;
|
|
2288
|
+
const scrollBarWidth = this.hScrollbarThumb.offsetWidth;
|
|
2289
|
+
this.setScrollBarValue( ( scrollWidth - scrollBarWidth ) * ( value / maxWidth ), 'horizontal' );
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
// Update cursor
|
|
2293
|
+
var cursor = this.cursors.children[0];
|
|
2294
|
+
cursor.style.left = "calc( " + (cursor._left - value) + "px + " + this.xPadding + ")";
|
|
2295
|
+
|
|
2296
|
+
// Update selection
|
|
2297
|
+
for( let s of this.selections.childNodes ) {
|
|
2298
|
+
s.style.left = "calc( " + (s._left - value) + "px + " + this.xPadding + ")";
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
this.code.customScroll.x = value;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
setScrollTop( value, keepScrollBar ) {
|
|
2305
|
+
|
|
2306
|
+
if(!this.code) return;
|
|
2307
|
+
|
|
2308
|
+
const realClientHeight = this.code.parentElement.offsetHeight - 36;
|
|
2309
|
+
const maxHeight = Math.max( this.code.scrollHeight - realClientHeight, 0 );
|
|
2310
|
+
|
|
2311
|
+
value = LX.UTILS.clamp( value, 0, maxHeight );
|
|
2312
|
+
|
|
2313
|
+
this.gutter.scrollTop = value;
|
|
2314
|
+
|
|
2315
|
+
this.code.style.marginTop = (-value) + "px";
|
|
2316
|
+
|
|
2317
|
+
if( !keepScrollBar )
|
|
2318
|
+
{
|
|
2319
|
+
const scrollHeight = this.scrollbarThumb.parentElement.offsetHeight;
|
|
2320
|
+
const scrollBarHeight = this.scrollbarThumb.offsetHeight;
|
|
2321
|
+
// this.setScrollBarValue( ( scrollHeight - scrollBarHeight ) * ( value / maxHeight ) )
|
|
2322
|
+
const firstLineInViewport = (this.getScrollTop() / this.lineHeight)|0;
|
|
2323
|
+
this.setScrollBarValue( ( scrollHeight - scrollBarHeight ) * ( firstLineInViewport / this.code.lines.length ) )
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
// Update cursor
|
|
2327
|
+
var cursor = this.cursors.children[0];
|
|
2328
|
+
cursor.style.top = (cursor._top - value) + "px";
|
|
2329
|
+
|
|
2330
|
+
// Update selection
|
|
2331
|
+
for( let s of this.selections.childNodes ) {
|
|
2332
|
+
s.style.top = (s._top - value) + "px";
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
this.code.customScroll.y = value;
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
resizeScrollBars() {
|
|
2339
|
+
|
|
2340
|
+
const numViewportLines = Math.floor( (this.code.parentElement.offsetHeight - 36) / this.lineHeight );
|
|
2341
|
+
|
|
2342
|
+
if( numViewportLines > this.code.lines.length )
|
|
2343
|
+
{
|
|
2344
|
+
this.scrollbar.classList.add( 'scrollbar-unused' );
|
|
2345
|
+
this.tabs.area.root.classList.remove( 'with-vscrollbar' );
|
|
2346
|
+
}
|
|
2347
|
+
else
|
|
2348
|
+
{
|
|
2349
|
+
this.scrollbar.classList.remove( 'scrollbar-unused' );
|
|
2350
|
+
this.tabs.area.root.classList.add( 'with-vscrollbar' );
|
|
2351
|
+
this.scrollbarThumb.size = (numViewportLines / this.code.lines.length);
|
|
2352
|
+
this.scrollbarThumb.style.height = (this.scrollbarThumb.size * 100.0) + "%";
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
const numViewportChars = Math.floor( this.code.clientWidth / this.charWidth );
|
|
2356
|
+
const line_lengths = this.code.lines.map( value => value.length );
|
|
2357
|
+
const maxLineLength = Math.max(...line_lengths);
|
|
2358
|
+
|
|
2359
|
+
if( numViewportChars > maxLineLength )
|
|
2360
|
+
{
|
|
2361
|
+
this.hScrollbar.classList.add( 'scrollbar-unused' );
|
|
2362
|
+
this.tabs.area.root.classList.remove( 'with-hscrollbar' );
|
|
2363
|
+
}
|
|
2364
|
+
else
|
|
2365
|
+
{
|
|
2366
|
+
this.hScrollbar.classList.remove( 'scrollbar-unused' );
|
|
2367
|
+
this.tabs.area.root.classList.add( 'with-hscrollbar' );
|
|
2368
|
+
this.hScrollbarThumb.size = (numViewportChars / maxLineLength);
|
|
2369
|
+
this.hScrollbarThumb.style.width = (this.hScrollbarThumb.size * 100.0) + "%";
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
setScrollBarValue( value, type = 'vertical' ) {
|
|
2374
|
+
|
|
2375
|
+
if( type == 'vertical' )
|
|
2376
|
+
{
|
|
2377
|
+
const scrollHeight = this.scrollbarThumb.parentElement.offsetHeight;
|
|
2378
|
+
const scrollBarHeight = this.scrollbarThumb.offsetHeight;
|
|
2379
|
+
|
|
2380
|
+
value = LX.UTILS.clamp( value, 0, ( scrollHeight - scrollBarHeight ) );
|
|
2381
|
+
|
|
2382
|
+
this.scrollbarThumb._top = value;
|
|
2383
|
+
this.scrollbarThumb.style.top = this.scrollbarThumb._top + "px";
|
|
2384
|
+
}
|
|
2385
|
+
else
|
|
2386
|
+
{
|
|
2387
|
+
const scrollWidth = this.hScrollbarThumb.parentElement.offsetWidth;
|
|
2388
|
+
const scrollBarWidth = this.hScrollbarThumb.offsetWidth;
|
|
2389
|
+
|
|
2390
|
+
value = LX.UTILS.clamp( value, 0, ( scrollWidth - scrollBarWidth ) );
|
|
2391
|
+
|
|
2392
|
+
this.hScrollbarThumb._left = value;
|
|
2393
|
+
this.hScrollbarThumb.style.left = this.hScrollbarThumb._left + "px";
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
applyHorizontalScrollFromScrollBar( value ) {
|
|
2398
|
+
|
|
2399
|
+
this.setScrollBarValue( value, 'horizontal');
|
|
2400
|
+
this.setScrollLeft( value / this.hScrollbarThumb.size, true );
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
applyVerticalScrollFromScrollBar( value ) {
|
|
2404
|
+
|
|
2405
|
+
this.setScrollBarValue( value );
|
|
2406
|
+
this.setScrollTop( value / this.scrollbarThumb.size, true );
|
|
2104
2407
|
}
|
|
2105
2408
|
|
|
2106
2409
|
getCharAtPos( cursor, offset = 0 ) {
|
|
@@ -2155,6 +2458,7 @@ class CodeEditor {
|
|
|
2155
2458
|
}
|
|
2156
2459
|
|
|
2157
2460
|
runScript( code ) {
|
|
2461
|
+
|
|
2158
2462
|
var script = document.createElement('script');
|
|
2159
2463
|
script.type = 'module';
|
|
2160
2464
|
script.innerHTML = code;
|
|
@@ -2211,15 +2515,15 @@ class CodeEditor {
|
|
|
2211
2515
|
|
|
2212
2516
|
// Add language special keys...
|
|
2213
2517
|
suggestions = suggestions.concat(
|
|
2214
|
-
this.builtin[ this.highlight ] ?? [],
|
|
2215
|
-
this.keywords[ this.highlight ] ?? [],
|
|
2216
|
-
this.statementsAndDeclarations[ this.highlight ] ?? [],
|
|
2217
|
-
this.types[ this.highlight ] ?? [],
|
|
2218
|
-
this.utils[ this.highlight ] ?? []
|
|
2518
|
+
Object.keys( this.builtin[ this.highlight ] ) ?? [],
|
|
2519
|
+
Object.keys( this.keywords[ this.highlight ] ) ?? [],
|
|
2520
|
+
Object.keys( this.statementsAndDeclarations[ this.highlight ] ) ?? [],
|
|
2521
|
+
Object.keys( this.types[ this.highlight ] ) ?? [],
|
|
2522
|
+
Object.keys( this.utils[ this.highlight ] ) ?? []
|
|
2219
2523
|
);
|
|
2220
2524
|
|
|
2221
2525
|
// Add words in current tab plus remove current word
|
|
2222
|
-
suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
|
|
2526
|
+
// suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
|
|
2223
2527
|
|
|
2224
2528
|
// Remove 1/2 char words and duplicates...
|
|
2225
2529
|
suggestions = suggestions.filter( (value, index) => value.length > 2 && suggestions.indexOf(value) === index );
|
|
@@ -2279,9 +2583,8 @@ class CodeEditor {
|
|
|
2279
2583
|
// Show box
|
|
2280
2584
|
this.autocomplete.classList.toggle('show', true);
|
|
2281
2585
|
this.autocomplete.classList.toggle('no-scrollbar', !(this.autocomplete.scrollHeight > this.autocomplete.offsetHeight));
|
|
2282
|
-
this.autocomplete.style.left = (cursor._left + 36) + "px";
|
|
2283
|
-
this.autocomplete.style.top = (cursor._top + 48) + "px";
|
|
2284
|
-
|
|
2586
|
+
this.autocomplete.style.left = (cursor._left + 36 - this.getScrollLeft()) + "px";
|
|
2587
|
+
this.autocomplete.style.top = (cursor._top + 48 - this.getScrollTop()) + "px";
|
|
2285
2588
|
|
|
2286
2589
|
this.isAutoCompleteActive = true;
|
|
2287
2590
|
}
|
package/build/lexgui.css
CHANGED
|
@@ -2709,7 +2709,7 @@ ul.lexassetscontent {
|
|
|
2709
2709
|
.lexcodeeditor {
|
|
2710
2710
|
outline: none;
|
|
2711
2711
|
overflow: hidden;
|
|
2712
|
-
width: calc(100%
|
|
2712
|
+
width: calc(100%);
|
|
2713
2713
|
-webkit-user-select: none; /* Safari 3.1+ */
|
|
2714
2714
|
-moz-user-select: none; /* Firefox 2+ */
|
|
2715
2715
|
-ms-user-select: none; /* IE 10+ */
|
|
@@ -2730,9 +2730,9 @@ ul.lexassetscontent {
|
|
|
2730
2730
|
|
|
2731
2731
|
.lexcodegutter {
|
|
2732
2732
|
width: 32px;
|
|
2733
|
-
height: calc(100% -
|
|
2733
|
+
height: calc(100% - 70px);
|
|
2734
2734
|
background-color: var(--global-branch-darker);
|
|
2735
|
-
margin-top:
|
|
2735
|
+
margin-top: 24px;
|
|
2736
2736
|
text-align: center;
|
|
2737
2737
|
overflow: hidden;
|
|
2738
2738
|
-webkit-user-select: none; /* Safari 3.1+ */
|
|
@@ -2753,19 +2753,29 @@ ul.lexassetscontent {
|
|
|
2753
2753
|
line-height: 22px;
|
|
2754
2754
|
}
|
|
2755
2755
|
|
|
2756
|
-
.lexcodeeditor .
|
|
2757
|
-
height: calc(100% - 30px);
|
|
2758
|
-
cursor: text;
|
|
2759
|
-
font-size: 16px;
|
|
2760
|
-
overflow: scroll;
|
|
2756
|
+
.lexcodeeditor .codetabsarea {
|
|
2761
2757
|
background-color: var(--global-branch-darker);
|
|
2762
|
-
padding-right: 6px;
|
|
2763
2758
|
}
|
|
2764
2759
|
|
|
2765
|
-
.lexcodeeditor .
|
|
2760
|
+
.lexcodeeditor .codetabsarea.with-vscrollbar {
|
|
2761
|
+
width: calc( 100% - 10px ) !important;
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
.lexcodeeditor .codetabsarea.with-hscrollbar {
|
|
2765
|
+
height: calc( 100% - 40px ) !important;
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
.lexcodeeditor .codetabsarea.dragging {
|
|
2766
2769
|
background-color: var(--global-color-secondary);
|
|
2767
2770
|
}
|
|
2768
2771
|
|
|
2772
|
+
.lexcodeeditor .code {
|
|
2773
|
+
cursor: text;
|
|
2774
|
+
font-size: 16px;
|
|
2775
|
+
overflow: hidden;
|
|
2776
|
+
padding-right: 6px;
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2769
2779
|
.codechar {
|
|
2770
2780
|
font-size: 16px;
|
|
2771
2781
|
font-family: 'Inconsolata', monospace;
|
|
@@ -2797,6 +2807,7 @@ ul.lexassetscontent {
|
|
|
2797
2807
|
-webkit-tap-highlight-color: transparent;
|
|
2798
2808
|
z-index: 0 !important;
|
|
2799
2809
|
pointer-events: none;
|
|
2810
|
+
height: 16px;
|
|
2800
2811
|
}
|
|
2801
2812
|
|
|
2802
2813
|
.lexcodeeditor span {
|
|
@@ -2852,6 +2863,56 @@ ul.lexassetscontent {
|
|
|
2852
2863
|
height: 18px;
|
|
2853
2864
|
}
|
|
2854
2865
|
|
|
2866
|
+
.lexcodescrollbar {
|
|
2867
|
+
position: absolute;
|
|
2868
|
+
background-color: var(--global-branch-darker);
|
|
2869
|
+
filter: brightness(0.9);
|
|
2870
|
+
box-sizing: border-box;
|
|
2871
|
+
margin: 0;
|
|
2872
|
+
padding: 0;
|
|
2873
|
+
margin-top: 26px;
|
|
2874
|
+
z-index: 1 !important;
|
|
2875
|
+
right: 0px;
|
|
2876
|
+
width: 10px;
|
|
2877
|
+
height: calc(100% - 62px);
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
.lexcodescrollbar.horizontal {
|
|
2881
|
+
position: absolute;
|
|
2882
|
+
background-color: var(--global-branch-darker);
|
|
2883
|
+
filter: brightness(0.9);
|
|
2884
|
+
box-sizing: border-box;
|
|
2885
|
+
margin: 0;
|
|
2886
|
+
padding: 0;
|
|
2887
|
+
z-index: 1 !important;
|
|
2888
|
+
bottom: 36px;
|
|
2889
|
+
right: 10px;
|
|
2890
|
+
width: calc( 100% - 40px );
|
|
2891
|
+
height: 10px;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
.lexcodescrollbar.scrollbar-unused {
|
|
2895
|
+
display: none;
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
.lexcodescrollbar div { /* thumb */
|
|
2899
|
+
background-color: #bbbbbb57 !important;
|
|
2900
|
+
box-sizing: border-box;
|
|
2901
|
+
margin: 0;
|
|
2902
|
+
padding: 0;
|
|
2903
|
+
border-right: none;
|
|
2904
|
+
position: absolute;
|
|
2905
|
+
z-index: 0 !important;
|
|
2906
|
+
top: 0px;
|
|
2907
|
+
width: 10px;
|
|
2908
|
+
height: 10px;
|
|
2909
|
+
transition: linear 0.1s background-color;
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
.lexcodescrollbar div:hover { /* thumb */
|
|
2913
|
+
background-color: #bbbbbb8c !important;
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2855
2916
|
.lexcodeeditor .lexcodeselection {
|
|
2856
2917
|
-webkit-text-size-adjust: 100%;
|
|
2857
2918
|
font-family: monospace;
|
|
@@ -3010,7 +3071,7 @@ ul.lexassetscontent {
|
|
|
3010
3071
|
.cm-std.batch { color: #cf6dcf; } /* statements & declarations */
|
|
3011
3072
|
.cm-bln.batch { color: inherit; } /* builtin */
|
|
3012
3073
|
.cm-dec.batch { color: #b1ce9b; } /* decimal */
|
|
3013
|
-
.cm-sym.batch { color: #
|
|
3074
|
+
.cm-sym.batch { color: #dfd85e; } /* symbol */
|
|
3014
3075
|
.cm-mtd.batch { color: inherit } /* method */
|
|
3015
3076
|
|
|
3016
3077
|
/* plain color */
|
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.16",
|
|
16
16
|
ready: false,
|
|
17
17
|
components: [], // specific pre-build components
|
|
18
18
|
signals: {} // events and triggers
|
|
@@ -5046,50 +5046,45 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
|
|
|
5046
5046
|
if(!name) {
|
|
5047
5047
|
throw("Set Widget Name!");
|
|
5048
5048
|
}
|
|
5049
|
-
|
|
5049
|
+
|
|
5050
5050
|
let widget = this.create_widget(name, Widget.FILE, options);
|
|
5051
5051
|
let element = widget.domEl;
|
|
5052
|
-
|
|
5052
|
+
|
|
5053
5053
|
let local = options.local ?? true;
|
|
5054
5054
|
let type = options.type ?? 'text';
|
|
5055
5055
|
let read = options.read ?? true;
|
|
5056
|
-
|
|
5056
|
+
|
|
5057
5057
|
// Create hidden input
|
|
5058
5058
|
let input = document.createElement('input');
|
|
5059
5059
|
input.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + " - 10%)";
|
|
5060
5060
|
input.type = 'file';
|
|
5061
5061
|
if(options.placeholder)
|
|
5062
5062
|
input.placeholder = options.placeholder;
|
|
5063
|
-
|
|
5063
|
+
|
|
5064
5064
|
input.addEventListener('change', function(e) {
|
|
5065
5065
|
const files = e.target.files;
|
|
5066
|
-
if(!files.length) return;
|
|
5067
|
-
if(read)
|
|
5066
|
+
if( !files.length ) return;
|
|
5067
|
+
if(read)
|
|
5068
|
+
{
|
|
5068
5069
|
const reader = new FileReader();
|
|
5069
|
-
|
|
5070
|
-
if(type === 'text')
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
}else if(type === 'url') {
|
|
5077
|
-
reader.readAsDataURL(files[0])
|
|
5078
|
-
}
|
|
5079
|
-
|
|
5080
|
-
reader.onload = (e) => { callback.call(this, e.target.result) } ;
|
|
5070
|
+
|
|
5071
|
+
if(type === 'text') reader.readAsText(files[0]);
|
|
5072
|
+
else if(type === 'buffer') reader.readAsArrayBuffer(files[0]);
|
|
5073
|
+
else if(type === 'bin') reader.readAsBinaryString(files[0]);
|
|
5074
|
+
else if(type === 'url') reader.readAsDataURL(files[0]);
|
|
5075
|
+
|
|
5076
|
+
reader.onload = (e) => { callback.call( this, e.target.result, files[0] ) } ;
|
|
5081
5077
|
}
|
|
5082
|
-
else
|
|
5083
|
-
callback(files[0]);
|
|
5078
|
+
else callback(files[0]);
|
|
5084
5079
|
|
|
5085
5080
|
});
|
|
5086
|
-
|
|
5081
|
+
|
|
5087
5082
|
element.appendChild(input);
|
|
5088
|
-
|
|
5083
|
+
|
|
5089
5084
|
this.queue( element );
|
|
5090
5085
|
|
|
5091
|
-
if(local)
|
|
5092
|
-
|
|
5086
|
+
if( local )
|
|
5087
|
+
{
|
|
5093
5088
|
this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-gear'></a>", () => {
|
|
5094
5089
|
|
|
5095
5090
|
new Dialog("Load Settings", p => {
|
|
@@ -5099,9 +5094,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
|
|
|
5099
5094
|
|
|
5100
5095
|
}, { className: "micro", skipInlineCount: true });
|
|
5101
5096
|
}
|
|
5102
|
-
|
|
5097
|
+
|
|
5103
5098
|
this.clearQueue();
|
|
5104
|
-
|
|
5099
|
+
|
|
5105
5100
|
return widget;
|
|
5106
5101
|
}
|
|
5107
5102
|
|
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.16",
|
|
12
12
|
ready: false,
|
|
13
13
|
components: [], // specific pre-build components
|
|
14
14
|
signals: {} // events and triggers
|
|
@@ -5059,24 +5059,19 @@ class Panel {
|
|
|
5059
5059
|
|
|
5060
5060
|
input.addEventListener('change', function(e) {
|
|
5061
5061
|
const files = e.target.files;
|
|
5062
|
-
if(!files.length) return;
|
|
5063
|
-
if(read)
|
|
5062
|
+
if( !files.length ) return;
|
|
5063
|
+
if(read)
|
|
5064
|
+
{
|
|
5064
5065
|
const reader = new FileReader();
|
|
5065
5066
|
|
|
5066
|
-
if(type === 'text')
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
}else if(type === 'bin') {
|
|
5071
|
-
reader.readAsBinaryString(files[0])
|
|
5072
|
-
}else if(type === 'url') {
|
|
5073
|
-
reader.readAsDataURL(files[0])
|
|
5074
|
-
}
|
|
5067
|
+
if(type === 'text') reader.readAsText(files[0]);
|
|
5068
|
+
else if(type === 'buffer') reader.readAsArrayBuffer(files[0]);
|
|
5069
|
+
else if(type === 'bin') reader.readAsBinaryString(files[0]);
|
|
5070
|
+
else if(type === 'url') reader.readAsDataURL(files[0]);
|
|
5075
5071
|
|
|
5076
|
-
reader.onload = (e) => { callback.call(this, e.target.result) } ;
|
|
5072
|
+
reader.onload = (e) => { callback.call( this, e.target.result, files[0] ) } ;
|
|
5077
5073
|
}
|
|
5078
|
-
else
|
|
5079
|
-
callback(files[0]);
|
|
5074
|
+
else callback(files[0]);
|
|
5080
5075
|
|
|
5081
5076
|
});
|
|
5082
5077
|
|
|
@@ -5084,8 +5079,8 @@ class Panel {
|
|
|
5084
5079
|
|
|
5085
5080
|
this.queue( element );
|
|
5086
5081
|
|
|
5087
|
-
if(local)
|
|
5088
|
-
|
|
5082
|
+
if( local )
|
|
5083
|
+
{
|
|
5089
5084
|
this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-gear'></a>", () => {
|
|
5090
5085
|
|
|
5091
5086
|
new Dialog("Load Settings", p => {
|
|
@@ -51,8 +51,16 @@
|
|
|
51
51
|
// editor.loadFile( "../data/style.css" );
|
|
52
52
|
// editor.loadFile( "../data/script.js" );
|
|
53
53
|
// editor.loadFile( "../data/engine.cpp" );
|
|
54
|
-
editor.loadFile( "../
|
|
54
|
+
editor.loadFile( "../demo.js" );
|
|
55
55
|
|
|
56
|
+
// var button = document.createElement('button');
|
|
57
|
+
// button.value = "CLICK ME";
|
|
58
|
+
// button.style.width = "100px";
|
|
59
|
+
// button.style.height = "100px";
|
|
60
|
+
// button.style.position = "absolute";
|
|
61
|
+
// button.onclick = editor.processLines.bind(editor);
|
|
62
|
+
// document.body.appendChild( button );
|
|
63
|
+
|
|
56
64
|
var ctx = canvas.getContext("2d");
|
|
57
65
|
ctx.fillStyle = "#b7a9b1";
|
|
58
66
|
ctx.font = "48px Monospace";
|