lexgui 0.1.13 → 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 +221 -171
- package/build/components/timeline.js +104 -68
- package/build/lexgui.css +30 -9
- package/build/lexgui.js +21 -17
- package/build/lexgui.module.js +22 -17
- package/demo.js +3 -0
- package/examples/code_editor.html +5 -1
- package/package.json +1 -1
|
@@ -106,9 +106,6 @@ class CodeEditor {
|
|
|
106
106
|
|
|
107
107
|
CodeEditor.__instances.push( this );
|
|
108
108
|
|
|
109
|
-
this.disable_edition = options.disable_edition ?? false;
|
|
110
|
-
|
|
111
|
-
this.skip_info = options.skip_info;
|
|
112
109
|
this.base_area = area;
|
|
113
110
|
this.area = new LX.Area( { className: "lexcodeeditor", height: "auto", no_append: true } );
|
|
114
111
|
|
|
@@ -129,14 +126,21 @@ class CodeEditor {
|
|
|
129
126
|
this.root.tabIndex = -1;
|
|
130
127
|
area.attach( this.root );
|
|
131
128
|
|
|
132
|
-
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
|
+
|
|
133
139
|
this.root.addEventListener( 'mousedown', this.processMouse.bind(this) );
|
|
134
140
|
this.root.addEventListener( 'mouseup', this.processMouse.bind(this) );
|
|
135
141
|
this.root.addEventListener( 'mousemove', this.processMouse.bind(this) );
|
|
136
142
|
this.root.addEventListener( 'click', this.processMouse.bind(this) );
|
|
137
143
|
this.root.addEventListener( 'contextmenu', this.processMouse.bind(this) );
|
|
138
|
-
this.root.addEventListener( 'focus', this.processFocus.bind(this, true) );
|
|
139
|
-
this.root.addEventListener( 'focusout', this.processFocus.bind(this, false) );
|
|
140
144
|
|
|
141
145
|
// Cursors and selection
|
|
142
146
|
|
|
@@ -198,8 +202,19 @@ class CodeEditor {
|
|
|
198
202
|
this.charWidth = 8; //this.measureChar();
|
|
199
203
|
this._lastTime = null;
|
|
200
204
|
|
|
205
|
+
this.pairKeys = {
|
|
206
|
+
"\"": "\"",
|
|
207
|
+
"'": "'",
|
|
208
|
+
"(": ")",
|
|
209
|
+
"{": "}",
|
|
210
|
+
"[": "]"
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Scan tokens..
|
|
214
|
+
setInterval( this.scanWordSuggestions.bind(this), 2000 );
|
|
215
|
+
|
|
201
216
|
this.languages = [
|
|
202
|
-
'Plain Text', 'JavaScript', 'CSS', 'GLSL', 'WGSL', 'JSON', 'XML'
|
|
217
|
+
'Plain Text', 'JavaScript', 'C++', 'CSS', 'GLSL', 'WGSL', 'JSON', 'XML', 'Python'
|
|
203
218
|
];
|
|
204
219
|
this.specialKeys = [
|
|
205
220
|
'Backspace', 'Enter', 'ArrowUp', 'ArrowDown',
|
|
@@ -207,38 +222,54 @@ class CodeEditor {
|
|
|
207
222
|
'End', 'Tab', 'Escape'
|
|
208
223
|
];
|
|
209
224
|
this.keywords = {
|
|
210
|
-
'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'],
|
|
211
228
|
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
212
229
|
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
|
|
213
230
|
'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
|
|
214
231
|
'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
|
|
215
232
|
'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
|
|
216
|
-
'texture_storage_2d_array', 'texture_storage_3d']
|
|
233
|
+
'texture_storage_2d_array', 'texture_storage_3d'],
|
|
234
|
+
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or']
|
|
217
235
|
};
|
|
218
236
|
this.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
|
|
219
237
|
'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
|
|
220
|
-
'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement'
|
|
221
|
-
|
|
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']
|
|
222
245
|
};
|
|
223
246
|
this.types = {
|
|
224
|
-
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number']
|
|
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' ]
|
|
225
252
|
};
|
|
226
253
|
this.builtin = {
|
|
227
|
-
'JavaScript': ['document', 'console', 'window', 'navigator'],
|
|
228
|
-
'CSS': ['*', '!important']
|
|
254
|
+
'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
|
|
255
|
+
'CSS': ['*', '!important'],
|
|
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
|
-
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform']
|
|
262
|
+
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'uniform'],
|
|
263
|
+
'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue', 'global', 'pass']
|
|
235
264
|
};
|
|
236
265
|
this.symbols = {
|
|
237
266
|
'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
267
|
+
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::'],
|
|
238
268
|
'JSON': ['[', ']', '{', '}', '(', ')'],
|
|
239
269
|
'GLSL': ['[', ']', '{', '}', '(', ')'],
|
|
240
270
|
'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
|
|
241
|
-
'CSS': ['{', '}', '(', ')', '*']
|
|
271
|
+
'CSS': ['{', '}', '(', ')', '*'],
|
|
272
|
+
'Python': ['<', '>', '[', ']', '(', ')', '=']
|
|
242
273
|
};
|
|
243
274
|
|
|
244
275
|
// Action keys
|
|
@@ -323,7 +354,7 @@ 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
360
|
if( e.shiftKey && !e.cancelShift )
|
|
@@ -752,11 +783,14 @@ class CodeEditor {
|
|
|
752
783
|
switch(ext.toLowerCase())
|
|
753
784
|
{
|
|
754
785
|
case 'js': return this._changeLanguage('JavaScript');
|
|
786
|
+
case 'cpp': return this._changeLanguage('C++');
|
|
787
|
+
case 'h': return this._changeLanguage('C++');
|
|
755
788
|
case 'glsl': return this._changeLanguage('GLSL');
|
|
756
789
|
case 'css': return this._changeLanguage('CSS');
|
|
757
790
|
case 'json': return this._changeLanguage('JSON');
|
|
758
791
|
case 'xml': return this._changeLanguage('XML');
|
|
759
792
|
case 'wgsl': return this._changeLanguage('WGSL');
|
|
793
|
+
case 'py': return this._changeLanguage('Python');
|
|
760
794
|
case 'txt':
|
|
761
795
|
default:
|
|
762
796
|
this._changeLanguage('Plain Text');
|
|
@@ -765,20 +799,20 @@ class CodeEditor {
|
|
|
765
799
|
|
|
766
800
|
_createPanelInfo() {
|
|
767
801
|
|
|
768
|
-
if( !this.
|
|
802
|
+
if( !this.skipCodeInfo )
|
|
769
803
|
{
|
|
770
804
|
let panel = new LX.Panel({ className: "lexcodetabinfo", width: "calc(100%)", height: "auto" });
|
|
771
805
|
panel.ln = 0;
|
|
772
806
|
panel.col = 0;
|
|
773
807
|
|
|
774
808
|
this._refreshCodeInfo = (ln = panel.ln, col = panel.col) => {
|
|
775
|
-
panel.ln = ln;
|
|
776
|
-
panel.col = col;
|
|
809
|
+
panel.ln = ln + 1;
|
|
810
|
+
panel.col = col + 1;
|
|
777
811
|
panel.clear();
|
|
778
812
|
panel.sameLine();
|
|
779
813
|
panel.addLabel(this.code.title, { float: 'right' });
|
|
780
|
-
panel.addLabel("Ln " + ln, { width: "64px" });
|
|
781
|
-
panel.addLabel("Col " + col, { width: "64px" });
|
|
814
|
+
panel.addLabel("Ln " + panel.ln, { width: "64px" });
|
|
815
|
+
panel.addLabel("Col " + panel.col, { width: "64px" });
|
|
782
816
|
panel.addButton("<b>{ }</b>", this.highlight, (value, event) => {
|
|
783
817
|
LX.addContextMenu( "Language", event, m => {
|
|
784
818
|
for( const lang of this.languages )
|
|
@@ -882,7 +916,7 @@ class CodeEditor {
|
|
|
882
916
|
this.restoreCursor(cursor, this.code.cursorState);
|
|
883
917
|
this.endSelection();
|
|
884
918
|
this._changeLanguageFromExtension( LX.getExtension(tabname) );
|
|
885
|
-
this._refreshCodeInfo(cursor.line
|
|
919
|
+
this._refreshCodeInfo(cursor.line, cursor.position);
|
|
886
920
|
|
|
887
921
|
// Restore scroll
|
|
888
922
|
this.gutter.scrollTop = this.code.scrollTop;
|
|
@@ -1012,15 +1046,18 @@ class CodeEditor {
|
|
|
1012
1046
|
return;
|
|
1013
1047
|
|
|
1014
1048
|
LX.addContextMenu( null, e, m => {
|
|
1015
|
-
m.add( "Format/JSON", () => {
|
|
1016
|
-
let json = this.toJSONFormat(this.getText());
|
|
1017
|
-
this.code.lines = json.split("\n");
|
|
1018
|
-
this.processLines();
|
|
1019
|
-
} );
|
|
1020
|
-
m.add( "" )
|
|
1021
|
-
m.add( "Cut", () => { this._cutContent(); } );
|
|
1022
1049
|
m.add( "Copy", () => { this._copyContent(); } );
|
|
1023
|
-
|
|
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
|
+
}
|
|
1024
1061
|
});
|
|
1025
1062
|
|
|
1026
1063
|
this.canOpenContextMenu = false;
|
|
@@ -1048,7 +1085,7 @@ class CodeEditor {
|
|
|
1048
1085
|
this.hideAutoCompleteBox();
|
|
1049
1086
|
|
|
1050
1087
|
if(!skip_refresh)
|
|
1051
|
-
this._refreshCodeInfo( ln
|
|
1088
|
+
this._refreshCodeInfo( ln, cursor.position );
|
|
1052
1089
|
}
|
|
1053
1090
|
|
|
1054
1091
|
processSelection( e, keep_range ) {
|
|
@@ -1303,36 +1340,33 @@ class CodeEditor {
|
|
|
1303
1340
|
lidx = cursor.line; // Update this, since it's from the old code
|
|
1304
1341
|
}
|
|
1305
1342
|
|
|
1306
|
-
// Append key
|
|
1343
|
+
// Append key
|
|
1307
1344
|
|
|
1308
|
-
this.
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1345
|
+
const isPairKey = (Object.values( this.pairKeys ).indexOf( key ) > -1) && !this.wasKeyPaired;
|
|
1346
|
+
const sameKeyNext = isPairKey && (this.code.lines[lidx][cursor.position] === key);
|
|
1347
|
+
|
|
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
|
+
}
|
|
1313
1356
|
|
|
1314
1357
|
this.cursorToRight( key );
|
|
1315
1358
|
|
|
1316
1359
|
// Some custom cases for auto key pair (), {}, "", '', ...
|
|
1317
|
-
|
|
1318
|
-
const
|
|
1319
|
-
if(
|
|
1360
|
+
|
|
1361
|
+
const keyMustPair = (this.pairKeys[ key ] !== undefined);
|
|
1362
|
+
if( keyMustPair && !this.wasKeyPaired )
|
|
1320
1363
|
{
|
|
1321
|
-
// Find pair key
|
|
1322
|
-
let pair = key;
|
|
1323
|
-
switch(key)
|
|
1324
|
-
{
|
|
1325
|
-
case "'":
|
|
1326
|
-
case "\"":
|
|
1327
|
-
break;
|
|
1328
|
-
case "(": pair = ")"; break;
|
|
1329
|
-
case "{": pair = "}"; break;
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
1364
|
// Make sure to detect later that the key is paired automatically to avoid loops...
|
|
1333
1365
|
this.wasKeyPaired = true;
|
|
1334
1366
|
|
|
1335
|
-
|
|
1367
|
+
if(sameKeyNext) return;
|
|
1368
|
+
|
|
1369
|
+
this.root.dispatchEvent(new KeyboardEvent('keydown', { 'key': this.pairKeys[ key ] }));
|
|
1336
1370
|
this.cursorToLeft( key, cursor );
|
|
1337
1371
|
return;
|
|
1338
1372
|
}
|
|
@@ -1424,58 +1458,103 @@ class CodeEditor {
|
|
|
1424
1458
|
};
|
|
1425
1459
|
}
|
|
1426
1460
|
|
|
1427
|
-
|
|
1461
|
+
scanWordSuggestions() {
|
|
1428
1462
|
|
|
1429
|
-
|
|
1430
|
-
{
|
|
1431
|
-
this.gutter.innerHTML = "";
|
|
1432
|
-
this.code.innerHTML = "";
|
|
1433
|
-
this.code.tokens = {};
|
|
1434
|
-
}
|
|
1463
|
+
this.code.tokens = {};
|
|
1435
1464
|
|
|
1436
|
-
for( let i =
|
|
1465
|
+
for( let i = 0; i < this.code.lines.length; ++i )
|
|
1437
1466
|
{
|
|
1438
|
-
this.
|
|
1467
|
+
const linestring = this.code.lines[ i ];
|
|
1468
|
+
const tokens = this._getTokensFromString( linestring, true );
|
|
1469
|
+
tokens.forEach( t => this.code.tokens[ t ] = 1 );
|
|
1439
1470
|
}
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
processLines( from ) {
|
|
1440
1474
|
|
|
1441
|
-
//
|
|
1442
|
-
|
|
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 )
|
|
1443
1481
|
{
|
|
1444
|
-
|
|
1445
|
-
this.gutter.lastChild.remove();
|
|
1446
|
-
while( this.code.lines.length != this.code.children.length )
|
|
1447
|
-
this.code.lastChild.remove();
|
|
1482
|
+
this.processLine( i, true );
|
|
1448
1483
|
}
|
|
1449
1484
|
|
|
1485
|
+
// console.log( performance.now() - start );
|
|
1450
1486
|
}
|
|
1451
1487
|
|
|
1452
|
-
processLine( linenum ) {
|
|
1488
|
+
processLine( linenum, force ) {
|
|
1489
|
+
|
|
1490
|
+
// if(!force)
|
|
1491
|
+
// {
|
|
1492
|
+
// this.processLines();
|
|
1493
|
+
// return;
|
|
1494
|
+
// }
|
|
1453
1495
|
|
|
1454
1496
|
delete this._building_string; // multi-line strings not supported by now
|
|
1455
1497
|
|
|
1456
1498
|
// It's allowed to process only 1 line to optimize
|
|
1457
1499
|
let linestring = this.code.lines[ linenum ];
|
|
1458
|
-
|
|
1459
|
-
var pre =
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
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 )
|
|
1466
1509
|
{
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
}
|
|
1471
|
-
else
|
|
1510
|
+
this.gutter.appendChild(linenumspan);
|
|
1511
|
+
this.code.appendChild( pre );
|
|
1512
|
+
} else
|
|
1472
1513
|
{
|
|
1473
|
-
|
|
1514
|
+
this.gutter.childNodes[ linenum ].remove();
|
|
1515
|
+
this.code.childNodes[ linenum ].remove();
|
|
1516
|
+
this.gutter.insertChildAtIndex( linenumspan, linenum );
|
|
1517
|
+
this.code.insertChildAtIndex( pre, linenum );
|
|
1474
1518
|
}
|
|
1475
1519
|
|
|
1476
1520
|
var linespan = document.createElement('span');
|
|
1477
1521
|
pre.appendChild(linespan);
|
|
1478
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
|
+
|
|
1479
1558
|
// Check if line comment
|
|
1480
1559
|
const is_comment = linestring.split('//');
|
|
1481
1560
|
linestring = ( is_comment.length > 1 ) ? is_comment[0] : linestring;
|
|
@@ -1485,7 +1564,15 @@ class CodeEditor {
|
|
|
1485
1564
|
|
|
1486
1565
|
for( let t of tokens )
|
|
1487
1566
|
{
|
|
1488
|
-
|
|
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);
|
|
1489
1576
|
let subtokens = iter.next();
|
|
1490
1577
|
if( subtokens.value )
|
|
1491
1578
|
{
|
|
@@ -1493,56 +1580,23 @@ class CodeEditor {
|
|
|
1493
1580
|
while( subtokens.value != undefined )
|
|
1494
1581
|
{
|
|
1495
1582
|
const _pt = t.substring(idx, subtokens.value.index);
|
|
1496
|
-
to_process.push( _pt );
|
|
1583
|
+
if( _pt.length ) to_process.push( _pt );
|
|
1497
1584
|
to_process.push( subtokens.value[0] );
|
|
1498
|
-
idx = subtokens.value.index +
|
|
1585
|
+
idx = subtokens.value.index + subtokens.value[0].length;
|
|
1499
1586
|
subtokens = iter.next();
|
|
1500
1587
|
if(!subtokens.value) {
|
|
1501
1588
|
const _at = t.substring(idx);
|
|
1502
|
-
to_process.push( _at );
|
|
1589
|
+
if( _at.length ) to_process.push( _at );
|
|
1503
1590
|
}
|
|
1504
1591
|
}
|
|
1505
1592
|
}
|
|
1506
|
-
else
|
|
1507
|
-
to_process.push( t );
|
|
1593
|
+
else to_process.push( t );
|
|
1508
1594
|
}
|
|
1509
1595
|
|
|
1510
|
-
if( is_comment.length > 1 )
|
|
1596
|
+
if( is_comment.length > 1 && !skipNonWords )
|
|
1511
1597
|
to_process.push( "//" + is_comment[1] );
|
|
1512
|
-
|
|
1513
|
-
// Process all tokens
|
|
1514
|
-
for( var i = 0; i < to_process.length; ++i )
|
|
1515
|
-
{
|
|
1516
|
-
let it = i - 1;
|
|
1517
|
-
let prev = to_process[it];
|
|
1518
|
-
while( prev == '' || prev == ' ' ) {
|
|
1519
|
-
it--;
|
|
1520
|
-
prev = to_process[it];
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
it = i + 1;
|
|
1524
|
-
let next = to_process[it];
|
|
1525
|
-
while( next == '' || next == ' ' || next == '"') {
|
|
1526
|
-
it++;
|
|
1527
|
-
next = to_process[it];
|
|
1528
|
-
}
|
|
1529
1598
|
|
|
1530
|
-
|
|
1531
|
-
if( token.substr(0, 2) == '/*' )
|
|
1532
|
-
this._building_block_comment = true;
|
|
1533
|
-
if( token.substr(token.length - 2) == '*/' )
|
|
1534
|
-
delete this._building_block_comment;
|
|
1535
|
-
|
|
1536
|
-
this.processToken(token, linespan, prev, next);
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
// add line gutter
|
|
1540
|
-
if(!single_update)
|
|
1541
|
-
{
|
|
1542
|
-
var linenumspan = document.createElement('span');
|
|
1543
|
-
linenumspan.innerHTML = (linenum + 1);
|
|
1544
|
-
this.gutter.appendChild(linenumspan);
|
|
1545
|
-
}
|
|
1599
|
+
return to_process;
|
|
1546
1600
|
}
|
|
1547
1601
|
|
|
1548
1602
|
_mustHightlightWord( token, kindArray ) {
|
|
@@ -1550,10 +1604,11 @@ class CodeEditor {
|
|
|
1550
1604
|
return kindArray[this.highlight] && kindArray[this.highlight].indexOf(token) > -1;
|
|
1551
1605
|
}
|
|
1552
1606
|
|
|
1553
|
-
processToken(token,
|
|
1607
|
+
processToken(token, prev, next) {
|
|
1554
1608
|
|
|
1555
1609
|
let sString = false;
|
|
1556
|
-
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 = "";
|
|
1557
1612
|
|
|
1558
1613
|
if(token == '"' || token == "'")
|
|
1559
1614
|
{
|
|
@@ -1563,75 +1618,68 @@ class CodeEditor {
|
|
|
1563
1618
|
|
|
1564
1619
|
if(token == ' ')
|
|
1565
1620
|
{
|
|
1566
|
-
|
|
1621
|
+
inner_html += token;
|
|
1567
1622
|
}
|
|
1568
1623
|
else
|
|
1569
1624
|
{
|
|
1570
|
-
var span = document.createElement('span');
|
|
1571
|
-
span.innerHTML = token;
|
|
1572
|
-
|
|
1573
1625
|
if( this._building_block_comment )
|
|
1574
|
-
|
|
1626
|
+
token_classname += "cm-com";
|
|
1575
1627
|
|
|
1576
1628
|
else if( this._building_string )
|
|
1577
|
-
|
|
1629
|
+
token_classname += "cm-str";
|
|
1578
1630
|
|
|
1579
1631
|
else if( this._mustHightlightWord( token, this.keywords ) )
|
|
1580
|
-
|
|
1632
|
+
token_classname += "cm-kwd";
|
|
1581
1633
|
|
|
1582
1634
|
else if( this._mustHightlightWord( token, this.builtin ) )
|
|
1583
|
-
|
|
1635
|
+
token_classname += "cm-bln";
|
|
1584
1636
|
|
|
1585
1637
|
else if( this._mustHightlightWord( token, this.statementsAndDeclarations ) )
|
|
1586
|
-
|
|
1638
|
+
token_classname += "cm-std";
|
|
1587
1639
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1640
|
+
else if( this._mustHightlightWord( token, this.symbols ) )
|
|
1641
|
+
token_classname += "cm-sym";
|
|
1590
1642
|
|
|
1591
1643
|
else if( token.substr(0, 2) == '//' )
|
|
1592
|
-
|
|
1644
|
+
token_classname += "cm-com";
|
|
1593
1645
|
|
|
1594
1646
|
else if( token.substr(0, 2) == '/*' )
|
|
1595
|
-
|
|
1647
|
+
token_classname += "cm-com";
|
|
1596
1648
|
|
|
1597
1649
|
else if( token.substr(token.length - 2) == '*/' )
|
|
1598
|
-
|
|
1650
|
+
token_classname += "cm-com";
|
|
1599
1651
|
|
|
1600
1652
|
else if( this.isNumber(token) || this.isNumber( token.replace(/[px]|[em]|%/g,'') ) )
|
|
1601
|
-
|
|
1653
|
+
token_classname += "cm-dec";
|
|
1602
1654
|
|
|
1603
1655
|
else if( this.isCSSClass(token, prev, next) )
|
|
1604
|
-
|
|
1656
|
+
token_classname += "cm-kwd";
|
|
1605
1657
|
|
|
1606
|
-
else if ( this.isType(token, prev, next) )
|
|
1607
|
-
|
|
1608
|
-
this.code.tokens[ token ] = CodeEditor.WORD_TYPE_CLASS;
|
|
1609
|
-
}
|
|
1658
|
+
else if ( this.isType(token, prev, next) )
|
|
1659
|
+
token_classname += "cm-typ";
|
|
1610
1660
|
|
|
1611
|
-
else if ( token[0] != '@' && next == '(' )
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
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";
|
|
1615
1669
|
|
|
1616
1670
|
else if ( highlight == 'css' && prev == ':' && (next == ';' || next == '!important') ) // CSS value
|
|
1617
|
-
|
|
1671
|
+
token_classname += "cm-str";
|
|
1618
1672
|
|
|
1619
1673
|
else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
|
|
1620
|
-
|
|
1621
|
-
else {
|
|
1674
|
+
token_classname += "cm-typ";
|
|
1622
1675
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
// Store in token map to show later as autocomplete suggestions
|
|
1626
|
-
this.code.tokens[ token ] = -1;
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
span.classList.add(highlight);
|
|
1631
|
-
linespan.appendChild(span);
|
|
1676
|
+
token_classname += " " + highlight;
|
|
1677
|
+
inner_html += "<span class=' " + token_classname + "'>" + token + "</span>";
|
|
1632
1678
|
}
|
|
1633
1679
|
|
|
1634
1680
|
if(sString) delete this._building_string;
|
|
1681
|
+
|
|
1682
|
+
return inner_html;
|
|
1635
1683
|
}
|
|
1636
1684
|
|
|
1637
1685
|
isCSSClass( token, prev, next ) {
|
|
@@ -1752,8 +1800,10 @@ class CodeEditor {
|
|
|
1752
1800
|
|
|
1753
1801
|
deleteSelection( cursor ) {
|
|
1754
1802
|
|
|
1755
|
-
|
|
1803
|
+
// I think it's not necessary but...
|
|
1804
|
+
if(this.disableEdition)
|
|
1756
1805
|
return;
|
|
1806
|
+
|
|
1757
1807
|
// Some selections don't depend on mouse up..
|
|
1758
1808
|
if(this.selection) this.selection.invertIfNecessary();
|
|
1759
1809
|
|
|
@@ -1796,7 +1846,7 @@ class CodeEditor {
|
|
|
1796
1846
|
cursor.style.left = "calc(" + (cursor._left - this.getScrollLeft()) + "px + " + this.xPadding + ")";
|
|
1797
1847
|
cursor.position++;
|
|
1798
1848
|
this.restartBlink();
|
|
1799
|
-
this._refreshCodeInfo( cursor.line
|
|
1849
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1800
1850
|
|
|
1801
1851
|
// Add horizontal scroll
|
|
1802
1852
|
|
|
@@ -1817,7 +1867,7 @@ class CodeEditor {
|
|
|
1817
1867
|
cursor.position--;
|
|
1818
1868
|
cursor.position = Math.max(cursor.position, 0);
|
|
1819
1869
|
this.restartBlink();
|
|
1820
|
-
this._refreshCodeInfo( cursor.line
|
|
1870
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1821
1871
|
|
|
1822
1872
|
doAsync(() => {
|
|
1823
1873
|
var first_char = (this.code.scrollLeft / this.charWidth)|0;
|
|
@@ -1837,7 +1887,7 @@ class CodeEditor {
|
|
|
1837
1887
|
if(resetLeft)
|
|
1838
1888
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
1839
1889
|
|
|
1840
|
-
this._refreshCodeInfo( cursor.line
|
|
1890
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1841
1891
|
|
|
1842
1892
|
doAsync(() => {
|
|
1843
1893
|
var first_line = (this.code.scrollTop / this.lineHeight)|0;
|
|
@@ -1856,7 +1906,7 @@ class CodeEditor {
|
|
|
1856
1906
|
if(resetLeft)
|
|
1857
1907
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
1858
1908
|
|
|
1859
|
-
this._refreshCodeInfo( cursor.line
|
|
1909
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1860
1910
|
|
|
1861
1911
|
doAsync(() => {
|
|
1862
1912
|
var last_line = ((this.code.scrollTop + this.code.offsetHeight) / this.lineHeight)|0;
|
|
@@ -2073,10 +2123,10 @@ class CodeEditor {
|
|
|
2073
2123
|
);
|
|
2074
2124
|
|
|
2075
2125
|
// Add words in current tab plus remove current word
|
|
2076
|
-
|
|
2126
|
+
suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
|
|
2077
2127
|
|
|
2078
|
-
// Remove
|
|
2079
|
-
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 );
|
|
2080
2130
|
|
|
2081
2131
|
// Order...
|
|
2082
2132
|
suggestions = suggestions.sort( (a, b) => a.localeCompare(b) );
|