lexgui 0.7.6 → 0.7.8
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 +281 -88
- package/build/extensions/timeline.js +84 -40
- package/build/lexgui.css +16 -16
- package/build/lexgui.js +64 -23
- package/build/lexgui.min.css +2 -2
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +64 -23
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +44 -1
- package/examples/asset-view.html +1 -1
- package/examples/code-editor.html +1 -1
- package/package.json +1 -1
|
@@ -347,6 +347,20 @@ 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;
|
|
363
|
+
this.onSelectTab = options.onSelectTab;
|
|
350
364
|
|
|
351
365
|
// File explorer
|
|
352
366
|
if( this.useFileExplorer )
|
|
@@ -377,8 +391,7 @@ class CodeEditor {
|
|
|
377
391
|
this.loadTab( event.node.id );
|
|
378
392
|
break;
|
|
379
393
|
case LX.TreeEvent.NODE_DELETED:
|
|
380
|
-
this.
|
|
381
|
-
delete this.loadedTabs[ event.node.id ];
|
|
394
|
+
this.closeTab( event.node.id );
|
|
382
395
|
break;
|
|
383
396
|
// case LX.TreeEvent.NODE_CONTEXTMENU:
|
|
384
397
|
// LX.addContextMenu( event.multiple ? "Selected Nodes" : event.node.id, event.value, m => {
|
|
@@ -547,36 +560,44 @@ class CodeEditor {
|
|
|
547
560
|
return;
|
|
548
561
|
}
|
|
549
562
|
|
|
550
|
-
|
|
563
|
+
// Vertical scroll
|
|
564
|
+
{
|
|
565
|
+
this.setScrollBarValue( 'vertical' );
|
|
551
566
|
|
|
552
|
-
|
|
567
|
+
const scrollTop = this.getScrollTop();
|
|
553
568
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
{
|
|
557
|
-
if( this.visibleLinesViewport.y < (this.code.lines.length - 1) )
|
|
569
|
+
// Scroll down...
|
|
570
|
+
if( scrollTop > lastScrollTopValue )
|
|
558
571
|
{
|
|
559
|
-
|
|
560
|
-
const scrollDownBoundary =
|
|
561
|
-
( Math.max( this.visibleLinesViewport.y - totalLinesInViewport, 0 ) - 1 ) * this.lineHeight;
|
|
562
|
-
|
|
563
|
-
if( scrollTop >= scrollDownBoundary )
|
|
572
|
+
if( this.visibleLinesViewport.y < (this.code.lines.length - 1) )
|
|
564
573
|
{
|
|
565
|
-
this.
|
|
574
|
+
const totalLinesInViewport = ( ( this.codeScroller.offsetHeight ) / this.lineHeight )|0;
|
|
575
|
+
const scrollDownBoundary =
|
|
576
|
+
( Math.max( this.visibleLinesViewport.y - totalLinesInViewport, 0 ) - 1 ) * this.lineHeight;
|
|
577
|
+
|
|
578
|
+
if( scrollTop >= scrollDownBoundary )
|
|
579
|
+
{
|
|
580
|
+
this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
|
|
581
|
+
}
|
|
566
582
|
}
|
|
567
583
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
else
|
|
571
|
-
{
|
|
572
|
-
const scrollUpBoundary = parseInt( this.code.style.top );
|
|
573
|
-
if( scrollTop < scrollUpBoundary )
|
|
584
|
+
// Scroll up...
|
|
585
|
+
else
|
|
574
586
|
{
|
|
575
|
-
|
|
587
|
+
const scrollUpBoundary = parseInt( this.code.style.top );
|
|
588
|
+
if( scrollTop < scrollUpBoundary )
|
|
589
|
+
{
|
|
590
|
+
this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
|
|
591
|
+
}
|
|
576
592
|
}
|
|
593
|
+
|
|
594
|
+
lastScrollTopValue = scrollTop;
|
|
577
595
|
}
|
|
578
596
|
|
|
579
|
-
|
|
597
|
+
// Horizontal scroll
|
|
598
|
+
{
|
|
599
|
+
this.setScrollBarValue( 'horizontal' );
|
|
600
|
+
}
|
|
580
601
|
});
|
|
581
602
|
|
|
582
603
|
this.codeScroller.addEventListener( 'wheel', e => {
|
|
@@ -651,16 +672,18 @@ class CodeEditor {
|
|
|
651
672
|
|
|
652
673
|
// Add search LINE box
|
|
653
674
|
{
|
|
654
|
-
|
|
655
|
-
box.className = "searchbox
|
|
675
|
+
const box = document.createElement( 'div' );
|
|
676
|
+
box.className = "searchbox";
|
|
656
677
|
|
|
657
|
-
|
|
678
|
+
const searchPanel = new LX.Panel();
|
|
658
679
|
box.appendChild( searchPanel.root );
|
|
659
680
|
|
|
681
|
+
searchPanel.sameLine( 2 );
|
|
660
682
|
searchPanel.addText( null, "", ( value, event ) => {
|
|
661
683
|
input.value = ":" + value.replaceAll( ':', '' );
|
|
662
684
|
this.goToLine( input.value.slice( 1 ) );
|
|
663
685
|
}, { placeholder: "Go to line", trigger: "input" } );
|
|
686
|
+
searchPanel.addButton( null, "x", this.hideSearchLineBox.bind( this ), { icon: "X", title: "Close", tooltip: true } );
|
|
664
687
|
|
|
665
688
|
let input = box.querySelector( 'input' );
|
|
666
689
|
input.addEventListener( 'keyup', e => {
|
|
@@ -697,9 +720,6 @@ class CodeEditor {
|
|
|
697
720
|
|
|
698
721
|
// Code
|
|
699
722
|
|
|
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
723
|
this.actions = {};
|
|
704
724
|
this.cursorBlinkRate = 550;
|
|
705
725
|
this.tabSpaces = 4;
|
|
@@ -784,14 +804,14 @@ class CodeEditor {
|
|
|
784
804
|
var numCharsDeleted = 1;
|
|
785
805
|
|
|
786
806
|
// Delete full word
|
|
787
|
-
if( e.
|
|
807
|
+
if( e.ctrlKey )
|
|
788
808
|
{
|
|
789
809
|
const [word, from, to] = this.getWordAtPos( cursor, -1 );
|
|
790
810
|
|
|
791
811
|
if( word.length > 1 )
|
|
792
812
|
{
|
|
793
813
|
deleteFromPosition = from;
|
|
794
|
-
numCharsDeleted = word.length;
|
|
814
|
+
numCharsDeleted = word.length - ( to - cursor.position );
|
|
795
815
|
}
|
|
796
816
|
}
|
|
797
817
|
|
|
@@ -966,7 +986,7 @@ class CodeEditor {
|
|
|
966
986
|
|
|
967
987
|
if( e.ctrlKey )
|
|
968
988
|
{
|
|
969
|
-
this.
|
|
989
|
+
this.onRun( this.getText() );
|
|
970
990
|
return;
|
|
971
991
|
}
|
|
972
992
|
|
|
@@ -1149,7 +1169,7 @@ class CodeEditor {
|
|
|
1149
1169
|
if( e.metaKey ) // Apple devices (Command)
|
|
1150
1170
|
{
|
|
1151
1171
|
e.preventDefault();
|
|
1152
|
-
this.actions[ 'End' ].callback( ln, cursor );
|
|
1172
|
+
this.actions[ 'End' ].callback( ln, cursor, e );
|
|
1153
1173
|
}
|
|
1154
1174
|
else if( e.ctrlKey ) // Next word
|
|
1155
1175
|
{
|
|
@@ -1290,7 +1310,7 @@ class CodeEditor {
|
|
|
1290
1310
|
for( let url of options.files )
|
|
1291
1311
|
{
|
|
1292
1312
|
const finalUrl = url.constructor === Array ? url[ 0 ] : url;
|
|
1293
|
-
const finalFileName = url.constructor === Array ? url[ 1 ] :
|
|
1313
|
+
const finalFileName = url.constructor === Array ? url[ 1 ] : undefined;
|
|
1294
1314
|
|
|
1295
1315
|
await this.loadFile( finalUrl, { filename: finalFileName, async: loadAsync, callback: ( name, text ) => {
|
|
1296
1316
|
filesLoaded++;
|
|
@@ -1300,17 +1320,28 @@ class CodeEditor {
|
|
|
1300
1320
|
|
|
1301
1321
|
if( options.onFilesLoaded )
|
|
1302
1322
|
{
|
|
1303
|
-
options.onFilesLoaded( this.loadedTabs, numFiles );
|
|
1323
|
+
options.onFilesLoaded( this, this.loadedTabs, numFiles );
|
|
1304
1324
|
}
|
|
1305
1325
|
}
|
|
1306
1326
|
}});
|
|
1307
1327
|
}
|
|
1308
1328
|
}
|
|
1309
|
-
else
|
|
1329
|
+
else if( options.defaultTab ?? true )
|
|
1310
1330
|
{
|
|
1311
1331
|
this.addTab( options.name || "untitled", true, options.title, { language: options.highlight ?? "Plain Text" } );
|
|
1312
1332
|
onLoadAll();
|
|
1313
1333
|
}
|
|
1334
|
+
else
|
|
1335
|
+
{
|
|
1336
|
+
onLoadAll();
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// Clear signals
|
|
1341
|
+
clear() {
|
|
1342
|
+
console.assert( this.rightStatusPanel && this.leftStatusPanel, "No panels to clear." );
|
|
1343
|
+
this.rightStatusPanel.clear();
|
|
1344
|
+
this.leftStatusPanel.clear();
|
|
1314
1345
|
}
|
|
1315
1346
|
|
|
1316
1347
|
static getInstances()
|
|
@@ -1711,7 +1742,12 @@ class CodeEditor {
|
|
|
1711
1742
|
|
|
1712
1743
|
let panel = new LX.Panel({ className: "lexcodetabinfo flex flex-row", height: "auto" });
|
|
1713
1744
|
|
|
1714
|
-
|
|
1745
|
+
if( this.onCreateStatusPanel )
|
|
1746
|
+
{
|
|
1747
|
+
this.onCreateStatusPanel( panel, this );
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
let leftStatusPanel = this.leftStatusPanel = new LX.Panel( { id: "FontSizeZoomStatusComponent", height: "auto" } );
|
|
1715
1751
|
leftStatusPanel.sameLine();
|
|
1716
1752
|
|
|
1717
1753
|
if( this.skipTabs )
|
|
@@ -1725,7 +1761,7 @@ class CodeEditor {
|
|
|
1725
1761
|
leftStatusPanel.endLine( "justify-start" );
|
|
1726
1762
|
panel.attach( leftStatusPanel.root );
|
|
1727
1763
|
|
|
1728
|
-
let rightStatusPanel = new LX.Panel( { height: "auto" } );
|
|
1764
|
+
let rightStatusPanel = this.rightStatusPanel = new LX.Panel( { height: "auto" } );
|
|
1729
1765
|
rightStatusPanel.sameLine();
|
|
1730
1766
|
rightStatusPanel.addLabel( this.code?.title ?? "", { id: "EditorFilenameStatusComponent", fit: true, signal: "@tab-name" });
|
|
1731
1767
|
rightStatusPanel.addButton( null, "Ln 1, Col 1", this.showSearchLineBox.bind( this ), { id: "EditorSelectionStatusComponent", fit: true, signal: "@cursor-data" });
|
|
@@ -1856,10 +1892,18 @@ class CodeEditor {
|
|
|
1856
1892
|
|
|
1857
1893
|
this.processFocus( false );
|
|
1858
1894
|
|
|
1859
|
-
|
|
1895
|
+
if( this.onNewTab )
|
|
1896
|
+
{
|
|
1897
|
+
this.onNewTab( e );
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
const dmOptions = this.newTabOptions ?? [
|
|
1860
1902
|
{ name: "Create file", icon: "FilePlus", callback: this._onCreateNewFile.bind( this ) },
|
|
1861
|
-
{ name: "Load file", icon: "FileUp", callback: this.loadTabFromFile.bind( this ) }
|
|
1862
|
-
]
|
|
1903
|
+
{ name: "Load file", icon: "FileUp", disabled: !this.allowLoadingFiles, callback: this.loadTabFromFile.bind( this ) }
|
|
1904
|
+
];
|
|
1905
|
+
|
|
1906
|
+
new LX.DropdownMenu( e.target, dmOptions, { side: "bottom", align: "start" });
|
|
1863
1907
|
}
|
|
1864
1908
|
|
|
1865
1909
|
_onCreateNewFile() {
|
|
@@ -1869,10 +1913,14 @@ class CodeEditor {
|
|
|
1869
1913
|
if( this.onCreateFile )
|
|
1870
1914
|
{
|
|
1871
1915
|
options = this.onCreateFile( this );
|
|
1916
|
+
if( !options ) // Skip adding new file
|
|
1917
|
+
{
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1872
1920
|
}
|
|
1873
1921
|
|
|
1874
1922
|
const name = options.name ?? "unnamed.js";
|
|
1875
|
-
this.addTab( name, true, name, { language: options.language ?? "JavaScript" } );
|
|
1923
|
+
this.addTab( name, true, name, { indexOffset: options.indexOffset, language: options.language ?? "JavaScript" } );
|
|
1876
1924
|
}
|
|
1877
1925
|
|
|
1878
1926
|
_onSelectTab( isNewTabButton, event, name ) {
|
|
@@ -1890,9 +1938,16 @@ class CodeEditor {
|
|
|
1890
1938
|
|
|
1891
1939
|
this._removeSecondaryCursors();
|
|
1892
1940
|
|
|
1893
|
-
|
|
1894
|
-
|
|
1941
|
+
const cursor = this.getCurrentCursor( true );
|
|
1942
|
+
const lastCode = this.code;
|
|
1943
|
+
|
|
1944
|
+
if( lastCode )
|
|
1945
|
+
{
|
|
1946
|
+
this.saveCursor( cursor, lastCode.cursorState );
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1895
1949
|
this.code = this.loadedTabs[ name ];
|
|
1950
|
+
|
|
1896
1951
|
this.restoreCursor( cursor, this.code.cursorState );
|
|
1897
1952
|
|
|
1898
1953
|
this.endSelection();
|
|
@@ -1911,6 +1966,11 @@ class CodeEditor {
|
|
|
1911
1966
|
}
|
|
1912
1967
|
|
|
1913
1968
|
this.processLines();
|
|
1969
|
+
|
|
1970
|
+
if( !isNewTabButton && this.onSelectTab )
|
|
1971
|
+
{
|
|
1972
|
+
this.onSelectTab( name, this );
|
|
1973
|
+
}
|
|
1914
1974
|
}
|
|
1915
1975
|
|
|
1916
1976
|
_onContextMenuTab( isNewTabButton, event, name, ) {
|
|
@@ -1921,19 +1981,19 @@ class CodeEditor {
|
|
|
1921
1981
|
}
|
|
1922
1982
|
|
|
1923
1983
|
new LX.DropdownMenu( event.target, [
|
|
1924
|
-
{ name: "Close", kbd: "MWB", callback: () => { this.
|
|
1925
|
-
{ name: "Close Others", callback: () => {
|
|
1984
|
+
{ name: "Close", kbd: "MWB", disabled: !this.allowClosingTabs, callback: () => { this.closeTab( name ) } },
|
|
1985
|
+
{ name: "Close Others", disabled: !this.allowClosingTabs, callback: () => {
|
|
1926
1986
|
for( const [ key, data ] of Object.entries( this.tabs.tabs ) )
|
|
1927
1987
|
{
|
|
1928
1988
|
if( key === '+' || key === name ) continue;
|
|
1929
|
-
this.
|
|
1989
|
+
this.closeTab( key )
|
|
1930
1990
|
}
|
|
1931
1991
|
} },
|
|
1932
|
-
{ name: "Close All", callback: () => {
|
|
1992
|
+
{ name: "Close All", disabled: !this.allowClosingTabs, callback: () => {
|
|
1933
1993
|
for( const [ key, data ] of Object.entries( this.tabs.tabs ) )
|
|
1934
1994
|
{
|
|
1935
1995
|
if( key === '+' ) continue;
|
|
1936
|
-
this.
|
|
1996
|
+
this.closeTab( key )
|
|
1937
1997
|
}
|
|
1938
1998
|
} },
|
|
1939
1999
|
null,
|
|
@@ -1998,6 +2058,10 @@ class CodeEditor {
|
|
|
1998
2058
|
this.loadedTabs[ name ] = code;
|
|
1999
2059
|
this.openedTabs[ name ] = code;
|
|
2000
2060
|
|
|
2061
|
+
const lastCode = this.code;
|
|
2062
|
+
|
|
2063
|
+
this.code = code;
|
|
2064
|
+
|
|
2001
2065
|
const tabIcon = this._getFileIcon( name );
|
|
2002
2066
|
|
|
2003
2067
|
if( this.useFileExplorer && !isNewTabButton )
|
|
@@ -2015,7 +2079,8 @@ class CodeEditor {
|
|
|
2015
2079
|
icon: tabIcon,
|
|
2016
2080
|
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
2017
2081
|
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton ),
|
|
2018
|
-
allowDelete:
|
|
2082
|
+
allowDelete: this.allowClosingTabs,
|
|
2083
|
+
indexOffset: options.indexOffset
|
|
2019
2084
|
} );
|
|
2020
2085
|
}
|
|
2021
2086
|
|
|
@@ -2024,20 +2089,29 @@ class CodeEditor {
|
|
|
2024
2089
|
|
|
2025
2090
|
this.endSelection();
|
|
2026
2091
|
|
|
2027
|
-
if(
|
|
2092
|
+
if( options.language )
|
|
2028
2093
|
{
|
|
2029
|
-
|
|
2030
|
-
this.
|
|
2094
|
+
code.languageOverride = options.language;
|
|
2095
|
+
this._changeLanguage( code.languageOverride );
|
|
2031
2096
|
this.mustProcessLines = true;
|
|
2032
2097
|
}
|
|
2033
2098
|
|
|
2034
|
-
if( options.
|
|
2099
|
+
if( options.codeLines )
|
|
2035
2100
|
{
|
|
2036
|
-
code.
|
|
2037
|
-
this._changeLanguage( code.languageOverride );
|
|
2101
|
+
code.lines = options.codeLines;
|
|
2038
2102
|
this.mustProcessLines = true;
|
|
2039
2103
|
}
|
|
2040
2104
|
|
|
2105
|
+
if( selected )
|
|
2106
|
+
{
|
|
2107
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
2108
|
+
this.mustProcessLines = true;
|
|
2109
|
+
}
|
|
2110
|
+
else
|
|
2111
|
+
{
|
|
2112
|
+
this.code = lastCode;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2041
2115
|
this._processLinesIfNecessary();
|
|
2042
2116
|
|
|
2043
2117
|
this._updateDataInfoPanel( "@tab-name", name );
|
|
@@ -2155,7 +2229,7 @@ class CodeEditor {
|
|
|
2155
2229
|
icon: tabIcon,
|
|
2156
2230
|
onSelect: this._onSelectTab.bind( this, isNewTabButton ),
|
|
2157
2231
|
onContextMenu: this._onContextMenuTab.bind( this, isNewTabButton ),
|
|
2158
|
-
allowDelete:
|
|
2232
|
+
allowDelete: this.allowClosingTabs
|
|
2159
2233
|
});
|
|
2160
2234
|
|
|
2161
2235
|
// Move into the sizer..
|
|
@@ -2173,6 +2247,11 @@ class CodeEditor {
|
|
|
2173
2247
|
|
|
2174
2248
|
closeTab( name, eraseAll ) {
|
|
2175
2249
|
|
|
2250
|
+
if( !this.allowClosingTabs )
|
|
2251
|
+
{
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2176
2255
|
this.tabs.delete( name );
|
|
2177
2256
|
|
|
2178
2257
|
if( eraseAll )
|
|
@@ -2290,22 +2369,65 @@ class CodeEditor {
|
|
|
2290
2369
|
e.preventDefault();
|
|
2291
2370
|
|
|
2292
2371
|
if( !this.canOpenContextMenu )
|
|
2372
|
+
{
|
|
2293
2373
|
return;
|
|
2374
|
+
}
|
|
2294
2375
|
|
|
2295
2376
|
LX.addContextMenu( null, e, m => {
|
|
2296
2377
|
m.add( "Copy", () => { this._copyContent( cursor ); } );
|
|
2378
|
+
|
|
2297
2379
|
if( !this.disableEdition )
|
|
2298
2380
|
{
|
|
2299
2381
|
m.add( "Cut", () => { this._cutContent( cursor ); } );
|
|
2300
2382
|
m.add( "Paste", () => { this._pasteContent( cursor ); } );
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
if( !this.onContextMenu )
|
|
2386
|
+
{
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
let content = null;
|
|
2391
|
+
|
|
2392
|
+
if( cursor.selection )
|
|
2393
|
+
{
|
|
2394
|
+
// Some selections don't depend on mouse up..
|
|
2395
|
+
if( cursor.selection ) cursor.selection.invertIfNecessary();
|
|
2396
|
+
|
|
2397
|
+
const separator = "_NEWLINE_";
|
|
2398
|
+
let code = this.code.lines.join( separator );
|
|
2399
|
+
|
|
2400
|
+
// Get linear start index
|
|
2401
|
+
let index = 0;
|
|
2402
|
+
|
|
2403
|
+
for( let i = 0; i <= cursor.selection.fromY; i++ )
|
|
2404
|
+
{
|
|
2405
|
+
index += ( i == cursor.selection.fromY ? cursor.selection.fromX : this.code.lines[ i ].length );
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
index += cursor.selection.fromY * separator.length;
|
|
2409
|
+
const num_chars = cursor.selection.chars + ( cursor.selection.toY - cursor.selection.fromY ) * separator.length;
|
|
2410
|
+
const text = code.substr( index, num_chars );
|
|
2411
|
+
content = text.split( separator ).join('\n');
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
const options = this.onContextMenu( this, content, e );
|
|
2415
|
+
if( options.length )
|
|
2416
|
+
{
|
|
2301
2417
|
m.add( "" );
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2418
|
+
|
|
2419
|
+
for( const o of options )
|
|
2420
|
+
{
|
|
2421
|
+
m.add( o.path, { disabled: o.disabled, callback: o.callback } );
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
// m.add( "Format/JSON", () => {
|
|
2425
|
+
// let json = this.toJSONFormat( this.getText() );
|
|
2426
|
+
// if( !json )
|
|
2427
|
+
// return;
|
|
2428
|
+
// this.code.lines = json.split( "\n" );
|
|
2429
|
+
// this.processLines();
|
|
2430
|
+
// } );
|
|
2309
2431
|
}
|
|
2310
2432
|
});
|
|
2311
2433
|
|
|
@@ -2638,10 +2760,17 @@ class CodeEditor {
|
|
|
2638
2760
|
e.preventDefault();
|
|
2639
2761
|
this.selectAll();
|
|
2640
2762
|
return true;
|
|
2763
|
+
case 'b': // k+b comment block
|
|
2764
|
+
e.preventDefault();
|
|
2765
|
+
if( this.state.keyChain == 'k' ) {
|
|
2766
|
+
this._commentLines( cursor, true );
|
|
2767
|
+
return true;
|
|
2768
|
+
}
|
|
2769
|
+
return false;
|
|
2641
2770
|
case 'c': // k+c, comment line
|
|
2642
2771
|
e.preventDefault();
|
|
2643
2772
|
if( this.state.keyChain == 'k' ) {
|
|
2644
|
-
this._commentLines();
|
|
2773
|
+
this._commentLines( cursor );
|
|
2645
2774
|
return true;
|
|
2646
2775
|
}
|
|
2647
2776
|
return false;
|
|
@@ -2663,12 +2792,12 @@ class CodeEditor {
|
|
|
2663
2792
|
return true;
|
|
2664
2793
|
case 's': // save
|
|
2665
2794
|
e.preventDefault();
|
|
2666
|
-
this.
|
|
2795
|
+
this.onSave( this.getText() );
|
|
2667
2796
|
return true;
|
|
2668
2797
|
case 'u': // k+u, uncomment line
|
|
2669
2798
|
e.preventDefault();
|
|
2670
2799
|
if( this.state.keyChain == 'k' ) {
|
|
2671
|
-
this._uncommentLines();
|
|
2800
|
+
this._uncommentLines( cursor );
|
|
2672
2801
|
return true;
|
|
2673
2802
|
}
|
|
2674
2803
|
return false;
|
|
@@ -2688,6 +2817,13 @@ class CodeEditor {
|
|
|
2688
2817
|
e.preventDefault();
|
|
2689
2818
|
this._decreaseFontSize();
|
|
2690
2819
|
return true;
|
|
2820
|
+
case ' ': // custom event
|
|
2821
|
+
if( this.onCtrlSpace )
|
|
2822
|
+
{
|
|
2823
|
+
e.preventDefault();
|
|
2824
|
+
this.onCtrlSpace( cursor );
|
|
2825
|
+
return true;
|
|
2826
|
+
}
|
|
2691
2827
|
}
|
|
2692
2828
|
}
|
|
2693
2829
|
|
|
@@ -2981,32 +3117,77 @@ class CodeEditor {
|
|
|
2981
3117
|
this.hideAutoCompleteBox();
|
|
2982
3118
|
}
|
|
2983
3119
|
|
|
2984
|
-
_commentLines() {
|
|
3120
|
+
_commentLines( cursor, useCommentBlock ) {
|
|
3121
|
+
|
|
3122
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
2985
3123
|
|
|
2986
3124
|
this.state.keyChain = null;
|
|
2987
3125
|
|
|
3126
|
+
cursor = cursor ?? this.getCurrentCursor();
|
|
3127
|
+
|
|
2988
3128
|
if( cursor.selection )
|
|
2989
3129
|
{
|
|
2990
|
-
|
|
3130
|
+
if( !( ( useCommentBlock ? lang.blockComments : lang.singleLineComments ) ?? true ) )
|
|
3131
|
+
{
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
|
|
2991
3135
|
this._addUndoStep( cursor, true );
|
|
2992
3136
|
|
|
2993
|
-
const selectedLines = this.code.lines.slice( cursor.selection.fromY, cursor.selection.toY );
|
|
3137
|
+
const selectedLines = this.code.lines.slice( cursor.selection.fromY, cursor.selection.toY + 1 );
|
|
2994
3138
|
const minIdx = Math.min(...selectedLines.map( v => {
|
|
2995
3139
|
var idx = firstNonspaceIndex( v );
|
|
2996
3140
|
return idx < 0 ? 1e10 : idx;
|
|
2997
3141
|
} ));
|
|
2998
3142
|
|
|
2999
|
-
|
|
3143
|
+
if( useCommentBlock )
|
|
3000
3144
|
{
|
|
3001
|
-
|
|
3145
|
+
const tokens = ( lang.blockCommentsTokens ?? this.defaultBlockCommentTokens );
|
|
3146
|
+
|
|
3147
|
+
const fromString = this.code.lines[ cursor.selection.fromY ];
|
|
3148
|
+
let fromIdx = firstNonspaceIndex( fromString );
|
|
3149
|
+
if( fromIdx == -1 )
|
|
3150
|
+
{
|
|
3151
|
+
fromIdx = 0;
|
|
3152
|
+
}
|
|
3153
|
+
|
|
3154
|
+
this.code.lines[ cursor.selection.fromY ] = [
|
|
3155
|
+
fromString.substring( 0, fromIdx ),
|
|
3156
|
+
tokens[ 0 ] + " ",
|
|
3157
|
+
fromString.substring( fromIdx )
|
|
3158
|
+
].join( '' );
|
|
3159
|
+
|
|
3160
|
+
this.code.lines[ cursor.selection.toY ] += " " + tokens[ 1 ];
|
|
3161
|
+
|
|
3162
|
+
cursor.selection.fromX += ( tokens[ 0 ].length + 1 );
|
|
3163
|
+
this._processSelection( cursor );
|
|
3164
|
+
}
|
|
3165
|
+
else
|
|
3166
|
+
{
|
|
3167
|
+
for( let i = cursor.selection.fromY; i <= cursor.selection.toY; ++i )
|
|
3168
|
+
{
|
|
3169
|
+
this._commentLine( cursor, i, minIdx, false );
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
const token = ( lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken ) + ' ';
|
|
3173
|
+
this.cursorToString( cursor, token );
|
|
3174
|
+
|
|
3175
|
+
cursor.selection.fromX += token.length;
|
|
3176
|
+
cursor.selection.toX += token.length;
|
|
3177
|
+
this._processSelection( cursor );
|
|
3002
3178
|
}
|
|
3003
3179
|
}
|
|
3004
3180
|
else
|
|
3005
3181
|
{
|
|
3006
|
-
|
|
3182
|
+
if( !( lang.singleLineComments ?? true ) )
|
|
3007
3183
|
{
|
|
3008
|
-
|
|
3009
|
-
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
for( const cr of this.cursors.children )
|
|
3188
|
+
{
|
|
3189
|
+
this._addUndoStep( cr, true );
|
|
3190
|
+
this._commentLine( cr, cr.line );
|
|
3010
3191
|
}
|
|
3011
3192
|
}
|
|
3012
3193
|
|
|
@@ -3014,33 +3195,37 @@ class CodeEditor {
|
|
|
3014
3195
|
this._hideActiveLine();
|
|
3015
3196
|
}
|
|
3016
3197
|
|
|
3017
|
-
_commentLine( cursor, line, minNonspaceIdx ) {
|
|
3198
|
+
_commentLine( cursor, line, minNonspaceIdx, updateCursor = true ) {
|
|
3018
3199
|
|
|
3019
3200
|
const lang = CodeEditor.languages[ this.highlight ];
|
|
3020
|
-
|
|
3021
|
-
if( !( lang.singleLineComments ?? true ))
|
|
3201
|
+
if( !( lang.singleLineComments ?? true ) )
|
|
3022
3202
|
return;
|
|
3023
3203
|
|
|
3024
3204
|
const token = ( lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken ) + ' ';
|
|
3025
3205
|
const string = this.code.lines[ line ];
|
|
3026
3206
|
|
|
3027
3207
|
let idx = firstNonspaceIndex( string );
|
|
3028
|
-
if( idx
|
|
3208
|
+
if( idx == -1 )
|
|
3029
3209
|
{
|
|
3030
|
-
|
|
3031
|
-
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3032
3212
|
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3213
|
+
// Update idx using min of the selected lines (if necessary..)
|
|
3214
|
+
idx = minNonspaceIdx ?? idx;
|
|
3215
|
+
|
|
3216
|
+
this.code.lines[ line ] = [
|
|
3217
|
+
string.substring( 0, idx ),
|
|
3218
|
+
token,
|
|
3219
|
+
string.substring( idx )
|
|
3220
|
+
].join( '' );
|
|
3038
3221
|
|
|
3222
|
+
if( updateCursor )
|
|
3223
|
+
{
|
|
3039
3224
|
this.cursorToString( cursor, token );
|
|
3040
3225
|
}
|
|
3041
3226
|
}
|
|
3042
3227
|
|
|
3043
|
-
_uncommentLines() {
|
|
3228
|
+
_uncommentLines( cursor ) {
|
|
3044
3229
|
|
|
3045
3230
|
this.state.keyChain = null;
|
|
3046
3231
|
|
|
@@ -4002,7 +4187,7 @@ class CodeEditor {
|
|
|
4002
4187
|
const customStringKeys = Object.assign( {}, this.stringKeys );
|
|
4003
4188
|
const lineNumber = this._currentLineNumber;
|
|
4004
4189
|
const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];
|
|
4005
|
-
const inBlockComment = ( this._buildingBlockComment ?? this._inBlockCommentSection( lineNumber, tokenStartIndex, token.length ) !== undefined
|
|
4190
|
+
const inBlockComment = ( this._buildingBlockComment ?? this._inBlockCommentSection( lineNumber, tokenStartIndex, token.length ) ) !== undefined;
|
|
4006
4191
|
|
|
4007
4192
|
var usePreviousTokenToCheckString = false;
|
|
4008
4193
|
|
|
@@ -4619,6 +4804,8 @@ class CodeEditor {
|
|
|
4619
4804
|
|
|
4620
4805
|
if( state.selection )
|
|
4621
4806
|
{
|
|
4807
|
+
this.endSelection();
|
|
4808
|
+
|
|
4622
4809
|
this.startSelection( cursor );
|
|
4623
4810
|
|
|
4624
4811
|
cursor.selection.load( state.selection );
|
|
@@ -4899,7 +5086,10 @@ class CodeEditor {
|
|
|
4899
5086
|
}
|
|
4900
5087
|
else
|
|
4901
5088
|
{
|
|
4902
|
-
|
|
5089
|
+
if( value )
|
|
5090
|
+
{
|
|
5091
|
+
this.codeScroller.scrollLeft += value;
|
|
5092
|
+
}
|
|
4903
5093
|
|
|
4904
5094
|
const scrollBarWidth = this.hScrollbar.thumb.parentElement.offsetWidth;
|
|
4905
5095
|
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
@@ -5109,6 +5299,9 @@ class CodeEditor {
|
|
|
5109
5299
|
suggestions = suggestions.concat( otherValues.slice( 0, -1 ) );
|
|
5110
5300
|
}
|
|
5111
5301
|
|
|
5302
|
+
// Add custom suggestions...
|
|
5303
|
+
suggestions = suggestions.concat( this.customSuggestions );
|
|
5304
|
+
|
|
5112
5305
|
// Remove 1/2 char words and duplicates...
|
|
5113
5306
|
suggestions = Array.from( new Set( suggestions )).filter( s => s.length > 2 && s.toLowerCase().includes( word.toLowerCase() ) );
|
|
5114
5307
|
|