lexgui 0.1.12 → 0.1.14
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 +412 -224
- package/build/components/timeline.js +105 -73
- package/build/lexgui.css +38 -10
- package/build/lexgui.js +43 -33
- package/build/lexgui.module.js +43 -32
- package/demo.js +3 -0
- package/examples/code_editor.html +3 -1
- package/package.json +1 -1
|
@@ -102,11 +102,10 @@ class CodeEditor {
|
|
|
102
102
|
|
|
103
103
|
constructor( area, options = {} ) {
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
window.editor = this;
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
CodeEditor.__instances.push( this );
|
|
108
108
|
|
|
109
|
-
this.skip_info = options.skip_info;
|
|
110
109
|
this.base_area = area;
|
|
111
110
|
this.area = new LX.Area( { className: "lexcodeeditor", height: "auto", no_append: true } );
|
|
112
111
|
|
|
@@ -127,14 +126,21 @@ class CodeEditor {
|
|
|
127
126
|
this.root.tabIndex = -1;
|
|
128
127
|
area.attach( this.root );
|
|
129
128
|
|
|
130
|
-
this.
|
|
129
|
+
this.skipCodeInfo = options.skip_info ?? false;
|
|
130
|
+
this.disableEdition = options.disable_edition ?? false;
|
|
131
|
+
|
|
132
|
+
if( !this.disableEdition )
|
|
133
|
+
{
|
|
134
|
+
this.root.addEventListener( 'keydown', this.processKey.bind(this), true);
|
|
135
|
+
this.root.addEventListener( 'focus', this.processFocus.bind(this, true) );
|
|
136
|
+
this.root.addEventListener( 'focusout', this.processFocus.bind(this, false) );
|
|
137
|
+
}
|
|
138
|
+
|
|
131
139
|
this.root.addEventListener( 'mousedown', this.processMouse.bind(this) );
|
|
132
140
|
this.root.addEventListener( 'mouseup', this.processMouse.bind(this) );
|
|
133
141
|
this.root.addEventListener( 'mousemove', this.processMouse.bind(this) );
|
|
134
142
|
this.root.addEventListener( 'click', this.processMouse.bind(this) );
|
|
135
143
|
this.root.addEventListener( 'contextmenu', this.processMouse.bind(this) );
|
|
136
|
-
this.root.addEventListener( 'focus', this.processFocus.bind(this, true) );
|
|
137
|
-
this.root.addEventListener( 'focusout', this.processFocus.bind(this, false) );
|
|
138
144
|
|
|
139
145
|
// Cursors and selection
|
|
140
146
|
|
|
@@ -160,6 +166,7 @@ class CodeEditor {
|
|
|
160
166
|
cursor.style.top = "4px";
|
|
161
167
|
cursor.position = 0;
|
|
162
168
|
cursor.line = 0;
|
|
169
|
+
cursor.print = (function() { console.log( this.line, this.position ) }).bind(cursor);
|
|
163
170
|
this.cursors.appendChild(cursor);
|
|
164
171
|
}
|
|
165
172
|
|
|
@@ -192,11 +199,22 @@ class CodeEditor {
|
|
|
192
199
|
this.tabSpaces = 4;
|
|
193
200
|
this.maxUndoSteps = 16;
|
|
194
201
|
this.lineHeight = 20;
|
|
195
|
-
this.charWidth = 8
|
|
202
|
+
this.charWidth = 8; //this.measureChar();
|
|
196
203
|
this._lastTime = null;
|
|
197
204
|
|
|
205
|
+
this.pairKeys = {
|
|
206
|
+
"\"": "\"",
|
|
207
|
+
"'": "'",
|
|
208
|
+
"(": ")",
|
|
209
|
+
"{": "}",
|
|
210
|
+
"[": "]"
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Scan tokens..
|
|
214
|
+
setInterval( this.scanWordSuggestions.bind(this), 2000 );
|
|
215
|
+
|
|
198
216
|
this.languages = [
|
|
199
|
-
'Plain Text', 'JavaScript', 'CSS', 'GLSL', 'WGSL', 'JSON', 'XML'
|
|
217
|
+
'Plain Text', 'JavaScript', 'C++', 'CSS', 'GLSL', 'WGSL', 'JSON', 'XML', 'Python'
|
|
200
218
|
];
|
|
201
219
|
this.specialKeys = [
|
|
202
220
|
'Backspace', 'Enter', 'ArrowUp', 'ArrowDown',
|
|
@@ -204,43 +222,54 @@ class CodeEditor {
|
|
|
204
222
|
'End', 'Tab', 'Escape'
|
|
205
223
|
];
|
|
206
224
|
this.keywords = {
|
|
207
|
-
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger'
|
|
225
|
+
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
|
|
226
|
+
'arguments', 'extends', 'instanceof'],
|
|
227
|
+
'C++': ['int', 'float', 'bool', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'struct', 'typedef'],
|
|
208
228
|
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
209
229
|
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
|
|
210
230
|
'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
|
|
211
231
|
'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
|
|
212
232
|
'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
|
|
213
233
|
'texture_storage_2d_array', 'texture_storage_3d'],
|
|
214
|
-
'
|
|
234
|
+
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or']
|
|
235
|
+
};
|
|
236
|
+
this.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
|
|
237
|
+
'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
|
|
238
|
+
'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement', 'prompt',
|
|
239
|
+
'alert'],
|
|
240
|
+
'WGSL': ['textureSample'],
|
|
241
|
+
'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
|
|
242
|
+
'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
|
|
243
|
+
'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr',
|
|
244
|
+
'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
|
|
215
245
|
};
|
|
216
|
-
this.
|
|
217
|
-
'JavaScript': ['
|
|
218
|
-
'
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
246
|
+
this.types = {
|
|
247
|
+
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder'],
|
|
248
|
+
'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
|
|
249
|
+
'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
|
|
250
|
+
'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError',
|
|
251
|
+
'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError', 'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError' ]
|
|
222
252
|
};
|
|
223
253
|
this.builtin = {
|
|
224
|
-
'JavaScript': ['document', 'console', 'window', 'navigator', '
|
|
254
|
+
'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
|
|
225
255
|
'CSS': ['*', '!important'],
|
|
226
|
-
'
|
|
227
|
-
'WGSL': [],
|
|
228
|
-
'JSON': []
|
|
256
|
+
'C++': ['vector', 'list', 'map']
|
|
229
257
|
};
|
|
230
258
|
this.statementsAndDeclarations = {
|
|
231
|
-
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import',
|
|
232
|
-
|
|
259
|
+
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
|
|
260
|
+
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'glm'],
|
|
233
261
|
'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
|
|
234
262
|
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
|
|
235
|
-
'
|
|
236
|
-
'CSS': []
|
|
263
|
+
'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue', 'global', 'pass']
|
|
237
264
|
};
|
|
238
265
|
this.symbols = {
|
|
239
266
|
'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
267
|
+
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::'],
|
|
240
268
|
'JSON': ['[', ']', '{', '}', '(', ')'],
|
|
241
269
|
'GLSL': ['[', ']', '{', '}', '(', ')'],
|
|
242
270
|
'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
|
|
243
|
-
'CSS': ['{', '}', '(', ')', '*']
|
|
271
|
+
'CSS': ['{', '}', '(', ')', '*'],
|
|
272
|
+
'Python': ['<', '>', '[', ']', '(', ')', '=']
|
|
244
273
|
};
|
|
245
274
|
|
|
246
275
|
// Action keys
|
|
@@ -265,6 +294,8 @@ class CodeEditor {
|
|
|
265
294
|
this.code.lines[ln] = sliceChar( this.code.lines[ln], cursor.position - 1 );
|
|
266
295
|
this.cursorToLeft( letter );
|
|
267
296
|
this.processLine(ln);
|
|
297
|
+
if( this.useAutoComplete )
|
|
298
|
+
this.showAutoCompleteBox( 'foo', cursor );
|
|
268
299
|
}
|
|
269
300
|
else if(this.code.lines[ln - 1] != undefined) {
|
|
270
301
|
this.lineUp();
|
|
@@ -323,19 +354,20 @@ class CodeEditor {
|
|
|
323
354
|
|
|
324
355
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
325
356
|
if(idx > 0) this.cursorToString(cursor, prestring);
|
|
326
|
-
this._refreshCodeInfo(cursor.line
|
|
357
|
+
this._refreshCodeInfo(cursor.line, cursor.position);
|
|
327
358
|
this.code.scrollLeft = 0;
|
|
328
359
|
|
|
329
|
-
if(
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if(this.selection)
|
|
360
|
+
if( e.shiftKey && !e.cancelShift )
|
|
361
|
+
{
|
|
362
|
+
// Get last selection range
|
|
363
|
+
if(this.selection)
|
|
334
364
|
last_pos += this.selection.chars;
|
|
335
365
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
366
|
+
this.startSelection(cursor);
|
|
367
|
+
var string = this.code.lines[ln].substring(idx, last_pos);
|
|
368
|
+
this.selection.selectInline(idx, cursor.line, this.measureString(string));
|
|
369
|
+
} else
|
|
370
|
+
this.endSelection();
|
|
339
371
|
});
|
|
340
372
|
|
|
341
373
|
this.action('End', false, ( ln, cursor, e ) => {
|
|
@@ -346,7 +378,8 @@ class CodeEditor {
|
|
|
346
378
|
if(!this.selection)
|
|
347
379
|
this.startSelection(cursor);
|
|
348
380
|
this.selection.selectInline(cursor.position, cursor.line, this.measureString(string));
|
|
349
|
-
}
|
|
381
|
+
} else
|
|
382
|
+
this.endSelection();
|
|
350
383
|
|
|
351
384
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
352
385
|
this.cursorToString( cursor, this.code.lines[ln] );
|
|
@@ -403,11 +436,18 @@ class CodeEditor {
|
|
|
403
436
|
if( e.shiftKey ) {
|
|
404
437
|
if(!this.selection)
|
|
405
438
|
this.startSelection(cursor);
|
|
406
|
-
|
|
407
|
-
this.selection.toY = this.selection.toY > 0 ? this.selection.toY - 1 : 0;
|
|
408
|
-
this.processSelection(null, true);
|
|
409
|
-
this.cursorToPosition(cursor, this.selection.fromX);
|
|
439
|
+
|
|
440
|
+
this.selection.toY = (this.selection.toY > 0) ? (this.selection.toY - 1) : 0;
|
|
410
441
|
this.cursorToLine(cursor, this.selection.toY);
|
|
442
|
+
|
|
443
|
+
var letter = this.getCharAtPos( cursor );
|
|
444
|
+
if(!letter) {
|
|
445
|
+
this.selection.toX = (this.code.lines[cursor.line].length - 1);
|
|
446
|
+
this.cursorToPosition(cursor, this.selection.toX);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
this.processSelection(null, true);
|
|
450
|
+
|
|
411
451
|
} else {
|
|
412
452
|
this.endSelection();
|
|
413
453
|
this.lineUp();
|
|
@@ -431,11 +471,17 @@ class CodeEditor {
|
|
|
431
471
|
if( e.shiftKey ) {
|
|
432
472
|
if(!this.selection)
|
|
433
473
|
this.startSelection(cursor);
|
|
434
|
-
|
|
474
|
+
|
|
435
475
|
this.selection.toY = this.selection.toY < this.code.lines.length - 1 ? this.selection.toY + 1 : this.code.lines.length - 1;
|
|
436
|
-
this.processSelection(null, true);
|
|
437
|
-
this.cursorToPosition(cursor, this.selection.toX);
|
|
438
476
|
this.cursorToLine(cursor, this.selection.toY);
|
|
477
|
+
|
|
478
|
+
var letter = this.getCharAtPos( cursor );
|
|
479
|
+
if(!letter) {
|
|
480
|
+
this.selection.toX = Math.max(this.code.lines[cursor.line].length - 1, 0);
|
|
481
|
+
this.cursorToPosition(cursor, this.selection.toX);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
this.processSelection(null, true);
|
|
439
485
|
} else {
|
|
440
486
|
|
|
441
487
|
if( this.code.lines[ ln + 1 ] == undefined )
|
|
@@ -487,7 +533,11 @@ class CodeEditor {
|
|
|
487
533
|
this.processSelection(null, true);
|
|
488
534
|
}
|
|
489
535
|
else {
|
|
490
|
-
if(!this.selection)
|
|
536
|
+
if(!this.selection) {
|
|
537
|
+
this.cursorToLeft( letter, cursor );
|
|
538
|
+
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
539
|
+
this.showAutoCompleteBox( 'foo', cursor );
|
|
540
|
+
}
|
|
491
541
|
else {
|
|
492
542
|
this.selection.invertIfNecessary();
|
|
493
543
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
|
|
@@ -546,7 +596,11 @@ class CodeEditor {
|
|
|
546
596
|
this.cursorToRight( letter, cursor );
|
|
547
597
|
this.processSelection(null, keep_range);
|
|
548
598
|
}else{
|
|
549
|
-
if(!this.selection)
|
|
599
|
+
if(!this.selection) {
|
|
600
|
+
this.cursorToRight( letter, cursor );
|
|
601
|
+
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
602
|
+
this.showAutoCompleteBox( 'foo', cursor );
|
|
603
|
+
}
|
|
550
604
|
else
|
|
551
605
|
{
|
|
552
606
|
this.selection.invertIfNecessary();
|
|
@@ -562,6 +616,7 @@ class CodeEditor {
|
|
|
562
616
|
this.lineDown( cursor );
|
|
563
617
|
e.cancelShift = true;
|
|
564
618
|
this.actions['Home'].callback(cursor.line, cursor, e);
|
|
619
|
+
this.hideAutoCompleteBox();
|
|
565
620
|
|
|
566
621
|
if( e.shiftKey ) {
|
|
567
622
|
if(!this.selection) this.startSelection(cursor);
|
|
@@ -728,11 +783,14 @@ class CodeEditor {
|
|
|
728
783
|
switch(ext.toLowerCase())
|
|
729
784
|
{
|
|
730
785
|
case 'js': return this._changeLanguage('JavaScript');
|
|
786
|
+
case 'cpp': return this._changeLanguage('C++');
|
|
787
|
+
case 'h': return this._changeLanguage('C++');
|
|
731
788
|
case 'glsl': return this._changeLanguage('GLSL');
|
|
732
789
|
case 'css': return this._changeLanguage('CSS');
|
|
733
790
|
case 'json': return this._changeLanguage('JSON');
|
|
734
791
|
case 'xml': return this._changeLanguage('XML');
|
|
735
792
|
case 'wgsl': return this._changeLanguage('WGSL');
|
|
793
|
+
case 'py': return this._changeLanguage('Python');
|
|
736
794
|
case 'txt':
|
|
737
795
|
default:
|
|
738
796
|
this._changeLanguage('Plain Text');
|
|
@@ -741,20 +799,20 @@ class CodeEditor {
|
|
|
741
799
|
|
|
742
800
|
_createPanelInfo() {
|
|
743
801
|
|
|
744
|
-
if( !this.
|
|
802
|
+
if( !this.skipCodeInfo )
|
|
745
803
|
{
|
|
746
804
|
let panel = new LX.Panel({ className: "lexcodetabinfo", width: "calc(100%)", height: "auto" });
|
|
747
805
|
panel.ln = 0;
|
|
748
806
|
panel.col = 0;
|
|
749
807
|
|
|
750
808
|
this._refreshCodeInfo = (ln = panel.ln, col = panel.col) => {
|
|
751
|
-
panel.ln = ln;
|
|
752
|
-
panel.col = col;
|
|
809
|
+
panel.ln = ln + 1;
|
|
810
|
+
panel.col = col + 1;
|
|
753
811
|
panel.clear();
|
|
754
812
|
panel.sameLine();
|
|
755
813
|
panel.addLabel(this.code.title, { float: 'right' });
|
|
756
|
-
panel.addLabel("Ln " + ln, { width: "64px" });
|
|
757
|
-
panel.addLabel("Col " + col, { width: "64px" });
|
|
814
|
+
panel.addLabel("Ln " + panel.ln, { width: "64px" });
|
|
815
|
+
panel.addLabel("Col " + panel.col, { width: "64px" });
|
|
758
816
|
panel.addButton("<b>{ }</b>", this.highlight, (value, event) => {
|
|
759
817
|
LX.addContextMenu( "Language", event, m => {
|
|
760
818
|
for( const lang of this.languages )
|
|
@@ -858,7 +916,7 @@ class CodeEditor {
|
|
|
858
916
|
this.restoreCursor(cursor, this.code.cursorState);
|
|
859
917
|
this.endSelection();
|
|
860
918
|
this._changeLanguageFromExtension( LX.getExtension(tabname) );
|
|
861
|
-
this._refreshCodeInfo(cursor.line
|
|
919
|
+
this._refreshCodeInfo(cursor.line, cursor.position);
|
|
862
920
|
|
|
863
921
|
// Restore scroll
|
|
864
922
|
this.gutter.scrollTop = this.code.scrollTop;
|
|
@@ -918,10 +976,26 @@ class CodeEditor {
|
|
|
918
976
|
{
|
|
919
977
|
if( mouse_pos[0] > this.code.scrollWidth || mouse_pos[1] > this.code.scrollHeight )
|
|
920
978
|
return; // Scrollbar click
|
|
979
|
+
|
|
980
|
+
// Left click only...
|
|
981
|
+
if( e.button === 2 )
|
|
982
|
+
{
|
|
983
|
+
this.processClick(e);
|
|
984
|
+
|
|
985
|
+
this.canOpenContextMenu = !this.selection;
|
|
986
|
+
|
|
987
|
+
if( this.selection )
|
|
988
|
+
{
|
|
989
|
+
this.canOpenContextMenu |= (cursor.line >= this.selection.fromY && cursor.line <= this.selection.toY
|
|
990
|
+
&& cursor.position >= this.selection.fromX && cursor.position <= this.selection.toX);
|
|
991
|
+
if( this.canOpenContextMenu )
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
921
996
|
this.lastMouseDown = LX.getTime();
|
|
922
997
|
this.state.selectingText = true;
|
|
923
998
|
this.endSelection();
|
|
924
|
-
return;
|
|
925
999
|
}
|
|
926
1000
|
|
|
927
1001
|
else if( e.type == 'mouseup' )
|
|
@@ -964,15 +1038,29 @@ class CodeEditor {
|
|
|
964
1038
|
}
|
|
965
1039
|
}
|
|
966
1040
|
|
|
967
|
-
else if ( e.type == 'contextmenu' )
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1041
|
+
else if ( e.type == 'contextmenu' )
|
|
1042
|
+
{
|
|
1043
|
+
e.preventDefault();
|
|
1044
|
+
|
|
1045
|
+
if( !this.canOpenContextMenu )
|
|
1046
|
+
return;
|
|
1047
|
+
|
|
1048
|
+
LX.addContextMenu( null, e, m => {
|
|
1049
|
+
m.add( "Copy", () => { this._copyContent(); } );
|
|
1050
|
+
if( !this.disableEdition )
|
|
1051
|
+
{
|
|
1052
|
+
m.add( "Cut", () => { this._cutContent(); } );
|
|
1053
|
+
m.add( "Paste", () => { this._pasteContent(); } );
|
|
1054
|
+
m.add( "" );
|
|
1055
|
+
m.add( "Format/JSON", () => {
|
|
1056
|
+
let json = this.toJSONFormat(this.getText());
|
|
1057
|
+
this.code.lines = json.split("\n");
|
|
1058
|
+
this.processLines();
|
|
1059
|
+
} );
|
|
1060
|
+
}
|
|
975
1061
|
});
|
|
1062
|
+
|
|
1063
|
+
this.canOpenContextMenu = false;
|
|
976
1064
|
}
|
|
977
1065
|
}
|
|
978
1066
|
|
|
@@ -997,7 +1085,7 @@ class CodeEditor {
|
|
|
997
1085
|
this.hideAutoCompleteBox();
|
|
998
1086
|
|
|
999
1087
|
if(!skip_refresh)
|
|
1000
|
-
this._refreshCodeInfo( ln
|
|
1088
|
+
this._refreshCodeInfo( ln, cursor.position );
|
|
1001
1089
|
}
|
|
1002
1090
|
|
|
1003
1091
|
processSelection( e, keep_range ) {
|
|
@@ -1143,27 +1231,7 @@ class CodeEditor {
|
|
|
1143
1231
|
this.cursorToLine(cursor, this.selection.toY);
|
|
1144
1232
|
break;
|
|
1145
1233
|
case 'c': // copy
|
|
1146
|
-
|
|
1147
|
-
if( !this.selection ) {
|
|
1148
|
-
text_to_copy = "\n" + this.code.lines[cursor.line];
|
|
1149
|
-
}
|
|
1150
|
-
else {
|
|
1151
|
-
const separator = "_NEWLINE_";
|
|
1152
|
-
let code = this.code.lines.join(separator);
|
|
1153
|
-
|
|
1154
|
-
// Get linear start index
|
|
1155
|
-
let index = 0;
|
|
1156
|
-
|
|
1157
|
-
for(let i = 0; i <= this.selection.fromY; i++)
|
|
1158
|
-
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
|
|
1159
|
-
|
|
1160
|
-
index += this.selection.fromY * separator.length;
|
|
1161
|
-
const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
|
|
1162
|
-
const text = code.substr(index, num_chars);
|
|
1163
|
-
const lines = text.split(separator);
|
|
1164
|
-
text_to_copy = lines.join('\n');
|
|
1165
|
-
}
|
|
1166
|
-
navigator.clipboard.writeText(text_to_copy).then(() => console.log("Successfully copied"), (err) => console.error("Error"));
|
|
1234
|
+
this._copyContent();
|
|
1167
1235
|
return;
|
|
1168
1236
|
case 'd': // duplicate line
|
|
1169
1237
|
e.preventDefault();
|
|
@@ -1176,13 +1244,10 @@ class CodeEditor {
|
|
|
1176
1244
|
this.onsave( this.getText() );
|
|
1177
1245
|
return;
|
|
1178
1246
|
case 'v': // paste
|
|
1179
|
-
|
|
1180
|
-
this.appendText(text);
|
|
1247
|
+
this._pasteContent();
|
|
1181
1248
|
return;
|
|
1182
1249
|
case 'x': // cut line
|
|
1183
|
-
|
|
1184
|
-
this.processLines(lidx);
|
|
1185
|
-
navigator.clipboard.writeText(to_copy + '\n');
|
|
1250
|
+
this._cutContent();
|
|
1186
1251
|
return;
|
|
1187
1252
|
case 'z': // undo
|
|
1188
1253
|
if(!this.code.undoSteps.length)
|
|
@@ -1272,27 +1337,43 @@ class CodeEditor {
|
|
|
1272
1337
|
if( this.selection )
|
|
1273
1338
|
{
|
|
1274
1339
|
this.actions['Backspace'].callback(lidx, cursor, e);
|
|
1340
|
+
lidx = cursor.line; // Update this, since it's from the old code
|
|
1275
1341
|
}
|
|
1276
1342
|
|
|
1277
|
-
// Append key
|
|
1343
|
+
// Append key
|
|
1278
1344
|
|
|
1279
|
-
this.
|
|
1280
|
-
|
|
1281
|
-
key,
|
|
1282
|
-
this.code.lines[lidx].slice(cursor.position)
|
|
1283
|
-
].join('');
|
|
1345
|
+
const isPairKey = (Object.values( this.pairKeys ).indexOf( key ) > -1) && !this.wasKeyPaired;
|
|
1346
|
+
const sameKeyNext = isPairKey && (this.code.lines[lidx][cursor.position] === key);
|
|
1284
1347
|
|
|
1285
|
-
|
|
1348
|
+
if( !sameKeyNext )
|
|
1349
|
+
{
|
|
1350
|
+
this.code.lines[lidx] = [
|
|
1351
|
+
this.code.lines[lidx].slice(0, cursor.position),
|
|
1352
|
+
key,
|
|
1353
|
+
this.code.lines[lidx].slice(cursor.position)
|
|
1354
|
+
].join('');
|
|
1355
|
+
}
|
|
1286
1356
|
|
|
1287
|
-
|
|
1357
|
+
this.cursorToRight( key );
|
|
1288
1358
|
|
|
1289
|
-
|
|
1359
|
+
// Some custom cases for auto key pair (), {}, "", '', ...
|
|
1360
|
+
|
|
1361
|
+
const keyMustPair = (this.pairKeys[ key ] !== undefined);
|
|
1362
|
+
if( keyMustPair && !this.wasKeyPaired )
|
|
1290
1363
|
{
|
|
1291
|
-
|
|
1364
|
+
// Make sure to detect later that the key is paired automatically to avoid loops...
|
|
1365
|
+
this.wasKeyPaired = true;
|
|
1366
|
+
|
|
1367
|
+
if(sameKeyNext) return;
|
|
1368
|
+
|
|
1369
|
+
this.root.dispatchEvent(new KeyboardEvent('keydown', { 'key': this.pairKeys[ key ] }));
|
|
1292
1370
|
this.cursorToLeft( key, cursor );
|
|
1293
|
-
return;
|
|
1371
|
+
return;
|
|
1294
1372
|
}
|
|
1295
1373
|
|
|
1374
|
+
// Once here we can pair keys again
|
|
1375
|
+
delete this.wasKeyPaired;
|
|
1376
|
+
|
|
1296
1377
|
// Update only the current line, since it's only an appended key
|
|
1297
1378
|
this.processLine( lidx );
|
|
1298
1379
|
|
|
@@ -1302,6 +1383,73 @@ class CodeEditor {
|
|
|
1302
1383
|
this.showAutoCompleteBox( key, cursor );
|
|
1303
1384
|
}
|
|
1304
1385
|
|
|
1386
|
+
async _pasteContent() {
|
|
1387
|
+
let text = await navigator.clipboard.readText();
|
|
1388
|
+
this.appendText(text);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
async _copyContent() {
|
|
1392
|
+
|
|
1393
|
+
let cursor = this.cursors.children[0];
|
|
1394
|
+
let text_to_copy = "";
|
|
1395
|
+
|
|
1396
|
+
if( !this.selection ) {
|
|
1397
|
+
text_to_copy = "\n" + this.code.lines[cursor.line];
|
|
1398
|
+
}
|
|
1399
|
+
else {
|
|
1400
|
+
const separator = "_NEWLINE_";
|
|
1401
|
+
let code = this.code.lines.join(separator);
|
|
1402
|
+
|
|
1403
|
+
// Get linear start index
|
|
1404
|
+
let index = 0;
|
|
1405
|
+
|
|
1406
|
+
for(let i = 0; i <= this.selection.fromY; i++)
|
|
1407
|
+
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
|
|
1408
|
+
|
|
1409
|
+
index += this.selection.fromY * separator.length;
|
|
1410
|
+
const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
|
|
1411
|
+
const text = code.substr(index, num_chars);
|
|
1412
|
+
const lines = text.split(separator);
|
|
1413
|
+
text_to_copy = lines.join('\n');
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
navigator.clipboard.writeText(text_to_copy).then(() => console.log("Successfully copied"), (err) => console.error("Error"));
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
async _cutContent() {
|
|
1420
|
+
|
|
1421
|
+
let cursor = this.cursors.children[0];
|
|
1422
|
+
let lidx = cursor.line;
|
|
1423
|
+
let text_to_cut = "";
|
|
1424
|
+
|
|
1425
|
+
if( !this.selection ) {
|
|
1426
|
+
text_to_cut = "\n" + this.code.lines[cursor.line];
|
|
1427
|
+
this.code.lines.splice(lidx, 1);
|
|
1428
|
+
this.processLines(lidx);
|
|
1429
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
1430
|
+
}
|
|
1431
|
+
else {
|
|
1432
|
+
const separator = "_NEWLINE_";
|
|
1433
|
+
let code = this.code.lines.join(separator);
|
|
1434
|
+
|
|
1435
|
+
// Get linear start index
|
|
1436
|
+
let index = 0;
|
|
1437
|
+
|
|
1438
|
+
for(let i = 0; i <= this.selection.fromY; i++)
|
|
1439
|
+
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
|
|
1440
|
+
|
|
1441
|
+
index += this.selection.fromY * separator.length;
|
|
1442
|
+
const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
|
|
1443
|
+
const text = code.substr(index, num_chars);
|
|
1444
|
+
const lines = text.split(separator);
|
|
1445
|
+
text_to_cut = lines.join('\n');
|
|
1446
|
+
|
|
1447
|
+
this.deleteSelection( cursor );
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
navigator.clipboard.writeText(text_to_cut).then(() => console.log("Successfully cut"), (err) => console.error("Error"));
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1305
1453
|
action( key, deleteSelection, fn ) {
|
|
1306
1454
|
|
|
1307
1455
|
this.actions[ key ] = {
|
|
@@ -1310,58 +1458,103 @@ class CodeEditor {
|
|
|
1310
1458
|
};
|
|
1311
1459
|
}
|
|
1312
1460
|
|
|
1313
|
-
|
|
1461
|
+
scanWordSuggestions() {
|
|
1314
1462
|
|
|
1315
|
-
|
|
1316
|
-
{
|
|
1317
|
-
this.gutter.innerHTML = "";
|
|
1318
|
-
this.code.innerHTML = "";
|
|
1319
|
-
this.code.tokens = {};
|
|
1320
|
-
}
|
|
1463
|
+
this.code.tokens = {};
|
|
1321
1464
|
|
|
1322
|
-
for( let i =
|
|
1465
|
+
for( let i = 0; i < this.code.lines.length; ++i )
|
|
1323
1466
|
{
|
|
1324
|
-
this.
|
|
1467
|
+
const linestring = this.code.lines[ i ];
|
|
1468
|
+
const tokens = this._getTokensFromString( linestring, true );
|
|
1469
|
+
tokens.forEach( t => this.code.tokens[ t ] = 1 );
|
|
1325
1470
|
}
|
|
1471
|
+
}
|
|
1326
1472
|
|
|
1327
|
-
|
|
1328
|
-
|
|
1473
|
+
processLines( from ) {
|
|
1474
|
+
|
|
1475
|
+
// const start = performance.now();
|
|
1476
|
+
|
|
1477
|
+
this.gutter.innerHTML = "";
|
|
1478
|
+
this.code.innerHTML = "";
|
|
1479
|
+
|
|
1480
|
+
for( let i = 0; i < this.code.lines.length; ++i )
|
|
1329
1481
|
{
|
|
1330
|
-
|
|
1331
|
-
this.gutter.lastChild.remove();
|
|
1332
|
-
while( this.code.lines.length != this.code.children.length )
|
|
1333
|
-
this.code.lastChild.remove();
|
|
1482
|
+
this.processLine( i, true );
|
|
1334
1483
|
}
|
|
1335
1484
|
|
|
1485
|
+
// console.log( performance.now() - start );
|
|
1336
1486
|
}
|
|
1337
1487
|
|
|
1338
|
-
processLine( linenum ) {
|
|
1488
|
+
processLine( linenum, force ) {
|
|
1489
|
+
|
|
1490
|
+
// if(!force)
|
|
1491
|
+
// {
|
|
1492
|
+
// this.processLines();
|
|
1493
|
+
// return;
|
|
1494
|
+
// }
|
|
1339
1495
|
|
|
1340
1496
|
delete this._building_string; // multi-line strings not supported by now
|
|
1341
1497
|
|
|
1342
1498
|
// It's allowed to process only 1 line to optimize
|
|
1343
1499
|
let linestring = this.code.lines[ linenum ];
|
|
1344
|
-
|
|
1345
|
-
var pre =
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1500
|
+
|
|
1501
|
+
var pre = document.createElement('pre');
|
|
1502
|
+
pre.dataset['linenum'] = linenum;
|
|
1503
|
+
|
|
1504
|
+
// Line gutter
|
|
1505
|
+
var linenumspan = document.createElement('span');
|
|
1506
|
+
linenumspan.innerHTML = (linenum + 1);
|
|
1507
|
+
|
|
1508
|
+
if( force )
|
|
1352
1509
|
{
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
}
|
|
1357
|
-
else
|
|
1510
|
+
this.gutter.appendChild(linenumspan);
|
|
1511
|
+
this.code.appendChild( pre );
|
|
1512
|
+
} else
|
|
1358
1513
|
{
|
|
1359
|
-
|
|
1514
|
+
this.gutter.childNodes[ linenum ].remove();
|
|
1515
|
+
this.code.childNodes[ linenum ].remove();
|
|
1516
|
+
this.gutter.insertChildAtIndex( linenumspan, linenum );
|
|
1517
|
+
this.code.insertChildAtIndex( pre, linenum );
|
|
1360
1518
|
}
|
|
1361
1519
|
|
|
1362
1520
|
var linespan = document.createElement('span');
|
|
1363
1521
|
pre.appendChild(linespan);
|
|
1364
1522
|
|
|
1523
|
+
const to_process = this._getTokensFromString( linestring );
|
|
1524
|
+
|
|
1525
|
+
let line_inner_html = "";
|
|
1526
|
+
|
|
1527
|
+
// Process all tokens
|
|
1528
|
+
for( var i = 0; i < to_process.length; ++i )
|
|
1529
|
+
{
|
|
1530
|
+
let it = i - 1;
|
|
1531
|
+
let prev = to_process[it];
|
|
1532
|
+
while( prev == ' ' ) {
|
|
1533
|
+
it--;
|
|
1534
|
+
prev = to_process[it];
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
it = i + 1;
|
|
1538
|
+
let next = to_process[it];
|
|
1539
|
+
while( next == ' ' || next == '"') {
|
|
1540
|
+
it++;
|
|
1541
|
+
next = to_process[it];
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
let token = to_process[i];
|
|
1545
|
+
if( token.substr(0, 2) == '/*' )
|
|
1546
|
+
this._building_block_comment = true;
|
|
1547
|
+
if( token.substr(token.length - 2) == '*/' )
|
|
1548
|
+
delete this._building_block_comment;
|
|
1549
|
+
|
|
1550
|
+
line_inner_html += this.processToken(token, prev, next);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
linespan.innerHTML = line_inner_html;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
_getTokensFromString( linestring, skipNonWords ) {
|
|
1557
|
+
|
|
1365
1558
|
// Check if line comment
|
|
1366
1559
|
const is_comment = linestring.split('//');
|
|
1367
1560
|
linestring = ( is_comment.length > 1 ) ? is_comment[0] : linestring;
|
|
@@ -1371,7 +1564,15 @@ class CodeEditor {
|
|
|
1371
1564
|
|
|
1372
1565
|
for( let t of tokens )
|
|
1373
1566
|
{
|
|
1374
|
-
|
|
1567
|
+
if( !t.length || (skipNonWords && ( t.includes('"') || t.length < 3 )) )
|
|
1568
|
+
continue;
|
|
1569
|
+
|
|
1570
|
+
if( t == ' ' ) {
|
|
1571
|
+
to_process.push( t );
|
|
1572
|
+
continue;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
let iter = t.matchAll(/(::|[\[\](){}<>.,;:"'])/g);
|
|
1375
1576
|
let subtokens = iter.next();
|
|
1376
1577
|
if( subtokens.value )
|
|
1377
1578
|
{
|
|
@@ -1379,62 +1580,35 @@ class CodeEditor {
|
|
|
1379
1580
|
while( subtokens.value != undefined )
|
|
1380
1581
|
{
|
|
1381
1582
|
const _pt = t.substring(idx, subtokens.value.index);
|
|
1382
|
-
to_process.push( _pt );
|
|
1583
|
+
if( _pt.length ) to_process.push( _pt );
|
|
1383
1584
|
to_process.push( subtokens.value[0] );
|
|
1384
|
-
idx = subtokens.value.index +
|
|
1585
|
+
idx = subtokens.value.index + subtokens.value[0].length;
|
|
1385
1586
|
subtokens = iter.next();
|
|
1386
1587
|
if(!subtokens.value) {
|
|
1387
1588
|
const _at = t.substring(idx);
|
|
1388
|
-
to_process.push( _at );
|
|
1589
|
+
if( _at.length ) to_process.push( _at );
|
|
1389
1590
|
}
|
|
1390
1591
|
}
|
|
1391
1592
|
}
|
|
1392
|
-
else
|
|
1393
|
-
to_process.push( t );
|
|
1593
|
+
else to_process.push( t );
|
|
1394
1594
|
}
|
|
1395
1595
|
|
|
1396
|
-
if( is_comment.length > 1 )
|
|
1596
|
+
if( is_comment.length > 1 && !skipNonWords )
|
|
1397
1597
|
to_process.push( "//" + is_comment[1] );
|
|
1398
|
-
|
|
1399
|
-
// Process all tokens
|
|
1400
|
-
for( var i = 0; i < to_process.length; ++i )
|
|
1401
|
-
{
|
|
1402
|
-
let it = i - 1;
|
|
1403
|
-
let prev = to_process[it];
|
|
1404
|
-
while( prev == '' || prev == ' ' ) {
|
|
1405
|
-
it--;
|
|
1406
|
-
prev = to_process[it];
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
it = i + 1;
|
|
1410
|
-
let next = to_process[it];
|
|
1411
|
-
while( next == '' || next == ' ' || next == '"') {
|
|
1412
|
-
it++;
|
|
1413
|
-
next = to_process[it];
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
let token = to_process[i];
|
|
1417
|
-
if( token.substr(0, 2) == '/*' )
|
|
1418
|
-
this._building_block_comment = true;
|
|
1419
|
-
if( token.substr(token.length - 2) == '*/' )
|
|
1420
|
-
delete this._building_block_comment;
|
|
1421
1598
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1599
|
+
return to_process;
|
|
1600
|
+
}
|
|
1424
1601
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
var linenumspan = document.createElement('span');
|
|
1429
|
-
linenumspan.innerHTML = (linenum + 1);
|
|
1430
|
-
this.gutter.appendChild(linenumspan);
|
|
1431
|
-
}
|
|
1602
|
+
_mustHightlightWord( token, kindArray ) {
|
|
1603
|
+
|
|
1604
|
+
return kindArray[this.highlight] && kindArray[this.highlight].indexOf(token) > -1;
|
|
1432
1605
|
}
|
|
1433
1606
|
|
|
1434
|
-
processToken(token,
|
|
1607
|
+
processToken(token, prev, next) {
|
|
1435
1608
|
|
|
1436
1609
|
let sString = false;
|
|
1437
|
-
let highlight = this.highlight.replace(/\s/g, '').toLowerCase();
|
|
1610
|
+
let highlight = this.highlight.replace(/\s/g, '').replaceAll("+", "p").toLowerCase();
|
|
1611
|
+
let inner_html = "", token_classname = "";
|
|
1438
1612
|
|
|
1439
1613
|
if(token == '"' || token == "'")
|
|
1440
1614
|
{
|
|
@@ -1444,75 +1618,68 @@ class CodeEditor {
|
|
|
1444
1618
|
|
|
1445
1619
|
if(token == ' ')
|
|
1446
1620
|
{
|
|
1447
|
-
|
|
1621
|
+
inner_html += token;
|
|
1448
1622
|
}
|
|
1449
1623
|
else
|
|
1450
1624
|
{
|
|
1451
|
-
var span = document.createElement('span');
|
|
1452
|
-
span.innerHTML = token;
|
|
1453
|
-
|
|
1454
1625
|
if( this._building_block_comment )
|
|
1455
|
-
|
|
1626
|
+
token_classname += "cm-com";
|
|
1456
1627
|
|
|
1457
1628
|
else if( this._building_string )
|
|
1458
|
-
|
|
1629
|
+
token_classname += "cm-str";
|
|
1459
1630
|
|
|
1460
|
-
else if( this.
|
|
1461
|
-
|
|
1631
|
+
else if( this._mustHightlightWord( token, this.keywords ) )
|
|
1632
|
+
token_classname += "cm-kwd";
|
|
1462
1633
|
|
|
1463
|
-
else if( this.
|
|
1464
|
-
|
|
1634
|
+
else if( this._mustHightlightWord( token, this.builtin ) )
|
|
1635
|
+
token_classname += "cm-bln";
|
|
1465
1636
|
|
|
1466
|
-
else if( this.
|
|
1467
|
-
|
|
1637
|
+
else if( this._mustHightlightWord( token, this.statementsAndDeclarations ) )
|
|
1638
|
+
token_classname += "cm-std";
|
|
1468
1639
|
|
|
1469
|
-
else if( this.
|
|
1470
|
-
|
|
1640
|
+
else if( this._mustHightlightWord( token, this.symbols ) )
|
|
1641
|
+
token_classname += "cm-sym";
|
|
1471
1642
|
|
|
1472
1643
|
else if( token.substr(0, 2) == '//' )
|
|
1473
|
-
|
|
1644
|
+
token_classname += "cm-com";
|
|
1474
1645
|
|
|
1475
1646
|
else if( token.substr(0, 2) == '/*' )
|
|
1476
|
-
|
|
1647
|
+
token_classname += "cm-com";
|
|
1477
1648
|
|
|
1478
1649
|
else if( token.substr(token.length - 2) == '*/' )
|
|
1479
|
-
|
|
1650
|
+
token_classname += "cm-com";
|
|
1480
1651
|
|
|
1481
1652
|
else if( this.isNumber(token) || this.isNumber( token.replace(/[px]|[em]|%/g,'') ) )
|
|
1482
|
-
|
|
1653
|
+
token_classname += "cm-dec";
|
|
1483
1654
|
|
|
1484
1655
|
else if( this.isCSSClass(token, prev, next) )
|
|
1485
|
-
|
|
1656
|
+
token_classname += "cm-kwd";
|
|
1486
1657
|
|
|
1487
|
-
else if ( this.isType(token, prev, next) )
|
|
1488
|
-
|
|
1489
|
-
this.code.tokens[ token ] = CodeEditor.WORD_TYPE_CLASS;
|
|
1490
|
-
}
|
|
1658
|
+
else if ( this.isType(token, prev, next) )
|
|
1659
|
+
token_classname += "cm-typ";
|
|
1491
1660
|
|
|
1492
|
-
else if ( token[0] != '@' && next == '(' )
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1661
|
+
else if ( token[0] != '@' && next == '(' )
|
|
1662
|
+
token_classname += "cm-mtd";
|
|
1663
|
+
|
|
1664
|
+
else if ( highlight == 'cpp' && token.includes('#') ) // C++ preprocessor
|
|
1665
|
+
token_classname += "cm-ppc";
|
|
1666
|
+
|
|
1667
|
+
else if ( highlight == 'cpp' && prev != 'WDWD:' && next == '::' ) // C++ Class
|
|
1668
|
+
token_classname += "cm-typ";
|
|
1496
1669
|
|
|
1497
1670
|
else if ( highlight == 'css' && prev == ':' && (next == ';' || next == '!important') ) // CSS value
|
|
1498
|
-
|
|
1671
|
+
token_classname += "cm-str";
|
|
1499
1672
|
|
|
1500
1673
|
else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
|
|
1501
|
-
|
|
1502
|
-
else {
|
|
1503
|
-
|
|
1504
|
-
if( token.length > 1 )
|
|
1505
|
-
{
|
|
1506
|
-
// Store in token map to show later as autocomplete suggestions
|
|
1507
|
-
this.code.tokens[ token ] = -1;
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1674
|
+
token_classname += "cm-typ";
|
|
1510
1675
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1676
|
+
token_classname += " " + highlight;
|
|
1677
|
+
inner_html += "<span class=' " + token_classname + "'>" + token + "</span>";
|
|
1513
1678
|
}
|
|
1514
1679
|
|
|
1515
1680
|
if(sString) delete this._building_string;
|
|
1681
|
+
|
|
1682
|
+
return inner_html;
|
|
1516
1683
|
}
|
|
1517
1684
|
|
|
1518
1685
|
isCSSClass( token, prev, next ) {
|
|
@@ -1525,6 +1692,10 @@ class CodeEditor {
|
|
|
1525
1692
|
|
|
1526
1693
|
isType( token, prev, next ) {
|
|
1527
1694
|
|
|
1695
|
+
// Common case
|
|
1696
|
+
if( this._mustHightlightWord( token, this.types ) )
|
|
1697
|
+
return true;
|
|
1698
|
+
|
|
1528
1699
|
if( this.highlight == 'JavaScript' )
|
|
1529
1700
|
{
|
|
1530
1701
|
return (prev == 'class' && next == '{') || (prev == 'new' && next == '(');
|
|
@@ -1543,7 +1714,7 @@ class CodeEditor {
|
|
|
1543
1714
|
|
|
1544
1715
|
if( !this.selection || (this.selection.fromY != this.selection.toY) )
|
|
1545
1716
|
return false;
|
|
1546
|
-
|
|
1717
|
+
|
|
1547
1718
|
const _lastLeft = cursor._left;
|
|
1548
1719
|
|
|
1549
1720
|
// Insert first..
|
|
@@ -1554,8 +1725,7 @@ class CodeEditor {
|
|
|
1554
1725
|
].join('');
|
|
1555
1726
|
|
|
1556
1727
|
// Go to the end of the word
|
|
1557
|
-
this.
|
|
1558
|
-
this.code.lines[lidx].slice(this.selection.fromX, this.selection.toX + 1));
|
|
1728
|
+
this.cursorToPosition(cursor, this.selection.toX + 1);
|
|
1559
1729
|
|
|
1560
1730
|
// Change next key?
|
|
1561
1731
|
switch(key)
|
|
@@ -1630,8 +1800,10 @@ class CodeEditor {
|
|
|
1630
1800
|
|
|
1631
1801
|
deleteSelection( cursor ) {
|
|
1632
1802
|
|
|
1633
|
-
|
|
1803
|
+
// I think it's not necessary but...
|
|
1804
|
+
if(this.disableEdition)
|
|
1634
1805
|
return;
|
|
1806
|
+
|
|
1635
1807
|
// Some selections don't depend on mouse up..
|
|
1636
1808
|
if(this.selection) this.selection.invertIfNecessary();
|
|
1637
1809
|
|
|
@@ -1674,7 +1846,7 @@ class CodeEditor {
|
|
|
1674
1846
|
cursor.style.left = "calc(" + (cursor._left - this.getScrollLeft()) + "px + " + this.xPadding + ")";
|
|
1675
1847
|
cursor.position++;
|
|
1676
1848
|
this.restartBlink();
|
|
1677
|
-
this._refreshCodeInfo( cursor.line
|
|
1849
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1678
1850
|
|
|
1679
1851
|
// Add horizontal scroll
|
|
1680
1852
|
|
|
@@ -1695,7 +1867,7 @@ class CodeEditor {
|
|
|
1695
1867
|
cursor.position--;
|
|
1696
1868
|
cursor.position = Math.max(cursor.position, 0);
|
|
1697
1869
|
this.restartBlink();
|
|
1698
|
-
this._refreshCodeInfo( cursor.line
|
|
1870
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1699
1871
|
|
|
1700
1872
|
doAsync(() => {
|
|
1701
1873
|
var first_char = (this.code.scrollLeft / this.charWidth)|0;
|
|
@@ -1715,7 +1887,7 @@ class CodeEditor {
|
|
|
1715
1887
|
if(resetLeft)
|
|
1716
1888
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
1717
1889
|
|
|
1718
|
-
this._refreshCodeInfo( cursor.line
|
|
1890
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1719
1891
|
|
|
1720
1892
|
doAsync(() => {
|
|
1721
1893
|
var first_line = (this.code.scrollTop / this.lineHeight)|0;
|
|
@@ -1734,7 +1906,7 @@ class CodeEditor {
|
|
|
1734
1906
|
if(resetLeft)
|
|
1735
1907
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
1736
1908
|
|
|
1737
|
-
this._refreshCodeInfo( cursor.line
|
|
1909
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1738
1910
|
|
|
1739
1911
|
doAsync(() => {
|
|
1740
1912
|
var last_line = ((this.code.scrollTop + this.code.offsetHeight) / this.lineHeight)|0;
|
|
@@ -1942,14 +2114,19 @@ class CodeEditor {
|
|
|
1942
2114
|
let suggestions = [];
|
|
1943
2115
|
|
|
1944
2116
|
// Add language special keys...
|
|
1945
|
-
suggestions = suggestions.concat(
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
2117
|
+
suggestions = suggestions.concat(
|
|
2118
|
+
this.builtin[ this.highlight ] ?? [],
|
|
2119
|
+
this.keywords[ this.highlight ] ?? [],
|
|
2120
|
+
this.statementsAndDeclarations[ this.highlight ] ?? [],
|
|
2121
|
+
this.types[ this.highlight ] ?? [],
|
|
2122
|
+
this.utils[ this.highlight ] ?? []
|
|
2123
|
+
);
|
|
2124
|
+
|
|
2125
|
+
// Add words in current tab plus remove current word
|
|
1949
2126
|
suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
|
|
1950
2127
|
|
|
1951
|
-
// Remove
|
|
1952
|
-
suggestions = suggestions.filter( (value, index) => value.length >
|
|
2128
|
+
// Remove 1/2 char words and duplicates...
|
|
2129
|
+
suggestions = suggestions.filter( (value, index) => value.length > 2 && suggestions.indexOf(value) === index );
|
|
1953
2130
|
|
|
1954
2131
|
// Order...
|
|
1955
2132
|
suggestions = suggestions.sort( (a, b) => a.localeCompare(b) );
|
|
@@ -1962,6 +2139,17 @@ class CodeEditor {
|
|
|
1962
2139
|
var pre = document.createElement('pre');
|
|
1963
2140
|
this.autocomplete.appendChild(pre);
|
|
1964
2141
|
|
|
2142
|
+
var icon = document.createElement('a');
|
|
2143
|
+
|
|
2144
|
+
if( this._mustHightlightWord( s, this.utils ) )
|
|
2145
|
+
icon.className = "fa fa-cube";
|
|
2146
|
+
else if( this._mustHightlightWord( s, this.types ) )
|
|
2147
|
+
icon.className = "fa fa-code";
|
|
2148
|
+
else
|
|
2149
|
+
icon.className = "fa fa-font";
|
|
2150
|
+
|
|
2151
|
+
pre.appendChild(icon);
|
|
2152
|
+
|
|
1965
2153
|
pre.addEventListener( 'click', () => {
|
|
1966
2154
|
this.autoCompleteWord( cursor, s );
|
|
1967
2155
|
} );
|
|
@@ -2042,13 +2230,13 @@ class CodeEditor {
|
|
|
2042
2230
|
for( let childSpan of child.childNodes )
|
|
2043
2231
|
word += childSpan.innerHTML;
|
|
2044
2232
|
|
|
2045
|
-
return [word, i ]; // Get text of the span inside the 'pre' element
|
|
2233
|
+
return [ word, i ]; // Get text of the span inside the 'pre' element
|
|
2046
2234
|
}
|
|
2047
2235
|
}
|
|
2048
2236
|
}
|
|
2049
2237
|
|
|
2050
2238
|
moveArrowSelectedAutoComplete( dir ) {
|
|
2051
|
-
|
|
2239
|
+
|
|
2052
2240
|
if( !this.isAutoCompleteActive )
|
|
2053
2241
|
return;
|
|
2054
2242
|
|
|
@@ -2061,7 +2249,7 @@ class CodeEditor {
|
|
|
2061
2249
|
if( (idx + offset) < 0 ) return;
|
|
2062
2250
|
}
|
|
2063
2251
|
|
|
2064
|
-
this.autocomplete.scrollTop += offset *
|
|
2252
|
+
this.autocomplete.scrollTop += offset * 18;
|
|
2065
2253
|
|
|
2066
2254
|
// Remove selected from the current word and add it to the next one
|
|
2067
2255
|
this.autocomplete.childNodes[ idx ].classList.remove('selected');
|