lexgui 0.7.3 → 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 +468 -156
- package/build/lexgui.css +9 -0
- package/build/lexgui.js +13 -1
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +13 -1
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +12 -1
- package/examples/code-editor.html +29 -19
- package/package.json +1 -1
|
@@ -151,7 +151,7 @@ class ScrollBar {
|
|
|
151
151
|
this.type = type;
|
|
152
152
|
|
|
153
153
|
this.root = document.createElement( 'div' );
|
|
154
|
-
this.root.className = "lexcodescrollbar";
|
|
154
|
+
this.root.className = "lexcodescrollbar hidden";
|
|
155
155
|
|
|
156
156
|
if( type & ScrollBar.SCROLLBAR_VERTICAL )
|
|
157
157
|
this.root.classList.add( 'vertical' );
|
|
@@ -966,11 +966,14 @@ class CodeEditor {
|
|
|
966
966
|
var spaces = firstNonspaceIndex( this.code.lines[ ln ]);
|
|
967
967
|
var tabs = Math.floor( spaces / this.tabSpaces );
|
|
968
968
|
|
|
969
|
-
if( _c0 == '{' && _c1 == '}' )
|
|
969
|
+
if( _c0 == '{' && _c1 == '}' )
|
|
970
|
+
{
|
|
970
971
|
this.code.lines.splice( cursor.line, 0, "" );
|
|
971
972
|
this._addSpaceTabs( cursor, tabs + 1 );
|
|
972
973
|
this.code.lines[ cursor.line + 1 ] = " ".repeat(spaces) + this.code.lines[ cursor.line + 1 ];
|
|
973
|
-
}
|
|
974
|
+
}
|
|
975
|
+
else
|
|
976
|
+
{
|
|
974
977
|
this._addSpaceTabs( cursor, tabs );
|
|
975
978
|
}
|
|
976
979
|
|
|
@@ -1206,9 +1209,10 @@ class CodeEditor {
|
|
|
1206
1209
|
this.openedTabs = { };
|
|
1207
1210
|
|
|
1208
1211
|
const onLoadAll = () => {
|
|
1212
|
+
|
|
1209
1213
|
// Create inspector panel when the initial state is complete
|
|
1210
1214
|
// and we have at least 1 tab opened
|
|
1211
|
-
this.statusPanel = this._createStatusPanel();
|
|
1215
|
+
this.statusPanel = this._createStatusPanel( options );
|
|
1212
1216
|
if( this.statusPanel )
|
|
1213
1217
|
{
|
|
1214
1218
|
area.attach( this.statusPanel );
|
|
@@ -1240,10 +1244,10 @@ class CodeEditor {
|
|
|
1240
1244
|
|
|
1241
1245
|
this.gutter.style.marginTop = `${ this._verticalTopOffset }px`;
|
|
1242
1246
|
this.gutter.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1243
|
-
this.vScrollbar.root.style.marginTop = `${ this.
|
|
1247
|
+
this.vScrollbar.root.style.marginTop = `${ this._verticalTopOffset }px`;
|
|
1244
1248
|
this.vScrollbar.root.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1245
1249
|
this.hScrollbar.root.style.bottom = `${ this._verticalBottomOffset }px`;
|
|
1246
|
-
this.codeArea.root.style.height = `calc(100% - ${ this.
|
|
1250
|
+
this.codeArea.root.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1247
1251
|
}, 50 );
|
|
1248
1252
|
|
|
1249
1253
|
});
|
|
@@ -1264,11 +1268,16 @@ class CodeEditor {
|
|
|
1264
1268
|
|
|
1265
1269
|
for( let url of options.files )
|
|
1266
1270
|
{
|
|
1267
|
-
this.loadFile( url, { callback: () => {
|
|
1271
|
+
this.loadFile( url, { callback: ( name, text ) => {
|
|
1268
1272
|
filesLoaded++;
|
|
1269
1273
|
if( filesLoaded == numFiles )
|
|
1270
1274
|
{
|
|
1271
1275
|
onLoadAll();
|
|
1276
|
+
|
|
1277
|
+
if( options.onFilesLoaded )
|
|
1278
|
+
{
|
|
1279
|
+
options.onFilesLoaded( this, numFiles );
|
|
1280
|
+
}
|
|
1272
1281
|
}
|
|
1273
1282
|
}});
|
|
1274
1283
|
}
|
|
@@ -1322,12 +1331,15 @@ class CodeEditor {
|
|
|
1322
1331
|
|
|
1323
1332
|
this.cursorToLine( cursor, newLines.length ); // Already substracted 1
|
|
1324
1333
|
this.cursorToPosition( cursor, lastLine.length );
|
|
1325
|
-
|
|
1334
|
+
|
|
1335
|
+
this.mustProcessLines = true;
|
|
1326
1336
|
|
|
1327
1337
|
if( lang )
|
|
1328
1338
|
{
|
|
1329
1339
|
this._changeLanguage( lang );
|
|
1330
1340
|
}
|
|
1341
|
+
|
|
1342
|
+
this._processLinesIfNecessary();
|
|
1331
1343
|
}
|
|
1332
1344
|
|
|
1333
1345
|
appendText( text, cursor ) {
|
|
@@ -1401,7 +1413,7 @@ class CodeEditor {
|
|
|
1401
1413
|
} );
|
|
1402
1414
|
}
|
|
1403
1415
|
|
|
1404
|
-
loadFile( file, options = {} ) {
|
|
1416
|
+
async loadFile( file, options = {} ) {
|
|
1405
1417
|
|
|
1406
1418
|
const _innerAddTab = ( text, name, title ) => {
|
|
1407
1419
|
|
|
@@ -1429,10 +1441,6 @@ class CodeEditor {
|
|
|
1429
1441
|
this.addExplorerItem( { id: name, skipVisibility: true, icon: this._getFileIcon( name, ext ) } );
|
|
1430
1442
|
this.explorer.innerTree.frefresh( name );
|
|
1431
1443
|
}
|
|
1432
|
-
else
|
|
1433
|
-
{
|
|
1434
|
-
|
|
1435
|
-
}
|
|
1436
1444
|
}
|
|
1437
1445
|
else
|
|
1438
1446
|
{
|
|
@@ -1448,17 +1456,19 @@ class CodeEditor {
|
|
|
1448
1456
|
|
|
1449
1457
|
if( options.callback )
|
|
1450
1458
|
{
|
|
1451
|
-
options.callback( text );
|
|
1459
|
+
options.callback( name, text );
|
|
1452
1460
|
}
|
|
1453
1461
|
};
|
|
1454
1462
|
|
|
1455
1463
|
if( file.constructor == String )
|
|
1456
1464
|
{
|
|
1457
1465
|
let filename = file;
|
|
1466
|
+
|
|
1458
1467
|
LX.request({ url: filename, success: text => {
|
|
1459
1468
|
const name = filename.substring(filename.lastIndexOf( '/' ) + 1);
|
|
1460
1469
|
_innerAddTab( text, name, filename );
|
|
1461
1470
|
} });
|
|
1471
|
+
|
|
1462
1472
|
}
|
|
1463
1473
|
else // File Blob
|
|
1464
1474
|
{
|
|
@@ -1598,7 +1608,8 @@ class CodeEditor {
|
|
|
1598
1608
|
}
|
|
1599
1609
|
|
|
1600
1610
|
this._updateDataInfoPanel( "@highlight", lang );
|
|
1601
|
-
|
|
1611
|
+
|
|
1612
|
+
this.mustProcessLines = true;
|
|
1602
1613
|
|
|
1603
1614
|
const ext = langExtension ?? CodeEditor.languages[ lang ].ext;
|
|
1604
1615
|
const icon = this._getFileIcon( null, ext );
|
|
@@ -1663,7 +1674,7 @@ class CodeEditor {
|
|
|
1663
1674
|
this._changeLanguage( 'Plain Text' );
|
|
1664
1675
|
}
|
|
1665
1676
|
|
|
1666
|
-
_createStatusPanel() {
|
|
1677
|
+
_createStatusPanel( options ) {
|
|
1667
1678
|
|
|
1668
1679
|
if( this.skipInfo )
|
|
1669
1680
|
{
|
|
@@ -1688,7 +1699,7 @@ class CodeEditor {
|
|
|
1688
1699
|
|
|
1689
1700
|
let rightStatusPanel = new LX.Panel( { height: "auto" } );
|
|
1690
1701
|
rightStatusPanel.sameLine();
|
|
1691
|
-
rightStatusPanel.addLabel( this.code
|
|
1702
|
+
rightStatusPanel.addLabel( this.code?.title ?? "", { id: "EditorFilenameStatusComponent", fit: true, signal: "@tab-name" });
|
|
1692
1703
|
rightStatusPanel.addButton( null, "Ln 1, Col 1", this.showSearchLineBox.bind( this ), { id: "EditorSelectionStatusComponent", fit: true, signal: "@cursor-data" });
|
|
1693
1704
|
rightStatusPanel.addButton( null, "Spaces: " + this.tabSpaces, ( value, event ) => {
|
|
1694
1705
|
LX.addContextMenu( "Spaces", event, m => {
|
|
@@ -1715,13 +1726,24 @@ class CodeEditor {
|
|
|
1715
1726
|
panel.attach( rightStatusPanel.root );
|
|
1716
1727
|
|
|
1717
1728
|
const itemVisibilityMap = {
|
|
1718
|
-
"Font Size Zoom": true,
|
|
1719
|
-
"Editor Filename": true,
|
|
1720
|
-
"Editor Selection": true,
|
|
1721
|
-
"Editor Indentation": true,
|
|
1722
|
-
"Editor Language": true,
|
|
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,
|
|
1723
1734
|
};
|
|
1724
1735
|
|
|
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 ] );
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
for( const [ itemName, v ] of Object.entries( itemVisibilityMap ) )
|
|
1743
|
+
{
|
|
1744
|
+
_setVisibility( itemName );
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1725
1747
|
panel.root.addEventListener( "contextmenu", (e) => {
|
|
1726
1748
|
|
|
1727
1749
|
if( e.target && ( e.target.classList.contains( "lexpanel" ) || e.target.classList.contains( "lexinlinecomponents" ) ) )
|
|
@@ -1735,9 +1757,7 @@ class CodeEditor {
|
|
|
1735
1757
|
icon: "Check",
|
|
1736
1758
|
callback: () => {
|
|
1737
1759
|
itemVisibilityMap[ itemName ] = !itemVisibilityMap[ itemName ];
|
|
1738
|
-
|
|
1739
|
-
console.assert( b, `${ itemName } has no status button!` );
|
|
1740
|
-
b.classList.toggle( "hidden", !itemVisibilityMap[ itemName ] );
|
|
1760
|
+
_setVisibility( itemName );
|
|
1741
1761
|
}
|
|
1742
1762
|
}
|
|
1743
1763
|
if( !itemVisibilityMap[ itemName ] ) delete item.icon;
|
|
@@ -1879,20 +1899,24 @@ class CodeEditor {
|
|
|
1879
1899
|
|
|
1880
1900
|
// Create code content
|
|
1881
1901
|
let code = document.createElement( 'div' );
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
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",
|
|
1896
1920
|
|
|
1897
1921
|
code.addEventListener( 'dragenter', function(e) {
|
|
1898
1922
|
e.preventDefault();
|
|
@@ -1942,15 +1966,18 @@ class CodeEditor {
|
|
|
1942
1966
|
{
|
|
1943
1967
|
this.code = code;
|
|
1944
1968
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
1945
|
-
this.
|
|
1969
|
+
this.mustProcessLines = true;
|
|
1946
1970
|
}
|
|
1947
1971
|
|
|
1948
1972
|
if( options.language )
|
|
1949
1973
|
{
|
|
1950
1974
|
code.languageOverride = options.language;
|
|
1951
1975
|
this._changeLanguage( code.languageOverride );
|
|
1976
|
+
this.mustProcessLines = true;
|
|
1952
1977
|
}
|
|
1953
1978
|
|
|
1979
|
+
this._processLinesIfNecessary();
|
|
1980
|
+
|
|
1954
1981
|
this._updateDataInfoPanel( "@tab-name", name );
|
|
1955
1982
|
|
|
1956
1983
|
// Bc it could be overrided..
|
|
@@ -1993,6 +2020,8 @@ class CodeEditor {
|
|
|
1993
2020
|
delete this._tabStorage[ name ];
|
|
1994
2021
|
}
|
|
1995
2022
|
|
|
2023
|
+
this._processLinesIfNecessary();
|
|
2024
|
+
|
|
1996
2025
|
return;
|
|
1997
2026
|
}
|
|
1998
2027
|
|
|
@@ -2005,9 +2034,12 @@ class CodeEditor {
|
|
|
2005
2034
|
|
|
2006
2035
|
// Select as current...
|
|
2007
2036
|
this.code = code;
|
|
2037
|
+
this.mustProcessLines = true;
|
|
2038
|
+
|
|
2008
2039
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
2009
2040
|
this.processLines();
|
|
2010
2041
|
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
2042
|
+
this._processLinesIfNecessary();
|
|
2011
2043
|
this._updateDataInfoPanel( "@tab-name", code.tabName );
|
|
2012
2044
|
}
|
|
2013
2045
|
|
|
@@ -2044,6 +2076,8 @@ class CodeEditor {
|
|
|
2044
2076
|
delete this._tabStorage[ name ];
|
|
2045
2077
|
}
|
|
2046
2078
|
|
|
2079
|
+
this._processLinesIfNecessary();
|
|
2080
|
+
|
|
2047
2081
|
return;
|
|
2048
2082
|
}
|
|
2049
2083
|
|
|
@@ -3029,10 +3063,24 @@ class CodeEditor {
|
|
|
3029
3063
|
return Math.max(...this.code.lines.map( v => v.length ));
|
|
3030
3064
|
}
|
|
3031
3065
|
|
|
3066
|
+
_processLinesIfNecessary() {
|
|
3067
|
+
if( this.mustProcessLines )
|
|
3068
|
+
{
|
|
3069
|
+
this.mustProcessLines = false;
|
|
3070
|
+
this.processLines();
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3032
3074
|
processLines( mode ) {
|
|
3033
3075
|
|
|
3076
|
+
if( !this.code )
|
|
3077
|
+
{
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3034
3081
|
var htmlCode = "";
|
|
3035
3082
|
this._blockCommentCache.length = 0;
|
|
3083
|
+
this.mustProcessLines = false;
|
|
3036
3084
|
|
|
3037
3085
|
// Reset all lines content
|
|
3038
3086
|
this.code.innerHTML = "";
|
|
@@ -3080,13 +3128,6 @@ class CodeEditor {
|
|
|
3080
3128
|
|
|
3081
3129
|
processLine( lineNumber, force, skipPropagation ) {
|
|
3082
3130
|
|
|
3083
|
-
// Check if we are in block comment sections..
|
|
3084
|
-
if( !force && this._inBlockCommentSection( lineNumber ) )
|
|
3085
|
-
{
|
|
3086
|
-
this.processLines();
|
|
3087
|
-
return;
|
|
3088
|
-
}
|
|
3089
|
-
|
|
3090
3131
|
if( this._scopeStack )
|
|
3091
3132
|
{
|
|
3092
3133
|
this.code.lineScopes[ lineNumber ] = [ ...this._scopeStack ];
|
|
@@ -3100,6 +3141,12 @@ class CodeEditor {
|
|
|
3100
3141
|
const lang = CodeEditor.languages[ this.highlight ];
|
|
3101
3142
|
const localLineNum = this.toLocalLine( lineNumber );
|
|
3102
3143
|
const lineString = this.code.lines[ lineNumber ];
|
|
3144
|
+
if( lineString === undefined )
|
|
3145
|
+
{
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
this._lastProcessedLine = lineNumber;
|
|
3103
3150
|
|
|
3104
3151
|
// multi-line strings not supported by now
|
|
3105
3152
|
delete this._buildingString;
|
|
@@ -3132,6 +3179,18 @@ class CodeEditor {
|
|
|
3132
3179
|
let lineInnerHtml = "";
|
|
3133
3180
|
let pushedScope = false;
|
|
3134
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" } ];
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3135
3194
|
// Process all tokens
|
|
3136
3195
|
for( let i = 0; i < tokensToEvaluate.length; ++i )
|
|
3137
3196
|
{
|
|
@@ -3152,34 +3211,44 @@ class CodeEditor {
|
|
|
3152
3211
|
}
|
|
3153
3212
|
|
|
3154
3213
|
const token = tokensToEvaluate[ i ];
|
|
3214
|
+
const tokenIndex = i;
|
|
3215
|
+
const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];;
|
|
3155
3216
|
|
|
3156
|
-
if(
|
|
3217
|
+
if( blockComments )
|
|
3157
3218
|
{
|
|
3158
|
-
|
|
3159
|
-
if( token.substr( 0, blockCommentsToken.length ) == blockCommentsToken )
|
|
3219
|
+
if( token.substr( 0, blockCommentsTokens[ 0 ].length ) == blockCommentsTokens[ 0 ] )
|
|
3160
3220
|
{
|
|
3161
|
-
this._buildingBlockComment = lineNumber;
|
|
3221
|
+
this._buildingBlockComment = [ lineNumber, tokenStartIndex ];
|
|
3162
3222
|
}
|
|
3163
3223
|
}
|
|
3164
3224
|
|
|
3165
|
-
//
|
|
3225
|
+
// Compare line signature for structural changes
|
|
3226
|
+
// to pop current scope if necessary
|
|
3166
3227
|
if( token === "}" && this._scopeStack.length > 1 )
|
|
3167
3228
|
{
|
|
3168
3229
|
this._scopeStack.pop();
|
|
3169
3230
|
}
|
|
3170
3231
|
|
|
3171
3232
|
lineInnerHtml += this._evaluateToken( {
|
|
3172
|
-
token
|
|
3173
|
-
prev
|
|
3233
|
+
token,
|
|
3234
|
+
prev,
|
|
3174
3235
|
prevWithSpaces: tokensToEvaluate[ i - 1 ],
|
|
3175
|
-
next
|
|
3236
|
+
next,
|
|
3176
3237
|
nextWithSpaces: tokensToEvaluate[ i + 1 ],
|
|
3177
|
-
tokenIndex
|
|
3178
|
-
isFirstToken: (
|
|
3179
|
-
isLastToken: (
|
|
3238
|
+
tokenIndex,
|
|
3239
|
+
isFirstToken: ( tokenIndex == 0 ),
|
|
3240
|
+
isLastToken: ( tokenIndex == tokensToEvaluate.length - 1 ),
|
|
3180
3241
|
tokens: tokensToEvaluate
|
|
3181
3242
|
} );
|
|
3182
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
|
+
|
|
3183
3252
|
if( token !== "{" )
|
|
3184
3253
|
{
|
|
3185
3254
|
continue;
|
|
@@ -3187,26 +3256,39 @@ class CodeEditor {
|
|
|
3187
3256
|
|
|
3188
3257
|
// Store current scopes
|
|
3189
3258
|
|
|
3190
|
-
// Get some context about the scope from previous lines
|
|
3191
3259
|
let contextTokens = [
|
|
3192
|
-
...this._getTokensFromLine( this.code.lines[ lineNumber ].substring( 0,
|
|
3260
|
+
...this._getTokensFromLine( this.code.lines[ lineNumber ].substring( 0, tokenStartIndex ) )
|
|
3193
3261
|
];
|
|
3194
3262
|
|
|
3195
|
-
|
|
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 )
|
|
3196
3266
|
{
|
|
3197
|
-
let
|
|
3198
|
-
if( !kLineString ) break;
|
|
3199
|
-
const closeIdx = kLineString.lastIndexOf( '}' );
|
|
3200
|
-
if( closeIdx > -1 )
|
|
3267
|
+
for( let k = 1; k < 50; k++ )
|
|
3201
3268
|
{
|
|
3202
|
-
kLineString =
|
|
3203
|
-
|
|
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
|
+
}
|
|
3204
3285
|
|
|
3205
|
-
|
|
3286
|
+
contextTokens = [ ...this._getTokensFromLine( kLineString ), ...contextTokens ];
|
|
3206
3287
|
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3288
|
+
if( kLineString.length !== this.code.lines[ lineNumber - k ] )
|
|
3289
|
+
{
|
|
3290
|
+
break;
|
|
3291
|
+
}
|
|
3210
3292
|
}
|
|
3211
3293
|
}
|
|
3212
3294
|
|
|
@@ -3249,28 +3331,111 @@ class CodeEditor {
|
|
|
3249
3331
|
}
|
|
3250
3332
|
}
|
|
3251
3333
|
|
|
3252
|
-
if
|
|
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 )
|
|
3253
3337
|
{
|
|
3254
|
-
this._scopeStack.push( { name: scopeName ?? "", type: scopeType } );
|
|
3255
|
-
}
|
|
3256
|
-
else
|
|
3257
|
-
{
|
|
3258
|
-
this._scopeStack.push( { name: "", type: "anonymous" } ); // anonymous scope
|
|
3338
|
+
this._scopeStack.push( { name: scopeName ?? "", type: scopeType ?? "anonymous", lineNumber } );
|
|
3259
3339
|
}
|
|
3260
3340
|
|
|
3261
3341
|
pushedScope = true;
|
|
3262
3342
|
}
|
|
3263
3343
|
|
|
3344
|
+
// Update scopes cache
|
|
3345
|
+
this.code.lineScopes[ lineNumber ] = [ ...this._scopeStack ];
|
|
3346
|
+
|
|
3264
3347
|
const symbols = this._parseLineForSymbols( lineNumber, lineString, tokensToEvaluate, pushedScope );
|
|
3265
|
-
|
|
3348
|
+
|
|
3349
|
+
return this._updateLine( force, lineNumber, lineInnerHtml, skipPropagation, symbols, tokensToEvaluate );
|
|
3266
3350
|
}
|
|
3267
3351
|
|
|
3268
|
-
|
|
3352
|
+
_getLineSignatureFromTokens( tokens ) {
|
|
3353
|
+
const structuralChars = new Set( [ '{', '}'] );
|
|
3354
|
+
const sign = tokens.filter( t => structuralChars.has( t ) );
|
|
3355
|
+
return sign.join( "_" );
|
|
3356
|
+
}
|
|
3269
3357
|
|
|
3270
|
-
|
|
3358
|
+
_updateBlockComments( section, lineNumber, tokens ) {
|
|
3359
|
+
|
|
3360
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
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 )
|
|
3271
3374
|
{
|
|
3272
|
-
|
|
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..
|
|
3273
3435
|
}
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
_processExtraLineIfNecessary( lineNumber, tokens, oldSymbols, skipPropagation ) {
|
|
3274
3439
|
|
|
3275
3440
|
if( !this._scopeStack )
|
|
3276
3441
|
{
|
|
@@ -3278,11 +3443,45 @@ class CodeEditor {
|
|
|
3278
3443
|
return;
|
|
3279
3444
|
}
|
|
3280
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
|
+
|
|
3281
3468
|
// Only update scope stack if something changed when editing a single line
|
|
3282
|
-
|
|
3469
|
+
// Compare line signature for structural changes
|
|
3470
|
+
if( ( mustUpdateScopes || this._scopesUpdated ) && ( !sameScopes && !skipPropagation ) )
|
|
3283
3471
|
{
|
|
3284
|
-
|
|
3472
|
+
if( mustUpdateScopes )
|
|
3473
|
+
{
|
|
3474
|
+
this._scopesUpdated = true;
|
|
3475
|
+
}
|
|
3285
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
|
|
3286
3485
|
for( const sym of oldSymbols )
|
|
3287
3486
|
{
|
|
3288
3487
|
const tableSymbol = this.code.symbolsTable.get( sym.name );
|
|
@@ -3301,20 +3500,15 @@ class CodeEditor {
|
|
|
3301
3500
|
this.processLine( occ.line, false, true );
|
|
3302
3501
|
}
|
|
3303
3502
|
}
|
|
3304
|
-
|
|
3305
|
-
return;
|
|
3306
3503
|
}
|
|
3307
|
-
|
|
3308
|
-
this.code.lineScopes[ lineNumber + 1 ] = [ ...this._scopeStack ];
|
|
3309
|
-
this.processLine( lineNumber + 1 );
|
|
3310
3504
|
}
|
|
3311
3505
|
|
|
3312
|
-
_updateLine( force, lineNumber, html, skipPropagation, symbols = [] ) {
|
|
3506
|
+
_updateLine( force, lineNumber, html, skipPropagation, symbols = [], tokens = [] ) {
|
|
3313
3507
|
|
|
3314
3508
|
const gutterLineHtml = `<span class='line-gutter'>${ lineNumber + 1 }</span>`;
|
|
3315
3509
|
const oldSymbols = this._updateLineSymbols( lineNumber, symbols );
|
|
3316
|
-
const lineScope = CodeEditor.debugScopes ? this.code.lineScopes[ lineNumber ].map( s => `${ s.type }` ).join( ", " ) : "";
|
|
3317
|
-
const lineSymbols = CodeEditor.debugSymbols ? this.code.lineSymbols[ lineNumber ].map( s => `${ s.name }(${ s.kind })` ).join( ", " ) : "";
|
|
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( ", " ) : "";
|
|
3318
3512
|
const debugString = lineScope + ( lineScope.length ? " - " : "" ) + lineSymbols;
|
|
3319
3513
|
|
|
3320
3514
|
if( !force ) // Single line update
|
|
@@ -3323,17 +3517,46 @@ class CodeEditor {
|
|
|
3323
3517
|
|
|
3324
3518
|
if( !skipPropagation )
|
|
3325
3519
|
{
|
|
3326
|
-
this._processExtraLineIfNecessary( lineNumber, oldSymbols );
|
|
3520
|
+
this._processExtraLineIfNecessary( lineNumber, tokens, oldSymbols, skipPropagation );
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
if( this.mustProcessNextLine )
|
|
3524
|
+
{
|
|
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" );
|
|
3327
3550
|
}
|
|
3328
3551
|
|
|
3329
3552
|
this._setActiveLine( lineNumber );
|
|
3330
3553
|
this._clearTmpVariables();
|
|
3331
3554
|
}
|
|
3332
|
-
else // Update all lines at once
|
|
3333
|
-
{
|
|
3334
3555
|
|
|
3335
|
-
|
|
3336
|
-
|
|
3556
|
+
this.code.lineSignatures[ lineNumber ] = this._getLineSignatureFromTokens( tokens );
|
|
3557
|
+
|
|
3558
|
+
// Update all lines at once
|
|
3559
|
+
return force ? `<pre>${ ( gutterLineHtml + html + debugString ) }</pre>` : undefined;
|
|
3337
3560
|
}
|
|
3338
3561
|
|
|
3339
3562
|
/**
|
|
@@ -3343,7 +3566,7 @@ class CodeEditor {
|
|
|
3343
3566
|
|
|
3344
3567
|
const scope = this._scopeStack.at( pushedScope ? -2 : -1 );
|
|
3345
3568
|
|
|
3346
|
-
if( !scope )
|
|
3569
|
+
if( !scope || this._inBlockCommentSection( lineNumber ) )
|
|
3347
3570
|
{
|
|
3348
3571
|
return [];
|
|
3349
3572
|
}
|
|
@@ -3351,7 +3574,19 @@ class CodeEditor {
|
|
|
3351
3574
|
const scopeName = scope.name;
|
|
3352
3575
|
const scopeType = scope.type;
|
|
3353
3576
|
const symbols = [];
|
|
3577
|
+
const symbolsMap = new Map();
|
|
3354
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
|
+
{
|
|
3585
|
+
return;
|
|
3586
|
+
}
|
|
3587
|
+
symbolsMap.set( signature, s );
|
|
3588
|
+
symbols.push( s );
|
|
3589
|
+
};
|
|
3355
3590
|
|
|
3356
3591
|
// Don't make symbols from preprocessor lines
|
|
3357
3592
|
if( text.startsWith( "#" ) )
|
|
@@ -3359,6 +3594,8 @@ class CodeEditor {
|
|
|
3359
3594
|
return [];
|
|
3360
3595
|
}
|
|
3361
3596
|
|
|
3597
|
+
const nativeTypes = CodeEditor.nativeTypes[ this.highlight ];
|
|
3598
|
+
|
|
3362
3599
|
const topLevelRegexes = [
|
|
3363
3600
|
[/^class\s+([A-Za-z0-9_]+)/, "class"],
|
|
3364
3601
|
[/^struct\s+([A-Za-z0-9_]+)/, "struct"],
|
|
@@ -3366,30 +3603,33 @@ class CodeEditor {
|
|
|
3366
3603
|
[/^interface\s+([A-Za-z0-9_]+)/, "interface"],
|
|
3367
3604
|
[/^type\s+([A-Za-z0-9_]+)/, "type"],
|
|
3368
3605
|
[/^function\s+([A-Za-z0-9_]+)/, "method"],
|
|
3606
|
+
[/^fn\s+([A-Za-z0-9_]+)/, "method"],
|
|
3607
|
+
[/^def\s+([A-Za-z0-9_]+)/, "method"],
|
|
3369
3608
|
[/^([A-Za-z0-9_]+)\s*=\s*\(?.*\)?\s*=>/, "method"] // arrow functions
|
|
3370
3609
|
];
|
|
3371
3610
|
|
|
3611
|
+
// Add regexes to detect methods, variables ( including "id : nativeType" )
|
|
3372
3612
|
{
|
|
3373
|
-
const nativeTypes = CodeEditor.nativeTypes[ this.highlight ];
|
|
3374
3613
|
if( nativeTypes )
|
|
3375
3614
|
{
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
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
|
+
}
|
|
3379
3621
|
}
|
|
3380
3622
|
|
|
3381
3623
|
const declarationKeywords = CodeEditor.declarationKeywords[ this.highlight ] ?? [ "const", "let", "var" ];
|
|
3382
|
-
|
|
3383
|
-
topLevelRegexes.push( [ new RegExp( regex ), 'variable' ] );
|
|
3624
|
+
topLevelRegexes.push( [ new RegExp( `^(?:${ declarationKeywords.join('|') })\\s+([A-Za-z0-9_]+)` ), 'variable' ] );
|
|
3384
3625
|
}
|
|
3385
3626
|
|
|
3386
|
-
for( let [ regex, kind ] of topLevelRegexes )
|
|
3627
|
+
for( let [ regex, kind, fn ] of topLevelRegexes )
|
|
3387
3628
|
{
|
|
3388
3629
|
const m = text.match( regex );
|
|
3389
3630
|
if( m )
|
|
3390
3631
|
{
|
|
3391
|
-
|
|
3392
|
-
break;
|
|
3632
|
+
_pushSymbol( { name: fn ? fn( m ) : m[ 1 ], kind, scope: scopeName, line: lineNumber } );
|
|
3393
3633
|
}
|
|
3394
3634
|
}
|
|
3395
3635
|
|
|
@@ -3398,15 +3638,28 @@ class CodeEditor {
|
|
|
3398
3638
|
[/this.([A-Za-z_][A-Za-z0-9_]*)\s*\=/, "class-property"],
|
|
3399
3639
|
];
|
|
3400
3640
|
|
|
3401
|
-
for( let [ regex, kind ] of usageRegexes )
|
|
3641
|
+
for( let [ regex, kind, fn ] of usageRegexes )
|
|
3402
3642
|
{
|
|
3403
3643
|
const m = text.match( regex );
|
|
3404
3644
|
if( m )
|
|
3405
3645
|
{
|
|
3406
|
-
|
|
3646
|
+
_pushSymbol( { name: fn ? fn( m ) : m[ 1 ], kind, scope: scopeName, line: lineNumber } );
|
|
3407
3647
|
}
|
|
3408
3648
|
}
|
|
3409
3649
|
|
|
3650
|
+
// Detect method calls
|
|
3651
|
+
const regex = /([A-Za-z0-9_]+)\s*\(/g;
|
|
3652
|
+
let match;
|
|
3653
|
+
while( match = regex.exec( text ) )
|
|
3654
|
+
{
|
|
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 } );
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3410
3663
|
// Stop after matches for top-level declarations and usage symbols
|
|
3411
3664
|
if( symbols.length )
|
|
3412
3665
|
{
|
|
@@ -3425,14 +3678,15 @@ class CodeEditor {
|
|
|
3425
3678
|
{
|
|
3426
3679
|
if( next === "(" && /^[a-zA-Z_]\w*$/.test( token ) && prev === undefined )
|
|
3427
3680
|
{
|
|
3428
|
-
|
|
3681
|
+
if( token === "constructor" ) continue; // skip constructor symbol
|
|
3682
|
+
_pushSymbol( { name: token, kind: "method", scope: scopeName, line: lineNumber } );
|
|
3429
3683
|
}
|
|
3430
3684
|
}
|
|
3431
3685
|
else if( scopeType.startsWith("enum") )
|
|
3432
3686
|
{
|
|
3433
3687
|
if( !isSymbol( token ) && !this._isNumber( token ) && !this._mustHightlightWord( token, CodeEditor.statements ) )
|
|
3434
3688
|
{
|
|
3435
|
-
|
|
3689
|
+
_pushSymbol({ name: token, kind: "enum_value", scope: scopeName, line: lineNumber });
|
|
3436
3690
|
}
|
|
3437
3691
|
}
|
|
3438
3692
|
}
|
|
@@ -3547,13 +3801,14 @@ class CodeEditor {
|
|
|
3547
3801
|
let idx = 0;
|
|
3548
3802
|
while( subtokens.value != undefined )
|
|
3549
3803
|
{
|
|
3550
|
-
const _pt = lineString.substring(idx, subtokens.value.index);
|
|
3804
|
+
const _pt = lineString.substring( idx, subtokens.value.index );
|
|
3551
3805
|
if( _pt.length ) pushToken( _pt );
|
|
3552
3806
|
pushToken( subtokens.value[ 0 ] );
|
|
3553
3807
|
idx = subtokens.value.index + subtokens.value[ 0 ].length;
|
|
3554
3808
|
subtokens = iter.next();
|
|
3555
|
-
if(!subtokens.value)
|
|
3556
|
-
|
|
3809
|
+
if( !subtokens.value )
|
|
3810
|
+
{
|
|
3811
|
+
const _at = lineString.substring( idx );
|
|
3557
3812
|
if( _at.length ) pushToken( _at );
|
|
3558
3813
|
}
|
|
3559
3814
|
}
|
|
@@ -3620,6 +3875,22 @@ class CodeEditor {
|
|
|
3620
3875
|
offset = offsetIdx;
|
|
3621
3876
|
}
|
|
3622
3877
|
}
|
|
3878
|
+
else if( this.highlight == 'WGSL' )
|
|
3879
|
+
{
|
|
3880
|
+
let offset = 0;
|
|
3881
|
+
let atIdx = tokens.indexOf( '@' );
|
|
3882
|
+
|
|
3883
|
+
while( atIdx > -1 )
|
|
3884
|
+
{
|
|
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;
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3623
3894
|
|
|
3624
3895
|
return tokens;
|
|
3625
3896
|
}
|
|
@@ -3664,10 +3935,12 @@ class CodeEditor {
|
|
|
3664
3935
|
|
|
3665
3936
|
let { token, prev, next, tokenIndex, isFirstToken, isLastToken } = ctxData;
|
|
3666
3937
|
|
|
3667
|
-
const lang = CodeEditor.languages[ this.highlight ]
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
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 )
|
|
3671
3944
|
|
|
3672
3945
|
var usePreviousTokenToCheckString = false;
|
|
3673
3946
|
|
|
@@ -3685,7 +3958,7 @@ class CodeEditor {
|
|
|
3685
3958
|
// Manage strings
|
|
3686
3959
|
this._stringEnded = false;
|
|
3687
3960
|
|
|
3688
|
-
if( usePreviousTokenToCheckString || (
|
|
3961
|
+
if( usePreviousTokenToCheckString || ( !inBlockComment && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) ) )
|
|
3689
3962
|
{
|
|
3690
3963
|
const _checkIfStringEnded = t => {
|
|
3691
3964
|
const idx = Object.values( customStringKeys ).indexOf( t );
|
|
@@ -3711,9 +3984,9 @@ class CodeEditor {
|
|
|
3711
3984
|
|
|
3712
3985
|
// Update context data for next tests
|
|
3713
3986
|
ctxData.discardToken = false;
|
|
3714
|
-
ctxData.inBlockComment =
|
|
3987
|
+
ctxData.inBlockComment = inBlockComment;
|
|
3715
3988
|
ctxData.markdownHeader = this._markdownHeader;
|
|
3716
|
-
ctxData.inString = this._buildingString;
|
|
3989
|
+
ctxData.inString = ( this._buildingString !== undefined );
|
|
3717
3990
|
ctxData.singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
3718
3991
|
ctxData.lang = lang;
|
|
3719
3992
|
ctxData.scope = this._scopeStack.at( -1 );
|
|
@@ -3728,16 +4001,8 @@ class CodeEditor {
|
|
|
3728
4001
|
// Get highlighting class based on language common and specific rules
|
|
3729
4002
|
let tokenClass = this._getTokenHighlighting( ctxData, highlight );
|
|
3730
4003
|
|
|
3731
|
-
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
3732
|
-
if( ( lang.blockComments ?? true ) && this._buildingBlockComment != undefined
|
|
3733
|
-
&& token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
|
|
3734
|
-
{
|
|
3735
|
-
this._blockCommentCache.push( new LX.vec2( this._buildingBlockComment, lineNumber ) );
|
|
3736
|
-
delete this._buildingBlockComment;
|
|
3737
|
-
}
|
|
3738
|
-
|
|
3739
4004
|
// We finished constructing a string
|
|
3740
|
-
if( this._buildingString && ( this._stringEnded || isLastToken ) )
|
|
4005
|
+
if( this._buildingString && ( this._stringEnded || isLastToken ) && !inBlockComment )
|
|
3741
4006
|
{
|
|
3742
4007
|
token = this._getCurrentString();
|
|
3743
4008
|
tokenClass = "cm-str";
|
|
@@ -3801,17 +4066,32 @@ class CodeEditor {
|
|
|
3801
4066
|
}
|
|
3802
4067
|
}
|
|
3803
4068
|
|
|
3804
|
-
_inBlockCommentSection(
|
|
4069
|
+
_inBlockCommentSection( lineNumber, tokenPosition, tokenLength ) {
|
|
3805
4070
|
|
|
3806
|
-
|
|
4071
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
4072
|
+
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
4073
|
+
|
|
4074
|
+
for( let section of this._blockCommentCache )
|
|
3807
4075
|
{
|
|
3808
|
-
|
|
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 )
|
|
3809
4082
|
{
|
|
3810
|
-
|
|
4083
|
+
continue;
|
|
3811
4084
|
}
|
|
3812
|
-
}
|
|
3813
4085
|
|
|
3814
|
-
|
|
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
|
+
}
|
|
3815
4095
|
}
|
|
3816
4096
|
|
|
3817
4097
|
_isKeyword( ctxData ) {
|
|
@@ -4479,7 +4759,7 @@ class CodeEditor {
|
|
|
4479
4759
|
|
|
4480
4760
|
if( flag & CodeEditor.RESIZE_SCROLLBAR_V )
|
|
4481
4761
|
{
|
|
4482
|
-
scrollHeight = this.code.lines.length * this.lineHeight
|
|
4762
|
+
scrollHeight = this.code.lines.length * this.lineHeight;
|
|
4483
4763
|
this.codeSizer.style.minHeight = scrollHeight + "px";
|
|
4484
4764
|
}
|
|
4485
4765
|
|
|
@@ -4538,7 +4818,7 @@ class CodeEditor {
|
|
|
4538
4818
|
}
|
|
4539
4819
|
|
|
4540
4820
|
this.hScrollbar.root.classList.toggle( 'hidden', !needsHorizontalScrollbar );
|
|
4541
|
-
this.codeArea.root.style.height = `calc(100% - ${ this.
|
|
4821
|
+
this.codeArea.root.style.height = `calc(100% - ${ this._fullVerticalOffset + ( needsHorizontalScrollbar ? ScrollBar.SCROLLBAR_HORIZONTAL_HEIGHT : 0 ) }px)`;
|
|
4542
4822
|
}
|
|
4543
4823
|
}
|
|
4544
4824
|
|
|
@@ -4754,8 +5034,6 @@ class CodeEditor {
|
|
|
4754
5034
|
...Array.from( CodeEditor.utils[ this.highlight ] ?? [] )
|
|
4755
5035
|
];
|
|
4756
5036
|
|
|
4757
|
-
suggestions = suggestions.concat( Object.keys(this.code.tokens).filter( a => a != word ) );
|
|
4758
|
-
|
|
4759
5037
|
const scopeStack = [ ...this.code.lineScopes[ cursor.line ] ];
|
|
4760
5038
|
const scope = scopeStack.at( -1 );
|
|
4761
5039
|
if( scope.type.startsWith( "enum" ) )
|
|
@@ -4769,10 +5047,8 @@ class CodeEditor {
|
|
|
4769
5047
|
suggestions = suggestions.concat( otherValues.slice( 0, -1 ) );
|
|
4770
5048
|
}
|
|
4771
5049
|
|
|
4772
|
-
const prefix = word.toLowerCase();
|
|
4773
|
-
|
|
4774
5050
|
// Remove 1/2 char words and duplicates...
|
|
4775
|
-
suggestions = Array.from( new Set( suggestions )).filter( s => s.length > 2 && s.toLowerCase().includes(
|
|
5051
|
+
suggestions = Array.from( new Set( suggestions )).filter( s => s.length > 2 && s.toLowerCase().includes( word.toLowerCase() ) );
|
|
4776
5052
|
|
|
4777
5053
|
// Order...
|
|
4778
5054
|
|
|
@@ -4782,21 +5058,49 @@ class CodeEditor {
|
|
|
4782
5058
|
return 2; // worst
|
|
4783
5059
|
}
|
|
4784
5060
|
|
|
4785
|
-
suggestions = suggestions.sort( ( a, b ) => scoreSuggestion( a,
|
|
5061
|
+
suggestions = suggestions.sort( ( a, b ) => ( scoreSuggestion( a, word ) - scoreSuggestion( b, word ) ) || a.localeCompare( b ) );
|
|
4786
5062
|
|
|
4787
5063
|
for( let s of suggestions )
|
|
4788
5064
|
{
|
|
4789
|
-
|
|
5065
|
+
const pre = document.createElement( 'pre' );
|
|
4790
5066
|
this.autocomplete.appendChild( pre );
|
|
4791
5067
|
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
icon = "Code";
|
|
5068
|
+
const symbol = this.code.symbolsTable.get( s );
|
|
5069
|
+
|
|
5070
|
+
let iconName = "CaseLower";
|
|
5071
|
+
let iconClass = "foo";
|
|
4797
5072
|
|
|
4798
|
-
|
|
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
|
+
}
|
|
4799
5101
|
|
|
5102
|
+
pre.appendChild( LX.makeIcon( iconName, { iconClass: "mr-1", svgClass: "sm " + iconClass } ) );
|
|
5103
|
+
s
|
|
4800
5104
|
pre.addEventListener( 'click', () => {
|
|
4801
5105
|
this.autoCompleteWord( s );
|
|
4802
5106
|
} );
|
|
@@ -5284,7 +5588,8 @@ CodeEditor.languages = {
|
|
|
5284
5588
|
};
|
|
5285
5589
|
|
|
5286
5590
|
CodeEditor.nativeTypes = {
|
|
5287
|
-
'C++': ['int', 'float', 'double', 'bool', 'long', 'short', 'char', 'wchar_t', 'void']
|
|
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']
|
|
5288
5593
|
};
|
|
5289
5594
|
|
|
5290
5595
|
CodeEditor.declarationKeywords = {
|
|
@@ -5308,10 +5613,9 @@ CodeEditor.keywords = {
|
|
|
5308
5613
|
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
5309
5614
|
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.', 'table', 'tr', 'td', 'th', 'label', 'video', 'img', 'code', 'button', 'select', 'option', 'svg', 'media', 'all',
|
|
5310
5615
|
'i', 'a', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'last-child', 'tbody', 'pre', 'monospace', 'font-face'],
|
|
5311
|
-
'WGSL': ['var', 'let', 'true', 'false', 'fn', '
|
|
5312
|
-
'
|
|
5313
|
-
'
|
|
5314
|
-
'texture_storage_2d_array', 'texture_storage_3d', 'vec2u', 'vec3u', 'vec4u', 'ptr'],
|
|
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'],
|
|
5315
5619
|
'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true',
|
|
5316
5620
|
'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
|
|
5317
5621
|
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
|
|
@@ -5330,7 +5634,14 @@ CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to
|
|
|
5330
5634
|
'Python': ['abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
|
|
5331
5635
|
'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
|
|
5332
5636
|
'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'repr',
|
|
5333
|
-
'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']
|
|
5334
5645
|
};
|
|
5335
5646
|
|
|
5336
5647
|
CodeEditor.types = {
|
|
@@ -5351,6 +5662,7 @@ CodeEditor.builtIn = {
|
|
|
5351
5662
|
'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
|
|
5352
5663
|
'CSS': ['*', '!important'],
|
|
5353
5664
|
'C++': ['vector', 'list', 'map'],
|
|
5665
|
+
'WGSL': ['@vertex', '@fragment'],
|
|
5354
5666
|
'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
|
|
5355
5667
|
'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
|
|
5356
5668
|
'PHP': ['echo', 'print'],
|