lexgui 0.7.13 → 0.7.14
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 +396 -224
- package/build/lexgui.css +2 -1
- package/build/lexgui.js +1 -1
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +1 -1
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +14 -1
- package/package.json +1 -1
|
@@ -356,6 +356,7 @@ class CodeEditor
|
|
|
356
356
|
this.highlight = options.highlight ?? 'Plain Text';
|
|
357
357
|
this.newTabOptions = options.newTabOptions;
|
|
358
358
|
this.customSuggestions = options.customSuggestions ?? [];
|
|
359
|
+
this.explorerName = options.explorerName ?? "EXPLORER";
|
|
359
360
|
|
|
360
361
|
// Editor callbacks
|
|
361
362
|
this.onSave = options.onSave ?? options.onsave; // LEGACY onsave
|
|
@@ -375,45 +376,43 @@ class CodeEditor
|
|
|
375
376
|
|
|
376
377
|
let panel = new LX.Panel();
|
|
377
378
|
|
|
378
|
-
panel.addTitle(
|
|
379
|
+
panel.addTitle( this.explorerName );
|
|
379
380
|
|
|
380
|
-
let sceneData =
|
|
381
|
-
'id': 'WORKSPACE',
|
|
382
|
-
'skipVisibility': true,
|
|
383
|
-
'children': []
|
|
384
|
-
};
|
|
381
|
+
let sceneData = [];
|
|
385
382
|
|
|
386
383
|
this.explorer = panel.addTree( null, sceneData, {
|
|
387
384
|
filter: false,
|
|
388
385
|
rename: false,
|
|
389
386
|
skipDefaultIcon: true,
|
|
390
|
-
onevent: (event) =>
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
387
|
+
onevent: (event) =>
|
|
388
|
+
{
|
|
389
|
+
switch( event.type )
|
|
390
|
+
{
|
|
391
|
+
// case LX.TreeEvent.NODE_SELECTED:
|
|
392
|
+
// if( !this.tabs.tabDOMs[ event.node.id ] ) break;
|
|
393
|
+
case LX.TreeEvent.NODE_DBLCLICKED:
|
|
394
|
+
this.loadTab( event.node.id );
|
|
395
|
+
break;
|
|
396
|
+
case LX.TreeEvent.NODE_DELETED:
|
|
397
|
+
this.closeTab( event.node.id );
|
|
398
|
+
break;
|
|
399
|
+
// case LX.TreeEvent.NODE_CONTEXTMENU:
|
|
400
|
+
// LX.addContextMenu( event.multiple ? "Selected Nodes" : event.node.id, event.value, m => {
|
|
401
|
+
//
|
|
402
|
+
// });
|
|
403
|
+
// break;
|
|
404
|
+
// case LX.TreeEvent.NODE_DRAGGED:
|
|
405
|
+
// console.log(event.node.id + " is now child of " + event.value.id);
|
|
406
|
+
// break;
|
|
408
407
|
}
|
|
409
408
|
}
|
|
410
409
|
});
|
|
411
410
|
|
|
412
411
|
this.addExplorerItem = function( item )
|
|
413
412
|
{
|
|
414
|
-
if( !this.explorer.innerTree.data.
|
|
413
|
+
if( !this.explorer.innerTree.data.find( ( value, index ) => value.id === item.id ) )
|
|
415
414
|
{
|
|
416
|
-
this.explorer.innerTree.data.
|
|
415
|
+
this.explorer.innerTree.data.push( item );
|
|
417
416
|
}
|
|
418
417
|
};
|
|
419
418
|
|
|
@@ -487,6 +486,17 @@ class CodeEditor
|
|
|
487
486
|
|
|
488
487
|
this.codeArea.root.classList.add( 'lexcodearea' );
|
|
489
488
|
|
|
489
|
+
const codeResizeObserver = new ResizeObserver( entries => {
|
|
490
|
+
|
|
491
|
+
if( !this.code )
|
|
492
|
+
{
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
this.resize();
|
|
497
|
+
});
|
|
498
|
+
codeResizeObserver.observe( this.codeArea.root );
|
|
499
|
+
|
|
490
500
|
// Full editor
|
|
491
501
|
area.root.classList.add('codebasearea');
|
|
492
502
|
|
|
@@ -894,11 +904,13 @@ class CodeEditor
|
|
|
894
904
|
}
|
|
895
905
|
else
|
|
896
906
|
{
|
|
897
|
-
const indentSpaces = this.tabSpaces - (cursor.position % this.tabSpaces);
|
|
898
|
-
this._addSpaces( indentSpaces );
|
|
907
|
+
const indentSpaces = this.tabSpaces - ( cursor.position % this.tabSpaces );
|
|
908
|
+
this._addSpaces( cursor, indentSpaces );
|
|
899
909
|
}
|
|
900
910
|
}
|
|
901
|
-
},
|
|
911
|
+
}, ( cursor, e ) => {
|
|
912
|
+
return e.shiftKey || ( cursor.selection && !cursor.selection.sameLine() );
|
|
913
|
+
});
|
|
902
914
|
|
|
903
915
|
this.action( 'Home', false, ( ln, cursor, e ) => {
|
|
904
916
|
|
|
@@ -910,7 +922,7 @@ class CodeEditor
|
|
|
910
922
|
const prestring = this.code.lines[ ln ].substring( 0, idx );
|
|
911
923
|
let lastX = cursor.position;
|
|
912
924
|
|
|
913
|
-
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
925
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor, true );
|
|
914
926
|
if( idx > 0 )
|
|
915
927
|
{
|
|
916
928
|
this.cursorToString( cursor, prestring );
|
|
@@ -921,7 +933,6 @@ class CodeEditor
|
|
|
921
933
|
idx = 0;
|
|
922
934
|
}
|
|
923
935
|
|
|
924
|
-
this.setScrollLeft( 0 );
|
|
925
936
|
this.mergeCursors( ln );
|
|
926
937
|
|
|
927
938
|
if( e.shiftKey && !e.cancelShift )
|
|
@@ -953,10 +964,10 @@ class CodeEditor
|
|
|
953
964
|
}
|
|
954
965
|
});
|
|
955
966
|
|
|
956
|
-
this.action( 'End', false, ( ln, cursor, e ) =>
|
|
957
|
-
|
|
958
|
-
if( ( e.shiftKey || e._shiftKey ) && !e.cancelShift )
|
|
959
|
-
|
|
967
|
+
this.action( 'End', false, ( ln, cursor, e ) =>
|
|
968
|
+
{
|
|
969
|
+
if( ( e.shiftKey || e._shiftKey ) && !e.cancelShift )
|
|
970
|
+
{
|
|
960
971
|
var string = this.code.lines[ ln ].substring( cursor.position );
|
|
961
972
|
if( !cursor.selection )
|
|
962
973
|
this.startSelection( cursor );
|
|
@@ -1075,11 +1086,14 @@ class CodeEditor
|
|
|
1075
1086
|
const letter = this.getCharAtPos( cursor );
|
|
1076
1087
|
|
|
1077
1088
|
// Go to end of line if out of range
|
|
1078
|
-
if( !letter || !canGoDown )
|
|
1079
|
-
|
|
1089
|
+
if( !letter || !canGoDown )
|
|
1090
|
+
{
|
|
1091
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
1092
|
+
this.cursorToRight( this.code.lines[ cursor.line ], cursor );
|
|
1080
1093
|
}
|
|
1081
1094
|
|
|
1082
|
-
if( e.shiftKey )
|
|
1095
|
+
if( e.shiftKey )
|
|
1096
|
+
{
|
|
1083
1097
|
this._processSelection( cursor, e );
|
|
1084
1098
|
}
|
|
1085
1099
|
}
|
|
@@ -1145,8 +1159,8 @@ class CodeEditor
|
|
|
1145
1159
|
else {
|
|
1146
1160
|
cursor.selection.invertIfNecessary();
|
|
1147
1161
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, cursor );
|
|
1148
|
-
this.cursorToLine( cursor, cursor.selection.fromY
|
|
1149
|
-
this.cursorToPosition( cursor, cursor.selection.fromX );
|
|
1162
|
+
this.cursorToLine( cursor, cursor.selection.fromY );
|
|
1163
|
+
this.cursorToPosition( cursor, cursor.selection.fromX, true );
|
|
1150
1164
|
this.endSelection();
|
|
1151
1165
|
}
|
|
1152
1166
|
}
|
|
@@ -1228,7 +1242,7 @@ class CodeEditor
|
|
|
1228
1242
|
cursor.selection.invertIfNecessary();
|
|
1229
1243
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, cursor );
|
|
1230
1244
|
this.cursorToLine( cursor, cursor.selection.toY );
|
|
1231
|
-
this.cursorToPosition( cursor, cursor.selection.toX );
|
|
1245
|
+
this.cursorToPosition( cursor, cursor.selection.toX, true );
|
|
1232
1246
|
this.endSelection( cursor );
|
|
1233
1247
|
}
|
|
1234
1248
|
}
|
|
@@ -1259,8 +1273,8 @@ class CodeEditor
|
|
|
1259
1273
|
this.loadedTabs = { };
|
|
1260
1274
|
this.openedTabs = { };
|
|
1261
1275
|
|
|
1262
|
-
const onLoadAll = () =>
|
|
1263
|
-
|
|
1276
|
+
const onLoadAll = async () =>
|
|
1277
|
+
{
|
|
1264
1278
|
// Create inspector panel when the initial state is complete
|
|
1265
1279
|
// and we have at least 1 tab opened
|
|
1266
1280
|
this.statusPanel = this._createStatusPanel( options );
|
|
@@ -1269,44 +1283,46 @@ class CodeEditor
|
|
|
1269
1283
|
area.attach( this.statusPanel );
|
|
1270
1284
|
}
|
|
1271
1285
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
if( savedFontSize )
|
|
1277
|
-
{
|
|
1278
|
-
this._setFontSize( parseInt( savedFontSize ) );
|
|
1279
|
-
}
|
|
1280
|
-
else // Use default size
|
|
1281
|
-
{
|
|
1282
|
-
const r = document.querySelector( ':root' );
|
|
1283
|
-
const s = getComputedStyle( r );
|
|
1284
|
-
this.fontSize = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
|
|
1285
|
-
this.charWidth = this._measureChar( "a", true );
|
|
1286
|
-
this.processLines();
|
|
1287
|
-
}
|
|
1286
|
+
if( document.fonts.status == "loading" )
|
|
1287
|
+
{
|
|
1288
|
+
await document.fonts.ready;
|
|
1289
|
+
}
|
|
1288
1290
|
|
|
1289
|
-
|
|
1291
|
+
// Load any font size from local storage
|
|
1292
|
+
const savedFontSize = window.localStorage.getItem( "lexcodeeditor-font-size" );
|
|
1293
|
+
if( savedFontSize )
|
|
1294
|
+
{
|
|
1295
|
+
this._setFontSize( parseInt( savedFontSize ) );
|
|
1296
|
+
}
|
|
1297
|
+
else // Use default size
|
|
1298
|
+
{
|
|
1299
|
+
const r = document.querySelector( ':root' );
|
|
1300
|
+
const s = getComputedStyle( r );
|
|
1301
|
+
this.fontSize = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
|
|
1302
|
+
this.charWidth = this._measureChar();
|
|
1303
|
+
this.processLines();
|
|
1304
|
+
}
|
|
1290
1305
|
|
|
1291
|
-
|
|
1292
|
-
LX.doAsync( () => {
|
|
1293
|
-
this._verticalTopOffset = this.tabs?.root.getBoundingClientRect().height ?? 0;
|
|
1294
|
-
this._verticalBottomOffset = this.statusPanel?.root.getBoundingClientRect().height ?? 0;
|
|
1295
|
-
this._fullVerticalOffset = this._verticalTopOffset + this._verticalBottomOffset;
|
|
1306
|
+
LX.emit( "@font-size", this.fontSize );
|
|
1296
1307
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
this.codeArea.root.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1303
|
-
}, 50 );
|
|
1308
|
+
// Get final sizes for editor elements based on Tabs and status bar offsets
|
|
1309
|
+
LX.doAsync( () => {
|
|
1310
|
+
this._verticalTopOffset = this.tabs?.root.getBoundingClientRect().height ?? 0;
|
|
1311
|
+
this._verticalBottomOffset = this.statusPanel?.root.getBoundingClientRect().height ?? 0;
|
|
1312
|
+
this._fullVerticalOffset = this._verticalTopOffset + this._verticalBottomOffset;
|
|
1304
1313
|
|
|
1305
|
-
|
|
1306
|
-
{
|
|
1307
|
-
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1314
|
+
this.gutter.style.marginTop = `${ this._verticalTopOffset }px`;
|
|
1315
|
+
this.gutter.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1316
|
+
this.vScrollbar.root.style.marginTop = `${ this._verticalTopOffset }px`;
|
|
1317
|
+
this.vScrollbar.root.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1318
|
+
this.hScrollbar.root.style.bottom = `${ this._verticalBottomOffset }px`;
|
|
1319
|
+
this.codeArea.root.style.height = `calc(100% - ${ this._fullVerticalOffset }px)`;
|
|
1320
|
+
}, 50 );
|
|
1321
|
+
|
|
1322
|
+
if( options.callback )
|
|
1323
|
+
{
|
|
1324
|
+
options.callback.call( this, this );
|
|
1325
|
+
}
|
|
1310
1326
|
|
|
1311
1327
|
window.editor = this;
|
|
1312
1328
|
};
|
|
@@ -1342,13 +1358,13 @@ class CodeEditor
|
|
|
1342
1358
|
}});
|
|
1343
1359
|
}
|
|
1344
1360
|
}
|
|
1345
|
-
else if( options.defaultTab ?? true )
|
|
1346
|
-
{
|
|
1347
|
-
this.addTab( options.name || "untitled", true, options.title, { language: options.highlight ?? "Plain Text" } );
|
|
1348
|
-
onLoadAll();
|
|
1349
|
-
}
|
|
1350
1361
|
else
|
|
1351
1362
|
{
|
|
1363
|
+
if( options.defaultTab ?? true )
|
|
1364
|
+
{
|
|
1365
|
+
this.addTab( options.name || "untitled", true, options.title, { language: options.highlight ?? "Plain Text" } );
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1352
1368
|
onLoadAll();
|
|
1353
1369
|
}
|
|
1354
1370
|
}
|
|
@@ -1403,7 +1419,7 @@ class CodeEditor
|
|
|
1403
1419
|
let lastLine = newLines.pop();
|
|
1404
1420
|
|
|
1405
1421
|
this.cursorToLine( cursor, newLines.length ); // Already substracted 1
|
|
1406
|
-
this.cursorToPosition( cursor, lastLine.length );
|
|
1422
|
+
this.cursorToPosition( cursor, lastLine.length, true );
|
|
1407
1423
|
|
|
1408
1424
|
this.mustProcessLines = true;
|
|
1409
1425
|
|
|
@@ -1727,7 +1743,7 @@ class CodeEditor
|
|
|
1727
1743
|
// Update explorer icon
|
|
1728
1744
|
if( this.useFileExplorer )
|
|
1729
1745
|
{
|
|
1730
|
-
const item = this.explorer.innerTree.data.
|
|
1746
|
+
const item = this.explorer.innerTree.data.filter( (v) => v.id === this.code.tabName )[ 0 ];
|
|
1731
1747
|
console.assert( item != undefined );
|
|
1732
1748
|
item.icon = icon;
|
|
1733
1749
|
this.explorer.innerTree.frefresh( this.code.tabName );
|
|
@@ -1813,7 +1829,8 @@ class CodeEditor
|
|
|
1813
1829
|
for( const lang of Object.keys( CodeEditor.languages ) )
|
|
1814
1830
|
{
|
|
1815
1831
|
m.add( lang, v => {
|
|
1816
|
-
this._changeLanguage( v, null, true )
|
|
1832
|
+
this._changeLanguage( v, null, true );
|
|
1833
|
+
this.processLines();
|
|
1817
1834
|
} );
|
|
1818
1835
|
}
|
|
1819
1836
|
});
|
|
@@ -2078,7 +2095,7 @@ class CodeEditor
|
|
|
2078
2095
|
});
|
|
2079
2096
|
code.addEventListener( 'dragleave', function(e) {
|
|
2080
2097
|
e.preventDefault();
|
|
2081
|
-
this.parentElement.remove( 'dragging' );
|
|
2098
|
+
this.parentElement.classList.remove( 'dragging' );
|
|
2082
2099
|
});
|
|
2083
2100
|
code.addEventListener( 'drop', e => {
|
|
2084
2101
|
e.preventDefault();
|
|
@@ -2136,7 +2153,7 @@ class CodeEditor
|
|
|
2136
2153
|
|
|
2137
2154
|
if( selected )
|
|
2138
2155
|
{
|
|
2139
|
-
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
2156
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, undefined, true );
|
|
2140
2157
|
this.mustProcessLines = true;
|
|
2141
2158
|
}
|
|
2142
2159
|
else
|
|
@@ -2204,7 +2221,7 @@ class CodeEditor
|
|
|
2204
2221
|
this.code = code;
|
|
2205
2222
|
this.mustProcessLines = true;
|
|
2206
2223
|
|
|
2207
|
-
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
2224
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, undefined, true );
|
|
2208
2225
|
this.processLines();
|
|
2209
2226
|
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
2210
2227
|
this._processLinesIfNecessary();
|
|
@@ -2249,6 +2266,9 @@ class CodeEditor
|
|
|
2249
2266
|
return;
|
|
2250
2267
|
}
|
|
2251
2268
|
|
|
2269
|
+
// Reset visibility
|
|
2270
|
+
code.style.display = "block";
|
|
2271
|
+
|
|
2252
2272
|
this.openedTabs[ name ] = code;
|
|
2253
2273
|
|
|
2254
2274
|
const isNewTabButton = ( name === '+' );
|
|
@@ -2271,10 +2291,11 @@ class CodeEditor
|
|
|
2271
2291
|
|
|
2272
2292
|
// Select as current...
|
|
2273
2293
|
this.code = code;
|
|
2274
|
-
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP );
|
|
2275
|
-
this.processLines();
|
|
2294
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, undefined, true );
|
|
2276
2295
|
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
2277
2296
|
this._updateDataInfoPanel( "@tab-name", code.tabName );
|
|
2297
|
+
|
|
2298
|
+
this.processLines();
|
|
2278
2299
|
}
|
|
2279
2300
|
|
|
2280
2301
|
closeTab( name, eraseAll )
|
|
@@ -2358,8 +2379,8 @@ class CodeEditor
|
|
|
2358
2379
|
}
|
|
2359
2380
|
}
|
|
2360
2381
|
|
|
2382
|
+
this._mouseDown = true;
|
|
2361
2383
|
this.lastMouseDown = LX.getTime();
|
|
2362
|
-
this.state.selectingText = true;
|
|
2363
2384
|
this.endSelection();
|
|
2364
2385
|
this.processClick( e );
|
|
2365
2386
|
}
|
|
@@ -2371,8 +2392,11 @@ class CodeEditor
|
|
|
2371
2392
|
|
|
2372
2393
|
else if( e.type == 'mousemove' )
|
|
2373
2394
|
{
|
|
2374
|
-
if( this.
|
|
2395
|
+
if( this._mouseDown )
|
|
2396
|
+
{
|
|
2397
|
+
this.state.selectingText = true;
|
|
2375
2398
|
this.processSelections( e );
|
|
2399
|
+
}
|
|
2376
2400
|
}
|
|
2377
2401
|
|
|
2378
2402
|
else if ( e.type == 'click' ) // trip
|
|
@@ -2391,7 +2415,7 @@ class CodeEditor
|
|
|
2391
2415
|
case LX.MOUSE_TRIPLE_CLICK:
|
|
2392
2416
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
2393
2417
|
e._shiftKey = true;
|
|
2394
|
-
this.actions['End'].callback(cursor.line, cursor, e);
|
|
2418
|
+
this.actions['End'].callback( cursor.line, cursor, e );
|
|
2395
2419
|
this._tripleClickSelection = true;
|
|
2396
2420
|
break;
|
|
2397
2421
|
}
|
|
@@ -2480,8 +2504,16 @@ class CodeEditor
|
|
|
2480
2504
|
if( cursor.selection )
|
|
2481
2505
|
{
|
|
2482
2506
|
cursor.selection.invertIfNecessary();
|
|
2507
|
+
|
|
2508
|
+
// If single line selection and no chars selected, remove selection
|
|
2509
|
+
const deltaY = cursor.selection.toY - cursor.selection.fromY;
|
|
2510
|
+
if( cursor.selection.chars == 0 && deltaY == 0 )
|
|
2511
|
+
{
|
|
2512
|
+
this.endSelection();
|
|
2513
|
+
}
|
|
2483
2514
|
}
|
|
2484
2515
|
|
|
2516
|
+
this._mouseDown = false;
|
|
2485
2517
|
this.state.selectingText = false;
|
|
2486
2518
|
delete this._lastSelectionKeyDir;
|
|
2487
2519
|
}
|
|
@@ -2644,7 +2676,7 @@ class CodeEditor
|
|
|
2644
2676
|
|
|
2645
2677
|
if( isVisible )
|
|
2646
2678
|
{
|
|
2647
|
-
domEl.style.width = ( stringWidth || 8 ) + "px";
|
|
2679
|
+
domEl.style.width = ( stringWidth || ( deltaY == 0 ? 0 : 8 ) ) + "px";
|
|
2648
2680
|
domEl._top = i * this.lineHeight;
|
|
2649
2681
|
domEl.style.top = domEl._top + "px";
|
|
2650
2682
|
}
|
|
@@ -2763,10 +2795,15 @@ class CodeEditor
|
|
|
2763
2795
|
|
|
2764
2796
|
cursorOffset.x += ( cursor.position - lastProcessedCursor.position );
|
|
2765
2797
|
cursorOffset.y += ( cursor.line - lastProcessedCursor.line );
|
|
2798
|
+
|
|
2799
|
+
// Set active line in case it's blurred
|
|
2800
|
+
if( !cursor.selection )
|
|
2801
|
+
{
|
|
2802
|
+
cursor.line = cursor.line;
|
|
2803
|
+
}
|
|
2766
2804
|
}
|
|
2767
2805
|
|
|
2768
2806
|
// Clear tmp
|
|
2769
|
-
|
|
2770
2807
|
delete this._lastProcessedCursorIndex;
|
|
2771
2808
|
}
|
|
2772
2809
|
|
|
@@ -2868,6 +2905,7 @@ class CodeEditor
|
|
|
2868
2905
|
async _processKeyAtCursor( e, key, cursor )
|
|
2869
2906
|
{
|
|
2870
2907
|
const skipUndo = e.detail.skipUndo ?? false;
|
|
2908
|
+
const skipDeleteSelection = e.detail.skipDeleteSelection ?? false;
|
|
2871
2909
|
|
|
2872
2910
|
// keys with length > 1 are probably special keys
|
|
2873
2911
|
if( key.length > 1 && this.specialKeys.indexOf( key ) == -1 )
|
|
@@ -2905,33 +2943,19 @@ class CodeEditor
|
|
|
2905
2943
|
}
|
|
2906
2944
|
}
|
|
2907
2945
|
}
|
|
2908
|
-
|
|
2909
2946
|
else if( e.altKey )
|
|
2910
2947
|
{
|
|
2911
|
-
switch( key )
|
|
2912
|
-
|
|
2948
|
+
switch( key )
|
|
2949
|
+
{
|
|
2950
|
+
case 'd': // duplicate line
|
|
2913
2951
|
e.preventDefault();
|
|
2914
2952
|
this._duplicateLine( lidx, cursor );
|
|
2915
2953
|
return;
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
return;
|
|
2919
|
-
this._addUndoStep( cursor, true );
|
|
2920
|
-
swapArrayElements( this.code.lines, lidx - 1, lidx );
|
|
2921
|
-
this.lineUp( cursor );
|
|
2922
|
-
this.processLine( lidx - 1 );
|
|
2923
|
-
this.processLine( lidx );
|
|
2924
|
-
this.hideAutoCompleteBox();
|
|
2954
|
+
case 'ArrowUp':
|
|
2955
|
+
this.swapLines( lidx, -1, cursor );
|
|
2925
2956
|
return;
|
|
2926
2957
|
case 'ArrowDown':
|
|
2927
|
-
|
|
2928
|
-
return;
|
|
2929
|
-
this._addUndoStep( cursor, true );
|
|
2930
|
-
swapArrayElements( this.code.lines, lidx, lidx + 1 );
|
|
2931
|
-
this.lineDown( cursor );
|
|
2932
|
-
this.processLine( lidx );
|
|
2933
|
-
this.processLine( lidx + 1 );
|
|
2934
|
-
this.hideAutoCompleteBox();
|
|
2958
|
+
this.swapLines( lidx, 1, cursor );
|
|
2935
2959
|
return;
|
|
2936
2960
|
}
|
|
2937
2961
|
}
|
|
@@ -2946,7 +2970,9 @@ class CodeEditor
|
|
|
2946
2970
|
e.preventDefault();
|
|
2947
2971
|
|
|
2948
2972
|
if( this._actionMustDelete( cursor, this.actions[ key ], e ) )
|
|
2973
|
+
{
|
|
2949
2974
|
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
2975
|
+
}
|
|
2950
2976
|
|
|
2951
2977
|
return this.actions[ key ].callback( lidx, cursor, e );
|
|
2952
2978
|
}
|
|
@@ -2974,7 +3000,7 @@ class CodeEditor
|
|
|
2974
3000
|
// Until this point, if there was a selection, we need
|
|
2975
3001
|
// to delete the content..
|
|
2976
3002
|
|
|
2977
|
-
if( cursor.selection )
|
|
3003
|
+
if( cursor.selection && !skipDeleteSelection )
|
|
2978
3004
|
{
|
|
2979
3005
|
this.actions['Backspace'].callback( lidx, cursor, e );
|
|
2980
3006
|
lidx = cursor.line;
|
|
@@ -3308,20 +3334,20 @@ class CodeEditor
|
|
|
3308
3334
|
}
|
|
3309
3335
|
}
|
|
3310
3336
|
|
|
3311
|
-
action( key, deleteSelection, fn,
|
|
3337
|
+
action( key, deleteSelection, fn, eventSkipDeleteFn )
|
|
3312
3338
|
{
|
|
3313
3339
|
this.actions[ key ] = {
|
|
3314
3340
|
"key": key,
|
|
3315
3341
|
"callback": fn,
|
|
3316
3342
|
"deleteSelection": deleteSelection,
|
|
3317
|
-
"
|
|
3343
|
+
"eventSkipDeleteFn": eventSkipDeleteFn
|
|
3318
3344
|
};
|
|
3319
3345
|
}
|
|
3320
3346
|
|
|
3321
3347
|
_actionMustDelete( cursor, action, e )
|
|
3322
3348
|
{
|
|
3323
3349
|
return cursor.selection && action.deleteSelection &&
|
|
3324
|
-
( action.
|
|
3350
|
+
!( action.eventSkipDeleteFn ? action.eventSkipDeleteFn( cursor, e ) : false );
|
|
3325
3351
|
}
|
|
3326
3352
|
|
|
3327
3353
|
scanWordSuggestions()
|
|
@@ -4542,35 +4568,43 @@ class CodeEditor
|
|
|
4542
4568
|
const tokenSet = new Set( this._getTokensFromLine( text, true ) );
|
|
4543
4569
|
const scores = {};
|
|
4544
4570
|
|
|
4545
|
-
|
|
4546
|
-
{
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
if( tokenSet.has( kw ) ) scores[ lang ]++;
|
|
4556
|
-
}
|
|
4571
|
+
// Check strong indicators first
|
|
4572
|
+
const strongIndicators = {
|
|
4573
|
+
"JavaScript": ["import ", "export default", "console.", "=>", "document.", "window."],
|
|
4574
|
+
"TypeScript": ["import ", "export default", "console.", "=>", "document.", "window."],
|
|
4575
|
+
"C++": ["#include", "::", "std::", "template <", "using namespace"],
|
|
4576
|
+
"Python": ["def ", "import ", "print(", "self", "None", "True", "False"],
|
|
4577
|
+
"HTML": ["<html", "<div", "<body", "<script", "<style"],
|
|
4578
|
+
"CSS": ["@media"],
|
|
4579
|
+
"Markdown": ["#", "##", "###", "](", "![", "**"],
|
|
4580
|
+
};
|
|
4557
4581
|
|
|
4558
|
-
for(
|
|
4582
|
+
for( const [ lang, indicators ] of Object.entries( strongIndicators ) )
|
|
4559
4583
|
{
|
|
4560
|
-
|
|
4561
|
-
|
|
4584
|
+
scores[ lang ] = scores[ lang ] ?? 0;
|
|
4585
|
+
for( const key of indicators )
|
|
4586
|
+
{
|
|
4587
|
+
if( text.includes( key ) ) scores[ lang ] += 20;
|
|
4588
|
+
}
|
|
4562
4589
|
}
|
|
4563
4590
|
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4591
|
+
// Check groups' words now with less importance score
|
|
4592
|
+
const groups = [
|
|
4593
|
+
CodeEditor.keywords,
|
|
4594
|
+
CodeEditor.statements,
|
|
4595
|
+
CodeEditor.utils,
|
|
4596
|
+
CodeEditor.types,
|
|
4597
|
+
CodeEditor.builtIn,
|
|
4598
|
+
];
|
|
4569
4599
|
|
|
4570
|
-
for(
|
|
4600
|
+
for( const group of groups )
|
|
4571
4601
|
{
|
|
4572
|
-
for( let
|
|
4573
|
-
|
|
4602
|
+
for( let [ lang, wordList ] of Object.entries( group ) )
|
|
4603
|
+
{
|
|
4604
|
+
scores[ lang ] = scores[ lang ] ?? 0;
|
|
4605
|
+
for( let kw of wordList )
|
|
4606
|
+
if( tokenSet.has( kw ) ) scores[ lang ]++;
|
|
4607
|
+
}
|
|
4574
4608
|
}
|
|
4575
4609
|
|
|
4576
4610
|
const sorted = Object.entries( scores ).sort( ( a, b ) => b[ 1 ] - a[ 1 ] );
|
|
@@ -4601,6 +4635,33 @@ class CodeEditor
|
|
|
4601
4635
|
return true;
|
|
4602
4636
|
}
|
|
4603
4637
|
|
|
4638
|
+
swapLines( lidx, offset, cursor )
|
|
4639
|
+
{
|
|
4640
|
+
if( this.code.lines[ lidx + offset ] == undefined )
|
|
4641
|
+
{
|
|
4642
|
+
return;
|
|
4643
|
+
}
|
|
4644
|
+
|
|
4645
|
+
this._addUndoStep( cursor, true );
|
|
4646
|
+
|
|
4647
|
+
swapArrayElements( this.code.lines, lidx + offset, lidx );
|
|
4648
|
+
|
|
4649
|
+
// Process both lines
|
|
4650
|
+
this.processLine( lidx + offset );
|
|
4651
|
+
this.processLine( lidx );
|
|
4652
|
+
|
|
4653
|
+
if( offset > 0 )
|
|
4654
|
+
{
|
|
4655
|
+
this.lineDown( cursor );
|
|
4656
|
+
}
|
|
4657
|
+
else
|
|
4658
|
+
{
|
|
4659
|
+
this.lineUp( cursor );
|
|
4660
|
+
}
|
|
4661
|
+
|
|
4662
|
+
this.hideAutoCompleteBox();
|
|
4663
|
+
}
|
|
4664
|
+
|
|
4604
4665
|
restartBlink()
|
|
4605
4666
|
{
|
|
4606
4667
|
if( !this.code ) return;
|
|
@@ -4675,11 +4736,13 @@ class CodeEditor
|
|
|
4675
4736
|
LX.deleteElement( this.selections[ cursor.name ] );
|
|
4676
4737
|
delete this.selections[ cursor.name ];
|
|
4677
4738
|
delete cursor.selection;
|
|
4739
|
+
cursor.line = cursor.line; // Update current line
|
|
4678
4740
|
}
|
|
4679
4741
|
else
|
|
4680
4742
|
{
|
|
4681
4743
|
for( let cursor of this.cursors.children )
|
|
4682
4744
|
{
|
|
4745
|
+
cursor.line = cursor.line; // Update current line
|
|
4683
4746
|
LX.deleteElement( this.selections[ cursor.name ] );
|
|
4684
4747
|
delete this.selections[ cursor.name ];
|
|
4685
4748
|
delete cursor.selection;
|
|
@@ -4695,7 +4758,7 @@ class CodeEditor
|
|
|
4695
4758
|
this._removeSecondaryCursors();
|
|
4696
4759
|
|
|
4697
4760
|
var cursor = this.getCurrentCursor();
|
|
4698
|
-
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, cursor );
|
|
4761
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT_TOP, cursor, true );
|
|
4699
4762
|
|
|
4700
4763
|
this.startSelection( cursor );
|
|
4701
4764
|
|
|
@@ -4724,18 +4787,7 @@ class CodeEditor
|
|
|
4724
4787
|
|
|
4725
4788
|
this.restartBlink();
|
|
4726
4789
|
|
|
4727
|
-
|
|
4728
|
-
const rightMargin = this.charWidth;
|
|
4729
|
-
const cursorX = ( cursor.position * this.charWidth );
|
|
4730
|
-
const currentScrollLeft = this.getScrollLeft();
|
|
4731
|
-
const viewportSizeX = this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH; // Gutter offset
|
|
4732
|
-
const viewportX = viewportSizeX + currentScrollLeft;
|
|
4733
|
-
|
|
4734
|
-
if( cursorX >= ( viewportX - rightMargin ) )
|
|
4735
|
-
{
|
|
4736
|
-
const scroll = Math.max( cursorX - ( viewportSizeX - rightMargin ), 0 );
|
|
4737
|
-
this.setScrollLeft( scroll );
|
|
4738
|
-
}
|
|
4790
|
+
this.updateScrollLeft( cursor );
|
|
4739
4791
|
}
|
|
4740
4792
|
|
|
4741
4793
|
cursorToLeft( text, cursor )
|
|
@@ -4753,15 +4805,18 @@ class CodeEditor
|
|
|
4753
4805
|
|
|
4754
4806
|
this.restartBlink();
|
|
4755
4807
|
|
|
4756
|
-
//
|
|
4757
|
-
|
|
4758
|
-
const cursorX = ( cursor.position * this.charWidth );
|
|
4759
|
-
const currentScrollLeft = this.getScrollLeft();
|
|
4760
|
-
|
|
4761
|
-
if( cursorX < ( currentScrollLeft + leftMargin ) )
|
|
4808
|
+
// Check if we need to add scroll; if not then we might have to reduce it
|
|
4809
|
+
if( !this.updateScrollLeft( cursor ) )
|
|
4762
4810
|
{
|
|
4763
|
-
const
|
|
4764
|
-
this.
|
|
4811
|
+
const leftMargin = this.charWidth;
|
|
4812
|
+
const cursorX = ( cursor.position * this.charWidth );
|
|
4813
|
+
const currentScrollLeft = this.getScrollLeft();
|
|
4814
|
+
|
|
4815
|
+
if( cursorX < ( currentScrollLeft + leftMargin ) )
|
|
4816
|
+
{
|
|
4817
|
+
const scroll = Math.max( cursorX - leftMargin, 0 );
|
|
4818
|
+
this.setScrollLeft( scroll );
|
|
4819
|
+
}
|
|
4765
4820
|
}
|
|
4766
4821
|
}
|
|
4767
4822
|
|
|
@@ -4774,7 +4829,7 @@ class CodeEditor
|
|
|
4774
4829
|
|
|
4775
4830
|
if( resetLeft )
|
|
4776
4831
|
{
|
|
4777
|
-
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
4832
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor, true );
|
|
4778
4833
|
}
|
|
4779
4834
|
|
|
4780
4835
|
const currentScrollTop = this.getScrollTop();
|
|
@@ -4794,7 +4849,7 @@ class CodeEditor
|
|
|
4794
4849
|
|
|
4795
4850
|
if( resetLeft )
|
|
4796
4851
|
{
|
|
4797
|
-
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
4852
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor, true );
|
|
4798
4853
|
}
|
|
4799
4854
|
|
|
4800
4855
|
const currentScrollTop = this.getScrollTop();
|
|
@@ -4823,11 +4878,16 @@ class CodeEditor
|
|
|
4823
4878
|
}
|
|
4824
4879
|
}
|
|
4825
4880
|
|
|
4826
|
-
cursorToPosition( cursor, position )
|
|
4881
|
+
cursorToPosition( cursor, position, updateScroll = false )
|
|
4827
4882
|
{
|
|
4828
4883
|
cursor.position = position;
|
|
4829
4884
|
cursor._left = position * this.charWidth;
|
|
4830
4885
|
cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
|
|
4886
|
+
|
|
4887
|
+
if( updateScroll )
|
|
4888
|
+
{
|
|
4889
|
+
this.updateScrollLeft( cursor );
|
|
4890
|
+
}
|
|
4831
4891
|
}
|
|
4832
4892
|
|
|
4833
4893
|
cursorToLine( cursor, line, resetLeft = false )
|
|
@@ -4918,7 +4978,7 @@ class CodeEditor
|
|
|
4918
4978
|
LX.deleteElement( cursor );
|
|
4919
4979
|
}
|
|
4920
4980
|
|
|
4921
|
-
resetCursorPos( flag, cursor )
|
|
4981
|
+
resetCursorPos( flag, cursor, resetScroll = false )
|
|
4922
4982
|
{
|
|
4923
4983
|
cursor = cursor ?? this.getCurrentCursor();
|
|
4924
4984
|
|
|
@@ -4927,7 +4987,11 @@ class CodeEditor
|
|
|
4927
4987
|
cursor._left = 0;
|
|
4928
4988
|
cursor.style.left = "calc(" + this.xPadding + ")";
|
|
4929
4989
|
cursor.position = 0;
|
|
4930
|
-
|
|
4990
|
+
|
|
4991
|
+
if( resetScroll )
|
|
4992
|
+
{
|
|
4993
|
+
this.setScrollLeft( 0 );
|
|
4994
|
+
}
|
|
4931
4995
|
}
|
|
4932
4996
|
|
|
4933
4997
|
if( flag & CodeEditor.CURSOR_TOP )
|
|
@@ -4935,7 +4999,11 @@ class CodeEditor
|
|
|
4935
4999
|
cursor._top = 0;
|
|
4936
5000
|
cursor.style.top = "0px";
|
|
4937
5001
|
cursor.line = 0;
|
|
4938
|
-
|
|
5002
|
+
|
|
5003
|
+
if( resetScroll )
|
|
5004
|
+
{
|
|
5005
|
+
this.setScrollTop( 0 );
|
|
5006
|
+
}
|
|
4939
5007
|
}
|
|
4940
5008
|
}
|
|
4941
5009
|
|
|
@@ -5024,8 +5092,56 @@ class CodeEditor
|
|
|
5024
5092
|
}
|
|
5025
5093
|
}
|
|
5026
5094
|
|
|
5027
|
-
_addSpaces( n )
|
|
5095
|
+
_addSpaces( cursor, n )
|
|
5028
5096
|
{
|
|
5097
|
+
if( cursor.selection && !cursor.selection.sameLine() )
|
|
5098
|
+
{
|
|
5099
|
+
cursor.selection.invertIfNecessary();
|
|
5100
|
+
|
|
5101
|
+
for( let lidx = cursor.selection.fromY; lidx <= cursor.selection.toY; ++lidx )
|
|
5102
|
+
{
|
|
5103
|
+
// Remove indentation
|
|
5104
|
+
let lineStart = firstNonspaceIndex( this.code.lines[ lidx ] );
|
|
5105
|
+
|
|
5106
|
+
// Only tabs/spaces in the line...
|
|
5107
|
+
if( lineStart == -1 )
|
|
5108
|
+
{
|
|
5109
|
+
lineStart = this.code.lines[ lidx ].length;
|
|
5110
|
+
}
|
|
5111
|
+
|
|
5112
|
+
let indentSpaces = lineStart % this.tabSpaces;
|
|
5113
|
+
indentSpaces = indentSpaces == 0 ? this.tabSpaces : this.tabSpaces - indentSpaces;
|
|
5114
|
+
|
|
5115
|
+
const spacesString = " ".repeat( indentSpaces );
|
|
5116
|
+
|
|
5117
|
+
this.code.lines[ lidx ] = [
|
|
5118
|
+
this.code.lines[ lidx ].slice( 0, lineStart ),
|
|
5119
|
+
spacesString,
|
|
5120
|
+
this.code.lines[ lidx ].slice( lineStart )
|
|
5121
|
+
].join('');
|
|
5122
|
+
|
|
5123
|
+
this.processLine( lidx );
|
|
5124
|
+
|
|
5125
|
+
if( cursor.line === lidx )
|
|
5126
|
+
{
|
|
5127
|
+
this.cursorToString( cursor, spacesString );
|
|
5128
|
+
}
|
|
5129
|
+
|
|
5130
|
+
if( cursor.selection.fromY === lidx )
|
|
5131
|
+
{
|
|
5132
|
+
cursor.selection.fromX = Math.max( cursor.selection.fromX + indentSpaces, 0 );
|
|
5133
|
+
}
|
|
5134
|
+
if( cursor.selection.toY === lidx )
|
|
5135
|
+
{
|
|
5136
|
+
cursor.selection.toX = Math.max( cursor.selection.toX + indentSpaces, 0 );
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5139
|
+
this._processSelection( cursor, undefined, true );
|
|
5140
|
+
}
|
|
5141
|
+
|
|
5142
|
+
return;
|
|
5143
|
+
}
|
|
5144
|
+
|
|
5029
5145
|
for( var i = 0; i < n; ++i )
|
|
5030
5146
|
{
|
|
5031
5147
|
this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
|
|
@@ -5038,41 +5154,79 @@ class CodeEditor
|
|
|
5038
5154
|
|
|
5039
5155
|
_removeSpaces( cursor )
|
|
5040
5156
|
{
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
// Remove indentation
|
|
5044
|
-
let lineStart = firstNonspaceIndex( this.code.lines[ lidx ] );
|
|
5045
|
-
|
|
5046
|
-
// Nothing to remove... we are at the start of the line
|
|
5047
|
-
if( lineStart == 0 )
|
|
5157
|
+
if( cursor.selection )
|
|
5048
5158
|
{
|
|
5049
|
-
|
|
5159
|
+
cursor.selection.invertIfNecessary();
|
|
5050
5160
|
}
|
|
5051
5161
|
|
|
5052
|
-
|
|
5053
|
-
|
|
5162
|
+
const lCount = cursor.selection ? 1 + ( cursor.selection.toY - cursor.selection.fromY ) : 1;
|
|
5163
|
+
|
|
5164
|
+
for( let i = 0; i < lCount; ++i )
|
|
5054
5165
|
{
|
|
5055
|
-
|
|
5056
|
-
}
|
|
5166
|
+
const lidx = ( cursor.selection ? cursor.selection.fromY : cursor.line ) + i;
|
|
5057
5167
|
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
const newStart = Math.max( lineStart - indentSpaces, 0 );
|
|
5168
|
+
// Remove indentation
|
|
5169
|
+
let lineStart = firstNonspaceIndex( this.code.lines[ lidx ] );
|
|
5061
5170
|
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5171
|
+
// Nothing to remove... we are at the start of the line
|
|
5172
|
+
if( lineStart == 0 )
|
|
5173
|
+
{
|
|
5174
|
+
continue;
|
|
5175
|
+
}
|
|
5066
5176
|
|
|
5067
|
-
|
|
5177
|
+
// Only tabs/spaces in the line...
|
|
5178
|
+
if( lineStart == -1 )
|
|
5179
|
+
{
|
|
5180
|
+
lineStart = this.code.lines[ lidx ].length;
|
|
5181
|
+
}
|
|
5068
5182
|
|
|
5069
|
-
|
|
5183
|
+
let indentSpaces = lineStart % this.tabSpaces;
|
|
5184
|
+
indentSpaces = indentSpaces == 0 ? this.tabSpaces : indentSpaces;
|
|
5185
|
+
const newStart = Math.max( lineStart - indentSpaces, 0 );
|
|
5070
5186
|
|
|
5071
|
-
|
|
5187
|
+
this.code.lines[ lidx ] = [
|
|
5188
|
+
this.code.lines[ lidx ].slice( 0, newStart ),
|
|
5189
|
+
this.code.lines[ lidx ].slice( lineStart )
|
|
5190
|
+
].join('');
|
|
5191
|
+
|
|
5192
|
+
this.processLine( lidx );
|
|
5193
|
+
|
|
5194
|
+
if( cursor.line === lidx )
|
|
5195
|
+
{
|
|
5196
|
+
this.cursorToString( cursor, " ".repeat( indentSpaces ), true );
|
|
5197
|
+
}
|
|
5198
|
+
|
|
5199
|
+
if( cursor.selection )
|
|
5200
|
+
{
|
|
5201
|
+
if( cursor.selection.fromY === lidx )
|
|
5202
|
+
{
|
|
5203
|
+
cursor.selection.fromX = Math.max( cursor.selection.fromX - indentSpaces, 0 );
|
|
5204
|
+
}
|
|
5205
|
+
if( cursor.selection.toY === lidx )
|
|
5206
|
+
{
|
|
5207
|
+
cursor.selection.toX = Math.max( cursor.selection.toX - indentSpaces, 0 );
|
|
5208
|
+
}
|
|
5209
|
+
|
|
5210
|
+
this._processSelection( cursor, undefined, true );
|
|
5211
|
+
}
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
|
|
5215
|
+
updateScrollLeft( cursor )
|
|
5216
|
+
{
|
|
5217
|
+
cursor = cursor ?? this.getCurrentCursor();
|
|
5218
|
+
|
|
5219
|
+
const rightMargin = this.charWidth;
|
|
5220
|
+
const cursorX = ( cursor.position * this.charWidth );
|
|
5221
|
+
const currentScrollLeft = this.getScrollLeft();
|
|
5222
|
+
const viewportSizeX = this.codeScroller.clientWidth - CodeEditor.LINE_GUTTER_WIDTH; // Gutter offset
|
|
5223
|
+
const viewportX = viewportSizeX + currentScrollLeft;
|
|
5224
|
+
|
|
5225
|
+
if( cursorX >= ( viewportX - rightMargin ) )
|
|
5072
5226
|
{
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5227
|
+
const scroll = Math.max( cursorX - ( viewportSizeX - rightMargin ), 0 );
|
|
5228
|
+
this.setScrollLeft( scroll );
|
|
5229
|
+
return true;
|
|
5076
5230
|
}
|
|
5077
5231
|
}
|
|
5078
5232
|
|
|
@@ -5190,14 +5344,16 @@ class CodeEditor
|
|
|
5190
5344
|
{
|
|
5191
5345
|
if( type == 'vertical' )
|
|
5192
5346
|
{
|
|
5193
|
-
const scrollBarHeight = this.vScrollbar.thumb.parentElement.offsetHeight;
|
|
5194
|
-
const scrollThumbHeight = this.vScrollbar.thumb.offsetHeight;
|
|
5195
|
-
|
|
5196
5347
|
const scrollHeight = this.codeScroller.scrollHeight - this.codeScroller.clientHeight;
|
|
5197
|
-
const currentScroll = this.codeScroller.scrollTop;
|
|
5198
5348
|
|
|
5199
|
-
|
|
5200
|
-
|
|
5349
|
+
if( scrollHeight > 0 )
|
|
5350
|
+
{
|
|
5351
|
+
const scrollBarHeight = this.vScrollbar.thumb.parentElement.offsetHeight;
|
|
5352
|
+
const scrollThumbHeight = this.vScrollbar.thumb.offsetHeight;
|
|
5353
|
+
const currentScroll = this.codeScroller.scrollTop;
|
|
5354
|
+
this.vScrollbar.thumb._top = ( currentScroll / scrollHeight ) * ( scrollBarHeight - scrollThumbHeight );
|
|
5355
|
+
this.vScrollbar.thumb.style.top = this.vScrollbar.thumb._top + "px";
|
|
5356
|
+
}
|
|
5201
5357
|
}
|
|
5202
5358
|
else
|
|
5203
5359
|
{
|
|
@@ -5206,14 +5362,16 @@ class CodeEditor
|
|
|
5206
5362
|
this.codeScroller.scrollLeft += value;
|
|
5207
5363
|
}
|
|
5208
5364
|
|
|
5209
|
-
|
|
5210
|
-
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
5211
|
-
|
|
5365
|
+
// Only when scroll is needed
|
|
5212
5366
|
const scrollWidth = this.codeScroller.scrollWidth - this.codeScroller.clientWidth;
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5367
|
+
if( scrollWidth > 0 )
|
|
5368
|
+
{
|
|
5369
|
+
const scrollBarWidth = this.hScrollbar.thumb.parentElement.offsetWidth;
|
|
5370
|
+
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
5371
|
+
const currentScroll = this.codeScroller.scrollLeft;
|
|
5372
|
+
this.hScrollbar.thumb._left = ( currentScroll / scrollWidth ) * ( scrollBarWidth - scrollThumbWidth );
|
|
5373
|
+
this.hScrollbar.thumb.style.left = this.hScrollbar.thumb._left + "px";
|
|
5374
|
+
}
|
|
5217
5375
|
}
|
|
5218
5376
|
}
|
|
5219
5377
|
|
|
@@ -5235,7 +5393,6 @@ class CodeEditor
|
|
|
5235
5393
|
const currentScroll = (this.hScrollbar.thumb._left * scrollWidth) / ( scrollBarWidth - scrollThumbWidth );
|
|
5236
5394
|
this.codeScroller.scrollLeft = currentScroll;
|
|
5237
5395
|
|
|
5238
|
-
|
|
5239
5396
|
this._discardScroll = true;
|
|
5240
5397
|
}
|
|
5241
5398
|
|
|
@@ -5313,7 +5470,7 @@ class CodeEditor
|
|
|
5313
5470
|
return [ word, from, to ];
|
|
5314
5471
|
}
|
|
5315
5472
|
|
|
5316
|
-
_measureChar( char = "
|
|
5473
|
+
_measureChar( char = "M", useFloating = true, getBB = false )
|
|
5317
5474
|
{
|
|
5318
5475
|
const parentContainer = LX.makeContainer( null, "lexcodeeditor", "", document.body );
|
|
5319
5476
|
const container = LX.makeContainer( null, "code", "", parentContainer );
|
|
@@ -5500,14 +5657,16 @@ class CodeEditor
|
|
|
5500
5657
|
return;
|
|
5501
5658
|
}
|
|
5502
5659
|
|
|
5660
|
+
const maxX = this.codeScroller.clientWidth - 256; // Viewport - box width
|
|
5661
|
+
|
|
5503
5662
|
// Select always first option
|
|
5504
5663
|
this.autocomplete.firstChild.classList.add( 'selected' );
|
|
5505
5664
|
|
|
5506
5665
|
// Show box
|
|
5507
5666
|
this.autocomplete.classList.toggle( 'show', true );
|
|
5508
5667
|
this.autocomplete.classList.toggle( 'no-scrollbar', !( this.autocomplete.scrollHeight > this.autocomplete.offsetHeight ) );
|
|
5509
|
-
this.autocomplete.style.left = (cursor._left + CodeEditor.LINE_GUTTER_WIDTH - this.getScrollLeft())
|
|
5510
|
-
this.autocomplete.style.top = (cursor._top +
|
|
5668
|
+
this.autocomplete.style.left = `${ Math.min( cursor._left + CodeEditor.LINE_GUTTER_WIDTH - this.getScrollLeft(), maxX ) }px`;
|
|
5669
|
+
this.autocomplete.style.top = `${ ( cursor._top + this._verticalTopOffset + this.lineHeight - this.getScrollTop() ) }px`;
|
|
5511
5670
|
|
|
5512
5671
|
this.isAutoCompleteActive = true;
|
|
5513
5672
|
}
|
|
@@ -5588,21 +5747,33 @@ class CodeEditor
|
|
|
5588
5747
|
|
|
5589
5748
|
const [ word, idx ] = this._getSelectedAutoComplete();
|
|
5590
5749
|
const offset = dir == 'down' ? 1 : -1;
|
|
5750
|
+
const fIdx = idx + offset;
|
|
5751
|
+
|
|
5752
|
+
const autocompleteRowHeight = 22;
|
|
5753
|
+
const autocompleteHeight = 132;
|
|
5591
5754
|
|
|
5592
5755
|
if( dir == 'down' )
|
|
5593
5756
|
{
|
|
5594
|
-
if(
|
|
5757
|
+
if( fIdx >= this.autocomplete.childElementCount ) return;
|
|
5758
|
+
|
|
5759
|
+
if( ( ( idx + offset * 2 ) * autocompleteRowHeight ) - this.autocomplete.scrollTop > autocompleteHeight )
|
|
5760
|
+
{
|
|
5761
|
+
this.autocomplete.scrollTop += autocompleteRowHeight;
|
|
5762
|
+
}
|
|
5595
5763
|
}
|
|
5596
5764
|
else if( dir == 'up')
|
|
5597
5765
|
{
|
|
5598
|
-
if(
|
|
5599
|
-
}
|
|
5766
|
+
if( fIdx < 0 ) return;
|
|
5600
5767
|
|
|
5601
|
-
|
|
5768
|
+
if( ( fIdx * autocompleteRowHeight ) < this.autocomplete.scrollTop )
|
|
5769
|
+
{
|
|
5770
|
+
this.autocomplete.scrollTop -= autocompleteRowHeight;
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5602
5773
|
|
|
5603
5774
|
// Remove selected from the current word and add it to the next one
|
|
5604
|
-
this.autocomplete.childNodes[ idx ].classList.remove('selected');
|
|
5605
|
-
this.autocomplete.childNodes[ idx + offset ].classList.add('selected');
|
|
5775
|
+
this.autocomplete.childNodes[ idx ].classList.remove( 'selected' );
|
|
5776
|
+
this.autocomplete.childNodes[ idx + offset ].classList.add( 'selected' );
|
|
5606
5777
|
}
|
|
5607
5778
|
|
|
5608
5779
|
showSearchBox( clear )
|
|
@@ -5904,7 +6075,7 @@ class CodeEditor
|
|
|
5904
6075
|
this.fontSize = size;
|
|
5905
6076
|
const r = document.querySelector( ':root' );
|
|
5906
6077
|
r.style.setProperty( "--code-editor-font-size", `${ this.fontSize }px` );
|
|
5907
|
-
this.charWidth = this._measureChar(
|
|
6078
|
+
this.charWidth = this._measureChar();
|
|
5908
6079
|
|
|
5909
6080
|
window.localStorage.setItem( "lexcodeeditor-font-size", this.fontSize );
|
|
5910
6081
|
|
|
@@ -6019,9 +6190,9 @@ CodeEditor.declarationKeywords =
|
|
|
6019
6190
|
CodeEditor.keywords =
|
|
6020
6191
|
{
|
|
6021
6192
|
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
|
|
6022
|
-
'arguments', 'extends', 'instanceof', 'Infinity'],
|
|
6193
|
+
'arguments', 'extends', 'instanceof', 'Infinity', 'get'],
|
|
6023
6194
|
'TypeScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'class', 'extends', 'instanceof', 'Infinity', 'private', 'public', 'protected', 'interface',
|
|
6024
|
-
'enum', 'type'],
|
|
6195
|
+
'enum', 'type', 'get'],
|
|
6025
6196
|
'C': ['int', 'float', 'double', 'long', 'short', 'char', 'const', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'signed', 'volatile', 'unsigned', 'static', 'extern', 'enum', 'register',
|
|
6026
6197
|
'union'],
|
|
6027
6198
|
'C++': [...CodeEditor.nativeTypes["C++"], 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'true', 'false', 'auto', 'class', 'struct', 'typedef', 'nullptr',
|
|
@@ -6071,7 +6242,8 @@ CodeEditor.types =
|
|
|
6071
6242
|
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent',
|
|
6072
6243
|
'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element'],
|
|
6073
6244
|
'TypeScript': ['arguments', 'constructor', 'null', 'typeof', 'debugger', 'abstract', 'Object', 'string', 'String', 'Function', 'Boolean', 'boolean', 'Error', 'Number', 'number', 'TextEncoder',
|
|
6074
|
-
'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent', 'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element'
|
|
6245
|
+
'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent', 'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element', 'bigint', 'unknown', 'any',
|
|
6246
|
+
'Record'],
|
|
6075
6247
|
'Rust': ['u128'],
|
|
6076
6248
|
'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
|
|
6077
6249
|
'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
|
|
@@ -6094,8 +6266,8 @@ CodeEditor.builtIn =
|
|
|
6094
6266
|
|
|
6095
6267
|
CodeEditor.statements =
|
|
6096
6268
|
{
|
|
6097
|
-
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
|
|
6098
|
-
'TypeScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
|
|
6269
|
+
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'default', 'export', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
|
|
6270
|
+
'TypeScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'default', 'export', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
|
|
6099
6271
|
'CSS': ['@', 'import'],
|
|
6100
6272
|
'C': ['for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'default', 'goto', 'do'],
|
|
6101
6273
|
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog', 'default'],
|