lexgui 8.0.0 → 8.1.1
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/README.md +3 -3
- package/build/components/AlertDialog.d.ts +7 -0
- package/build/components/Counter.d.ts +1 -0
- package/build/components/Dialog.d.ts +1 -1
- package/build/components/Footer.d.ts +1 -1
- package/build/components/Menubar.d.ts +2 -2
- package/build/core/Area.d.ts +22 -22
- package/build/core/Namespace.js +34 -34
- package/build/core/Namespace.js.map +1 -1
- package/build/core/Panel.d.ts +2 -3
- package/build/extensions/AssetView.d.ts +136 -134
- package/build/extensions/AssetView.js +1367 -1320
- package/build/extensions/AssetView.js.map +1 -1
- package/build/extensions/Audio.js +19 -19
- package/build/extensions/Audio.js.map +1 -1
- package/build/extensions/CodeEditor.js +867 -647
- package/build/extensions/CodeEditor.js.map +1 -1
- package/build/extensions/DocMaker.d.ts +1 -1
- package/build/extensions/DocMaker.js +73 -61
- package/build/extensions/DocMaker.js.map +1 -1
- package/build/extensions/GraphEditor.js +406 -305
- package/build/extensions/GraphEditor.js.map +1 -1
- package/build/extensions/ImUi.js +21 -20
- package/build/extensions/ImUi.js.map +1 -1
- package/build/extensions/Timeline.d.ts +29 -36
- package/build/extensions/Timeline.js +421 -424
- package/build/extensions/Timeline.js.map +1 -1
- package/build/extensions/VideoEditor.js +101 -97
- package/build/extensions/VideoEditor.js.map +1 -1
- package/build/extensions/index.d.ts +8 -8
- package/build/extensions/index.js +1 -1
- package/build/index.all.d.ts +2 -2
- package/build/index.css.d.ts +1 -1
- package/build/index.d.ts +56 -55
- package/build/lexgui.all.js +28488 -27640
- package/build/lexgui.all.js.map +1 -1
- package/build/lexgui.all.min.js +1 -1
- package/build/lexgui.all.module.js +28412 -27565
- package/build/lexgui.all.module.js.map +1 -1
- package/build/lexgui.all.module.min.js +1 -1
- package/build/lexgui.css +176 -69
- package/build/lexgui.js +13796 -13330
- package/build/lexgui.js.map +1 -1
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +13733 -13268
- package/build/lexgui.module.js.map +1 -1
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +22 -1
- package/demo.js +6 -5
- package/examples/all-components.html +3 -0
- package/examples/asset-view.html +52 -6
- package/examples/dialogs.html +3 -3
- package/examples/editor.html +1 -1
- package/examples/index.html +1 -1
- package/package.json +4 -1
|
@@ -3,7 +3,7 @@ import { LX } from '../core/Namespace.js';
|
|
|
3
3
|
|
|
4
4
|
// CodeEditor.ts @jxarco
|
|
5
5
|
if (!LX) {
|
|
6
|
-
throw (
|
|
6
|
+
throw ('Missing LX namespace!');
|
|
7
7
|
}
|
|
8
8
|
LX.extensions.push('CodeEditor');
|
|
9
9
|
const g = globalThis;
|
|
@@ -13,20 +13,36 @@ LX.Panel;
|
|
|
13
13
|
LX.Tree;
|
|
14
14
|
LX.Tabs;
|
|
15
15
|
LX.ContextMenu;
|
|
16
|
-
function swapElements(obj, a, b) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function
|
|
16
|
+
function swapElements(obj, a, b) {
|
|
17
|
+
[obj[a], obj[b]] = [obj[b], obj[a]];
|
|
18
|
+
}
|
|
19
|
+
function swapArrayElements(array, id0, id1) {
|
|
20
|
+
[array[id0], array[id1]] = [array[id1], array[id0]];
|
|
21
|
+
}
|
|
22
|
+
function sliceChars(str, idx, n = 1) {
|
|
23
|
+
return str.substr(0, idx) + str.substr(idx + n);
|
|
24
|
+
}
|
|
25
|
+
function firstNonspaceIndex(str) {
|
|
26
|
+
const index = str.search(/\S|$/);
|
|
27
|
+
return index < str.length ? index : -1;
|
|
28
|
+
}
|
|
29
|
+
function strReverse(str) {
|
|
30
|
+
return str.split('').reverse().join('');
|
|
31
|
+
}
|
|
32
|
+
function isLetter(c) {
|
|
33
|
+
return /[a-zA-Z]/.test(c);
|
|
34
|
+
}
|
|
35
|
+
function isSymbol(c) {
|
|
36
|
+
return /[^\w\s]/.test(c);
|
|
37
|
+
}
|
|
23
38
|
function indexOfFrom(str, reg, from, reverse = false) {
|
|
24
39
|
from = from ?? 0;
|
|
25
40
|
if (reverse) {
|
|
26
41
|
str = str.substring(0, from);
|
|
27
42
|
var k = from - 1;
|
|
28
|
-
while (str[k] && str[k] != reg)
|
|
43
|
+
while (str[k] && str[k] != reg) {
|
|
29
44
|
k--;
|
|
45
|
+
}
|
|
30
46
|
return str[k] ? k : -1;
|
|
31
47
|
}
|
|
32
48
|
else {
|
|
@@ -45,24 +61,28 @@ function codeScopesEqual(a, b) {
|
|
|
45
61
|
}
|
|
46
62
|
class Cursor {
|
|
47
63
|
root;
|
|
48
|
-
name =
|
|
64
|
+
name = '';
|
|
49
65
|
editor;
|
|
50
66
|
isMain = false;
|
|
51
67
|
selection = null;
|
|
52
68
|
_line = 0;
|
|
53
69
|
_position = 0;
|
|
54
|
-
get line() {
|
|
70
|
+
get line() {
|
|
71
|
+
return this._line;
|
|
72
|
+
}
|
|
55
73
|
set line(v) {
|
|
56
74
|
this._line = v;
|
|
57
75
|
if (this.isMain)
|
|
58
76
|
this.editor._setActiveLine(v);
|
|
59
77
|
}
|
|
60
|
-
get position() {
|
|
78
|
+
get position() {
|
|
79
|
+
return this._position;
|
|
80
|
+
}
|
|
61
81
|
set position(v) {
|
|
62
82
|
this._position = v;
|
|
63
83
|
if (this.isMain) {
|
|
64
84
|
const activeLine = this.editor.state.activeLine;
|
|
65
|
-
this.editor._updateDataInfoPanel(
|
|
85
|
+
this.editor._updateDataInfoPanel('@cursor-data', `Ln ${activeLine + 1}, Col ${v + 1}`);
|
|
66
86
|
}
|
|
67
87
|
}
|
|
68
88
|
left = 0;
|
|
@@ -73,8 +93,8 @@ class Cursor {
|
|
|
73
93
|
this.isMain = isMain;
|
|
74
94
|
this.root = document.createElement('div');
|
|
75
95
|
this.root.name = name;
|
|
76
|
-
this.root.className =
|
|
77
|
-
this.root.innerHTML =
|
|
96
|
+
this.root.className = 'cursor';
|
|
97
|
+
this.root.innerHTML = ' ';
|
|
78
98
|
this.set(position, line, false);
|
|
79
99
|
}
|
|
80
100
|
set(position = 0, line = 0, updateEditor = true) {
|
|
@@ -107,7 +127,7 @@ class CodeSelection {
|
|
|
107
127
|
editor;
|
|
108
128
|
cursor;
|
|
109
129
|
className;
|
|
110
|
-
constructor(editor, cursor, className =
|
|
130
|
+
constructor(editor, cursor, className = 'lexcodeselection') {
|
|
111
131
|
this.editor = editor;
|
|
112
132
|
this.cursor = cursor;
|
|
113
133
|
this.className = className;
|
|
@@ -142,10 +162,10 @@ class CodeSelection {
|
|
|
142
162
|
var domEl = document.createElement('div');
|
|
143
163
|
domEl.className = this.className;
|
|
144
164
|
domEl._top = y * this.editor.lineHeight;
|
|
145
|
-
domEl.style.top = domEl._top +
|
|
165
|
+
domEl.style.top = domEl._top + 'px';
|
|
146
166
|
domEl._left = x * this.editor.charWidth;
|
|
147
|
-
domEl.style.left =
|
|
148
|
-
domEl.style.width = width +
|
|
167
|
+
domEl.style.left = 'calc(' + domEl._left + 'px + ' + this.editor.xPadding + ')';
|
|
168
|
+
domEl.style.width = width + 'px';
|
|
149
169
|
if (isSearchResult) {
|
|
150
170
|
this.editor.searchResultSelections.appendChild(domEl);
|
|
151
171
|
}
|
|
@@ -190,40 +210,44 @@ class ScrollBar {
|
|
|
190
210
|
this.editor = editor;
|
|
191
211
|
this.type = type;
|
|
192
212
|
this.root = document.createElement('div');
|
|
193
|
-
this.root.className =
|
|
194
|
-
if (type & ScrollBar.SCROLLBAR_VERTICAL)
|
|
213
|
+
this.root.className = 'lexcodescrollbar hidden';
|
|
214
|
+
if (type & ScrollBar.SCROLLBAR_VERTICAL) {
|
|
195
215
|
this.root.classList.add('vertical');
|
|
196
|
-
|
|
216
|
+
}
|
|
217
|
+
else if (type & ScrollBar.SCROLLBAR_HORIZONTAL) {
|
|
197
218
|
this.root.classList.add('horizontal');
|
|
219
|
+
}
|
|
198
220
|
this.thumb = document.createElement('div');
|
|
199
221
|
this.thumb._top = 0;
|
|
200
222
|
this.thumb._left = 0;
|
|
201
223
|
this.root.appendChild(this.thumb);
|
|
202
|
-
this.thumb.addEventListener(
|
|
224
|
+
this.thumb.addEventListener('mousedown', inner_mousedown);
|
|
203
225
|
this.lastPosition = new LX.vec2(0, 0);
|
|
204
226
|
let that = this;
|
|
205
227
|
function inner_mousedown(e) {
|
|
206
228
|
var doc = editor.root.ownerDocument;
|
|
207
|
-
doc.addEventListener(
|
|
208
|
-
doc.addEventListener(
|
|
229
|
+
doc.addEventListener('mousemove', inner_mousemove);
|
|
230
|
+
doc.addEventListener('mouseup', inner_mouseup);
|
|
209
231
|
that.lastPosition.set(e.x, e.y);
|
|
210
232
|
e.stopPropagation();
|
|
211
233
|
e.preventDefault();
|
|
212
234
|
}
|
|
213
235
|
function inner_mousemove(e) {
|
|
214
236
|
var dt = that.lastPosition.sub(new LX.vec2(e.x, e.y));
|
|
215
|
-
if (that.type & ScrollBar.SCROLLBAR_VERTICAL)
|
|
237
|
+
if (that.type & ScrollBar.SCROLLBAR_VERTICAL) {
|
|
216
238
|
editor.updateVerticalScrollFromScrollBar(dt.y);
|
|
217
|
-
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
218
241
|
editor.updateHorizontalScrollFromScrollBar(dt.x);
|
|
242
|
+
}
|
|
219
243
|
that.lastPosition.set(e.x, e.y);
|
|
220
244
|
e.stopPropagation();
|
|
221
245
|
e.preventDefault();
|
|
222
246
|
}
|
|
223
247
|
function inner_mouseup(e) {
|
|
224
248
|
var doc = editor.root.ownerDocument;
|
|
225
|
-
doc.removeEventListener(
|
|
226
|
-
doc.removeEventListener(
|
|
249
|
+
doc.removeEventListener('mousemove', inner_mousemove);
|
|
250
|
+
doc.removeEventListener('mouseup', inner_mouseup);
|
|
227
251
|
}
|
|
228
252
|
}
|
|
229
253
|
}
|
|
@@ -236,58 +260,106 @@ class ScrollBar {
|
|
|
236
260
|
*/
|
|
237
261
|
const HighlightRules = {
|
|
238
262
|
common: [
|
|
239
|
-
{ test: (ctx) => ctx.inBlockComment, className:
|
|
240
|
-
{ test: (ctx) => ctx.inString,
|
|
241
|
-
|
|
242
|
-
{ test: (ctx
|
|
243
|
-
|
|
244
|
-
{ test: (ctx, editor) => editor.
|
|
245
|
-
{
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
263
|
+
{ test: (ctx) => ctx.inBlockComment, className: 'cm-com' },
|
|
264
|
+
{ test: (ctx) => ctx.inString,
|
|
265
|
+
action: (ctx, editor) => editor._appendStringToken(ctx.token), discard: true },
|
|
266
|
+
{ test: (ctx) => ctx.token.substr(0, ctx.singleLineCommentToken.length) == ctx.singleLineCommentToken,
|
|
267
|
+
className: 'cm-com' },
|
|
268
|
+
{ test: (ctx, editor) => editor._isKeyword(ctx), className: 'cm-kwd' },
|
|
269
|
+
{
|
|
270
|
+
test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.builtIn, ctx.lang) && (ctx.lang.tags ?? false
|
|
271
|
+
? (editor._enclosedByTokens(ctx.token, ctx.tokenIndex, '<', '>'))
|
|
272
|
+
: true),
|
|
273
|
+
className: 'cm-bln'
|
|
274
|
+
},
|
|
275
|
+
{ test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.statements, ctx.lang),
|
|
276
|
+
className: 'cm-std' },
|
|
277
|
+
{ test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.symbols, ctx.lang),
|
|
278
|
+
className: 'cm-sym' },
|
|
279
|
+
{ test: (ctx, editor) => editor._mustHightlightWord(ctx.token, CE.types, ctx.lang),
|
|
280
|
+
className: 'cm-typ' },
|
|
281
|
+
{
|
|
282
|
+
test: (ctx, editor) => editor._isNumber(ctx.token) || editor._isNumber(ctx.token.replace(/[px]|[em]|%/g, '')),
|
|
283
|
+
className: 'cm-dec'
|
|
284
|
+
},
|
|
285
|
+
{ test: (ctx) => ctx.lang.usePreprocessor && ctx.token.includes('#'), className: 'cm-ppc' }
|
|
249
286
|
],
|
|
250
287
|
javascript: [
|
|
251
|
-
{ test: (ctx) => (ctx.prev === 'class' && ctx.next === '{'), className:
|
|
288
|
+
{ test: (ctx) => (ctx.prev === 'class' && ctx.next === '{'), className: 'cm-typ' }
|
|
252
289
|
],
|
|
253
290
|
typescript: [
|
|
254
|
-
{ test: (ctx) => ctx.scope && (ctx.token !== ',' && ctx.scope.type ==
|
|
255
|
-
{
|
|
256
|
-
|
|
257
|
-
|
|
291
|
+
{ test: (ctx) => ctx.scope && (ctx.token !== ',' && ctx.scope.type == 'enum'), className: 'cm-enu' },
|
|
292
|
+
{
|
|
293
|
+
test: (ctx) => (ctx.prev === ':' && ctx.next !== undefined && isLetter(ctx.token))
|
|
294
|
+
|| (ctx.prev === 'interface' && ctx.next === '{') || (ctx.prev === 'enum' && ctx.next === '{'),
|
|
295
|
+
className: 'cm-typ'
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
test: (ctx) => (ctx.prev === 'class' && ctx.next === '{') || (ctx.prev === 'class' && ctx.next === '<')
|
|
299
|
+
|| (ctx.prev === 'new' && ctx.next === '(') || (ctx.prev === 'new' && ctx.next === '<'),
|
|
300
|
+
className: 'cm-typ'
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
test: (ctx, editor) => ctx.token !== ',' && editor._enclosedByTokens(ctx.token, ctx.tokenIndex, '<', '>'),
|
|
304
|
+
className: 'cm-typ'
|
|
305
|
+
}
|
|
258
306
|
],
|
|
259
307
|
cpp: [
|
|
260
|
-
{ test: (ctx) => ctx.scope && (ctx.token !== ',' && ctx.scope.type ==
|
|
261
|
-
{ test: (ctx) => ctx.isEnumValueSymbol(ctx.token), className:
|
|
262
|
-
{
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
308
|
+
{ test: (ctx) => ctx.scope && (ctx.token !== ',' && ctx.scope.type == 'enum'), className: 'cm-enu' },
|
|
309
|
+
{ test: (ctx) => ctx.isEnumValueSymbol(ctx.token), className: 'cm-enu' },
|
|
310
|
+
{
|
|
311
|
+
test: (ctx) => (ctx.prev === 'class' && ctx.next === '{') || (ctx.prev === 'struct' && ctx.next === '{'),
|
|
312
|
+
className: 'cm-typ'
|
|
313
|
+
},
|
|
314
|
+
{ test: (ctx) => ctx.prev === '<' && (ctx.next === '>' || ctx.next === '*'), className: 'cm-typ' }, // Defining template type in C++
|
|
315
|
+
{ test: (ctx) => ctx.next === '::' || (ctx.prev === '::' && ctx.next !== '('), className: 'cm-typ' }, // C++ Class
|
|
316
|
+
{ test: (ctx) => ctx.isClassSymbol(ctx.token) || ctx.isStructSymbol(ctx.token), className: 'cm-typ' }
|
|
266
317
|
],
|
|
267
318
|
wgsl: [
|
|
268
|
-
{ test: (ctx) => ctx.prev === '>' && (!ctx.next || ctx.next === '{'), className:
|
|
269
|
-
{
|
|
270
|
-
|
|
319
|
+
{ test: (ctx) => ctx.prev === '>' && (!ctx.next || ctx.next === '{'), className: 'cm-typ' }, // Function return type
|
|
320
|
+
{
|
|
321
|
+
test: (ctx) => (ctx.prev === ':' && ctx.next !== undefined) || (ctx.prev === 'struct' && ctx.next === '{'),
|
|
322
|
+
className: 'cm-typ'
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
test: (ctx, editor) => ctx.token !== ',' && editor._enclosedByTokens(ctx.token, ctx.tokenIndex, '<', '>'),
|
|
326
|
+
className: 'cm-typ'
|
|
327
|
+
}
|
|
271
328
|
],
|
|
272
329
|
css: [
|
|
273
|
-
{
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
330
|
+
{
|
|
331
|
+
test: (ctx) => (ctx.prev == '.' || ctx.prev == '::' || (ctx.prev == ':' && ctx.next == '{')
|
|
332
|
+
|| (ctx.token[0] == '#' && ctx.prev != ':')),
|
|
333
|
+
className: 'cm-kwd'
|
|
334
|
+
},
|
|
335
|
+
{ test: (ctx) => ctx.prev === ':' && (ctx.next === ';' || ctx.next === '!important'),
|
|
336
|
+
className: 'cm-str' }, // CSS value
|
|
337
|
+
{ test: (ctx) => (ctx.prev === undefined || ctx.prev === '{' || ctx.prev === ';') && ctx.next === ':',
|
|
338
|
+
className: 'cm-typ' }, // CSS attribute
|
|
339
|
+
{ test: (ctx) => ctx.prev === '(' && ctx.next === ')' && ctx.token.startsWith('--'),
|
|
340
|
+
className: 'cm-typ' } // CSS vars
|
|
277
341
|
],
|
|
278
342
|
batch: [
|
|
279
|
-
{ test: (ctx) => ctx.token === '@' || ctx.prev === ':' || ctx.prev === '@', className:
|
|
343
|
+
{ test: (ctx) => ctx.token === '@' || ctx.prev === ':' || ctx.prev === '@', className: 'cm-kwd' }
|
|
280
344
|
],
|
|
281
345
|
markdown: [
|
|
282
|
-
{ test: (ctx) => ctx.isFirstToken && ctx.token.replaceAll('#', '').length != ctx.token.length,
|
|
346
|
+
{ test: (ctx) => ctx.isFirstToken && ctx.token.replaceAll('#', '').length != ctx.token.length,
|
|
347
|
+
action: (ctx, editor) => editor._markdownHeader = true, className: 'cm-kwd' }
|
|
283
348
|
],
|
|
284
349
|
php: [
|
|
285
|
-
{ test: (ctx) => ctx.token.startsWith('$'), className:
|
|
286
|
-
{
|
|
350
|
+
{ test: (ctx) => ctx.token.startsWith('$'), className: 'cm-var' },
|
|
351
|
+
{
|
|
352
|
+
test: (ctx) => (ctx.prev === 'class' && (ctx.next === '{' || ctx.next === 'implements'))
|
|
353
|
+
|| (ctx.prev === 'enum'),
|
|
354
|
+
className: 'cm-typ'
|
|
355
|
+
}
|
|
287
356
|
],
|
|
288
357
|
post_common: [
|
|
289
|
-
{
|
|
290
|
-
|
|
358
|
+
{
|
|
359
|
+
test: (ctx) => isLetter(ctx.token) && (ctx.token[0] != '@') && (ctx.token[0] != ',') && (ctx.next === '('),
|
|
360
|
+
className: 'cm-mtd'
|
|
361
|
+
}
|
|
362
|
+
]
|
|
291
363
|
};
|
|
292
364
|
/**
|
|
293
365
|
* @class CodeEditor
|
|
@@ -331,7 +403,7 @@ class CodeEditor {
|
|
|
331
403
|
explorerArea;
|
|
332
404
|
code;
|
|
333
405
|
gutter;
|
|
334
|
-
xPadding =
|
|
406
|
+
xPadding = '0px';
|
|
335
407
|
hScrollbar;
|
|
336
408
|
vScrollbar;
|
|
337
409
|
codeScroller;
|
|
@@ -385,8 +457,8 @@ class CodeEditor {
|
|
|
385
457
|
useAutoComplete = true;
|
|
386
458
|
allowClosingTabs = true;
|
|
387
459
|
allowLoadingFiles = true;
|
|
388
|
-
highlight =
|
|
389
|
-
explorerName =
|
|
460
|
+
highlight = 'Plain Text';
|
|
461
|
+
explorerName = 'EXPLORER';
|
|
390
462
|
newTabOptions;
|
|
391
463
|
customSuggestions = [];
|
|
392
464
|
// Editor callbacks
|
|
@@ -408,7 +480,7 @@ class CodeEditor {
|
|
|
408
480
|
_lastProcessedCursorIndex = null;
|
|
409
481
|
_lastMaxLineLength = undefined;
|
|
410
482
|
_lastMouseDown = 0;
|
|
411
|
-
_lastTextFound =
|
|
483
|
+
_lastTextFound = '';
|
|
412
484
|
_lastBaseareaWidth = undefined;
|
|
413
485
|
_blockCommentCache = [];
|
|
414
486
|
_pendingString = undefined;
|
|
@@ -471,7 +543,7 @@ class CodeEditor {
|
|
|
471
543
|
this.onSelectTab = options.onSelectTab;
|
|
472
544
|
// File explorer
|
|
473
545
|
if (this.useFileExplorer) {
|
|
474
|
-
let [explorerArea, editorArea] = area.split({ sizes: [
|
|
546
|
+
let [explorerArea, editorArea] = area.split({ sizes: ['15%', '85%'] });
|
|
475
547
|
// explorerArea.setLimitBox( 180, 20, 512 );
|
|
476
548
|
this.explorerArea = explorerArea;
|
|
477
549
|
let panel = new LX.Panel();
|
|
@@ -512,7 +584,7 @@ class CodeEditor {
|
|
|
512
584
|
area = editorArea;
|
|
513
585
|
}
|
|
514
586
|
this.baseArea = area;
|
|
515
|
-
this.area = new LX.Area({ className:
|
|
587
|
+
this.area = new LX.Area({ className: 'lexcodeeditor', height: '100%', skipAppend: true });
|
|
516
588
|
if (!this.skipTabs) {
|
|
517
589
|
this.tabs = this.area.addTabs({ onclose: (name) => {
|
|
518
590
|
delete this.openedTabs[name];
|
|
@@ -521,7 +593,7 @@ class CodeEditor {
|
|
|
521
593
|
LX.removeClas(this.cursorsDOM, 'show');
|
|
522
594
|
}
|
|
523
595
|
} });
|
|
524
|
-
LX.addClass(this.tabs.root.parentElement,
|
|
596
|
+
LX.addClass(this.tabs.root.parentElement, 'rounded-t-lg');
|
|
525
597
|
if (!this.disableEdition) {
|
|
526
598
|
this.tabs.root.parentElement.addEventListener('dblclick', (e) => {
|
|
527
599
|
if (options.allowAddScripts ?? true) {
|
|
@@ -535,12 +607,13 @@ class CodeEditor {
|
|
|
535
607
|
else {
|
|
536
608
|
this.codeArea = new LX.Area({ skipAppend: true });
|
|
537
609
|
this.area.attach(this.codeArea);
|
|
538
|
-
const loadFileButton = LX.makeElement(
|
|
539
|
-
bottom:
|
|
610
|
+
const loadFileButton = LX.makeElement('button', 'grid absolute self-center z-100 p-3 rounded-full bg-secondary hover:bg-tertiary cursor-pointer border', LX.makeIcon('FolderOpen').innerHTML, this.area, {
|
|
611
|
+
bottom: '8px'
|
|
540
612
|
});
|
|
541
|
-
loadFileButton.addEventListener(
|
|
613
|
+
loadFileButton.addEventListener('click', (e) => {
|
|
542
614
|
const dropdownOptions = [];
|
|
543
|
-
for (const [key, value] of [...Object.entries(this.loadedTabs).slice(1),
|
|
615
|
+
for (const [key, value] of [...Object.entries(this.loadedTabs).slice(1),
|
|
616
|
+
...Object.entries(this._tabStorage)]) {
|
|
544
617
|
const icon = this._getFileIcon(key);
|
|
545
618
|
const classes = icon ? icon.split(' ') : [];
|
|
546
619
|
dropdownOptions.push({
|
|
@@ -552,11 +625,11 @@ class CodeEditor {
|
|
|
552
625
|
}
|
|
553
626
|
});
|
|
554
627
|
}
|
|
555
|
-
new LX.DropdownMenu(loadFileButton, dropdownOptions, { side:
|
|
628
|
+
new LX.DropdownMenu(loadFileButton, dropdownOptions, { side: 'top', align: 'center' });
|
|
556
629
|
});
|
|
557
630
|
}
|
|
558
631
|
this.codeArea.root.classList.add('lexcodearea');
|
|
559
|
-
const codeResizeObserver = new ResizeObserver(entries => {
|
|
632
|
+
const codeResizeObserver = new ResizeObserver((entries) => {
|
|
560
633
|
if (!this.code) {
|
|
561
634
|
return;
|
|
562
635
|
}
|
|
@@ -566,13 +639,13 @@ class CodeEditor {
|
|
|
566
639
|
// Full editor
|
|
567
640
|
area.root.classList.add('codebasearea');
|
|
568
641
|
const observer = new MutationObserver((e) => {
|
|
569
|
-
if (e[0].attributeName ==
|
|
642
|
+
if (e[0].attributeName == 'style') {
|
|
570
643
|
this.resize();
|
|
571
644
|
}
|
|
572
645
|
});
|
|
573
646
|
observer.observe(area.root.parentNode, {
|
|
574
647
|
attributes: true,
|
|
575
|
-
attributeFilter: ['class', 'style']
|
|
648
|
+
attributeFilter: ['class', 'style']
|
|
576
649
|
});
|
|
577
650
|
this.root = this.area.root;
|
|
578
651
|
this.root.tabIndex = -1;
|
|
@@ -583,7 +656,7 @@ class CodeEditor {
|
|
|
583
656
|
this.root.addEventListener('focusout', this.processFocus.bind(this, false));
|
|
584
657
|
}
|
|
585
658
|
else {
|
|
586
|
-
this.root.classList.add(
|
|
659
|
+
this.root.classList.add('disabled');
|
|
587
660
|
}
|
|
588
661
|
this.root.addEventListener('mousedown', this.processMouse.bind(this));
|
|
589
662
|
this.root.addEventListener('mouseup', this.processMouse.bind(this));
|
|
@@ -603,7 +676,7 @@ class CodeEditor {
|
|
|
603
676
|
// Store here selections per cursor
|
|
604
677
|
this.selections = {};
|
|
605
678
|
// Css char synchronization
|
|
606
|
-
this.xPadding = CodeEditor.LINE_GUTTER_WIDTH +
|
|
679
|
+
this.xPadding = CodeEditor.LINE_GUTTER_WIDTH + 'px';
|
|
607
680
|
// Add main cursor
|
|
608
681
|
this._addCursor(0, 0, true, true);
|
|
609
682
|
// Scroll stuff
|
|
@@ -625,8 +698,10 @@ class CodeEditor {
|
|
|
625
698
|
// Scroll down...
|
|
626
699
|
if (scrollTop > lastScrollTopValue) {
|
|
627
700
|
if (this.visibleLinesViewport.y < (this.code.lines.length - 1)) {
|
|
628
|
-
const totalLinesInViewport = ((this.codeScroller.offsetHeight) / this.lineHeight)
|
|
629
|
-
|
|
701
|
+
const totalLinesInViewport = ((this.codeScroller.offsetHeight) / this.lineHeight)
|
|
702
|
+
| 0;
|
|
703
|
+
const scrollDownBoundary = (Math.max(this.visibleLinesViewport.y - totalLinesInViewport, 0) - 1)
|
|
704
|
+
* this.lineHeight;
|
|
630
705
|
if (scrollTop >= scrollDownBoundary) {
|
|
631
706
|
this.processLines(CodeEditor.UPDATE_VISIBLE_LINES);
|
|
632
707
|
}
|
|
@@ -650,7 +725,7 @@ class CodeEditor {
|
|
|
650
725
|
if (e.ctrlKey) {
|
|
651
726
|
e.preventDefault();
|
|
652
727
|
e.stopPropagation();
|
|
653
|
-
|
|
728
|
+
e.deltaY > 0.0 ? this._decreaseFontSize() : this._increaseFontSize();
|
|
654
729
|
}
|
|
655
730
|
});
|
|
656
731
|
this.codeScroller.addEventListener('wheel', (e) => {
|
|
@@ -666,7 +741,7 @@ class CodeEditor {
|
|
|
666
741
|
{
|
|
667
742
|
// This is only the container, line numbers are in the same line div
|
|
668
743
|
this.gutter = document.createElement('div');
|
|
669
|
-
this.gutter.className =
|
|
744
|
+
this.gutter.className = 'lexcodegutter';
|
|
670
745
|
area.attach(this.gutter);
|
|
671
746
|
// Add custom vertical scroll bar
|
|
672
747
|
this.vScrollbar = new ScrollBar(this, ScrollBar.SCROLLBAR_VERTICAL);
|
|
@@ -680,22 +755,25 @@ class CodeEditor {
|
|
|
680
755
|
// Add autocomplete box
|
|
681
756
|
{
|
|
682
757
|
this.autocomplete = document.createElement('div');
|
|
683
|
-
this.autocomplete.className =
|
|
758
|
+
this.autocomplete.className = 'autocomplete';
|
|
684
759
|
this.codeArea.attach(this.autocomplete);
|
|
685
760
|
}
|
|
686
761
|
// Add search box
|
|
687
762
|
{
|
|
688
763
|
const box = document.createElement('div');
|
|
689
|
-
box.className =
|
|
764
|
+
box.className = 'searchbox';
|
|
690
765
|
const searchPanel = new LX.Panel();
|
|
691
766
|
box.appendChild(searchPanel.root);
|
|
692
767
|
searchPanel.sameLine(4);
|
|
693
|
-
searchPanel.addText(null,
|
|
694
|
-
searchPanel.addButton(null,
|
|
695
|
-
|
|
696
|
-
searchPanel.addButton(null,
|
|
768
|
+
searchPanel.addText(null, '', null, { placeholder: 'Find', inputClass: 'bg-secondary' });
|
|
769
|
+
searchPanel.addButton(null, 'up', () => this.search(null, true), { icon: 'ArrowUp',
|
|
770
|
+
title: 'Previous Match', tooltip: true });
|
|
771
|
+
searchPanel.addButton(null, 'down', () => this.search(), { icon: 'ArrowDown', title: 'Next Match',
|
|
772
|
+
tooltip: true });
|
|
773
|
+
searchPanel.addButton(null, 'x', this.hideSearchBox.bind(this), { icon: 'X', title: 'Close',
|
|
774
|
+
tooltip: true });
|
|
697
775
|
const searchInput = box.querySelector('input');
|
|
698
|
-
searchInput?.addEventListener('keyup', e => {
|
|
776
|
+
searchInput?.addEventListener('keyup', (e) => {
|
|
699
777
|
if (e.key == 'Escape')
|
|
700
778
|
this.hideSearchBox();
|
|
701
779
|
else if (e.key == 'Enter')
|
|
@@ -707,15 +785,16 @@ class CodeEditor {
|
|
|
707
785
|
// Add search LINE box
|
|
708
786
|
{
|
|
709
787
|
const box = document.createElement('div');
|
|
710
|
-
box.className =
|
|
788
|
+
box.className = 'searchbox';
|
|
711
789
|
const searchPanel = new LX.Panel();
|
|
712
790
|
box.appendChild(searchPanel.root);
|
|
713
791
|
searchPanel.sameLine(2);
|
|
714
|
-
searchPanel.addText(null,
|
|
715
|
-
input.value =
|
|
792
|
+
searchPanel.addText(null, '', (value) => {
|
|
793
|
+
input.value = ':' + value.replaceAll(':', '');
|
|
716
794
|
this.goToLine(input.value.slice(1));
|
|
717
|
-
}, { placeholder:
|
|
718
|
-
searchPanel.addButton(null,
|
|
795
|
+
}, { placeholder: 'Go to line', trigger: 'input' });
|
|
796
|
+
searchPanel.addButton(null, 'x', this.hideSearchLineBox.bind(this), { icon: 'X', title: 'Close',
|
|
797
|
+
tooltip: true });
|
|
719
798
|
let input = box.querySelector('input');
|
|
720
799
|
input.addEventListener('keyup', (e) => {
|
|
721
800
|
if (e.key == 'Escape')
|
|
@@ -728,7 +807,7 @@ class CodeEditor {
|
|
|
728
807
|
// Add code-sizer
|
|
729
808
|
{
|
|
730
809
|
this.codeSizer = document.createElement('div');
|
|
731
|
-
this.codeSizer.className =
|
|
810
|
+
this.codeSizer.className = 'code-sizer pseudoparent-tabs';
|
|
732
811
|
// Append all childs
|
|
733
812
|
while (this.codeScroller.firstChild) {
|
|
734
813
|
this.codeSizer.appendChild(this.codeScroller.firstChild);
|
|
@@ -744,22 +823,30 @@ class CodeEditor {
|
|
|
744
823
|
};
|
|
745
824
|
// Code
|
|
746
825
|
this.pairKeys = {
|
|
747
|
-
"
|
|
826
|
+
'"': '"',
|
|
748
827
|
"'": "'",
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
828
|
+
'(': ')',
|
|
829
|
+
'{': '}',
|
|
830
|
+
'[': ']'
|
|
752
831
|
};
|
|
753
832
|
this.stringKeys = {
|
|
754
|
-
"
|
|
833
|
+
'@"': '"',
|
|
755
834
|
"@'": "'"
|
|
756
835
|
};
|
|
757
836
|
// Scan tokens..
|
|
758
837
|
// setInterval( this.scanWordSuggestions.bind( this ), 2000 );
|
|
759
838
|
this.specialKeys = [
|
|
760
|
-
'Backspace',
|
|
761
|
-
'
|
|
762
|
-
'
|
|
839
|
+
'Backspace',
|
|
840
|
+
'Enter',
|
|
841
|
+
'ArrowUp',
|
|
842
|
+
'ArrowDown',
|
|
843
|
+
'ArrowRight',
|
|
844
|
+
'ArrowLeft',
|
|
845
|
+
'Delete',
|
|
846
|
+
'Home',
|
|
847
|
+
'End',
|
|
848
|
+
'Tab',
|
|
849
|
+
'Escape'
|
|
763
850
|
];
|
|
764
851
|
// Convert reserved word arrays to maps so we can search tokens faster
|
|
765
852
|
if (!CodeEditor._staticReady) {
|
|
@@ -771,8 +858,9 @@ class CodeEditor {
|
|
|
771
858
|
CodeEditor.types[lang] = new Set(CodeEditor.types[lang]);
|
|
772
859
|
for (let lang in CodeEditor.builtIn)
|
|
773
860
|
CodeEditor.builtIn[lang] = new Set(CodeEditor.builtIn[lang]);
|
|
774
|
-
for (let lang in CodeEditor.statements)
|
|
861
|
+
for (let lang in CodeEditor.statements) {
|
|
775
862
|
CodeEditor.statements[lang] = new Set(CodeEditor.statements[lang]);
|
|
863
|
+
}
|
|
776
864
|
for (let lang in CodeEditor.symbols)
|
|
777
865
|
CodeEditor.symbols[lang] = new Set(CodeEditor.symbols[lang]);
|
|
778
866
|
CodeEditor._staticReady = true;
|
|
@@ -780,10 +868,12 @@ class CodeEditor {
|
|
|
780
868
|
// Action keys
|
|
781
869
|
{
|
|
782
870
|
this.action('Escape', false, (ln, cursor, e) => {
|
|
783
|
-
if (this.hideAutoCompleteBox())
|
|
871
|
+
if (this.hideAutoCompleteBox()) {
|
|
784
872
|
return;
|
|
785
|
-
|
|
873
|
+
}
|
|
874
|
+
if (this.hideSearchBox()) {
|
|
786
875
|
return;
|
|
876
|
+
}
|
|
787
877
|
// Remove selections and cursors
|
|
788
878
|
this.endSelection();
|
|
789
879
|
this._removeSecondaryCursors();
|
|
@@ -912,23 +1002,28 @@ class CodeEditor {
|
|
|
912
1002
|
this.action('End', false, (ln, cursor, e) => {
|
|
913
1003
|
if ((e.shiftKey || e._shiftKey) && !e.cancelShift) {
|
|
914
1004
|
var string = this.code.lines[ln].substring(cursor.position);
|
|
915
|
-
if (!cursor.selection)
|
|
1005
|
+
if (!cursor.selection) {
|
|
916
1006
|
this.startSelection(cursor);
|
|
917
|
-
|
|
1007
|
+
}
|
|
1008
|
+
if (cursor.selection.sameLine()) {
|
|
918
1009
|
cursor.selection.selectInline(cursor, cursor.position, cursor.line, this.measureString(string));
|
|
1010
|
+
}
|
|
919
1011
|
else {
|
|
920
1012
|
this.resetCursorPos(CodeEditor.CURSOR_LEFT, cursor);
|
|
921
1013
|
this.cursorToString(cursor, this.code.lines[ln]);
|
|
922
1014
|
this._processSelection(cursor, e);
|
|
923
1015
|
}
|
|
924
1016
|
}
|
|
925
|
-
else if (!e.keepSelection)
|
|
1017
|
+
else if (!e.keepSelection) {
|
|
926
1018
|
this.endSelection();
|
|
1019
|
+
}
|
|
927
1020
|
this.resetCursorPos(CodeEditor.CURSOR_LEFT, cursor);
|
|
928
1021
|
this.cursorToString(cursor, this.code.lines[ln]);
|
|
929
|
-
var viewportSizeX = (this.codeScroller.clientWidth + this.getScrollLeft())
|
|
930
|
-
|
|
1022
|
+
var viewportSizeX = (this.codeScroller.clientWidth + this.getScrollLeft())
|
|
1023
|
+
- CodeEditor.LINE_GUTTER_WIDTH; // Gutter offset
|
|
1024
|
+
if ((cursor.position * this.charWidth) >= viewportSizeX) {
|
|
931
1025
|
this.setScrollLeft(this.code.lines[ln].length * this.charWidth);
|
|
1026
|
+
}
|
|
932
1027
|
// Merge cursors
|
|
933
1028
|
this.mergeCursors(ln);
|
|
934
1029
|
});
|
|
@@ -945,7 +1040,7 @@ class CodeEditor {
|
|
|
945
1040
|
this._addUndoStep(cursor, true);
|
|
946
1041
|
var _c0 = this.getCharAtPos(cursor, -1);
|
|
947
1042
|
var _c1 = this.getCharAtPos(cursor);
|
|
948
|
-
this.code.lines.splice(cursor.line + 1, 0,
|
|
1043
|
+
this.code.lines.splice(cursor.line + 1, 0, '');
|
|
949
1044
|
this.code.lines[cursor.line + 1] = this.code.lines[ln].substr(cursor.position); // new line (below)
|
|
950
1045
|
this.code.lines[ln] = this.code.lines[ln].substr(0, cursor.position); // line above
|
|
951
1046
|
this.lineDown(cursor, true);
|
|
@@ -953,9 +1048,9 @@ class CodeEditor {
|
|
|
953
1048
|
var spaces = firstNonspaceIndex(this.code.lines[ln]);
|
|
954
1049
|
var tabs = Math.floor(spaces / this.tabSpaces);
|
|
955
1050
|
if (_c0 == '{' && _c1 == '}') {
|
|
956
|
-
this.code.lines.splice(cursor.line, 0,
|
|
1051
|
+
this.code.lines.splice(cursor.line, 0, '');
|
|
957
1052
|
this._addSpaceTabs(cursor, tabs + 1);
|
|
958
|
-
this.code.lines[cursor.line + 1] =
|
|
1053
|
+
this.code.lines[cursor.line + 1] = ' '.repeat(spaces) + this.code.lines[cursor.line + 1];
|
|
959
1054
|
}
|
|
960
1055
|
else {
|
|
961
1056
|
this._addSpaceTabs(cursor, tabs);
|
|
@@ -966,8 +1061,9 @@ class CodeEditor {
|
|
|
966
1061
|
// Move cursor..
|
|
967
1062
|
if (!this.isAutoCompleteActive) {
|
|
968
1063
|
if (e.shiftKey) {
|
|
969
|
-
if (!cursor.selection)
|
|
1064
|
+
if (!cursor.selection) {
|
|
970
1065
|
this.startSelection(cursor);
|
|
1066
|
+
}
|
|
971
1067
|
this.lineUp(cursor);
|
|
972
1068
|
var letter = this.getCharAtPos(cursor);
|
|
973
1069
|
if (!letter) {
|
|
@@ -993,8 +1089,9 @@ class CodeEditor {
|
|
|
993
1089
|
// Move cursor..
|
|
994
1090
|
if (!this.isAutoCompleteActive) {
|
|
995
1091
|
if (e.shiftKey) {
|
|
996
|
-
if (!cursor.selection)
|
|
1092
|
+
if (!cursor.selection) {
|
|
997
1093
|
this.startSelection(cursor);
|
|
1094
|
+
}
|
|
998
1095
|
}
|
|
999
1096
|
else {
|
|
1000
1097
|
this.endSelection();
|
|
@@ -1017,8 +1114,9 @@ class CodeEditor {
|
|
|
1017
1114
|
});
|
|
1018
1115
|
this.action('ArrowLeft', false, (ln, cursor, e) => {
|
|
1019
1116
|
// Nothing to do..
|
|
1020
|
-
if (cursor.line == 0 && cursor.position == 0)
|
|
1117
|
+
if (cursor.line == 0 && cursor.position == 0) {
|
|
1021
1118
|
return;
|
|
1119
|
+
}
|
|
1022
1120
|
if (e.metaKey) { // Apple devices (Command)
|
|
1023
1121
|
e.preventDefault();
|
|
1024
1122
|
this.actions['Home'].callback(ln, cursor, e);
|
|
@@ -1039,14 +1137,17 @@ class CodeEditor {
|
|
|
1039
1137
|
var substr = word.substr(0, diff);
|
|
1040
1138
|
// Selections...
|
|
1041
1139
|
if (e.shiftKey) {
|
|
1042
|
-
if (!cursor.selection)
|
|
1140
|
+
if (!cursor.selection) {
|
|
1043
1141
|
this.startSelection(cursor);
|
|
1142
|
+
}
|
|
1044
1143
|
}
|
|
1045
|
-
else
|
|
1144
|
+
else {
|
|
1046
1145
|
this.endSelection();
|
|
1146
|
+
}
|
|
1047
1147
|
this.cursorToString(cursor, substr, true);
|
|
1048
|
-
if (e.shiftKey)
|
|
1148
|
+
if (e.shiftKey) {
|
|
1049
1149
|
this._processSelection(cursor, e);
|
|
1150
|
+
}
|
|
1050
1151
|
}
|
|
1051
1152
|
else {
|
|
1052
1153
|
var letter = this.getCharAtPos(cursor, -1);
|
|
@@ -1060,8 +1161,9 @@ class CodeEditor {
|
|
|
1060
1161
|
else {
|
|
1061
1162
|
if (!cursor.selection) {
|
|
1062
1163
|
this.cursorToLeft(letter, cursor);
|
|
1063
|
-
if (this.useAutoComplete && this.isAutoCompleteActive)
|
|
1164
|
+
if (this.useAutoComplete && this.isAutoCompleteActive) {
|
|
1064
1165
|
this.showAutoCompleteBox('foo', cursor);
|
|
1166
|
+
}
|
|
1065
1167
|
}
|
|
1066
1168
|
else {
|
|
1067
1169
|
cursor.selection.invertIfNecessary();
|
|
@@ -1087,16 +1189,15 @@ class CodeEditor {
|
|
|
1087
1189
|
});
|
|
1088
1190
|
this.action('ArrowRight', false, (ln, cursor, e) => {
|
|
1089
1191
|
// Nothing to do..
|
|
1090
|
-
if (cursor.line == this.code.lines.length - 1
|
|
1091
|
-
cursor.position == this.code.lines[cursor.line].length)
|
|
1192
|
+
if (cursor.line == this.code.lines.length - 1
|
|
1193
|
+
&& cursor.position == this.code.lines[cursor.line].length) {
|
|
1092
1194
|
return;
|
|
1093
|
-
|
|
1094
|
-
|
|
1195
|
+
}
|
|
1196
|
+
if (e.metaKey) { // Apple devices (Command)
|
|
1095
1197
|
e.preventDefault();
|
|
1096
1198
|
this.actions['End'].callback(ln, cursor, e);
|
|
1097
1199
|
}
|
|
1098
|
-
else if (e.ctrlKey) // Next word
|
|
1099
|
-
{
|
|
1200
|
+
else if (e.ctrlKey) { // Next word
|
|
1100
1201
|
// Get next word
|
|
1101
1202
|
const [word, from, to] = this.getWordAtPos(cursor);
|
|
1102
1203
|
// If no length, we change line..
|
|
@@ -1106,31 +1207,36 @@ class CodeEditor {
|
|
|
1106
1207
|
var substr = word.substr(diff);
|
|
1107
1208
|
// Selections...
|
|
1108
1209
|
if (e.shiftKey) {
|
|
1109
|
-
if (!cursor.selection)
|
|
1210
|
+
if (!cursor.selection) {
|
|
1110
1211
|
this.startSelection(cursor);
|
|
1212
|
+
}
|
|
1111
1213
|
}
|
|
1112
|
-
else
|
|
1214
|
+
else {
|
|
1113
1215
|
this.endSelection();
|
|
1216
|
+
}
|
|
1114
1217
|
this.cursorToString(cursor, substr);
|
|
1115
|
-
if (e.shiftKey)
|
|
1218
|
+
if (e.shiftKey) {
|
|
1116
1219
|
this._processSelection(cursor, e);
|
|
1220
|
+
}
|
|
1117
1221
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1222
|
+
// Next char
|
|
1223
|
+
else {
|
|
1120
1224
|
var letter = this.getCharAtPos(cursor);
|
|
1121
1225
|
if (letter) {
|
|
1122
1226
|
// Selecting chars
|
|
1123
1227
|
if (e.shiftKey) {
|
|
1124
|
-
if (!cursor.selection)
|
|
1228
|
+
if (!cursor.selection) {
|
|
1125
1229
|
this.startSelection(cursor);
|
|
1230
|
+
}
|
|
1126
1231
|
this.cursorToRight(letter, cursor);
|
|
1127
1232
|
this._processSelection(cursor, e, false, CodeEditor.SELECTION_X);
|
|
1128
1233
|
}
|
|
1129
1234
|
else {
|
|
1130
1235
|
if (!cursor.selection) {
|
|
1131
1236
|
this.cursorToRight(letter, cursor);
|
|
1132
|
-
if (this.useAutoComplete && this.isAutoCompleteActive)
|
|
1237
|
+
if (this.useAutoComplete && this.isAutoCompleteActive) {
|
|
1133
1238
|
this.showAutoCompleteBox('foo', cursor);
|
|
1239
|
+
}
|
|
1134
1240
|
}
|
|
1135
1241
|
else {
|
|
1136
1242
|
cursor.selection.invertIfNecessary();
|
|
@@ -1165,22 +1271,22 @@ class CodeEditor {
|
|
|
1165
1271
|
if (this.statusPanel) {
|
|
1166
1272
|
area.attach(this.statusPanel);
|
|
1167
1273
|
}
|
|
1168
|
-
if (document.fonts.status ==
|
|
1274
|
+
if (document.fonts.status == 'loading') {
|
|
1169
1275
|
await document.fonts.ready;
|
|
1170
1276
|
}
|
|
1171
1277
|
// Load any font size from local storage
|
|
1172
|
-
const savedFontSize = window.localStorage.getItem(
|
|
1278
|
+
const savedFontSize = window.localStorage.getItem('lexcodeeditor-font-size');
|
|
1173
1279
|
if (savedFontSize) {
|
|
1174
1280
|
this._setFontSize(parseInt(savedFontSize));
|
|
1175
1281
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1282
|
+
// Use default size
|
|
1283
|
+
else {
|
|
1178
1284
|
const r = document.querySelector(':root');
|
|
1179
1285
|
const s = getComputedStyle(r);
|
|
1180
|
-
this.fontSize = parseInt(s.getPropertyValue(
|
|
1286
|
+
this.fontSize = parseInt(s.getPropertyValue('--code-editor-font-size'));
|
|
1181
1287
|
this.charWidth = this._measureChar();
|
|
1182
1288
|
}
|
|
1183
|
-
LX.emitSignal(
|
|
1289
|
+
LX.emitSignal('@font-size', this.fontSize);
|
|
1184
1290
|
// Get final sizes for editor elements based on Tabs and status bar offsets
|
|
1185
1291
|
LX.doAsync(() => {
|
|
1186
1292
|
this._verticalTopOffset = this.tabs?.root.getBoundingClientRect().height ?? 0;
|
|
@@ -1202,17 +1308,18 @@ class CodeEditor {
|
|
|
1202
1308
|
};
|
|
1203
1309
|
if (options.allowAddScripts ?? true) {
|
|
1204
1310
|
this.onCreateFile = options.onCreateFile;
|
|
1205
|
-
this.addTab(
|
|
1311
|
+
this.addTab('+', false, 'Create file');
|
|
1206
1312
|
}
|
|
1207
1313
|
if (options.files) {
|
|
1208
|
-
console.assert(options.files.constructor === Array,
|
|
1314
|
+
console.assert(options.files.constructor === Array, '_files_ must be an Array!');
|
|
1209
1315
|
const numFiles = options.files.length;
|
|
1210
|
-
const loadAsync =
|
|
1316
|
+
const loadAsync = options.filesAsync !== undefined;
|
|
1211
1317
|
let filesLoaded = 0;
|
|
1212
1318
|
for (let url of options.files) {
|
|
1213
1319
|
const finalUrl = url.constructor === Array ? url[0] : url;
|
|
1214
1320
|
const finalFileName = url.constructor === Array ? url[1] : undefined;
|
|
1215
|
-
await this.loadFile(finalUrl, { filename: finalFileName, async: loadAsync,
|
|
1321
|
+
await this.loadFile(finalUrl, { filename: finalFileName, async: loadAsync,
|
|
1322
|
+
callback: (name, text) => {
|
|
1216
1323
|
filesLoaded++;
|
|
1217
1324
|
if (filesLoaded == numFiles) {
|
|
1218
1325
|
onLoadAll();
|
|
@@ -1225,14 +1332,16 @@ class CodeEditor {
|
|
|
1225
1332
|
}
|
|
1226
1333
|
else {
|
|
1227
1334
|
if (options.defaultTab ?? true) {
|
|
1228
|
-
this.addTab(options.name ||
|
|
1335
|
+
this.addTab(options.name || 'untitled', true, options.title, {
|
|
1336
|
+
language: options.highlight ?? 'Plain Text'
|
|
1337
|
+
});
|
|
1229
1338
|
}
|
|
1230
1339
|
onLoadAll();
|
|
1231
1340
|
}
|
|
1232
1341
|
}
|
|
1233
1342
|
// Clear signals
|
|
1234
1343
|
clear() {
|
|
1235
|
-
console.assert(this.rightStatusPanel && this.leftStatusPanel,
|
|
1344
|
+
console.assert(this.rightStatusPanel && this.leftStatusPanel, 'No panels to clear.');
|
|
1236
1345
|
this.rightStatusPanel.clear();
|
|
1237
1346
|
this.leftStatusPanel.clear();
|
|
1238
1347
|
}
|
|
@@ -1243,14 +1352,14 @@ class CodeEditor {
|
|
|
1243
1352
|
onKeyPressed(e) {
|
|
1244
1353
|
// Toggle visibility of the file explorer
|
|
1245
1354
|
if (e.key == 'b' && e.ctrlKey && this.useFileExplorer) {
|
|
1246
|
-
this.explorerArea.root.classList.toggle(
|
|
1355
|
+
this.explorerArea.root.classList.toggle('hidden');
|
|
1247
1356
|
if (this._lastBaseareaWidth) {
|
|
1248
1357
|
this.baseArea.root.style.width = this._lastBaseareaWidth;
|
|
1249
1358
|
delete this._lastBaseareaWidth;
|
|
1250
1359
|
}
|
|
1251
1360
|
else {
|
|
1252
1361
|
this._lastBaseareaWidth = this.baseArea.root.style.width;
|
|
1253
|
-
this.baseArea.root.style.width =
|
|
1362
|
+
this.baseArea.root.style.width = '100%';
|
|
1254
1363
|
}
|
|
1255
1364
|
}
|
|
1256
1365
|
}
|
|
@@ -1258,7 +1367,7 @@ class CodeEditor {
|
|
|
1258
1367
|
return this.code.lines.join(min ? ' ' : '\n');
|
|
1259
1368
|
}
|
|
1260
1369
|
// This can be used to empty all text...
|
|
1261
|
-
setText(text =
|
|
1370
|
+
setText(text = '', langString) {
|
|
1262
1371
|
let newLines = text.split('\n');
|
|
1263
1372
|
this.code.lines = [].concat(newLines);
|
|
1264
1373
|
this._removeSecondaryCursors();
|
|
@@ -1292,15 +1401,16 @@ class CodeEditor {
|
|
|
1292
1401
|
this.code.lines[lidx].slice(0, cursor.position),
|
|
1293
1402
|
firstLine
|
|
1294
1403
|
].join('');
|
|
1295
|
-
this.cursorToPosition(cursor,
|
|
1404
|
+
this.cursorToPosition(cursor, cursor.position + (firstLine?.length ?? 0));
|
|
1296
1405
|
// Enter next lines...
|
|
1297
1406
|
let _text = null;
|
|
1298
1407
|
for (var i = 0; i < newLines.length; ++i) {
|
|
1299
1408
|
_text = newLines[i];
|
|
1300
1409
|
this.cursorToLine(cursor, cursor.line++, true);
|
|
1301
1410
|
// Add remaining...
|
|
1302
|
-
if (i == (newLines.length - 1))
|
|
1411
|
+
if (i == (newLines.length - 1)) {
|
|
1303
1412
|
_text += remaining;
|
|
1413
|
+
}
|
|
1304
1414
|
this.code.lines.splice(1 + lidx + i, 0, _text);
|
|
1305
1415
|
}
|
|
1306
1416
|
if (_text)
|
|
@@ -1315,7 +1425,7 @@ class CodeEditor {
|
|
|
1315
1425
|
newLines[0],
|
|
1316
1426
|
this.code.lines[lidx].slice(cursor.position)
|
|
1317
1427
|
].join('');
|
|
1318
|
-
this.cursorToPosition(cursor,
|
|
1428
|
+
this.cursorToPosition(cursor, cursor.position + newLines[0].length);
|
|
1319
1429
|
this.processLine(lidx);
|
|
1320
1430
|
}
|
|
1321
1431
|
this.resize(CodeEditor.RESIZE_SCROLLBAR_H_V, undefined, () => {
|
|
@@ -1365,7 +1475,7 @@ class CodeEditor {
|
|
|
1365
1475
|
const filename = file;
|
|
1366
1476
|
const name = options.filename ?? filename.substring(filename.lastIndexOf('/') + 1);
|
|
1367
1477
|
if (options.async ?? false) {
|
|
1368
|
-
const text = await this._requestFileAsync(filename,
|
|
1478
|
+
const text = await this._requestFileAsync(filename, 'text');
|
|
1369
1479
|
_innerAddTab(text, name, options.filename ?? filename);
|
|
1370
1480
|
}
|
|
1371
1481
|
else {
|
|
@@ -1374,11 +1484,11 @@ class CodeEditor {
|
|
|
1374
1484
|
} });
|
|
1375
1485
|
}
|
|
1376
1486
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1487
|
+
// File Blob
|
|
1488
|
+
else {
|
|
1379
1489
|
const fr = new FileReader();
|
|
1380
1490
|
fr.readAsText(file);
|
|
1381
|
-
fr.onload = e => {
|
|
1491
|
+
fr.onload = (e) => {
|
|
1382
1492
|
const text = e.currentTarget.result;
|
|
1383
1493
|
_innerAddTab(text, file.name);
|
|
1384
1494
|
};
|
|
@@ -1386,8 +1496,9 @@ class CodeEditor {
|
|
|
1386
1496
|
}
|
|
1387
1497
|
_addUndoStep(cursor, force = false, deleteRedo = true) {
|
|
1388
1498
|
// Only the mainc cursor stores undo steps
|
|
1389
|
-
if (!cursor.isMain)
|
|
1499
|
+
if (!cursor.isMain) {
|
|
1390
1500
|
return;
|
|
1501
|
+
}
|
|
1391
1502
|
const d = new Date();
|
|
1392
1503
|
const current = d.getTime();
|
|
1393
1504
|
if (!force) {
|
|
@@ -1471,7 +1582,7 @@ class CodeEditor {
|
|
|
1471
1582
|
if (override) {
|
|
1472
1583
|
this.code.languageOverride = langString;
|
|
1473
1584
|
}
|
|
1474
|
-
this._updateDataInfoPanel(
|
|
1585
|
+
this._updateDataInfoPanel('@highlight', langString);
|
|
1475
1586
|
this.mustProcessLines = true;
|
|
1476
1587
|
const ext = langExtension ?? CodeEditor.languages[langString].ext;
|
|
1477
1588
|
const icon = this._getFileIcon(null, ext);
|
|
@@ -1481,14 +1592,13 @@ class CodeEditor {
|
|
|
1481
1592
|
tab.firstChild.remove();
|
|
1482
1593
|
console.assert(tab != undefined);
|
|
1483
1594
|
var iconEl;
|
|
1484
|
-
if (!icon.includes('.')) // Not a file
|
|
1485
|
-
{
|
|
1595
|
+
if (!icon.includes('.')) { // Not a file
|
|
1486
1596
|
const classes = icon.split(' ');
|
|
1487
1597
|
iconEl = LX.makeIcon(classes[0], { svgClass: classes.slice(0).join(' ') });
|
|
1488
1598
|
}
|
|
1489
1599
|
else {
|
|
1490
1600
|
iconEl = document.createElement('img');
|
|
1491
|
-
iconEl.src =
|
|
1601
|
+
iconEl.src = 'https://raw.githubusercontent.com/jxarco/lexgui.js/master/' + icon;
|
|
1492
1602
|
}
|
|
1493
1603
|
tab.prepend(iconEl);
|
|
1494
1604
|
}
|
|
@@ -1523,37 +1633,47 @@ class CodeEditor {
|
|
|
1523
1633
|
if (this.skipInfo) {
|
|
1524
1634
|
return;
|
|
1525
1635
|
}
|
|
1526
|
-
let panel = new LX.Panel({ className:
|
|
1636
|
+
let panel = new LX.Panel({ className: 'lexcodetabinfo flex flex-row', height: 'auto' });
|
|
1527
1637
|
if (this.onCreateStatusPanel) {
|
|
1528
1638
|
this.onCreateStatusPanel(panel, this);
|
|
1529
1639
|
}
|
|
1530
|
-
let leftStatusPanel = this.leftStatusPanel = new LX.Panel({ id:
|
|
1640
|
+
let leftStatusPanel = this.leftStatusPanel = new LX.Panel({ id: 'FontSizeZoomStatusComponent',
|
|
1641
|
+
height: 'auto' });
|
|
1531
1642
|
leftStatusPanel.sameLine();
|
|
1532
1643
|
if (this.skipTabs) {
|
|
1533
|
-
leftStatusPanel.addButton(null,
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
leftStatusPanel.
|
|
1537
|
-
|
|
1538
|
-
leftStatusPanel.
|
|
1644
|
+
leftStatusPanel.addButton(null, 'ZoomOutButton', this._decreaseFontSize.bind(this), { icon: 'ZoomOut',
|
|
1645
|
+
width: '32px', title: 'Zoom Out', tooltip: true });
|
|
1646
|
+
}
|
|
1647
|
+
leftStatusPanel.addButton(null, 'ZoomOutButton', this._decreaseFontSize.bind(this), { icon: 'ZoomOut',
|
|
1648
|
+
width: '32px', title: 'Zoom Out', tooltip: true });
|
|
1649
|
+
leftStatusPanel.addLabel(this.fontSize, { fit: true, signal: '@font-size' });
|
|
1650
|
+
leftStatusPanel.addButton(null, 'ZoomInButton', this._increaseFontSize.bind(this), { icon: 'ZoomIn',
|
|
1651
|
+
width: '32px', title: 'Zoom In', tooltip: true });
|
|
1652
|
+
leftStatusPanel.endLine('justify-start');
|
|
1539
1653
|
panel.attach(leftStatusPanel.root);
|
|
1540
|
-
let rightStatusPanel = this.rightStatusPanel = new LX.Panel({ height:
|
|
1654
|
+
let rightStatusPanel = this.rightStatusPanel = new LX.Panel({ height: 'auto' });
|
|
1541
1655
|
rightStatusPanel.sameLine();
|
|
1542
|
-
rightStatusPanel.addLabel(this.code?.title ??
|
|
1543
|
-
|
|
1544
|
-
rightStatusPanel.addButton(null,
|
|
1545
|
-
|
|
1656
|
+
rightStatusPanel.addLabel(this.code?.title ?? '', { id: 'EditorFilenameStatusComponent', fit: true,
|
|
1657
|
+
signal: '@tab-name' });
|
|
1658
|
+
rightStatusPanel.addButton(null, 'Ln 1, Col 1', this.showSearchLineBox.bind(this), {
|
|
1659
|
+
id: 'EditorSelectionStatusComponent',
|
|
1660
|
+
fit: true,
|
|
1661
|
+
signal: '@cursor-data'
|
|
1662
|
+
});
|
|
1663
|
+
rightStatusPanel.addButton(null, 'Spaces: ' + this.tabSpaces, (value, event) => {
|
|
1664
|
+
LX.addContextMenu('Spaces', event, (m) => {
|
|
1546
1665
|
const options = [2, 4, 8];
|
|
1547
|
-
for (const n of options)
|
|
1666
|
+
for (const n of options) {
|
|
1548
1667
|
m.add(n, (v) => {
|
|
1549
1668
|
this.tabSpaces = v;
|
|
1550
1669
|
this.processLines();
|
|
1551
|
-
this._updateDataInfoPanel(
|
|
1670
|
+
this._updateDataInfoPanel('@tab-spaces', 'Spaces: ' + this.tabSpaces);
|
|
1552
1671
|
});
|
|
1672
|
+
}
|
|
1553
1673
|
});
|
|
1554
|
-
}, { id:
|
|
1555
|
-
rightStatusPanel.addButton(
|
|
1556
|
-
LX.addContextMenu(
|
|
1674
|
+
}, { id: 'EditorIndentationStatusComponent', nameWidth: '15%', signal: '@tab-spaces' });
|
|
1675
|
+
rightStatusPanel.addButton('<b>{ }</b>', this.highlight, (value, event) => {
|
|
1676
|
+
LX.addContextMenu('Language', event, (m) => {
|
|
1557
1677
|
for (const lang of Object.keys(CodeEditor.languages)) {
|
|
1558
1678
|
m.add(lang, (v) => {
|
|
1559
1679
|
this._changeLanguage(v, undefined, true);
|
|
@@ -1561,32 +1681,34 @@ class CodeEditor {
|
|
|
1561
1681
|
});
|
|
1562
1682
|
}
|
|
1563
1683
|
});
|
|
1564
|
-
}, { id:
|
|
1565
|
-
rightStatusPanel.endLine(
|
|
1684
|
+
}, { id: 'EditorLanguageStatusComponent', nameWidth: '15%', signal: '@highlight' });
|
|
1685
|
+
rightStatusPanel.endLine('justify-end');
|
|
1566
1686
|
panel.attach(rightStatusPanel.root);
|
|
1567
1687
|
const itemVisibilityMap = {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1688
|
+
'Font Size Zoom': options.statusShowFontSizeZoom ?? true,
|
|
1689
|
+
'Editor Filename': options.statusShowEditorFilename ?? true,
|
|
1690
|
+
'Editor Selection': options.statusShowEditorSelection ?? true,
|
|
1691
|
+
'Editor Indentation': options.statusShowEditorIndentation ?? true,
|
|
1692
|
+
'Editor Language': options.statusShowEditorLanguage ?? true
|
|
1573
1693
|
};
|
|
1574
1694
|
const _setVisibility = (itemName) => {
|
|
1575
|
-
const b = panel.root.querySelector(`#${itemName.replaceAll(
|
|
1695
|
+
const b = panel.root.querySelector(`#${itemName.replaceAll(' ', '')}StatusComponent`);
|
|
1576
1696
|
console.assert(b, `${itemName} has no status button!`);
|
|
1577
|
-
b.classList.toggle(
|
|
1697
|
+
b.classList.toggle('hidden', !itemVisibilityMap[itemName]);
|
|
1578
1698
|
};
|
|
1579
1699
|
for (const [itemName, v] of Object.entries(itemVisibilityMap)) {
|
|
1580
1700
|
_setVisibility(itemName);
|
|
1581
1701
|
}
|
|
1582
|
-
panel.root.addEventListener(
|
|
1583
|
-
if (e.target
|
|
1702
|
+
panel.root.addEventListener('contextmenu', (e) => {
|
|
1703
|
+
if (e.target
|
|
1704
|
+
&& (e.target.classList.contains('lexpanel')
|
|
1705
|
+
|| e.target.classList.contains('lexinlinecomponents'))) {
|
|
1584
1706
|
return;
|
|
1585
1707
|
}
|
|
1586
1708
|
const menuOptions = Object.keys(itemVisibilityMap).map((itemName, idx) => {
|
|
1587
1709
|
const item = {
|
|
1588
1710
|
name: itemName,
|
|
1589
|
-
icon:
|
|
1711
|
+
icon: 'Check',
|
|
1590
1712
|
callback: () => {
|
|
1591
1713
|
itemVisibilityMap[itemName] = !itemVisibilityMap[itemName];
|
|
1592
1714
|
_setVisibility(itemName);
|
|
@@ -1596,7 +1718,7 @@ class CodeEditor {
|
|
|
1596
1718
|
delete item.icon;
|
|
1597
1719
|
return item;
|
|
1598
1720
|
});
|
|
1599
|
-
new LX.DropdownMenu(e.target, menuOptions, { side:
|
|
1721
|
+
new LX.DropdownMenu(e.target, menuOptions, { side: 'top', align: 'start' });
|
|
1600
1722
|
});
|
|
1601
1723
|
return panel;
|
|
1602
1724
|
}
|
|
@@ -1631,13 +1753,13 @@ class CodeEditor {
|
|
|
1631
1753
|
}
|
|
1632
1754
|
}
|
|
1633
1755
|
if (langString === undefined) {
|
|
1634
|
-
return
|
|
1756
|
+
return 'AlignLeft fg-neutral-500';
|
|
1635
1757
|
}
|
|
1636
1758
|
const iconPlusClasses = CodeEditor.languages[langString]?.icon;
|
|
1637
1759
|
if (iconPlusClasses) {
|
|
1638
1760
|
return iconPlusClasses[extension] ?? iconPlusClasses;
|
|
1639
1761
|
}
|
|
1640
|
-
return
|
|
1762
|
+
return 'AlignLeft fg-neutral-500';
|
|
1641
1763
|
}
|
|
1642
1764
|
_onNewTab(e) {
|
|
1643
1765
|
this.processFocus(false);
|
|
@@ -1646,22 +1768,23 @@ class CodeEditor {
|
|
|
1646
1768
|
return;
|
|
1647
1769
|
}
|
|
1648
1770
|
const dmOptions = this.newTabOptions ?? [
|
|
1649
|
-
{ name:
|
|
1650
|
-
{ name:
|
|
1771
|
+
{ name: 'Create file', icon: 'FilePlus', callback: this._onCreateNewFile.bind(this) },
|
|
1772
|
+
{ name: 'Load file', icon: 'FileUp', disabled: !this.allowLoadingFiles,
|
|
1773
|
+
callback: this.loadTabFromFile.bind(this) }
|
|
1651
1774
|
];
|
|
1652
|
-
new LX.DropdownMenu(e.target, dmOptions, { side:
|
|
1775
|
+
new LX.DropdownMenu(e.target, dmOptions, { side: 'bottom', align: 'start' });
|
|
1653
1776
|
}
|
|
1654
1777
|
_onCreateNewFile() {
|
|
1655
1778
|
let options = {};
|
|
1656
1779
|
if (this.onCreateFile) {
|
|
1657
1780
|
options = this.onCreateFile(this);
|
|
1658
|
-
if (!options) // Skip adding new file
|
|
1659
|
-
{
|
|
1781
|
+
if (!options) { // Skip adding new file
|
|
1660
1782
|
return;
|
|
1661
1783
|
}
|
|
1662
1784
|
}
|
|
1663
|
-
const name = options.name ??
|
|
1664
|
-
this.addTab(name, true, name, { indexOffset: options.indexOffset,
|
|
1785
|
+
const name = options.name ?? 'unnamed.js';
|
|
1786
|
+
this.addTab(name, true, name, { indexOffset: options.indexOffset,
|
|
1787
|
+
language: options.language ?? 'JavaScript' });
|
|
1665
1788
|
}
|
|
1666
1789
|
_onSelectTab(isNewTabButton, event, name) {
|
|
1667
1790
|
if (this.disableEdition) {
|
|
@@ -1681,7 +1804,7 @@ class CodeEditor {
|
|
|
1681
1804
|
this.restoreCursor(cursor, this.code.cursorState);
|
|
1682
1805
|
this.endSelection();
|
|
1683
1806
|
this.hideAutoCompleteBox();
|
|
1684
|
-
this._updateDataInfoPanel(
|
|
1807
|
+
this._updateDataInfoPanel('@tab-name', name);
|
|
1685
1808
|
if (this.code.languageOverride) {
|
|
1686
1809
|
this._changeLanguage(this.code.languageOverride);
|
|
1687
1810
|
}
|
|
@@ -1698,15 +1821,17 @@ class CodeEditor {
|
|
|
1698
1821
|
return;
|
|
1699
1822
|
}
|
|
1700
1823
|
new LX.DropdownMenu(event.target, [
|
|
1701
|
-
{ name:
|
|
1702
|
-
|
|
1824
|
+
{ name: 'Close', kbd: 'MWB', disabled: !this.allowClosingTabs, callback: () => {
|
|
1825
|
+
this.closeTab(name);
|
|
1826
|
+
} },
|
|
1827
|
+
{ name: 'Close Others', disabled: !this.allowClosingTabs, callback: () => {
|
|
1703
1828
|
for (const [key, data] of Object.entries(this.tabs.tabs)) {
|
|
1704
1829
|
if (key === '+' || key === name)
|
|
1705
1830
|
continue;
|
|
1706
1831
|
this.closeTab(key);
|
|
1707
1832
|
}
|
|
1708
1833
|
} },
|
|
1709
|
-
{ name:
|
|
1834
|
+
{ name: 'Close All', disabled: !this.allowClosingTabs, callback: () => {
|
|
1710
1835
|
for (const [key, data] of Object.entries(this.tabs.tabs)) {
|
|
1711
1836
|
if (key === '+')
|
|
1712
1837
|
continue;
|
|
@@ -1714,10 +1839,10 @@ class CodeEditor {
|
|
|
1714
1839
|
}
|
|
1715
1840
|
} },
|
|
1716
1841
|
null,
|
|
1717
|
-
{ name:
|
|
1718
|
-
navigator.clipboard.writeText(this.openedTabs[name].path ??
|
|
1842
|
+
{ name: 'Copy Path', icon: 'Copy', callback: () => {
|
|
1843
|
+
navigator.clipboard.writeText(this.openedTabs[name].path ?? '');
|
|
1719
1844
|
} }
|
|
1720
|
-
], { side:
|
|
1845
|
+
], { side: 'bottom', align: 'start', event });
|
|
1721
1846
|
}
|
|
1722
1847
|
addTab(name, selected, title, options = {}) {
|
|
1723
1848
|
// If already loaded, set new name...
|
|
@@ -1728,15 +1853,15 @@ class CodeEditor {
|
|
|
1728
1853
|
if (repeats > 0) {
|
|
1729
1854
|
name = name.split('.').join('_' + repeats + '.');
|
|
1730
1855
|
}
|
|
1731
|
-
const isNewTabButton =
|
|
1856
|
+
const isNewTabButton = name === '+';
|
|
1732
1857
|
// Create code content
|
|
1733
1858
|
let code = document.createElement('div');
|
|
1734
1859
|
let codeAny = code;
|
|
1735
1860
|
Object.assign(code, {
|
|
1736
|
-
path: options.path ??
|
|
1861
|
+
path: options.path ?? '',
|
|
1737
1862
|
className: 'code',
|
|
1738
|
-
lines: [
|
|
1739
|
-
language: options.language ??
|
|
1863
|
+
lines: [''],
|
|
1864
|
+
language: options.language ?? 'Plain Text',
|
|
1740
1865
|
cursorState: {},
|
|
1741
1866
|
undoSteps: [],
|
|
1742
1867
|
redoSteps: [],
|
|
@@ -1748,17 +1873,15 @@ class CodeEditor {
|
|
|
1748
1873
|
title: title ?? name,
|
|
1749
1874
|
tokens: {}
|
|
1750
1875
|
});
|
|
1751
|
-
code.style.left =
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
this.parentElement?.classList.add('dragging');
|
|
1756
|
-
});
|
|
1876
|
+
code.style.left = '0px', code.style.top = '0px', code.addEventListener('dragenter', function (e) {
|
|
1877
|
+
e.preventDefault();
|
|
1878
|
+
this.parentElement?.classList.add('dragging');
|
|
1879
|
+
});
|
|
1757
1880
|
code.addEventListener('dragleave', function (e) {
|
|
1758
1881
|
e.preventDefault();
|
|
1759
1882
|
this.parentElement?.classList.remove('dragging');
|
|
1760
1883
|
});
|
|
1761
|
-
code.addEventListener('drop', e => {
|
|
1884
|
+
code.addEventListener('drop', (e) => {
|
|
1762
1885
|
e.preventDefault();
|
|
1763
1886
|
code.parentElement?.classList.remove('dragging');
|
|
1764
1887
|
if (e.dataTransfer?.files) {
|
|
@@ -1808,17 +1931,17 @@ class CodeEditor {
|
|
|
1808
1931
|
this.code = lastCode;
|
|
1809
1932
|
}
|
|
1810
1933
|
this._processLinesIfNecessary();
|
|
1811
|
-
this._updateDataInfoPanel(
|
|
1934
|
+
this._updateDataInfoPanel('@tab-name', name);
|
|
1812
1935
|
// Bc it could be overrided..
|
|
1813
1936
|
return name;
|
|
1814
1937
|
}
|
|
1815
1938
|
loadCode(name) {
|
|
1816
1939
|
// Hide all others
|
|
1817
|
-
this.codeSizer.querySelectorAll(
|
|
1940
|
+
this.codeSizer.querySelectorAll('.code').forEach((c) => c.classList.add('hidden'));
|
|
1818
1941
|
// Already open...
|
|
1819
1942
|
if (this.openedTabs[name]) {
|
|
1820
1943
|
let code = this.openedTabs[name];
|
|
1821
|
-
code.classList.remove(
|
|
1944
|
+
code.classList.remove('hidden');
|
|
1822
1945
|
return;
|
|
1823
1946
|
}
|
|
1824
1947
|
let code = this.loadedTabs[name];
|
|
@@ -1850,7 +1973,7 @@ class CodeEditor {
|
|
|
1850
1973
|
this.processLines();
|
|
1851
1974
|
this._changeLanguageFromExtension(LX.getExtension(name));
|
|
1852
1975
|
this._processLinesIfNecessary();
|
|
1853
|
-
this._updateDataInfoPanel(
|
|
1976
|
+
this._updateDataInfoPanel('@tab-name', code.tabName);
|
|
1854
1977
|
}
|
|
1855
1978
|
loadTab(name) {
|
|
1856
1979
|
// Already open...
|
|
@@ -1877,9 +2000,9 @@ class CodeEditor {
|
|
|
1877
2000
|
return;
|
|
1878
2001
|
}
|
|
1879
2002
|
// Reset visibility
|
|
1880
|
-
code.style.display =
|
|
2003
|
+
code.style.display = 'block';
|
|
1881
2004
|
this.openedTabs[name] = code;
|
|
1882
|
-
const isNewTabButton =
|
|
2005
|
+
const isNewTabButton = name === '+';
|
|
1883
2006
|
const tabIcon = this._getFileIcon(name);
|
|
1884
2007
|
this.tabs.add(name, code, {
|
|
1885
2008
|
selected: true,
|
|
@@ -1897,7 +2020,7 @@ class CodeEditor {
|
|
|
1897
2020
|
this.code = code;
|
|
1898
2021
|
this.resetCursorPos(CodeEditor.CURSOR_LEFT_TOP, undefined, true);
|
|
1899
2022
|
this._changeLanguageFromExtension(LX.getExtension(name));
|
|
1900
|
-
this._updateDataInfoPanel(
|
|
2023
|
+
this._updateDataInfoPanel('@tab-name', code.tabName);
|
|
1901
2024
|
this.processLines();
|
|
1902
2025
|
}
|
|
1903
2026
|
closeTab(name, eraseAll = false) {
|
|
@@ -1919,7 +2042,7 @@ class CodeEditor {
|
|
|
1919
2042
|
input.type = 'file';
|
|
1920
2043
|
document.body.appendChild(input);
|
|
1921
2044
|
input.click();
|
|
1922
|
-
input.addEventListener('change', e => {
|
|
2045
|
+
input.addEventListener('change', (e) => {
|
|
1923
2046
|
const target = e.target;
|
|
1924
2047
|
if (target.files && target.files[0]) {
|
|
1925
2048
|
this.loadFile(target.files[0]);
|
|
@@ -1944,7 +2067,7 @@ class CodeEditor {
|
|
|
1944
2067
|
return;
|
|
1945
2068
|
var cursor = this.getCurrentCursor();
|
|
1946
2069
|
var code_rect = this.code.getBoundingClientRect();
|
|
1947
|
-
var mouse_pos = [
|
|
2070
|
+
var mouse_pos = [e.clientX - code_rect.x, e.clientY - code_rect.y];
|
|
1948
2071
|
// Discard out of lines click...
|
|
1949
2072
|
var ln = (mouse_pos[1] / this.lineHeight) | 0;
|
|
1950
2073
|
if (ln < 0)
|
|
@@ -1955,10 +2078,12 @@ class CodeEditor {
|
|
|
1955
2078
|
this.processClick(e);
|
|
1956
2079
|
this.canOpenContextMenu = !cursor.selection;
|
|
1957
2080
|
if (cursor.selection) {
|
|
1958
|
-
this.canOpenContextMenu = this.canOpenContextMenu
|
|
1959
|
-
|
|
1960
|
-
|
|
2081
|
+
this.canOpenContextMenu = this.canOpenContextMenu
|
|
2082
|
+
|| (cursor.line >= cursor.selection.fromY && cursor.line <= cursor.selection.toY
|
|
2083
|
+
&& cursor.position >= cursor.selection.fromX && cursor.position <= cursor.selection.toX);
|
|
2084
|
+
if (this.canOpenContextMenu) {
|
|
1961
2085
|
return;
|
|
2086
|
+
}
|
|
1962
2087
|
}
|
|
1963
2088
|
}
|
|
1964
2089
|
this._mouseDown = true;
|
|
@@ -1975,8 +2100,7 @@ class CodeEditor {
|
|
|
1975
2100
|
this.processSelections(e);
|
|
1976
2101
|
}
|
|
1977
2102
|
}
|
|
1978
|
-
else if (e.type == 'click') // trip
|
|
1979
|
-
{
|
|
2103
|
+
else if (e.type == 'click') { // trip
|
|
1980
2104
|
switch (e.detail) {
|
|
1981
2105
|
case LX.MOUSE_DOUBLE_CLICK:
|
|
1982
2106
|
const [word, from, to] = this.getWordAtPos(cursor);
|
|
@@ -2001,10 +2125,16 @@ class CodeEditor {
|
|
|
2001
2125
|
return;
|
|
2002
2126
|
}
|
|
2003
2127
|
LX.addContextMenu(null, e, (m) => {
|
|
2004
|
-
m.add(
|
|
2128
|
+
m.add('Copy', () => {
|
|
2129
|
+
this._copyContent(cursor);
|
|
2130
|
+
});
|
|
2005
2131
|
if (!this.disableEdition) {
|
|
2006
|
-
m.add(
|
|
2007
|
-
|
|
2132
|
+
m.add('Cut', () => {
|
|
2133
|
+
this._cutContent(cursor);
|
|
2134
|
+
});
|
|
2135
|
+
m.add('Paste', () => {
|
|
2136
|
+
this._pasteContent(cursor);
|
|
2137
|
+
});
|
|
2008
2138
|
}
|
|
2009
2139
|
if (!this.onContextMenu) {
|
|
2010
2140
|
return;
|
|
@@ -2014,21 +2144,22 @@ class CodeEditor {
|
|
|
2014
2144
|
// Some selections don't depend on mouse up..
|
|
2015
2145
|
if (cursor.selection)
|
|
2016
2146
|
cursor.selection.invertIfNecessary();
|
|
2017
|
-
const separator =
|
|
2147
|
+
const separator = '_NEWLINE_';
|
|
2018
2148
|
let code = this.code.lines.join(separator);
|
|
2019
2149
|
// Get linear start index
|
|
2020
2150
|
let index = 0;
|
|
2021
2151
|
for (let i = 0; i <= cursor.selection.fromY; i++) {
|
|
2022
|
-
index +=
|
|
2152
|
+
index += i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[i].length;
|
|
2023
2153
|
}
|
|
2024
2154
|
index += cursor.selection.fromY * separator.length;
|
|
2025
|
-
const num_chars = cursor.selection.chars
|
|
2155
|
+
const num_chars = cursor.selection.chars
|
|
2156
|
+
+ (cursor.selection.toY - cursor.selection.fromY) * separator.length;
|
|
2026
2157
|
const text = code.substr(index, num_chars);
|
|
2027
2158
|
content = text.split(separator).join('\n');
|
|
2028
2159
|
}
|
|
2029
2160
|
const options = this.onContextMenu(this, content, e);
|
|
2030
2161
|
if (options.length) {
|
|
2031
|
-
m.add(
|
|
2162
|
+
m.add('');
|
|
2032
2163
|
for (const o of options) {
|
|
2033
2164
|
m.add(o.path, { disabled: o.disabled, callback: o.callback });
|
|
2034
2165
|
}
|
|
@@ -2065,7 +2196,8 @@ class CodeEditor {
|
|
|
2065
2196
|
processClick(e) {
|
|
2066
2197
|
var cursor = this.getCurrentCursor();
|
|
2067
2198
|
var code_rect = this.codeScroller.getBoundingClientRect();
|
|
2068
|
-
var position = [(e.clientX - code_rect.x) + this.getScrollLeft(),
|
|
2199
|
+
var position = [(e.clientX - code_rect.x) + this.getScrollLeft(),
|
|
2200
|
+
(e.clientY - code_rect.y) + this.getScrollTop()];
|
|
2069
2201
|
var ln = (position[1] / this.lineHeight) | 0;
|
|
2070
2202
|
// Check out of range line
|
|
2071
2203
|
const outOfRange = ln > this.code.lines.length - 1;
|
|
@@ -2112,9 +2244,10 @@ class CodeEditor {
|
|
|
2112
2244
|
if (!keepRange) {
|
|
2113
2245
|
let ccw = true;
|
|
2114
2246
|
// Check if we must change ccw or not ... (not with mouse)
|
|
2115
|
-
if (!isMouseEvent && cursor.line >= selection.fromY
|
|
2116
|
-
(cursor.line == selection.fromY ? cursor.position >= selection.fromX : true)) {
|
|
2117
|
-
ccw =
|
|
2247
|
+
if (!isMouseEvent && cursor.line >= selection.fromY
|
|
2248
|
+
&& (cursor.line == selection.fromY ? cursor.position >= selection.fromX : true)) {
|
|
2249
|
+
ccw = e && this._lastSelectionKeyDir
|
|
2250
|
+
&& (e.key == 'ArrowRight' || e.key == 'ArrowDown' || e.key == 'End');
|
|
2118
2251
|
}
|
|
2119
2252
|
if (ccw) {
|
|
2120
2253
|
if (flags & CodeEditor.SELECTION_X)
|
|
@@ -2141,8 +2274,9 @@ class CodeEditor {
|
|
|
2141
2274
|
let cursorSelections = this.selections[cursor.name];
|
|
2142
2275
|
// Selection goes down...
|
|
2143
2276
|
if (deltaY >= 0) {
|
|
2144
|
-
while (deltaY < (cursorSelections.childElementCount - 1))
|
|
2277
|
+
while (deltaY < (cursorSelections.childElementCount - 1)) {
|
|
2145
2278
|
LX.deleteElement(cursorSelections.lastChild);
|
|
2279
|
+
}
|
|
2146
2280
|
for (let i = fromY; i <= toY; i++) {
|
|
2147
2281
|
const sId = i - fromY;
|
|
2148
2282
|
const isVisible = i >= this.visibleLinesViewport.x && i <= this.visibleLinesViewport.y;
|
|
@@ -2152,17 +2286,19 @@ class CodeEditor {
|
|
|
2152
2286
|
domEl = cursorSelections.childNodes[sId];
|
|
2153
2287
|
if (!domEl) {
|
|
2154
2288
|
domEl = document.createElement('div');
|
|
2155
|
-
domEl.className =
|
|
2289
|
+
domEl.className = 'lexcodeselection';
|
|
2156
2290
|
cursorSelections.appendChild(domEl);
|
|
2157
2291
|
}
|
|
2158
2292
|
}
|
|
2159
2293
|
// Compute new width and selection margins
|
|
2160
|
-
let string =
|
|
2161
|
-
if (sId == 0) // First line 2 cases (single line, multiline)
|
|
2162
|
-
{
|
|
2294
|
+
let string = '';
|
|
2295
|
+
if (sId == 0) { // First line 2 cases (single line, multiline)
|
|
2163
2296
|
const reverse = fromX > toX;
|
|
2164
|
-
if (deltaY == 0)
|
|
2165
|
-
string = !reverse
|
|
2297
|
+
if (deltaY == 0) {
|
|
2298
|
+
string = !reverse
|
|
2299
|
+
? this.code.lines[i].substring(fromX, toX)
|
|
2300
|
+
: this.code.lines[i].substring(toX, fromX);
|
|
2301
|
+
}
|
|
2166
2302
|
else
|
|
2167
2303
|
string = this.code.lines[i].substr(fromX);
|
|
2168
2304
|
const pixels = (reverse && deltaY == 0 ? toX : fromX) * this.charWidth;
|
|
@@ -2177,16 +2313,17 @@ class CodeEditor {
|
|
|
2177
2313
|
const stringWidth = this.measureString(string);
|
|
2178
2314
|
selection.chars += stringWidth / this.charWidth;
|
|
2179
2315
|
if (isVisible) {
|
|
2180
|
-
domEl.style.width = (stringWidth || (deltaY == 0 ? 0 : 8)) +
|
|
2316
|
+
domEl.style.width = (stringWidth || (deltaY == 0 ? 0 : 8)) + 'px';
|
|
2181
2317
|
domEl._top = i * this.lineHeight;
|
|
2182
|
-
domEl.style.top = domEl._top +
|
|
2318
|
+
domEl.style.top = domEl._top + 'px';
|
|
2183
2319
|
}
|
|
2184
2320
|
}
|
|
2185
2321
|
}
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
while (Math.abs(deltaY) < (cursorSelections.childElementCount - 1))
|
|
2322
|
+
// Selection goes up...
|
|
2323
|
+
else {
|
|
2324
|
+
while (Math.abs(deltaY) < (cursorSelections.childElementCount - 1)) {
|
|
2189
2325
|
LX.deleteElement(cursorSelections.firstChild);
|
|
2326
|
+
}
|
|
2190
2327
|
for (let i = toY; i <= fromY; i++) {
|
|
2191
2328
|
const sId = i - toY;
|
|
2192
2329
|
const isVisible = i >= this.visibleLinesViewport.x && i <= this.visibleLinesViewport.y;
|
|
@@ -2196,7 +2333,7 @@ class CodeEditor {
|
|
|
2196
2333
|
domEl = cursorSelections.childNodes[sId];
|
|
2197
2334
|
if (!domEl) {
|
|
2198
2335
|
domEl = document.createElement('div');
|
|
2199
|
-
domEl.className =
|
|
2336
|
+
domEl.className = 'lexcodeselection';
|
|
2200
2337
|
cursorSelections.appendChild(domEl);
|
|
2201
2338
|
}
|
|
2202
2339
|
}
|
|
@@ -2206,7 +2343,7 @@ class CodeEditor {
|
|
|
2206
2343
|
string = this.code.lines[i].substr(toX);
|
|
2207
2344
|
const pixels = toX * this.charWidth;
|
|
2208
2345
|
if (isVisible)
|
|
2209
|
-
domEl.style.left =
|
|
2346
|
+
domEl.style.left = 'calc(' + pixels + 'px + ' + this.xPadding + ')';
|
|
2210
2347
|
}
|
|
2211
2348
|
else {
|
|
2212
2349
|
string = (i == fromY) ? this.code.lines[i].substring(0, fromX) : this.code.lines[i]; // Last line, any multiple line...
|
|
@@ -2216,17 +2353,18 @@ class CodeEditor {
|
|
|
2216
2353
|
const stringWidth = this.measureString(string);
|
|
2217
2354
|
selection.chars += stringWidth / this.charWidth;
|
|
2218
2355
|
if (isVisible) {
|
|
2219
|
-
domEl.style.width = (stringWidth || 8) +
|
|
2356
|
+
domEl.style.width = (stringWidth || 8) + 'px';
|
|
2220
2357
|
domEl._top = i * this.lineHeight;
|
|
2221
|
-
domEl.style.top = domEl._top +
|
|
2358
|
+
domEl.style.top = domEl._top + 'px';
|
|
2222
2359
|
}
|
|
2223
2360
|
}
|
|
2224
2361
|
}
|
|
2225
2362
|
}
|
|
2226
2363
|
async processKey(e) {
|
|
2227
2364
|
const numCursors = this.cursors.length;
|
|
2228
|
-
if (!this.code || e.srcElement?.constructor != HTMLDivElement)
|
|
2365
|
+
if (!this.code || e.srcElement?.constructor != HTMLDivElement) {
|
|
2229
2366
|
return;
|
|
2367
|
+
}
|
|
2230
2368
|
const detail = e.detail ?? {};
|
|
2231
2369
|
const key = e.key ?? detail.key;
|
|
2232
2370
|
// Do not propagate "space to scroll" event
|
|
@@ -2251,8 +2389,9 @@ class CodeEditor {
|
|
|
2251
2389
|
for (var i = 0; i < numCursors; i++) {
|
|
2252
2390
|
let cursor = this.cursors[i];
|
|
2253
2391
|
// We could delete secondary cursor while iterating..
|
|
2254
|
-
if (!cursor)
|
|
2392
|
+
if (!cursor) {
|
|
2255
2393
|
break;
|
|
2394
|
+
}
|
|
2256
2395
|
// Arrows don't modify code lines.. And only add offset if in the same line
|
|
2257
2396
|
if (lastProcessedCursor && lastProcessedCursor.line == cursor.line && !key.includes('Arrow')) {
|
|
2258
2397
|
cursor.position += cursorOffset.x;
|
|
@@ -2262,8 +2401,8 @@ class CodeEditor {
|
|
|
2262
2401
|
lastProcessedCursor = this.saveCursor(cursor);
|
|
2263
2402
|
this._lastProcessedCursorIndex = i;
|
|
2264
2403
|
this._processKeyAtCursor(e, key, cursor);
|
|
2265
|
-
cursorOffset.x +=
|
|
2266
|
-
cursorOffset.y +=
|
|
2404
|
+
cursorOffset.x += cursor.position - lastProcessedCursor.position;
|
|
2405
|
+
cursorOffset.y += cursor.line - lastProcessedCursor.line;
|
|
2267
2406
|
// Set active line in case it's blurred
|
|
2268
2407
|
if (!cursor.selection) {
|
|
2269
2408
|
cursor.line = cursor.line;
|
|
@@ -2275,8 +2414,9 @@ class CodeEditor {
|
|
|
2275
2414
|
async processKeyAtTargetCursor(e, key, targetIdx) {
|
|
2276
2415
|
let cursor = this.cursors[targetIdx];
|
|
2277
2416
|
// We could delete secondary cursor while iterating..
|
|
2278
|
-
if (!cursor)
|
|
2417
|
+
if (!cursor) {
|
|
2279
2418
|
return;
|
|
2419
|
+
}
|
|
2280
2420
|
this._processKeyAtCursor(e, key, cursor);
|
|
2281
2421
|
this._processGlobalKeys(e, key);
|
|
2282
2422
|
}
|
|
@@ -2367,7 +2507,7 @@ class CodeEditor {
|
|
|
2367
2507
|
return;
|
|
2368
2508
|
}
|
|
2369
2509
|
let lidx = cursor.line;
|
|
2370
|
-
this.code.lines[lidx] = this.code.lines[lidx] ??
|
|
2510
|
+
this.code.lines[lidx] = this.code.lines[lidx] ?? '';
|
|
2371
2511
|
// Check combinations
|
|
2372
2512
|
const isLastCursor = cursor.isLast();
|
|
2373
2513
|
if (e.ctrlKey || e.metaKey) {
|
|
@@ -2409,8 +2549,9 @@ class CodeEditor {
|
|
|
2409
2549
|
}
|
|
2410
2550
|
// Apply binded actions...
|
|
2411
2551
|
for (const actKey in this.actions) {
|
|
2412
|
-
if (key != actKey)
|
|
2552
|
+
if (key != actKey) {
|
|
2413
2553
|
continue;
|
|
2554
|
+
}
|
|
2414
2555
|
e.preventDefault();
|
|
2415
2556
|
if (this._actionMustDelete(cursor, this.actions[key], e)) {
|
|
2416
2557
|
this.actions['Backspace'].callback(lidx, cursor, e);
|
|
@@ -2418,17 +2559,19 @@ class CodeEditor {
|
|
|
2418
2559
|
return this.actions[key].callback(lidx, cursor, e);
|
|
2419
2560
|
}
|
|
2420
2561
|
// From now on, don't allow ctrl, shift or meta (mac) combinations
|
|
2421
|
-
if (e.ctrlKey || e.metaKey)
|
|
2562
|
+
if (e.ctrlKey || e.metaKey) {
|
|
2422
2563
|
return;
|
|
2564
|
+
}
|
|
2423
2565
|
// Add undo steps
|
|
2424
2566
|
if (!skipUndo && this.code.lines.length) {
|
|
2425
2567
|
this._addUndoStep(cursor);
|
|
2426
2568
|
}
|
|
2427
2569
|
// Some custom cases for word enclosing (), {}, "", '', ...
|
|
2428
|
-
const enclosableKeys = ["
|
|
2570
|
+
const enclosableKeys = ['"', "'", '(', '{'];
|
|
2429
2571
|
if (enclosableKeys.indexOf(key) > -1) {
|
|
2430
|
-
if (this._encloseSelectedWordWithKey(key, lidx, cursor))
|
|
2572
|
+
if (this._encloseSelectedWordWithKey(key, lidx, cursor)) {
|
|
2431
2573
|
return;
|
|
2574
|
+
}
|
|
2432
2575
|
}
|
|
2433
2576
|
// Until this point, if there was a selection, we need
|
|
2434
2577
|
// to delete the content..
|
|
@@ -2448,7 +2591,7 @@ class CodeEditor {
|
|
|
2448
2591
|
}
|
|
2449
2592
|
this.cursorToRight(key, cursor);
|
|
2450
2593
|
// Some custom cases for auto key pair (), {}, "", '', ...
|
|
2451
|
-
const keyMustPair =
|
|
2594
|
+
const keyMustPair = this.pairKeys[key] !== undefined;
|
|
2452
2595
|
if (keyMustPair && !this.wasKeyPaired) {
|
|
2453
2596
|
// Make sure to detect later that the key is paired automatically to avoid loops...
|
|
2454
2597
|
this.wasKeyPaired = true;
|
|
@@ -2470,7 +2613,7 @@ class CodeEditor {
|
|
|
2470
2613
|
}
|
|
2471
2614
|
}
|
|
2472
2615
|
async _pasteContent(cursor) {
|
|
2473
|
-
const mustDetectLanguage =
|
|
2616
|
+
const mustDetectLanguage = !this.getText().length;
|
|
2474
2617
|
let text = await navigator.clipboard.readText();
|
|
2475
2618
|
// Remove any possible tabs (\t) and add spaces
|
|
2476
2619
|
text = text.replaceAll(/\t|\\t/g, ' '.repeat(this.tabSpaces));
|
|
@@ -2489,23 +2632,24 @@ class CodeEditor {
|
|
|
2489
2632
|
}
|
|
2490
2633
|
}
|
|
2491
2634
|
async _copyContent(cursor) {
|
|
2492
|
-
let textToCopy =
|
|
2635
|
+
let textToCopy = '';
|
|
2493
2636
|
if (!cursor.selection) {
|
|
2494
|
-
textToCopy =
|
|
2637
|
+
textToCopy = '\n' + this.code.lines[cursor.line];
|
|
2495
2638
|
}
|
|
2496
2639
|
else {
|
|
2497
2640
|
// Some selections don't depend on mouse up..
|
|
2498
2641
|
if (cursor.selection)
|
|
2499
2642
|
cursor.selection.invertIfNecessary();
|
|
2500
|
-
const separator =
|
|
2643
|
+
const separator = '_NEWLINE_';
|
|
2501
2644
|
let code = this.code.lines.join(separator);
|
|
2502
2645
|
// Get linear start index
|
|
2503
2646
|
let index = 0;
|
|
2504
2647
|
for (let i = 0; i <= cursor.selection.fromY; i++) {
|
|
2505
|
-
index +=
|
|
2648
|
+
index += i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[i].length;
|
|
2506
2649
|
}
|
|
2507
2650
|
index += cursor.selection.fromY * separator.length;
|
|
2508
|
-
const num_chars = cursor.selection.chars
|
|
2651
|
+
const num_chars = cursor.selection.chars
|
|
2652
|
+
+ (cursor.selection.toY - cursor.selection.fromY) * separator.length;
|
|
2509
2653
|
const text = code.substr(index, num_chars);
|
|
2510
2654
|
const lines = text.split(separator);
|
|
2511
2655
|
textToCopy = lines.join('\n');
|
|
@@ -2515,10 +2659,10 @@ class CodeEditor {
|
|
|
2515
2659
|
}
|
|
2516
2660
|
async _cutContent(cursor) {
|
|
2517
2661
|
let lidx = cursor.line;
|
|
2518
|
-
let textToCut =
|
|
2662
|
+
let textToCut = '';
|
|
2519
2663
|
this._addUndoStep(cursor, true);
|
|
2520
2664
|
if (!cursor.selection) {
|
|
2521
|
-
textToCut =
|
|
2665
|
+
textToCut = '\n' + this.code.lines[cursor.line];
|
|
2522
2666
|
this.code.lines.splice(lidx, 1);
|
|
2523
2667
|
this.processLines();
|
|
2524
2668
|
this.resetCursorPos(CodeEditor.CURSOR_LEFT, cursor);
|
|
@@ -2530,15 +2674,16 @@ class CodeEditor {
|
|
|
2530
2674
|
// Some selections don't depend on mouse up..
|
|
2531
2675
|
if (cursor.selection)
|
|
2532
2676
|
cursor.selection.invertIfNecessary();
|
|
2533
|
-
const separator =
|
|
2677
|
+
const separator = '_NEWLINE_';
|
|
2534
2678
|
let code = this.code.lines.join(separator);
|
|
2535
2679
|
// Get linear start index
|
|
2536
2680
|
let index = 0;
|
|
2537
2681
|
for (let i = 0; i <= cursor.selection.fromY; i++) {
|
|
2538
|
-
index +=
|
|
2682
|
+
index += i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[i].length;
|
|
2539
2683
|
}
|
|
2540
2684
|
index += cursor.selection.fromY * separator.length;
|
|
2541
|
-
const numChars = cursor.selection.chars
|
|
2685
|
+
const numChars = cursor.selection.chars
|
|
2686
|
+
+ (cursor.selection.toY - cursor.selection.fromY) * separator.length;
|
|
2542
2687
|
const text = code.substr(index, numChars);
|
|
2543
2688
|
const lines = text.split(separator);
|
|
2544
2689
|
textToCut = lines.join('\n');
|
|
@@ -2570,7 +2715,7 @@ class CodeEditor {
|
|
|
2570
2715
|
return idx < 0 ? 1e10 : idx;
|
|
2571
2716
|
}));
|
|
2572
2717
|
if (useCommentBlock) {
|
|
2573
|
-
const tokens =
|
|
2718
|
+
const tokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
2574
2719
|
const fromString = this.code.lines[cursor.selection.fromY];
|
|
2575
2720
|
let fromIdx = firstNonspaceIndex(fromString);
|
|
2576
2721
|
if (fromIdx == -1) {
|
|
@@ -2578,11 +2723,11 @@ class CodeEditor {
|
|
|
2578
2723
|
}
|
|
2579
2724
|
this.code.lines[cursor.selection.fromY] = [
|
|
2580
2725
|
fromString.substring(0, fromIdx),
|
|
2581
|
-
tokens[0] +
|
|
2726
|
+
tokens[0] + ' ',
|
|
2582
2727
|
fromString.substring(fromIdx)
|
|
2583
2728
|
].join('');
|
|
2584
|
-
this.code.lines[cursor.selection.toY] +=
|
|
2585
|
-
cursor.selection.fromX +=
|
|
2729
|
+
this.code.lines[cursor.selection.toY] += ' ' + tokens[1];
|
|
2730
|
+
cursor.selection.fromX += tokens[0].length + 1;
|
|
2586
2731
|
this._processSelection(cursor);
|
|
2587
2732
|
}
|
|
2588
2733
|
else {
|
|
@@ -2610,8 +2755,9 @@ class CodeEditor {
|
|
|
2610
2755
|
}
|
|
2611
2756
|
_commentLine(cursor, line, minNonspaceIdx, updateCursor = true) {
|
|
2612
2757
|
const lang = CodeEditor.languages[this.highlight];
|
|
2613
|
-
if (!(lang.singleLineComments ?? true))
|
|
2758
|
+
if (!(lang.singleLineComments ?? true)) {
|
|
2614
2759
|
return;
|
|
2760
|
+
}
|
|
2615
2761
|
const token = (lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken) + ' ';
|
|
2616
2762
|
const string = this.code.lines[line];
|
|
2617
2763
|
let idx = firstNonspaceIndex(string);
|
|
@@ -2649,29 +2795,31 @@ class CodeEditor {
|
|
|
2649
2795
|
}
|
|
2650
2796
|
_uncommentLine(cursor, line) {
|
|
2651
2797
|
const lang = CodeEditor.languages[this.highlight];
|
|
2652
|
-
if (!(lang.singleLineComments ?? true))
|
|
2798
|
+
if (!(lang.singleLineComments ?? true)) {
|
|
2653
2799
|
return;
|
|
2800
|
+
}
|
|
2654
2801
|
const token = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
2655
2802
|
const string = this.code.lines[line];
|
|
2656
2803
|
if (string.includes(token)) {
|
|
2657
2804
|
this.code.lines[line] = string.replace(token + ' ', '');
|
|
2658
2805
|
// try deleting token + space, and then if not, delete only the token
|
|
2659
|
-
if (string.length == this.code.lines[line].length)
|
|
2806
|
+
if (string.length == this.code.lines[line].length) {
|
|
2660
2807
|
this.code.lines[line] = string.replace(token, '');
|
|
2808
|
+
}
|
|
2661
2809
|
this.cursorToString(cursor, 'X'.repeat(Math.abs(string.length - this.code.lines[line].length)), true);
|
|
2662
2810
|
}
|
|
2663
2811
|
}
|
|
2664
2812
|
action(key, deleteSelection = false, fn, eventSkipDeleteFn) {
|
|
2665
2813
|
this.actions[key] = {
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2814
|
+
'key': key,
|
|
2815
|
+
'callback': fn,
|
|
2816
|
+
'deleteSelection': deleteSelection,
|
|
2817
|
+
'eventSkipDeleteFn': eventSkipDeleteFn
|
|
2670
2818
|
};
|
|
2671
2819
|
}
|
|
2672
2820
|
_actionMustDelete(cursor, action, e) {
|
|
2673
|
-
return cursor.selection && action.deleteSelection
|
|
2674
|
-
!(action.eventSkipDeleteFn ? action.eventSkipDeleteFn(cursor, e) : false);
|
|
2821
|
+
return cursor.selection && action.deleteSelection
|
|
2822
|
+
&& !(action.eventSkipDeleteFn ? action.eventSkipDeleteFn(cursor, e) : false);
|
|
2675
2823
|
}
|
|
2676
2824
|
scanWordSuggestions() {
|
|
2677
2825
|
this.code.tokens = {};
|
|
@@ -2698,15 +2846,16 @@ class CodeEditor {
|
|
|
2698
2846
|
if (!this.code) {
|
|
2699
2847
|
return;
|
|
2700
2848
|
}
|
|
2701
|
-
var htmlCode =
|
|
2849
|
+
var htmlCode = '';
|
|
2702
2850
|
this._blockCommentCache.length = 0;
|
|
2703
2851
|
this.mustProcessLines = false;
|
|
2704
2852
|
// Reset all lines content
|
|
2705
|
-
this.code.innerHTML =
|
|
2853
|
+
this.code.innerHTML = '';
|
|
2706
2854
|
// Get info about lines in viewport
|
|
2707
2855
|
const lastScrollTop = this.getScrollTop();
|
|
2708
|
-
this.firstLineInViewport = (mode ?? CodeEditor.KEEP_VISIBLE_LINES) & CodeEditor.UPDATE_VISIBLE_LINES
|
|
2709
|
-
((lastScrollTop / this.lineHeight) | 0)
|
|
2856
|
+
this.firstLineInViewport = (mode ?? CodeEditor.KEEP_VISIBLE_LINES) & CodeEditor.UPDATE_VISIBLE_LINES
|
|
2857
|
+
? ((lastScrollTop / this.lineHeight) | 0)
|
|
2858
|
+
: this.firstLineInViewport;
|
|
2710
2859
|
const totalLinesInViewport = ((this.codeScroller.offsetHeight) / this.lineHeight) | 0;
|
|
2711
2860
|
this.visibleLinesViewport = new LX.vec2(Math.max(this.firstLineInViewport - this.lineScrollMargin.x, 0), Math.min(this.firstLineInViewport + totalLinesInViewport + this.lineScrollMargin.y, this.code.lines.length));
|
|
2712
2861
|
// Add remaining lines if we are near the end of the scroll
|
|
@@ -2716,7 +2865,7 @@ class CodeEditor {
|
|
|
2716
2865
|
this.visibleLinesViewport.y += diff;
|
|
2717
2866
|
}
|
|
2718
2867
|
}
|
|
2719
|
-
this._scopeStack = [{ name:
|
|
2868
|
+
this._scopeStack = [{ name: '', type: 'global' }];
|
|
2720
2869
|
// Process visible lines
|
|
2721
2870
|
for (let i = this.visibleLinesViewport.x; i < this.visibleLinesViewport.y; ++i) {
|
|
2722
2871
|
htmlCode += this.processLine(i, true);
|
|
@@ -2724,7 +2873,7 @@ class CodeEditor {
|
|
|
2724
2873
|
this.code.innerHTML = htmlCode;
|
|
2725
2874
|
// Update scroll data
|
|
2726
2875
|
this.codeScroller.scrollTop = lastScrollTop;
|
|
2727
|
-
this.code.style.top = (this.visibleLinesViewport.x * this.lineHeight) +
|
|
2876
|
+
this.code.style.top = (this.visibleLinesViewport.x * this.lineHeight) + 'px';
|
|
2728
2877
|
// Update selections
|
|
2729
2878
|
this.updateSelections(null, true);
|
|
2730
2879
|
this._clearTmpVariables();
|
|
@@ -2764,9 +2913,9 @@ class CodeEditor {
|
|
|
2764
2913
|
this._currentLineString = lineString;
|
|
2765
2914
|
const tokensToEvaluate = this._getTokensFromLine(lineString);
|
|
2766
2915
|
if (!tokensToEvaluate.length) {
|
|
2767
|
-
return this._updateLine(force, lineNumber,
|
|
2916
|
+
return this._updateLine(force, lineNumber, '', skipPropagation);
|
|
2768
2917
|
}
|
|
2769
|
-
let lineInnerHtml =
|
|
2918
|
+
let lineInnerHtml = '';
|
|
2770
2919
|
let pushedScope = false;
|
|
2771
2920
|
const newSignature = this._getLineSignatureFromTokens(tokensToEvaluate);
|
|
2772
2921
|
const cachedSignature = this.code.lineSignatures[lineNumber];
|
|
@@ -2775,7 +2924,7 @@ class CodeEditor {
|
|
|
2775
2924
|
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
2776
2925
|
// Reset scope stack if structural changes in current line
|
|
2777
2926
|
if (mustUpdateScopes) {
|
|
2778
|
-
this._scopeStack = [{ name:
|
|
2927
|
+
this._scopeStack = [{ name: '', type: 'global' }];
|
|
2779
2928
|
}
|
|
2780
2929
|
// Process all tokens
|
|
2781
2930
|
for (let i = 0; i < tokensToEvaluate.length; ++i) {
|
|
@@ -2801,7 +2950,7 @@ class CodeEditor {
|
|
|
2801
2950
|
}
|
|
2802
2951
|
// Compare line signature for structural changes
|
|
2803
2952
|
// to pop current scope if necessary
|
|
2804
|
-
if (token ===
|
|
2953
|
+
if (token === '}' && this._scopeStack.length > 1) {
|
|
2805
2954
|
this._scopeStack.pop();
|
|
2806
2955
|
}
|
|
2807
2956
|
lineInnerHtml += this._evaluateToken({
|
|
@@ -2818,10 +2967,11 @@ class CodeEditor {
|
|
|
2818
2967
|
if (blockComments && this._buildingBlockComment != undefined
|
|
2819
2968
|
&& token.substr(0, blockCommentsTokens[1].length) == blockCommentsTokens[1]) {
|
|
2820
2969
|
const [commentLineNumber, tokenPos] = this._buildingBlockComment;
|
|
2821
|
-
this._blockCommentCache.push([new LX.vec2(commentLineNumber, lineNumber),
|
|
2970
|
+
this._blockCommentCache.push([new LX.vec2(commentLineNumber, lineNumber),
|
|
2971
|
+
new LX.vec2(tokenPos, tokenStartIndex)]);
|
|
2822
2972
|
delete this._buildingBlockComment;
|
|
2823
2973
|
}
|
|
2824
|
-
if (token !==
|
|
2974
|
+
if (token !== '{') {
|
|
2825
2975
|
continue;
|
|
2826
2976
|
}
|
|
2827
2977
|
// Store current scopes
|
|
@@ -2850,10 +3000,10 @@ class CodeEditor {
|
|
|
2850
3000
|
}
|
|
2851
3001
|
}
|
|
2852
3002
|
}
|
|
2853
|
-
contextTokens = contextTokens.reverse().filter(v => v.length && v != ' ');
|
|
3003
|
+
contextTokens = contextTokens.reverse().filter((v) => v.length && v != ' ');
|
|
2854
3004
|
// Keywords that can open a *named* scope
|
|
2855
3005
|
// TODO: Do this per language
|
|
2856
|
-
const scopeKeywords = [
|
|
3006
|
+
const scopeKeywords = ['class', 'enum', 'function', 'interface', 'type', 'struct', 'namespace'];
|
|
2857
3007
|
let scopeType = null; // This is the type of scope (function, class, enum, etc)
|
|
2858
3008
|
let scopeName = null;
|
|
2859
3009
|
for (let i = 0; i < contextTokens.length; i++) {
|
|
@@ -2865,21 +3015,21 @@ class CodeEditor {
|
|
|
2865
3015
|
}
|
|
2866
3016
|
}
|
|
2867
3017
|
// Special case: enum type specification `enum Foo : int {`
|
|
2868
|
-
if (scopeType ===
|
|
2869
|
-
const colonIndex = contextTokens.indexOf(
|
|
3018
|
+
if (scopeType === 'enum' && contextTokens.includes(':')) {
|
|
3019
|
+
const colonIndex = contextTokens.indexOf(':');
|
|
2870
3020
|
scopeName = contextTokens[colonIndex + 1] || scopeName;
|
|
2871
3021
|
}
|
|
2872
3022
|
if (!scopeType) {
|
|
2873
|
-
const parOpenIndex = contextTokens.indexOf(
|
|
3023
|
+
const parOpenIndex = contextTokens.indexOf('(');
|
|
2874
3024
|
scopeName = contextTokens[parOpenIndex + 1] || scopeName;
|
|
2875
3025
|
if (scopeName) {
|
|
2876
|
-
scopeType =
|
|
3026
|
+
scopeType = 'method';
|
|
2877
3027
|
}
|
|
2878
3028
|
}
|
|
2879
3029
|
// Only push if it's not already reflected in the cached scopes
|
|
2880
3030
|
const lastScope = this._scopeStack.at(-1);
|
|
2881
3031
|
if (lastScope?.lineNumber !== lineNumber) {
|
|
2882
|
-
this._scopeStack.push({ name: scopeName ??
|
|
3032
|
+
this._scopeStack.push({ name: scopeName ?? '', type: scopeType ?? 'anonymous', lineNumber });
|
|
2883
3033
|
}
|
|
2884
3034
|
pushedScope = true;
|
|
2885
3035
|
}
|
|
@@ -2890,14 +3040,14 @@ class CodeEditor {
|
|
|
2890
3040
|
}
|
|
2891
3041
|
_getLineSignatureFromTokens(tokens) {
|
|
2892
3042
|
const structuralChars = new Set(['{', '}']);
|
|
2893
|
-
const sign = tokens.filter(t => structuralChars.has(t));
|
|
2894
|
-
return sign.join(
|
|
3043
|
+
const sign = tokens.filter((t) => structuralChars.has(t));
|
|
3044
|
+
return sign.join('_');
|
|
2895
3045
|
}
|
|
2896
3046
|
_updateBlockComments(section, lineNumber, tokens) {
|
|
2897
3047
|
const lang = CodeEditor.languages[this.highlight];
|
|
2898
3048
|
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
2899
|
-
const lineOpensBlock =
|
|
2900
|
-
const lineClosesBlock =
|
|
3049
|
+
const lineOpensBlock = section[0].x === lineNumber;
|
|
3050
|
+
const lineClosesBlock = section[0].y === lineNumber;
|
|
2901
3051
|
(section[0].x !== lineNumber) && (section[0].y !== lineNumber);
|
|
2902
3052
|
delete this._buildingBlockComment;
|
|
2903
3053
|
/*
|
|
@@ -2905,7 +3055,7 @@ class CodeEditor {
|
|
|
2905
3055
|
until reaching new delimiters
|
|
2906
3056
|
*/
|
|
2907
3057
|
if (lineOpensBlock) {
|
|
2908
|
-
const r = tokens.filter(t => t.substr(0, blockCommentsTokens[0].length) == blockCommentsTokens[0]);
|
|
3058
|
+
const r = tokens.filter((t) => t.substr(0, blockCommentsTokens[0].length) == blockCommentsTokens[0]);
|
|
2909
3059
|
if (!r.length) {
|
|
2910
3060
|
this._buildingBlockComment = [lineNumber - 1, 0];
|
|
2911
3061
|
this.mustProcessPreviousLine = (tokens) => {
|
|
@@ -2927,7 +3077,7 @@ class CodeEditor {
|
|
|
2927
3077
|
}
|
|
2928
3078
|
}
|
|
2929
3079
|
else if (lineClosesBlock) {
|
|
2930
|
-
const r = tokens.filter(t => t.substr(0, blockCommentsTokens[1].length) == blockCommentsTokens[1]);
|
|
3080
|
+
const r = tokens.filter((t) => t.substr(0, blockCommentsTokens[1].length) == blockCommentsTokens[1]);
|
|
2931
3081
|
if (!r.length) {
|
|
2932
3082
|
this._buildingBlockComment = [section[0].x, section[1].x];
|
|
2933
3083
|
this.mustProcessNextLine = (tokens) => {
|
|
@@ -2952,7 +3102,7 @@ class CodeEditor {
|
|
|
2952
3102
|
}
|
|
2953
3103
|
_processExtraLineIfNecessary(lineNumber, tokens, oldSymbols, skipPropagation = false) {
|
|
2954
3104
|
if (!this._scopeStack) {
|
|
2955
|
-
console.warn(
|
|
3105
|
+
console.warn('CodeEditor: No scope available');
|
|
2956
3106
|
return;
|
|
2957
3107
|
}
|
|
2958
3108
|
// Update block comments if necessary
|
|
@@ -2969,7 +3119,7 @@ class CodeEditor {
|
|
|
2969
3119
|
}
|
|
2970
3120
|
const newSignature = this._getLineSignatureFromTokens(tokens);
|
|
2971
3121
|
const cachedSignature = this.code.lineSignatures[lineNumber];
|
|
2972
|
-
const mustUpdateScopes =
|
|
3122
|
+
const mustUpdateScopes = cachedSignature !== newSignature;
|
|
2973
3123
|
const sameScopes = codeScopesEqual(this._scopeStack, this.code.lineScopes[lineNumber + 1]);
|
|
2974
3124
|
// Only update scope stack if something changed when editing a single line
|
|
2975
3125
|
// Compare line signature for structural changes
|
|
@@ -3000,12 +3150,15 @@ class CodeEditor {
|
|
|
3000
3150
|
_updateLine(force = false, lineNumber, html, skipPropagation = false, symbols = [], tokens = []) {
|
|
3001
3151
|
const gutterLineHtml = `<span class='line-gutter'>${lineNumber + 1}</span>`;
|
|
3002
3152
|
const oldSymbols = this._updateLineSymbols(lineNumber, symbols);
|
|
3003
|
-
const lineScope = CodeEditor.debugScopes && this.code.lineScopes[lineNumber]
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3153
|
+
const lineScope = CodeEditor.debugScopes && this.code.lineScopes[lineNumber]
|
|
3154
|
+
? this.code.lineScopes[lineNumber].map((s) => `${s.type}`).join(', ')
|
|
3155
|
+
: '';
|
|
3156
|
+
const lineSymbols = CodeEditor.debugSymbols && this.code.lineSymbols[lineNumber]
|
|
3157
|
+
? this.code.lineSymbols[lineNumber].map((s) => `${s.name}(${s.kind})`).join(', ')
|
|
3158
|
+
: '';
|
|
3159
|
+
const debugString = lineScope + (lineScope.length ? ' - ' : '') + lineSymbols;
|
|
3160
|
+
if (!force) { // Single line update
|
|
3161
|
+
this.code.childNodes[this.toLocalLine(lineNumber)].innerHTML = gutterLineHtml + html + debugString;
|
|
3009
3162
|
if (!skipPropagation) {
|
|
3010
3163
|
this._processExtraLineIfNecessary(lineNumber, tokens, oldSymbols, skipPropagation);
|
|
3011
3164
|
}
|
|
@@ -3026,7 +3179,7 @@ class CodeEditor {
|
|
|
3026
3179
|
}
|
|
3027
3180
|
}
|
|
3028
3181
|
if (CodeEditor.debugProcessedLines) {
|
|
3029
|
-
this.code.childNodes[lineNumber]?.classList.add(
|
|
3182
|
+
this.code.childNodes[lineNumber]?.classList.add('debug');
|
|
3030
3183
|
}
|
|
3031
3184
|
this._setActiveLine(lineNumber);
|
|
3032
3185
|
this._clearTmpVariables();
|
|
@@ -3058,31 +3211,34 @@ class CodeEditor {
|
|
|
3058
3211
|
symbols.push(s);
|
|
3059
3212
|
};
|
|
3060
3213
|
// Don't make symbols from preprocessor lines
|
|
3061
|
-
if (text.startsWith(
|
|
3214
|
+
if (text.startsWith('#')) {
|
|
3062
3215
|
return [];
|
|
3063
3216
|
}
|
|
3064
3217
|
const nativeTypes = CodeEditor.nativeTypes[this.highlight];
|
|
3065
3218
|
const topLevelRegexes = [
|
|
3066
|
-
[/^class\s+([A-Za-z0-9_]+)/,
|
|
3067
|
-
[/^struct\s+([A-Za-z0-9_]+)/,
|
|
3068
|
-
[/^enum\s+([A-Za-z0-9_]+)/,
|
|
3069
|
-
[/^interface\s+([A-Za-z0-9_]+)/,
|
|
3070
|
-
[/^type\s+([A-Za-z0-9_]+)/,
|
|
3071
|
-
[/^function\s+([A-Za-z0-9_]+)/,
|
|
3072
|
-
[/^fn\s+([A-Za-z0-9_]+)/,
|
|
3073
|
-
[/^def\s+([A-Za-z0-9_]+)/,
|
|
3074
|
-
[/^([A-Za-z0-9_]+)\s*=\s*\(?.*\)?\s*=>/,
|
|
3219
|
+
[/^class\s+([A-Za-z0-9_]+)/, 'class'],
|
|
3220
|
+
[/^struct\s+([A-Za-z0-9_]+)/, 'struct'],
|
|
3221
|
+
[/^enum\s+([A-Za-z0-9_]+)/, 'enum'],
|
|
3222
|
+
[/^interface\s+([A-Za-z0-9_]+)/, 'interface'],
|
|
3223
|
+
[/^type\s+([A-Za-z0-9_]+)/, 'type'],
|
|
3224
|
+
[/^function\s+([A-Za-z0-9_]+)/, 'method'],
|
|
3225
|
+
[/^fn\s+([A-Za-z0-9_]+)/, 'method'],
|
|
3226
|
+
[/^def\s+([A-Za-z0-9_]+)/, 'method'],
|
|
3227
|
+
[/^([A-Za-z0-9_]+)\s*=\s*\(?.*\)?\s*=>/, 'method'] // arrow functions
|
|
3075
3228
|
];
|
|
3076
3229
|
// Add regexes to detect methods, variables ( including "id : nativeType" )
|
|
3077
3230
|
{
|
|
3078
3231
|
if (nativeTypes) {
|
|
3079
|
-
topLevelRegexes.push([new RegExp(`^(?:${nativeTypes.join('|')})\\s+([A-Za-z0-9_]+)\s*[\(]+`),
|
|
3080
|
-
|
|
3081
|
-
|
|
3232
|
+
topLevelRegexes.push([new RegExp(`^(?:${nativeTypes.join('|')})\\s+([A-Za-z0-9_]+)\s*[\(]+`),
|
|
3233
|
+
'method']);
|
|
3234
|
+
if (this.highlight === 'WGSL') {
|
|
3235
|
+
topLevelRegexes.push([new RegExp(`[A-Za-z0-9]+(\\s*)+:(\\s*)+(${nativeTypes.join('|')})`),
|
|
3236
|
+
'variable', (m) => m[0].split(':')[0].trim()]);
|
|
3082
3237
|
}
|
|
3083
3238
|
}
|
|
3084
|
-
const declarationKeywords = CodeEditor.declarationKeywords[this.highlight] ?? [
|
|
3085
|
-
topLevelRegexes.push([new RegExp(`^(?:${declarationKeywords.join('|')})\\s+([A-Za-z0-9_]+)`),
|
|
3239
|
+
const declarationKeywords = CodeEditor.declarationKeywords[this.highlight] ?? ['const', 'let', 'var'];
|
|
3240
|
+
topLevelRegexes.push([new RegExp(`^(?:${declarationKeywords.join('|')})\\s+([A-Za-z0-9_]+)`),
|
|
3241
|
+
'variable']);
|
|
3086
3242
|
}
|
|
3087
3243
|
for (let [regex, kind, fn] of topLevelRegexes) {
|
|
3088
3244
|
const m = text.match(regex);
|
|
@@ -3091,8 +3247,8 @@ class CodeEditor {
|
|
|
3091
3247
|
}
|
|
3092
3248
|
}
|
|
3093
3249
|
const usageRegexes = [
|
|
3094
|
-
[/new\s+([A-Za-z0-9_]+)\s*\(/,
|
|
3095
|
-
[/this.([A-Za-z_][A-Za-z0-9_]*)\s*\=/,
|
|
3250
|
+
[/new\s+([A-Za-z0-9_]+)\s*\(/, 'constructor-call'],
|
|
3251
|
+
[/this.([A-Za-z_][A-Za-z0-9_]*)\s*\=/, 'class-property']
|
|
3096
3252
|
];
|
|
3097
3253
|
for (let [regex, kind, fn] of usageRegexes) {
|
|
3098
3254
|
const m = text.match(regex);
|
|
@@ -3108,31 +3264,32 @@ class CodeEditor {
|
|
|
3108
3264
|
const before = text.slice(0, match.index);
|
|
3109
3265
|
if (/(new|function|fn|def)\s+$/.test(before))
|
|
3110
3266
|
continue; // skip constructor calls
|
|
3111
|
-
if ([
|
|
3267
|
+
if (['constructor', 'location', ...(nativeTypes ?? [])].indexOf(name) > -1)
|
|
3112
3268
|
continue; // skip hardcoded non method symbol
|
|
3113
|
-
if (previousLineScope && previousLineScope.at(-1)?.type ===
|
|
3269
|
+
if (previousLineScope && previousLineScope.at(-1)?.type === 'class')
|
|
3114
3270
|
continue; // skip class methods
|
|
3115
|
-
_pushSymbol({ name, kind:
|
|
3271
|
+
_pushSymbol({ name, kind: 'method-call', scope: scopeName, line: lineNumber });
|
|
3116
3272
|
}
|
|
3117
3273
|
// Stop after matches for top-level declarations and usage symbols
|
|
3118
3274
|
if (symbols.length) {
|
|
3119
3275
|
return symbols;
|
|
3120
3276
|
}
|
|
3121
|
-
const nonWhiteSpaceTokens = tokens.filter(t => t.trim().length);
|
|
3277
|
+
const nonWhiteSpaceTokens = tokens.filter((t) => t.trim().length);
|
|
3122
3278
|
for (let i = 0; i < nonWhiteSpaceTokens.length; i++) {
|
|
3123
3279
|
const prev = nonWhiteSpaceTokens[i - 1];
|
|
3124
3280
|
const token = nonWhiteSpaceTokens[i];
|
|
3125
3281
|
const next = nonWhiteSpaceTokens[i + 1];
|
|
3126
|
-
if (scopeType.startsWith(
|
|
3127
|
-
if (next ===
|
|
3128
|
-
if (token ===
|
|
3282
|
+
if (scopeType.startsWith('class')) {
|
|
3283
|
+
if (next === '(' && /^[a-zA-Z_]\w*$/.test(token) && prev === undefined) {
|
|
3284
|
+
if (token === 'constructor')
|
|
3129
3285
|
continue; // skip constructor symbol
|
|
3130
|
-
_pushSymbol({ name: token, kind:
|
|
3286
|
+
_pushSymbol({ name: token, kind: 'method', scope: scopeName, line: lineNumber });
|
|
3131
3287
|
}
|
|
3132
3288
|
}
|
|
3133
|
-
else if (scopeType.startsWith(
|
|
3134
|
-
if (!isSymbol(token) && !this._isNumber(token)
|
|
3135
|
-
|
|
3289
|
+
else if (scopeType.startsWith('enum')) {
|
|
3290
|
+
if (!isSymbol(token) && !this._isNumber(token)
|
|
3291
|
+
&& !this._mustHightlightWord(token, CodeEditor.statements)) {
|
|
3292
|
+
_pushSymbol({ name: token, kind: 'enum_value', scope: scopeName, line: lineNumber });
|
|
3136
3293
|
}
|
|
3137
3294
|
}
|
|
3138
3295
|
}
|
|
@@ -3182,8 +3339,8 @@ class CodeEditor {
|
|
|
3182
3339
|
// Count times we started a string BEFORE the comment
|
|
3183
3340
|
var err = false;
|
|
3184
3341
|
err = err || (stringKeys.some(function (v) {
|
|
3185
|
-
var re = new RegExp(v,
|
|
3186
|
-
var matches =
|
|
3342
|
+
var re = new RegExp(v, 'g');
|
|
3343
|
+
var matches = lineString.substring(0, idx).match(re) || [];
|
|
3187
3344
|
return (matches.length % 2) !== 0;
|
|
3188
3345
|
}));
|
|
3189
3346
|
return err ? undefined : idx;
|
|
@@ -3203,8 +3360,9 @@ class CodeEditor {
|
|
|
3203
3360
|
let charCounterList = [];
|
|
3204
3361
|
let charCounter = 0;
|
|
3205
3362
|
const pushToken = function (t) {
|
|
3206
|
-
if ((skipNonWords && (t.includes('"') || t.length < 3)))
|
|
3363
|
+
if ((skipNonWords && (t.includes('"') || t.length < 3))) {
|
|
3207
3364
|
return;
|
|
3365
|
+
}
|
|
3208
3366
|
tokensToEvaluate.push(t);
|
|
3209
3367
|
charCounterList.push(charCounter);
|
|
3210
3368
|
// Update positions
|
|
@@ -3252,7 +3410,7 @@ class CodeEditor {
|
|
|
3252
3410
|
}
|
|
3253
3411
|
const importantIdx = tokens.indexOf('important');
|
|
3254
3412
|
if (this.highlight == 'CSS' && importantIdx > -1 && tokens[importantIdx - 1] === '!') {
|
|
3255
|
-
tokens[importantIdx - 1] =
|
|
3413
|
+
tokens[importantIdx - 1] = '!important';
|
|
3256
3414
|
tokens.splice(importantIdx, 1);
|
|
3257
3415
|
}
|
|
3258
3416
|
}
|
|
@@ -3262,11 +3420,11 @@ class CodeEditor {
|
|
|
3262
3420
|
while (dollarIdx > -1) {
|
|
3263
3421
|
const offsetIdx = dollarIdx + offset;
|
|
3264
3422
|
if (tokens[offsetIdx + 1] === 'this-') {
|
|
3265
|
-
tokens[offsetIdx] =
|
|
3266
|
-
tokens[offsetIdx + 1] =
|
|
3423
|
+
tokens[offsetIdx] = '$this';
|
|
3424
|
+
tokens[offsetIdx + 1] = '-';
|
|
3267
3425
|
}
|
|
3268
3426
|
else {
|
|
3269
|
-
tokens[offsetIdx] +=
|
|
3427
|
+
tokens[offsetIdx] += tokens[offsetIdx + 1] ?? '';
|
|
3270
3428
|
tokens.splice(offsetIdx + 1, 1);
|
|
3271
3429
|
}
|
|
3272
3430
|
dollarIdx = tokens.slice(offsetIdx).indexOf('$');
|
|
@@ -3278,7 +3436,7 @@ class CodeEditor {
|
|
|
3278
3436
|
let atIdx = tokens.indexOf('@');
|
|
3279
3437
|
while (atIdx > -1) {
|
|
3280
3438
|
const offsetIdx = atIdx + offset;
|
|
3281
|
-
tokens[offsetIdx] +=
|
|
3439
|
+
tokens[offsetIdx] += tokens[offsetIdx + 1] ?? '';
|
|
3282
3440
|
tokens.splice(offsetIdx + 1, 1);
|
|
3283
3441
|
atIdx = tokens.slice(offsetIdx).indexOf('$');
|
|
3284
3442
|
offset = offsetIdx;
|
|
@@ -3297,7 +3455,8 @@ class CodeEditor {
|
|
|
3297
3455
|
return wordCategory[this.highlight] && wordCategory[this.highlight].has(t);
|
|
3298
3456
|
}
|
|
3299
3457
|
_getTokenHighlighting(ctx, highlight) {
|
|
3300
|
-
const rules = [...HighlightRules.common, ...(HighlightRules[highlight] || []),
|
|
3458
|
+
const rules = [...HighlightRules.common, ...(HighlightRules[highlight] || []),
|
|
3459
|
+
...HighlightRules.post_common];
|
|
3301
3460
|
for (const rule of rules) {
|
|
3302
3461
|
if (!rule.test(ctx, this)) {
|
|
3303
3462
|
continue;
|
|
@@ -3312,14 +3471,14 @@ class CodeEditor {
|
|
|
3312
3471
|
_evaluateToken(ctxData) {
|
|
3313
3472
|
let { token, prev, next, tokenIndex, isFirstToken, isLastToken } = ctxData;
|
|
3314
3473
|
const lang = CodeEditor.languages[this.highlight];
|
|
3315
|
-
const highlight = this.highlight.replace(/\s/g, '').replaceAll(
|
|
3474
|
+
const highlight = this.highlight.replace(/\s/g, '').replaceAll('+', 'p').toLowerCase();
|
|
3316
3475
|
const customStringKeys = Object.assign({}, this.stringKeys);
|
|
3317
3476
|
const lineNumber = this._currentLineNumber;
|
|
3318
3477
|
const tokenStartIndex = this._currentTokenPositions[tokenIndex];
|
|
3319
|
-
const inBlockComment = (this._buildingBlockComment ?? this._inBlockCommentSection(lineNumber, tokenStartIndex, token.length))
|
|
3478
|
+
const inBlockComment = (this._buildingBlockComment ?? this._inBlockCommentSection(lineNumber, tokenStartIndex, token.length))
|
|
3479
|
+
!== undefined;
|
|
3320
3480
|
var usePreviousTokenToCheckString = false;
|
|
3321
|
-
if (['cpp', 'c'].indexOf(highlight) > -1 && prev && prev.includes('#')) // preprocessor code..
|
|
3322
|
-
{
|
|
3481
|
+
if (['cpp', 'c'].indexOf(highlight) > -1 && prev && prev.includes('#')) { // preprocessor code..
|
|
3323
3482
|
customStringKeys['@<'] = '>';
|
|
3324
3483
|
}
|
|
3325
3484
|
else if (highlight == 'markdown' && (ctxData.prevWithSpaces == '[' || ctxData.nextWithSpaces == ']')) {
|
|
@@ -3328,14 +3487,16 @@ class CodeEditor {
|
|
|
3328
3487
|
customStringKeys['@['] = ']';
|
|
3329
3488
|
}
|
|
3330
3489
|
else if (highlight == 'javascript' || highlight == 'typescript') {
|
|
3331
|
-
customStringKeys[
|
|
3490
|
+
customStringKeys['@`'] = '`';
|
|
3332
3491
|
}
|
|
3333
3492
|
// Manage strings
|
|
3334
3493
|
this._stringEnded = false;
|
|
3335
|
-
if (usePreviousTokenToCheckString
|
|
3494
|
+
if (usePreviousTokenToCheckString
|
|
3495
|
+
|| (!inBlockComment
|
|
3496
|
+
&& (lang.tags ?? false ? (this._enclosedByTokens(token, tokenIndex, '<', '>')) : true))) {
|
|
3336
3497
|
const _checkIfStringEnded = (t) => {
|
|
3337
3498
|
if (this._stringInterpolation) {
|
|
3338
|
-
if (token ==
|
|
3499
|
+
if (token == '$' && next == '{') {
|
|
3339
3500
|
delete this._stringInterpolation;
|
|
3340
3501
|
this._stringInterpolationOpened = true;
|
|
3341
3502
|
this._stringEnded = true;
|
|
@@ -3343,15 +3504,17 @@ class CodeEditor {
|
|
|
3343
3504
|
}
|
|
3344
3505
|
}
|
|
3345
3506
|
const idx = Object.values(customStringKeys).indexOf(t);
|
|
3346
|
-
this._stringEnded = (idx > -1)
|
|
3507
|
+
this._stringEnded = (idx > -1)
|
|
3508
|
+
&& (idx
|
|
3509
|
+
== Object.values(customStringKeys).indexOf(customStringKeys['@' + this._buildingString]));
|
|
3347
3510
|
};
|
|
3348
3511
|
if (this._buildingString != undefined) {
|
|
3349
3512
|
_checkIfStringEnded(usePreviousTokenToCheckString ? ctxData.nextWithSpaces : token);
|
|
3350
3513
|
}
|
|
3351
3514
|
else if (customStringKeys['@' + (usePreviousTokenToCheckString ? ctxData.prevWithSpaces : token)]) {
|
|
3352
3515
|
// Start new string
|
|
3353
|
-
this._buildingString =
|
|
3354
|
-
if ((highlight == 'javascript' || highlight == 'typescript') && token ==
|
|
3516
|
+
this._buildingString = usePreviousTokenToCheckString ? ctxData.prevWithSpaces : token;
|
|
3517
|
+
if ((highlight == 'javascript' || highlight == 'typescript') && token == '`') {
|
|
3355
3518
|
this._stringInterpolation = true;
|
|
3356
3519
|
}
|
|
3357
3520
|
// Check if string ended in same token using next...
|
|
@@ -3359,47 +3522,47 @@ class CodeEditor {
|
|
|
3359
3522
|
_checkIfStringEnded(ctxData.nextWithSpaces);
|
|
3360
3523
|
}
|
|
3361
3524
|
}
|
|
3362
|
-
else if (this._stringInterpolationOpened && prev ==
|
|
3525
|
+
else if (this._stringInterpolationOpened && prev == '}') {
|
|
3363
3526
|
delete this._stringInterpolationOpened;
|
|
3364
3527
|
this._stringInterpolation = true;
|
|
3365
|
-
this._buildingString =
|
|
3528
|
+
this._buildingString = '`';
|
|
3366
3529
|
}
|
|
3367
3530
|
}
|
|
3368
3531
|
// Update context data for next tests
|
|
3369
3532
|
ctxData.discardToken = false;
|
|
3370
3533
|
ctxData.inBlockComment = inBlockComment;
|
|
3371
3534
|
ctxData.markdownHeader = this._markdownHeader;
|
|
3372
|
-
ctxData.inString =
|
|
3535
|
+
ctxData.inString = this._buildingString !== undefined;
|
|
3373
3536
|
ctxData.singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
3374
3537
|
ctxData.lang = lang;
|
|
3375
3538
|
ctxData.scope = this._scopeStack.at(-1);
|
|
3376
3539
|
// Add utils functions for the rules
|
|
3377
|
-
ctxData.isVariableSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind ===
|
|
3378
|
-
ctxData.isEnumValueSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind ===
|
|
3379
|
-
ctxData.isClassSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind ===
|
|
3380
|
-
ctxData.isStructSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind ===
|
|
3381
|
-
ctxData.isEnumSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind ===
|
|
3540
|
+
ctxData.isVariableSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind === 'variable';
|
|
3541
|
+
ctxData.isEnumValueSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind === 'enum_value';
|
|
3542
|
+
ctxData.isClassSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind === 'class';
|
|
3543
|
+
ctxData.isStructSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind === 'struct';
|
|
3544
|
+
ctxData.isEnumSymbol = (token) => this.code.symbolsTable.has(token) && this.code.symbolsTable.get(token)[0].kind === 'enum';
|
|
3382
3545
|
// Get highlighting class based on language common and specific rules
|
|
3383
3546
|
let tokenClass = this._getTokenHighlighting(ctxData, highlight);
|
|
3384
3547
|
if (this._stringInterpolationOpened && this._pendingString) {
|
|
3385
|
-
this._pendingString = this._pendingString.substring(0, this._pendingString.indexOf(
|
|
3386
|
-
if (ctxData.tokens[tokenIndex + 1] ==
|
|
3387
|
-
ctxData.tokens[tokenIndex + 1] =
|
|
3548
|
+
this._pendingString = this._pendingString.substring(0, this._pendingString.indexOf('$'));
|
|
3549
|
+
if (ctxData.tokens[tokenIndex + 1] == '{') {
|
|
3550
|
+
ctxData.tokens[tokenIndex + 1] = '${';
|
|
3388
3551
|
}
|
|
3389
3552
|
}
|
|
3390
3553
|
// We finished constructing a string
|
|
3391
3554
|
if (this._buildingString && (this._stringEnded || isLastToken) && !inBlockComment) {
|
|
3392
3555
|
token = this._getCurrentString();
|
|
3393
|
-
tokenClass =
|
|
3556
|
+
tokenClass = 'cm-str';
|
|
3394
3557
|
ctxData.discardToken = false;
|
|
3395
3558
|
}
|
|
3396
3559
|
// Update state
|
|
3397
3560
|
this._buildingString = this._stringEnded ? undefined : this._buildingString;
|
|
3398
3561
|
if (ctxData.discardToken) {
|
|
3399
|
-
return
|
|
3562
|
+
return '';
|
|
3400
3563
|
}
|
|
3401
3564
|
// Replace html chars
|
|
3402
|
-
token = token.replace(
|
|
3565
|
+
token = token.replace('<', '<').replace('>', '>');
|
|
3403
3566
|
// No highlighting, no need to put it inside another span..
|
|
3404
3567
|
if (!tokenClass) {
|
|
3405
3568
|
return token;
|
|
@@ -3408,7 +3571,7 @@ class CodeEditor {
|
|
|
3408
3571
|
}
|
|
3409
3572
|
_appendStringToken(token) {
|
|
3410
3573
|
if (!this._pendingString) {
|
|
3411
|
-
this._pendingString =
|
|
3574
|
+
this._pendingString = '';
|
|
3412
3575
|
}
|
|
3413
3576
|
this._pendingString += token;
|
|
3414
3577
|
return true;
|
|
@@ -3421,15 +3584,19 @@ class CodeEditor {
|
|
|
3421
3584
|
_enclosedByTokens(token, tokenIndex, tagStart, tagEnd) {
|
|
3422
3585
|
const tokenStartIndex = this._currentTokenPositions[tokenIndex];
|
|
3423
3586
|
const tagStartIndex = indexOfFrom(this._currentLineString, tagStart, tokenStartIndex, true);
|
|
3424
|
-
if (tagStartIndex < 0) // Not found..
|
|
3587
|
+
if (tagStartIndex < 0) { // Not found..
|
|
3425
3588
|
return;
|
|
3589
|
+
}
|
|
3426
3590
|
const tagStartIndexOpposite = indexOfFrom(this._currentLineString, tagEnd, tokenStartIndex, true);
|
|
3427
|
-
if (tagStartIndexOpposite >= 0 && tagStartIndexOpposite > tagStartIndex) // Found the opposite first while reversing..
|
|
3591
|
+
if (tagStartIndexOpposite >= 0 && tagStartIndexOpposite > tagStartIndex) { // Found the opposite first while reversing..
|
|
3428
3592
|
return;
|
|
3593
|
+
}
|
|
3429
3594
|
const tagEndIndex = indexOfFrom(this._currentLineString, tagEnd, tokenStartIndex);
|
|
3430
|
-
if (tagEndIndex < 0) // Not found..
|
|
3595
|
+
if (tagEndIndex < 0) { // Not found..
|
|
3431
3596
|
return;
|
|
3432
|
-
|
|
3597
|
+
}
|
|
3598
|
+
if ((tagStartIndex < tokenStartIndex) && (tagEndIndex >= (tokenStartIndex + token.length))
|
|
3599
|
+
&& !this._mustHightlightWord(token, CodeEditor.symbols)) {
|
|
3433
3600
|
return [tagStartIndex, tagEndIndex];
|
|
3434
3601
|
}
|
|
3435
3602
|
}
|
|
@@ -3440,15 +3607,18 @@ class CodeEditor {
|
|
|
3440
3607
|
const lineRange = section[0];
|
|
3441
3608
|
const posRange = section[1];
|
|
3442
3609
|
// Outside the lines range
|
|
3443
|
-
const meetsLineRange =
|
|
3610
|
+
const meetsLineRange = lineNumber >= lineRange.x && lineNumber <= lineRange.y;
|
|
3444
3611
|
if (!meetsLineRange) {
|
|
3445
3612
|
continue;
|
|
3446
3613
|
}
|
|
3447
|
-
if ((lineNumber != lineRange.x && lineNumber != lineRange.y)
|
|
3448
|
-
(lineNumber == lineRange.x && tokenPosition >= posRange.x
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3614
|
+
if ((lineNumber != lineRange.x && lineNumber != lineRange.y) // Inside the block, not first nor last line
|
|
3615
|
+
|| (lineNumber == lineRange.x && tokenPosition >= posRange.x
|
|
3616
|
+
&& ((lineNumber == lineRange.y
|
|
3617
|
+
&& (tokenPosition + tokenLength) <= (posRange.y + blockCommentsTokens[1].length))
|
|
3618
|
+
|| lineNumber !== lineRange.y))
|
|
3619
|
+
|| (lineNumber == lineRange.y
|
|
3620
|
+
&& ((tokenPosition + tokenLength) <= (posRange.y + blockCommentsTokens[1].length)))
|
|
3621
|
+
&& ((lineNumber == lineRange.x && tokenPosition >= posRange.x) || lineNumber !== lineRange.x)) {
|
|
3452
3622
|
return section;
|
|
3453
3623
|
}
|
|
3454
3624
|
}
|
|
@@ -3467,7 +3637,7 @@ class CodeEditor {
|
|
|
3467
3637
|
}
|
|
3468
3638
|
}
|
|
3469
3639
|
if (this.highlight == 'Markdown') {
|
|
3470
|
-
isKwd =
|
|
3640
|
+
isKwd = this._markdownHeader !== undefined;
|
|
3471
3641
|
}
|
|
3472
3642
|
else if (lang.tags) {
|
|
3473
3643
|
isKwd = isKwd && (this._enclosedByTokens(token, tokenIndex, '<', '>') != undefined);
|
|
@@ -3516,13 +3686,13 @@ class CodeEditor {
|
|
|
3516
3686
|
// Change next key?
|
|
3517
3687
|
switch (key) {
|
|
3518
3688
|
case "'":
|
|
3519
|
-
case "
|
|
3689
|
+
case '"':
|
|
3520
3690
|
break;
|
|
3521
|
-
case
|
|
3522
|
-
key =
|
|
3691
|
+
case '(':
|
|
3692
|
+
key = ')';
|
|
3523
3693
|
break;
|
|
3524
|
-
case
|
|
3525
|
-
key =
|
|
3694
|
+
case '{':
|
|
3695
|
+
key = '}';
|
|
3526
3696
|
break;
|
|
3527
3697
|
}
|
|
3528
3698
|
// Insert the other
|
|
@@ -3544,13 +3714,13 @@ class CodeEditor {
|
|
|
3544
3714
|
const scores = {};
|
|
3545
3715
|
// Check strong indicators first
|
|
3546
3716
|
const strongIndicators = {
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3717
|
+
'JavaScript': ['import ', 'export default', 'console.', '=>', 'document.', 'window.'],
|
|
3718
|
+
'TypeScript': ['import ', 'export default', 'console.', '=>', 'document.', 'window.'],
|
|
3719
|
+
'C++': ['#include', '::', 'std::', 'template <', 'using namespace'],
|
|
3720
|
+
'Python': ['def ', 'import ', 'print(', 'self', 'None', 'True', 'False'],
|
|
3721
|
+
'HTML': ['<html', '<div', '<body', '<script', '<style'],
|
|
3722
|
+
'CSS': ['@media'],
|
|
3723
|
+
'Markdown': ['#', '##', '###', '](', '![', '**']
|
|
3554
3724
|
};
|
|
3555
3725
|
for (const [lang, indicators] of Object.entries(strongIndicators)) {
|
|
3556
3726
|
scores[lang] = scores[lang] ?? 0;
|
|
@@ -3565,30 +3735,33 @@ class CodeEditor {
|
|
|
3565
3735
|
CodeEditor.statements,
|
|
3566
3736
|
CodeEditor.utils,
|
|
3567
3737
|
CodeEditor.types,
|
|
3568
|
-
CodeEditor.builtIn
|
|
3738
|
+
CodeEditor.builtIn
|
|
3569
3739
|
];
|
|
3570
3740
|
for (const group of groups) {
|
|
3571
3741
|
for (let [lang, wordList] of Object.entries(group)) {
|
|
3572
3742
|
scores[lang] = scores[lang] ?? 0;
|
|
3573
|
-
for (let kw of wordList)
|
|
3743
|
+
for (let kw of wordList) {
|
|
3574
3744
|
if (tokenSet.has(kw))
|
|
3575
3745
|
scores[lang]++;
|
|
3746
|
+
}
|
|
3576
3747
|
}
|
|
3577
3748
|
}
|
|
3578
3749
|
const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
3579
3750
|
return sorted[0][1] > 0 ? sorted[0][0] : undefined;
|
|
3580
3751
|
}
|
|
3581
3752
|
lineUp(cursor, resetLeft = false) {
|
|
3582
|
-
if (this.code.lines[cursor.line - 1] == undefined)
|
|
3753
|
+
if (this.code.lines[cursor.line - 1] == undefined) {
|
|
3583
3754
|
return false;
|
|
3755
|
+
}
|
|
3584
3756
|
cursor.line--;
|
|
3585
3757
|
cursor.line = Math.max(0, cursor.line);
|
|
3586
3758
|
this.cursorToTop(cursor, resetLeft);
|
|
3587
3759
|
return true;
|
|
3588
3760
|
}
|
|
3589
3761
|
lineDown(cursor, resetLeft = false) {
|
|
3590
|
-
if (this.code.lines[cursor.line + 1] == undefined)
|
|
3762
|
+
if (this.code.lines[cursor.line + 1] == undefined) {
|
|
3591
3763
|
return false;
|
|
3764
|
+
}
|
|
3592
3765
|
cursor.line++;
|
|
3593
3766
|
this.cursorToBottom(cursor, resetLeft);
|
|
3594
3767
|
return true;
|
|
@@ -3615,12 +3788,14 @@ class CodeEditor {
|
|
|
3615
3788
|
return;
|
|
3616
3789
|
clearInterval(this.blinker);
|
|
3617
3790
|
LX.addClass(this.cursorsDOM, 'show');
|
|
3618
|
-
if (this.cursorBlinkRate > 0)
|
|
3791
|
+
if (this.cursorBlinkRate > 0) {
|
|
3619
3792
|
this.blinker = setInterval(() => {
|
|
3620
3793
|
LX.toggleClass(this.cursorsDOM, 'show');
|
|
3621
3794
|
}, this.cursorBlinkRate);
|
|
3622
|
-
|
|
3795
|
+
}
|
|
3796
|
+
else if (this.cursorBlinkRate < 0) {
|
|
3623
3797
|
LX.removeClass(this.cursorsDOM, 'show');
|
|
3798
|
+
}
|
|
3624
3799
|
}
|
|
3625
3800
|
startSelection(cursor) {
|
|
3626
3801
|
// Show elements
|
|
@@ -3641,12 +3816,13 @@ class CodeEditor {
|
|
|
3641
3816
|
if (cursor.selection)
|
|
3642
3817
|
cursor.selection.invertIfNecessary();
|
|
3643
3818
|
const selection = cursor.selection;
|
|
3644
|
-
const separator =
|
|
3819
|
+
const separator = '_NEWLINE_';
|
|
3645
3820
|
let code = this.code.lines.join(separator);
|
|
3646
3821
|
// Get linear start index
|
|
3647
3822
|
let index = 0;
|
|
3648
|
-
for (let i = 0; i <= selection.fromY; i++)
|
|
3649
|
-
index +=
|
|
3823
|
+
for (let i = 0; i <= selection.fromY; i++) {
|
|
3824
|
+
index += i == selection.fromY ? selection.fromX : this.code.lines[i].length;
|
|
3825
|
+
}
|
|
3650
3826
|
index += selection.fromY * separator.length;
|
|
3651
3827
|
const num_chars = selection.chars + (selection.toY - selection.fromY) * separator.length;
|
|
3652
3828
|
const pre = code.slice(0, index);
|
|
@@ -3718,7 +3894,7 @@ class CodeEditor {
|
|
|
3718
3894
|
// Check if we need to add scroll; if not then we might have to reduce it
|
|
3719
3895
|
if (!this.updateScrollLeft(cursor)) {
|
|
3720
3896
|
const leftMargin = this.charWidth;
|
|
3721
|
-
const cursorX =
|
|
3897
|
+
const cursorX = cursor.position * this.charWidth;
|
|
3722
3898
|
const currentScrollLeft = this.getScrollLeft();
|
|
3723
3899
|
if (cursorX < (currentScrollLeft + leftMargin)) {
|
|
3724
3900
|
const scroll = Math.max(cursorX - leftMargin, 0);
|
|
@@ -3776,7 +3952,7 @@ class CodeEditor {
|
|
|
3776
3952
|
cursorToLine(cursor, line, resetLeft = false) {
|
|
3777
3953
|
cursor.line = line;
|
|
3778
3954
|
cursor.top = this.lineHeight * line;
|
|
3779
|
-
cursor.root.style.top = cursor.top +
|
|
3955
|
+
cursor.root.style.top = cursor.top + 'px';
|
|
3780
3956
|
if (resetLeft)
|
|
3781
3957
|
this.resetCursorPos(CodeEditor.CURSOR_LEFT, cursor);
|
|
3782
3958
|
}
|
|
@@ -3832,7 +4008,7 @@ class CodeEditor {
|
|
|
3832
4008
|
cursor = cursor ?? this.getCurrentCursor();
|
|
3833
4009
|
if (flag & CodeEditor.CURSOR_LEFT) {
|
|
3834
4010
|
cursor.left = 0;
|
|
3835
|
-
cursor.root.style.left =
|
|
4011
|
+
cursor.root.style.left = 'calc(' + this.xPadding + ')';
|
|
3836
4012
|
cursor.position = 0;
|
|
3837
4013
|
if (resetScroll) {
|
|
3838
4014
|
this.setScrollLeft(0);
|
|
@@ -3840,7 +4016,7 @@ class CodeEditor {
|
|
|
3840
4016
|
}
|
|
3841
4017
|
if (flag & CodeEditor.CURSOR_TOP) {
|
|
3842
4018
|
cursor.top = 0;
|
|
3843
|
-
cursor.root.style.top =
|
|
4019
|
+
cursor.root.style.top = '0px';
|
|
3844
4020
|
cursor.line = 0;
|
|
3845
4021
|
if (resetScroll) {
|
|
3846
4022
|
this.setScrollTop(0);
|
|
@@ -3851,11 +4027,12 @@ class CodeEditor {
|
|
|
3851
4027
|
// If cursor in that position exists, remove it instead..
|
|
3852
4028
|
const exists = this.cursors.find((v) => v.position == position && v.line == line);
|
|
3853
4029
|
if (exists && !force) {
|
|
3854
|
-
if (!exists.isMain)
|
|
4030
|
+
if (!exists.isMain) {
|
|
3855
4031
|
exists.remove();
|
|
4032
|
+
}
|
|
3856
4033
|
return null;
|
|
3857
4034
|
}
|
|
3858
|
-
let cursor = new Cursor(
|
|
4035
|
+
let cursor = new Cursor('cursor' + this.cursors.length, position, line, isMain, this);
|
|
3859
4036
|
this.cursors.push(cursor);
|
|
3860
4037
|
this.cursorsDOM.appendChild(cursor.root);
|
|
3861
4038
|
return cursor;
|
|
@@ -3898,7 +4075,7 @@ class CodeEditor {
|
|
|
3898
4075
|
}
|
|
3899
4076
|
let indentSpaces = lineStart % this.tabSpaces;
|
|
3900
4077
|
indentSpaces = indentSpaces == 0 ? this.tabSpaces : this.tabSpaces - indentSpaces;
|
|
3901
|
-
const spacesString =
|
|
4078
|
+
const spacesString = ' '.repeat(indentSpaces);
|
|
3902
4079
|
this.code.lines[lidx] = [
|
|
3903
4080
|
lineString.slice(0, lineStart),
|
|
3904
4081
|
spacesString,
|
|
@@ -3953,7 +4130,7 @@ class CodeEditor {
|
|
|
3953
4130
|
].join('');
|
|
3954
4131
|
this.processLine(lidx);
|
|
3955
4132
|
if (cursor.line === lidx) {
|
|
3956
|
-
this.cursorToString(cursor,
|
|
4133
|
+
this.cursorToString(cursor, ' '.repeat(indentSpaces), true);
|
|
3957
4134
|
}
|
|
3958
4135
|
if (cursor.selection) {
|
|
3959
4136
|
if (cursor.selection.fromY === lidx) {
|
|
@@ -3969,7 +4146,7 @@ class CodeEditor {
|
|
|
3969
4146
|
updateScrollLeft(cursor) {
|
|
3970
4147
|
cursor = cursor ?? this.getCurrentCursor();
|
|
3971
4148
|
const rightMargin = this.charWidth;
|
|
3972
|
-
const cursorX =
|
|
4149
|
+
const cursorX = cursor.position * this.charWidth;
|
|
3973
4150
|
const currentScrollLeft = this.getScrollLeft();
|
|
3974
4151
|
const viewportSizeX = this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH; // Gutter offset
|
|
3975
4152
|
const viewportX = viewportSizeX + currentScrollLeft;
|
|
@@ -4013,11 +4190,11 @@ class CodeEditor {
|
|
|
4013
4190
|
const maxLineLength = pMaxLength ?? this.getMaxLineLength();
|
|
4014
4191
|
this._lastMaxLineLength = maxLineLength;
|
|
4015
4192
|
scrollWidth = maxLineLength * this.charWidth + CodeEditor.LINE_GUTTER_WIDTH;
|
|
4016
|
-
this.codeSizer.style.minWidth = scrollWidth +
|
|
4193
|
+
this.codeSizer.style.minWidth = scrollWidth + 'px';
|
|
4017
4194
|
}
|
|
4018
4195
|
if (flag & CodeEditor.RESIZE_SCROLLBAR_V) {
|
|
4019
4196
|
scrollHeight = this.code.lines.length * this.lineHeight;
|
|
4020
|
-
this.codeSizer.style.minHeight = scrollHeight +
|
|
4197
|
+
this.codeSizer.style.minHeight = scrollHeight + 'px';
|
|
4021
4198
|
}
|
|
4022
4199
|
this.resizeScrollBars(flag);
|
|
4023
4200
|
if (onResize) {
|
|
@@ -4039,10 +4216,10 @@ class CodeEditor {
|
|
|
4039
4216
|
resizeScrollBars(flag = CodeEditor.RESIZE_SCROLLBAR_H_V) {
|
|
4040
4217
|
if (flag & CodeEditor.RESIZE_SCROLLBAR_V) {
|
|
4041
4218
|
const totalLinesInViewport = ((this.codeScroller.offsetHeight) / this.lineHeight) | 0;
|
|
4042
|
-
const needsVerticalScrollbar =
|
|
4219
|
+
const needsVerticalScrollbar = this.code.lines.length >= totalLinesInViewport;
|
|
4043
4220
|
if (needsVerticalScrollbar) {
|
|
4044
|
-
this.vScrollbar.thumb.size =
|
|
4045
|
-
this.vScrollbar.thumb.style.height = (this.vScrollbar.thumb.size * 100.0) +
|
|
4221
|
+
this.vScrollbar.thumb.size = totalLinesInViewport / this.code.lines.length;
|
|
4222
|
+
this.vScrollbar.thumb.style.height = (this.vScrollbar.thumb.size * 100.0) + '%';
|
|
4046
4223
|
}
|
|
4047
4224
|
this.vScrollbar.root.classList.toggle('hidden', !needsVerticalScrollbar);
|
|
4048
4225
|
this.hScrollbar.root.style.width = `calc(100% - ${48 + (needsVerticalScrollbar ? ScrollBar.SCROLLBAR_VERTICAL_WIDTH : 0)}px)`; // 48 is the line gutter
|
|
@@ -4053,8 +4230,8 @@ class CodeEditor {
|
|
|
4053
4230
|
const maxLineLength = this._lastMaxLineLength;
|
|
4054
4231
|
const needsHorizontalScrollbar = maxLineLength >= numViewportChars;
|
|
4055
4232
|
if (needsHorizontalScrollbar) {
|
|
4056
|
-
this.hScrollbar.thumb.size =
|
|
4057
|
-
this.hScrollbar.thumb.style.width = (this.hScrollbar.thumb.size * 100.0) +
|
|
4233
|
+
this.hScrollbar.thumb.size = numViewportChars / maxLineLength;
|
|
4234
|
+
this.hScrollbar.thumb.style.width = (this.hScrollbar.thumb.size * 100.0) + '%';
|
|
4058
4235
|
}
|
|
4059
4236
|
this.hScrollbar.root.classList.toggle('hidden', !needsHorizontalScrollbar);
|
|
4060
4237
|
this.codeArea.root.style.height = `calc(100% - ${this._fullVerticalOffset + (needsHorizontalScrollbar ? ScrollBar.SCROLLBAR_HORIZONTAL_HEIGHT : 0)}px)`;
|
|
@@ -4068,7 +4245,7 @@ class CodeEditor {
|
|
|
4068
4245
|
const scrollThumbHeight = this.vScrollbar.thumb.offsetHeight;
|
|
4069
4246
|
const currentScroll = this.codeScroller.scrollTop;
|
|
4070
4247
|
this.vScrollbar.thumb._top = (currentScroll / scrollHeight) * (scrollBarHeight - scrollThumbHeight);
|
|
4071
|
-
this.vScrollbar.thumb.style.top = this.vScrollbar.thumb._top +
|
|
4248
|
+
this.vScrollbar.thumb.style.top = this.vScrollbar.thumb._top + 'px';
|
|
4072
4249
|
}
|
|
4073
4250
|
}
|
|
4074
4251
|
else {
|
|
@@ -4082,7 +4259,7 @@ class CodeEditor {
|
|
|
4082
4259
|
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
4083
4260
|
const currentScroll = this.codeScroller.scrollLeft;
|
|
4084
4261
|
this.hScrollbar.thumb._left = (currentScroll / scrollWidth) * (scrollBarWidth - scrollThumbWidth);
|
|
4085
|
-
this.hScrollbar.thumb.style.left = this.hScrollbar.thumb._left +
|
|
4262
|
+
this.hScrollbar.thumb.style.left = this.hScrollbar.thumb._left + 'px';
|
|
4086
4263
|
}
|
|
4087
4264
|
}
|
|
4088
4265
|
}
|
|
@@ -4091,8 +4268,8 @@ class CodeEditor {
|
|
|
4091
4268
|
// Move scrollbar thumb
|
|
4092
4269
|
const scrollBarWidth = this.hScrollbar.thumb.parentElement.offsetWidth;
|
|
4093
4270
|
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
4094
|
-
this.hScrollbar.thumb._left = LX.clamp(value, 0,
|
|
4095
|
-
this.hScrollbar.thumb.style.left = this.hScrollbar.thumb._left +
|
|
4271
|
+
this.hScrollbar.thumb._left = LX.clamp(value, 0, scrollBarWidth - scrollThumbWidth);
|
|
4272
|
+
this.hScrollbar.thumb.style.left = this.hScrollbar.thumb._left + 'px';
|
|
4096
4273
|
// Scroll code
|
|
4097
4274
|
const scrollWidth = this.codeScroller.scrollWidth - this.codeScroller.clientWidth;
|
|
4098
4275
|
const currentScroll = (this.hScrollbar.thumb._left * scrollWidth) / (scrollBarWidth - scrollThumbWidth);
|
|
@@ -4104,8 +4281,8 @@ class CodeEditor {
|
|
|
4104
4281
|
// Move scrollbar thumb
|
|
4105
4282
|
const scrollBarHeight = this.vScrollbar.thumb.parentElement.offsetHeight;
|
|
4106
4283
|
const scrollThumbHeight = this.vScrollbar.thumb.offsetHeight;
|
|
4107
|
-
this.vScrollbar.thumb._top = LX.clamp(value, 0,
|
|
4108
|
-
this.vScrollbar.thumb.style.top = this.vScrollbar.thumb._top +
|
|
4284
|
+
this.vScrollbar.thumb._top = LX.clamp(value, 0, scrollBarHeight - scrollThumbHeight);
|
|
4285
|
+
this.vScrollbar.thumb.style.top = this.vScrollbar.thumb._top + 'px';
|
|
4109
4286
|
// Scroll code
|
|
4110
4287
|
const scrollHeight = this.codeScroller.scrollHeight - this.codeScroller.clientHeight;
|
|
4111
4288
|
const currentScroll = (this.vScrollbar.thumb._top * scrollHeight) / (scrollBarHeight - scrollThumbHeight);
|
|
@@ -4120,46 +4297,52 @@ class CodeEditor {
|
|
|
4120
4297
|
const isChar = (char) => {
|
|
4121
4298
|
const exceptions = ['_', '#', '!'];
|
|
4122
4299
|
const code = char.charCodeAt(0);
|
|
4123
|
-
return (exceptions.indexOf(char) > -1) || (code > 47 && code < 58) || (code > 64 && code < 91)
|
|
4300
|
+
return (exceptions.indexOf(char) > -1) || (code > 47 && code < 58) || (code > 64 && code < 91)
|
|
4301
|
+
|| (code > 96 && code < 123);
|
|
4124
4302
|
};
|
|
4125
4303
|
let from = cursor.position + offset;
|
|
4126
4304
|
let to = cursor.position + offset;
|
|
4127
4305
|
// Check left ...
|
|
4128
|
-
while (words[from] && isChar(words[from]))
|
|
4306
|
+
while (words[from] && isChar(words[from])) {
|
|
4129
4307
|
from--;
|
|
4308
|
+
}
|
|
4130
4309
|
from++;
|
|
4131
4310
|
// Check right ...
|
|
4132
|
-
while (words[to] && isChar(words[to]))
|
|
4311
|
+
while (words[to] && isChar(words[to])) {
|
|
4133
4312
|
to++;
|
|
4313
|
+
}
|
|
4134
4314
|
// Skip spaces ...
|
|
4135
4315
|
let word = words.substring(from, to);
|
|
4136
4316
|
if (word == ' ') {
|
|
4137
4317
|
if (offset < 0) {
|
|
4138
|
-
while (words[from - 1] != undefined && words[from - 1] == ' ')
|
|
4318
|
+
while (words[from - 1] != undefined && words[from - 1] == ' ') {
|
|
4139
4319
|
from--;
|
|
4320
|
+
}
|
|
4140
4321
|
to++;
|
|
4141
4322
|
word = words.substring(from, to + 1);
|
|
4142
4323
|
}
|
|
4143
4324
|
else {
|
|
4144
|
-
while (words[to] != undefined && words[to] == ' ')
|
|
4325
|
+
while (words[to] != undefined && words[to] == ' ') {
|
|
4145
4326
|
to++;
|
|
4327
|
+
}
|
|
4146
4328
|
from--;
|
|
4147
4329
|
word = words.substring(from, to);
|
|
4148
4330
|
}
|
|
4149
4331
|
}
|
|
4150
4332
|
return [word, from, to];
|
|
4151
4333
|
}
|
|
4152
|
-
_measureChar(char =
|
|
4153
|
-
const parentContainer = LX.makeContainer(null,
|
|
4154
|
-
const container = LX.makeContainer(null,
|
|
4155
|
-
const line = document.createElement(
|
|
4334
|
+
_measureChar(char = 'M', useFloating = true, getBB = false) {
|
|
4335
|
+
const parentContainer = LX.makeContainer(null, 'lexcodeeditor', '', document.body);
|
|
4336
|
+
const container = LX.makeContainer(null, 'code', '', parentContainer);
|
|
4337
|
+
const line = document.createElement('pre');
|
|
4156
4338
|
container.appendChild(line);
|
|
4157
|
-
const text = document.createElement(
|
|
4339
|
+
const text = document.createElement('span');
|
|
4158
4340
|
line.appendChild(text);
|
|
4159
4341
|
text.innerText = char;
|
|
4160
4342
|
var rect = text.getBoundingClientRect();
|
|
4161
4343
|
LX.deleteElement(parentContainer);
|
|
4162
|
-
const bb = [useFloating ? rect.width : Math.floor(rect.width),
|
|
4344
|
+
const bb = [useFloating ? rect.width : Math.floor(rect.width),
|
|
4345
|
+
useFloating ? rect.height : Math.floor(rect.height)];
|
|
4163
4346
|
return getBB ? bb : bb[0];
|
|
4164
4347
|
}
|
|
4165
4348
|
measureString(str) {
|
|
@@ -4179,14 +4362,17 @@ class CodeEditor {
|
|
|
4179
4362
|
for (let i = 0; i < params.length; i++) {
|
|
4180
4363
|
let key = params[i].split(',');
|
|
4181
4364
|
if (key.length > 1) {
|
|
4182
|
-
if (key[key.length - 1].includes(']'))
|
|
4365
|
+
if (key[key.length - 1].includes(']')) {
|
|
4183
4366
|
continue;
|
|
4367
|
+
}
|
|
4184
4368
|
key = key[key.length - 1];
|
|
4185
4369
|
}
|
|
4186
|
-
else if (key[0].includes('}'))
|
|
4370
|
+
else if (key[0].includes('}')) {
|
|
4187
4371
|
continue;
|
|
4188
|
-
|
|
4372
|
+
}
|
|
4373
|
+
else {
|
|
4189
4374
|
key = key[0];
|
|
4375
|
+
}
|
|
4190
4376
|
key = key.replaceAll(/[{}\n\r]/g, '').replaceAll(' ', '');
|
|
4191
4377
|
if (key[0] != '"' && key[key.length - 1] != '"') {
|
|
4192
4378
|
params[i] = params[i].replace(key, '"' + key + '"');
|
|
@@ -4198,7 +4384,7 @@ class CodeEditor {
|
|
|
4198
4384
|
return JSON.stringify(json, undefined, 4);
|
|
4199
4385
|
}
|
|
4200
4386
|
catch (e) {
|
|
4201
|
-
alert(
|
|
4387
|
+
alert('Invalid JSON format');
|
|
4202
4388
|
return;
|
|
4203
4389
|
}
|
|
4204
4390
|
}
|
|
@@ -4211,7 +4397,7 @@ class CodeEditor {
|
|
|
4211
4397
|
this.hideAutoCompleteBox();
|
|
4212
4398
|
return;
|
|
4213
4399
|
}
|
|
4214
|
-
this.autocomplete.innerHTML =
|
|
4400
|
+
this.autocomplete.innerHTML = ''; // Clear all suggestions
|
|
4215
4401
|
// Add language special keys...
|
|
4216
4402
|
let suggestions = [
|
|
4217
4403
|
...Array.from(CodeEditor.keywords[this.highlight] ?? []),
|
|
@@ -4222,8 +4408,8 @@ class CodeEditor {
|
|
|
4222
4408
|
];
|
|
4223
4409
|
const scopeStack = [...this.code.lineScopes[cursor.line]];
|
|
4224
4410
|
const scope = scopeStack.at(-1);
|
|
4225
|
-
if (scope.type.startsWith(
|
|
4226
|
-
const enumValues = Array.from(this.code.symbolsTable).filter((s) => s[1][0].kind ===
|
|
4411
|
+
if (scope.type.startsWith('enum')) {
|
|
4412
|
+
const enumValues = Array.from(this.code.symbolsTable).filter((s) => s[1][0].kind === 'enum_value' && s[1][0].scope === scope.name).map((s) => s[0]);
|
|
4227
4413
|
suggestions = suggestions.concat(enumValues.slice(0, -1));
|
|
4228
4414
|
}
|
|
4229
4415
|
else {
|
|
@@ -4248,34 +4434,36 @@ class CodeEditor {
|
|
|
4248
4434
|
const pre = document.createElement('pre');
|
|
4249
4435
|
this.autocomplete.appendChild(pre);
|
|
4250
4436
|
const symbol = this.code.symbolsTable.get(s);
|
|
4251
|
-
let iconName =
|
|
4252
|
-
let iconClass =
|
|
4437
|
+
let iconName = 'CaseLower';
|
|
4438
|
+
let iconClass = 'foo';
|
|
4253
4439
|
if (symbol) {
|
|
4254
|
-
switch (symbol[0].kind)
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4440
|
+
switch (symbol[0].kind)
|
|
4441
|
+
// Get first occurrence
|
|
4442
|
+
{
|
|
4443
|
+
case 'variable':
|
|
4444
|
+
iconName = 'Cuboid';
|
|
4445
|
+
iconClass = 'fg-blue-400';
|
|
4259
4446
|
break;
|
|
4260
|
-
case
|
|
4261
|
-
iconName =
|
|
4262
|
-
iconClass =
|
|
4447
|
+
case 'method':
|
|
4448
|
+
iconName = 'Box';
|
|
4449
|
+
iconClass = 'fg-fuchsia-500';
|
|
4263
4450
|
break;
|
|
4264
|
-
case
|
|
4265
|
-
iconName =
|
|
4266
|
-
iconClass =
|
|
4451
|
+
case 'class':
|
|
4452
|
+
iconName = 'CircleNodes';
|
|
4453
|
+
iconClass = 'fg-orange-500';
|
|
4267
4454
|
break;
|
|
4268
4455
|
}
|
|
4269
4456
|
}
|
|
4270
4457
|
else {
|
|
4271
|
-
if (this._mustHightlightWord(currSuggestion, CodeEditor.utils))
|
|
4272
|
-
iconName =
|
|
4458
|
+
if (this._mustHightlightWord(currSuggestion, CodeEditor.utils)) {
|
|
4459
|
+
iconName = 'ToolCase';
|
|
4460
|
+
}
|
|
4273
4461
|
else if (this._mustHightlightWord(currSuggestion, CodeEditor.types)) {
|
|
4274
|
-
iconName =
|
|
4275
|
-
iconClass =
|
|
4462
|
+
iconName = 'Type';
|
|
4463
|
+
iconClass = 'fg-blue-400';
|
|
4276
4464
|
}
|
|
4277
4465
|
}
|
|
4278
|
-
pre.appendChild(LX.makeIcon(iconName, { iconClass:
|
|
4466
|
+
pre.appendChild(LX.makeIcon(iconName, { iconClass: 'mr-1', svgClass: 'sm ' + iconClass }));
|
|
4279
4467
|
pre.addEventListener('click', () => {
|
|
4280
4468
|
this.autoCompleteWord(currSuggestion);
|
|
4281
4469
|
});
|
|
@@ -4303,7 +4491,8 @@ class CodeEditor {
|
|
|
4303
4491
|
this.autocomplete.classList.toggle('show', true);
|
|
4304
4492
|
this.autocomplete.classList.toggle('no-scrollbar', !(this.autocomplete.scrollHeight > this.autocomplete.offsetHeight));
|
|
4305
4493
|
this.autocomplete.style.left = `${Math.min(cursor.left + CodeEditor.LINE_GUTTER_WIDTH - this.getScrollLeft(), maxX)}px`;
|
|
4306
|
-
this.autocomplete.style.top =
|
|
4494
|
+
this.autocomplete.style.top =
|
|
4495
|
+
`${(cursor.top + this._verticalTopOffset + this.lineHeight - this.getScrollTop())}px`;
|
|
4307
4496
|
this.isAutoCompleteActive = true;
|
|
4308
4497
|
}
|
|
4309
4498
|
hideAutoCompleteBox() {
|
|
@@ -4313,19 +4502,19 @@ class CodeEditor {
|
|
|
4313
4502
|
const isActive = this.isAutoCompleteActive;
|
|
4314
4503
|
this.isAutoCompleteActive = false;
|
|
4315
4504
|
this.autocomplete.classList.remove('show');
|
|
4316
|
-
this.autocomplete.innerHTML =
|
|
4505
|
+
this.autocomplete.innerHTML = ''; // Clear all suggestions
|
|
4317
4506
|
return isActive != this.isAutoCompleteActive;
|
|
4318
4507
|
}
|
|
4319
4508
|
autoCompleteWord(suggestion) {
|
|
4320
|
-
if (!this.isAutoCompleteActive)
|
|
4509
|
+
if (!this.isAutoCompleteActive) {
|
|
4321
4510
|
return;
|
|
4511
|
+
}
|
|
4322
4512
|
let [suggestedWord, idx] = this._getSelectedAutoComplete();
|
|
4323
4513
|
suggestedWord = suggestion ?? suggestedWord;
|
|
4324
4514
|
for (let cursor of this.cursors) {
|
|
4325
4515
|
const [word, start, end] = this.getWordAtPos(cursor, -1);
|
|
4326
4516
|
const lineString = this.code.lines[cursor.line];
|
|
4327
|
-
this.code.lines[cursor.line] =
|
|
4328
|
-
lineString.slice(0, start) + suggestedWord + lineString.slice(end);
|
|
4517
|
+
this.code.lines[cursor.line] = lineString.slice(0, start) + suggestedWord + lineString.slice(end);
|
|
4329
4518
|
// Process lines and remove suggestion box
|
|
4330
4519
|
this.cursorToPosition(cursor, start + suggestedWord.length);
|
|
4331
4520
|
this.processLine(cursor.line);
|
|
@@ -4338,7 +4527,7 @@ class CodeEditor {
|
|
|
4338
4527
|
for (let i = 0; i < this.autocomplete.childElementCount; ++i) {
|
|
4339
4528
|
const child = this.autocomplete.childNodes[i];
|
|
4340
4529
|
if (child.classList.contains('selected')) {
|
|
4341
|
-
var word =
|
|
4530
|
+
var word = '';
|
|
4342
4531
|
for (let childSpan of child.childNodes) {
|
|
4343
4532
|
if (childSpan.constructor != HTMLSpanElement) {
|
|
4344
4533
|
continue;
|
|
@@ -4351,8 +4540,9 @@ class CodeEditor {
|
|
|
4351
4540
|
return [null, -1];
|
|
4352
4541
|
}
|
|
4353
4542
|
_moveArrowSelectedAutoComplete(dir) {
|
|
4354
|
-
if (!this.isAutoCompleteActive)
|
|
4543
|
+
if (!this.isAutoCompleteActive) {
|
|
4355
4544
|
return;
|
|
4545
|
+
}
|
|
4356
4546
|
const [word, idx] = this._getSelectedAutoComplete();
|
|
4357
4547
|
const offset = dir == 'down' ? 1 : -1;
|
|
4358
4548
|
const fIdx = idx + offset;
|
|
@@ -4373,8 +4563,8 @@ class CodeEditor {
|
|
|
4373
4563
|
}
|
|
4374
4564
|
}
|
|
4375
4565
|
// Remove selected from the current word and add it to the next one
|
|
4376
|
-
LX.removeClass(this.autocomplete.childNodes[idx],
|
|
4377
|
-
LX.addClass(this.autocomplete.childNodes[idx + offset],
|
|
4566
|
+
LX.removeClass(this.autocomplete.childNodes[idx], 'selected');
|
|
4567
|
+
LX.addClass(this.autocomplete.childNodes[idx + offset], 'selected');
|
|
4378
4568
|
}
|
|
4379
4569
|
showSearchBox(clear = false) {
|
|
4380
4570
|
this.hideSearchLineBox();
|
|
@@ -4382,7 +4572,7 @@ class CodeEditor {
|
|
|
4382
4572
|
this.isSearchboxActive = true;
|
|
4383
4573
|
const input = this.searchbox.querySelector('input');
|
|
4384
4574
|
if (clear) {
|
|
4385
|
-
input.value =
|
|
4575
|
+
input.value = '';
|
|
4386
4576
|
}
|
|
4387
4577
|
else {
|
|
4388
4578
|
const cursor = this.getCurrentCursor();
|
|
@@ -4454,7 +4644,7 @@ class CodeEditor {
|
|
|
4454
4644
|
}
|
|
4455
4645
|
if (line == null) {
|
|
4456
4646
|
if (!skipAlert) {
|
|
4457
|
-
alert(
|
|
4647
|
+
alert('No results!');
|
|
4458
4648
|
}
|
|
4459
4649
|
const lastLine = this.code.lines.length - 1;
|
|
4460
4650
|
this._lastResult = {
|
|
@@ -4469,7 +4659,7 @@ class CodeEditor {
|
|
|
4469
4659
|
have to add the length of the substring (0, first_ocurrence)
|
|
4470
4660
|
*/
|
|
4471
4661
|
if (!reverse) {
|
|
4472
|
-
char +=
|
|
4662
|
+
char += line == cursorData.y ? cursorData.x : 0;
|
|
4473
4663
|
}
|
|
4474
4664
|
// Text found..
|
|
4475
4665
|
this._lastTextFound = text;
|
|
@@ -4481,7 +4671,7 @@ class CodeEditor {
|
|
|
4481
4671
|
// Show elements
|
|
4482
4672
|
this.searchResultSelections.classList.add('show');
|
|
4483
4673
|
// Create new selection instance
|
|
4484
|
-
cursor.selection = new CodeSelection(this, cursor,
|
|
4674
|
+
cursor.selection = new CodeSelection(this, cursor, 'lexcodesearchresult');
|
|
4485
4675
|
cursor.selection.selectInline(cursor, char, line, this.measureString(text), true);
|
|
4486
4676
|
}
|
|
4487
4677
|
this._lastResult = {
|
|
@@ -4500,7 +4690,7 @@ class CodeEditor {
|
|
|
4500
4690
|
this.searchlinebox.classList.add('opened');
|
|
4501
4691
|
this.isSearchlineboxActive = true;
|
|
4502
4692
|
const input = this.searchlinebox.querySelector('input');
|
|
4503
|
-
input.value =
|
|
4693
|
+
input.value = ':';
|
|
4504
4694
|
input.focus();
|
|
4505
4695
|
}
|
|
4506
4696
|
hideSearchLineBox() {
|
|
@@ -4510,19 +4700,22 @@ class CodeEditor {
|
|
|
4510
4700
|
}
|
|
4511
4701
|
}
|
|
4512
4702
|
goToLine(line) {
|
|
4513
|
-
if (!this._isNumber(line))
|
|
4703
|
+
if (!this._isNumber(line)) {
|
|
4514
4704
|
return;
|
|
4705
|
+
}
|
|
4515
4706
|
this.codeScroller.scrollTo(0, Math.max(line - 15) * this.lineHeight);
|
|
4516
4707
|
// Select line ?
|
|
4517
4708
|
var cursor = this.getCurrentCursor(true);
|
|
4518
4709
|
this.cursorToLine(cursor, line - 1, true);
|
|
4519
4710
|
}
|
|
4520
4711
|
selectNextOcurrence(cursor) {
|
|
4521
|
-
if (!cursor.selection)
|
|
4712
|
+
if (!cursor.selection) {
|
|
4522
4713
|
return;
|
|
4714
|
+
}
|
|
4523
4715
|
const text = cursor.selection.getText();
|
|
4524
|
-
if (!text)
|
|
4716
|
+
if (!text) {
|
|
4525
4717
|
return;
|
|
4718
|
+
}
|
|
4526
4719
|
if (!this._currentOcurrences) {
|
|
4527
4720
|
const currentKey = [cursor.position - text.length, cursor.line].join('_');
|
|
4528
4721
|
this._currentOcurrences = {};
|
|
@@ -4545,7 +4738,7 @@ class CodeEditor {
|
|
|
4545
4738
|
_updateDataInfoPanel(signal, value) {
|
|
4546
4739
|
if (!this.skipInfo) {
|
|
4547
4740
|
if (this.cursors.length > 1) {
|
|
4548
|
-
value =
|
|
4741
|
+
value = '';
|
|
4549
4742
|
}
|
|
4550
4743
|
LX.emitSignal(signal, value);
|
|
4551
4744
|
}
|
|
@@ -4556,7 +4749,7 @@ class CodeEditor {
|
|
|
4556
4749
|
return;
|
|
4557
4750
|
}
|
|
4558
4751
|
const cursor = this.getCurrentCursor();
|
|
4559
|
-
this._updateDataInfoPanel(
|
|
4752
|
+
this._updateDataInfoPanel('@cursor-data', `Ln ${n + 1}, Col ${cursor.position + 1}`);
|
|
4560
4753
|
const oldLocal = this.toLocalLine(this.state.activeLine);
|
|
4561
4754
|
let line = this.code.childNodes[oldLocal];
|
|
4562
4755
|
if (!line) {
|
|
@@ -4579,19 +4772,19 @@ class CodeEditor {
|
|
|
4579
4772
|
// Change font size
|
|
4580
4773
|
this.fontSize = size;
|
|
4581
4774
|
const r = document.querySelector(':root');
|
|
4582
|
-
r.style.setProperty(
|
|
4775
|
+
r.style.setProperty('--code-editor-font-size', `${this.fontSize}px`);
|
|
4583
4776
|
this.charWidth = this._measureChar();
|
|
4584
|
-
window.localStorage.setItem(
|
|
4777
|
+
window.localStorage.setItem('lexcodeeditor-font-size', `${this.fontSize}`);
|
|
4585
4778
|
// Change row size
|
|
4586
4779
|
const rowPixels = this.fontSize + 6;
|
|
4587
|
-
r.style.setProperty(
|
|
4780
|
+
r.style.setProperty('--code-editor-row-height', `${rowPixels}px`);
|
|
4588
4781
|
this.lineHeight = rowPixels;
|
|
4589
4782
|
// Relocate cursors
|
|
4590
4783
|
this.relocateCursors();
|
|
4591
4784
|
// Resize the code area
|
|
4592
4785
|
this.processLines();
|
|
4593
4786
|
// Emit event
|
|
4594
|
-
LX.emitSignal(
|
|
4787
|
+
LX.emitSignal('@font-size', this.fontSize);
|
|
4595
4788
|
}
|
|
4596
4789
|
_applyFontSizeOffset(offset = 0) {
|
|
4597
4790
|
const newFontSize = LX.clamp(this.fontSize + offset, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE);
|
|
@@ -4615,19 +4808,21 @@ class CodeEditor {
|
|
|
4615
4808
|
}
|
|
4616
4809
|
async _requestFileAsync(url, dataType, nocache = false) {
|
|
4617
4810
|
return new Promise((resolve, reject) => {
|
|
4618
|
-
dataType = dataType ??
|
|
4619
|
-
const mimeType = dataType ===
|
|
4811
|
+
dataType = dataType ?? 'arraybuffer';
|
|
4812
|
+
const mimeType = dataType === 'arraybuffer' ? 'application/octet-stream' : undefined;
|
|
4620
4813
|
var xhr = new XMLHttpRequest();
|
|
4621
4814
|
xhr.open('GET', url, true);
|
|
4622
4815
|
xhr.responseType = dataType;
|
|
4623
|
-
if (mimeType)
|
|
4816
|
+
if (mimeType) {
|
|
4624
4817
|
xhr.overrideMimeType(mimeType);
|
|
4625
|
-
|
|
4818
|
+
}
|
|
4819
|
+
if (nocache) {
|
|
4626
4820
|
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
|
4821
|
+
}
|
|
4627
4822
|
xhr.onload = function () {
|
|
4628
4823
|
var response = this.response;
|
|
4629
4824
|
if (this.status != 200) {
|
|
4630
|
-
var err =
|
|
4825
|
+
var err = 'Error ' + this.status;
|
|
4631
4826
|
reject(err);
|
|
4632
4827
|
return;
|
|
4633
4828
|
}
|
|
@@ -4642,143 +4837,168 @@ class CodeEditor {
|
|
|
4642
4837
|
}
|
|
4643
4838
|
}
|
|
4644
4839
|
const CE = CodeEditor;
|
|
4645
|
-
CE.languages =
|
|
4646
|
-
{
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
'
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
'
|
|
4682
|
-
|
|
4683
|
-
'
|
|
4684
|
-
|
|
4685
|
-
'
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
'
|
|
4689
|
-
|
|
4690
|
-
'
|
|
4691
|
-
|
|
4692
|
-
'
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
'
|
|
4699
|
-
'
|
|
4700
|
-
|
|
4701
|
-
'
|
|
4702
|
-
|
|
4703
|
-
|
|
4840
|
+
CE.languages = {
|
|
4841
|
+
'Plain Text': { ext: 'txt', blockComments: false, singleLineComments: false, numbers: false,
|
|
4842
|
+
icon: 'AlignLeft fg-neutral-500' },
|
|
4843
|
+
'JavaScript': { ext: 'js', icon: 'Js fg-yellow-500' },
|
|
4844
|
+
'TypeScript': { ext: 'ts', icon: 'Ts fg-blue-600' },
|
|
4845
|
+
'C': { ext: ['c', 'h'], usePreprocessor: true, icon: { 'c': 'C fg-sky-400', 'h': 'C fg-fuchsia-500' } },
|
|
4846
|
+
'C++': { ext: ['cpp', 'hpp'], usePreprocessor: true,
|
|
4847
|
+
icon: { 'cpp': 'CPlusPlus fg-sky-400', 'hpp': 'CPlusPlus fg-fuchsia-500' } },
|
|
4848
|
+
'CSS': { ext: 'css', icon: 'Hash fg-blue-700' },
|
|
4849
|
+
'CMake': { ext: 'cmake', singleLineCommentToken: '#', blockComments: false, ignoreCase: true },
|
|
4850
|
+
'GLSL': { ext: 'glsl', usePreprocessor: true },
|
|
4851
|
+
'WGSL': { ext: 'wgsl', usePreprocessor: true },
|
|
4852
|
+
'JSON': { ext: 'json', blockComments: false, singleLineComments: false, icon: 'Json fg-yellow-400' },
|
|
4853
|
+
'XML': { ext: 'xml', tags: true, icon: 'Rss fg-orange-500' },
|
|
4854
|
+
'Rust': { ext: 'rs', icon: 'Rust fg-primary' },
|
|
4855
|
+
'Python': { ext: 'py', singleLineCommentToken: '#', icon: 'Python fg-cyan-600' },
|
|
4856
|
+
'HTML': { ext: 'html', tags: true, singleLineComments: false, blockCommentsTokens: ['<!--', '-->'],
|
|
4857
|
+
numbers: false, icon: 'Code fg-orange-500' },
|
|
4858
|
+
'Batch': { ext: 'bat', blockComments: false, singleLineCommentToken: '::', ignoreCase: true,
|
|
4859
|
+
icon: 'Windows fg-blue-400' },
|
|
4860
|
+
'Markdown': { ext: 'md', blockComments: false, singleLineCommentToken: '::', tags: true, numbers: false,
|
|
4861
|
+
icon: 'Markdown fg-primary' },
|
|
4862
|
+
'PHP': { ext: 'php', icon: 'Php fg-purple-700' }
|
|
4863
|
+
};
|
|
4864
|
+
CE.nativeTypes = {
|
|
4865
|
+
'C++': ['int', 'float', 'double', 'bool', 'long', 'short', 'char', 'wchar_t', 'void'],
|
|
4866
|
+
'WGSL': ['bool', 'u32', 'i32', 'f16', 'f32', 'vec2', 'vec3', 'vec4', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f',
|
|
4867
|
+
'mat3x3f', 'mat4x4f', 'array', 'vec2u', 'vec3u', 'vec4u', 'ptr', 'sampler']
|
|
4868
|
+
};
|
|
4869
|
+
CE.declarationKeywords = {
|
|
4870
|
+
'JavaScript': ['var', 'let', 'const', 'this', 'static', 'class'],
|
|
4871
|
+
'C++': [...CE.nativeTypes['C++'], 'const', 'auto', 'class', 'struct', 'namespace', 'enum', 'extern']
|
|
4872
|
+
};
|
|
4873
|
+
CE.keywords = {
|
|
4874
|
+
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static',
|
|
4875
|
+
'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract', 'arguments', 'extends', 'instanceof',
|
|
4876
|
+
'Infinity', 'get'],
|
|
4877
|
+
'TypeScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'class', 'extends',
|
|
4878
|
+
'instanceof', 'Infinity', 'private', 'public', 'protected', 'interface', 'enum', 'type', 'get'],
|
|
4879
|
+
'C': ['int', 'float', 'double', 'long', 'short', 'char', 'const', 'void', 'true', 'false', 'auto', 'struct',
|
|
4880
|
+
'typedef', 'signed', 'volatile', 'unsigned', 'static', 'extern', 'enum', 'register', 'union'],
|
|
4881
|
+
'C++': [...CE.nativeTypes['C++'], 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'true', 'false', 'auto',
|
|
4882
|
+
'class', 'struct', 'typedef', 'nullptr', 'NULL', 'signed', 'unsigned', 'namespace', 'enum', 'extern', 'union',
|
|
4883
|
+
'sizeof', 'static', 'private', 'public'],
|
|
4884
|
+
'CMake': ['cmake_minimum_required', 'set', 'not', 'if', 'endif', 'exists', 'string', 'strequal', 'add_definitions',
|
|
4885
|
+
'macro', 'endmacro', 'file', 'list', 'source_group', 'add_executable', 'target_include_directories',
|
|
4886
|
+
'set_target_properties', 'set_property', 'add_compile_options', 'add_link_options', 'include_directories',
|
|
4887
|
+
'add_library', 'target_link_libraries', 'target_link_options', 'add_subdirectory', 'add_compile_definitions',
|
|
4888
|
+
'project', 'cache'],
|
|
4889
|
+
'JSON': ['true', 'false'],
|
|
4890
|
+
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4',
|
|
4891
|
+
'struct'],
|
|
4892
|
+
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.', 'table', 'tr', 'td', 'th', 'label', 'video', 'img',
|
|
4893
|
+
'code', 'button', 'select', 'option', 'svg', 'media', 'all', 'i', 'a', 'li', 'h1', 'h2', 'h3', 'h4', 'h5',
|
|
4894
|
+
'last-child', 'tbody', 'pre', 'monospace', 'font-face'],
|
|
4895
|
+
'WGSL': [...CE.nativeTypes['WGSL'], 'var', 'let', 'true', 'false', 'fn', 'atomic', 'struct', 'sampler_comparison',
|
|
4896
|
+
'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array',
|
|
4897
|
+
'texture_depth_multisampled_2d', 'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array',
|
|
4898
|
+
'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
|
|
4899
|
+
'texture_storage_2d_array', 'texture_storage_3d'],
|
|
4900
|
+
'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub',
|
|
4901
|
+
'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where',
|
|
4902
|
+
'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
|
|
4903
|
+
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
|
|
4904
|
+
'Batch': ['set', 'echo', 'off', 'del', 'defined', 'setlocal', 'enabledelayedexpansion', 'driverquery', 'print'],
|
|
4905
|
+
'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr',
|
|
4906
|
+
'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
4907
|
+
'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
4908
|
+
'PHP': ['const', 'function', 'array', 'new', 'int', 'string', '$this', 'public', 'null', 'private', 'protected',
|
|
4909
|
+
'implements', 'class', 'use', 'namespace', 'abstract', 'clone', 'final', 'enum']
|
|
4910
|
+
};
|
|
4704
4911
|
// These ones don't have hightlight, used as suggestions to autocomplete only...
|
|
4705
|
-
CE.utils =
|
|
4706
|
-
|
|
4707
|
-
'
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
'
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
'
|
|
4726
|
-
|
|
4727
|
-
'
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
'
|
|
4736
|
-
'
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
'
|
|
4741
|
-
'
|
|
4742
|
-
'
|
|
4743
|
-
'
|
|
4744
|
-
'
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
CE.
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
'
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
'
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
'
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
'
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
'
|
|
4776
|
-
'
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
};
|
|
4912
|
+
CE.utils = {
|
|
4913
|
+
'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys',
|
|
4914
|
+
'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite', 'bind', 'prototype', 'length', 'assign',
|
|
4915
|
+
'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild',
|
|
4916
|
+
'createElement', 'prompt', 'alert'],
|
|
4917
|
+
'WGSL': ['textureSample'],
|
|
4918
|
+
'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod',
|
|
4919
|
+
'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'filter', 'float',
|
|
4920
|
+
'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int',
|
|
4921
|
+
'isinstance', 'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next',
|
|
4922
|
+
'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set',
|
|
4923
|
+
'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'],
|
|
4924
|
+
'CSS': [...Object.keys(document.body.style).map(LX.toKebabCase), 'block', 'inline', 'inline-block', 'flex',
|
|
4925
|
+
'grid', 'none', 'inherit', 'initial', 'unset', 'revert', 'sticky', 'relative', 'absolute', 'fixed', 'static',
|
|
4926
|
+
'auto', 'visible', 'hidden', 'scroll', 'clip', 'ellipsis', 'nowrap', 'wrap', 'break-word', 'solid', 'dashed',
|
|
4927
|
+
'dotted', 'double', 'groove', 'ridge', 'inset', 'outset', 'left', 'right', 'center', 'top', 'bottom', 'start',
|
|
4928
|
+
'end', 'justify', 'stretch', 'space-between', 'space-around', 'space-evenly', 'baseline', 'middle', 'normal',
|
|
4929
|
+
'bold', 'lighter', 'bolder', 'italic', 'blur', 'uppercase', 'lowercase', 'capitalize', 'transparent',
|
|
4930
|
+
'currentColor', 'pointer', 'default', 'move', 'grab', 'grabbing', 'not-allowed', 'none', 'cover', 'contain',
|
|
4931
|
+
'repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'round', 'space', 'linear-gradient', 'radial-gradient',
|
|
4932
|
+
'conic-gradient', 'url', 'calc', 'min', 'max', 'clamp', 'red', 'blue', 'green', 'black', 'white', 'gray',
|
|
4933
|
+
'silver', 'yellow', 'orange', 'purple', 'pink', 'cyan', 'magenta', 'lime', 'teal', 'navy', 'transparent',
|
|
4934
|
+
'currentcolor', 'inherit', 'initial', 'unset', 'revert', 'none', 'auto', 'fit-content', 'min-content',
|
|
4935
|
+
'max-content']
|
|
4936
|
+
};
|
|
4937
|
+
CE.types = {
|
|
4938
|
+
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder',
|
|
4939
|
+
'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent', 'Int8Array', 'Int16Array', 'Int32Array',
|
|
4940
|
+
'Float32Array', 'Float64Array', 'Element'],
|
|
4941
|
+
'TypeScript': ['arguments', 'constructor', 'null', 'typeof', 'debugger', 'abstract', 'Object', 'string', 'String',
|
|
4942
|
+
'Function', 'Boolean', 'boolean', 'Error', 'Number', 'number', 'TextEncoder', 'TextDecoder', 'Array',
|
|
4943
|
+
'ArrayBuffer', 'InputEvent', 'MouseEvent', 'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array',
|
|
4944
|
+
'Float64Array', 'Element', 'bigint', 'unknown', 'any', 'Record'],
|
|
4945
|
+
'Rust': ['u128'],
|
|
4946
|
+
'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError',
|
|
4947
|
+
'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit', 'ImportError', 'IndentationError', 'IndexError',
|
|
4948
|
+
'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
|
|
4949
|
+
'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError',
|
|
4950
|
+
'SystemExit', 'TypeError', 'UnboundLocalError', 'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError',
|
|
4951
|
+
'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError'],
|
|
4952
|
+
'C++': ['uint8_t', 'uint16_t', 'uint32_t'],
|
|
4953
|
+
'PHP': ['Exception', 'DateTime', 'JsonSerializable']
|
|
4954
|
+
};
|
|
4955
|
+
CE.builtIn = {
|
|
4956
|
+
'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
|
|
4957
|
+
'CSS': ['*', '!important'],
|
|
4958
|
+
'C++': ['vector', 'list', 'map'],
|
|
4959
|
+
'WGSL': ['@vertex', '@fragment'],
|
|
4960
|
+
'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
|
|
4961
|
+
'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
|
|
4962
|
+
'PHP': ['echo', 'print']
|
|
4963
|
+
};
|
|
4964
|
+
CE.statements = {
|
|
4965
|
+
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import',
|
|
4966
|
+
'default', 'export', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
|
|
4967
|
+
'TypeScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import',
|
|
4968
|
+
'default', 'export', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
|
|
4969
|
+
'CSS': ['@', 'import'],
|
|
4970
|
+
'C': ['for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'default', 'goto',
|
|
4971
|
+
'do'],
|
|
4972
|
+
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm',
|
|
4973
|
+
'spdlog', 'default'],
|
|
4974
|
+
'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
|
|
4975
|
+
'WGSL': ['const', 'for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'read_write', 'uniform',
|
|
4976
|
+
'function', 'workgroup', 'bitcast'],
|
|
4977
|
+
'Rust': ['break', 'else', 'continue', 'for', 'if', 'loop', 'match', 'return', 'while', 'do', 'yield'],
|
|
4978
|
+
'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with',
|
|
4979
|
+
'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue', 'global', 'pass', 'from'],
|
|
4980
|
+
'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT'],
|
|
4981
|
+
'PHP': ['declare', 'enddeclare', 'foreach', 'endforeach', 'if', 'else', 'elseif', 'endif', 'for', 'endfor',
|
|
4982
|
+
'while', 'endwhile', 'switch', 'case', 'default', 'endswitch', 'return', 'break', 'continue', 'try', 'catch',
|
|
4983
|
+
'die', 'do', 'exit', 'finally']
|
|
4984
|
+
};
|
|
4985
|
+
CE.symbols = {
|
|
4986
|
+
'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
4987
|
+
'TypeScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
4988
|
+
'C': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '*', '-', '+'],
|
|
4989
|
+
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
|
|
4990
|
+
'CMake': ['{', '}'],
|
|
4991
|
+
'JSON': ['[', ']', '{', '}', '(', ')'],
|
|
4992
|
+
'GLSL': ['[', ']', '{', '}', '(', ')'],
|
|
4993
|
+
'WGSL': ['[', ']', '{', '}', '(', ')', '->'],
|
|
4994
|
+
'CSS': ['{', '}', '(', ')', '*'],
|
|
4995
|
+
'Rust': ['<', '>', '[', ']', '(', ')', '='],
|
|
4996
|
+
'Python': ['<', '>', '[', ']', '(', ')', '='],
|
|
4997
|
+
'Batch': ['[', ']', '(', ')', '%'],
|
|
4998
|
+
'HTML': ['<', '>', '/'],
|
|
4999
|
+
'XML': ['<', '>', '/'],
|
|
5000
|
+
'PHP': ['[', ']', '{', '}', '(', ')']
|
|
5001
|
+
};
|
|
4782
5002
|
CE.REGISTER_LANGUAGE = function (name, options = {}, def, rules) {
|
|
4783
5003
|
CE.languages[name] = options;
|
|
4784
5004
|
if (def?.keywords)
|