lexgui 0.7.6 → 0.7.7
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 +238 -74
- package/build/extensions/timeline.js +40 -12
- package/build/lexgui.css +0 -1
- package/build/lexgui.js +9 -6
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +9 -6
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +28 -1
- package/examples/code-editor.html +1 -1
- package/package.json +1 -1
|
@@ -347,6 +347,19 @@ class CodeEditor {
|
|
|
347
347
|
this.skipTabs = options.skipTabs ?? false;
|
|
348
348
|
this.useFileExplorer = ( options.fileExplorer ?? false ) && !this.skipTabs;
|
|
349
349
|
this.useAutoComplete = options.autocomplete ?? true;
|
|
350
|
+
this.allowClosingTabs = options.allowClosingTabs ?? true;
|
|
351
|
+
this.allowLoadingFiles = options.allowLoadingFiles ?? true;
|
|
352
|
+
this.highlight = options.highlight ?? 'Plain Text';
|
|
353
|
+
this.newTabOptions = options.newTabOptions;
|
|
354
|
+
this.customSuggestions = options.customSuggestions ?? [];
|
|
355
|
+
|
|
356
|
+
// Editor callbacks
|
|
357
|
+
this.onSave = options.onSave ?? options.onsave; // LEGACY onsave
|
|
358
|
+
this.onRun = options.onRun ?? options.onrun; // LEGACY onrun
|
|
359
|
+
this.onCtrlSpace = options.onCtrlSpace;
|
|
360
|
+
this.onCreateStatusPanel = options.onCreateStatusPanel;
|
|
361
|
+
this.onContextMenu = options.onContextMenu;
|
|
362
|
+
this.onNewTab = options.onNewTab;
|
|
350
363
|
|
|
351
364
|
// File explorer
|
|
352
365
|
if( this.useFileExplorer )
|
|
@@ -377,8 +390,7 @@ class CodeEditor {
|
|
|
377
390
|
this.loadTab( event.node.id );
|
|
378
391
|
break;
|
|
379
392
|
case LX.TreeEvent.NODE_DELETED:
|
|
380
|
-
this.
|
|
381
|
-
delete this.loadedTabs[ event.node.id ];
|
|
393
|
+
this.closeTab( event.node.id );
|
|
382
394
|
break;
|
|
383
395
|
// case LX.TreeEvent.NODE_CONTEXTMENU:
|
|
384
396
|
// LX.addContextMenu( event.multiple ? "Selected Nodes" : event.node.id, event.value, m => {
|
|
@@ -547,36 +559,44 @@ class CodeEditor {
|
|
|
547
559
|
return;
|
|
548
560
|
}
|
|
549
561
|
|
|
550
|
-
|
|
562
|
+
// Vertical scroll
|
|
563
|
+
{
|
|
564
|
+
this.setScrollBarValue( 'vertical' );
|
|
551
565
|
|
|
552
|
-
|
|
566
|
+
const scrollTop = this.getScrollTop();
|
|
553
567
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
{
|
|
557
|
-
if( this.visibleLinesViewport.y < (this.code.lines.length - 1) )
|
|
568
|
+
// Scroll down...
|
|
569
|
+
if( scrollTop > lastScrollTopValue )
|
|
558
570
|
{
|
|
559
|
-
|
|
560
|
-
const scrollDownBoundary =
|
|
561
|
-
( Math.max( this.visibleLinesViewport.y - totalLinesInViewport, 0 ) - 1 ) * this.lineHeight;
|
|
562
|
-
|
|
563
|
-
if( scrollTop >= scrollDownBoundary )
|
|
571
|
+
if( this.visibleLinesViewport.y < (this.code.lines.length - 1) )
|
|
564
572
|
{
|
|
565
|
-
this.
|
|
573
|
+
const totalLinesInViewport = ( ( this.codeScroller.offsetHeight ) / this.lineHeight )|0;
|
|
574
|
+
const scrollDownBoundary =
|
|
575
|
+
( Math.max( this.visibleLinesViewport.y - totalLinesInViewport, 0 ) - 1 ) * this.lineHeight;
|
|
576
|
+
|
|
577
|
+
if( scrollTop >= scrollDownBoundary )
|
|
578
|
+
{
|
|
579
|
+
this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
|
|
580
|
+
}
|
|
566
581
|
}
|
|
567
582
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
else
|
|
571
|
-
{
|
|
572
|
-
const scrollUpBoundary = parseInt( this.code.style.top );
|
|
573
|
-
if( scrollTop < scrollUpBoundary )
|
|
583
|
+
// Scroll up...
|
|
584
|
+
else
|
|
574
585
|
{
|
|
575
|
-
|
|
586
|
+
const scrollUpBoundary = parseInt( this.code.style.top );
|
|
587
|
+
if( scrollTop < scrollUpBoundary )
|
|
588
|
+
{
|
|
589
|
+
this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
|
|
590
|
+
}
|
|
576
591
|
}
|
|
592
|
+
|
|
593
|
+
lastScrollTopValue = scrollTop;
|
|
577
594
|
}
|
|
578
595
|
|
|
579
|
-
|
|
596
|
+
// Horizontal scroll
|
|
597
|
+
{
|
|
598
|
+
this.setScrollBarValue( 'horizontal' );
|
|
599
|
+
}
|
|
580
600
|
});
|
|
581
601
|
|
|
582
602
|
this.codeScroller.addEventListener( 'wheel', e => {
|
|
@@ -697,9 +717,6 @@ class CodeEditor {
|
|
|
697
717
|
|
|
698
718
|
// Code
|
|
699
719
|
|
|
700
|
-
this.highlight = options.highlight ?? 'Plain Text';
|
|
701
|
-
this.onsave = options.onsave ?? ((code) => { console.log( code, "save" ) });
|
|
702
|
-
this.onrun = options.onrun ?? ((code) => { this.runScript(code) });
|
|
703
720
|
this.actions = {};
|
|
704
721
|
this.cursorBlinkRate = 550;
|
|
705
722
|
this.tabSpaces = 4;
|
|
@@ -784,14 +801,14 @@ class CodeEditor {
|
|
|
784
801
|
var numCharsDeleted = 1;
|
|
785
802
|
|
|
786
803
|
// Delete full word
|
|
787
|
-
if( e.
|
|
804
|
+
if( e.ctrlKey )
|
|
788
805
|
{
|
|
789
806
|
const [word, from, to] = this.getWordAtPos( cursor, -1 );
|
|
790
807
|
|
|
791
808
|
if( word.length > 1 )
|
|
792
809
|
{
|
|
793
810
|
deleteFromPosition = from;
|
|
794
|
-
numCharsDeleted = word.length;
|
|
811
|
+
numCharsDeleted = word.length - ( to - cursor.position );
|
|
795
812
|
}
|
|
796
813
|
}
|
|
797
814
|
|
|
@@ -966,7 +983,7 @@ class CodeEditor {
|
|
|
966
983
|
|
|
967
984
|
if( e.ctrlKey )
|
|
968
985
|
{
|
|
969
|
-
this.
|
|
986
|
+
this.onRun( this.getText() );
|
|
970
987
|
return;
|
|
971
988
|
}
|
|
972
989
|
|
|
@@ -1149,7 +1166,7 @@ class CodeEditor {
|
|
|
1149
1166
|
if( e.metaKey ) // Apple devices (Command)
|
|
1150
1167
|
{
|
|
1151
1168
|
e.preventDefault();
|
|
1152
|
-
this.actions[ 'End' ].callback( ln, cursor );
|
|
1169
|
+
this.actions[ 'End' ].callback( ln, cursor, e );
|
|
1153
1170
|
}
|
|
1154
1171
|
else if( e.ctrlKey ) // Next word
|
|
1155
1172
|
{
|
|
@@ -1290,7 +1307,7 @@ class CodeEditor {
|
|
|
1290
1307
|
for( let url of options.files )
|
|
1291
1308
|
{
|
|
1292
1309
|
const finalUrl = url.constructor === Array ? url[ 0 ] : url;
|
|
1293
|
-
const finalFileName = url.constructor === Array ? url[ 1 ] :
|
|
1310
|
+
const finalFileName = url.constructor === Array ? url[ 1 ] : undefined;
|
|
1294
1311
|
|
|
1295
1312
|
await this.loadFile( finalUrl, { filename: finalFileName, async: loadAsync, callback: ( name, text ) => {
|
|
1296
1313
|
filesLoaded++;
|
|
@@ -1300,17 +1317,21 @@ class CodeEditor {
|
|
|
1300
1317
|
|
|
1301
1318
|
if( options.onFilesLoaded )
|
|
1302
1319
|
{
|
|
1303
|
-
options.onFilesLoaded( this.loadedTabs, numFiles );
|
|
1320
|
+
options.onFilesLoaded( this, this.loadedTabs, numFiles );
|
|
1304
1321
|
}
|
|
1305
1322
|
}
|
|
1306
1323
|
}});
|
|
1307
1324
|
}
|
|
1308
1325
|
}
|
|
1309
|
-
else
|
|
1326
|
+
else if( options.defaultTab ?? true )
|
|
1310
1327
|
{
|
|
1311
1328
|
this.addTab( options.name || "untitled", true, options.title, { language: options.highlight ?? "Plain Text" } );
|
|
1312
1329
|
onLoadAll();
|
|
1313
1330
|
}
|
|
1331
|
+
else
|
|
1332
|
+
{
|
|
1333
|
+
onLoadAll();
|
|
1334
|
+
}
|
|
1314
1335
|
}
|
|
1315
1336
|
|
|
1316
1337
|
static getInstances()
|
|
@@ -1711,6 +1732,11 @@ class CodeEditor {
|
|
|
1711
1732
|
|
|
1712
1733
|
let panel = new LX.Panel({ className: "lexcodetabinfo flex flex-row", height: "auto" });
|
|
1713
1734
|
|
|
1735
|
+
if( this.onCreateStatusPanel )
|
|
1736
|
+
{
|
|
1737
|
+
this.onCreateStatusPanel( panel, this );
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1714
1740
|
let leftStatusPanel = new LX.Panel( { id: "FontSizeZoomStatusComponent", height: "auto" } );
|
|
1715
1741
|
leftStatusPanel.sameLine();
|
|
1716
1742
|
|
|
@@ -1856,10 +1882,18 @@ class CodeEditor {
|
|
|
1856
1882
|
|
|
1857
1883
|
this.processFocus( false );
|
|
1858
1884
|
|
|
1859
|
-
|
|
1885
|
+
if( this.onNewTab )
|
|
1886
|
+
{
|
|
1887
|
+
this.onNewTab( e );
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
const dmOptions = this.newTabOptions ?? [
|
|
1860
1892
|
{ name: "Create file", icon: "FilePlus", callback: this._onCreateNewFile.bind( this ) },
|
|
1861
|
-
{ name: "Load file", icon: "FileUp", callback: this.loadTabFromFile.bind( this ) }
|
|
1862
|
-
]
|
|
1893
|
+
{ name: "Load file", icon: "FileUp", disabled: !this.allowLoadingFiles, callback: this.loadTabFromFile.bind( this ) }
|
|
1894
|
+
];
|
|
1895
|
+
|
|
1896
|
+
new LX.DropdownMenu( e.target, dmOptions, { side: "bottom", align: "start" });
|
|
1863
1897
|
}
|
|
1864
1898
|
|
|
1865
1899
|
_onCreateNewFile() {
|
|
@@ -1869,10 +1903,14 @@ class CodeEditor {
|
|
|
1869
1903
|
if( this.onCreateFile )
|
|
1870
1904
|
{
|
|
1871
1905
|
options = this.onCreateFile( this );
|
|
1906
|
+
if( !options ) // Skip adding new file
|
|
1907
|
+
{
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1872
1910
|
}
|
|
1873
1911
|
|
|
1874
1912
|
const name = options.name ?? "unnamed.js";
|
|
1875
|
-
this.addTab( name, true, name, { language: options.language ?? "JavaScript" } );
|
|
1913
|
+
this.addTab( name, true, name, { indexOffset: options.indexOffset, language: options.language ?? "JavaScript" } );
|
|
1876
1914
|
}
|
|
1877
1915
|
|
|
1878
1916
|
_onSelectTab( isNewTabButton, event, name ) {
|
|
@@ -1921,19 +1959,19 @@ class CodeEditor {
|
|
|
1921
1959
|
}
|
|
1922
1960
|
|
|
1923
1961
|
new LX.DropdownMenu( event.target, [
|
|
1924
|
-
{ name: "Close", kbd: "MWB", callback: () => { this.
|
|
1925
|
-
{ name: "Close Others", callback: () => {
|
|
1962
|
+
{ name: "Close", kbd: "MWB", disabled: !this.allowClosingTabs, callback: () => { this.closeTab( name ) } },
|
|
1963
|
+
{ name: "Close Others", disabled: !this.allowClosingTabs, callback: () => {
|
|
1926
1964
|
for( const [ key, data ] of Object.entries( this.tabs.tabs ) )
|
|
1927
1965
|
{
|
|
1928
1966
|
if( key === '+' || key === name ) continue;
|
|
1929
|
-
this.
|
|
1967
|
+
this.closeTab( key )
|
|
1930
1968
|
}
|
|
1931
1969
|
} },
|
|
1932
|
-
{ name: "Close All", callback: () => {
|
|
1970
|
+
{ name: "Close All", disabled: !this.allowClosingTabs, callback: () => {
|
|
1933
1971
|
for( const [ key, data ] of Object.entries( this.tabs.tabs ) )
|
|
1934
1972
|
{
|
|
1935
1973
|
if( key === '+' ) continue;
|
|
1936
|
-
this.
|
|
1974
|
+
this.closeTab( key )
|
|
1937
1975
|
}
|
|
1938
1976
|
} },
|
|
1939
1977
|
null,
|
|
@@ -2015,7 +2053,8 @@ class CodeEditor {
|
|
|
2015
2053
|
icon: tabIcon,
|
|
2016
2054
|
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
2017
2055
|
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton ),
|
|
2018
|
-
allowDelete:
|
|
2056
|
+
allowDelete: this.allowClosingTabs,
|
|
2057
|
+
indexOffset: options.indexOffset
|
|
2019
2058
|
} );
|
|
2020
2059
|
}
|
|
2021
2060
|
|
|
@@ -2038,6 +2077,12 @@ class CodeEditor {
|
|
|
2038
2077
|
this.mustProcessLines = true;
|
|
2039
2078
|
}
|
|
2040
2079
|
|
|
2080
|
+
if( options.codeLines )
|
|
2081
|
+
{
|
|
2082
|
+
code.lines = options.codeLines;
|
|
2083
|
+
this.mustProcessLines = true;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2041
2086
|
this._processLinesIfNecessary();
|
|
2042
2087
|
|
|
2043
2088
|
this._updateDataInfoPanel( "@tab-name", name );
|
|
@@ -2155,7 +2200,7 @@ class CodeEditor {
|
|
|
2155
2200
|
icon: tabIcon,
|
|
2156
2201
|
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
2157
2202
|
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton ),
|
|
2158
|
-
allowDelete:
|
|
2203
|
+
allowDelete: this.allowClosingTabs
|
|
2159
2204
|
});
|
|
2160
2205
|
|
|
2161
2206
|
// Move into the sizer..
|
|
@@ -2173,6 +2218,11 @@ class CodeEditor {
|
|
|
2173
2218
|
|
|
2174
2219
|
closeTab( name, eraseAll ) {
|
|
2175
2220
|
|
|
2221
|
+
if( !this.allowClosingTabs )
|
|
2222
|
+
{
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2176
2226
|
this.tabs.delete( name );
|
|
2177
2227
|
|
|
2178
2228
|
if( eraseAll )
|
|
@@ -2290,22 +2340,65 @@ class CodeEditor {
|
|
|
2290
2340
|
e.preventDefault();
|
|
2291
2341
|
|
|
2292
2342
|
if( !this.canOpenContextMenu )
|
|
2343
|
+
{
|
|
2293
2344
|
return;
|
|
2345
|
+
}
|
|
2294
2346
|
|
|
2295
2347
|
LX.addContextMenu( null, e, m => {
|
|
2296
2348
|
m.add( "Copy", () => { this._copyContent( cursor ); } );
|
|
2349
|
+
|
|
2297
2350
|
if( !this.disableEdition )
|
|
2298
2351
|
{
|
|
2299
2352
|
m.add( "Cut", () => { this._cutContent( cursor ); } );
|
|
2300
2353
|
m.add( "Paste", () => { this._pasteContent( cursor ); } );
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
if( !this.onContextMenu )
|
|
2357
|
+
{
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
let content = null;
|
|
2362
|
+
|
|
2363
|
+
if( cursor.selection )
|
|
2364
|
+
{
|
|
2365
|
+
// Some selections don't depend on mouse up..
|
|
2366
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2367
|
+
|
|
2368
|
+
const separator = "_NEWLINE_";
|
|
2369
|
+
let code = this.code.lines.join( separator );
|
|
2370
|
+
|
|
2371
|
+
// Get linear start index
|
|
2372
|
+
let index = 0;
|
|
2373
|
+
|
|
2374
|
+
for( let i = 0; i <= cursor.selection.fromY; i++ )
|
|
2375
|
+
{
|
|
2376
|
+
index += ( i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length );
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
index += cursor.selection.fromY * separator.length;
|
|
2380
|
+
const num_chars = cursor.selection.chars + ( cursor.selection.toY - cursor.selection.fromY ) * separator.length;
|
|
2381
|
+
const text = code.substr( index, num_chars );
|
|
2382
|
+
content = text.split( separator ).join('\n');
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
const options = this.onContextMenu( this, content, e );
|
|
2386
|
+
if( options.length )
|
|
2387
|
+
{
|
|
2301
2388
|
m.add( "" );
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2389
|
+
|
|
2390
|
+
for( const o of options )
|
|
2391
|
+
{
|
|
2392
|
+
m.add( o.path, { disabled: o.disabled, callback: o.callback } );
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
// m.add( "Format/JSON", () => {
|
|
2396
|
+
// let json = this.toJSONFormat( this.getText() );
|
|
2397
|
+
// if( !json )
|
|
2398
|
+
// return;
|
|
2399
|
+
// this.code.lines = json.split( "\n" );
|
|
2400
|
+
// this.processLines();
|
|
2401
|
+
// } );
|
|
2309
2402
|
}
|
|
2310
2403
|
});
|
|
2311
2404
|
|
|
@@ -2638,10 +2731,17 @@ class CodeEditor {
|
|
|
2638
2731
|
e.preventDefault();
|
|
2639
2732
|
this.selectAll();
|
|
2640
2733
|
return true;
|
|
2734
|
+
case 'b': // k+b comment block
|
|
2735
|
+
e.preventDefault();
|
|
2736
|
+
if( this.state.keyChain == 'k' ) {
|
|
2737
|
+
this._commentLines( cursor, true );
|
|
2738
|
+
return true;
|
|
2739
|
+
}
|
|
2740
|
+
return false;
|
|
2641
2741
|
case 'c': // k+c, comment line
|
|
2642
2742
|
e.preventDefault();
|
|
2643
2743
|
if( this.state.keyChain == 'k' ) {
|
|
2644
|
-
this._commentLines();
|
|
2744
|
+
this._commentLines( cursor );
|
|
2645
2745
|
return true;
|
|
2646
2746
|
}
|
|
2647
2747
|
return false;
|
|
@@ -2663,12 +2763,12 @@ class CodeEditor {
|
|
|
2663
2763
|
return true;
|
|
2664
2764
|
case 's': // save
|
|
2665
2765
|
e.preventDefault();
|
|
2666
|
-
this.
|
|
2766
|
+
this.onSave( this.getText() );
|
|
2667
2767
|
return true;
|
|
2668
2768
|
case 'u': // k+u, uncomment line
|
|
2669
2769
|
e.preventDefault();
|
|
2670
2770
|
if( this.state.keyChain == 'k' ) {
|
|
2671
|
-
this._uncommentLines();
|
|
2771
|
+
this._uncommentLines( cursor );
|
|
2672
2772
|
return true;
|
|
2673
2773
|
}
|
|
2674
2774
|
return false;
|
|
@@ -2688,6 +2788,13 @@ class CodeEditor {
|
|
|
2688
2788
|
e.preventDefault();
|
|
2689
2789
|
this._decreaseFontSize();
|
|
2690
2790
|
return true;
|
|
2791
|
+
case ' ': // custom event
|
|
2792
|
+
if( this.onCtrlSpace )
|
|
2793
|
+
{
|
|
2794
|
+
e.preventDefault();
|
|
2795
|
+
this.onCtrlSpace( cursor );
|
|
2796
|
+
return true;
|
|
2797
|
+
}
|
|
2691
2798
|
}
|
|
2692
2799
|
}
|
|
2693
2800
|
|
|
@@ -2981,32 +3088,77 @@ class CodeEditor {
|
|
|
2981
3088
|
this.hideAutoCompleteBox();
|
|
2982
3089
|
}
|
|
2983
3090
|
|
|
2984
|
-
_commentLines() {
|
|
3091
|
+
_commentLines( cursor, useCommentBlock ) {
|
|
3092
|
+
|
|
3093
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
2985
3094
|
|
|
2986
3095
|
this.state.keyChain = null;
|
|
2987
3096
|
|
|
3097
|
+
cursor = cursor ?? this.getCurrentCursor();
|
|
3098
|
+
|
|
2988
3099
|
if( cursor.selection )
|
|
2989
3100
|
{
|
|
2990
|
-
|
|
3101
|
+
if( !( ( useCommentBlock ? lang.blockComments : lang.singleLineComments ) ?? true ) )
|
|
3102
|
+
{
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
3105
|
+
|
|
2991
3106
|
this._addUndoStep( cursor, true );
|
|
2992
3107
|
|
|
2993
|
-
const selectedLines = this.code.lines.slice( cursor.selection.fromY, cursor.selection.toY );
|
|
3108
|
+
const selectedLines = this.code.lines.slice( cursor.selection.fromY, cursor.selection.toY + 1 );
|
|
2994
3109
|
const minIdx = Math.min(...selectedLines.map( v => {
|
|
2995
3110
|
var idx = firstNonspaceIndex( v );
|
|
2996
3111
|
return idx < 0 ? 1e10 : idx;
|
|
2997
3112
|
} ));
|
|
2998
3113
|
|
|
2999
|
-
|
|
3114
|
+
if( useCommentBlock )
|
|
3115
|
+
{
|
|
3116
|
+
const tokens = ( lang.blockCommentsTokens ?? this.defaultBlockCommentTokens );
|
|
3117
|
+
|
|
3118
|
+
const fromString = this.code.lines[ cursor.selection.fromY ];
|
|
3119
|
+
let fromIdx = firstNonspaceIndex( fromString );
|
|
3120
|
+
if( fromIdx == -1 )
|
|
3121
|
+
{
|
|
3122
|
+
fromIdx = 0;
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
this.code.lines[ cursor.selection.fromY ] = [
|
|
3126
|
+
fromString.substring( 0, fromIdx ),
|
|
3127
|
+
tokens[ 0 ] + " ",
|
|
3128
|
+
fromString.substring( fromIdx )
|
|
3129
|
+
].join( '' );
|
|
3130
|
+
|
|
3131
|
+
this.code.lines[ cursor.selection.toY ] += " " + tokens[ 1 ];
|
|
3132
|
+
|
|
3133
|
+
cursor.selection.fromX += ( tokens[ 0 ].length + 1 );
|
|
3134
|
+
this._processSelection( cursor );
|
|
3135
|
+
}
|
|
3136
|
+
else
|
|
3000
3137
|
{
|
|
3001
|
-
|
|
3138
|
+
for( let i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
3139
|
+
{
|
|
3140
|
+
this._commentLine( cursor, i, minIdx, false );
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
const token = ( lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken ) + ' ';
|
|
3144
|
+
this.cursorToString( cursor, token );
|
|
3145
|
+
|
|
3146
|
+
cursor.selection.fromX += token.length;
|
|
3147
|
+
cursor.selection.toX += token.length;
|
|
3148
|
+
this._processSelection( cursor );
|
|
3002
3149
|
}
|
|
3003
3150
|
}
|
|
3004
3151
|
else
|
|
3005
3152
|
{
|
|
3006
|
-
|
|
3153
|
+
if( !( lang.singleLineComments ?? true ) )
|
|
3007
3154
|
{
|
|
3008
|
-
|
|
3009
|
-
|
|
3155
|
+
return;
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
for( const cr of this.cursors.children )
|
|
3159
|
+
{
|
|
3160
|
+
this._addUndoStep( cr, true );
|
|
3161
|
+
this._commentLine( cr, cr.line );
|
|
3010
3162
|
}
|
|
3011
3163
|
}
|
|
3012
3164
|
|
|
@@ -3014,33 +3166,37 @@ class CodeEditor {
|
|
|
3014
3166
|
this._hideActiveLine();
|
|
3015
3167
|
}
|
|
3016
3168
|
|
|
3017
|
-
_commentLine( cursor, line, minNonspaceIdx ) {
|
|
3169
|
+
_commentLine( cursor, line, minNonspaceIdx, updateCursor = true ) {
|
|
3018
3170
|
|
|
3019
3171
|
const lang = CodeEditor.languages[ this.highlight ];
|
|
3020
|
-
|
|
3021
|
-
if( !( lang.singleLineComments ?? true ))
|
|
3172
|
+
if( !( lang.singleLineComments ?? true ) )
|
|
3022
3173
|
return;
|
|
3023
3174
|
|
|
3024
3175
|
const token = ( lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken ) + ' ';
|
|
3025
3176
|
const string = this.code.lines[ line ];
|
|
3026
3177
|
|
|
3027
3178
|
let idx = firstNonspaceIndex( string );
|
|
3028
|
-
if( idx
|
|
3179
|
+
if( idx == -1 )
|
|
3029
3180
|
{
|
|
3030
|
-
|
|
3031
|
-
|
|
3181
|
+
return;
|
|
3182
|
+
}
|
|
3032
3183
|
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
token,
|
|
3036
|
-
string.substring( idx )
|
|
3037
|
-
].join( '' );
|
|
3184
|
+
// Update idx using min of the selected lines (if necessary..)
|
|
3185
|
+
idx = minNonspaceIdx ?? idx;
|
|
3038
3186
|
|
|
3187
|
+
this.code.lines[ line ] = [
|
|
3188
|
+
string.substring( 0, idx ),
|
|
3189
|
+
token,
|
|
3190
|
+
string.substring( idx )
|
|
3191
|
+
].join( '' );
|
|
3192
|
+
|
|
3193
|
+
if( updateCursor )
|
|
3194
|
+
{
|
|
3039
3195
|
this.cursorToString( cursor, token );
|
|
3040
3196
|
}
|
|
3041
3197
|
}
|
|
3042
3198
|
|
|
3043
|
-
_uncommentLines() {
|
|
3199
|
+
_uncommentLines( cursor ) {
|
|
3044
3200
|
|
|
3045
3201
|
this.state.keyChain = null;
|
|
3046
3202
|
|
|
@@ -4619,6 +4775,8 @@ class CodeEditor {
|
|
|
4619
4775
|
|
|
4620
4776
|
if( state.selection )
|
|
4621
4777
|
{
|
|
4778
|
+
this.endSelection();
|
|
4779
|
+
|
|
4622
4780
|
this.startSelection( cursor );
|
|
4623
4781
|
|
|
4624
4782
|
cursor.selection.load( state.selection );
|
|
@@ -4899,7 +5057,10 @@ class CodeEditor {
|
|
|
4899
5057
|
}
|
|
4900
5058
|
else
|
|
4901
5059
|
{
|
|
4902
|
-
|
|
5060
|
+
if( value )
|
|
5061
|
+
{
|
|
5062
|
+
this.codeScroller.scrollLeft += value;
|
|
5063
|
+
}
|
|
4903
5064
|
|
|
4904
5065
|
const scrollBarWidth = this.hScrollbar.thumb.parentElement.offsetWidth;
|
|
4905
5066
|
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
@@ -5109,6 +5270,9 @@ class CodeEditor {
|
|
|
5109
5270
|
suggestions = suggestions.concat( otherValues.slice( 0, -1 ) );
|
|
5110
5271
|
}
|
|
5111
5272
|
|
|
5273
|
+
// Add custom suggestions...
|
|
5274
|
+
suggestions = suggestions.concat( this.customSuggestions );
|
|
5275
|
+
|
|
5112
5276
|
// Remove 1/2 char words and duplicates...
|
|
5113
5277
|
suggestions = Array.from( new Set( suggestions )).filter( s => s.length > 2 && s.toLowerCase().includes( word.toLowerCase() ) );
|
|
5114
5278
|
|
|
@@ -193,7 +193,6 @@ class Timeline {
|
|
|
193
193
|
this.setTime(value);
|
|
194
194
|
}, {
|
|
195
195
|
units: "s",
|
|
196
|
-
signal: "@on_set_time_" + this.uniqueID,
|
|
197
196
|
step: 0.01, min: 0, precision: 3,
|
|
198
197
|
skipSlider: true,
|
|
199
198
|
skipReset: true,
|
|
@@ -205,7 +204,6 @@ class Timeline {
|
|
|
205
204
|
}, {
|
|
206
205
|
units: "s",
|
|
207
206
|
step: 0.01, min: 0,
|
|
208
|
-
signal: "@on_set_duration_" + this.uniqueID,
|
|
209
207
|
skipReset: true,
|
|
210
208
|
nameWidth: "auto"
|
|
211
209
|
});
|
|
@@ -261,7 +259,6 @@ class Timeline {
|
|
|
261
259
|
*/
|
|
262
260
|
updateLeftPanel( ) {
|
|
263
261
|
|
|
264
|
-
const scrollTop = this.trackTreesPanel ? this.trackTreesPanel.root.scrollTop : 0;
|
|
265
262
|
this.leftPanel.clear();
|
|
266
263
|
|
|
267
264
|
const panel = this.leftPanel;
|
|
@@ -326,7 +323,7 @@ class Timeline {
|
|
|
326
323
|
}
|
|
327
324
|
});
|
|
328
325
|
|
|
329
|
-
this.trackTreesPanel.root.scrollTop =
|
|
326
|
+
this.trackTreesPanel.root.scrollTop = this.currentScrollInPixels;
|
|
330
327
|
|
|
331
328
|
if( this.leftPanel.parent.root.classList.contains("hidden") || !this.root.parentElement ){
|
|
332
329
|
return;
|
|
@@ -694,7 +691,7 @@ class Timeline {
|
|
|
694
691
|
this.duration = this.animationClip.duration = v;
|
|
695
692
|
|
|
696
693
|
if(updateHeader) {
|
|
697
|
-
|
|
694
|
+
this.header.components["Duration"].set( +this.duration.toFixed(2), true ); // skipcallback = true
|
|
698
695
|
}
|
|
699
696
|
|
|
700
697
|
if( this.onSetDuration && !skipCallback )
|
|
@@ -703,7 +700,7 @@ class Timeline {
|
|
|
703
700
|
|
|
704
701
|
setTime(time, skipCallback = false ){
|
|
705
702
|
this.currentTime = Math.max(0,Math.min(time,this.duration));
|
|
706
|
-
|
|
703
|
+
this.header.components["Current Time"].set( +this.currentTime.toFixed(2), true ); // skipcallback = true
|
|
707
704
|
|
|
708
705
|
if(this.onSetTime && !skipCallback)
|
|
709
706
|
this.onSetTime(this.currentTime);
|
|
@@ -733,6 +730,41 @@ class Timeline {
|
|
|
733
730
|
this.visualOriginTime += this.currentTime - this.xToTime(xCurrentTime);
|
|
734
731
|
}
|
|
735
732
|
|
|
733
|
+
/**
|
|
734
|
+
* @method setScroll
|
|
735
|
+
* not delta from last state, but full scroll amount.
|
|
736
|
+
* @param {Number} scrollY either pixels or [0,1]
|
|
737
|
+
* @param {Bool} normalized if true, scrollY is in range[0,1] being 1 fully scrolled. Otherwised scrollY represents pixels
|
|
738
|
+
* @returns
|
|
739
|
+
*/
|
|
740
|
+
|
|
741
|
+
setScroll( scrollY, normalized = true ){
|
|
742
|
+
if ( !this.trackTreesPanel ){
|
|
743
|
+
this.currentScroll = 0;
|
|
744
|
+
this.currentScrollInPixels = 0;
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const r = this.trackTreesPanel.root;
|
|
749
|
+
if (r.scrollHeight > r.clientHeight){
|
|
750
|
+
if ( normalized ){
|
|
751
|
+
this.currentScroll = scrollY;
|
|
752
|
+
this.currentScrollInPixels = scrollY * (r.scrollHeight - r.clientHeight);
|
|
753
|
+
}else{
|
|
754
|
+
this.currentScroll = scrollY / (r.scrollHeight - r.clientHeight);
|
|
755
|
+
this.currentScrollInPixels = scrollY;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
else{
|
|
759
|
+
this.currentScroll = 0;
|
|
760
|
+
this.currentScrollInPixels = 0;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// automatically calls event.
|
|
764
|
+
this.trackTreesPanel.root.scrollTop = this.currentScrollInPixels;
|
|
765
|
+
|
|
766
|
+
}
|
|
767
|
+
|
|
736
768
|
/**
|
|
737
769
|
* @method processMouse
|
|
738
770
|
* @param {*} e
|
|
@@ -1567,9 +1599,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1567
1599
|
*/
|
|
1568
1600
|
changeSelectedItems( itemsToAdd = null, itemsToRemove = null, skipCallback = false ) {
|
|
1569
1601
|
|
|
1570
|
-
|
|
1571
|
-
this.deselectAllKeyFrames();
|
|
1572
|
-
this.unHoverAll();
|
|
1602
|
+
this.deselectAllElements();
|
|
1573
1603
|
|
|
1574
1604
|
const tracks = this.animationClip.tracks;
|
|
1575
1605
|
const tracksPerGroup = this.animationClip.tracksPerGroup;
|
|
@@ -3219,9 +3249,7 @@ class ClipsTimeline extends Timeline {
|
|
|
3219
3249
|
*/
|
|
3220
3250
|
changeSelectedItems( ) {
|
|
3221
3251
|
|
|
3222
|
-
|
|
3223
|
-
this.deselectAllClips();
|
|
3224
|
-
this.unHoverAll();
|
|
3252
|
+
this.deselectAllElements();
|
|
3225
3253
|
|
|
3226
3254
|
this.selectedItems = this.animationClip.tracks.slice();
|
|
3227
3255
|
|