lexgui 0.1.23 → 0.1.24
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/components/codeeditor.js +281 -107
- package/build/lexgui.css +21 -9
- package/build/lexgui.js +1 -1
- package/build/lexgui.module.js +1 -1
- package/changelog.md +37 -20
- package/examples/code_editor.html +1 -1
- package/package.json +1 -1
|
@@ -333,7 +333,7 @@ class CodeEditor {
|
|
|
333
333
|
|
|
334
334
|
// Add main cursor
|
|
335
335
|
{
|
|
336
|
-
this._addCursor( 0, 0, true );
|
|
336
|
+
this._addCursor( 0, 0, true, true );
|
|
337
337
|
|
|
338
338
|
Object.defineProperty( this, 'line', {
|
|
339
339
|
get: (v) => { return this._getCurrentCursor().line }
|
|
@@ -739,14 +739,18 @@ class CodeEditor {
|
|
|
739
739
|
if(idx > 0) this.cursorToString( cursor, prestring );
|
|
740
740
|
this.setScrollLeft( 0 );
|
|
741
741
|
|
|
742
|
+
// Merge cursors
|
|
743
|
+
this.mergeCursors( ln );
|
|
744
|
+
|
|
742
745
|
if( e.shiftKey && !e.cancelShift )
|
|
743
746
|
{
|
|
744
747
|
// Get last selection range
|
|
745
748
|
if( this.selection )
|
|
746
|
-
|
|
749
|
+
lastX += this.selection.chars;
|
|
747
750
|
|
|
748
751
|
if( !this.selection )
|
|
749
752
|
this.startSelection( cursor );
|
|
753
|
+
|
|
750
754
|
var string = this.code.lines[ ln ].substring( idx, lastX );
|
|
751
755
|
if( this.selection.sameLine() )
|
|
752
756
|
this.selection.selectInline( idx, cursor.line, this.measureString( string ) );
|
|
@@ -781,6 +785,9 @@ class CodeEditor {
|
|
|
781
785
|
|
|
782
786
|
const last_char = ( this.code.clientWidth / this.charWidth )|0;
|
|
783
787
|
this.setScrollLeft( cursor.position >= last_char ? ( cursor.position - last_char ) * this.charWidth : 0 );
|
|
788
|
+
|
|
789
|
+
// Merge cursors
|
|
790
|
+
this.mergeCursors( ln );
|
|
784
791
|
});
|
|
785
792
|
|
|
786
793
|
this.action( 'Enter', true, ( ln, cursor, e ) => {
|
|
@@ -806,6 +813,7 @@ class CodeEditor {
|
|
|
806
813
|
this.code.lines.splice( cursor.line + 1, 0, "" );
|
|
807
814
|
this.code.lines[cursor.line + 1] = this.code.lines[ ln ].substr( cursor.position ); // new line (below)
|
|
808
815
|
this.code.lines[ ln ] = this.code.lines[ ln ].substr( 0, cursor.position ); // line above
|
|
816
|
+
|
|
809
817
|
this.lineDown( cursor, true );
|
|
810
818
|
|
|
811
819
|
// Check indentation
|
|
@@ -864,24 +872,20 @@ class CodeEditor {
|
|
|
864
872
|
if( e.shiftKey ) {
|
|
865
873
|
if( !this.selection )
|
|
866
874
|
this.startSelection( cursor );
|
|
875
|
+
} else {
|
|
876
|
+
this.endSelection();
|
|
877
|
+
}
|
|
867
878
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
879
|
+
const canGoDown = this.lineDown( cursor );
|
|
880
|
+
const letter = this.getCharAtPos( cursor );
|
|
881
|
+
|
|
882
|
+
// Go to end of line if out of range
|
|
883
|
+
if( !letter || !canGoDown ) {
|
|
884
|
+
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
885
|
+
}
|
|
874
886
|
|
|
887
|
+
if( e.shiftKey ) {
|
|
875
888
|
this.processSelection( e );
|
|
876
|
-
} else {
|
|
877
|
-
|
|
878
|
-
if( this.code.lines[ ln + 1 ] == undefined )
|
|
879
|
-
return;
|
|
880
|
-
this.endSelection();
|
|
881
|
-
this.lineDown( cursor );
|
|
882
|
-
// Go to end of line if out of line
|
|
883
|
-
var letter = this.getCharAtPos( cursor );
|
|
884
|
-
if( !letter ) this.actions['End'].callback(cursor.line, cursor, e);
|
|
885
889
|
}
|
|
886
890
|
}
|
|
887
891
|
// Move down autocomplete selection
|
|
@@ -1212,25 +1216,36 @@ class CodeEditor {
|
|
|
1212
1216
|
}
|
|
1213
1217
|
}
|
|
1214
1218
|
|
|
1215
|
-
_addCursor( line = 0, position = 0, isMain = false ) {
|
|
1219
|
+
_addCursor( line = 0, position = 0, force, isMain = false ) {
|
|
1220
|
+
|
|
1221
|
+
// If cursor in that position exists, remove it instead..
|
|
1222
|
+
const exists = Array.from( this.cursors.children ).find( v => v.position == position && v.line == line );
|
|
1223
|
+
if( exists && !force )
|
|
1224
|
+
{
|
|
1225
|
+
if( !exists.isMain )
|
|
1226
|
+
exists.remove();
|
|
1227
|
+
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1216
1230
|
|
|
1217
1231
|
let cursor = document.createElement( 'div' );
|
|
1218
1232
|
cursor.className = "cursor";
|
|
1219
1233
|
cursor.innerHTML = " ";
|
|
1220
|
-
cursor.
|
|
1234
|
+
cursor.isMain = isMain;
|
|
1221
1235
|
cursor._left = position * this.charWidth;
|
|
1222
1236
|
cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
|
|
1223
1237
|
cursor._top = line * this.lineHeight;
|
|
1224
1238
|
cursor.style.top = cursor._top + "px";
|
|
1225
1239
|
cursor._position = position;
|
|
1226
1240
|
cursor._line = line;
|
|
1227
|
-
cursor.print = (function() { console.log( this
|
|
1241
|
+
cursor.print = (function() { console.log( this._line, this._position ) }).bind( cursor );
|
|
1242
|
+
cursor.isLast = (function() { return this.cursors.lastChild == cursor; }).bind( this );
|
|
1228
1243
|
|
|
1229
1244
|
Object.defineProperty( cursor, 'line', {
|
|
1230
1245
|
get: (v) => { return cursor._line },
|
|
1231
1246
|
set: (v) => {
|
|
1232
1247
|
cursor._line = v;
|
|
1233
|
-
if( cursor.
|
|
1248
|
+
if( cursor.isMain ) this._setActiveLine( v );
|
|
1234
1249
|
}
|
|
1235
1250
|
} );
|
|
1236
1251
|
|
|
@@ -1238,7 +1253,7 @@ class CodeEditor {
|
|
|
1238
1253
|
get: (v) => { return cursor._position },
|
|
1239
1254
|
set: (v) => {
|
|
1240
1255
|
cursor._position = v;
|
|
1241
|
-
if( cursor.
|
|
1256
|
+
if( cursor.isMain ) this._updateDataInfoPanel( "@cursor-pos", "Col " + v );
|
|
1242
1257
|
}
|
|
1243
1258
|
} );
|
|
1244
1259
|
|
|
@@ -1273,6 +1288,10 @@ class CodeEditor {
|
|
|
1273
1288
|
|
|
1274
1289
|
_addUndoStep( cursor, force, deleteRedo = true ) {
|
|
1275
1290
|
|
|
1291
|
+
// Only the mainc cursor stores undo steps
|
|
1292
|
+
if( !cursor.isMain )
|
|
1293
|
+
return;
|
|
1294
|
+
|
|
1276
1295
|
const d = new Date();
|
|
1277
1296
|
const current = d.getTime();
|
|
1278
1297
|
|
|
@@ -1281,7 +1300,7 @@ class CodeEditor {
|
|
|
1281
1300
|
if( !this._lastTime ) {
|
|
1282
1301
|
this._lastTime = current;
|
|
1283
1302
|
} else {
|
|
1284
|
-
if( ( current - this._lastTime ) >
|
|
1303
|
+
if( ( current - this._lastTime ) > 2000 ){
|
|
1285
1304
|
this._lastTime = null;
|
|
1286
1305
|
} else {
|
|
1287
1306
|
// If time not enough, reset timer
|
|
@@ -1299,22 +1318,78 @@ class CodeEditor {
|
|
|
1299
1318
|
|
|
1300
1319
|
this.code.undoSteps.push( {
|
|
1301
1320
|
lines: LX.deepCopy( this.code.lines ),
|
|
1302
|
-
|
|
1303
|
-
line: cursor.line,
|
|
1304
|
-
position: cursor.position
|
|
1321
|
+
cursors: this.saveCursors()
|
|
1305
1322
|
} );
|
|
1306
1323
|
}
|
|
1307
1324
|
|
|
1325
|
+
_doUndo( cursor ) {
|
|
1326
|
+
|
|
1327
|
+
if( !this.code.undoSteps.length )
|
|
1328
|
+
return;
|
|
1329
|
+
|
|
1330
|
+
this._addRedoStep( cursor );
|
|
1331
|
+
|
|
1332
|
+
// Extract info from the last code state
|
|
1333
|
+
const step = this.code.undoSteps.pop();
|
|
1334
|
+
|
|
1335
|
+
// Set old state lines
|
|
1336
|
+
this.code.lines = step.lines;
|
|
1337
|
+
this.processLines();
|
|
1338
|
+
|
|
1339
|
+
this._removeSecondaryCursors();
|
|
1340
|
+
|
|
1341
|
+
for( let i = 0; i < step.cursors.length; ++i )
|
|
1342
|
+
{
|
|
1343
|
+
var currentCursor = this.cursors.children[ i ];
|
|
1344
|
+
|
|
1345
|
+
// Generate new if needed
|
|
1346
|
+
if( !currentCursor )
|
|
1347
|
+
currentCursor = this._addCursor();
|
|
1348
|
+
|
|
1349
|
+
this.restoreCursor( currentCursor, step.cursors[ i ] );
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1308
1353
|
_addRedoStep( cursor ) {
|
|
1309
1354
|
|
|
1355
|
+
// Only the mainc cursor stores redo steps
|
|
1356
|
+
if( !cursor.isMain )
|
|
1357
|
+
return;
|
|
1358
|
+
|
|
1310
1359
|
this.code.redoSteps.push( {
|
|
1311
1360
|
lines: LX.deepCopy( this.code.lines ),
|
|
1312
|
-
|
|
1313
|
-
line: cursor.line,
|
|
1314
|
-
position: cursor.position
|
|
1361
|
+
cursors: this.saveCursors()
|
|
1315
1362
|
} );
|
|
1316
1363
|
}
|
|
1317
1364
|
|
|
1365
|
+
_doRedo( cursor ) {
|
|
1366
|
+
|
|
1367
|
+
if( !this.code.redoSteps.length )
|
|
1368
|
+
return;
|
|
1369
|
+
|
|
1370
|
+
this._addUndoStep( cursor, true, false);
|
|
1371
|
+
|
|
1372
|
+
// Extract info from the next saved code state
|
|
1373
|
+
const step = this.code.redoSteps.pop();
|
|
1374
|
+
|
|
1375
|
+
// Set old state lines
|
|
1376
|
+
this.code.lines = step.lines;
|
|
1377
|
+
this.processLines();
|
|
1378
|
+
|
|
1379
|
+
this._removeSecondaryCursors();
|
|
1380
|
+
|
|
1381
|
+
for( let i = 0; i < step.cursors.length; ++i )
|
|
1382
|
+
{
|
|
1383
|
+
var currentCursor = this.cursors.children[ i ];
|
|
1384
|
+
|
|
1385
|
+
// Generate new if needed
|
|
1386
|
+
if( !currentCursor )
|
|
1387
|
+
currentCursor = this._addCursor();
|
|
1388
|
+
|
|
1389
|
+
this.restoreCursor( currentCursor, step.cursors[ i ] );
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1318
1393
|
_changeLanguage( lang ) {
|
|
1319
1394
|
|
|
1320
1395
|
this.code.language = lang;
|
|
@@ -1610,7 +1685,7 @@ class CodeEditor {
|
|
|
1610
1685
|
|
|
1611
1686
|
processMouse( e ) {
|
|
1612
1687
|
|
|
1613
|
-
if( !e.target.classList.contains('code') ) return;
|
|
1688
|
+
if( !e.target.classList.contains('code') && !e.target.classList.contains('codetabsarea') ) return;
|
|
1614
1689
|
if( !this.code ) return;
|
|
1615
1690
|
|
|
1616
1691
|
var cursor = this._getCurrentCursor();
|
|
@@ -1618,11 +1693,8 @@ class CodeEditor {
|
|
|
1618
1693
|
var mouse_pos = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
|
|
1619
1694
|
|
|
1620
1695
|
// Discard out of lines click...
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
var ln = (mouse_pos[1] / this.lineHeight)|0;
|
|
1624
|
-
if(this.code.lines[ ln ] == undefined) return;
|
|
1625
|
-
}
|
|
1696
|
+
var ln = ( mouse_pos[ 1 ] / this.lineHeight ) | 0;
|
|
1697
|
+
if( ln < 0 ) return;
|
|
1626
1698
|
|
|
1627
1699
|
if( e.type == 'mousedown' )
|
|
1628
1700
|
{
|
|
@@ -1730,11 +1802,12 @@ class CodeEditor {
|
|
|
1730
1802
|
var position = [( e.clientX - code_rect.x ) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
|
|
1731
1803
|
var ln = (position[ 1 ] / this.lineHeight)|0;
|
|
1732
1804
|
|
|
1733
|
-
|
|
1734
|
-
|
|
1805
|
+
// Check out of range line
|
|
1806
|
+
const outOfRange = ln > this.code.lines.length - 1;
|
|
1807
|
+
ln = Math.min( ln, this.code.lines.length - 1 );
|
|
1735
1808
|
|
|
1736
1809
|
var ch = ( ( position[ 0 ] - parseInt( this.xPadding ) + 3) / this.charWidth )|0;
|
|
1737
|
-
var string = this.code.lines[ ln ].slice( 0, ch );
|
|
1810
|
+
var string = outOfRange ? this.code.lines[ ln ] : this.code.lines[ ln ].slice( 0, ch );
|
|
1738
1811
|
|
|
1739
1812
|
// Move main cursor there...
|
|
1740
1813
|
if( !e.altKey )
|
|
@@ -1911,10 +1984,29 @@ class CodeEditor {
|
|
|
1911
1984
|
}
|
|
1912
1985
|
}
|
|
1913
1986
|
|
|
1914
|
-
async processKey(
|
|
1987
|
+
async processKey( e ) {
|
|
1915
1988
|
|
|
1916
1989
|
const numCursors = this.cursors.childElementCount;
|
|
1917
1990
|
|
|
1991
|
+
if( !this.code || e.srcElement.constructor != HTMLDivElement )
|
|
1992
|
+
return;
|
|
1993
|
+
|
|
1994
|
+
const key = e.key ?? e.detail.key;
|
|
1995
|
+
const target = e.detail.targetCursor;
|
|
1996
|
+
|
|
1997
|
+
if( target !== undefined )
|
|
1998
|
+
{
|
|
1999
|
+
this.processKeyAtTargetCursor( e, key, target );
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// By cursor keys
|
|
2004
|
+
|
|
2005
|
+
this._lastProcessedCursorIndex = null;
|
|
2006
|
+
|
|
2007
|
+
var lastProcessedCursor = null;
|
|
2008
|
+
var cursorOffset = new LX.vec2( 0, 0 );
|
|
2009
|
+
|
|
1918
2010
|
for( var i = 0; i < numCursors; i++ )
|
|
1919
2011
|
{
|
|
1920
2012
|
let cursor = this.cursors.children[ i ];
|
|
@@ -1923,16 +2015,83 @@ class CodeEditor {
|
|
|
1923
2015
|
if( !cursor )
|
|
1924
2016
|
break;
|
|
1925
2017
|
|
|
1926
|
-
|
|
2018
|
+
// Arrows don't modify code lines.. And only add offset if in the same line
|
|
2019
|
+
if( lastProcessedCursor && lastProcessedCursor.line == cursor.line && !key.includes( 'Arrow' ) )
|
|
2020
|
+
{
|
|
2021
|
+
cursor.position += cursorOffset.x;
|
|
2022
|
+
cursor.line += cursorOffset.y;
|
|
2023
|
+
|
|
2024
|
+
this.relocateCursors();
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
lastProcessedCursor = this.saveCursor( cursor );
|
|
2028
|
+
this._lastProcessedCursorIndex = i;
|
|
2029
|
+
|
|
2030
|
+
this._processKeyAtCursor( e, key, cursor );
|
|
2031
|
+
|
|
2032
|
+
cursorOffset.x += ( cursor.position - lastProcessedCursor.position );
|
|
2033
|
+
cursorOffset.y += ( cursor.line - lastProcessedCursor.line );
|
|
1927
2034
|
}
|
|
2035
|
+
|
|
2036
|
+
// Global keys
|
|
2037
|
+
|
|
2038
|
+
this._processGlobalKeys( e, key );
|
|
2039
|
+
|
|
2040
|
+
// Clear tmp
|
|
2041
|
+
|
|
2042
|
+
delete this._lastProcessedCursorIndex;
|
|
1928
2043
|
}
|
|
1929
2044
|
|
|
1930
|
-
async
|
|
2045
|
+
async processKeyAtTargetCursor( e, key, targetIdx ) {
|
|
1931
2046
|
|
|
1932
|
-
|
|
2047
|
+
let cursor = this.cursors.children[ targetIdx ];
|
|
2048
|
+
|
|
2049
|
+
// We could delete secondary cursor while iterating..
|
|
2050
|
+
if( !cursor )
|
|
1933
2051
|
return;
|
|
1934
2052
|
|
|
1935
|
-
|
|
2053
|
+
this._processKeyAtCursor( e, key, cursor );
|
|
2054
|
+
this._processGlobalKeys( e, key );
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
async _processGlobalKeys( e, key ) {
|
|
2058
|
+
|
|
2059
|
+
let cursor = this._getCurrentCursor();
|
|
2060
|
+
|
|
2061
|
+
if( e.ctrlKey || e.metaKey )
|
|
2062
|
+
{
|
|
2063
|
+
e.preventDefault();
|
|
2064
|
+
|
|
2065
|
+
switch( key.toLowerCase() ) {
|
|
2066
|
+
case 'a': // select all
|
|
2067
|
+
this.selectAll();
|
|
2068
|
+
break;
|
|
2069
|
+
case 'f': // find/search
|
|
2070
|
+
this.showSearchBox();
|
|
2071
|
+
break;
|
|
2072
|
+
case 'g': // find line
|
|
2073
|
+
this.showSearchLineBox();
|
|
2074
|
+
break;
|
|
2075
|
+
case 's': // save
|
|
2076
|
+
this.onsave( this.getText() );
|
|
2077
|
+
break;
|
|
2078
|
+
case 'y': // redo
|
|
2079
|
+
this._doRedo( cursor );
|
|
2080
|
+
break;
|
|
2081
|
+
case 'z': // undo
|
|
2082
|
+
this._doUndo( cursor );
|
|
2083
|
+
break;
|
|
2084
|
+
case '+': // increase size
|
|
2085
|
+
this._increaseFontSize();
|
|
2086
|
+
break;
|
|
2087
|
+
case '-': // decrease size
|
|
2088
|
+
this._decreaseFontSize();
|
|
2089
|
+
break;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
async _processKeyAtCursor( e, key, cursor ) {
|
|
1936
2095
|
|
|
1937
2096
|
const skip_undo = e.detail.skip_undo ?? false;
|
|
1938
2097
|
|
|
@@ -1945,32 +2104,20 @@ class CodeEditor {
|
|
|
1945
2104
|
|
|
1946
2105
|
// Check combinations
|
|
1947
2106
|
|
|
2107
|
+
const isLastCursor = cursor.isLast();
|
|
2108
|
+
|
|
1948
2109
|
if( e.ctrlKey || e.metaKey )
|
|
1949
2110
|
{
|
|
1950
2111
|
switch( key.toLowerCase() ) {
|
|
1951
|
-
case 'a': // select all
|
|
1952
|
-
e.preventDefault();
|
|
1953
|
-
this.selectAll( cursor );
|
|
1954
|
-
break;
|
|
1955
2112
|
case 'c': // copy
|
|
2113
|
+
// TODO: COPY TEXT FROM EVERY CURSOR
|
|
1956
2114
|
this._copyContent( cursor );
|
|
1957
2115
|
return;
|
|
1958
2116
|
case 'd': // duplicate line
|
|
1959
2117
|
e.preventDefault();
|
|
2118
|
+
// TODO: UPDATE NEXT CURSOR ON MODIFY STATE
|
|
1960
2119
|
this._duplicateLine( lidx, cursor );
|
|
1961
2120
|
return;
|
|
1962
|
-
case 'f': // find/search
|
|
1963
|
-
e.preventDefault();
|
|
1964
|
-
this.showSearchBox();
|
|
1965
|
-
return;
|
|
1966
|
-
case 'g': // find line
|
|
1967
|
-
e.preventDefault();
|
|
1968
|
-
this.showSearchLineBox();
|
|
1969
|
-
return;
|
|
1970
|
-
case 's': // save
|
|
1971
|
-
e.preventDefault();
|
|
1972
|
-
this.onsave( this.getText() );
|
|
1973
|
-
return;
|
|
1974
2121
|
case 'v': // paste
|
|
1975
2122
|
this._pasteContent( cursor );
|
|
1976
2123
|
return;
|
|
@@ -1978,39 +2125,13 @@ class CodeEditor {
|
|
|
1978
2125
|
this._cutContent( cursor );
|
|
1979
2126
|
this.hideAutoCompleteBox();
|
|
1980
2127
|
return;
|
|
1981
|
-
case 'y': // redo
|
|
1982
|
-
if( !this.code.redoSteps.length )
|
|
1983
|
-
return;
|
|
1984
|
-
this._addUndoStep( cursor, true, false);
|
|
1985
|
-
const redo_step = this.code.redoSteps.pop();
|
|
1986
|
-
this.code.lines = redo_step.lines;
|
|
1987
|
-
this.processLines();
|
|
1988
|
-
this.restoreCursor( cursor, redo_step.cursor );
|
|
1989
|
-
return;
|
|
1990
|
-
case 'z': // undo
|
|
1991
|
-
if( !this.code.undoSteps.length )
|
|
1992
|
-
return;
|
|
1993
|
-
this._addRedoStep( cursor );
|
|
1994
|
-
const undo_step = this.code.undoSteps.pop();
|
|
1995
|
-
this.code.lines = undo_step.lines;
|
|
1996
|
-
this.processLines();
|
|
1997
|
-
this.restoreCursor( cursor, undo_step.cursor );
|
|
1998
|
-
return;
|
|
1999
|
-
case '+': // increase size
|
|
2000
|
-
e.preventDefault();
|
|
2001
|
-
this._increaseFontSize();
|
|
2002
|
-
return;
|
|
2003
|
-
case '-': // decrease size
|
|
2004
|
-
e.preventDefault();
|
|
2005
|
-
this._decreaseFontSize();
|
|
2006
|
-
return;
|
|
2007
2128
|
case 'arrowdown': // add cursor below only for the main cursor..
|
|
2008
|
-
if(
|
|
2129
|
+
if( isLastCursor && this.code.lines[ lidx + 1 ] != undefined )
|
|
2009
2130
|
{
|
|
2010
|
-
var new_cursor = this._addCursor( cursor.line, cursor.position );
|
|
2131
|
+
var new_cursor = this._addCursor( cursor.line, cursor.position, true );
|
|
2011
2132
|
this.lineDown( new_cursor );
|
|
2012
|
-
return;
|
|
2013
2133
|
}
|
|
2134
|
+
return;
|
|
2014
2135
|
}
|
|
2015
2136
|
}
|
|
2016
2137
|
|
|
@@ -2136,6 +2257,9 @@ class CodeEditor {
|
|
|
2136
2257
|
async _pasteContent( cursor ) {
|
|
2137
2258
|
|
|
2138
2259
|
let text = await navigator.clipboard.readText();
|
|
2260
|
+
|
|
2261
|
+
this._addUndoStep( cursor, true );
|
|
2262
|
+
|
|
2139
2263
|
this.appendText( text, cursor );
|
|
2140
2264
|
}
|
|
2141
2265
|
|
|
@@ -2152,18 +2276,18 @@ class CodeEditor {
|
|
|
2152
2276
|
if( this.selection ) this.selection.invertIfNecessary();
|
|
2153
2277
|
|
|
2154
2278
|
const separator = "_NEWLINE_";
|
|
2155
|
-
let code = this.code.lines.join(separator);
|
|
2279
|
+
let code = this.code.lines.join( separator );
|
|
2156
2280
|
|
|
2157
2281
|
// Get linear start index
|
|
2158
2282
|
let index = 0;
|
|
2159
2283
|
|
|
2160
|
-
for(let i = 0; i <= this.selection.fromY; i++)
|
|
2161
|
-
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[ i ].length);
|
|
2284
|
+
for( let i = 0; i <= this.selection.fromY; i++ )
|
|
2285
|
+
index += ( i == this.selection.fromY ? this.selection.fromX : this.code.lines[ i ].length );
|
|
2162
2286
|
|
|
2163
2287
|
index += this.selection.fromY * separator.length;
|
|
2164
|
-
const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
|
|
2165
|
-
const text = code.substr(index, num_chars);
|
|
2166
|
-
const lines = text.split(separator);
|
|
2288
|
+
const num_chars = this.selection.chars + ( this.selection.toY - this.selection.fromY ) * separator.length;
|
|
2289
|
+
const text = code.substr( index, num_chars );
|
|
2290
|
+
const lines = text.split( separator );
|
|
2167
2291
|
text_to_copy = lines.join('\n');
|
|
2168
2292
|
}
|
|
2169
2293
|
|
|
@@ -2254,8 +2378,6 @@ class CodeEditor {
|
|
|
2254
2378
|
|
|
2255
2379
|
processLines( mode ) {
|
|
2256
2380
|
|
|
2257
|
-
const start = performance.now();
|
|
2258
|
-
|
|
2259
2381
|
var code_html = "";
|
|
2260
2382
|
|
|
2261
2383
|
// Reset all lines content
|
|
@@ -2804,6 +2926,7 @@ class CodeEditor {
|
|
|
2804
2926
|
return false;
|
|
2805
2927
|
|
|
2806
2928
|
cursor.line++;
|
|
2929
|
+
|
|
2807
2930
|
this.cursorToBottom( cursor, resetLeft );
|
|
2808
2931
|
|
|
2809
2932
|
return true;
|
|
@@ -2876,16 +2999,25 @@ class CodeEditor {
|
|
|
2876
2999
|
delete this._lastSelectionKeyDir;
|
|
2877
3000
|
}
|
|
2878
3001
|
|
|
2879
|
-
selectAll(
|
|
3002
|
+
selectAll() {
|
|
3003
|
+
|
|
3004
|
+
// Use main cursor
|
|
3005
|
+
this._removeSecondaryCursors();
|
|
2880
3006
|
|
|
3007
|
+
var cursor = this._getCurrentCursor();
|
|
2881
3008
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP, cursor );
|
|
3009
|
+
|
|
2882
3010
|
this.startSelection( cursor );
|
|
3011
|
+
|
|
2883
3012
|
const nlines = this.code.lines.length - 1;
|
|
2884
3013
|
this.selection.toX = this.code.lines[ nlines ].length;
|
|
2885
3014
|
this.selection.toY = nlines;
|
|
3015
|
+
|
|
2886
3016
|
this.cursorToPosition( cursor, this.selection.toX );
|
|
2887
3017
|
this.cursorToLine( cursor, this.selection.toY );
|
|
3018
|
+
|
|
2888
3019
|
this.processSelection( null, true );
|
|
3020
|
+
|
|
2889
3021
|
this.hideAutoCompleteBox();
|
|
2890
3022
|
}
|
|
2891
3023
|
|
|
@@ -2988,22 +3120,51 @@ class CodeEditor {
|
|
|
2988
3120
|
|
|
2989
3121
|
saveCursor( cursor, state = {} ) {
|
|
2990
3122
|
|
|
2991
|
-
state.top = cursor._top;
|
|
2992
|
-
state.left = cursor._left;
|
|
2993
|
-
state.line = cursor.line;
|
|
2994
3123
|
state.position = cursor.position;
|
|
2995
|
-
|
|
3124
|
+
state.line = cursor.line;
|
|
2996
3125
|
return state;
|
|
2997
3126
|
}
|
|
2998
3127
|
|
|
3128
|
+
saveCursors() {
|
|
3129
|
+
|
|
3130
|
+
var cursors = [];
|
|
3131
|
+
|
|
3132
|
+
for( let cursor of this.cursors.children )
|
|
3133
|
+
{
|
|
3134
|
+
cursors.push( this.saveCursor( cursor ) );
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
return cursors;
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
relocateCursors() {
|
|
3141
|
+
|
|
3142
|
+
for( let cursor of this.cursors.children )
|
|
3143
|
+
{
|
|
3144
|
+
cursor._left = cursor.position * this.charWidth;
|
|
3145
|
+
cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
|
|
3146
|
+
cursor._top = cursor.line * this.lineHeight;
|
|
3147
|
+
cursor.style.top = "calc(" + cursor._top + "px)";
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
mergeCursors( line ) {
|
|
3152
|
+
|
|
3153
|
+
console.assert( line >= 0 );
|
|
3154
|
+
const cursorsInLine = Array.from( this.cursors.children ).filter( v => v.line == line );
|
|
3155
|
+
|
|
3156
|
+
while( cursorsInLine.length > 1 )
|
|
3157
|
+
cursorsInLine.pop().remove();
|
|
3158
|
+
}
|
|
3159
|
+
|
|
2999
3160
|
restoreCursor( cursor, state ) {
|
|
3000
3161
|
|
|
3001
|
-
cursor.line = state.line ?? 0;
|
|
3002
3162
|
cursor.position = state.position ?? 0;
|
|
3163
|
+
cursor.line = state.line ?? 0;
|
|
3003
3164
|
|
|
3004
|
-
cursor._left =
|
|
3165
|
+
cursor._left = cursor.position * this.charWidth;
|
|
3005
3166
|
cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
|
|
3006
|
-
cursor._top =
|
|
3167
|
+
cursor._top = cursor.line * this.lineHeight;
|
|
3007
3168
|
cursor.style.top = "calc(" + cursor._top + "px)";
|
|
3008
3169
|
}
|
|
3009
3170
|
|
|
@@ -3038,7 +3199,8 @@ class CodeEditor {
|
|
|
3038
3199
|
for( var i = 0; i < n; ++i ) {
|
|
3039
3200
|
this.root.dispatchEvent( new CustomEvent( 'keydown', { 'detail': {
|
|
3040
3201
|
skip_undo: true,
|
|
3041
|
-
key: ' '
|
|
3202
|
+
key: ' ',
|
|
3203
|
+
targetCursor: this._lastProcessedCursorIndex
|
|
3042
3204
|
}}));
|
|
3043
3205
|
}
|
|
3044
3206
|
}
|
|
@@ -3664,7 +3826,13 @@ class CodeEditor {
|
|
|
3664
3826
|
r.style.setProperty( "--code-editor-row-height", row_pixels + "px" );
|
|
3665
3827
|
this.lineHeight = row_pixels;
|
|
3666
3828
|
|
|
3667
|
-
|
|
3829
|
+
// Relocate cursors
|
|
3830
|
+
|
|
3831
|
+
this.relocateCursors();
|
|
3832
|
+
|
|
3833
|
+
// Resize the code area
|
|
3834
|
+
|
|
3835
|
+
this.processLines();
|
|
3668
3836
|
}
|
|
3669
3837
|
|
|
3670
3838
|
_decreaseFontSize() {
|
|
@@ -3684,7 +3852,13 @@ class CodeEditor {
|
|
|
3684
3852
|
r.style.setProperty( "--code-editor-row-height", row_pixels + "px" );
|
|
3685
3853
|
this.lineHeight = row_pixels;
|
|
3686
3854
|
|
|
3687
|
-
|
|
3855
|
+
// Relocate cursors
|
|
3856
|
+
|
|
3857
|
+
this.relocateCursors();
|
|
3858
|
+
|
|
3859
|
+
// Resize the code area
|
|
3860
|
+
|
|
3861
|
+
this.processLines();
|
|
3688
3862
|
}
|
|
3689
3863
|
|
|
3690
3864
|
_clearTmpVariables() {
|
package/build/lexgui.css
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
--global-font: 'Noto Sans', sans-serif; /*'Rubik', sans-serif; */
|
|
6
6
|
--global-selected: #3e57e4;
|
|
7
7
|
--global-selected-light: #7b8ae2;
|
|
8
|
+
--global-selected-dark: #344bccb7;
|
|
8
9
|
--global-color-primary: #232323;
|
|
9
10
|
--global-color-secondary: #343434;
|
|
10
11
|
--global-color-terciary: #444;
|
|
@@ -1786,10 +1787,10 @@ meter::-webkit-meter-even-less-good-value {
|
|
|
1786
1787
|
z-index: 102;
|
|
1787
1788
|
position: absolute;
|
|
1788
1789
|
padding-right: 20px;
|
|
1789
|
-
border-
|
|
1790
|
-
|
|
1791
|
-
box-shadow: 0 0 6px black !important;
|
|
1790
|
+
border-radius: 4px;
|
|
1791
|
+
box-shadow: 0 0 8px rgba(0, 0, 0, 0.63) !important;
|
|
1792
1792
|
background-color: var(--global-blur-background);
|
|
1793
|
+
border: 1px solid #91909036;
|
|
1793
1794
|
}
|
|
1794
1795
|
|
|
1795
1796
|
.lexcontextmenubox:before {
|
|
@@ -1802,13 +1803,14 @@ meter::-webkit-meter-even-less-good-value {
|
|
|
1802
1803
|
-webkit-backdrop-filter: blur(10px);
|
|
1803
1804
|
backdrop-filter: blur(10px);
|
|
1804
1805
|
z-index:-1;
|
|
1806
|
+
border-radius: 4px;
|
|
1805
1807
|
}
|
|
1806
1808
|
|
|
1807
1809
|
.lexcontextmenubox .lexcontextmenuentry {
|
|
1808
1810
|
width: 100%;
|
|
1809
1811
|
color: #c0c4cbe3;
|
|
1810
1812
|
padding: 3px;
|
|
1811
|
-
padding-left:
|
|
1813
|
+
padding-left: 10px;
|
|
1812
1814
|
padding-right: 10px;
|
|
1813
1815
|
padding-bottom: 4px;
|
|
1814
1816
|
cursor: pointer;
|
|
@@ -1816,7 +1818,16 @@ meter::-webkit-meter-even-less-good-value {
|
|
|
1816
1818
|
-moz-user-select: none;
|
|
1817
1819
|
-ms-user-select: none;
|
|
1818
1820
|
user-select: none;
|
|
1819
|
-
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
.lexcontextmenubox .lexcontextmenuentry:first-child {
|
|
1824
|
+
border-top-left-radius: 4px;
|
|
1825
|
+
border-top-right-radius: 4px;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
.lexcontextmenubox .lexcontextmenuentry:last-child {
|
|
1829
|
+
border-bottom-left-radius: 4px;
|
|
1830
|
+
border-bottom-right-radius: 4px;
|
|
1820
1831
|
}
|
|
1821
1832
|
|
|
1822
1833
|
.lexcontextmenubox .lexcontextmenuentry.cmtitle {
|
|
@@ -1848,17 +1859,17 @@ meter::-webkit-meter-even-less-good-value {
|
|
|
1848
1859
|
float: right;
|
|
1849
1860
|
margin-right: 0px;
|
|
1850
1861
|
margin-top: 10px;
|
|
1851
|
-
font-size:
|
|
1862
|
+
font-size: 10px;
|
|
1852
1863
|
}
|
|
1853
1864
|
|
|
1854
1865
|
/* submenu specified arrow */
|
|
1855
1866
|
.lexcontextmenubox .lexcontextmenuentry a.fa-xs {
|
|
1856
1867
|
float: right;
|
|
1857
|
-
margin-top: -
|
|
1868
|
+
margin-top: -8px;
|
|
1858
1869
|
}
|
|
1859
1870
|
|
|
1860
1871
|
.lexcontextmenubox .lexcontextmenuentry:hover:not(.cmtitle) {
|
|
1861
|
-
background-color: var(--global-selected);
|
|
1872
|
+
background-color: var(--global-selected-dark);
|
|
1862
1873
|
color: #f5f5f5;
|
|
1863
1874
|
}
|
|
1864
1875
|
|
|
@@ -2950,7 +2961,8 @@ pre .line-gutter {
|
|
|
2950
2961
|
height: var(--code-editor-row-height);
|
|
2951
2962
|
position: absolute;
|
|
2952
2963
|
border-left: 3px solid #fff !important;
|
|
2953
|
-
z-index:
|
|
2964
|
+
z-index: 1 !important;
|
|
2965
|
+
opacity: 0.6;
|
|
2954
2966
|
left: 0px;
|
|
2955
2967
|
top: 0px;
|
|
2956
2968
|
}
|
package/build/lexgui.js
CHANGED
|
@@ -12,7 +12,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
var LX = global.LX = {
|
|
15
|
-
version: "0.1.
|
|
15
|
+
version: "0.1.24",
|
|
16
16
|
ready: false,
|
|
17
17
|
components: [], // specific pre-build components
|
|
18
18
|
signals: {} // events and triggers
|
package/build/lexgui.module.js
CHANGED
package/changelog.md
CHANGED
|
@@ -1,38 +1,55 @@
|
|
|
1
1
|
# lexgui.js changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.24
|
|
4
|
+
|
|
5
|
+
Code Editor:
|
|
6
|
+
- Improved single and multiple cursor usability.
|
|
7
|
+
- Cursor can be added or removed using "Alt+LeftClick".
|
|
8
|
+
- Fixed clicks outside the code area.
|
|
9
|
+
- Minor bug fixes.
|
|
10
|
+
|
|
3
11
|
## 0.1.23
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
Code Editor:
|
|
14
|
+
- Begin integration of multiple cursors ("Ctrl+ArrowDown").
|
|
15
|
+
- Code tabs have new VS Code-alike style.
|
|
16
|
+
- Improved CSS highlighting.
|
|
17
|
+
- Add Undo Steps to some actions that were missing.
|
|
18
|
+
- When using Ctrl+G, the selected line is now highlighted.
|
|
19
|
+
|
|
20
|
+
Minor fixes.
|
|
11
21
|
|
|
12
22
|
## 0.1.22
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
Added
|
|
16
|
-
Added "Ctrl+
|
|
17
|
-
|
|
24
|
+
Code Editor:
|
|
25
|
+
- Added REDO using "Ctrl+Y".
|
|
26
|
+
- Added FontSize customization. "Ctrl+PLUS", "Ctrl+MINUS" or "Ctrl+Wheel".
|
|
27
|
+
- Added "Ctrl+G" to scroll to specific line.
|
|
28
|
+
|
|
29
|
+
Minor fixes.
|
|
18
30
|
|
|
19
31
|
## 0.1.21
|
|
20
32
|
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
Code Editor:
|
|
34
|
+
- Added "Ctrl+F" to find text in code tabs.
|
|
35
|
+
- "Shift+Backspace" deletes word at current position.
|
|
36
|
+
- Added "Markdown" syntax highlighting.
|
|
37
|
+
- Improved hightlighting of tag languages (HTML, Markdown, XML).
|
|
25
38
|
|
|
26
39
|
## 0.1.20
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
Code Editor:
|
|
42
|
+
- Active line is now hightlighted.
|
|
43
|
+
- Using CommitMono font (https://commitmono.com/) as the font for the Code Editor.
|
|
44
|
+
- Added "Rust" syntax highlighting.
|
|
45
|
+
- Improved all code selections (and some bugs fixed).
|
|
46
|
+
- Block comments are now working again (with bugs probably).
|
|
47
|
+
|
|
33
48
|
Minor fixes.
|
|
34
49
|
|
|
35
50
|
## 0.1.19
|
|
36
51
|
|
|
37
|
-
|
|
52
|
+
Code Editor:
|
|
53
|
+
- Add file explorer to Code Editor component.
|
|
54
|
+
|
|
38
55
|
Minor fixes.
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
file_explorer: file_explorer
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
editor.loadFile( "../
|
|
58
|
+
editor.loadFile( "../data/js_sample.js" );
|
|
59
59
|
// editor.loadFile( "../data/json_sample.json" );
|
|
60
60
|
// editor.loadFile( "../data/css_sample.css" );
|
|
61
61
|
// editor.loadFile( "../data/cpp_sample.cpp" );
|