lexgui 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/extensions/codeeditor.js +1821 -965
- package/build/extensions/videoeditor.js +17 -6
- package/build/lexgui.css +26 -34
- package/build/lexgui.js +15 -3
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +15 -3
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +23 -1
- package/examples/code-editor.html +33 -19
- package/examples/video-editor.html +2 -1
- package/examples/video-editor2.html +5 -4
- package/package.json +1 -1
|
@@ -33,6 +33,15 @@ function indexOfFrom( str, reg, from, reverse ) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function codeScopesEqual( a, b ) {
|
|
37
|
+
if( a.length !== b.length ) return false;
|
|
38
|
+
for( let i = 0; i < a.length; i++ )
|
|
39
|
+
{
|
|
40
|
+
if( a[ i ].type !== b[ i ].type ) return false;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
class CodeSelection {
|
|
37
46
|
|
|
38
47
|
constructor( editor, cursor, className = "lexcodeselection" ) {
|
|
@@ -133,13 +142,16 @@ class ScrollBar {
|
|
|
133
142
|
static SCROLLBAR_VERTICAL = 1;
|
|
134
143
|
static SCROLLBAR_HORIZONTAL = 2;
|
|
135
144
|
|
|
145
|
+
static SCROLLBAR_VERTICAL_WIDTH = 10;
|
|
146
|
+
static SCROLLBAR_HORIZONTAL_HEIGHT = 10;
|
|
147
|
+
|
|
136
148
|
constructor( editor, type ) {
|
|
137
149
|
|
|
138
150
|
this.editor = editor;
|
|
139
151
|
this.type = type;
|
|
140
152
|
|
|
141
153
|
this.root = document.createElement( 'div' );
|
|
142
|
-
this.root.className = "lexcodescrollbar";
|
|
154
|
+
this.root.className = "lexcodescrollbar hidden";
|
|
143
155
|
|
|
144
156
|
if( type & ScrollBar.SCROLLBAR_VERTICAL )
|
|
145
157
|
this.root.classList.add( 'vertical' );
|
|
@@ -206,7 +218,7 @@ const HighlightRules = {
|
|
|
206
218
|
{ test: ctx => ctx.token.substr( 0, ctx.singleLineCommentToken.length ) == ctx.singleLineCommentToken, className: "cm-com" },
|
|
207
219
|
{ test: (ctx, editor) => editor._isKeyword( ctx ), className: "cm-kwd" },
|
|
208
220
|
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.builtIn, ctx.lang ) && ( ctx.lang.tags ?? false ? ( editor._enclosedByTokens( ctx.token, ctx.tokenIndex, '<', '>' ) ) : true ), className: "cm-bln" },
|
|
209
|
-
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.
|
|
221
|
+
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.statements, ctx.lang ), className: "cm-std" },
|
|
210
222
|
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.symbols, ctx.lang ), className: "cm-sym" },
|
|
211
223
|
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.types, ctx.lang ), className: "cm-typ" },
|
|
212
224
|
{ test: (ctx, editor) => editor._isNumber( ctx.token ) || editor._isNumber( ctx.token.replace(/[px]|[em]|%/g,'') ), className: "cm-dec" },
|
|
@@ -214,21 +226,23 @@ const HighlightRules = {
|
|
|
214
226
|
],
|
|
215
227
|
|
|
216
228
|
javascript: [
|
|
217
|
-
{ test: ctx => (ctx.prev === 'class' && ctx.next === '{')
|
|
229
|
+
{ test: ctx => (ctx.prev === 'class' && ctx.next === '{') , className: "cm-typ" },
|
|
218
230
|
],
|
|
219
231
|
|
|
220
232
|
typescript: [
|
|
221
|
-
{ test: ctx => ctx.scope && (ctx.token !== ',' && ctx.scope == "enum"), className: "cm-enu" },
|
|
233
|
+
{ test: ctx => ctx.scope && (ctx.token !== ',' && ctx.scope.type == "enum"), className: "cm-enu" },
|
|
222
234
|
{ test: ctx => (ctx.prev === ':' && ctx.next !== undefined && isLetter(ctx.token) ) || (ctx.prev === 'interface' && ctx.next === '{') || (ctx.prev === 'enum' && ctx.next === '{'), className: "cm-typ" },
|
|
223
235
|
{ test: ctx => (ctx.prev === 'class' && ctx.next === '{') || (ctx.prev === 'class' && ctx.next === '<') || (ctx.prev === 'new' && ctx.next === '(') || (ctx.prev === 'new' && ctx.next === '<'), className: "cm-typ" },
|
|
224
236
|
{ test: (ctx, editor) => ctx.token !== ',' && editor._enclosedByTokens( ctx.token, ctx.tokenIndex, '<', '>' ), className: "cm-typ" },
|
|
225
237
|
],
|
|
226
238
|
|
|
227
239
|
cpp: [
|
|
228
|
-
{ test: ctx => ctx.scope && (ctx.token !== ',' && ctx.scope == "enum"), className: "cm-enu" },
|
|
240
|
+
{ test: ctx => ctx.scope && (ctx.token !== ',' && ctx.scope.type == "enum"), className: "cm-enu" },
|
|
241
|
+
{ test: ctx => ctx.isEnumValueSymbol( ctx.token ), className: "cm-enu" },
|
|
229
242
|
{ test: ctx => (ctx.prev === 'class' && ctx.next === '{') || (ctx.prev === 'struct' && ctx.next === '{'), className: "cm-typ" },
|
|
230
243
|
{ test: ctx => ctx.prev === "<" && (ctx.next === ">" || ctx.next === "*"), className: "cm-typ" }, // Defining template type in C++
|
|
231
|
-
{ test: ctx => ctx.next === "::" || (ctx.prev === "::" && ctx.next !== "("), className: "cm-typ" } // C++ Class
|
|
244
|
+
{ test: ctx => ctx.next === "::" || (ctx.prev === "::" && ctx.next !== "("), className: "cm-typ" }, // C++ Class
|
|
245
|
+
{ test: ctx => ctx.isClassSymbol( ctx.token ) || ctx.isStructSymbol( ctx.token ), className: "cm-typ" },
|
|
232
246
|
],
|
|
233
247
|
|
|
234
248
|
wgsl: [
|
|
@@ -240,7 +254,8 @@ const HighlightRules = {
|
|
|
240
254
|
css: [
|
|
241
255
|
{ test: ctx => ( ctx.prev == '.' || ctx.prev == '::' || ( ctx.prev == ':' && ctx.next == '{' ) || ( ctx.token[ 0 ] == '#' && ctx.prev != ':' ) ), className: "cm-kwd" },
|
|
242
256
|
{ test: ctx => ctx.prev === ':' && (ctx.next === ';' || ctx.next === '!important'), className: "cm-str" }, // CSS value
|
|
243
|
-
{ test: ctx => ctx.prev === undefined && ctx.next === ":", className: "cm-typ" }, // CSS attribute
|
|
257
|
+
{ test: ctx => ( ctx.prev === undefined || ctx.prev === '{' || ctx.prev === ';' ) && ctx.next === ":", className: "cm-typ" }, // CSS attribute
|
|
258
|
+
{ test: ctx => ctx.prev === "(" && ctx.next === ")" && ctx.token.startsWith( "--" ), className: "cm-typ" }, // CSS vars
|
|
244
259
|
],
|
|
245
260
|
|
|
246
261
|
batch: [
|
|
@@ -252,7 +267,8 @@ const HighlightRules = {
|
|
|
252
267
|
],
|
|
253
268
|
|
|
254
269
|
php: [
|
|
255
|
-
{ test: ctx =>
|
|
270
|
+
{ test: ctx => ctx.token.startsWith( '$' ), className: "cm-var" },
|
|
271
|
+
{ test: ctx => (ctx.prev === 'class' && (ctx.next === '{' || ctx.next === 'implements') ) || (ctx.prev === 'enum'), className: "cm-typ" },
|
|
256
272
|
],
|
|
257
273
|
|
|
258
274
|
post_common: [
|
|
@@ -285,8 +301,13 @@ class CodeEditor {
|
|
|
285
301
|
static CODE_MIN_FONT_SIZE = 9;
|
|
286
302
|
static CODE_MAX_FONT_SIZE = 22;
|
|
287
303
|
|
|
304
|
+
static LINE_GUTTER_WIDTH = 48;
|
|
288
305
|
static LINE_GUTTER_WIDTH = 48;
|
|
289
306
|
|
|
307
|
+
static RESIZE_SCROLLBAR_H = 1;
|
|
308
|
+
static RESIZE_SCROLLBAR_V = 2;
|
|
309
|
+
static RESIZE_SCROLLBAR_H_V = CodeEditor.RESIZE_SCROLLBAR_H | CodeEditor.RESIZE_SCROLLBAR_V;
|
|
310
|
+
|
|
290
311
|
/**
|
|
291
312
|
* @param {*} options
|
|
292
313
|
* name:
|
|
@@ -302,11 +323,17 @@ class CodeEditor {
|
|
|
302
323
|
|
|
303
324
|
CodeEditor.__instances.push( this );
|
|
304
325
|
|
|
326
|
+
this.skipInfo = options.skipInfo ?? false;
|
|
327
|
+
this.disableEdition = options.disableEdition ?? false;
|
|
328
|
+
this.skipTabs = options.skipTabs ?? false;
|
|
329
|
+
this.useFileExplorer = ( options.fileExplorer ?? false ) && !this.skipTabs;
|
|
330
|
+
this.useAutoComplete = options.autocomplete ?? true;
|
|
331
|
+
|
|
305
332
|
// File explorer
|
|
306
|
-
if(
|
|
333
|
+
if( this.useFileExplorer )
|
|
307
334
|
{
|
|
308
|
-
|
|
309
|
-
explorerArea.setLimitBox( 180, 20, 512 );
|
|
335
|
+
let [ explorerArea, editorArea ] = area.split({ sizes:[ "15%","85%" ] });
|
|
336
|
+
// explorerArea.setLimitBox( 180, 20, 512 );
|
|
310
337
|
this.explorerArea = explorerArea;
|
|
311
338
|
|
|
312
339
|
let panel = new LX.Panel();
|
|
@@ -357,35 +384,73 @@ class CodeEditor {
|
|
|
357
384
|
explorerArea.attach( panel );
|
|
358
385
|
|
|
359
386
|
// Update area
|
|
360
|
-
area =
|
|
387
|
+
area = editorArea;
|
|
361
388
|
}
|
|
362
389
|
|
|
363
|
-
this.
|
|
390
|
+
this.baseArea = area;
|
|
364
391
|
this.area = new LX.Area( { className: "lexcodeeditor", height: "100%", skipAppend: true } );
|
|
365
392
|
|
|
366
|
-
this.
|
|
367
|
-
|
|
393
|
+
if( !this.skipTabs )
|
|
394
|
+
{
|
|
395
|
+
this.tabs = this.area.addTabs( { onclose: (name) => {
|
|
396
|
+
delete this.openedTabs[ name ];
|
|
397
|
+
if( Object.keys( this.openedTabs ).length < 2 )
|
|
398
|
+
{
|
|
399
|
+
clearInterval( this.blinker );
|
|
400
|
+
this.cursors.classList.remove( 'show' );
|
|
401
|
+
}
|
|
402
|
+
} } );
|
|
368
403
|
|
|
369
|
-
|
|
370
|
-
this.tabs = this.area.addTabs( { onclose: (name) => {
|
|
371
|
-
delete this.openedTabs[ name ];
|
|
372
|
-
if( Object.keys( this.openedTabs ).length < 2 )
|
|
404
|
+
if( !this.disableEdition )
|
|
373
405
|
{
|
|
374
|
-
|
|
375
|
-
|
|
406
|
+
this.tabs.root.addEventListener( 'dblclick', (e) => {
|
|
407
|
+
if( options.allowAddScripts ?? true )
|
|
408
|
+
{
|
|
409
|
+
e.preventDefault();
|
|
410
|
+
this.addTab( "unnamed.js", true );
|
|
411
|
+
}
|
|
412
|
+
} );
|
|
376
413
|
}
|
|
377
|
-
} } );
|
|
378
414
|
|
|
379
|
-
|
|
415
|
+
this.codeArea = this.tabs.area;
|
|
416
|
+
}
|
|
417
|
+
else
|
|
380
418
|
{
|
|
381
|
-
this.
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
419
|
+
this.codeArea = new LX.Area( { skipAppend: true } );
|
|
420
|
+
this.area.attach( this.codeArea );
|
|
421
|
+
this._loadFileButton = LX.makeElement( "button",
|
|
422
|
+
"grid absolute self-center z-100 p-3 rounded-full bg-secondary hover:bg-tertiary cursor-pointer border",
|
|
423
|
+
LX.makeIcon( "FolderOpen" ).innerHTML,
|
|
424
|
+
this.area,
|
|
425
|
+
{
|
|
426
|
+
bottom: "8px"
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
this._loadFileButton.addEventListener( "click", e => {
|
|
430
|
+
|
|
431
|
+
const dropdownOptions = [];
|
|
432
|
+
|
|
433
|
+
for( const [ key, value ] of [ ...Object.entries( this.loadedTabs ).slice( 1 ), ...Object.entries( this._tabStorage ) ] )
|
|
434
|
+
{
|
|
435
|
+
const icon = this._getFileIcon( key );
|
|
436
|
+
const classes = icon ? icon.split( ' ' ) : [];
|
|
437
|
+
dropdownOptions.push( {
|
|
438
|
+
name: key,
|
|
439
|
+
icon: classes[ 0 ],
|
|
440
|
+
svgClass: classes.slice( 0 ).join( ' ' ),
|
|
441
|
+
callback: (v) => {
|
|
442
|
+
this.loadCode( v );
|
|
443
|
+
}
|
|
444
|
+
} );
|
|
385
445
|
}
|
|
446
|
+
|
|
447
|
+
new LX.DropdownMenu( this._loadFileButton, dropdownOptions, { side: "top", align: "center" });
|
|
448
|
+
|
|
386
449
|
} );
|
|
387
450
|
}
|
|
388
451
|
|
|
452
|
+
this.codeArea.root.classList.add( 'lexcodearea' );
|
|
453
|
+
|
|
389
454
|
// Full editor
|
|
390
455
|
area.root.classList.add('codebasearea');
|
|
391
456
|
|
|
@@ -401,9 +466,6 @@ class CodeEditor {
|
|
|
401
466
|
attributeFilter: ['class', 'style'],
|
|
402
467
|
});
|
|
403
468
|
|
|
404
|
-
// Code area
|
|
405
|
-
this.tabs.area.root.classList.add( 'codetabsarea' );
|
|
406
|
-
|
|
407
469
|
this.root = this.area.root;
|
|
408
470
|
this.root.tabIndex = -1;
|
|
409
471
|
area.attach( this.root );
|
|
@@ -432,12 +494,12 @@ class CodeEditor {
|
|
|
432
494
|
|
|
433
495
|
this.cursors = document.createElement( 'div' );
|
|
434
496
|
this.cursors.className = 'cursors';
|
|
435
|
-
this.
|
|
497
|
+
this.codeArea.attach( this.cursors );
|
|
436
498
|
|
|
437
499
|
this.searchResultSelections = document.createElement( 'div' );
|
|
438
500
|
this.searchResultSelections.id = 'search-selections';
|
|
439
501
|
this.searchResultSelections.className = 'selections';
|
|
440
|
-
this.
|
|
502
|
+
this.codeArea.attach( this.searchResultSelections );
|
|
441
503
|
|
|
442
504
|
// Store here selections per cursor
|
|
443
505
|
this.selections = {};
|
|
@@ -450,7 +512,7 @@ class CodeEditor {
|
|
|
450
512
|
|
|
451
513
|
// Scroll stuff
|
|
452
514
|
{
|
|
453
|
-
this.codeScroller = this.
|
|
515
|
+
this.codeScroller = this.codeArea.root;
|
|
454
516
|
this.firstLineInViewport = 0;
|
|
455
517
|
this.lineScrollMargin = new LX.vec2( 20, 20 ); // [ mUp, mDown ]
|
|
456
518
|
|
|
@@ -475,12 +537,14 @@ class CodeEditor {
|
|
|
475
537
|
{
|
|
476
538
|
if( this.visibleLinesViewport.y < (this.code.lines.length - 1) )
|
|
477
539
|
{
|
|
478
|
-
const totalLinesInViewport = ((this.codeScroller.offsetHeight
|
|
540
|
+
const totalLinesInViewport = ( ( this.codeScroller.offsetHeight ) / this.lineHeight )|0;
|
|
479
541
|
const scrollDownBoundary =
|
|
480
542
|
( Math.max( this.visibleLinesViewport.y - totalLinesInViewport, 0 ) - 1 ) * this.lineHeight;
|
|
481
543
|
|
|
482
544
|
if( scrollTop >= scrollDownBoundary )
|
|
545
|
+
{
|
|
483
546
|
this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
|
|
547
|
+
}
|
|
484
548
|
}
|
|
485
549
|
}
|
|
486
550
|
// Scroll up...
|
|
@@ -488,7 +552,9 @@ class CodeEditor {
|
|
|
488
552
|
{
|
|
489
553
|
const scrollUpBoundary = parseInt( this.code.style.top );
|
|
490
554
|
if( scrollTop < scrollUpBoundary )
|
|
555
|
+
{
|
|
491
556
|
this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
|
|
557
|
+
}
|
|
492
558
|
}
|
|
493
559
|
|
|
494
560
|
lastScrollTopValue = scrollTop;
|
|
@@ -513,34 +579,31 @@ class CodeEditor {
|
|
|
513
579
|
}
|
|
514
580
|
}
|
|
515
581
|
|
|
516
|
-
//
|
|
582
|
+
// Line numbers and scrollbars
|
|
517
583
|
{
|
|
584
|
+
// This is only the container, line numbers are in the same line div
|
|
518
585
|
this.gutter = document.createElement( 'div' );
|
|
519
586
|
this.gutter.className = "lexcodegutter";
|
|
520
587
|
area.attach( this.gutter );
|
|
521
|
-
}
|
|
522
588
|
|
|
523
|
-
|
|
524
|
-
{
|
|
589
|
+
// Add custom vertical scroll bar
|
|
525
590
|
this.vScrollbar = new ScrollBar( this, ScrollBar.SCROLLBAR_VERTICAL );
|
|
526
591
|
area.attach( this.vScrollbar.root );
|
|
527
|
-
}
|
|
528
592
|
|
|
529
|
-
|
|
530
|
-
{
|
|
593
|
+
// Add custom horizontal scroll bar
|
|
531
594
|
this.hScrollbar = new ScrollBar( this, ScrollBar.SCROLLBAR_HORIZONTAL );
|
|
532
595
|
area.attach( this.hScrollbar.root );
|
|
533
596
|
}
|
|
534
597
|
|
|
598
|
+
// Add autocomplete, search boxes (IF edition enabled)
|
|
535
599
|
if( !this.disableEdition )
|
|
536
600
|
{
|
|
537
601
|
// Add autocomplete box
|
|
538
602
|
{
|
|
539
|
-
|
|
603
|
+
const box = document.createElement( 'div' );
|
|
540
604
|
box.className = "autocomplete";
|
|
541
605
|
this.autocomplete = box;
|
|
542
|
-
this.
|
|
543
|
-
|
|
606
|
+
this.codeArea.attach( box );
|
|
544
607
|
this.isAutoCompleteActive = false;
|
|
545
608
|
}
|
|
546
609
|
|
|
@@ -564,7 +627,7 @@ class CodeEditor {
|
|
|
564
627
|
} );
|
|
565
628
|
|
|
566
629
|
this.searchbox = box;
|
|
567
|
-
this.
|
|
630
|
+
this.codeArea.attach( box );
|
|
568
631
|
}
|
|
569
632
|
|
|
570
633
|
// Add search LINE box
|
|
@@ -586,7 +649,7 @@ class CodeEditor {
|
|
|
586
649
|
} );
|
|
587
650
|
|
|
588
651
|
this.searchlinebox = box;
|
|
589
|
-
this.
|
|
652
|
+
this.codeArea.attach( box );
|
|
590
653
|
}
|
|
591
654
|
}
|
|
592
655
|
|
|
@@ -597,7 +660,9 @@ class CodeEditor {
|
|
|
597
660
|
|
|
598
661
|
// Append all childs
|
|
599
662
|
while( this.codeScroller.firstChild )
|
|
663
|
+
{
|
|
600
664
|
this.codeSizer.appendChild( this.codeScroller.firstChild );
|
|
665
|
+
}
|
|
601
666
|
|
|
602
667
|
this.codeScroller.appendChild( this.codeSizer );
|
|
603
668
|
}
|
|
@@ -613,7 +678,6 @@ class CodeEditor {
|
|
|
613
678
|
|
|
614
679
|
// Code
|
|
615
680
|
|
|
616
|
-
this.useAutoComplete = options.autocomplete ?? true;
|
|
617
681
|
this.highlight = options.highlight ?? 'Plain Text';
|
|
618
682
|
this.onsave = options.onsave ?? ((code) => { console.log( code, "save" ) });
|
|
619
683
|
this.onrun = options.onrun ?? ((code) => { this.runScript(code) });
|
|
@@ -625,8 +689,8 @@ class CodeEditor {
|
|
|
625
689
|
this.charWidth = 7; // To update later depending on size..
|
|
626
690
|
this.defaultSingleLineCommentToken = '//';
|
|
627
691
|
this.defaultBlockCommentTokens = [ '/*', '*/' ];
|
|
628
|
-
this.lineScopes = [];
|
|
629
692
|
this._lastTime = null;
|
|
693
|
+
this._tabStorage = {};
|
|
630
694
|
|
|
631
695
|
this.pairKeys = {
|
|
632
696
|
"\"": "\"",
|
|
@@ -656,476 +720,488 @@ class CodeEditor {
|
|
|
656
720
|
|
|
657
721
|
if( !CodeEditor._staticReady )
|
|
658
722
|
{
|
|
659
|
-
for( let lang in CodeEditor.keywords ) CodeEditor.keywords[lang] = CodeEditor.keywords[lang]
|
|
660
|
-
for( let lang in CodeEditor.utils ) CodeEditor.utils[lang] = CodeEditor.utils[lang]
|
|
661
|
-
for( let lang in CodeEditor.types ) CodeEditor.types[lang] = CodeEditor.types[lang]
|
|
662
|
-
for( let lang in CodeEditor.builtIn ) CodeEditor.builtIn[lang] = CodeEditor.builtIn[lang]
|
|
663
|
-
for( let lang in CodeEditor.
|
|
664
|
-
for( let lang in CodeEditor.symbols ) CodeEditor.symbols[lang] = CodeEditor.symbols[lang]
|
|
723
|
+
for( let lang in CodeEditor.keywords ) CodeEditor.keywords[lang] = new Set( CodeEditor.keywords[lang] );
|
|
724
|
+
for( let lang in CodeEditor.utils ) CodeEditor.utils[lang] = new Set( CodeEditor.utils[lang] );
|
|
725
|
+
for( let lang in CodeEditor.types ) CodeEditor.types[lang] = new Set( CodeEditor.types[lang] );
|
|
726
|
+
for( let lang in CodeEditor.builtIn ) CodeEditor.builtIn[lang] = new Set( CodeEditor.builtIn[lang] );
|
|
727
|
+
for( let lang in CodeEditor.statements ) CodeEditor.statements[lang] = new Set( CodeEditor.statements[lang] );
|
|
728
|
+
for( let lang in CodeEditor.symbols ) CodeEditor.symbols[lang] = new Set( CodeEditor.symbols[lang] );
|
|
665
729
|
|
|
666
730
|
CodeEditor._staticReady = true;
|
|
667
731
|
}
|
|
668
732
|
|
|
669
733
|
// Action keys
|
|
734
|
+
{
|
|
735
|
+
this.action( 'Escape', false, ( ln, cursor, e ) => {
|
|
736
|
+
if( this.hideAutoCompleteBox() )
|
|
737
|
+
return;
|
|
738
|
+
if( this.hideSearchBox() )
|
|
739
|
+
return;
|
|
740
|
+
// Remove selections and cursors
|
|
741
|
+
this.endSelection();
|
|
742
|
+
this._removeSecondaryCursors();
|
|
743
|
+
});
|
|
670
744
|
|
|
671
|
-
|
|
672
|
-
if( this.hideAutoCompleteBox() )
|
|
673
|
-
return;
|
|
674
|
-
if( this.hideSearchBox() )
|
|
675
|
-
return;
|
|
676
|
-
// Remove selections and cursors
|
|
677
|
-
this.endSelection();
|
|
678
|
-
this._removeSecondaryCursors();
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
this.action( 'Backspace', false, ( ln, cursor, e ) => {
|
|
745
|
+
this.action( 'Backspace', false, ( ln, cursor, e ) => {
|
|
682
746
|
|
|
683
|
-
|
|
747
|
+
this._addUndoStep( cursor );
|
|
684
748
|
|
|
685
|
-
|
|
686
|
-
this.deleteSelection( cursor );
|
|
687
|
-
// Remove entire line when selecting with triple click
|
|
688
|
-
if( this._tripleClickSelection )
|
|
749
|
+
if( cursor.selection )
|
|
689
750
|
{
|
|
690
|
-
this.
|
|
691
|
-
|
|
751
|
+
this.deleteSelection( cursor );
|
|
752
|
+
// Remove entire line when selecting with triple click
|
|
753
|
+
if( this._tripleClickSelection )
|
|
754
|
+
{
|
|
755
|
+
this.actions['Backspace'].callback( ln, cursor, e );
|
|
756
|
+
this.lineDown( cursor, true );
|
|
757
|
+
}
|
|
692
758
|
}
|
|
693
|
-
|
|
694
|
-
else {
|
|
695
|
-
|
|
696
|
-
var letter = this.getCharAtPos( cursor, -1 );
|
|
697
|
-
if( letter ) {
|
|
759
|
+
else {
|
|
698
760
|
|
|
699
|
-
var
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
// Delete full word
|
|
703
|
-
if( e.shiftKey )
|
|
761
|
+
var letter = this.getCharAtPos( cursor, -1 );
|
|
762
|
+
if( letter )
|
|
704
763
|
{
|
|
705
|
-
|
|
764
|
+
var deleteFromPosition = cursor.position - 1;
|
|
765
|
+
var numCharsDeleted = 1;
|
|
706
766
|
|
|
707
|
-
|
|
767
|
+
// Delete full word
|
|
768
|
+
if( e.shiftKey )
|
|
708
769
|
{
|
|
709
|
-
|
|
710
|
-
|
|
770
|
+
const [word, from, to] = this.getWordAtPos( cursor, -1 );
|
|
771
|
+
|
|
772
|
+
if( word.length > 1 )
|
|
773
|
+
{
|
|
774
|
+
deleteFromPosition = from;
|
|
775
|
+
numCharsDeleted = word.length;
|
|
776
|
+
}
|
|
711
777
|
}
|
|
712
|
-
}
|
|
713
778
|
|
|
714
|
-
|
|
715
|
-
|
|
779
|
+
this.code.lines[ ln ] = sliceChars( this.code.lines[ ln ], deleteFromPosition, numCharsDeleted );
|
|
780
|
+
this.processLine( ln );
|
|
716
781
|
|
|
717
|
-
|
|
782
|
+
this.cursorToPosition( cursor, deleteFromPosition );
|
|
718
783
|
|
|
719
|
-
|
|
784
|
+
if( this.useAutoComplete )
|
|
785
|
+
{
|
|
786
|
+
this.showAutoCompleteBox( 'foo', cursor );
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
else if( this.code.lines[ ln - 1 ] != undefined )
|
|
720
790
|
{
|
|
721
|
-
this.
|
|
791
|
+
this.lineUp( cursor );
|
|
792
|
+
e.cancelShift = true;
|
|
793
|
+
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
794
|
+
// Move line on top
|
|
795
|
+
this.code.lines[ ln - 1 ] += this.code.lines[ ln ];
|
|
796
|
+
this.code.lines.splice( ln, 1 );
|
|
797
|
+
this.processLines();
|
|
722
798
|
}
|
|
723
799
|
}
|
|
724
|
-
else if( this.code.lines[ ln - 1 ] != undefined ) {
|
|
725
|
-
|
|
726
|
-
this.lineUp( cursor );
|
|
727
|
-
e.cancelShift = true;
|
|
728
|
-
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
729
|
-
// Move line on top
|
|
730
|
-
this.code.lines[ ln - 1 ] += this.code.lines[ ln ];
|
|
731
|
-
this.code.lines.splice( ln, 1 );
|
|
732
|
-
this.processLines();
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
this.action( 'Delete', false, ( ln, cursor, e ) => {
|
|
738
|
-
|
|
739
|
-
this._addUndoStep( cursor );
|
|
740
800
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
this.actions['Backspace'].callback( ln, cursor, e );
|
|
744
|
-
}
|
|
745
|
-
else
|
|
746
|
-
{
|
|
747
|
-
var letter = this.getCharAtPos( cursor );
|
|
748
|
-
if( letter ) {
|
|
749
|
-
this.code.lines[ ln ] = sliceChars( this.code.lines[ ln ], cursor.position );
|
|
750
|
-
this.processLine( ln );
|
|
751
|
-
}
|
|
752
|
-
else if( this.code.lines[ ln + 1 ] != undefined ) {
|
|
753
|
-
this.code.lines[ ln ] += this.code.lines[ ln + 1 ];
|
|
754
|
-
this.code.lines.splice( ln + 1, 1 );
|
|
755
|
-
this.processLines();
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
});
|
|
801
|
+
this.resizeIfNecessary( cursor, true );
|
|
802
|
+
});
|
|
759
803
|
|
|
760
|
-
|
|
804
|
+
this.action( 'Delete', false, ( ln, cursor, e ) => {
|
|
761
805
|
|
|
762
|
-
if( this._skipTabs )
|
|
763
|
-
{
|
|
764
|
-
this._skipTabs--;
|
|
765
|
-
if( !this._skipTabs )
|
|
766
|
-
delete this._skipTabs;
|
|
767
|
-
}
|
|
768
|
-
else if( this.isAutoCompleteActive )
|
|
769
|
-
{
|
|
770
|
-
this.autoCompleteWord();
|
|
771
|
-
}
|
|
772
|
-
else
|
|
773
|
-
{
|
|
774
806
|
this._addUndoStep( cursor );
|
|
775
807
|
|
|
776
|
-
if(
|
|
808
|
+
if( cursor.selection )
|
|
777
809
|
{
|
|
778
|
-
|
|
810
|
+
// Use 'Backspace' as it's the same callback...
|
|
811
|
+
this.actions['Backspace'].callback( ln, cursor, e );
|
|
779
812
|
}
|
|
780
813
|
else
|
|
781
814
|
{
|
|
782
|
-
|
|
783
|
-
|
|
815
|
+
var letter = this.getCharAtPos( cursor );
|
|
816
|
+
if( letter )
|
|
817
|
+
{
|
|
818
|
+
this.code.lines[ ln ] = sliceChars( this.code.lines[ ln ], cursor.position );
|
|
819
|
+
this.processLine( ln );
|
|
820
|
+
}
|
|
821
|
+
else if( this.code.lines[ ln + 1 ] != undefined )
|
|
822
|
+
{
|
|
823
|
+
this.code.lines[ ln ] += this.code.lines[ ln + 1 ];
|
|
824
|
+
this.code.lines.splice( ln + 1, 1 );
|
|
825
|
+
this.processLines();
|
|
826
|
+
}
|
|
784
827
|
}
|
|
785
|
-
}
|
|
786
|
-
}, "shiftKey");
|
|
787
|
-
|
|
788
|
-
this.action( 'Home', false, ( ln, cursor, e ) => {
|
|
789
|
-
|
|
790
|
-
let idx = firstNonspaceIndex( this.code.lines[ ln ] );
|
|
791
|
-
|
|
792
|
-
// We already are in the first non space index...
|
|
793
|
-
if( idx == cursor.position ) idx = 0;
|
|
794
|
-
|
|
795
|
-
const prestring = this.code.lines[ ln ].substring( 0, idx );
|
|
796
|
-
let lastX = cursor.position;
|
|
797
|
-
|
|
798
|
-
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
799
|
-
if( idx > 0 )
|
|
800
|
-
{
|
|
801
|
-
this.cursorToString( cursor, prestring );
|
|
802
|
-
}
|
|
803
|
-
else
|
|
804
|
-
{
|
|
805
|
-
// No spaces, start from char 0
|
|
806
|
-
idx = 0;
|
|
807
|
-
}
|
|
808
828
|
|
|
809
|
-
|
|
810
|
-
|
|
829
|
+
this.resizeIfNecessary( cursor, true );
|
|
830
|
+
});
|
|
811
831
|
|
|
812
|
-
|
|
813
|
-
{
|
|
814
|
-
// Get last selection range
|
|
815
|
-
if( cursor.selection )
|
|
816
|
-
{
|
|
817
|
-
lastX += cursor.selection.chars;
|
|
818
|
-
}
|
|
832
|
+
this.action( 'Tab', true, ( ln, cursor, e ) => {
|
|
819
833
|
|
|
820
|
-
if(
|
|
834
|
+
if( this._skipTabs )
|
|
821
835
|
{
|
|
822
|
-
this.
|
|
836
|
+
this._skipTabs--;
|
|
837
|
+
if( !this._skipTabs )
|
|
838
|
+
delete this._skipTabs;
|
|
823
839
|
}
|
|
824
|
-
|
|
825
|
-
var string = this.code.lines[ ln ].substring( idx, lastX );
|
|
826
|
-
if( cursor.selection.sameLine() )
|
|
840
|
+
else if( this.isAutoCompleteActive )
|
|
827
841
|
{
|
|
828
|
-
|
|
842
|
+
this.autoCompleteWord();
|
|
829
843
|
}
|
|
830
844
|
else
|
|
831
845
|
{
|
|
832
|
-
this.
|
|
846
|
+
this._addUndoStep( cursor );
|
|
847
|
+
|
|
848
|
+
if( e && e.shiftKey )
|
|
849
|
+
{
|
|
850
|
+
this._removeSpaces( cursor );
|
|
851
|
+
}
|
|
852
|
+
else
|
|
853
|
+
{
|
|
854
|
+
const indentSpaces = this.tabSpaces - (cursor.position % this.tabSpaces);
|
|
855
|
+
this._addSpaces( indentSpaces );
|
|
856
|
+
}
|
|
833
857
|
}
|
|
834
|
-
}
|
|
835
|
-
this.endSelection();
|
|
836
|
-
});
|
|
858
|
+
}, "shiftKey");
|
|
837
859
|
|
|
838
|
-
|
|
860
|
+
this.action( 'Home', false, ( ln, cursor, e ) => {
|
|
839
861
|
|
|
840
|
-
|
|
862
|
+
let idx = firstNonspaceIndex( this.code.lines[ ln ] );
|
|
841
863
|
|
|
842
|
-
|
|
843
|
-
if(
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
864
|
+
// We already are in the first non space index...
|
|
865
|
+
if( idx == cursor.position ) idx = 0;
|
|
866
|
+
|
|
867
|
+
const prestring = this.code.lines[ ln ].substring( 0, idx );
|
|
868
|
+
let lastX = cursor.position;
|
|
869
|
+
|
|
870
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
871
|
+
if( idx > 0 )
|
|
872
|
+
{
|
|
873
|
+
this.cursorToString( cursor, prestring );
|
|
874
|
+
}
|
|
847
875
|
else
|
|
848
876
|
{
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
this._processSelection( cursor, e );
|
|
877
|
+
// No spaces, start from char 0
|
|
878
|
+
idx = 0;
|
|
852
879
|
}
|
|
853
|
-
} else if( !e.keepSelection )
|
|
854
|
-
this.endSelection();
|
|
855
880
|
|
|
856
|
-
|
|
857
|
-
|
|
881
|
+
this.setScrollLeft( 0 );
|
|
882
|
+
this.mergeCursors( ln );
|
|
858
883
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
884
|
+
if( e.shiftKey && !e.cancelShift )
|
|
885
|
+
{
|
|
886
|
+
// Get last selection range
|
|
887
|
+
if( cursor.selection )
|
|
888
|
+
{
|
|
889
|
+
lastX += cursor.selection.chars;
|
|
890
|
+
}
|
|
862
891
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
892
|
+
if( !cursor.selection )
|
|
893
|
+
{
|
|
894
|
+
this.startSelection( cursor );
|
|
895
|
+
}
|
|
866
896
|
|
|
867
|
-
|
|
897
|
+
var string = this.code.lines[ ln ].substring( idx, lastX );
|
|
898
|
+
if( cursor.selection.sameLine() )
|
|
899
|
+
{
|
|
900
|
+
cursor.selection.selectInline( cursor, idx, cursor.line, this.measureString( string ) );
|
|
901
|
+
}
|
|
902
|
+
else
|
|
903
|
+
{
|
|
904
|
+
this._processSelection( cursor, e );
|
|
905
|
+
}
|
|
906
|
+
} else if( !e.keepSelection )
|
|
907
|
+
this.endSelection();
|
|
908
|
+
});
|
|
868
909
|
|
|
869
|
-
|
|
870
|
-
if( this.isAutoCompleteActive )
|
|
871
|
-
{
|
|
872
|
-
this.autoCompleteWord();
|
|
873
|
-
return;
|
|
874
|
-
}
|
|
910
|
+
this.action( 'End', false, ( ln, cursor, e ) => {
|
|
875
911
|
|
|
876
|
-
|
|
877
|
-
{
|
|
878
|
-
this.onrun( this.getText() );
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
912
|
+
if( ( e.shiftKey || e._shiftKey ) && !e.cancelShift ) {
|
|
881
913
|
|
|
882
|
-
|
|
914
|
+
var string = this.code.lines[ ln ].substring( cursor.position );
|
|
915
|
+
if( !cursor.selection )
|
|
916
|
+
this.startSelection( cursor );
|
|
917
|
+
if( cursor.selection.sameLine() )
|
|
918
|
+
cursor.selection.selectInline( cursor, cursor.position, cursor.line, this.measureString( string ));
|
|
919
|
+
else
|
|
920
|
+
{
|
|
921
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
922
|
+
this.cursorToString( cursor, this.code.lines[ ln ] );
|
|
923
|
+
this._processSelection( cursor, e );
|
|
924
|
+
}
|
|
925
|
+
} else if( !e.keepSelection )
|
|
926
|
+
this.endSelection();
|
|
883
927
|
|
|
884
|
-
|
|
885
|
-
|
|
928
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
929
|
+
this.cursorToString( cursor, this.code.lines[ ln ] );
|
|
886
930
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
931
|
+
var viewportSizeX = ( this.codeScroller.clientWidth + this.getScrollLeft() ) - CodeEditor.LINE_GUTTER_WIDTH; // Gutter offset
|
|
932
|
+
if( ( cursor.position * this.charWidth ) >= viewportSizeX )
|
|
933
|
+
this.setScrollLeft( this.code.lines[ ln ].length * this.charWidth );
|
|
890
934
|
|
|
891
|
-
|
|
935
|
+
// Merge cursors
|
|
936
|
+
this.mergeCursors( ln );
|
|
937
|
+
});
|
|
892
938
|
|
|
893
|
-
|
|
894
|
-
var spaces = firstNonspaceIndex( this.code.lines[ ln ]);
|
|
895
|
-
var tabs = Math.floor( spaces / this.tabSpaces );
|
|
939
|
+
this.action( 'Enter', true, ( ln, cursor, e ) => {
|
|
896
940
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
941
|
+
// Add word
|
|
942
|
+
if( this.isAutoCompleteActive )
|
|
943
|
+
{
|
|
944
|
+
this.autoCompleteWord();
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
904
947
|
|
|
905
|
-
|
|
906
|
-
|
|
948
|
+
if( e.ctrlKey )
|
|
949
|
+
{
|
|
950
|
+
this.onrun( this.getText() );
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
907
953
|
|
|
908
|
-
|
|
954
|
+
this._addUndoStep( cursor, true );
|
|
909
955
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
{
|
|
913
|
-
if( e.shiftKey ) {
|
|
914
|
-
if( !cursor.selection )
|
|
915
|
-
this.startSelection( cursor );
|
|
956
|
+
var _c0 = this.getCharAtPos( cursor, -1 );
|
|
957
|
+
var _c1 = this.getCharAtPos( cursor );
|
|
916
958
|
|
|
917
|
-
|
|
959
|
+
this.code.lines.splice( cursor.line + 1, 0, "" );
|
|
960
|
+
this.code.lines[cursor.line + 1] = this.code.lines[ ln ].substr( cursor.position ); // new line (below)
|
|
961
|
+
this.code.lines[ ln ] = this.code.lines[ ln ].substr( 0, cursor.position ); // line above
|
|
918
962
|
|
|
919
|
-
|
|
920
|
-
if( !letter ) {
|
|
921
|
-
this.cursorToPosition( cursor, this.code.lines[ cursor.line ].length );
|
|
922
|
-
}
|
|
963
|
+
this.lineDown( cursor, true );
|
|
923
964
|
|
|
924
|
-
|
|
965
|
+
// Check indentation
|
|
966
|
+
var spaces = firstNonspaceIndex( this.code.lines[ ln ]);
|
|
967
|
+
var tabs = Math.floor( spaces / this.tabSpaces );
|
|
925
968
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
this.
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
969
|
+
if( _c0 == '{' && _c1 == '}' )
|
|
970
|
+
{
|
|
971
|
+
this.code.lines.splice( cursor.line, 0, "" );
|
|
972
|
+
this._addSpaceTabs( cursor, tabs + 1 );
|
|
973
|
+
this.code.lines[ cursor.line + 1 ] = " ".repeat(spaces) + this.code.lines[ cursor.line + 1 ];
|
|
974
|
+
}
|
|
975
|
+
else
|
|
976
|
+
{
|
|
977
|
+
this._addSpaceTabs( cursor, tabs );
|
|
932
978
|
}
|
|
933
|
-
}
|
|
934
|
-
// Move up autocomplete selection
|
|
935
|
-
else
|
|
936
|
-
{
|
|
937
|
-
this._moveArrowSelectedAutoComplete('up');
|
|
938
|
-
}
|
|
939
|
-
});
|
|
940
979
|
|
|
941
|
-
|
|
980
|
+
this.processLines();
|
|
981
|
+
});
|
|
942
982
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
if(
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
}
|
|
983
|
+
this.action( 'ArrowUp', false, ( ln, cursor, e ) => {
|
|
984
|
+
|
|
985
|
+
// Move cursor..
|
|
986
|
+
if( !this.isAutoCompleteActive )
|
|
987
|
+
{
|
|
988
|
+
if( e.shiftKey ) {
|
|
989
|
+
if( !cursor.selection )
|
|
990
|
+
this.startSelection( cursor );
|
|
952
991
|
|
|
953
|
-
|
|
954
|
-
const letter = this.getCharAtPos( cursor );
|
|
992
|
+
this.lineUp( cursor );
|
|
955
993
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
994
|
+
var letter = this.getCharAtPos( cursor );
|
|
995
|
+
if( !letter ) {
|
|
996
|
+
this.cursorToPosition( cursor, this.code.lines[ cursor.line ].length );
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
this._processSelection( cursor, e, false );
|
|
960
1000
|
|
|
961
|
-
|
|
962
|
-
|
|
1001
|
+
} else {
|
|
1002
|
+
this.endSelection();
|
|
1003
|
+
this.lineUp( cursor );
|
|
1004
|
+
// Go to end of line if out of line
|
|
1005
|
+
var letter = this.getCharAtPos( cursor );
|
|
1006
|
+
if( !letter ) this.actions['End'].callback( cursor.line, cursor, e );
|
|
1007
|
+
}
|
|
963
1008
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
}
|
|
970
|
-
});
|
|
1009
|
+
// Move up autocomplete selection
|
|
1010
|
+
else
|
|
1011
|
+
{
|
|
1012
|
+
this._moveArrowSelectedAutoComplete('up');
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
971
1015
|
|
|
972
|
-
|
|
1016
|
+
this.action( 'ArrowDown', false, ( ln, cursor, e ) => {
|
|
973
1017
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1018
|
+
// Move cursor..
|
|
1019
|
+
if( !this.isAutoCompleteActive )
|
|
1020
|
+
{
|
|
1021
|
+
if( e.shiftKey ) {
|
|
1022
|
+
if( !cursor.selection )
|
|
1023
|
+
this.startSelection( cursor );
|
|
1024
|
+
} else {
|
|
1025
|
+
this.endSelection();
|
|
1026
|
+
}
|
|
977
1027
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
this.actions[ 'Home' ].callback( ln, cursor, e );
|
|
981
|
-
}
|
|
982
|
-
else if( e.ctrlKey ) {
|
|
983
|
-
// Get next word
|
|
984
|
-
const [word, from, to] = this.getWordAtPos( cursor, -1 );
|
|
985
|
-
// If no length, we change line..
|
|
986
|
-
if( !word.length && this.lineUp( cursor, true ) ) {
|
|
987
|
-
const cS = e.cancelShift, kS = e.keepSelection;
|
|
988
|
-
e.cancelShift = true;
|
|
989
|
-
e.keepSelection = true;
|
|
990
|
-
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
991
|
-
e.cancelShift = cS;
|
|
992
|
-
e.keepSelection = kS;
|
|
993
|
-
}
|
|
994
|
-
var diff = Math.max( cursor.position - from, 1 );
|
|
995
|
-
var substr = word.substr( 0, diff );
|
|
1028
|
+
const canGoDown = this.lineDown( cursor );
|
|
1029
|
+
const letter = this.getCharAtPos( cursor );
|
|
996
1030
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1031
|
+
// Go to end of line if out of range
|
|
1032
|
+
if( !letter || !canGoDown ) {
|
|
1033
|
+
this.cursorToPosition( cursor, Math.max(this.code.lines[ cursor.line ].length, 0) );
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
if( e.shiftKey ) {
|
|
1037
|
+
this._processSelection( cursor, e );
|
|
1038
|
+
}
|
|
1001
1039
|
}
|
|
1040
|
+
// Move down autocomplete selection
|
|
1002
1041
|
else
|
|
1003
|
-
|
|
1042
|
+
{
|
|
1043
|
+
this._moveArrowSelectedAutoComplete('down');
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1004
1046
|
|
|
1005
|
-
|
|
1047
|
+
this.action( 'ArrowLeft', false, ( ln, cursor, e ) => {
|
|
1006
1048
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1049
|
+
// Nothing to do..
|
|
1050
|
+
if( cursor.line == 0 && cursor.position == 0 )
|
|
1051
|
+
return;
|
|
1052
|
+
|
|
1053
|
+
if( e.metaKey ) { // Apple devices (Command)
|
|
1054
|
+
e.preventDefault();
|
|
1055
|
+
this.actions[ 'Home' ].callback( ln, cursor, e );
|
|
1056
|
+
}
|
|
1057
|
+
else if( e.ctrlKey ) {
|
|
1058
|
+
// Get next word
|
|
1059
|
+
const [word, from, to] = this.getWordAtPos( cursor, -1 );
|
|
1060
|
+
// If no length, we change line..
|
|
1061
|
+
if( !word.length && this.lineUp( cursor, true ) ) {
|
|
1062
|
+
const cS = e.cancelShift, kS = e.keepSelection;
|
|
1063
|
+
e.cancelShift = true;
|
|
1064
|
+
e.keepSelection = true;
|
|
1065
|
+
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
1066
|
+
e.cancelShift = cS;
|
|
1067
|
+
e.keepSelection = kS;
|
|
1068
|
+
}
|
|
1069
|
+
var diff = Math.max( cursor.position - from, 1 );
|
|
1070
|
+
var substr = word.substr( 0, diff );
|
|
1071
|
+
|
|
1072
|
+
// Selections...
|
|
1013
1073
|
if( e.shiftKey ) {
|
|
1014
|
-
if( !cursor.selection )
|
|
1015
|
-
this.
|
|
1016
|
-
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
1074
|
+
if( !cursor.selection )
|
|
1075
|
+
this.startSelection( cursor );
|
|
1017
1076
|
}
|
|
1018
|
-
else
|
|
1019
|
-
|
|
1077
|
+
else
|
|
1078
|
+
this.endSelection();
|
|
1079
|
+
|
|
1080
|
+
this.cursorToString( cursor, substr, true );
|
|
1081
|
+
|
|
1082
|
+
if( e.shiftKey )
|
|
1083
|
+
this._processSelection( cursor, e );
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
var letter = this.getCharAtPos( cursor, -1 );
|
|
1087
|
+
if( letter ) {
|
|
1088
|
+
if( e.shiftKey ) {
|
|
1089
|
+
if( !cursor.selection ) this.startSelection( cursor );
|
|
1020
1090
|
this.cursorToLeft( letter, cursor );
|
|
1021
|
-
|
|
1022
|
-
this.showAutoCompleteBox( 'foo', cursor );
|
|
1091
|
+
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
1023
1092
|
}
|
|
1024
1093
|
else {
|
|
1025
|
-
cursor.selection
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1094
|
+
if( !cursor.selection ) {
|
|
1095
|
+
this.cursorToLeft( letter, cursor );
|
|
1096
|
+
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
1097
|
+
this.showAutoCompleteBox( 'foo', cursor );
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
cursor.selection.invertIfNecessary();
|
|
1101
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, cursor );
|
|
1102
|
+
this.cursorToLine( cursor, cursor.selection.fromY, true );
|
|
1103
|
+
this.cursorToPosition( cursor, cursor.selection.fromX );
|
|
1104
|
+
this.endSelection();
|
|
1105
|
+
}
|
|
1030
1106
|
}
|
|
1031
1107
|
}
|
|
1032
|
-
|
|
1033
|
-
else if( cursor.line > 0 ) {
|
|
1108
|
+
else if( cursor.line > 0 ) {
|
|
1034
1109
|
|
|
1035
|
-
|
|
1110
|
+
if( e.shiftKey && !cursor.selection ) this.startSelection( cursor );
|
|
1036
1111
|
|
|
1037
|
-
|
|
1112
|
+
this.lineUp( cursor );
|
|
1038
1113
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1114
|
+
e.cancelShift = e.keepSelection = true;
|
|
1115
|
+
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
1116
|
+
delete e.cancelShift; delete e.keepSelection;
|
|
1042
1117
|
|
|
1043
|
-
|
|
1118
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false );
|
|
1119
|
+
}
|
|
1044
1120
|
}
|
|
1045
|
-
}
|
|
1046
|
-
});
|
|
1047
|
-
|
|
1048
|
-
this.action( 'ArrowRight', false, ( ln, cursor, e ) => {
|
|
1049
|
-
|
|
1050
|
-
// Nothing to do..
|
|
1051
|
-
if( cursor.line == this.code.lines.length - 1 &&
|
|
1052
|
-
cursor.position == this.code.lines[ cursor.line ].length )
|
|
1053
|
-
return;
|
|
1121
|
+
});
|
|
1054
1122
|
|
|
1055
|
-
|
|
1056
|
-
{
|
|
1057
|
-
e.preventDefault();
|
|
1058
|
-
this.actions[ 'End' ].callback( ln, cursor );
|
|
1059
|
-
}
|
|
1060
|
-
else if( e.ctrlKey ) // Next word
|
|
1061
|
-
{
|
|
1062
|
-
// Get next word
|
|
1063
|
-
const [ word, from, to ] = this.getWordAtPos( cursor );
|
|
1123
|
+
this.action( 'ArrowRight', false, ( ln, cursor, e ) => {
|
|
1064
1124
|
|
|
1065
|
-
//
|
|
1066
|
-
if(
|
|
1067
|
-
|
|
1068
|
-
|
|
1125
|
+
// Nothing to do..
|
|
1126
|
+
if( cursor.line == this.code.lines.length - 1 &&
|
|
1127
|
+
cursor.position == this.code.lines[ cursor.line ].length )
|
|
1128
|
+
return;
|
|
1069
1129
|
|
|
1070
|
-
//
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1130
|
+
if( e.metaKey ) // Apple devices (Command)
|
|
1131
|
+
{
|
|
1132
|
+
e.preventDefault();
|
|
1133
|
+
this.actions[ 'End' ].callback( ln, cursor );
|
|
1074
1134
|
}
|
|
1075
|
-
else
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1135
|
+
else if( e.ctrlKey ) // Next word
|
|
1136
|
+
{
|
|
1137
|
+
// Get next word
|
|
1138
|
+
const [ word, from, to ] = this.getWordAtPos( cursor );
|
|
1079
1139
|
|
|
1080
|
-
|
|
1081
|
-
this.
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
{
|
|
1085
|
-
var letter = this.getCharAtPos( cursor );
|
|
1086
|
-
if( letter ) {
|
|
1140
|
+
// If no length, we change line..
|
|
1141
|
+
if( !word.length ) this.lineDown( cursor, true );
|
|
1142
|
+
var diff = cursor.position - from;
|
|
1143
|
+
var substr = word.substr( diff );
|
|
1087
1144
|
|
|
1088
|
-
//
|
|
1089
|
-
if( e.shiftKey )
|
|
1090
|
-
{
|
|
1145
|
+
// Selections...
|
|
1146
|
+
if( e.shiftKey ) {
|
|
1091
1147
|
if( !cursor.selection )
|
|
1092
1148
|
this.startSelection( cursor );
|
|
1093
|
-
|
|
1094
|
-
this.cursorToRight( letter, cursor );
|
|
1095
|
-
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
1096
1149
|
}
|
|
1097
1150
|
else
|
|
1098
|
-
|
|
1099
|
-
|
|
1151
|
+
this.endSelection();
|
|
1152
|
+
|
|
1153
|
+
this.cursorToString( cursor, substr );
|
|
1154
|
+
|
|
1155
|
+
if( e.shiftKey )
|
|
1156
|
+
this._processSelection( cursor, e );
|
|
1157
|
+
}
|
|
1158
|
+
else // Next char
|
|
1159
|
+
{
|
|
1160
|
+
var letter = this.getCharAtPos( cursor );
|
|
1161
|
+
if( letter ) {
|
|
1162
|
+
|
|
1163
|
+
// Selecting chars
|
|
1164
|
+
if( e.shiftKey )
|
|
1165
|
+
{
|
|
1166
|
+
if( !cursor.selection )
|
|
1167
|
+
this.startSelection( cursor );
|
|
1168
|
+
|
|
1100
1169
|
this.cursorToRight( letter, cursor );
|
|
1101
|
-
|
|
1102
|
-
this.showAutoCompleteBox( 'foo', cursor );
|
|
1170
|
+
this._processSelection( cursor, e, false, CodeEditor.SELECTION_X );
|
|
1103
1171
|
}
|
|
1104
1172
|
else
|
|
1105
1173
|
{
|
|
1106
|
-
cursor.selection
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1174
|
+
if( !cursor.selection ) {
|
|
1175
|
+
this.cursorToRight( letter, cursor );
|
|
1176
|
+
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
1177
|
+
this.showAutoCompleteBox( 'foo', cursor );
|
|
1178
|
+
}
|
|
1179
|
+
else
|
|
1180
|
+
{
|
|
1181
|
+
cursor.selection.invertIfNecessary();
|
|
1182
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, cursor );
|
|
1183
|
+
this.cursorToLine( cursor, cursor.selection.toY );
|
|
1184
|
+
this.cursorToPosition( cursor, cursor.selection.toX );
|
|
1185
|
+
this.endSelection();
|
|
1186
|
+
}
|
|
1111
1187
|
}
|
|
1112
1188
|
}
|
|
1113
|
-
|
|
1114
|
-
else if( this.code.lines[ cursor.line + 1 ] !== undefined ) {
|
|
1189
|
+
else if( this.code.lines[ cursor.line + 1 ] !== undefined ) {
|
|
1115
1190
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1191
|
+
if( e.shiftKey ) {
|
|
1192
|
+
if( !cursor.selection ) this.startSelection( cursor );
|
|
1193
|
+
}
|
|
1194
|
+
else this.endSelection();
|
|
1120
1195
|
|
|
1121
|
-
|
|
1196
|
+
this.lineDown( cursor, true );
|
|
1122
1197
|
|
|
1123
|
-
|
|
1198
|
+
if( e.shiftKey ) this._processSelection( cursor, e, false );
|
|
1124
1199
|
|
|
1125
|
-
|
|
1200
|
+
this.hideAutoCompleteBox();
|
|
1201
|
+
}
|
|
1126
1202
|
}
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1129
1205
|
|
|
1130
1206
|
// Default code tab
|
|
1131
1207
|
|
|
@@ -1133,9 +1209,10 @@ class CodeEditor {
|
|
|
1133
1209
|
this.openedTabs = { };
|
|
1134
1210
|
|
|
1135
1211
|
const onLoadAll = () => {
|
|
1212
|
+
|
|
1136
1213
|
// Create inspector panel when the initial state is complete
|
|
1137
1214
|
// and we have at least 1 tab opened
|
|
1138
|
-
this.statusPanel = this._createStatusPanel();
|
|
1215
|
+
this.statusPanel = this._createStatusPanel( options );
|
|
1139
1216
|
if( this.statusPanel )
|
|
1140
1217
|
{
|
|
1141
1218
|
area.attach( this.statusPanel );
|
|
@@ -1158,6 +1235,21 @@ class CodeEditor {
|
|
|
1158
1235
|
}
|
|
1159
1236
|
|
|
1160
1237
|
LX.emit( "@font-size", this.fontSize );
|
|
1238
|
+
|
|
1239
|
+
// Get final sizes for editor elements based on Tabs and status bar offsets
|
|
1240
|
+
LX.doAsync( () => {
|
|
1241
|
+
this._verticalTopOffset = this.tabs?.root.getBoundingClientRect().height ?? 0;
|
|
1242
|
+
this._verticalBottomOffset = this.statusPanel?.root.getBoundingClientRect().height ?? 0;
|
|
1243
|
+
this._fullVerticalOffset = this._verticalTopOffset + this._verticalBottomOffset;
|
|
1244
|
+
|
|
1245
|
+
this.gutter.style.marginTop = `${ this._verticalTopOffset }px`;
|
|
1246
|
+
this.gutter.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1247
|
+
this.vScrollbar.root.style.marginTop = `${ this._verticalTopOffset }px`;
|
|
1248
|
+
this.vScrollbar.root.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1249
|
+
this.hScrollbar.root.style.bottom = `${ this._verticalBottomOffset }px`;
|
|
1250
|
+
this.codeArea.root.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1251
|
+
}, 50 );
|
|
1252
|
+
|
|
1161
1253
|
});
|
|
1162
1254
|
|
|
1163
1255
|
window.editor = this;
|
|
@@ -1176,11 +1268,16 @@ class CodeEditor {
|
|
|
1176
1268
|
|
|
1177
1269
|
for( let url of options.files )
|
|
1178
1270
|
{
|
|
1179
|
-
this.loadFile( url, { callback: () => {
|
|
1271
|
+
this.loadFile( url, { callback: ( name, text ) => {
|
|
1180
1272
|
filesLoaded++;
|
|
1181
1273
|
if( filesLoaded == numFiles )
|
|
1182
1274
|
{
|
|
1183
1275
|
onLoadAll();
|
|
1276
|
+
|
|
1277
|
+
if( options.onFilesLoaded )
|
|
1278
|
+
{
|
|
1279
|
+
options.onFilesLoaded( this, numFiles );
|
|
1280
|
+
}
|
|
1184
1281
|
}
|
|
1185
1282
|
}});
|
|
1186
1283
|
}
|
|
@@ -1201,67 +1298,70 @@ class CodeEditor {
|
|
|
1201
1298
|
onKeyPressed( e ) {
|
|
1202
1299
|
|
|
1203
1300
|
// Toggle visibility of the file explorer
|
|
1204
|
-
if( e.key == 'b' && e.ctrlKey && this.
|
|
1301
|
+
if( e.key == 'b' && e.ctrlKey && this.useFileExplorer )
|
|
1205
1302
|
{
|
|
1206
1303
|
this.explorerArea.root.classList.toggle( "hidden" );
|
|
1207
1304
|
if( this._lastBaseareaWidth )
|
|
1208
1305
|
{
|
|
1209
|
-
this.
|
|
1306
|
+
this.baseArea.root.style.width = this._lastBaseareaWidth;
|
|
1210
1307
|
delete this._lastBaseareaWidth;
|
|
1211
1308
|
|
|
1212
1309
|
} else
|
|
1213
1310
|
{
|
|
1214
|
-
this._lastBaseareaWidth = this.
|
|
1215
|
-
this.
|
|
1311
|
+
this._lastBaseareaWidth = this.baseArea.root.style.width;
|
|
1312
|
+
this.baseArea.root.style.width = "100%";
|
|
1216
1313
|
}
|
|
1217
1314
|
}
|
|
1218
1315
|
}
|
|
1219
1316
|
|
|
1220
1317
|
getText( min ) {
|
|
1221
|
-
|
|
1222
1318
|
return this.code.lines.join( min ? ' ' : '\n' );
|
|
1223
1319
|
}
|
|
1224
1320
|
|
|
1225
1321
|
// This can be used to empty all text...
|
|
1226
1322
|
setText( text = "", lang ) {
|
|
1227
1323
|
|
|
1228
|
-
let
|
|
1229
|
-
this.code.lines = [].concat(
|
|
1324
|
+
let newLines = text.split( '\n' );
|
|
1325
|
+
this.code.lines = [].concat( newLines );
|
|
1230
1326
|
|
|
1231
1327
|
this._removeSecondaryCursors();
|
|
1232
1328
|
|
|
1233
|
-
let cursor = this.
|
|
1234
|
-
let lastLine =
|
|
1329
|
+
let cursor = this.getCurrentCursor( true );
|
|
1330
|
+
let lastLine = newLines.pop();
|
|
1235
1331
|
|
|
1236
|
-
this.cursorToLine( cursor,
|
|
1332
|
+
this.cursorToLine( cursor, newLines.length ); // Already substracted 1
|
|
1237
1333
|
this.cursorToPosition( cursor, lastLine.length );
|
|
1238
|
-
|
|
1334
|
+
|
|
1335
|
+
this.mustProcessLines = true;
|
|
1239
1336
|
|
|
1240
1337
|
if( lang )
|
|
1241
1338
|
{
|
|
1242
1339
|
this._changeLanguage( lang );
|
|
1243
1340
|
}
|
|
1341
|
+
|
|
1342
|
+
this._processLinesIfNecessary();
|
|
1244
1343
|
}
|
|
1245
1344
|
|
|
1246
1345
|
appendText( text, cursor ) {
|
|
1247
1346
|
|
|
1248
1347
|
let lidx = cursor.line;
|
|
1249
1348
|
|
|
1250
|
-
if( cursor.selection )
|
|
1349
|
+
if( cursor.selection )
|
|
1350
|
+
{
|
|
1251
1351
|
this.deleteSelection( cursor );
|
|
1252
1352
|
lidx = cursor.line;
|
|
1253
1353
|
}
|
|
1254
1354
|
|
|
1255
1355
|
this.endSelection();
|
|
1256
1356
|
|
|
1257
|
-
const
|
|
1357
|
+
const newLines = text.replaceAll( '\r', '' ).split( '\n' );
|
|
1258
1358
|
|
|
1259
1359
|
// Pasting Multiline...
|
|
1260
|
-
if(
|
|
1360
|
+
if( newLines.length != 1 )
|
|
1261
1361
|
{
|
|
1262
|
-
let num_lines =
|
|
1362
|
+
let num_lines = newLines.length;
|
|
1263
1363
|
console.assert( num_lines > 0 );
|
|
1264
|
-
const first_line =
|
|
1364
|
+
const first_line = newLines.shift();
|
|
1265
1365
|
num_lines--;
|
|
1266
1366
|
|
|
1267
1367
|
const remaining = this.code.lines[ lidx ].slice( cursor.position );
|
|
@@ -1278,11 +1378,11 @@ class CodeEditor {
|
|
|
1278
1378
|
|
|
1279
1379
|
let _text = null;
|
|
1280
1380
|
|
|
1281
|
-
for( var i = 0; i <
|
|
1282
|
-
_text =
|
|
1381
|
+
for( var i = 0; i < newLines.length; ++i ) {
|
|
1382
|
+
_text = newLines[ i ];
|
|
1283
1383
|
this.cursorToLine( cursor, cursor.line++, true );
|
|
1284
1384
|
// Add remaining...
|
|
1285
|
-
if( i == (
|
|
1385
|
+
if( i == (newLines.length - 1) )
|
|
1286
1386
|
_text += remaining;
|
|
1287
1387
|
this.code.lines.splice( 1 + lidx + i, 0, _text );
|
|
1288
1388
|
}
|
|
@@ -1296,24 +1396,26 @@ class CodeEditor {
|
|
|
1296
1396
|
{
|
|
1297
1397
|
this.code.lines[ lidx ] = [
|
|
1298
1398
|
this.code.lines[ lidx ].slice( 0, cursor.position ),
|
|
1299
|
-
|
|
1399
|
+
newLines[ 0 ],
|
|
1300
1400
|
this.code.lines[ lidx ].slice( cursor.position )
|
|
1301
1401
|
].join('');
|
|
1302
1402
|
|
|
1303
|
-
this.cursorToPosition( cursor, ( cursor.position +
|
|
1403
|
+
this.cursorToPosition( cursor, ( cursor.position + newLines[ 0 ].length ) );
|
|
1304
1404
|
this.processLine( lidx );
|
|
1305
1405
|
}
|
|
1306
1406
|
|
|
1307
|
-
this.resize( null, ( scrollWidth, scrollHeight ) => {
|
|
1407
|
+
this.resize( CodeEditor.RESIZE_SCROLLBAR_H_V, null, ( scrollWidth, scrollHeight ) => {
|
|
1308
1408
|
var viewportSizeX = ( this.codeScroller.clientWidth + this.getScrollLeft() ) - CodeEditor.LINE_GUTTER_WIDTH; // Gutter offset
|
|
1309
1409
|
if( ( cursor.position * this.charWidth ) >= viewportSizeX )
|
|
1410
|
+
{
|
|
1310
1411
|
this.setScrollLeft( this.code.lines[ lidx ].length * this.charWidth );
|
|
1412
|
+
}
|
|
1311
1413
|
} );
|
|
1312
1414
|
}
|
|
1313
1415
|
|
|
1314
|
-
loadFile( file, options = {} ) {
|
|
1416
|
+
async loadFile( file, options = {} ) {
|
|
1315
1417
|
|
|
1316
|
-
const
|
|
1418
|
+
const _innerAddTab = ( text, name, title ) => {
|
|
1317
1419
|
|
|
1318
1420
|
// Remove Carriage Return in some cases and sub tabs using spaces
|
|
1319
1421
|
text = text.replaceAll( '\r', '' );
|
|
@@ -1325,7 +1427,7 @@ class CodeEditor {
|
|
|
1325
1427
|
|
|
1326
1428
|
// Add item in the explorer if used
|
|
1327
1429
|
|
|
1328
|
-
if( this.
|
|
1430
|
+
if( this.useFileExplorer || this.skipTabs )
|
|
1329
1431
|
{
|
|
1330
1432
|
this._tabStorage[ name ] = {
|
|
1331
1433
|
lines: lines,
|
|
@@ -1333,8 +1435,12 @@ class CodeEditor {
|
|
|
1333
1435
|
};
|
|
1334
1436
|
|
|
1335
1437
|
const ext = CodeEditor.languages[ options.language ] ?. ext;
|
|
1336
|
-
|
|
1337
|
-
this.
|
|
1438
|
+
|
|
1439
|
+
if( this.useFileExplorer )
|
|
1440
|
+
{
|
|
1441
|
+
this.addExplorerItem( { id: name, skipVisibility: true, icon: this._getFileIcon( name, ext ) } );
|
|
1442
|
+
this.explorer.innerTree.frefresh( name );
|
|
1443
|
+
}
|
|
1338
1444
|
}
|
|
1339
1445
|
else
|
|
1340
1446
|
{
|
|
@@ -1350,17 +1456,19 @@ class CodeEditor {
|
|
|
1350
1456
|
|
|
1351
1457
|
if( options.callback )
|
|
1352
1458
|
{
|
|
1353
|
-
options.callback( text );
|
|
1459
|
+
options.callback( name, text );
|
|
1354
1460
|
}
|
|
1355
1461
|
};
|
|
1356
1462
|
|
|
1357
1463
|
if( file.constructor == String )
|
|
1358
1464
|
{
|
|
1359
1465
|
let filename = file;
|
|
1466
|
+
|
|
1360
1467
|
LX.request({ url: filename, success: text => {
|
|
1361
1468
|
const name = filename.substring(filename.lastIndexOf( '/' ) + 1);
|
|
1362
|
-
|
|
1469
|
+
_innerAddTab( text, name, filename );
|
|
1363
1470
|
} });
|
|
1471
|
+
|
|
1364
1472
|
}
|
|
1365
1473
|
else // File Blob
|
|
1366
1474
|
{
|
|
@@ -1368,86 +1476,11 @@ class CodeEditor {
|
|
|
1368
1476
|
fr.readAsText( file );
|
|
1369
1477
|
fr.onload = e => {
|
|
1370
1478
|
const text = e.currentTarget.result;
|
|
1371
|
-
|
|
1479
|
+
_innerAddTab( text, file.name );
|
|
1372
1480
|
};
|
|
1373
1481
|
}
|
|
1374
1482
|
}
|
|
1375
1483
|
|
|
1376
|
-
_addCursor( line = 0, position = 0, force, isMain = false ) {
|
|
1377
|
-
|
|
1378
|
-
// If cursor in that position exists, remove it instead..
|
|
1379
|
-
const exists = Array.from( this.cursors.children ).find( v => v.position == position && v.line == line );
|
|
1380
|
-
if( exists && !force )
|
|
1381
|
-
{
|
|
1382
|
-
if( !exists.isMain )
|
|
1383
|
-
exists.remove();
|
|
1384
|
-
|
|
1385
|
-
return;
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
let cursor = document.createElement( 'div' );
|
|
1389
|
-
cursor.name = "cursor" + this.cursors.childElementCount;
|
|
1390
|
-
cursor.className = "cursor";
|
|
1391
|
-
cursor.innerHTML = " ";
|
|
1392
|
-
cursor.isMain = isMain;
|
|
1393
|
-
cursor._left = position * this.charWidth;
|
|
1394
|
-
cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
|
|
1395
|
-
cursor._top = line * this.lineHeight;
|
|
1396
|
-
cursor.style.top = cursor._top + "px";
|
|
1397
|
-
cursor._position = position;
|
|
1398
|
-
cursor._line = line;
|
|
1399
|
-
cursor.print = (function() { console.log( this._line, this._position ) }).bind( cursor );
|
|
1400
|
-
cursor.isLast = (function() { return this.cursors.lastChild == cursor; }).bind( this );
|
|
1401
|
-
|
|
1402
|
-
Object.defineProperty( cursor, 'line', {
|
|
1403
|
-
get: (v) => { return cursor._line },
|
|
1404
|
-
set: (v) => {
|
|
1405
|
-
cursor._line = v;
|
|
1406
|
-
if( cursor.isMain ) this._setActiveLine( v );
|
|
1407
|
-
}
|
|
1408
|
-
} );
|
|
1409
|
-
|
|
1410
|
-
Object.defineProperty( cursor, 'position', {
|
|
1411
|
-
get: (v) => { return cursor._position },
|
|
1412
|
-
set: (v) => {
|
|
1413
|
-
cursor._position = v;
|
|
1414
|
-
if( cursor.isMain )
|
|
1415
|
-
{
|
|
1416
|
-
const activeLine = this.state.activeLine;
|
|
1417
|
-
this._updateDataInfoPanel( "@cursor-data", `Ln ${ activeLine + 1 }, Col ${ v + 1 }` );
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
} );
|
|
1421
|
-
|
|
1422
|
-
this.cursors.appendChild( cursor );
|
|
1423
|
-
|
|
1424
|
-
return cursor;
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
_getCurrentCursor( removeOthers ) {
|
|
1428
|
-
|
|
1429
|
-
if( removeOthers )
|
|
1430
|
-
{
|
|
1431
|
-
this._removeSecondaryCursors();
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
return this.cursors.children[ 0 ];
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
_removeSecondaryCursors() {
|
|
1438
|
-
|
|
1439
|
-
while( this.cursors.childElementCount > 1 )
|
|
1440
|
-
this.cursors.lastChild.remove();
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
_logCursors() {
|
|
1444
|
-
|
|
1445
|
-
for( let cursor of this.cursors.children )
|
|
1446
|
-
{
|
|
1447
|
-
cursor.print();
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
1484
|
_addUndoStep( cursor, force, deleteRedo = true ) {
|
|
1452
1485
|
|
|
1453
1486
|
// Only the mainc cursor stores undo steps
|
|
@@ -1459,12 +1492,18 @@ class CodeEditor {
|
|
|
1459
1492
|
|
|
1460
1493
|
if( !force )
|
|
1461
1494
|
{
|
|
1462
|
-
if( !this._lastTime )
|
|
1495
|
+
if( !this._lastTime )
|
|
1496
|
+
{
|
|
1463
1497
|
this._lastTime = current;
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1498
|
+
}
|
|
1499
|
+
else
|
|
1500
|
+
{
|
|
1501
|
+
if( ( current - this._lastTime ) > 2000 )
|
|
1502
|
+
{
|
|
1466
1503
|
this._lastTime = null;
|
|
1467
|
-
}
|
|
1504
|
+
}
|
|
1505
|
+
else
|
|
1506
|
+
{
|
|
1468
1507
|
// If time not enough, reset timer
|
|
1469
1508
|
this._lastTime = current;
|
|
1470
1509
|
return;
|
|
@@ -1518,7 +1557,9 @@ class CodeEditor {
|
|
|
1518
1557
|
|
|
1519
1558
|
// Only the mainc cursor stores redo steps
|
|
1520
1559
|
if( !cursor.isMain )
|
|
1560
|
+
{
|
|
1521
1561
|
return;
|
|
1562
|
+
}
|
|
1522
1563
|
|
|
1523
1564
|
this.code.redoSteps.push( {
|
|
1524
1565
|
lines: LX.deepCopy( this.code.lines ),
|
|
@@ -1548,7 +1589,9 @@ class CodeEditor {
|
|
|
1548
1589
|
|
|
1549
1590
|
// Generate new if needed
|
|
1550
1591
|
if( !currentCursor )
|
|
1592
|
+
{
|
|
1551
1593
|
currentCursor = this._addCursor();
|
|
1594
|
+
}
|
|
1552
1595
|
|
|
1553
1596
|
this.restoreCursor( currentCursor, step.cursors[ i ] );
|
|
1554
1597
|
}
|
|
@@ -1565,12 +1608,14 @@ class CodeEditor {
|
|
|
1565
1608
|
}
|
|
1566
1609
|
|
|
1567
1610
|
this._updateDataInfoPanel( "@highlight", lang );
|
|
1568
|
-
|
|
1611
|
+
|
|
1612
|
+
this.mustProcessLines = true;
|
|
1569
1613
|
|
|
1570
1614
|
const ext = langExtension ?? CodeEditor.languages[ lang ].ext;
|
|
1571
1615
|
const icon = this._getFileIcon( null, ext );
|
|
1572
1616
|
|
|
1573
1617
|
// Update tab icon
|
|
1618
|
+
if( !this.skipTabs )
|
|
1574
1619
|
{
|
|
1575
1620
|
const tab = this.tabs.tabDOMs[ this.code.tabName ];
|
|
1576
1621
|
tab.firstChild.remove();
|
|
@@ -1589,7 +1634,7 @@ class CodeEditor {
|
|
|
1589
1634
|
}
|
|
1590
1635
|
|
|
1591
1636
|
// Update explorer icon
|
|
1592
|
-
if( this.
|
|
1637
|
+
if( this.useFileExplorer )
|
|
1593
1638
|
{
|
|
1594
1639
|
const item = this.explorer.innerTree.data.children.filter( (v) => v.id === this.code.tabName )[ 0 ];
|
|
1595
1640
|
console.assert( item != undefined );
|
|
@@ -1629,146 +1674,159 @@ class CodeEditor {
|
|
|
1629
1674
|
this._changeLanguage( 'Plain Text' );
|
|
1630
1675
|
}
|
|
1631
1676
|
|
|
1632
|
-
_createStatusPanel() {
|
|
1677
|
+
_createStatusPanel( options ) {
|
|
1633
1678
|
|
|
1634
|
-
if(
|
|
1679
|
+
if( this.skipInfo )
|
|
1635
1680
|
{
|
|
1636
|
-
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1637
1683
|
|
|
1638
|
-
|
|
1639
|
-
leftStatusPanel.sameLine();
|
|
1640
|
-
leftStatusPanel.addButton( null, "ZoomOutButton", this._decreaseFontSize.bind( this ), { icon: "ZoomOut", width: "32px", title: "Zoom Out", tooltip: true } );
|
|
1641
|
-
leftStatusPanel.addLabel( this.fontSize ?? 14, { fit: true, signal: "@font-size" });
|
|
1642
|
-
leftStatusPanel.addButton( null, "ZoomInButton", this._increaseFontSize.bind( this ), { icon: "ZoomIn", width: "32px", title: "Zoom In", tooltip: true } );
|
|
1643
|
-
leftStatusPanel.endLine( "justify-start" );
|
|
1644
|
-
panel.attach( leftStatusPanel.root );
|
|
1645
|
-
|
|
1646
|
-
let rightStatusPanel = new LX.Panel( { height: "auto" } );
|
|
1647
|
-
rightStatusPanel.sameLine();
|
|
1648
|
-
rightStatusPanel.addLabel( this.code.title, { id: "EditorFilenameStatusComponent", fit: true, signal: "@tab-name" });
|
|
1649
|
-
rightStatusPanel.addButton( null, "Ln 1, Col 1", this.showSearchLineBox.bind( this ), { id: "EditorSelectionStatusComponent", fit: true, signal: "@cursor-data" });
|
|
1650
|
-
rightStatusPanel.addButton( null, "Spaces: " + this.tabSpaces, ( value, event ) => {
|
|
1651
|
-
LX.addContextMenu( "Spaces", event, m => {
|
|
1652
|
-
const options = [ 2, 4, 8 ];
|
|
1653
|
-
for( const n of options )
|
|
1654
|
-
m.add( n, (v) => {
|
|
1655
|
-
this.tabSpaces = v;
|
|
1656
|
-
this.processLines();
|
|
1657
|
-
this._updateDataInfoPanel( "@tab-spaces", "Spaces: " + this.tabSpaces );
|
|
1658
|
-
} );
|
|
1659
|
-
});
|
|
1660
|
-
}, { id: "EditorIndentationStatusComponent", nameWidth: "15%", signal: "@tab-spaces" });
|
|
1661
|
-
rightStatusPanel.addButton( "<b>{ }</b>", this.highlight, ( value, event ) => {
|
|
1662
|
-
LX.addContextMenu( "Language", event, m => {
|
|
1663
|
-
for( const lang of Object.keys( CodeEditor.languages ) )
|
|
1664
|
-
{
|
|
1665
|
-
m.add( lang, v => {
|
|
1666
|
-
this._changeLanguage( v, null, true )
|
|
1667
|
-
} );
|
|
1668
|
-
}
|
|
1669
|
-
});
|
|
1670
|
-
}, { id: "EditorLanguageStatusComponent", nameWidth: "15%", signal: "@highlight" });
|
|
1671
|
-
rightStatusPanel.endLine( "justify-end" );
|
|
1672
|
-
panel.attach( rightStatusPanel.root );
|
|
1673
|
-
|
|
1674
|
-
const itemVisibilityMap = {
|
|
1675
|
-
"Font Size Zoom": true,
|
|
1676
|
-
"Editor Filename": true,
|
|
1677
|
-
"Editor Selection": true,
|
|
1678
|
-
"Editor Indentation": true,
|
|
1679
|
-
"Editor Language": true,
|
|
1680
|
-
};
|
|
1684
|
+
let panel = new LX.Panel({ className: "lexcodetabinfo flex flex-row", height: "auto" });
|
|
1681
1685
|
|
|
1682
|
-
|
|
1686
|
+
let leftStatusPanel = new LX.Panel( { id: "FontSizeZoomStatusComponent", height: "auto" } );
|
|
1687
|
+
leftStatusPanel.sameLine();
|
|
1688
|
+
|
|
1689
|
+
if( this.skipTabs )
|
|
1690
|
+
{
|
|
1691
|
+
leftStatusPanel.addButton( null, "ZoomOutButton", this._decreaseFontSize.bind( this ), { icon: "ZoomOut", width: "32px", title: "Zoom Out", tooltip: true } );
|
|
1692
|
+
}
|
|
1683
1693
|
|
|
1684
|
-
|
|
1694
|
+
leftStatusPanel.addButton( null, "ZoomOutButton", this._decreaseFontSize.bind( this ), { icon: "ZoomOut", width: "32px", title: "Zoom Out", tooltip: true } );
|
|
1695
|
+
leftStatusPanel.addLabel( this.fontSize ?? 14, { fit: true, signal: "@font-size" });
|
|
1696
|
+
leftStatusPanel.addButton( null, "ZoomInButton", this._increaseFontSize.bind( this ), { icon: "ZoomIn", width: "32px", title: "Zoom In", tooltip: true } );
|
|
1697
|
+
leftStatusPanel.endLine( "justify-start" );
|
|
1698
|
+
panel.attach( leftStatusPanel.root );
|
|
1699
|
+
|
|
1700
|
+
let rightStatusPanel = new LX.Panel( { height: "auto" } );
|
|
1701
|
+
rightStatusPanel.sameLine();
|
|
1702
|
+
rightStatusPanel.addLabel( this.code?.title ?? "", { id: "EditorFilenameStatusComponent", fit: true, signal: "@tab-name" });
|
|
1703
|
+
rightStatusPanel.addButton( null, "Ln 1, Col 1", this.showSearchLineBox.bind( this ), { id: "EditorSelectionStatusComponent", fit: true, signal: "@cursor-data" });
|
|
1704
|
+
rightStatusPanel.addButton( null, "Spaces: " + this.tabSpaces, ( value, event ) => {
|
|
1705
|
+
LX.addContextMenu( "Spaces", event, m => {
|
|
1706
|
+
const options = [ 2, 4, 8 ];
|
|
1707
|
+
for( const n of options )
|
|
1708
|
+
m.add( n, (v) => {
|
|
1709
|
+
this.tabSpaces = v;
|
|
1710
|
+
this.processLines();
|
|
1711
|
+
this._updateDataInfoPanel( "@tab-spaces", "Spaces: " + this.tabSpaces );
|
|
1712
|
+
} );
|
|
1713
|
+
});
|
|
1714
|
+
}, { id: "EditorIndentationStatusComponent", nameWidth: "15%", signal: "@tab-spaces" });
|
|
1715
|
+
rightStatusPanel.addButton( "<b>{ }</b>", this.highlight, ( value, event ) => {
|
|
1716
|
+
LX.addContextMenu( "Language", event, m => {
|
|
1717
|
+
for( const lang of Object.keys( CodeEditor.languages ) )
|
|
1685
1718
|
{
|
|
1686
|
-
|
|
1719
|
+
m.add( lang, v => {
|
|
1720
|
+
this._changeLanguage( v, null, true )
|
|
1721
|
+
} );
|
|
1687
1722
|
}
|
|
1723
|
+
});
|
|
1724
|
+
}, { id: "EditorLanguageStatusComponent", nameWidth: "15%", signal: "@highlight" });
|
|
1725
|
+
rightStatusPanel.endLine( "justify-end" );
|
|
1726
|
+
panel.attach( rightStatusPanel.root );
|
|
1727
|
+
|
|
1728
|
+
const itemVisibilityMap = {
|
|
1729
|
+
"Font Size Zoom": options.statusShowFontSizeZoom ?? true,
|
|
1730
|
+
"Editor Filename": options.statusShowEditorFilename ?? true,
|
|
1731
|
+
"Editor Selection": options.statusShowEditorSelection ?? true,
|
|
1732
|
+
"Editor Indentation": options.statusShowEditorIndentation ?? true,
|
|
1733
|
+
"Editor Language": options.statusShowEditorLanguage ?? true,
|
|
1734
|
+
};
|
|
1688
1735
|
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
callback: () => {
|
|
1694
|
-
itemVisibilityMap[ itemName ] = !itemVisibilityMap[ itemName ];
|
|
1695
|
-
const b = panel.root.querySelector( `#${ itemName.replaceAll( " ", "" ) }StatusComponent` );
|
|
1696
|
-
console.assert( b, `${ itemName } has no status button!` );
|
|
1697
|
-
b.classList.toggle( "hidden", !itemVisibilityMap[ itemName ] );
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
if( !itemVisibilityMap[ itemName ] ) delete item.icon;
|
|
1701
|
-
return item;
|
|
1702
|
-
} );
|
|
1703
|
-
new LX.DropdownMenu( e.target, menuOptions, { side: "top", align: "start" });
|
|
1704
|
-
} );
|
|
1705
|
-
|
|
1706
|
-
return panel;
|
|
1736
|
+
const _setVisibility = ( itemName ) => {
|
|
1737
|
+
const b = panel.root.querySelector( `#${ itemName.replaceAll( " ", "" ) }StatusComponent` );
|
|
1738
|
+
console.assert( b, `${ itemName } has no status button!` );
|
|
1739
|
+
b.classList.toggle( "hidden", !itemVisibilityMap[ itemName ] );
|
|
1707
1740
|
}
|
|
1708
|
-
|
|
1741
|
+
|
|
1742
|
+
for( const [ itemName, v ] of Object.entries( itemVisibilityMap ) )
|
|
1709
1743
|
{
|
|
1710
|
-
|
|
1744
|
+
_setVisibility( itemName );
|
|
1745
|
+
}
|
|
1711
1746
|
|
|
1712
|
-
|
|
1713
|
-
this.gutter.style.height = "calc(100% - 28px)";
|
|
1714
|
-
this.root.querySelectorAll( '.code' ).forEach( e => e.style.height = "calc(100% - 6px)" );
|
|
1715
|
-
this.root.querySelector( '.lexareatabscontent' ).style.height = "calc(100% - 23px)";
|
|
1716
|
-
this.base_area.root.querySelector( '.lexcodescrollbar.vertical' ).style.height = "calc(100% - 27px)";
|
|
1717
|
-
this.tabs.area.root.classList.add( 'no-code-info' );
|
|
1747
|
+
panel.root.addEventListener( "contextmenu", (e) => {
|
|
1718
1748
|
|
|
1719
|
-
|
|
1720
|
-
|
|
1749
|
+
if( e.target && ( e.target.classList.contains( "lexpanel" ) || e.target.classList.contains( "lexinlinecomponents" ) ) )
|
|
1750
|
+
{
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
const menuOptions = Object.keys( itemVisibilityMap ).map( ( itemName, idx ) => {
|
|
1755
|
+
const item = {
|
|
1756
|
+
name: itemName,
|
|
1757
|
+
icon: "Check",
|
|
1758
|
+
callback: () => {
|
|
1759
|
+
itemVisibilityMap[ itemName ] = !itemVisibilityMap[ itemName ];
|
|
1760
|
+
_setVisibility( itemName );
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
if( !itemVisibilityMap[ itemName ] ) delete item.icon;
|
|
1764
|
+
return item;
|
|
1765
|
+
} );
|
|
1766
|
+
new LX.DropdownMenu( e.target, menuOptions, { side: "top", align: "start" });
|
|
1767
|
+
} );
|
|
1768
|
+
|
|
1769
|
+
return panel;
|
|
1721
1770
|
}
|
|
1722
1771
|
|
|
1723
|
-
_getFileIcon( name, extension ) {
|
|
1772
|
+
_getFileIcon( name, extension, lang ) {
|
|
1724
1773
|
|
|
1725
1774
|
const isNewTabButton = name ? ( name === '+' ) : false;
|
|
1726
|
-
|
|
1727
|
-
if( !extension )
|
|
1775
|
+
if( isNewTabButton )
|
|
1728
1776
|
{
|
|
1729
|
-
|
|
1777
|
+
return;
|
|
1730
1778
|
}
|
|
1731
|
-
else
|
|
1732
|
-
{
|
|
1733
|
-
const possibleExtensions = [].concat( extension );
|
|
1734
1779
|
|
|
1735
|
-
|
|
1780
|
+
if( !lang )
|
|
1781
|
+
{
|
|
1782
|
+
if( !extension )
|
|
1783
|
+
{
|
|
1784
|
+
extension = LX.getExtension( name );
|
|
1785
|
+
}
|
|
1786
|
+
else
|
|
1736
1787
|
{
|
|
1737
|
-
const
|
|
1738
|
-
|
|
1788
|
+
const possibleExtensions = [].concat( extension );
|
|
1789
|
+
|
|
1790
|
+
if( name )
|
|
1791
|
+
{
|
|
1792
|
+
const fileExtension = LX.getExtension( name );
|
|
1793
|
+
const idx = possibleExtensions.indexOf( fileExtension );
|
|
1739
1794
|
|
|
1740
|
-
|
|
1795
|
+
if( idx > -1)
|
|
1796
|
+
{
|
|
1797
|
+
extension = possibleExtensions[ idx ];
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
else
|
|
1741
1801
|
{
|
|
1742
|
-
extension = possibleExtensions[
|
|
1802
|
+
extension = possibleExtensions[ 0 ];
|
|
1743
1803
|
}
|
|
1744
1804
|
}
|
|
1745
|
-
|
|
1805
|
+
|
|
1806
|
+
for( const [ l, lData ] of Object.entries( CodeEditor.languages ) )
|
|
1746
1807
|
{
|
|
1747
|
-
|
|
1808
|
+
const extensions = [].concat( lData.ext );
|
|
1809
|
+
if( extensions.includes( extension ) )
|
|
1810
|
+
{
|
|
1811
|
+
lang = l;
|
|
1812
|
+
break;
|
|
1813
|
+
}
|
|
1748
1814
|
}
|
|
1815
|
+
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
const iconPlusClasses = CodeEditor.languages[ lang ]?.icon;
|
|
1819
|
+
if( iconPlusClasses )
|
|
1820
|
+
{
|
|
1821
|
+
return iconPlusClasses[ extension ] ?? iconPlusClasses;
|
|
1749
1822
|
}
|
|
1750
1823
|
|
|
1751
|
-
return
|
|
1752
|
-
extension == "css" ? "Hash dodgerblue" :
|
|
1753
|
-
extension == "xml" ? "Rss orange" :
|
|
1754
|
-
extension == "bat" ? "Windows lightblue" :
|
|
1755
|
-
extension == "json" ? "Braces fg-primary" :
|
|
1756
|
-
extension == "js" ? "Js goldenrod" :
|
|
1757
|
-
extension == "ts" ? "Ts pipelineblue" :
|
|
1758
|
-
extension == "py" ? "Python munsellblue" :
|
|
1759
|
-
extension == "rs" ? "Rust fg-primary" :
|
|
1760
|
-
extension == "md" ? "Markdown fg-primary" :
|
|
1761
|
-
extension == "cpp" ? "CPlusPlus pictonblue" :
|
|
1762
|
-
extension == "hpp" ? "CPlusPlus heliotrope" :
|
|
1763
|
-
extension == "c" ? "C pictonblue" :
|
|
1764
|
-
extension == "h" ? "C heliotrope" :
|
|
1765
|
-
extension == "php" ? "Php blueviolet" :
|
|
1766
|
-
!isNewTabButton ? "AlignLeft gray" : undefined;
|
|
1824
|
+
return "AlignLeft gray";
|
|
1767
1825
|
}
|
|
1768
1826
|
|
|
1769
1827
|
_onNewTab( e ) {
|
|
1770
1828
|
|
|
1771
|
-
this.processFocus(false);
|
|
1829
|
+
this.processFocus( false );
|
|
1772
1830
|
|
|
1773
1831
|
LX.addContextMenu( null, e, m => {
|
|
1774
1832
|
m.add( "Create", this.addTab.bind( this, "unnamed.js", true, "", { language: "JavaScript" } ) );
|
|
@@ -1791,7 +1849,7 @@ class CodeEditor {
|
|
|
1791
1849
|
|
|
1792
1850
|
this._removeSecondaryCursors();
|
|
1793
1851
|
|
|
1794
|
-
var cursor = this.
|
|
1852
|
+
var cursor = this.getCurrentCursor( true );
|
|
1795
1853
|
this.saveCursor( cursor, this.code.cursorState );
|
|
1796
1854
|
this.code = this.loadedTabs[ name ];
|
|
1797
1855
|
this.restoreCursor( cursor, this.code.cursorState );
|
|
@@ -1815,12 +1873,12 @@ class CodeEditor {
|
|
|
1815
1873
|
_onContextMenuTab( isNewTabButton, event, name, ) {
|
|
1816
1874
|
|
|
1817
1875
|
if( isNewTabButton )
|
|
1818
|
-
|
|
1876
|
+
return;
|
|
1819
1877
|
|
|
1820
1878
|
LX.addContextMenu( null, event, m => {
|
|
1821
1879
|
m.add( "Close", () => { this.tabs.delete( name ) } );
|
|
1822
|
-
m.add( "" );
|
|
1823
|
-
m.add( "Rename", () => { console.warn( "TODO" )} );
|
|
1880
|
+
// m.add( "" );
|
|
1881
|
+
// m.add( "Rename", () => { console.warn( "TODO" )} );
|
|
1824
1882
|
});
|
|
1825
1883
|
}
|
|
1826
1884
|
|
|
@@ -1841,17 +1899,24 @@ class CodeEditor {
|
|
|
1841
1899
|
|
|
1842
1900
|
// Create code content
|
|
1843
1901
|
let code = document.createElement( 'div' );
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1902
|
+
Object.assign( code, {
|
|
1903
|
+
className: 'code',
|
|
1904
|
+
lines: [ "" ],
|
|
1905
|
+
language: options.language ?? "Plain Text",
|
|
1906
|
+
cursorState: {},
|
|
1907
|
+
undoSteps: [],
|
|
1908
|
+
redoSteps: [],
|
|
1909
|
+
lineScopes: [],
|
|
1910
|
+
lineSymbols: [],
|
|
1911
|
+
lineSignatures: [],
|
|
1912
|
+
symbolsTable: new Map(),
|
|
1913
|
+
tabName: name,
|
|
1914
|
+
title: title ?? name,
|
|
1915
|
+
tokens: {}
|
|
1916
|
+
} );
|
|
1917
|
+
|
|
1918
|
+
code.style.left = "0px",
|
|
1919
|
+
code.style.top = "0px",
|
|
1855
1920
|
|
|
1856
1921
|
code.addEventListener( 'dragenter', function(e) {
|
|
1857
1922
|
e.preventDefault();
|
|
@@ -1873,21 +1938,24 @@ class CodeEditor {
|
|
|
1873
1938
|
|
|
1874
1939
|
const tabIcon = this._getFileIcon( name );
|
|
1875
1940
|
|
|
1876
|
-
if( this.
|
|
1941
|
+
if( this.useFileExplorer && !isNewTabButton )
|
|
1877
1942
|
{
|
|
1878
1943
|
this.addExplorerItem( { id: name, skipVisibility: true, icon: tabIcon } );
|
|
1879
1944
|
this.explorer.innerTree.frefresh( name );
|
|
1880
1945
|
}
|
|
1881
1946
|
|
|
1882
|
-
this.
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1947
|
+
if( !this.skipTabs )
|
|
1948
|
+
{
|
|
1949
|
+
this.tabs.add( name, code, {
|
|
1950
|
+
selected: selected,
|
|
1951
|
+
fixed: isNewTabButton,
|
|
1952
|
+
title: code.title,
|
|
1953
|
+
icon: tabIcon,
|
|
1954
|
+
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
1955
|
+
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton ),
|
|
1956
|
+
allowDelete: true
|
|
1957
|
+
} );
|
|
1958
|
+
}
|
|
1891
1959
|
|
|
1892
1960
|
// Move into the sizer..
|
|
1893
1961
|
this.codeSizer.appendChild( code );
|
|
@@ -1898,32 +1966,38 @@ class CodeEditor {
|
|
|
1898
1966
|
{
|
|
1899
1967
|
this.code = code;
|
|
1900
1968
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
1901
|
-
this.
|
|
1969
|
+
this.mustProcessLines = true;
|
|
1902
1970
|
}
|
|
1903
1971
|
|
|
1904
1972
|
if( options.language )
|
|
1905
1973
|
{
|
|
1906
1974
|
code.languageOverride = options.language;
|
|
1907
1975
|
this._changeLanguage( code.languageOverride );
|
|
1976
|
+
this.mustProcessLines = true;
|
|
1908
1977
|
}
|
|
1909
1978
|
|
|
1979
|
+
this._processLinesIfNecessary();
|
|
1980
|
+
|
|
1910
1981
|
this._updateDataInfoPanel( "@tab-name", name );
|
|
1911
1982
|
|
|
1912
1983
|
// Bc it could be overrided..
|
|
1913
1984
|
return name;
|
|
1914
1985
|
}
|
|
1915
1986
|
|
|
1916
|
-
|
|
1987
|
+
loadCode( name ) {
|
|
1988
|
+
|
|
1989
|
+
// Hide all others
|
|
1990
|
+
this.codeSizer.querySelectorAll( ".code" ).forEach( c => c.classList.add( "hidden" ) );
|
|
1917
1991
|
|
|
1918
1992
|
// Already open...
|
|
1919
1993
|
if( this.openedTabs[ name ] )
|
|
1920
1994
|
{
|
|
1921
|
-
this.
|
|
1995
|
+
let code = this.openedTabs[ name ]
|
|
1996
|
+
code.classList.remove( "hidden" );
|
|
1922
1997
|
return;
|
|
1923
1998
|
}
|
|
1924
1999
|
|
|
1925
2000
|
let code = this.loadedTabs[ name ]
|
|
1926
|
-
|
|
1927
2001
|
if( !code )
|
|
1928
2002
|
{
|
|
1929
2003
|
this.addTab( name, true );
|
|
@@ -1946,24 +2020,13 @@ class CodeEditor {
|
|
|
1946
2020
|
delete this._tabStorage[ name ];
|
|
1947
2021
|
}
|
|
1948
2022
|
|
|
2023
|
+
this._processLinesIfNecessary();
|
|
2024
|
+
|
|
1949
2025
|
return;
|
|
1950
2026
|
}
|
|
1951
2027
|
|
|
1952
2028
|
this.openedTabs[ name ] = code;
|
|
1953
2029
|
|
|
1954
|
-
const isNewTabButton = ( name === '+' );
|
|
1955
|
-
const tabIcon = this._getFileIcon( name );
|
|
1956
|
-
|
|
1957
|
-
this.tabs.add(name, code, {
|
|
1958
|
-
selected: true,
|
|
1959
|
-
fixed: isNewTabButton,
|
|
1960
|
-
title: code.title,
|
|
1961
|
-
icon: tabIcon,
|
|
1962
|
-
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
1963
|
-
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton ),
|
|
1964
|
-
allowDelete: true
|
|
1965
|
-
});
|
|
1966
|
-
|
|
1967
2030
|
// Move into the sizer..
|
|
1968
2031
|
this.codeSizer.appendChild( code );
|
|
1969
2032
|
|
|
@@ -1971,17 +2034,86 @@ class CodeEditor {
|
|
|
1971
2034
|
|
|
1972
2035
|
// Select as current...
|
|
1973
2036
|
this.code = code;
|
|
2037
|
+
this.mustProcessLines = true;
|
|
2038
|
+
|
|
1974
2039
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
1975
2040
|
this.processLines();
|
|
1976
2041
|
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
2042
|
+
this._processLinesIfNecessary();
|
|
1977
2043
|
this._updateDataInfoPanel( "@tab-name", code.tabName );
|
|
1978
2044
|
}
|
|
1979
2045
|
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
this.tabs.delete( name );
|
|
2046
|
+
loadTab( name ) {
|
|
1983
2047
|
|
|
1984
|
-
|
|
2048
|
+
// Already open...
|
|
2049
|
+
if( this.openedTabs[ name ] )
|
|
2050
|
+
{
|
|
2051
|
+
this.tabs.select( name );
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
let code = this.loadedTabs[ name ]
|
|
2056
|
+
|
|
2057
|
+
if( !code )
|
|
2058
|
+
{
|
|
2059
|
+
this.addTab( name, true );
|
|
2060
|
+
|
|
2061
|
+
// Unload lines from storage...
|
|
2062
|
+
const tabData = this._tabStorage[ name ];
|
|
2063
|
+
if( tabData )
|
|
2064
|
+
{
|
|
2065
|
+
this.code.lines = tabData.lines;
|
|
2066
|
+
|
|
2067
|
+
if( tabData.options.language )
|
|
2068
|
+
{
|
|
2069
|
+
this._changeLanguage( tabData.options.language, null, true );
|
|
2070
|
+
}
|
|
2071
|
+
else
|
|
2072
|
+
{
|
|
2073
|
+
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
delete this._tabStorage[ name ];
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
this._processLinesIfNecessary();
|
|
2080
|
+
|
|
2081
|
+
return;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
this.openedTabs[ name ] = code;
|
|
2085
|
+
|
|
2086
|
+
const isNewTabButton = ( name === '+' );
|
|
2087
|
+
const tabIcon = this._getFileIcon( name );
|
|
2088
|
+
|
|
2089
|
+
this.tabs.add(name, code, {
|
|
2090
|
+
selected: true,
|
|
2091
|
+
fixed: isNewTabButton,
|
|
2092
|
+
title: code.title,
|
|
2093
|
+
icon: tabIcon,
|
|
2094
|
+
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
2095
|
+
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton ),
|
|
2096
|
+
allowDelete: true
|
|
2097
|
+
});
|
|
2098
|
+
|
|
2099
|
+
// Move into the sizer..
|
|
2100
|
+
this.codeSizer.appendChild( code );
|
|
2101
|
+
|
|
2102
|
+
this.endSelection();
|
|
2103
|
+
|
|
2104
|
+
// Select as current...
|
|
2105
|
+
this.code = code;
|
|
2106
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
2107
|
+
this.processLines();
|
|
2108
|
+
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
2109
|
+
this._updateDataInfoPanel( "@tab-name", code.tabName );
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
closeTab( name, eraseAll ) {
|
|
2113
|
+
|
|
2114
|
+
this.tabs.delete( name );
|
|
2115
|
+
|
|
2116
|
+
if( eraseAll )
|
|
1985
2117
|
{
|
|
1986
2118
|
delete this.openedTabs[ name ];
|
|
1987
2119
|
delete this.loadedTabs[ name ];
|
|
@@ -1994,12 +2126,14 @@ class CodeEditor {
|
|
|
1994
2126
|
}
|
|
1995
2127
|
|
|
1996
2128
|
loadTabFromFile() {
|
|
2129
|
+
|
|
1997
2130
|
const input = document.createElement( 'input' );
|
|
1998
2131
|
input.type = 'file';
|
|
1999
2132
|
document.body.appendChild( input );
|
|
2000
2133
|
input.click();
|
|
2001
2134
|
input.addEventListener('change', e => {
|
|
2002
|
-
if (e.target.files[ 0 ])
|
|
2135
|
+
if (e.target.files[ 0 ])
|
|
2136
|
+
{
|
|
2003
2137
|
this.loadFile( e.target.files[ 0 ] );
|
|
2004
2138
|
}
|
|
2005
2139
|
input.remove();
|
|
@@ -2009,8 +2143,11 @@ class CodeEditor {
|
|
|
2009
2143
|
processFocus( active ) {
|
|
2010
2144
|
|
|
2011
2145
|
if( active )
|
|
2146
|
+
{
|
|
2012
2147
|
this.restartBlink();
|
|
2013
|
-
|
|
2148
|
+
}
|
|
2149
|
+
else
|
|
2150
|
+
{
|
|
2014
2151
|
clearInterval( this.blinker );
|
|
2015
2152
|
this.cursors.classList.remove( 'show' );
|
|
2016
2153
|
}
|
|
@@ -2018,10 +2155,10 @@ class CodeEditor {
|
|
|
2018
2155
|
|
|
2019
2156
|
processMouse( e ) {
|
|
2020
2157
|
|
|
2021
|
-
if( !e.target.classList.contains('code') && !e.target.classList.contains('
|
|
2158
|
+
if( !e.target.classList.contains('code') && !e.target.classList.contains('lexcodearea') ) return;
|
|
2022
2159
|
if( !this.code ) return;
|
|
2023
2160
|
|
|
2024
|
-
var cursor = this.
|
|
2161
|
+
var cursor = this.getCurrentCursor();
|
|
2025
2162
|
var code_rect = this.code.getBoundingClientRect();
|
|
2026
2163
|
var mouse_pos = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
|
|
2027
2164
|
|
|
@@ -2116,14 +2253,17 @@ class CodeEditor {
|
|
|
2116
2253
|
|
|
2117
2254
|
_onMouseUp( e ) {
|
|
2118
2255
|
|
|
2119
|
-
if( (LX.getTime() - this.lastMouseDown) < 120 )
|
|
2256
|
+
if( ( LX.getTime() - this.lastMouseDown ) < 120 )
|
|
2257
|
+
{
|
|
2120
2258
|
this.state.selectingText = false;
|
|
2121
2259
|
this.endSelection();
|
|
2122
2260
|
}
|
|
2123
2261
|
|
|
2124
|
-
const cursor = this.
|
|
2262
|
+
const cursor = this.getCurrentCursor();
|
|
2125
2263
|
if( cursor.selection )
|
|
2264
|
+
{
|
|
2126
2265
|
cursor.selection.invertIfNecessary();
|
|
2266
|
+
}
|
|
2127
2267
|
|
|
2128
2268
|
this.state.selectingText = false;
|
|
2129
2269
|
delete this._lastSelectionKeyDir;
|
|
@@ -2131,7 +2271,7 @@ class CodeEditor {
|
|
|
2131
2271
|
|
|
2132
2272
|
processClick( e ) {
|
|
2133
2273
|
|
|
2134
|
-
var cursor = this.
|
|
2274
|
+
var cursor = this.getCurrentCursor();
|
|
2135
2275
|
var code_rect = this.codeScroller.getBoundingClientRect();
|
|
2136
2276
|
var position = [( e.clientX - code_rect.x ) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
|
|
2137
2277
|
var ln = (position[ 1 ] / this.lineHeight)|0;
|
|
@@ -2148,7 +2288,6 @@ class CodeEditor {
|
|
|
2148
2288
|
{
|
|
2149
2289
|
// Make sure we only keep the main cursor..
|
|
2150
2290
|
this._removeSecondaryCursors();
|
|
2151
|
-
|
|
2152
2291
|
this.cursorToLine( cursor, ln, true );
|
|
2153
2292
|
this.cursorToPosition( cursor, string.length );
|
|
2154
2293
|
}
|
|
@@ -2162,38 +2301,45 @@ class CodeEditor {
|
|
|
2162
2301
|
this.hideAutoCompleteBox();
|
|
2163
2302
|
}
|
|
2164
2303
|
|
|
2165
|
-
updateSelections( e,
|
|
2304
|
+
updateSelections( e, keepRange, flags = CodeEditor.SELECTION_X_Y ) {
|
|
2166
2305
|
|
|
2167
2306
|
for( let cursor of this.cursors.children )
|
|
2168
2307
|
{
|
|
2169
2308
|
if( !cursor.selection )
|
|
2309
|
+
{
|
|
2170
2310
|
continue;
|
|
2311
|
+
}
|
|
2171
2312
|
|
|
2172
|
-
this._processSelection( cursor, e,
|
|
2313
|
+
this._processSelection( cursor, e, keepRange, flags );
|
|
2173
2314
|
}
|
|
2174
2315
|
}
|
|
2175
2316
|
|
|
2176
|
-
processSelections( e,
|
|
2317
|
+
processSelections( e, keepRange, flags = CodeEditor.SELECTION_X_Y ) {
|
|
2177
2318
|
|
|
2178
2319
|
for( let cursor of this.cursors.children )
|
|
2179
2320
|
{
|
|
2180
|
-
this._processSelection( cursor, e,
|
|
2321
|
+
this._processSelection( cursor, e, keepRange, flags );
|
|
2181
2322
|
}
|
|
2182
2323
|
}
|
|
2183
2324
|
|
|
2184
|
-
_processSelection( cursor, e,
|
|
2325
|
+
_processSelection( cursor, e, keepRange, flags = CodeEditor.SELECTION_X_Y ) {
|
|
2185
2326
|
|
|
2186
2327
|
const isMouseEvent = e && ( e.constructor == MouseEvent );
|
|
2187
2328
|
|
|
2188
|
-
if( isMouseEvent )
|
|
2329
|
+
if( isMouseEvent )
|
|
2330
|
+
{
|
|
2331
|
+
this.processClick( e );
|
|
2332
|
+
}
|
|
2189
2333
|
|
|
2190
2334
|
if( !cursor.selection )
|
|
2335
|
+
{
|
|
2191
2336
|
this.startSelection( cursor );
|
|
2337
|
+
}
|
|
2192
2338
|
|
|
2193
2339
|
this._hideActiveLine();
|
|
2194
2340
|
|
|
2195
2341
|
// Update selection
|
|
2196
|
-
if( !
|
|
2342
|
+
if( !keepRange )
|
|
2197
2343
|
{
|
|
2198
2344
|
let ccw = true;
|
|
2199
2345
|
|
|
@@ -2260,19 +2406,19 @@ class CodeEditor {
|
|
|
2260
2406
|
}
|
|
2261
2407
|
|
|
2262
2408
|
// Compute new width and selection margins
|
|
2263
|
-
let string;
|
|
2409
|
+
let string = "";
|
|
2264
2410
|
|
|
2265
|
-
if(sId == 0) // First line 2 cases (single line, multiline)
|
|
2411
|
+
if( sId == 0 ) // First line 2 cases (single line, multiline)
|
|
2266
2412
|
{
|
|
2267
2413
|
const reverse = fromX > toX;
|
|
2268
2414
|
if(deltaY == 0) string = !reverse ? this.code.lines[ i ].substring( fromX, toX ) : this.code.lines[ i ].substring(toX, fromX);
|
|
2269
2415
|
else string = this.code.lines[ i ].substr( fromX );
|
|
2270
|
-
const pixels = (reverse && deltaY == 0 ? toX : fromX) * this.charWidth;
|
|
2271
|
-
if( isVisible ) domEl.style.left =
|
|
2416
|
+
const pixels = ( reverse && deltaY == 0 ? toX : fromX ) * this.charWidth;
|
|
2417
|
+
if( isVisible ) domEl.style.left = `calc(${ pixels }px + ${ this.xPadding })`;
|
|
2272
2418
|
}
|
|
2273
2419
|
else
|
|
2274
2420
|
{
|
|
2275
|
-
string = (i == toY) ? this.code.lines[ i ].substring( 0, toX ) : this.code.lines[ i ]; // Last line, any multiple line...
|
|
2421
|
+
string = ( i == toY ) ? this.code.lines[ i ].substring( 0, toX ) : this.code.lines[ i ]; // Last line, any multiple line...
|
|
2276
2422
|
if( isVisible ) domEl.style.left = this.xPadding;
|
|
2277
2423
|
}
|
|
2278
2424
|
|
|
@@ -2281,7 +2427,7 @@ class CodeEditor {
|
|
|
2281
2427
|
|
|
2282
2428
|
if( isVisible )
|
|
2283
2429
|
{
|
|
2284
|
-
domEl.style.width = (stringWidth || 8) + "px";
|
|
2430
|
+
domEl.style.width = ( stringWidth || 8 ) + "px";
|
|
2285
2431
|
domEl._top = i * this.lineHeight;
|
|
2286
2432
|
domEl.style.top = domEl._top + "px";
|
|
2287
2433
|
}
|
|
@@ -2302,7 +2448,7 @@ class CodeEditor {
|
|
|
2302
2448
|
{
|
|
2303
2449
|
// Make sure that the line selection is generated...
|
|
2304
2450
|
domEl = cursorSelections.childNodes[ sId ];
|
|
2305
|
-
if(!domEl)
|
|
2451
|
+
if( !domEl )
|
|
2306
2452
|
{
|
|
2307
2453
|
domEl = document.createElement( 'div' );
|
|
2308
2454
|
domEl.className = "lexcodeselection";
|
|
@@ -2421,7 +2567,7 @@ class CodeEditor {
|
|
|
2421
2567
|
|
|
2422
2568
|
_processGlobalKeys( e, key ) {
|
|
2423
2569
|
|
|
2424
|
-
let cursor = this.
|
|
2570
|
+
let cursor = this.getCurrentCursor();
|
|
2425
2571
|
|
|
2426
2572
|
if( e.ctrlKey || e.metaKey )
|
|
2427
2573
|
{
|
|
@@ -2489,7 +2635,7 @@ class CodeEditor {
|
|
|
2489
2635
|
|
|
2490
2636
|
async _processKeyAtCursor( e, key, cursor ) {
|
|
2491
2637
|
|
|
2492
|
-
const
|
|
2638
|
+
const skipUndo = e.detail.skipUndo ?? false;
|
|
2493
2639
|
|
|
2494
2640
|
// keys with length > 1 are probably special keys
|
|
2495
2641
|
if( key.length > 1 && this.specialKeys.indexOf( key ) == -1 )
|
|
@@ -2579,7 +2725,7 @@ class CodeEditor {
|
|
|
2579
2725
|
|
|
2580
2726
|
// Add undo steps
|
|
2581
2727
|
|
|
2582
|
-
if( !
|
|
2728
|
+
if( !skipUndo && this.code.lines.length )
|
|
2583
2729
|
{
|
|
2584
2730
|
this._addUndoStep( cursor );
|
|
2585
2731
|
}
|
|
@@ -2640,17 +2786,7 @@ class CodeEditor {
|
|
|
2640
2786
|
this.processLine( lidx );
|
|
2641
2787
|
|
|
2642
2788
|
// We are out of the viewport and max length is different? Resize scrollbars...
|
|
2643
|
-
|
|
2644
|
-
const numViewportChars = Math.floor( ( this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH ) / this.charWidth );
|
|
2645
|
-
if( maxLineLength >= numViewportChars && maxLineLength != this._lastMaxLineLength )
|
|
2646
|
-
{
|
|
2647
|
-
this.resize( maxLineLength, () => {
|
|
2648
|
-
if( cursor.position > numViewportChars )
|
|
2649
|
-
{
|
|
2650
|
-
this.setScrollLeft( cursor.position * this.charWidth );
|
|
2651
|
-
}
|
|
2652
|
-
} );
|
|
2653
|
-
}
|
|
2789
|
+
this.resizeIfNecessary( cursor );
|
|
2654
2790
|
|
|
2655
2791
|
// Manage autocomplete
|
|
2656
2792
|
|
|
@@ -2662,6 +2798,8 @@ class CodeEditor {
|
|
|
2662
2798
|
|
|
2663
2799
|
async _pasteContent( cursor ) {
|
|
2664
2800
|
|
|
2801
|
+
const mustDetectLanguage = ( !this.getText().length );
|
|
2802
|
+
|
|
2665
2803
|
let text = await navigator.clipboard.readText();
|
|
2666
2804
|
|
|
2667
2805
|
// Remove any possible tabs (\t) and add spaces
|
|
@@ -2674,9 +2812,19 @@ class CodeEditor {
|
|
|
2674
2812
|
const currentScroll = this.getScrollTop();
|
|
2675
2813
|
const scroll = Math.max( cursor.line * this.lineHeight - this.codeScroller.offsetWidth, 0 );
|
|
2676
2814
|
|
|
2677
|
-
if( currentScroll < scroll )
|
|
2815
|
+
if( currentScroll < scroll )
|
|
2816
|
+
{
|
|
2678
2817
|
this.codeScroller.scrollTo( 0, cursor.line * this.lineHeight );
|
|
2679
2818
|
}
|
|
2819
|
+
|
|
2820
|
+
if( mustDetectLanguage )
|
|
2821
|
+
{
|
|
2822
|
+
const detectedLang = this._detectLanguage( text );
|
|
2823
|
+
if( detectedLang )
|
|
2824
|
+
{
|
|
2825
|
+
this._changeLanguage( detectedLang );
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2680
2828
|
}
|
|
2681
2829
|
|
|
2682
2830
|
async _copyContent( cursor ) {
|
|
@@ -2699,7 +2847,9 @@ class CodeEditor {
|
|
|
2699
2847
|
let index = 0;
|
|
2700
2848
|
|
|
2701
2849
|
for( let i = 0; i <= cursor.selection.fromY; i++ )
|
|
2850
|
+
{
|
|
2702
2851
|
index += ( i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length );
|
|
2852
|
+
}
|
|
2703
2853
|
|
|
2704
2854
|
index += cursor.selection.fromY * separator.length;
|
|
2705
2855
|
const num_chars = cursor.selection.chars + ( cursor.selection.toY - cursor.selection.fromY ) * separator.length;
|
|
@@ -2775,7 +2925,7 @@ class CodeEditor {
|
|
|
2775
2925
|
|
|
2776
2926
|
if( cursor.selection )
|
|
2777
2927
|
{
|
|
2778
|
-
var cursor = this.
|
|
2928
|
+
var cursor = this.getCurrentCursor();
|
|
2779
2929
|
this._addUndoStep( cursor, true );
|
|
2780
2930
|
|
|
2781
2931
|
const selectedLines = this.code.lines.slice( cursor.selection.fromY, cursor.selection.toY );
|
|
@@ -2834,7 +2984,7 @@ class CodeEditor {
|
|
|
2834
2984
|
|
|
2835
2985
|
if( cursor.selection )
|
|
2836
2986
|
{
|
|
2837
|
-
var cursor = this.
|
|
2987
|
+
var cursor = this.getCurrentCursor();
|
|
2838
2988
|
this._addUndoStep( cursor, true );
|
|
2839
2989
|
|
|
2840
2990
|
for( var i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
@@ -2888,7 +3038,6 @@ class CodeEditor {
|
|
|
2888
3038
|
}
|
|
2889
3039
|
|
|
2890
3040
|
_actionMustDelete( cursor, action, e ) {
|
|
2891
|
-
|
|
2892
3041
|
return cursor.selection && action.deleteSelection &&
|
|
2893
3042
|
( action.eventSkipDelete ? !e[ action.eventSkipDelete ] : true );
|
|
2894
3043
|
}
|
|
@@ -2906,20 +3055,32 @@ class CodeEditor {
|
|
|
2906
3055
|
}
|
|
2907
3056
|
|
|
2908
3057
|
toLocalLine( line ) {
|
|
2909
|
-
|
|
2910
3058
|
const d = Math.max( this.firstLineInViewport - this.lineScrollMargin.x, 0 );
|
|
2911
3059
|
return Math.min( Math.max( line - d, 0 ), this.code.lines.length - 1 );
|
|
2912
3060
|
}
|
|
2913
3061
|
|
|
2914
3062
|
getMaxLineLength() {
|
|
2915
|
-
|
|
2916
3063
|
return Math.max(...this.code.lines.map( v => v.length ));
|
|
2917
3064
|
}
|
|
2918
3065
|
|
|
3066
|
+
_processLinesIfNecessary() {
|
|
3067
|
+
if( this.mustProcessLines )
|
|
3068
|
+
{
|
|
3069
|
+
this.mustProcessLines = false;
|
|
3070
|
+
this.processLines();
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
|
|
2919
3074
|
processLines( mode ) {
|
|
2920
3075
|
|
|
3076
|
+
if( !this.code )
|
|
3077
|
+
{
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
|
|
2921
3081
|
var htmlCode = "";
|
|
2922
3082
|
this._blockCommentCache.length = 0;
|
|
3083
|
+
this.mustProcessLines = false;
|
|
2923
3084
|
|
|
2924
3085
|
// Reset all lines content
|
|
2925
3086
|
this.code.innerHTML = "";
|
|
@@ -2928,7 +3089,7 @@ class CodeEditor {
|
|
|
2928
3089
|
const lastScrollTop = this.getScrollTop();
|
|
2929
3090
|
this.firstLineInViewport = ( mode ?? CodeEditor.KEEP_VISIBLE_LINES ) & CodeEditor.UPDATE_VISIBLE_LINES ?
|
|
2930
3091
|
( (lastScrollTop / this.lineHeight)|0 ) : this.firstLineInViewport;
|
|
2931
|
-
const totalLinesInViewport = ((this.codeScroller.offsetHeight
|
|
3092
|
+
const totalLinesInViewport = ( ( this.codeScroller.offsetHeight ) / this.lineHeight )|0;
|
|
2932
3093
|
this.visibleLinesViewport = new LX.vec2(
|
|
2933
3094
|
Math.max( this.firstLineInViewport - this.lineScrollMargin.x, 0 ),
|
|
2934
3095
|
Math.min( this.firstLineInViewport + totalLinesInViewport + this.lineScrollMargin.y, this.code.lines.length )
|
|
@@ -2943,7 +3104,7 @@ class CodeEditor {
|
|
|
2943
3104
|
}
|
|
2944
3105
|
}
|
|
2945
3106
|
|
|
2946
|
-
this._scopeStack = [];
|
|
3107
|
+
this._scopeStack = [ { name: "", type: "global" } ];
|
|
2947
3108
|
|
|
2948
3109
|
// Process visible lines
|
|
2949
3110
|
for( let i = this.visibleLinesViewport.x; i < this.visibleLinesViewport.y; ++i )
|
|
@@ -2965,162 +3126,617 @@ class CodeEditor {
|
|
|
2965
3126
|
this.resize();
|
|
2966
3127
|
}
|
|
2967
3128
|
|
|
2968
|
-
processLine( lineNumber, force ) {
|
|
3129
|
+
processLine( lineNumber, force, skipPropagation ) {
|
|
3130
|
+
|
|
3131
|
+
if( this._scopeStack )
|
|
3132
|
+
{
|
|
3133
|
+
this.code.lineScopes[ lineNumber ] = [ ...this._scopeStack ];
|
|
3134
|
+
}
|
|
3135
|
+
else
|
|
3136
|
+
{
|
|
3137
|
+
this.code.lineScopes[ lineNumber ] = this.code.lineScopes[ lineNumber ] ?? [];
|
|
3138
|
+
this._scopeStack = [ ...this.code.lineScopes[ lineNumber ] ];
|
|
3139
|
+
}
|
|
2969
3140
|
|
|
2970
|
-
|
|
2971
|
-
|
|
3141
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
3142
|
+
const localLineNum = this.toLocalLine( lineNumber );
|
|
3143
|
+
const lineString = this.code.lines[ lineNumber ];
|
|
3144
|
+
if( lineString === undefined )
|
|
2972
3145
|
{
|
|
2973
|
-
this.processLines();
|
|
2974
3146
|
return;
|
|
2975
3147
|
}
|
|
2976
3148
|
|
|
2977
|
-
|
|
3149
|
+
this._lastProcessedLine = lineNumber;
|
|
3150
|
+
|
|
3151
|
+
// multi-line strings not supported by now
|
|
3152
|
+
delete this._buildingString;
|
|
3153
|
+
delete this._pendingString;
|
|
3154
|
+
delete this._markdownHeader;
|
|
3155
|
+
|
|
3156
|
+
// Single line
|
|
3157
|
+
if( !force )
|
|
2978
3158
|
{
|
|
2979
|
-
this.
|
|
3159
|
+
LX.deleteElement( this.code.childNodes[ localLineNum ] );
|
|
3160
|
+
this.code.insertChildAtIndex( document.createElement( 'pre' ), localLineNum );
|
|
2980
3161
|
}
|
|
2981
|
-
|
|
3162
|
+
|
|
3163
|
+
// Early out check for no highlighting languages
|
|
3164
|
+
if( this.highlight == 'Plain Text' )
|
|
2982
3165
|
{
|
|
2983
|
-
|
|
3166
|
+
const plainTextHtml = lineString.replaceAll('<', '<').replaceAll('>', '>');
|
|
3167
|
+
return this._updateLine( force, lineNumber, plainTextHtml, skipPropagation );
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
this._currentLineNumber = lineNumber;
|
|
3171
|
+
this._currentLineString = lineString;
|
|
3172
|
+
|
|
3173
|
+
const tokensToEvaluate = this._getTokensFromLine( lineString );
|
|
3174
|
+
if( !tokensToEvaluate.length )
|
|
3175
|
+
{
|
|
3176
|
+
return this._updateLine( force, lineNumber, "", skipPropagation );
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
let lineInnerHtml = "";
|
|
3180
|
+
let pushedScope = false;
|
|
3181
|
+
|
|
3182
|
+
const newSignature = this._getLineSignatureFromTokens( tokensToEvaluate );
|
|
3183
|
+
const cachedSignature = this.code.lineSignatures[ lineNumber ];
|
|
3184
|
+
const mustUpdateScopes = ( cachedSignature !== newSignature ) && !force;
|
|
3185
|
+
const blockComments = lang.blockComments ?? true;
|
|
3186
|
+
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
3187
|
+
|
|
3188
|
+
// Reset scope stack if structural changes in current line
|
|
3189
|
+
if( mustUpdateScopes )
|
|
3190
|
+
{
|
|
3191
|
+
this._scopeStack = [ { name: "", type: "global" } ];
|
|
2984
3192
|
}
|
|
2985
3193
|
|
|
3194
|
+
// Process all tokens
|
|
3195
|
+
for( let i = 0; i < tokensToEvaluate.length; ++i )
|
|
3196
|
+
{
|
|
3197
|
+
let it = i - 1;
|
|
3198
|
+
let prev = tokensToEvaluate[ it ];
|
|
3199
|
+
while( prev == ' ' )
|
|
3200
|
+
{
|
|
3201
|
+
it--;
|
|
3202
|
+
prev = tokensToEvaluate[ it ];
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3205
|
+
it = i + 1;
|
|
3206
|
+
let next = tokensToEvaluate[ it ];
|
|
3207
|
+
while( next == ' ' || next == '"' )
|
|
3208
|
+
{
|
|
3209
|
+
it++;
|
|
3210
|
+
next = tokensToEvaluate[ it ];
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
const token = tokensToEvaluate[ i ];
|
|
3214
|
+
const tokenIndex = i;
|
|
3215
|
+
const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];;
|
|
3216
|
+
|
|
3217
|
+
if( blockComments )
|
|
3218
|
+
{
|
|
3219
|
+
if( token.substr( 0, blockCommentsTokens[ 0 ].length ) == blockCommentsTokens[ 0 ] )
|
|
3220
|
+
{
|
|
3221
|
+
this._buildingBlockComment = [ lineNumber, tokenStartIndex ];
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
// Compare line signature for structural changes
|
|
3226
|
+
// to pop current scope if necessary
|
|
3227
|
+
if( token === "}" && this._scopeStack.length > 1 )
|
|
3228
|
+
{
|
|
3229
|
+
this._scopeStack.pop();
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
lineInnerHtml += this._evaluateToken( {
|
|
3233
|
+
token,
|
|
3234
|
+
prev,
|
|
3235
|
+
prevWithSpaces: tokensToEvaluate[ i - 1 ],
|
|
3236
|
+
next,
|
|
3237
|
+
nextWithSpaces: tokensToEvaluate[ i + 1 ],
|
|
3238
|
+
tokenIndex,
|
|
3239
|
+
isFirstToken: ( tokenIndex == 0 ),
|
|
3240
|
+
isLastToken: ( tokenIndex == tokensToEvaluate.length - 1 ),
|
|
3241
|
+
tokens: tokensToEvaluate
|
|
3242
|
+
} );
|
|
3243
|
+
|
|
3244
|
+
if( blockComments && this._buildingBlockComment != undefined
|
|
3245
|
+
&& token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
|
|
3246
|
+
{
|
|
3247
|
+
const [ commentLineNumber, tokenPos ] = this._buildingBlockComment;
|
|
3248
|
+
this._blockCommentCache.push( [ new LX.vec2( commentLineNumber, lineNumber ), new LX.vec2( tokenPos, tokenStartIndex ) ] );
|
|
3249
|
+
delete this._buildingBlockComment;
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
if( token !== "{" )
|
|
3253
|
+
{
|
|
3254
|
+
continue;
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
// Store current scopes
|
|
3258
|
+
|
|
3259
|
+
let contextTokens = [
|
|
3260
|
+
...this._getTokensFromLine( this.code.lines[ lineNumber ].substring( 0, tokenStartIndex ) )
|
|
3261
|
+
];
|
|
3262
|
+
|
|
3263
|
+
// Add token context from above lines in case we don't have information
|
|
3264
|
+
// in the same line to get the scope data
|
|
3265
|
+
if( !prev )
|
|
3266
|
+
{
|
|
3267
|
+
for( let k = 1; k < 50; k++ )
|
|
3268
|
+
{
|
|
3269
|
+
let kLineString = this.code.lines[ lineNumber - k ];
|
|
3270
|
+
if( !kLineString )
|
|
3271
|
+
{
|
|
3272
|
+
break;
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3275
|
+
const openIdx = kLineString.lastIndexOf( '{' );
|
|
3276
|
+
const closeIdx = kLineString.lastIndexOf( '}' );
|
|
3277
|
+
if( openIdx > -1 )
|
|
3278
|
+
{
|
|
3279
|
+
kLineString = kLineString.substr( openIdx );
|
|
3280
|
+
}
|
|
3281
|
+
else if( closeIdx > -1 )
|
|
3282
|
+
{
|
|
3283
|
+
kLineString = kLineString.substr( closeIdx );
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
contextTokens = [ ...this._getTokensFromLine( kLineString ), ...contextTokens ];
|
|
3287
|
+
|
|
3288
|
+
if( kLineString.length !== this.code.lines[ lineNumber - k ] )
|
|
3289
|
+
{
|
|
3290
|
+
break;
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3295
|
+
contextTokens = contextTokens.reverse().filter( v => v.length && v != ' ' );
|
|
3296
|
+
|
|
3297
|
+
// Keywords that can open a *named* scope
|
|
3298
|
+
// TODO: Do this per language
|
|
3299
|
+
const scopeKeywords = ["class", "enum", "function", "interface", "type", "struct", "namespace"];
|
|
3300
|
+
|
|
3301
|
+
let scopeType = null; // This is the type of scope (function, class, enum, etc)
|
|
3302
|
+
let scopeName = null;
|
|
3303
|
+
|
|
3304
|
+
for( let i = 0; i < contextTokens.length; i++ )
|
|
3305
|
+
{
|
|
3306
|
+
const t = contextTokens[ i ];
|
|
3307
|
+
|
|
3308
|
+
if ( scopeKeywords.includes( t ) )
|
|
3309
|
+
{
|
|
3310
|
+
scopeType = t;
|
|
3311
|
+
scopeName = contextTokens[ i - 1 ]; // usually right before the keyword in reversed array
|
|
3312
|
+
break;
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
// Special case: enum type specification `enum Foo : int {`
|
|
3317
|
+
if( scopeType === "enum" && contextTokens.includes( ":" ) )
|
|
3318
|
+
{
|
|
3319
|
+
const colonIndex = contextTokens.indexOf( ":" );
|
|
3320
|
+
scopeName = contextTokens[ colonIndex + 1 ] || scopeName;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
if( !scopeType )
|
|
3324
|
+
{
|
|
3325
|
+
const parOpenIndex = contextTokens.indexOf( "(" );
|
|
3326
|
+
scopeName = contextTokens[ parOpenIndex + 1 ] || scopeName;
|
|
3327
|
+
|
|
3328
|
+
if( scopeName )
|
|
3329
|
+
{
|
|
3330
|
+
scopeType = "method";
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// Only push if it's not already reflected in the cached scopes
|
|
3335
|
+
const lastScope = this._scopeStack.at( -1 );
|
|
3336
|
+
if( lastScope?.lineNumber !== lineNumber )
|
|
3337
|
+
{
|
|
3338
|
+
this._scopeStack.push( { name: scopeName ?? "", type: scopeType ?? "anonymous", lineNumber } );
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
pushedScope = true;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
// Update scopes cache
|
|
3345
|
+
this.code.lineScopes[ lineNumber ] = [ ...this._scopeStack ];
|
|
3346
|
+
|
|
3347
|
+
const symbols = this._parseLineForSymbols( lineNumber, lineString, tokensToEvaluate, pushedScope );
|
|
3348
|
+
|
|
3349
|
+
return this._updateLine( force, lineNumber, lineInnerHtml, skipPropagation, symbols, tokensToEvaluate );
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
_getLineSignatureFromTokens( tokens ) {
|
|
3353
|
+
const structuralChars = new Set( [ '{', '}'] );
|
|
3354
|
+
const sign = tokens.filter( t => structuralChars.has( t ) );
|
|
3355
|
+
return sign.join( "_" );
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
_updateBlockComments( section, lineNumber, tokens ) {
|
|
3359
|
+
|
|
2986
3360
|
const lang = CodeEditor.languages[ this.highlight ];
|
|
2987
|
-
const
|
|
3361
|
+
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
3362
|
+
const lineOpensBlock = ( section[ 0 ].x === lineNumber );
|
|
3363
|
+
const lineClosesBlock = ( section[ 0 ].y === lineNumber );
|
|
3364
|
+
const lineInsideBlock = ( section[ 0 ].x !== lineNumber ) && ( section[ 0 ].y !== lineNumber );
|
|
3365
|
+
|
|
3366
|
+
delete this._buildingBlockComment;
|
|
3367
|
+
|
|
3368
|
+
/*
|
|
3369
|
+
Check if delimiters have been removed and process lines backwards/forward
|
|
3370
|
+
until reaching new delimiters
|
|
3371
|
+
*/
|
|
3372
|
+
|
|
3373
|
+
if( lineOpensBlock )
|
|
3374
|
+
{
|
|
3375
|
+
const r = tokens.filter( t => t.substr( 0, blockCommentsTokens[ 0 ].length ) == blockCommentsTokens[ 0 ] );
|
|
3376
|
+
if( !r.length )
|
|
3377
|
+
{
|
|
3378
|
+
this._buildingBlockComment = [ lineNumber - 1, 0 ];
|
|
3379
|
+
|
|
3380
|
+
this.mustProcessPreviousLine = ( tokens ) => {
|
|
3381
|
+
const idx = tokens.indexOf( blockCommentsTokens[ 0 ] );
|
|
3382
|
+
return ( idx === -1 );
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
this.processLine( lineNumber - 1, false, true );
|
|
3386
|
+
|
|
3387
|
+
section[ 0 ].x = this._lastProcessedLine;
|
|
3388
|
+
|
|
3389
|
+
const lastProcessedString = this.code.lines[ this._lastProcessedLine ];
|
|
3390
|
+
const idx = lastProcessedString.indexOf( blockCommentsTokens[ 0 ] );
|
|
3391
|
+
section[ 1 ].x = idx > 0 ? idx : 0;
|
|
3392
|
+
}
|
|
3393
|
+
else
|
|
3394
|
+
{
|
|
3395
|
+
const tokenIndex = tokens.indexOf( blockCommentsTokens[ 0 ] );
|
|
3396
|
+
const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];
|
|
3397
|
+
section[ 1 ].x = tokenStartIndex;
|
|
3398
|
+
console.log(tokenStartIndex)
|
|
3399
|
+
// Process current line to update new sections
|
|
3400
|
+
this.processLine( lineNumber, false, true );
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
else if( lineClosesBlock )
|
|
3404
|
+
{
|
|
3405
|
+
const r = tokens.filter( t => t.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] );
|
|
3406
|
+
if( !r.length )
|
|
3407
|
+
{
|
|
3408
|
+
this._buildingBlockComment = [ section[ 0 ].x, section[ 1 ].x ];
|
|
3409
|
+
|
|
3410
|
+
this.mustProcessNextLine = ( tokens ) => {
|
|
3411
|
+
const idx = tokens.indexOf( blockCommentsTokens[ 1 ] );
|
|
3412
|
+
return ( idx === -1 );
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
this.processLine( lineNumber + 1, false, true );
|
|
3416
|
+
|
|
3417
|
+
section[ 0 ].y = this._lastProcessedLine;
|
|
3418
|
+
|
|
3419
|
+
const lastProcessedString = this.code.lines[ this._lastProcessedLine ];
|
|
3420
|
+
const idx = lastProcessedString.indexOf( blockCommentsTokens[ 1 ] );
|
|
3421
|
+
section[ 1 ].y = idx > 0 ? idx : ( lastProcessedString.length - 1 );
|
|
3422
|
+
}
|
|
3423
|
+
else
|
|
3424
|
+
{
|
|
3425
|
+
const tokenIndex = tokens.indexOf( blockCommentsTokens[ 1 ] );
|
|
3426
|
+
const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];
|
|
3427
|
+
section[ 1 ].y = tokenStartIndex;
|
|
3428
|
+
// Process current line to update new sections
|
|
3429
|
+
this.processLine( lineNumber, false, true );
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
else if( lineInsideBlock )
|
|
3433
|
+
{
|
|
3434
|
+
// Here it can't modify delimiters..
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
_processExtraLineIfNecessary( lineNumber, tokens, oldSymbols, skipPropagation ) {
|
|
3439
|
+
|
|
3440
|
+
if( !this._scopeStack )
|
|
3441
|
+
{
|
|
3442
|
+
console.warn( "CodeEditor: No scope available" );
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
// Update block comments if necessary
|
|
3447
|
+
{
|
|
3448
|
+
const commentBlockSection = this._inBlockCommentSection( lineNumber, 1e10, -1e10 );
|
|
3449
|
+
if( tokens && commentBlockSection !== undefined )
|
|
3450
|
+
{
|
|
3451
|
+
this._updateBlockComments( commentBlockSection, lineNumber, tokens );
|
|
3452
|
+
|
|
3453
|
+
// Get again correct scope
|
|
3454
|
+
this._scopeStack = [ ...this.code.lineScopes[ lineNumber ] ];
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3458
|
+
if( ( (lineNumber + 1) === this.code.lines.length ) || !this.code.lineScopes[ lineNumber + 1 ] )
|
|
3459
|
+
{
|
|
3460
|
+
return;
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
const newSignature = this._getLineSignatureFromTokens( tokens );
|
|
3464
|
+
const cachedSignature = this.code.lineSignatures[ lineNumber ];
|
|
3465
|
+
const mustUpdateScopes = ( cachedSignature !== newSignature );
|
|
3466
|
+
const sameScopes = codeScopesEqual( this._scopeStack, this.code.lineScopes[ lineNumber + 1 ] );
|
|
3467
|
+
|
|
3468
|
+
// Only update scope stack if something changed when editing a single line
|
|
3469
|
+
// Compare line signature for structural changes
|
|
3470
|
+
if( ( mustUpdateScopes || this._scopesUpdated ) && ( !sameScopes && !skipPropagation ) )
|
|
3471
|
+
{
|
|
3472
|
+
if( mustUpdateScopes )
|
|
3473
|
+
{
|
|
3474
|
+
this._scopesUpdated = true;
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3477
|
+
this.code.lineScopes[ lineNumber + 1 ] = [ ...this._scopeStack ];
|
|
3478
|
+
this.processLine( lineNumber + 1 );
|
|
3479
|
+
|
|
3480
|
+
delete this._scopesUpdated;
|
|
3481
|
+
}
|
|
3482
|
+
else if( sameScopes )
|
|
3483
|
+
{
|
|
3484
|
+
// In case of same scope, check for occurrencies of the old symbols, to reprocess that lines
|
|
3485
|
+
for( const sym of oldSymbols )
|
|
3486
|
+
{
|
|
3487
|
+
const tableSymbol = this.code.symbolsTable.get( sym.name );
|
|
3488
|
+
if( tableSymbol === undefined )
|
|
3489
|
+
{
|
|
3490
|
+
return;
|
|
3491
|
+
}
|
|
3492
|
+
|
|
3493
|
+
for( const occ of tableSymbol )
|
|
3494
|
+
{
|
|
3495
|
+
if( occ.line === lineNumber )
|
|
3496
|
+
{
|
|
3497
|
+
continue;
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
this.processLine( occ.line, false, true );
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
|
|
3506
|
+
_updateLine( force, lineNumber, html, skipPropagation, symbols = [], tokens = [] ) {
|
|
3507
|
+
|
|
2988
3508
|
const gutterLineHtml = `<span class='line-gutter'>${ lineNumber + 1 }</span>`;
|
|
3509
|
+
const oldSymbols = this._updateLineSymbols( lineNumber, symbols );
|
|
3510
|
+
const lineScope = CodeEditor.debugScopes && this.code.lineScopes[ lineNumber ] ? this.code.lineScopes[ lineNumber ].map( s => `${ s.type }` ).join( ", " ) : "";
|
|
3511
|
+
const lineSymbols = CodeEditor.debugSymbols && this.code.lineSymbols[ lineNumber ] ? this.code.lineSymbols[ lineNumber ].map( s => `${ s.name }(${ s.kind })` ).join( ", " ) : "";
|
|
3512
|
+
const debugString = lineScope + ( lineScope.length ? " - " : "" ) + lineSymbols;
|
|
3513
|
+
|
|
3514
|
+
if( !force ) // Single line update
|
|
3515
|
+
{
|
|
3516
|
+
this.code.childNodes[ this.toLocalLine( lineNumber ) ].innerHTML = ( gutterLineHtml + html + debugString );
|
|
2989
3517
|
|
|
2990
|
-
|
|
2991
|
-
if( !force ) // Single line update
|
|
3518
|
+
if( !skipPropagation )
|
|
2992
3519
|
{
|
|
2993
|
-
|
|
2994
|
-
this.code.childNodes[ localLineNum ].innerHTML = gutterLineHtml + html;
|
|
2995
|
-
this._setActiveLine( lineNumber );
|
|
2996
|
-
this._clearTmpVariables();
|
|
3520
|
+
this._processExtraLineIfNecessary( lineNumber, tokens, oldSymbols, skipPropagation );
|
|
2997
3521
|
}
|
|
2998
|
-
|
|
3522
|
+
|
|
3523
|
+
if( this.mustProcessNextLine )
|
|
2999
3524
|
{
|
|
3000
|
-
|
|
3525
|
+
if( this.mustProcessNextLine( tokens ) && ( ( lineNumber + 1 ) < this.code.lines.length ) )
|
|
3526
|
+
{
|
|
3527
|
+
this.processLine( lineNumber + 1, false, true );
|
|
3528
|
+
}
|
|
3529
|
+
else
|
|
3530
|
+
{
|
|
3531
|
+
delete this.mustProcessNextLine;
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
if( this.mustProcessPreviousLine )
|
|
3536
|
+
{
|
|
3537
|
+
if( this.mustProcessPreviousLine( tokens ) && ( ( lineNumber - 1 ) >= 0 ) )
|
|
3538
|
+
{
|
|
3539
|
+
this.processLine( lineNumber - 1, false, true );
|
|
3540
|
+
}
|
|
3541
|
+
else
|
|
3542
|
+
{
|
|
3543
|
+
delete this.mustProcessPreviousLine;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
if( CodeEditor.debugProcessedLines )
|
|
3548
|
+
{
|
|
3549
|
+
this.code.childNodes[ lineNumber ]?.classList.add( "debug" );
|
|
3001
3550
|
}
|
|
3551
|
+
|
|
3552
|
+
this._setActiveLine( lineNumber );
|
|
3553
|
+
this._clearTmpVariables();
|
|
3554
|
+
}
|
|
3555
|
+
|
|
3556
|
+
this.code.lineSignatures[ lineNumber ] = this._getLineSignatureFromTokens( tokens );
|
|
3557
|
+
|
|
3558
|
+
// Update all lines at once
|
|
3559
|
+
return force ? `<pre>${ ( gutterLineHtml + html + debugString ) }</pre>` : undefined;
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
/**
|
|
3563
|
+
* Parses a single line of code and extract declared symbols
|
|
3564
|
+
*/
|
|
3565
|
+
_parseLineForSymbols( lineNumber, lineString, tokens, pushedScope ) {
|
|
3566
|
+
|
|
3567
|
+
const scope = this._scopeStack.at( pushedScope ? -2 : -1 );
|
|
3568
|
+
|
|
3569
|
+
if( !scope || this._inBlockCommentSection( lineNumber ) )
|
|
3570
|
+
{
|
|
3571
|
+
return [];
|
|
3002
3572
|
}
|
|
3003
3573
|
|
|
3004
|
-
const
|
|
3005
|
-
|
|
3006
|
-
|
|
3574
|
+
const scopeName = scope.name;
|
|
3575
|
+
const scopeType = scope.type;
|
|
3576
|
+
const symbols = [];
|
|
3577
|
+
const symbolsMap = new Map();
|
|
3578
|
+
const text = lineString.trim();
|
|
3579
|
+
const previousLineScope = this.code.lineScopes[ lineNumber - 1 ];
|
|
3580
|
+
|
|
3581
|
+
const _pushSymbol = ( s ) => {
|
|
3582
|
+
const signature = `${ s.name }_${ s.kind }_${ s.scope }_${ s.line }`;
|
|
3583
|
+
if( symbolsMap.has( signature ) )
|
|
3584
|
+
{
|
|
3007
3585
|
return;
|
|
3586
|
+
}
|
|
3587
|
+
symbolsMap.set( signature, s );
|
|
3588
|
+
symbols.push( s );
|
|
3589
|
+
};
|
|
3590
|
+
|
|
3591
|
+
// Don't make symbols from preprocessor lines
|
|
3592
|
+
if( text.startsWith( "#" ) )
|
|
3593
|
+
{
|
|
3594
|
+
return [];
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
const nativeTypes = CodeEditor.nativeTypes[ this.highlight ];
|
|
3008
3598
|
|
|
3009
|
-
|
|
3599
|
+
const topLevelRegexes = [
|
|
3600
|
+
[/^class\s+([A-Za-z0-9_]+)/, "class"],
|
|
3601
|
+
[/^struct\s+([A-Za-z0-9_]+)/, "struct"],
|
|
3602
|
+
[/^enum\s+([A-Za-z0-9_]+)/, "enum"],
|
|
3603
|
+
[/^interface\s+([A-Za-z0-9_]+)/, "interface"],
|
|
3604
|
+
[/^type\s+([A-Za-z0-9_]+)/, "type"],
|
|
3605
|
+
[/^function\s+([A-Za-z0-9_]+)/, "method"],
|
|
3606
|
+
[/^fn\s+([A-Za-z0-9_]+)/, "method"],
|
|
3607
|
+
[/^def\s+([A-Za-z0-9_]+)/, "method"],
|
|
3608
|
+
[/^([A-Za-z0-9_]+)\s*=\s*\(?.*\)?\s*=>/, "method"] // arrow functions
|
|
3609
|
+
];
|
|
3010
3610
|
|
|
3011
|
-
|
|
3611
|
+
// Add regexes to detect methods, variables ( including "id : nativeType" )
|
|
3612
|
+
{
|
|
3613
|
+
if( nativeTypes )
|
|
3012
3614
|
{
|
|
3013
|
-
|
|
3615
|
+
topLevelRegexes.push( [ new RegExp( `^(?:${nativeTypes.join('|')})\\s+([A-Za-z0-9_]+)\s*[\(]+` ), 'method' ] );
|
|
3616
|
+
|
|
3617
|
+
if( this.highlight === "WGSL" )
|
|
3618
|
+
{
|
|
3619
|
+
topLevelRegexes.push( [ new RegExp( `[A-Za-z0-9]+(\\s*)+:(\\s*)+(${nativeTypes.join('|')})` ), 'variable', ( m ) => m[ 0 ].split( ":" )[ 0 ].trim() ] );
|
|
3620
|
+
}
|
|
3014
3621
|
}
|
|
3622
|
+
|
|
3623
|
+
const declarationKeywords = CodeEditor.declarationKeywords[ this.highlight ] ?? [ "const", "let", "var" ];
|
|
3624
|
+
topLevelRegexes.push( [ new RegExp( `^(?:${ declarationKeywords.join('|') })\\s+([A-Za-z0-9_]+)` ), 'variable' ] );
|
|
3015
3625
|
}
|
|
3016
3626
|
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3627
|
+
for( let [ regex, kind, fn ] of topLevelRegexes )
|
|
3628
|
+
{
|
|
3629
|
+
const m = text.match( regex );
|
|
3630
|
+
if( m )
|
|
3631
|
+
{
|
|
3632
|
+
_pushSymbol( { name: fn ? fn( m ) : m[ 1 ], kind, scope: scopeName, line: lineNumber } );
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3021
3635
|
|
|
3022
|
-
|
|
3636
|
+
const usageRegexes = [
|
|
3637
|
+
[/new\s+([A-Za-z0-9_]+)\s*\(/, "constructor-call"],
|
|
3638
|
+
[/this.([A-Za-z_][A-Za-z0-9_]*)\s*\=/, "class-property"],
|
|
3639
|
+
];
|
|
3023
3640
|
|
|
3024
|
-
|
|
3025
|
-
if( !force )
|
|
3641
|
+
for( let [ regex, kind, fn ] of usageRegexes )
|
|
3026
3642
|
{
|
|
3027
|
-
|
|
3028
|
-
|
|
3643
|
+
const m = text.match( regex );
|
|
3644
|
+
if( m )
|
|
3645
|
+
{
|
|
3646
|
+
_pushSymbol( { name: fn ? fn( m ) : m[ 1 ], kind, scope: scopeName, line: lineNumber } );
|
|
3647
|
+
}
|
|
3029
3648
|
}
|
|
3030
3649
|
|
|
3031
|
-
//
|
|
3032
|
-
|
|
3650
|
+
// Detect method calls
|
|
3651
|
+
const regex = /([A-Za-z0-9_]+)\s*\(/g;
|
|
3652
|
+
let match;
|
|
3653
|
+
while( match = regex.exec( text ) )
|
|
3033
3654
|
{
|
|
3034
|
-
const
|
|
3035
|
-
|
|
3655
|
+
const name = match[ 1 ];
|
|
3656
|
+
const before = text.slice( 0, match.index );
|
|
3657
|
+
if( /(new|function|fn|def)\s+$/.test( before ) ) continue; // skip constructor calls
|
|
3658
|
+
if( [ "constructor", "location", ...( nativeTypes ?? [] ) ].indexOf( name ) > -1 ) continue; // skip hardcoded non method symbol
|
|
3659
|
+
if( previousLineScope && previousLineScope.at( -1 )?.type === "class" ) continue; // skip class methods
|
|
3660
|
+
_pushSymbol( { name, kind: "method-call", scope: scopeName, line: lineNumber } );
|
|
3036
3661
|
}
|
|
3037
3662
|
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
const tokensToEvaluate = this._getTokensFromLine( lineString );
|
|
3042
|
-
if( !tokensToEvaluate.length )
|
|
3663
|
+
// Stop after matches for top-level declarations and usage symbols
|
|
3664
|
+
if( symbols.length )
|
|
3043
3665
|
{
|
|
3044
|
-
return
|
|
3666
|
+
return symbols;
|
|
3045
3667
|
}
|
|
3046
3668
|
|
|
3047
|
-
|
|
3669
|
+
const nonWhiteSpaceTokens = tokens.filter( t => t.trim().length );
|
|
3048
3670
|
|
|
3049
|
-
|
|
3050
|
-
for( let i = 0; i < tokensToEvaluate.length; ++i )
|
|
3671
|
+
for( let i = 0; i < nonWhiteSpaceTokens.length; i++ )
|
|
3051
3672
|
{
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3673
|
+
const prev = nonWhiteSpaceTokens[ i - 1 ];
|
|
3674
|
+
const token = nonWhiteSpaceTokens[ i ];
|
|
3675
|
+
const next = nonWhiteSpaceTokens[ i + 1 ];
|
|
3676
|
+
|
|
3677
|
+
if( scopeType.startsWith("class") )
|
|
3055
3678
|
{
|
|
3056
|
-
|
|
3057
|
-
|
|
3679
|
+
if( next === "(" && /^[a-zA-Z_]\w*$/.test( token ) && prev === undefined )
|
|
3680
|
+
{
|
|
3681
|
+
if( token === "constructor" ) continue; // skip constructor symbol
|
|
3682
|
+
_pushSymbol( { name: token, kind: "method", scope: scopeName, line: lineNumber } );
|
|
3683
|
+
}
|
|
3058
3684
|
}
|
|
3059
|
-
|
|
3060
|
-
it = i + 1;
|
|
3061
|
-
let next = tokensToEvaluate[ it ];
|
|
3062
|
-
while( next == ' ' || next == '"' )
|
|
3685
|
+
else if( scopeType.startsWith("enum") )
|
|
3063
3686
|
{
|
|
3064
|
-
|
|
3065
|
-
|
|
3687
|
+
if( !isSymbol( token ) && !this._isNumber( token ) && !this._mustHightlightWord( token, CodeEditor.statements ) )
|
|
3688
|
+
{
|
|
3689
|
+
_pushSymbol({ name: token, kind: "enum_value", scope: scopeName, line: lineNumber });
|
|
3690
|
+
}
|
|
3066
3691
|
}
|
|
3692
|
+
}
|
|
3067
3693
|
|
|
3068
|
-
|
|
3694
|
+
return symbols;
|
|
3695
|
+
}
|
|
3069
3696
|
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3697
|
+
/**
|
|
3698
|
+
* Updates the symbol table for a single line
|
|
3699
|
+
* - Removes old symbols from that line
|
|
3700
|
+
* - Inserts the new symbols
|
|
3701
|
+
*/
|
|
3702
|
+
_updateLineSymbols( lineNumber, newSymbols ) {
|
|
3703
|
+
|
|
3704
|
+
this.code.lineSymbols[ lineNumber ] = this.code.lineSymbols[ lineNumber ] ?? [];
|
|
3705
|
+
const oldSymbols = LX.deepCopy( this.code.lineSymbols[ lineNumber ] );
|
|
3076
3706
|
|
|
3077
|
-
|
|
3078
|
-
|
|
3707
|
+
// Clean old symbols from current line
|
|
3708
|
+
for( let sym of this.code.lineSymbols[ lineNumber ] )
|
|
3709
|
+
{
|
|
3710
|
+
let array = this.code.symbolsTable.get( sym.name );
|
|
3711
|
+
if( !array )
|
|
3079
3712
|
{
|
|
3080
|
-
|
|
3713
|
+
continue;
|
|
3081
3714
|
}
|
|
3082
3715
|
|
|
3083
|
-
|
|
3084
|
-
token: token,
|
|
3085
|
-
prev: prev,
|
|
3086
|
-
prevWithSpaces: tokensToEvaluate[ i - 1 ],
|
|
3087
|
-
next: next,
|
|
3088
|
-
nextWithSpaces: tokensToEvaluate[ i + 1 ],
|
|
3089
|
-
tokenIndex: i,
|
|
3090
|
-
isFirstToken: (i == 0),
|
|
3091
|
-
isLastToken: (i == tokensToEvaluate.length - 1),
|
|
3092
|
-
tokens: tokensToEvaluate
|
|
3093
|
-
} );
|
|
3716
|
+
array = array.filter( s => s.line !== lineNumber );
|
|
3094
3717
|
|
|
3095
|
-
|
|
3096
|
-
if( token === "{" )
|
|
3718
|
+
if( array.length )
|
|
3097
3719
|
{
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
].reverse().filter( v => v.length && v != ' ' );
|
|
3104
|
-
|
|
3105
|
-
let scopeType = contextTokens[ 1 ]; // This is the type of scope (function, class, enum, etc)
|
|
3106
|
-
|
|
3107
|
-
// Special cases
|
|
3108
|
-
if( scopeType === ":" ) // enum type specification
|
|
3109
|
-
{
|
|
3110
|
-
scopeType = contextTokens[ 3 ];
|
|
3111
|
-
}
|
|
3112
|
-
|
|
3113
|
-
if( !this._mustHightlightWord( scopeType, CodeEditor.keywords, lang ) )
|
|
3114
|
-
{
|
|
3115
|
-
// If the scope type is not a keyword, use an empty string
|
|
3116
|
-
scopeType = "";
|
|
3117
|
-
}
|
|
3118
|
-
|
|
3119
|
-
this._scopeStack.push( scopeType );
|
|
3720
|
+
this.code.symbolsTable.set( sym.name, array );
|
|
3721
|
+
}
|
|
3722
|
+
else
|
|
3723
|
+
{
|
|
3724
|
+
this.code.symbolsTable.delete( sym.name );
|
|
3120
3725
|
}
|
|
3121
3726
|
}
|
|
3122
3727
|
|
|
3123
|
-
|
|
3728
|
+
// Add new symbols to table
|
|
3729
|
+
for( let sym of newSymbols )
|
|
3730
|
+
{
|
|
3731
|
+
let arr = this.code.symbolsTable.get( sym.name ) ?? [];
|
|
3732
|
+
arr.push(sym);
|
|
3733
|
+
this.code.symbolsTable.set( sym.name, arr );
|
|
3734
|
+
}
|
|
3735
|
+
|
|
3736
|
+
// Keep lineSymbols in sync
|
|
3737
|
+
this.code.lineSymbols[ lineNumber ] = newSymbols;
|
|
3738
|
+
|
|
3739
|
+
return oldSymbols;
|
|
3124
3740
|
}
|
|
3125
3741
|
|
|
3126
3742
|
_lineHasComment( lineString ) {
|
|
@@ -3185,13 +3801,14 @@ class CodeEditor {
|
|
|
3185
3801
|
let idx = 0;
|
|
3186
3802
|
while( subtokens.value != undefined )
|
|
3187
3803
|
{
|
|
3188
|
-
const _pt = lineString.substring(idx, subtokens.value.index);
|
|
3804
|
+
const _pt = lineString.substring( idx, subtokens.value.index );
|
|
3189
3805
|
if( _pt.length ) pushToken( _pt );
|
|
3190
3806
|
pushToken( subtokens.value[ 0 ] );
|
|
3191
3807
|
idx = subtokens.value.index + subtokens.value[ 0 ].length;
|
|
3192
3808
|
subtokens = iter.next();
|
|
3193
|
-
if(!subtokens.value)
|
|
3194
|
-
|
|
3809
|
+
if( !subtokens.value )
|
|
3810
|
+
{
|
|
3811
|
+
const _at = lineString.substring( idx );
|
|
3195
3812
|
if( _at.length ) pushToken( _at );
|
|
3196
3813
|
}
|
|
3197
3814
|
}
|
|
@@ -3236,18 +3853,49 @@ class CodeEditor {
|
|
|
3236
3853
|
}
|
|
3237
3854
|
else if( this.highlight == 'PHP' )
|
|
3238
3855
|
{
|
|
3239
|
-
|
|
3240
|
-
|
|
3856
|
+
let offset = 0;
|
|
3857
|
+
let dollarIdx = tokens.indexOf( '$' );
|
|
3858
|
+
|
|
3859
|
+
while( dollarIdx > -1 )
|
|
3860
|
+
{
|
|
3861
|
+
const offsetIdx = dollarIdx + offset;
|
|
3862
|
+
|
|
3863
|
+
if( tokens[ offsetIdx + 1 ] === 'this-' )
|
|
3864
|
+
{
|
|
3865
|
+
tokens[ offsetIdx ] = "$this";
|
|
3866
|
+
tokens[ offsetIdx + 1 ] = "-";
|
|
3867
|
+
}
|
|
3868
|
+
else
|
|
3869
|
+
{
|
|
3870
|
+
tokens[ offsetIdx ] += ( tokens[ offsetIdx + 1 ] ?? "" );
|
|
3871
|
+
tokens.splice( offsetIdx + 1, 1 );
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
dollarIdx = tokens.slice( offsetIdx ).indexOf( '$' );
|
|
3875
|
+
offset = offsetIdx;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
else if( this.highlight == 'WGSL' )
|
|
3879
|
+
{
|
|
3880
|
+
let offset = 0;
|
|
3881
|
+
let atIdx = tokens.indexOf( '@' );
|
|
3882
|
+
|
|
3883
|
+
while( atIdx > -1 )
|
|
3241
3884
|
{
|
|
3242
|
-
|
|
3243
|
-
|
|
3885
|
+
const offsetIdx = atIdx + offset;
|
|
3886
|
+
|
|
3887
|
+
tokens[ offsetIdx ] += ( tokens[ offsetIdx + 1 ] ?? "" );
|
|
3888
|
+
tokens.splice( offsetIdx + 1, 1 );
|
|
3889
|
+
|
|
3890
|
+
atIdx = tokens.slice( offsetIdx ).indexOf( '$' );
|
|
3891
|
+
offset = offsetIdx;
|
|
3244
3892
|
}
|
|
3245
3893
|
}
|
|
3246
3894
|
|
|
3247
3895
|
return tokens;
|
|
3248
3896
|
}
|
|
3249
3897
|
|
|
3250
|
-
_mustHightlightWord( token,
|
|
3898
|
+
_mustHightlightWord( token, wordCategory, lang ) {
|
|
3251
3899
|
|
|
3252
3900
|
if( !lang )
|
|
3253
3901
|
{
|
|
@@ -3261,12 +3909,12 @@ class CodeEditor {
|
|
|
3261
3909
|
t = t.toLowerCase();
|
|
3262
3910
|
}
|
|
3263
3911
|
|
|
3264
|
-
return
|
|
3912
|
+
return wordCategory[ this.highlight ] && wordCategory[ this.highlight ].has( t );
|
|
3265
3913
|
}
|
|
3266
3914
|
|
|
3267
3915
|
_getTokenHighlighting( ctx, highlight ) {
|
|
3268
3916
|
|
|
3269
|
-
const rules = [ ...HighlightRules.common, ...( HighlightRules[highlight] || [] ), ...HighlightRules.post_common ];
|
|
3917
|
+
const rules = [ ...HighlightRules.common, ...( HighlightRules[ highlight ] || [] ), ...HighlightRules.post_common ];
|
|
3270
3918
|
|
|
3271
3919
|
for( const rule of rules )
|
|
3272
3920
|
{
|
|
@@ -3287,9 +3935,12 @@ class CodeEditor {
|
|
|
3287
3935
|
|
|
3288
3936
|
let { token, prev, next, tokenIndex, isFirstToken, isLastToken } = ctxData;
|
|
3289
3937
|
|
|
3290
|
-
const lang = CodeEditor.languages[ this.highlight ]
|
|
3291
|
-
|
|
3292
|
-
|
|
3938
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
3939
|
+
const highlight = this.highlight.replace( /\s/g, '' ).replaceAll( "+", "p" ).toLowerCase();
|
|
3940
|
+
const customStringKeys = Object.assign( {}, this.stringKeys );
|
|
3941
|
+
const lineNumber = this._currentLineNumber;
|
|
3942
|
+
const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];
|
|
3943
|
+
const inBlockComment = ( this._buildingBlockComment ?? this._inBlockCommentSection( lineNumber, tokenStartIndex, token.length ) !== undefined )
|
|
3293
3944
|
|
|
3294
3945
|
var usePreviousTokenToCheckString = false;
|
|
3295
3946
|
|
|
@@ -3307,7 +3958,7 @@ class CodeEditor {
|
|
|
3307
3958
|
// Manage strings
|
|
3308
3959
|
this._stringEnded = false;
|
|
3309
3960
|
|
|
3310
|
-
if( usePreviousTokenToCheckString || (
|
|
3961
|
+
if( usePreviousTokenToCheckString || ( !inBlockComment && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) ) )
|
|
3311
3962
|
{
|
|
3312
3963
|
const _checkIfStringEnded = t => {
|
|
3313
3964
|
const idx = Object.values( customStringKeys ).indexOf( t );
|
|
@@ -3333,26 +3984,25 @@ class CodeEditor {
|
|
|
3333
3984
|
|
|
3334
3985
|
// Update context data for next tests
|
|
3335
3986
|
ctxData.discardToken = false;
|
|
3336
|
-
ctxData.inBlockComment =
|
|
3987
|
+
ctxData.inBlockComment = inBlockComment;
|
|
3337
3988
|
ctxData.markdownHeader = this._markdownHeader;
|
|
3338
|
-
ctxData.inString = this._buildingString;
|
|
3989
|
+
ctxData.inString = ( this._buildingString !== undefined );
|
|
3339
3990
|
ctxData.singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
3340
3991
|
ctxData.lang = lang;
|
|
3341
|
-
ctxData.scope = this._scopeStack
|
|
3992
|
+
ctxData.scope = this._scopeStack.at( -1 );
|
|
3993
|
+
|
|
3994
|
+
// Add utils functions for the rules
|
|
3995
|
+
ctxData.isVariableSymbol = ( token ) => this.code.symbolsTable.has( token ) && this.code.symbolsTable.get( token )[ 0 ].kind === "variable";
|
|
3996
|
+
ctxData.isEnumValueSymbol = ( token ) => this.code.symbolsTable.has( token ) && this.code.symbolsTable.get( token )[ 0 ].kind === "enum_value";
|
|
3997
|
+
ctxData.isClassSymbol = ( token ) => this.code.symbolsTable.has( token ) && this.code.symbolsTable.get( token )[ 0 ].kind === "class";
|
|
3998
|
+
ctxData.isStructSymbol = ( token ) => this.code.symbolsTable.has( token ) && this.code.symbolsTable.get( token )[ 0 ].kind === "struct";
|
|
3999
|
+
ctxData.isEnumSymbol = ( token ) => this.code.symbolsTable.has( token ) && this.code.symbolsTable.get( token )[ 0 ].kind === "enum";
|
|
3342
4000
|
|
|
3343
4001
|
// Get highlighting class based on language common and specific rules
|
|
3344
4002
|
let tokenClass = this._getTokenHighlighting( ctxData, highlight );
|
|
3345
4003
|
|
|
3346
|
-
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
3347
|
-
if( ( lang.blockComments ?? true ) && this._buildingBlockComment != undefined
|
|
3348
|
-
&& token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
|
|
3349
|
-
{
|
|
3350
|
-
this._blockCommentCache.push( new LX.vec2( this._buildingBlockComment, this._currentLineNumber ) );
|
|
3351
|
-
delete this._buildingBlockComment;
|
|
3352
|
-
}
|
|
3353
|
-
|
|
3354
4004
|
// We finished constructing a string
|
|
3355
|
-
if( this._buildingString && ( this._stringEnded || isLastToken ) )
|
|
4005
|
+
if( this._buildingString && ( this._stringEnded || isLastToken ) && !inBlockComment )
|
|
3356
4006
|
{
|
|
3357
4007
|
token = this._getCurrentString();
|
|
3358
4008
|
tokenClass = "cm-str";
|
|
@@ -3392,7 +4042,6 @@ class CodeEditor {
|
|
|
3392
4042
|
}
|
|
3393
4043
|
|
|
3394
4044
|
_getCurrentString() {
|
|
3395
|
-
|
|
3396
4045
|
const chars = this._pendingString;
|
|
3397
4046
|
delete this._pendingString;
|
|
3398
4047
|
return chars;
|
|
@@ -3417,17 +4066,32 @@ class CodeEditor {
|
|
|
3417
4066
|
}
|
|
3418
4067
|
}
|
|
3419
4068
|
|
|
3420
|
-
_inBlockCommentSection(
|
|
4069
|
+
_inBlockCommentSection( lineNumber, tokenPosition, tokenLength ) {
|
|
4070
|
+
|
|
4071
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
4072
|
+
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
3421
4073
|
|
|
3422
|
-
for(
|
|
4074
|
+
for( let section of this._blockCommentCache )
|
|
3423
4075
|
{
|
|
3424
|
-
|
|
4076
|
+
const lineRange = section[ 0 ];
|
|
4077
|
+
const posRange = section[ 1 ];
|
|
4078
|
+
|
|
4079
|
+
// Outside the lines range
|
|
4080
|
+
const meetsLineRange = ( lineNumber >= lineRange.x && lineNumber <= lineRange.y );
|
|
4081
|
+
if( !meetsLineRange )
|
|
3425
4082
|
{
|
|
3426
|
-
|
|
4083
|
+
continue;
|
|
3427
4084
|
}
|
|
3428
|
-
}
|
|
3429
4085
|
|
|
3430
|
-
|
|
4086
|
+
if( ( lineNumber != lineRange.x && lineNumber != lineRange.y ) || // Inside the block, not first nor last line
|
|
4087
|
+
( lineNumber == lineRange.x && tokenPosition >= posRange.x &&
|
|
4088
|
+
(( lineNumber == lineRange.y && ( tokenPosition + tokenLength ) <= ( posRange.y + blockCommentsTokens[ 1 ].length ) ) || lineNumber !== lineRange.y) ) ||
|
|
4089
|
+
( lineNumber == lineRange.y && ( ( tokenPosition + tokenLength ) <= ( posRange.y + blockCommentsTokens[ 1 ].length ) ) ) &&
|
|
4090
|
+
(( lineNumber == lineRange.x && tokenPosition >= posRange.x ) || lineNumber !== lineRange.x) )
|
|
4091
|
+
{
|
|
4092
|
+
return section;
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
3431
4095
|
}
|
|
3432
4096
|
|
|
3433
4097
|
_isKeyword( ctxData ) {
|
|
@@ -3521,7 +4185,7 @@ class CodeEditor {
|
|
|
3521
4185
|
this.cursorToPosition( cursor, cursor.selection.toX + 1 );
|
|
3522
4186
|
|
|
3523
4187
|
// Change next key?
|
|
3524
|
-
switch(key)
|
|
4188
|
+
switch( key )
|
|
3525
4189
|
{
|
|
3526
4190
|
case "'":
|
|
3527
4191
|
case "\"":
|
|
@@ -3549,6 +4213,46 @@ class CodeEditor {
|
|
|
3549
4213
|
return true;
|
|
3550
4214
|
}
|
|
3551
4215
|
|
|
4216
|
+
_detectLanguage( text ) {
|
|
4217
|
+
|
|
4218
|
+
const tokenSet = new Set( this._getTokensFromLine( text, true ) );
|
|
4219
|
+
const scores = {};
|
|
4220
|
+
|
|
4221
|
+
for( let [ lang, wordList ] of Object.entries( CodeEditor.keywords ) )
|
|
4222
|
+
{
|
|
4223
|
+
scores[ lang ] = 0;
|
|
4224
|
+
for( let kw of wordList )
|
|
4225
|
+
if( tokenSet.has( kw ) ) scores[ lang ]++;
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
for( let [ lang, wordList ] of Object.entries( CodeEditor.statements ) )
|
|
4229
|
+
{
|
|
4230
|
+
for( let kw of wordList )
|
|
4231
|
+
if( tokenSet.has( kw ) ) scores[ lang ]++;
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4234
|
+
for( let [ lang, wordList ] of Object.entries( CodeEditor.utils ) )
|
|
4235
|
+
{
|
|
4236
|
+
for( let kw of wordList )
|
|
4237
|
+
if( tokenSet.has( kw ) ) scores[ lang ]++;
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4240
|
+
for( let [ lang, wordList ] of Object.entries( CodeEditor.types ) )
|
|
4241
|
+
{
|
|
4242
|
+
for( let kw of wordList )
|
|
4243
|
+
if( tokenSet.has( kw ) ) scores[ lang ]++;
|
|
4244
|
+
}
|
|
4245
|
+
|
|
4246
|
+
for( let [ lang, wordList ] of Object.entries( CodeEditor.builtIn ) )
|
|
4247
|
+
{
|
|
4248
|
+
for( let kw of wordList )
|
|
4249
|
+
if( tokenSet.has( kw ) ) scores[ lang ]++;
|
|
4250
|
+
}
|
|
4251
|
+
|
|
4252
|
+
const sorted = Object.entries( scores ).sort( ( a, b ) => b[ 1 ] - a[ 1 ] );
|
|
4253
|
+
return sorted[0][1] > 0 ? sorted[0][0] : undefined;
|
|
4254
|
+
}
|
|
4255
|
+
|
|
3552
4256
|
lineUp( cursor, resetLeft ) {
|
|
3553
4257
|
|
|
3554
4258
|
if( this.code.lines[ cursor.line - 1 ] == undefined )
|
|
@@ -3664,7 +4368,7 @@ class CodeEditor {
|
|
|
3664
4368
|
// Use main cursor
|
|
3665
4369
|
this._removeSecondaryCursors();
|
|
3666
4370
|
|
|
3667
|
-
var cursor = this.
|
|
4371
|
+
var cursor = this.getCurrentCursor();
|
|
3668
4372
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, cursor );
|
|
3669
4373
|
|
|
3670
4374
|
this.startSelection( cursor );
|
|
@@ -3754,11 +4458,8 @@ class CodeEditor {
|
|
|
3754
4458
|
}
|
|
3755
4459
|
|
|
3756
4460
|
const currentScrollTop = this.getScrollTop();
|
|
3757
|
-
const
|
|
3758
|
-
const
|
|
3759
|
-
const scrollerHeight = this.codeScroller.offsetHeight;
|
|
3760
|
-
|
|
3761
|
-
var lastLine = ( ( scrollerHeight - tabsHeight - statusPanelHeight + currentScrollTop ) / this.lineHeight )|0;
|
|
4461
|
+
const scrollerHeight = this.codeScroller.offsetHeight - this._fullVerticalOffset;
|
|
4462
|
+
const lastLine = ( ( scrollerHeight + currentScrollTop ) / this.lineHeight )|0;
|
|
3762
4463
|
if( cursor.line >= lastLine )
|
|
3763
4464
|
{
|
|
3764
4465
|
this.setScrollTop( currentScrollTop + this.lineHeight );
|
|
@@ -3814,6 +4515,16 @@ class CodeEditor {
|
|
|
3814
4515
|
return cursors;
|
|
3815
4516
|
}
|
|
3816
4517
|
|
|
4518
|
+
getCurrentCursor( removeOthers ) {
|
|
4519
|
+
|
|
4520
|
+
if( removeOthers )
|
|
4521
|
+
{
|
|
4522
|
+
this._removeSecondaryCursors();
|
|
4523
|
+
}
|
|
4524
|
+
|
|
4525
|
+
return this.cursors.children[ 0 ];
|
|
4526
|
+
}
|
|
4527
|
+
|
|
3817
4528
|
relocateCursors() {
|
|
3818
4529
|
|
|
3819
4530
|
for( let cursor of this.cursors.children )
|
|
@@ -3863,7 +4574,7 @@ class CodeEditor {
|
|
|
3863
4574
|
|
|
3864
4575
|
resetCursorPos( flag, cursor ) {
|
|
3865
4576
|
|
|
3866
|
-
cursor = cursor ?? this.
|
|
4577
|
+
cursor = cursor ?? this.getCurrentCursor();
|
|
3867
4578
|
|
|
3868
4579
|
if( flag & CodeEditor.CURSOR_LEFT )
|
|
3869
4580
|
{
|
|
@@ -3880,6 +4591,71 @@ class CodeEditor {
|
|
|
3880
4591
|
}
|
|
3881
4592
|
}
|
|
3882
4593
|
|
|
4594
|
+
_addCursor( line = 0, position = 0, force, isMain = false ) {
|
|
4595
|
+
|
|
4596
|
+
// If cursor in that position exists, remove it instead..
|
|
4597
|
+
const exists = Array.from( this.cursors.children ).find( v => v.position == position && v.line == line );
|
|
4598
|
+
if( exists && !force )
|
|
4599
|
+
{
|
|
4600
|
+
if( !exists.isMain )
|
|
4601
|
+
exists.remove();
|
|
4602
|
+
|
|
4603
|
+
return;
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4606
|
+
let cursor = document.createElement( 'div' );
|
|
4607
|
+
cursor.name = "cursor" + this.cursors.childElementCount;
|
|
4608
|
+
cursor.className = "cursor";
|
|
4609
|
+
cursor.innerHTML = " ";
|
|
4610
|
+
cursor.isMain = isMain;
|
|
4611
|
+
cursor._left = position * this.charWidth;
|
|
4612
|
+
cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
|
|
4613
|
+
cursor._top = line * this.lineHeight;
|
|
4614
|
+
cursor.style.top = cursor._top + "px";
|
|
4615
|
+
cursor._position = position;
|
|
4616
|
+
cursor._line = line;
|
|
4617
|
+
cursor.print = (function() { console.log( this._line, this._position ) }).bind( cursor );
|
|
4618
|
+
cursor.isLast = (function() { return this.cursors.lastChild == cursor; }).bind( this );
|
|
4619
|
+
|
|
4620
|
+
Object.defineProperty( cursor, 'line', {
|
|
4621
|
+
get: (v) => { return cursor._line },
|
|
4622
|
+
set: (v) => {
|
|
4623
|
+
cursor._line = v;
|
|
4624
|
+
if( cursor.isMain ) this._setActiveLine( v );
|
|
4625
|
+
}
|
|
4626
|
+
} );
|
|
4627
|
+
|
|
4628
|
+
Object.defineProperty( cursor, 'position', {
|
|
4629
|
+
get: (v) => { return cursor._position },
|
|
4630
|
+
set: (v) => {
|
|
4631
|
+
cursor._position = v;
|
|
4632
|
+
if( cursor.isMain )
|
|
4633
|
+
{
|
|
4634
|
+
const activeLine = this.state.activeLine;
|
|
4635
|
+
this._updateDataInfoPanel( "@cursor-data", `Ln ${ activeLine + 1 }, Col ${ v + 1 }` );
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
} );
|
|
4639
|
+
|
|
4640
|
+
this.cursors.appendChild( cursor );
|
|
4641
|
+
|
|
4642
|
+
return cursor;
|
|
4643
|
+
}
|
|
4644
|
+
|
|
4645
|
+
_removeSecondaryCursors() {
|
|
4646
|
+
|
|
4647
|
+
while( this.cursors.childElementCount > 1 )
|
|
4648
|
+
this.cursors.lastChild.remove();
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
_logCursors() {
|
|
4652
|
+
|
|
4653
|
+
for( let cursor of this.cursors.children )
|
|
4654
|
+
{
|
|
4655
|
+
cursor.print();
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
|
|
3883
4659
|
_addSpaceTabs( cursor, n ) {
|
|
3884
4660
|
|
|
3885
4661
|
for( var i = 0; i < n; ++i )
|
|
@@ -3890,9 +4666,10 @@ class CodeEditor {
|
|
|
3890
4666
|
|
|
3891
4667
|
_addSpaces( n ) {
|
|
3892
4668
|
|
|
3893
|
-
for( var i = 0; i < n; ++i )
|
|
4669
|
+
for( var i = 0; i < n; ++i )
|
|
4670
|
+
{
|
|
3894
4671
|
this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
|
|
3895
|
-
|
|
4672
|
+
skipUndo: true,
|
|
3896
4673
|
key: ' ',
|
|
3897
4674
|
targetCursor: this._lastProcessedCursorIndex
|
|
3898
4675
|
}}));
|
|
@@ -3913,7 +4690,8 @@ class CodeEditor {
|
|
|
3913
4690
|
}
|
|
3914
4691
|
|
|
3915
4692
|
// Only tabs/spaces in the line...
|
|
3916
|
-
if( lineStart == -1 )
|
|
4693
|
+
if( lineStart == -1 )
|
|
4694
|
+
{
|
|
3917
4695
|
lineStart = this.code.lines[ lidx ].length;
|
|
3918
4696
|
}
|
|
3919
4697
|
|
|
@@ -3964,24 +4742,28 @@ class CodeEditor {
|
|
|
3964
4742
|
}, 20 );
|
|
3965
4743
|
}
|
|
3966
4744
|
|
|
3967
|
-
resize( pMaxLength, onResize ) {
|
|
4745
|
+
resize( flag = CodeEditor.RESIZE_SCROLLBAR_H_V, pMaxLength, onResize ) {
|
|
3968
4746
|
|
|
3969
4747
|
setTimeout( () => {
|
|
3970
4748
|
|
|
3971
|
-
|
|
3972
|
-
const maxLineLength = pMaxLength ?? this.getMaxLineLength();
|
|
3973
|
-
const scrollWidth = maxLineLength * this.charWidth + CodeEditor.LINE_GUTTER_WIDTH;
|
|
3974
|
-
|
|
3975
|
-
const tabsHeight = this.tabs.root.getBoundingClientRect().height;
|
|
3976
|
-
const statusPanelHeight = this.skipInfo ? 0 : this.statusPanel.root.getBoundingClientRect().height;
|
|
3977
|
-
const scrollHeight = this.code.lines.length * this.lineHeight;
|
|
4749
|
+
let scrollWidth, scrollHeight;
|
|
3978
4750
|
|
|
3979
|
-
|
|
4751
|
+
if( flag & CodeEditor.RESIZE_SCROLLBAR_H )
|
|
4752
|
+
{
|
|
4753
|
+
// Update max viewport
|
|
4754
|
+
const maxLineLength = pMaxLength ?? this.getMaxLineLength();
|
|
4755
|
+
this._lastMaxLineLength = maxLineLength;
|
|
4756
|
+
scrollWidth = maxLineLength * this.charWidth + CodeEditor.LINE_GUTTER_WIDTH;
|
|
4757
|
+
this.codeSizer.style.minWidth = scrollWidth + "px";
|
|
4758
|
+
}
|
|
3980
4759
|
|
|
3981
|
-
|
|
3982
|
-
|
|
4760
|
+
if( flag & CodeEditor.RESIZE_SCROLLBAR_V )
|
|
4761
|
+
{
|
|
4762
|
+
scrollHeight = this.code.lines.length * this.lineHeight;
|
|
4763
|
+
this.codeSizer.style.minHeight = scrollHeight + "px";
|
|
4764
|
+
}
|
|
3983
4765
|
|
|
3984
|
-
this.resizeScrollBars();
|
|
4766
|
+
this.resizeScrollBars( flag );
|
|
3985
4767
|
|
|
3986
4768
|
if( onResize )
|
|
3987
4769
|
{
|
|
@@ -3991,37 +4773,52 @@ class CodeEditor {
|
|
|
3991
4773
|
}, 10 );
|
|
3992
4774
|
}
|
|
3993
4775
|
|
|
3994
|
-
|
|
4776
|
+
resizeIfNecessary( cursor, force ) {
|
|
3995
4777
|
|
|
3996
|
-
const
|
|
3997
|
-
|
|
3998
|
-
if(
|
|
3999
|
-
{
|
|
4000
|
-
this.codeScroller.classList.remove( 'with-vscrollbar' );
|
|
4001
|
-
this.vScrollbar.root.classList.add( 'scrollbar-unused' );
|
|
4002
|
-
}
|
|
4003
|
-
else
|
|
4778
|
+
const maxLineLength = this.getMaxLineLength();
|
|
4779
|
+
const numViewportChars = Math.floor( ( this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH ) / this.charWidth );
|
|
4780
|
+
if( force || ( maxLineLength >= numViewportChars && maxLineLength != this._lastMaxLineLength ) )
|
|
4004
4781
|
{
|
|
4005
|
-
this.
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4782
|
+
this.resize( CodeEditor.RESIZE_SCROLLBAR_H, maxLineLength, () => {
|
|
4783
|
+
if( cursor.position > numViewportChars )
|
|
4784
|
+
{
|
|
4785
|
+
this.setScrollLeft( cursor.position * this.charWidth );
|
|
4786
|
+
}
|
|
4787
|
+
} );
|
|
4009
4788
|
}
|
|
4789
|
+
}
|
|
4010
4790
|
|
|
4011
|
-
|
|
4012
|
-
const maxLineLength = this._lastMaxLineLength;
|
|
4791
|
+
resizeScrollBars( flag = CodeEditor.RESIZE_SCROLLBAR_H_V ) {
|
|
4013
4792
|
|
|
4014
|
-
if(
|
|
4793
|
+
if( flag & CodeEditor.RESIZE_SCROLLBAR_V )
|
|
4015
4794
|
{
|
|
4016
|
-
this.codeScroller.
|
|
4017
|
-
this.
|
|
4795
|
+
const totalLinesInViewport = (( this.codeScroller.offsetHeight ) / this.lineHeight)|0;
|
|
4796
|
+
const needsVerticalScrollbar = ( this.code.lines.length >= totalLinesInViewport );
|
|
4797
|
+
if( needsVerticalScrollbar )
|
|
4798
|
+
{
|
|
4799
|
+
this.vScrollbar.thumb.size = ( totalLinesInViewport / this.code.lines.length );
|
|
4800
|
+
this.vScrollbar.thumb.style.height = (this.vScrollbar.thumb.size * 100.0) + "%";
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4803
|
+
this.vScrollbar.root.classList.toggle( 'hidden', !needsVerticalScrollbar );
|
|
4804
|
+
this.hScrollbar.root.style.width = `calc(100% - ${ 48 + ( needsVerticalScrollbar ? ScrollBar.SCROLLBAR_VERTICAL_WIDTH : 0 ) }px)`; // 48 is the line gutter
|
|
4805
|
+
this.codeArea.root.style.width = `calc(100% - ${ needsVerticalScrollbar ? ScrollBar.SCROLLBAR_VERTICAL_WIDTH : 0 }px)`;
|
|
4018
4806
|
}
|
|
4019
|
-
|
|
4807
|
+
|
|
4808
|
+
if( flag & CodeEditor.RESIZE_SCROLLBAR_H )
|
|
4020
4809
|
{
|
|
4021
|
-
this.codeScroller.
|
|
4022
|
-
this.
|
|
4023
|
-
|
|
4024
|
-
|
|
4810
|
+
const numViewportChars = Math.floor( ( this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH ) / this.charWidth );
|
|
4811
|
+
const maxLineLength = this._lastMaxLineLength;
|
|
4812
|
+
const needsHorizontalScrollbar = maxLineLength >= numViewportChars;
|
|
4813
|
+
|
|
4814
|
+
if( needsHorizontalScrollbar )
|
|
4815
|
+
{
|
|
4816
|
+
this.hScrollbar.thumb.size = ( numViewportChars / maxLineLength );
|
|
4817
|
+
this.hScrollbar.thumb.style.width = ( this.hScrollbar.thumb.size * 100.0 ) + "%";
|
|
4818
|
+
}
|
|
4819
|
+
|
|
4820
|
+
this.hScrollbar.root.classList.toggle( 'hidden', !needsHorizontalScrollbar );
|
|
4821
|
+
this.codeArea.root.style.height = `calc(100% - ${ this._fullVerticalOffset + ( needsHorizontalScrollbar ? ScrollBar.SCROLLBAR_HORIZONTAL_HEIGHT : 0 ) }px)`;
|
|
4025
4822
|
}
|
|
4026
4823
|
}
|
|
4027
4824
|
|
|
@@ -4095,7 +4892,6 @@ class CodeEditor {
|
|
|
4095
4892
|
}
|
|
4096
4893
|
|
|
4097
4894
|
getCharAtPos( cursor, offset = 0 ) {
|
|
4098
|
-
|
|
4099
4895
|
return this.code.lines[ cursor.line ][ cursor.position + offset ];
|
|
4100
4896
|
}
|
|
4101
4897
|
|
|
@@ -4164,7 +4960,6 @@ class CodeEditor {
|
|
|
4164
4960
|
}
|
|
4165
4961
|
|
|
4166
4962
|
measureString( str ) {
|
|
4167
|
-
|
|
4168
4963
|
return str.length * this.charWidth;
|
|
4169
4964
|
}
|
|
4170
4965
|
|
|
@@ -4217,53 +5012,95 @@ class CodeEditor {
|
|
|
4217
5012
|
showAutoCompleteBox( key, cursor ) {
|
|
4218
5013
|
|
|
4219
5014
|
if( !cursor.isMain )
|
|
5015
|
+
{
|
|
4220
5016
|
return;
|
|
5017
|
+
}
|
|
4221
5018
|
|
|
4222
|
-
const [word, start, end] = this.getWordAtPos( cursor, -1 );
|
|
4223
|
-
if( key == ' ' || !word.length )
|
|
5019
|
+
const [ word, start, end ] = this.getWordAtPos( cursor, -1 );
|
|
5020
|
+
if( key == ' ' || !word.length )
|
|
5021
|
+
{
|
|
4224
5022
|
this.hideAutoCompleteBox();
|
|
4225
5023
|
return;
|
|
4226
5024
|
}
|
|
4227
5025
|
|
|
4228
5026
|
this.autocomplete.innerHTML = ""; // Clear all suggestions
|
|
4229
5027
|
|
|
4230
|
-
let suggestions = [];
|
|
4231
|
-
|
|
4232
5028
|
// Add language special keys...
|
|
4233
|
-
suggestions =
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
5029
|
+
let suggestions = [
|
|
5030
|
+
...Array.from( CodeEditor.keywords[ this.highlight ] ?? [] ),
|
|
5031
|
+
...Array.from( CodeEditor.builtIn[ this.highlight ] ?? [] ),
|
|
5032
|
+
...Array.from( CodeEditor.statements[ this.highlight ] ?? [] ),
|
|
5033
|
+
...Array.from( CodeEditor.types[ this.highlight ] ?? [] ),
|
|
5034
|
+
...Array.from( CodeEditor.utils[ this.highlight ] ?? [] )
|
|
5035
|
+
];
|
|
4240
5036
|
|
|
4241
|
-
|
|
4242
|
-
|
|
5037
|
+
const scopeStack = [ ...this.code.lineScopes[ cursor.line ] ];
|
|
5038
|
+
const scope = scopeStack.at( -1 );
|
|
5039
|
+
if( scope.type.startsWith( "enum" ) )
|
|
5040
|
+
{
|
|
5041
|
+
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 ] );
|
|
5042
|
+
suggestions = suggestions.concat( enumValues.slice( 0, -1 ) );
|
|
5043
|
+
}
|
|
5044
|
+
else
|
|
5045
|
+
{
|
|
5046
|
+
const otherValues = Array.from( this.code.symbolsTable ).map( s => s[ 0 ] );
|
|
5047
|
+
suggestions = suggestions.concat( otherValues.slice( 0, -1 ) );
|
|
5048
|
+
}
|
|
4243
5049
|
|
|
4244
5050
|
// Remove 1/2 char words and duplicates...
|
|
4245
|
-
suggestions =
|
|
5051
|
+
suggestions = Array.from( new Set( suggestions )).filter( s => s.length > 2 && s.toLowerCase().includes( word.toLowerCase() ) );
|
|
4246
5052
|
|
|
4247
5053
|
// Order...
|
|
4248
|
-
|
|
5054
|
+
|
|
5055
|
+
function scoreSuggestion( s, prefix ) {
|
|
5056
|
+
if( s.startsWith( prefix ) ) return 0; // best option
|
|
5057
|
+
if( s.includes( prefix )) return 1;
|
|
5058
|
+
return 2; // worst
|
|
5059
|
+
}
|
|
5060
|
+
|
|
5061
|
+
suggestions = suggestions.sort( ( a, b ) => ( scoreSuggestion( a, word ) - scoreSuggestion( b, word ) ) || a.localeCompare( b ) );
|
|
4249
5062
|
|
|
4250
5063
|
for( let s of suggestions )
|
|
4251
5064
|
{
|
|
4252
|
-
|
|
4253
|
-
continue;
|
|
4254
|
-
|
|
4255
|
-
var pre = document.createElement( 'pre' );
|
|
5065
|
+
const pre = document.createElement( 'pre' );
|
|
4256
5066
|
this.autocomplete.appendChild( pre );
|
|
4257
5067
|
|
|
4258
|
-
|
|
5068
|
+
const symbol = this.code.symbolsTable.get( s );
|
|
4259
5069
|
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
else if( this._mustHightlightWord( s, CodeEditor.types ) )
|
|
4263
|
-
icon = "Code";
|
|
5070
|
+
let iconName = "CaseLower";
|
|
5071
|
+
let iconClass = "foo";
|
|
4264
5072
|
|
|
4265
|
-
|
|
5073
|
+
if( symbol )
|
|
5074
|
+
{
|
|
5075
|
+
switch( symbol[ 0 ].kind ) // Get first occurrence
|
|
5076
|
+
{
|
|
5077
|
+
case "variable":
|
|
5078
|
+
iconName = "Cuboid";
|
|
5079
|
+
iconClass = "lightblue";
|
|
5080
|
+
break;
|
|
5081
|
+
case "method":
|
|
5082
|
+
iconName = "Box";
|
|
5083
|
+
iconClass = "heliotrope";
|
|
5084
|
+
break;
|
|
5085
|
+
case "class":
|
|
5086
|
+
iconName = "CircleNodes";
|
|
5087
|
+
iconClass = "orange";
|
|
5088
|
+
break;
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
else
|
|
5092
|
+
{
|
|
5093
|
+
if( this._mustHightlightWord( s, CodeEditor.utils ) )
|
|
5094
|
+
iconName = "ToolCase";
|
|
5095
|
+
else if( this._mustHightlightWord( s, CodeEditor.types ) )
|
|
5096
|
+
{
|
|
5097
|
+
iconName = "Type";
|
|
5098
|
+
iconClass = "lightblue";
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
4266
5101
|
|
|
5102
|
+
pre.appendChild( LX.makeIcon( iconName, { iconClass: "mr-1", svgClass: "sm " + iconClass } ) );
|
|
5103
|
+
s
|
|
4267
5104
|
pre.addEventListener( 'click', () => {
|
|
4268
5105
|
this.autoCompleteWord( s );
|
|
4269
5106
|
} );
|
|
@@ -4292,11 +5129,11 @@ class CodeEditor {
|
|
|
4292
5129
|
}
|
|
4293
5130
|
|
|
4294
5131
|
// Select always first option
|
|
4295
|
-
this.autocomplete.firstChild.classList.add('selected');
|
|
5132
|
+
this.autocomplete.firstChild.classList.add( 'selected' );
|
|
4296
5133
|
|
|
4297
5134
|
// Show box
|
|
4298
|
-
this.autocomplete.classList.toggle('show', true);
|
|
4299
|
-
this.autocomplete.classList.toggle('no-scrollbar', !(this.autocomplete.scrollHeight > this.autocomplete.offsetHeight));
|
|
5135
|
+
this.autocomplete.classList.toggle( 'show', true );
|
|
5136
|
+
this.autocomplete.classList.toggle( 'no-scrollbar', !( this.autocomplete.scrollHeight > this.autocomplete.offsetHeight ) );
|
|
4300
5137
|
this.autocomplete.style.left = (cursor._left + CodeEditor.LINE_GUTTER_WIDTH - this.getScrollLeft()) + "px";
|
|
4301
5138
|
this.autocomplete.style.top = (cursor._top + 28 + this.lineHeight - this.getScrollTop()) + "px";
|
|
4302
5139
|
|
|
@@ -4408,7 +5245,7 @@ class CodeEditor {
|
|
|
4408
5245
|
}
|
|
4409
5246
|
else
|
|
4410
5247
|
{
|
|
4411
|
-
const cursor = this.
|
|
5248
|
+
const cursor = this.getCurrentCursor();
|
|
4412
5249
|
|
|
4413
5250
|
if( cursor.selection )
|
|
4414
5251
|
{
|
|
@@ -4452,7 +5289,7 @@ class CodeEditor {
|
|
|
4452
5289
|
return;
|
|
4453
5290
|
}
|
|
4454
5291
|
|
|
4455
|
-
let cursor = this.
|
|
5292
|
+
let cursor = this.getCurrentCursor();
|
|
4456
5293
|
let cursorData = new LX.vec2( cursor.position, cursor.line );
|
|
4457
5294
|
let line = null;
|
|
4458
5295
|
let char = -1;
|
|
@@ -4599,7 +5436,7 @@ class CodeEditor {
|
|
|
4599
5436
|
this.codeScroller.scrollTo( 0, Math.max( line - 15 ) * this.lineHeight );
|
|
4600
5437
|
|
|
4601
5438
|
// Select line ?
|
|
4602
|
-
var cursor = this.
|
|
5439
|
+
var cursor = this.getCurrentCursor( true );
|
|
4603
5440
|
this.cursorToLine( cursor, line - 1, true );
|
|
4604
5441
|
}
|
|
4605
5442
|
|
|
@@ -4654,7 +5491,7 @@ class CodeEditor {
|
|
|
4654
5491
|
|
|
4655
5492
|
number = number ?? this.state.activeLine;
|
|
4656
5493
|
|
|
4657
|
-
const cursor = this.
|
|
5494
|
+
const cursor = this.getCurrentCursor();
|
|
4658
5495
|
this._updateDataInfoPanel( "@cursor-data", `Ln ${ number + 1 }, Col ${ cursor.position + 1 }` );
|
|
4659
5496
|
|
|
4660
5497
|
const oldLocal = this.toLocalLine( this.state.activeLine );
|
|
@@ -4731,23 +5568,33 @@ class CodeEditor {
|
|
|
4731
5568
|
}
|
|
4732
5569
|
|
|
4733
5570
|
CodeEditor.languages = {
|
|
4734
|
-
'Plain Text': { ext:
|
|
4735
|
-
'JavaScript': { ext:
|
|
4736
|
-
'TypeScript': { ext:
|
|
4737
|
-
'C': { ext: [ 'c', 'h' ], usePreprocessor: true },
|
|
4738
|
-
'C++': { ext: [
|
|
4739
|
-
'CSS': { ext:
|
|
4740
|
-
'CMake': { ext:
|
|
4741
|
-
'GLSL': { ext:
|
|
4742
|
-
'WGSL': { ext:
|
|
4743
|
-
'JSON': { ext:
|
|
4744
|
-
'XML': { ext:
|
|
4745
|
-
'Rust': { ext:
|
|
4746
|
-
'Python': { ext:
|
|
4747
|
-
'HTML': { ext:
|
|
4748
|
-
'Batch': { ext:
|
|
4749
|
-
'Markdown': { ext:
|
|
4750
|
-
'PHP': { ext:
|
|
5571
|
+
'Plain Text': { ext: "txt", blockComments: false, singleLineComments: false, numbers: false, icon: "AlignLeft gray" },
|
|
5572
|
+
'JavaScript': { ext: "js", icon: "Js goldenrod" },
|
|
5573
|
+
'TypeScript': { ext: "ts", icon: "Ts pipelineblue" },
|
|
5574
|
+
'C': { ext: [ 'c', 'h' ], usePreprocessor: true, icon: { 'c': "C pictonblue", 'h': "C heliotrope" } },
|
|
5575
|
+
'C++': { ext: [ "cpp", "hpp" ], usePreprocessor: true, icon: { 'cpp': "CPlusPlus pictonblue", 'hpp': "CPlusPlus heliotrope" } },
|
|
5576
|
+
'CSS': { ext: "css", icon: "Hash dodgerblue" },
|
|
5577
|
+
'CMake': { ext: "cmake", singleLineCommentToken: '#', blockComments: false, ignoreCase: true },
|
|
5578
|
+
'GLSL': { ext: "glsl", usePreprocessor: true },
|
|
5579
|
+
'WGSL': { ext: "wgsl", usePreprocessor: true },
|
|
5580
|
+
'JSON': { ext: "json", blockComments: false, singleLineComments: false, icon: "Braces fg-primary" },
|
|
5581
|
+
'XML': { ext: "xml", tags: true, icon: "Rss orange" },
|
|
5582
|
+
'Rust': { ext: "rs", icon: "Rust fg-primary" },
|
|
5583
|
+
'Python': { ext: "py", singleLineCommentToken: '#', icon: "Python munsellblue" },
|
|
5584
|
+
'HTML': { ext: "html", tags: true, singleLineComments: false, blockCommentsTokens: [ '<!--', '-->' ], numbers: false, icon: "Code orange" },
|
|
5585
|
+
'Batch': { ext: "bat", blockComments: false, singleLineCommentToken: '::', ignoreCase: true, icon: "Windows lightblue" },
|
|
5586
|
+
'Markdown': { ext: "md", blockComments: false, singleLineCommentToken: '::', tags: true, numbers: false, icon: "Markdown fg-primary" },
|
|
5587
|
+
'PHP': { ext: "php", icon: "Php blueviolet" },
|
|
5588
|
+
};
|
|
5589
|
+
|
|
5590
|
+
CodeEditor.nativeTypes = {
|
|
5591
|
+
'C++': ['int', 'float', 'double', 'bool', 'long', 'short', 'char', 'wchar_t', 'void'],
|
|
5592
|
+
'WGSL': ['bool', 'u32', 'i32', 'f16', 'f32', 'vec2', 'vec3', 'vec4', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'vec2u', 'vec3u', 'vec4u', 'ptr', 'sampler']
|
|
5593
|
+
};
|
|
5594
|
+
|
|
5595
|
+
CodeEditor.declarationKeywords = {
|
|
5596
|
+
'JavaScript': ['var', 'let', 'const', 'this', 'static', 'class'],
|
|
5597
|
+
'C++': [...CodeEditor.nativeTypes["C++"], 'const', 'auto', 'class', 'struct', 'namespace', 'enum', 'extern']
|
|
4751
5598
|
};
|
|
4752
5599
|
|
|
4753
5600
|
CodeEditor.keywords = {
|
|
@@ -4757,26 +5604,26 @@ CodeEditor.keywords = {
|
|
|
4757
5604
|
'enum', 'type'],
|
|
4758
5605
|
'C': ['int', 'float', 'double', 'long', 'short', 'char', 'const', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'signed', 'volatile', 'unsigned', 'static', 'extern', 'enum', 'register',
|
|
4759
5606
|
'union'],
|
|
4760
|
-
'C++': [
|
|
5607
|
+
'C++': [...CodeEditor.nativeTypes["C++"], 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'true', 'false', 'auto', 'class', 'struct', 'typedef', 'nullptr',
|
|
4761
5608
|
'NULL', 'signed', 'unsigned', 'namespace', 'enum', 'extern', 'union', 'sizeof', 'static', 'private', 'public'],
|
|
4762
5609
|
'CMake': ['cmake_minimum_required', 'set', 'not', 'if', 'endif', 'exists', 'string', 'strequal', 'add_definitions', 'macro', 'endmacro', 'file', 'list', 'source_group', 'add_executable',
|
|
4763
5610
|
'target_include_directories', 'set_target_properties', 'set_property', 'add_compile_options', 'add_link_options', 'include_directories', 'add_library', 'target_link_libraries',
|
|
4764
5611
|
'target_link_options', 'add_subdirectory', 'add_compile_definitions', 'project', 'cache'],
|
|
4765
5612
|
'JSON': ['true', 'false'],
|
|
4766
5613
|
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
4767
|
-
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
'
|
|
4771
|
-
'
|
|
5614
|
+
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.', 'table', 'tr', 'td', 'th', 'label', 'video', 'img', 'code', 'button', 'select', 'option', 'svg', 'media', 'all',
|
|
5615
|
+
'i', 'a', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'last-child', 'tbody', 'pre', 'monospace', 'font-face'],
|
|
5616
|
+
'WGSL': [...CodeEditor.nativeTypes["WGSL"], 'var', 'let', 'true', 'false', 'fn', 'atomic', 'struct', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube',
|
|
5617
|
+
'texture_depth_cube_array', 'texture_depth_multisampled_2d', 'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array',
|
|
5618
|
+
'texture_storage_1d', 'texture_storage_2d', 'texture_storage_2d_array', 'texture_storage_3d'],
|
|
4772
5619
|
'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true',
|
|
4773
5620
|
'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
|
|
4774
5621
|
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
|
|
4775
|
-
'Batch': ['set', '
|
|
4776
|
-
'DRIVERQUERY', 'print', 'PRINT'],
|
|
5622
|
+
'Batch': ['set', 'echo', 'off', 'del', 'defined', 'setlocal', 'enabledelayedexpansion', 'driverquery', 'print'],
|
|
4777
5623
|
'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
4778
5624
|
'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
4779
|
-
'PHP': ['const', 'function', 'array', 'new', 'int', 'string', '$this', 'public', 'null', 'private', 'protected', 'implements', 'class', 'use', 'namespace', 'abstract', 'clone', 'final'
|
|
5625
|
+
'PHP': ['const', 'function', 'array', 'new', 'int', 'string', '$this', 'public', 'null', 'private', 'protected', 'implements', 'class', 'use', 'namespace', 'abstract', 'clone', 'final',
|
|
5626
|
+
'enum'],
|
|
4780
5627
|
};
|
|
4781
5628
|
|
|
4782
5629
|
CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
|
|
@@ -4787,7 +5634,14 @@ CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to
|
|
|
4787
5634
|
'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
|
|
4788
5635
|
'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
|
|
4789
5636
|
'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr',
|
|
4790
|
-
'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
|
|
5637
|
+
'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'],
|
|
5638
|
+
'CSS': [ ...Object.keys( document.body.style ).map( LX.toKebabCase ), 'block', 'inline', 'inline-block', 'flex', 'grid', 'none', 'inherit', 'initial', 'unset', 'revert', 'sticky',
|
|
5639
|
+
'relative', 'absolute', 'fixed', 'static', 'auto', 'visible', 'hidden', 'scroll', 'clip', 'ellipsis', 'nowrap', 'wrap', 'break-word', 'solid', 'dashed', 'dotted', 'double',
|
|
5640
|
+
'groove', 'ridge', 'inset', 'outset', 'left', 'right', 'center', 'top', 'bottom', 'start', 'end', 'justify', 'stretch', 'space-between', 'space-around', 'space-evenly',
|
|
5641
|
+
'baseline', 'middle', 'normal', 'bold', 'lighter', 'bolder', 'italic', 'blur', 'uppercase', 'lowercase', 'capitalize', 'transparent', 'currentColor', 'pointer', 'default',
|
|
5642
|
+
'move', 'grab', 'grabbing', 'not-allowed', 'none', 'cover', 'contain', 'repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'round', 'space', 'linear-gradient', 'radial-gradient',
|
|
5643
|
+
'conic-gradient', 'url', 'calc', 'min', 'max', 'clamp', 'red', 'blue', 'green', 'black', 'white', 'gray', 'silver', 'yellow', 'orange', 'purple', 'pink', 'cyan', 'magenta',
|
|
5644
|
+
'lime', 'teal', 'navy', 'transparent', 'currentcolor', 'inherit', 'initial', 'unset', 'revert', 'none', 'auto', 'fit-content', 'min-content', 'max-content']
|
|
4791
5645
|
};
|
|
4792
5646
|
|
|
4793
5647
|
CodeEditor.types = {
|
|
@@ -4808,12 +5662,13 @@ CodeEditor.builtIn = {
|
|
|
4808
5662
|
'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
|
|
4809
5663
|
'CSS': ['*', '!important'],
|
|
4810
5664
|
'C++': ['vector', 'list', 'map'],
|
|
5665
|
+
'WGSL': ['@vertex', '@fragment'],
|
|
4811
5666
|
'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
|
|
4812
5667
|
'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
|
|
4813
5668
|
'PHP': ['echo', 'print'],
|
|
4814
5669
|
};
|
|
4815
5670
|
|
|
4816
|
-
CodeEditor.
|
|
5671
|
+
CodeEditor.statements = {
|
|
4817
5672
|
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
|
|
4818
5673
|
'TypeScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
|
|
4819
5674
|
'CSS': ['@', 'import'],
|
|
@@ -4831,6 +5686,7 @@ CodeEditor.statementsAndDeclarations = {
|
|
|
4831
5686
|
|
|
4832
5687
|
CodeEditor.symbols = {
|
|
4833
5688
|
'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
5689
|
+
'TypeScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
4834
5690
|
'C': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '*', '-', '+'],
|
|
4835
5691
|
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
|
|
4836
5692
|
'CMake': ['{', '}'],
|
|
@@ -4843,19 +5699,19 @@ CodeEditor.symbols = {
|
|
|
4843
5699
|
'Batch': ['[', ']', '(', ')', '%'],
|
|
4844
5700
|
'HTML': ['<', '>', '/'],
|
|
4845
5701
|
'XML': ['<', '>', '/'],
|
|
4846
|
-
'PHP': ['{', '}', '(', ')'],
|
|
5702
|
+
'PHP': ['[', ']', '{', '}', '(', ')'],
|
|
4847
5703
|
};
|
|
4848
5704
|
|
|
4849
5705
|
CodeEditor.REGISTER_LANGUAGE = function( name, options = {}, def, rules )
|
|
4850
5706
|
{
|
|
4851
5707
|
CodeEditor.languages[ name ] = options;
|
|
4852
5708
|
|
|
4853
|
-
if( def?.keywords ) CodeEditor.keywords[ name ] = def.keywords
|
|
4854
|
-
if( def?.utils ) CodeEditor.utils[ name ] = def.utils
|
|
4855
|
-
if( def?.types ) CodeEditor.types[ name ] = def.types
|
|
4856
|
-
if( def?.builtIn ) CodeEditor.builtIn[ name ] = def.builtIn
|
|
4857
|
-
if( def?.
|
|
4858
|
-
if( def?.symbols ) CodeEditor.symbols[ name ] = def.symbols
|
|
5709
|
+
if( def?.keywords ) CodeEditor.keywords[ name ] = new Set( def.keywords );
|
|
5710
|
+
if( def?.utils ) CodeEditor.utils[ name ] = new Set( def.utils );
|
|
5711
|
+
if( def?.types ) CodeEditor.types[ name ] = new Set( def.types );
|
|
5712
|
+
if( def?.builtIn ) CodeEditor.builtIn[ name ] = new Set( def.builtIn );
|
|
5713
|
+
if( def?.statements ) CodeEditor.statements[ name ] = new Set( def.statements );
|
|
5714
|
+
if( def?.symbols ) CodeEditor.symbols[ name ] = new Set( def.symbols );
|
|
4859
5715
|
|
|
4860
5716
|
if( rules ) HighlightRules[ name ] = rules;
|
|
4861
5717
|
};
|