lexgui 0.7.0 → 0.7.2
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 +483 -330
- package/build/extensions/videoeditor.js +4 -1
- package/build/lexgui.css +32 -23
- package/build/lexgui.js +172 -48
- package/build/lexgui.min.css +7 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +172 -48
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +26 -1
- package/demo.js +2 -1
- package/examples/all-components.html +1 -0
- package/examples/code-editor.html +7 -2
- package/package.json +1 -1
|
@@ -6,26 +6,13 @@ if(!LX) {
|
|
|
6
6
|
|
|
7
7
|
LX.extensions.push( 'CodeEditor' );
|
|
8
8
|
|
|
9
|
-
function swapElements( obj, a, b ) {
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
function sliceChars( str, idx, n = 1 ) {
|
|
18
|
-
return str.substr(0, idx) + str.substr(idx + n);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function firstNonspaceIndex( str ) {
|
|
22
|
-
const index = str.search(/\S|$/)
|
|
23
|
-
return index < str.length ? index : -1;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function strReverse( str ) {
|
|
27
|
-
return str.split( "" ).reverse().join( "" );
|
|
28
|
-
}
|
|
9
|
+
function swapElements( obj, a, b ) { [obj[a], obj[b]] = [obj[b], obj[a]]; }
|
|
10
|
+
function swapArrayElements( array, id0, id1 ) { [array[id0], array[id1]] = [array[id1], array[id0]]; };
|
|
11
|
+
function sliceChars( str, idx, n = 1 ) { return str.substr(0, idx) + str.substr(idx + n); }
|
|
12
|
+
function firstNonspaceIndex( str ) { const index = str.search(/\S|$/); return index < str.length ? index : -1; }
|
|
13
|
+
function strReverse( str ) { return str.split( "" ).reverse().join( "" ); }
|
|
14
|
+
function isLetter( c ){ return /[a-zA-Z]/.test( c ); };
|
|
15
|
+
function isSymbol( c ){ return /[^\w\s]/.test( c ); };
|
|
29
16
|
|
|
30
17
|
function indexOfFrom( str, reg, from, reverse ) {
|
|
31
18
|
|
|
@@ -46,15 +33,6 @@ function indexOfFrom( str, reg, from, reverse ) {
|
|
|
46
33
|
}
|
|
47
34
|
}
|
|
48
35
|
|
|
49
|
-
let ASYNC_ENABLED = true;
|
|
50
|
-
|
|
51
|
-
function doAsync( fn, ms ) {
|
|
52
|
-
if( ASYNC_ENABLED )
|
|
53
|
-
setTimeout( fn, ms ?? 0 );
|
|
54
|
-
else
|
|
55
|
-
fn();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
36
|
class CodeSelection {
|
|
59
37
|
|
|
60
38
|
constructor( editor, cursor, className = "lexcodeselection" ) {
|
|
@@ -104,7 +82,6 @@ class CodeSelection {
|
|
|
104
82
|
|
|
105
83
|
var domEl = document.createElement( 'div' );
|
|
106
84
|
domEl.className = this.className;
|
|
107
|
-
|
|
108
85
|
domEl._top = y * this.editor.lineHeight;
|
|
109
86
|
domEl.style.top = domEl._top + "px";
|
|
110
87
|
domEl._left = x * this.editor.charWidth;
|
|
@@ -213,6 +190,76 @@ class ScrollBar {
|
|
|
213
190
|
|
|
214
191
|
}
|
|
215
192
|
|
|
193
|
+
/* Highlight rules
|
|
194
|
+
- test: function that receives a context object and returns true or false
|
|
195
|
+
- className: class to apply if test is true
|
|
196
|
+
- action: optional function to execute if test is true, receives context and editor as parameter
|
|
197
|
+
- discard: optional boolean, if true the token is discarded, action value is returned
|
|
198
|
+
to "ctx.discardToken" and no class is applied
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
const HighlightRules = {
|
|
202
|
+
|
|
203
|
+
common: [
|
|
204
|
+
{ test: ctx => ctx.inBlockComment, className: "cm-com" },
|
|
205
|
+
{ test: ctx => ctx.inString, action: (ctx, editor) => editor._appendStringToken( ctx.token ), discard: true },
|
|
206
|
+
{ test: ctx => ctx.token.substr( 0, ctx.singleLineCommentToken.length ) == ctx.singleLineCommentToken, className: "cm-com" },
|
|
207
|
+
{ test: (ctx, editor) => editor._isKeyword( ctx ), className: "cm-kwd" },
|
|
208
|
+
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.builtIn, ctx.lang ) && ( ctx.lang.tags ?? false ? ( editor._enclosedByTokens( ctx.token, ctx.tokenIndex, '<', '>' ) ) : true ), className: "cm-bln" },
|
|
209
|
+
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.statementsAndDeclarations, ctx.lang ), className: "cm-std" },
|
|
210
|
+
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.symbols, ctx.lang ), className: "cm-sym" },
|
|
211
|
+
{ test: (ctx, editor) => editor._mustHightlightWord( ctx.token, CodeEditor.types, ctx.lang ), className: "cm-typ" },
|
|
212
|
+
{ test: (ctx, editor) => editor._isNumber( ctx.token ) || editor._isNumber( ctx.token.replace(/[px]|[em]|%/g,'') ), className: "cm-dec" },
|
|
213
|
+
{ test: ctx => ctx.lang.usePreprocessor && ctx.token.includes( '#' ), className: "cm-ppc" },
|
|
214
|
+
],
|
|
215
|
+
|
|
216
|
+
javascript: [
|
|
217
|
+
{ test: ctx => (ctx.prev === 'class' && ctx.next === '{') || (ctx.prev === 'new' && ctx.next === '('), className: "cm-typ" }
|
|
218
|
+
],
|
|
219
|
+
|
|
220
|
+
typescript: [
|
|
221
|
+
{ test: ctx => ctx.scope && (ctx.token !== ',' && ctx.scope == "enum"), className: "cm-enu" },
|
|
222
|
+
{ test: ctx => (ctx.prev === ':' && ctx.next !== undefined && isLetter(ctx.token) ) || (ctx.prev === 'interface' && ctx.next === '{') || (ctx.prev === 'enum' && ctx.next === '{'), className: "cm-typ" },
|
|
223
|
+
{ test: ctx => (ctx.prev === 'class' && ctx.next === '{') || (ctx.prev === 'class' && ctx.next === '<') || (ctx.prev === 'new' && ctx.next === '(') || (ctx.prev === 'new' && ctx.next === '<'), className: "cm-typ" },
|
|
224
|
+
{ test: (ctx, editor) => ctx.token !== ',' && editor._enclosedByTokens( ctx.token, ctx.tokenIndex, '<', '>' ), className: "cm-typ" },
|
|
225
|
+
],
|
|
226
|
+
|
|
227
|
+
cpp: [
|
|
228
|
+
{ test: ctx => ctx.scope && (ctx.token !== ',' && ctx.scope == "enum"), className: "cm-enu" },
|
|
229
|
+
{ test: ctx => (ctx.prev === 'class' && ctx.next === '{') || (ctx.prev === 'struct' && ctx.next === '{'), className: "cm-typ" },
|
|
230
|
+
{ test: ctx => ctx.prev === "<" && (ctx.next === ">" || ctx.next === "*"), className: "cm-typ" }, // Defining template type in C++
|
|
231
|
+
{ test: ctx => ctx.next === "::" || (ctx.prev === "::" && ctx.next !== "("), className: "cm-typ" } // C++ Class
|
|
232
|
+
],
|
|
233
|
+
|
|
234
|
+
wgsl: [
|
|
235
|
+
{ test: ctx => ctx.prev === '>' && (!ctx.next || ctx.next === '{'), className: "cm-typ" }, // Function return type
|
|
236
|
+
{ test: ctx => (ctx.prev === ':' && ctx.next !== undefined) || (ctx.prev === 'struct' && ctx.next === '{'), className: "cm-typ" },
|
|
237
|
+
{ test: (ctx, editor) => ctx.token !== ',' && editor._enclosedByTokens( ctx.token, ctx.tokenIndex, '<', '>' ), className: "cm-typ" },
|
|
238
|
+
],
|
|
239
|
+
|
|
240
|
+
css: [
|
|
241
|
+
{ test: ctx => ( ctx.prev == '.' || ctx.prev == '::' || ( ctx.prev == ':' && ctx.next == '{' ) || ( ctx.token[ 0 ] == '#' && ctx.prev != ':' ) ), className: "cm-kwd" },
|
|
242
|
+
{ test: ctx => ctx.prev === ':' && (ctx.next === ';' || ctx.next === '!important'), className: "cm-str" }, // CSS value
|
|
243
|
+
{ test: ctx => ctx.prev === undefined && ctx.next === ":", className: "cm-typ" }, // CSS attribute
|
|
244
|
+
],
|
|
245
|
+
|
|
246
|
+
batch: [
|
|
247
|
+
{ test: ctx => ctx.token === '@' || ctx.prev === ':' || ctx.prev === '@', className: "cm-kwd" }
|
|
248
|
+
],
|
|
249
|
+
|
|
250
|
+
markdown: [
|
|
251
|
+
{ test: ctx => ctx.isFirstToken && ctx.token.replaceAll('#', '').length != ctx.token.length, action: (ctx, editor) => editor._markdownHeader = true, className: "cm-kwd" }
|
|
252
|
+
],
|
|
253
|
+
|
|
254
|
+
php: [
|
|
255
|
+
{ test: ctx => (ctx.prev === 'class' && ctx.next === '{') || (ctx.prev === 'class' && ctx.next === 'implements'), className: "cm-typ" },
|
|
256
|
+
],
|
|
257
|
+
|
|
258
|
+
post_common: [
|
|
259
|
+
{ test: ctx => isLetter(ctx.token) && (ctx.token[ 0 ] != '@') && (ctx.token[ 0 ] != ',') && (ctx.next === '('), className: "cm-mtd" }
|
|
260
|
+
],
|
|
261
|
+
};
|
|
262
|
+
|
|
216
263
|
/**
|
|
217
264
|
* @class CodeEditor
|
|
218
265
|
*/
|
|
@@ -451,9 +498,13 @@ class CodeEditor {
|
|
|
451
498
|
if( e.ctrlKey )
|
|
452
499
|
{
|
|
453
500
|
e.preventDefault();
|
|
501
|
+
e.stopPropagation();
|
|
454
502
|
( e.deltaY > 0.0 ? this._decreaseFontSize() : this._increaseFontSize() );
|
|
455
503
|
}
|
|
456
|
-
|
|
504
|
+
} );
|
|
505
|
+
|
|
506
|
+
this.codeScroller.addEventListener( 'wheel', e => {
|
|
507
|
+
if( !e.ctrlKey )
|
|
457
508
|
{
|
|
458
509
|
const dX = ( e.deltaY > 0.0 ? 10.0 : -10.0 ) * ( e.shiftKey ? 1.0 : 0.0 );
|
|
459
510
|
if( dX != 0.0 ) this.setScrollBarValue( 'horizontal', dX );
|
|
@@ -495,17 +546,17 @@ class CodeEditor {
|
|
|
495
546
|
|
|
496
547
|
// Add search box
|
|
497
548
|
{
|
|
498
|
-
|
|
549
|
+
const box = document.createElement( 'div' );
|
|
499
550
|
box.className = "searchbox";
|
|
500
551
|
|
|
501
|
-
|
|
552
|
+
const searchPanel = new LX.Panel();
|
|
502
553
|
box.appendChild( searchPanel.root );
|
|
503
554
|
|
|
504
555
|
searchPanel.sameLine( 4 );
|
|
505
556
|
searchPanel.addText( null, "", null, { placeholder: "Find" } );
|
|
506
|
-
searchPanel.addButton( null, "up", () => this.search( null, true ), { icon: "ArrowUp" } );
|
|
507
|
-
searchPanel.addButton( null, "down", () => this.search(), { icon: "ArrowDown" } );
|
|
508
|
-
searchPanel.addButton( null, "x", this.hideSearchBox.bind( this ), { icon: "X" } );
|
|
557
|
+
searchPanel.addButton( null, "up", () => this.search( null, true ), { icon: "ArrowUp", title: "Previous Match", tooltip: true } );
|
|
558
|
+
searchPanel.addButton( null, "down", () => this.search(), { icon: "ArrowDown", title: "Next Match", tooltip: true } );
|
|
559
|
+
searchPanel.addButton( null, "x", this.hideSearchBox.bind( this ), { icon: "X", title: "Close", tooltip: true } );
|
|
509
560
|
|
|
510
561
|
box.querySelector( 'input' ).addEventListener( 'keyup', e => {
|
|
511
562
|
if( e.key == 'Escape' ) this.hideSearchBox();
|
|
@@ -574,6 +625,7 @@ class CodeEditor {
|
|
|
574
625
|
this.charWidth = 7; // To update later depending on size..
|
|
575
626
|
this.defaultSingleLineCommentToken = '//';
|
|
576
627
|
this.defaultBlockCommentTokens = [ '/*', '*/' ];
|
|
628
|
+
this.lineScopes = [];
|
|
577
629
|
this._lastTime = null;
|
|
578
630
|
|
|
579
631
|
this.pairKeys = {
|
|
@@ -592,24 +644,6 @@ class CodeEditor {
|
|
|
592
644
|
// Scan tokens..
|
|
593
645
|
// setInterval( this.scanWordSuggestions.bind( this ), 2000 );
|
|
594
646
|
|
|
595
|
-
this.languages = {
|
|
596
|
-
'Plain Text': { ext: 'txt', blockComments: false, singleLineComments: false },
|
|
597
|
-
'JavaScript': { ext: 'js' },
|
|
598
|
-
'C': { ext: [ 'c', 'h' ] },
|
|
599
|
-
'C++': { ext: [ 'cpp', 'hpp' ] },
|
|
600
|
-
'CSS': { ext: 'css' },
|
|
601
|
-
'CMake': { ext: 'cmake', singleLineCommentToken: '#', blockComments: false, ignoreCase: true },
|
|
602
|
-
'GLSL': { ext: 'glsl' },
|
|
603
|
-
'WGSL': { ext: 'wgsl' },
|
|
604
|
-
'JSON': { ext: 'json', blockComments: false, singleLineComments: false },
|
|
605
|
-
'XML': { ext: 'xml', tags: true },
|
|
606
|
-
'Rust': { ext: 'rs' },
|
|
607
|
-
'Python': { ext: 'py', singleLineCommentToken: '#' },
|
|
608
|
-
'HTML': { ext: 'html', tags: true, singleLineComments: false, blockCommentsTokens: [ '<!--', '-->' ] },
|
|
609
|
-
'Batch': { ext: 'bat', blockComments: false, singleLineCommentToken: '::' },
|
|
610
|
-
'Markdown': { ext: 'md', blockComments: false, singleLineCommentToken: '::', tags: true }
|
|
611
|
-
};
|
|
612
|
-
|
|
613
647
|
this.specialKeys = [
|
|
614
648
|
'Backspace', 'Enter', 'ArrowUp', 'ArrowDown',
|
|
615
649
|
'ArrowRight', 'ArrowLeft', 'Delete', 'Home',
|
|
@@ -1101,15 +1135,29 @@ class CodeEditor {
|
|
|
1101
1135
|
const onLoadAll = () => {
|
|
1102
1136
|
// Create inspector panel when the initial state is complete
|
|
1103
1137
|
// and we have at least 1 tab opened
|
|
1104
|
-
this.
|
|
1105
|
-
if( this.
|
|
1138
|
+
this.statusPanel = this._createStatusPanel();
|
|
1139
|
+
if( this.statusPanel )
|
|
1106
1140
|
{
|
|
1107
|
-
area.attach( this.
|
|
1141
|
+
area.attach( this.statusPanel );
|
|
1108
1142
|
}
|
|
1109
1143
|
|
|
1110
1144
|
// Wait until the fonts are all loaded
|
|
1111
1145
|
document.fonts.ready.then(() => {
|
|
1112
|
-
|
|
1146
|
+
// Load any font size from local storage
|
|
1147
|
+
const savedFontSize = window.localStorage.getItem( "lexcodeeditor-font-size" );
|
|
1148
|
+
if( savedFontSize )
|
|
1149
|
+
{
|
|
1150
|
+
this._setFontSize( parseInt( savedFontSize ) );
|
|
1151
|
+
}
|
|
1152
|
+
else // Use default size
|
|
1153
|
+
{
|
|
1154
|
+
const r = document.querySelector( ':root' );
|
|
1155
|
+
const s = getComputedStyle( r );
|
|
1156
|
+
this.fontSize = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
|
|
1157
|
+
this.charWidth = this._measureChar( "a", true );
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
LX.emit( "@font-size", this.fontSize );
|
|
1113
1161
|
});
|
|
1114
1162
|
|
|
1115
1163
|
window.editor = this;
|
|
@@ -1284,7 +1332,7 @@ class CodeEditor {
|
|
|
1284
1332
|
options: options
|
|
1285
1333
|
};
|
|
1286
1334
|
|
|
1287
|
-
const ext =
|
|
1335
|
+
const ext = CodeEditor.languages[ options.language ] ?. ext;
|
|
1288
1336
|
this.addExplorerItem( { id: name, skipVisibility: true, icon: this._getFileIcon( name, ext ) } );
|
|
1289
1337
|
this.explorer.innerTree.frefresh( name );
|
|
1290
1338
|
}
|
|
@@ -1363,7 +1411,11 @@ class CodeEditor {
|
|
|
1363
1411
|
get: (v) => { return cursor._position },
|
|
1364
1412
|
set: (v) => {
|
|
1365
1413
|
cursor._position = v;
|
|
1366
|
-
if( cursor.isMain )
|
|
1414
|
+
if( cursor.isMain )
|
|
1415
|
+
{
|
|
1416
|
+
const activeLine = this.state.activeLine;
|
|
1417
|
+
this._updateDataInfoPanel( "@cursor-data", `Ln ${ activeLine + 1 }, Col ${ v + 1 }` );
|
|
1418
|
+
}
|
|
1367
1419
|
}
|
|
1368
1420
|
} );
|
|
1369
1421
|
|
|
@@ -1515,7 +1567,7 @@ class CodeEditor {
|
|
|
1515
1567
|
this._updateDataInfoPanel( "@highlight", lang );
|
|
1516
1568
|
this.processLines();
|
|
1517
1569
|
|
|
1518
|
-
const ext = langExtension ??
|
|
1570
|
+
const ext = langExtension ?? CodeEditor.languages[ lang ].ext;
|
|
1519
1571
|
const icon = this._getFileIcon( null, ext );
|
|
1520
1572
|
|
|
1521
1573
|
// Update tab icon
|
|
@@ -1553,9 +1605,9 @@ class CodeEditor {
|
|
|
1553
1605
|
return this._changeLanguage( this.code.language );
|
|
1554
1606
|
}
|
|
1555
1607
|
|
|
1556
|
-
for( let l in
|
|
1608
|
+
for( let l in CodeEditor.languages )
|
|
1557
1609
|
{
|
|
1558
|
-
const langExtension =
|
|
1610
|
+
const langExtension = CodeEditor.languages[ l ].ext;
|
|
1559
1611
|
|
|
1560
1612
|
if( langExtension.constructor == Array )
|
|
1561
1613
|
{
|
|
@@ -1577,17 +1629,25 @@ class CodeEditor {
|
|
|
1577
1629
|
this._changeLanguage( 'Plain Text' );
|
|
1578
1630
|
}
|
|
1579
1631
|
|
|
1580
|
-
|
|
1632
|
+
_createStatusPanel() {
|
|
1581
1633
|
|
|
1582
1634
|
if( !this.skipInfo )
|
|
1583
1635
|
{
|
|
1584
|
-
let panel = new LX.Panel({ className: "lexcodetabinfo
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1636
|
+
let panel = new LX.Panel({ className: "lexcodetabinfo flex flex-row", height: "auto" });
|
|
1637
|
+
|
|
1638
|
+
let leftStatusPanel = new LX.Panel( { id: "FontSizeZoomStatusComponent", height: "auto" } );
|
|
1639
|
+
leftStatusPanel.sameLine();
|
|
1640
|
+
leftStatusPanel.addButton( null, "ZoomOutButton", this._decreaseFontSize.bind( this ), { icon: "ZoomOut", width: "32px", title: "Zoom Out", tooltip: true } );
|
|
1641
|
+
leftStatusPanel.addLabel( this.fontSize ?? 14, { fit: true, signal: "@font-size" });
|
|
1642
|
+
leftStatusPanel.addButton( null, "ZoomInButton", this._increaseFontSize.bind( this ), { icon: "ZoomIn", width: "32px", title: "Zoom In", tooltip: true } );
|
|
1643
|
+
leftStatusPanel.endLine( "justify-start" );
|
|
1644
|
+
panel.attach( leftStatusPanel.root );
|
|
1645
|
+
|
|
1646
|
+
let rightStatusPanel = new LX.Panel( { height: "auto" } );
|
|
1647
|
+
rightStatusPanel.sameLine();
|
|
1648
|
+
rightStatusPanel.addLabel( this.code.title, { id: "EditorFilenameStatusComponent", fit: true, signal: "@tab-name" });
|
|
1649
|
+
rightStatusPanel.addButton( null, "Ln 1, Col 1", this.showSearchLineBox.bind( this ), { id: "EditorSelectionStatusComponent", fit: true, signal: "@cursor-data" });
|
|
1650
|
+
rightStatusPanel.addButton( null, "Spaces: " + this.tabSpaces, ( value, event ) => {
|
|
1591
1651
|
LX.addContextMenu( "Spaces", event, m => {
|
|
1592
1652
|
const options = [ 2, 4, 8 ];
|
|
1593
1653
|
for( const n of options )
|
|
@@ -1597,24 +1657,57 @@ class CodeEditor {
|
|
|
1597
1657
|
this._updateDataInfoPanel( "@tab-spaces", "Spaces: " + this.tabSpaces );
|
|
1598
1658
|
} );
|
|
1599
1659
|
});
|
|
1600
|
-
}, { nameWidth: "15%", signal: "@tab-spaces" });
|
|
1601
|
-
|
|
1660
|
+
}, { id: "EditorIndentationStatusComponent", nameWidth: "15%", signal: "@tab-spaces" });
|
|
1661
|
+
rightStatusPanel.addButton( "<b>{ }</b>", this.highlight, ( value, event ) => {
|
|
1602
1662
|
LX.addContextMenu( "Language", event, m => {
|
|
1603
|
-
for( const lang of Object.keys(
|
|
1663
|
+
for( const lang of Object.keys( CodeEditor.languages ) )
|
|
1604
1664
|
{
|
|
1605
1665
|
m.add( lang, v => {
|
|
1606
1666
|
this._changeLanguage( v, null, true )
|
|
1607
1667
|
} );
|
|
1608
1668
|
}
|
|
1609
1669
|
});
|
|
1610
|
-
}, { nameWidth: "15%", signal: "@highlight" });
|
|
1611
|
-
|
|
1670
|
+
}, { id: "EditorLanguageStatusComponent", nameWidth: "15%", signal: "@highlight" });
|
|
1671
|
+
rightStatusPanel.endLine( "justify-end" );
|
|
1672
|
+
panel.attach( rightStatusPanel.root );
|
|
1673
|
+
|
|
1674
|
+
const itemVisibilityMap = {
|
|
1675
|
+
"Font Size Zoom": true,
|
|
1676
|
+
"Editor Filename": true,
|
|
1677
|
+
"Editor Selection": true,
|
|
1678
|
+
"Editor Indentation": true,
|
|
1679
|
+
"Editor Language": true,
|
|
1680
|
+
};
|
|
1681
|
+
|
|
1682
|
+
panel.root.addEventListener( "contextmenu", (e) => {
|
|
1683
|
+
|
|
1684
|
+
if( e.target && ( e.target.classList.contains( "lexpanel" ) || e.target.classList.contains( "lexinlinecomponents" ) ) )
|
|
1685
|
+
{
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
const menuOptions = Object.keys( itemVisibilityMap ).map( ( itemName, idx ) => {
|
|
1690
|
+
const item = {
|
|
1691
|
+
name: itemName,
|
|
1692
|
+
icon: "Check",
|
|
1693
|
+
callback: () => {
|
|
1694
|
+
itemVisibilityMap[ itemName ] = !itemVisibilityMap[ itemName ];
|
|
1695
|
+
const b = panel.root.querySelector( `#${ itemName.replaceAll( " ", "" ) }StatusComponent` );
|
|
1696
|
+
console.assert( b, `${ itemName } has no status button!` );
|
|
1697
|
+
b.classList.toggle( "hidden", !itemVisibilityMap[ itemName ] );
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
if( !itemVisibilityMap[ itemName ] ) delete item.icon;
|
|
1701
|
+
return item;
|
|
1702
|
+
} );
|
|
1703
|
+
new LX.DropdownMenu( e.target, menuOptions, { side: "top", align: "start" });
|
|
1704
|
+
} );
|
|
1612
1705
|
|
|
1613
1706
|
return panel;
|
|
1614
1707
|
}
|
|
1615
1708
|
else
|
|
1616
1709
|
{
|
|
1617
|
-
doAsync( () => {
|
|
1710
|
+
LX.doAsync( () => {
|
|
1618
1711
|
|
|
1619
1712
|
// Change css a little bit...
|
|
1620
1713
|
this.gutter.style.height = "calc(100% - 28px)";
|
|
@@ -1623,7 +1716,7 @@ class CodeEditor {
|
|
|
1623
1716
|
this.base_area.root.querySelector( '.lexcodescrollbar.vertical' ).style.height = "calc(100% - 27px)";
|
|
1624
1717
|
this.tabs.area.root.classList.add( 'no-code-info' );
|
|
1625
1718
|
|
|
1626
|
-
}, 100);
|
|
1719
|
+
}, 100 );
|
|
1627
1720
|
}
|
|
1628
1721
|
}
|
|
1629
1722
|
|
|
@@ -1661,6 +1754,7 @@ class CodeEditor {
|
|
|
1661
1754
|
extension == "bat" ? "Windows lightblue" :
|
|
1662
1755
|
extension == "json" ? "Braces fg-primary" :
|
|
1663
1756
|
extension == "js" ? "Js goldenrod" :
|
|
1757
|
+
extension == "ts" ? "Ts pipelineblue" :
|
|
1664
1758
|
extension == "py" ? "Python munsellblue" :
|
|
1665
1759
|
extension == "rs" ? "Rust fg-primary" :
|
|
1666
1760
|
extension == "md" ? "Markdown fg-primary" :
|
|
@@ -1668,6 +1762,7 @@ class CodeEditor {
|
|
|
1668
1762
|
extension == "hpp" ? "CPlusPlus heliotrope" :
|
|
1669
1763
|
extension == "c" ? "C pictonblue" :
|
|
1670
1764
|
extension == "h" ? "C heliotrope" :
|
|
1765
|
+
extension == "php" ? "Php blueviolet" :
|
|
1671
1766
|
!isNewTabButton ? "AlignLeft gray" : undefined;
|
|
1672
1767
|
}
|
|
1673
1768
|
|
|
@@ -2709,7 +2804,7 @@ class CodeEditor {
|
|
|
2709
2804
|
|
|
2710
2805
|
_commentLine( cursor, line, minNonspaceIdx ) {
|
|
2711
2806
|
|
|
2712
|
-
const lang =
|
|
2807
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
2713
2808
|
|
|
2714
2809
|
if( !( lang.singleLineComments ?? true ))
|
|
2715
2810
|
return;
|
|
@@ -2762,7 +2857,7 @@ class CodeEditor {
|
|
|
2762
2857
|
|
|
2763
2858
|
_uncommentLine( cursor, line ) {
|
|
2764
2859
|
|
|
2765
|
-
const lang =
|
|
2860
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
2766
2861
|
|
|
2767
2862
|
if( !( lang.singleLineComments ?? true ))
|
|
2768
2863
|
return;
|
|
@@ -2804,8 +2899,8 @@ class CodeEditor {
|
|
|
2804
2899
|
|
|
2805
2900
|
for( let i = 0; i < this.code.lines.length; ++i )
|
|
2806
2901
|
{
|
|
2807
|
-
const
|
|
2808
|
-
const tokens = this._getTokensFromLine(
|
|
2902
|
+
const lineString = this.code.lines[ i ];
|
|
2903
|
+
const tokens = this._getTokensFromLine( lineString, true );
|
|
2809
2904
|
tokens.forEach( t => this.code.tokens[ t ] = 1 );
|
|
2810
2905
|
}
|
|
2811
2906
|
}
|
|
@@ -2823,7 +2918,7 @@ class CodeEditor {
|
|
|
2823
2918
|
|
|
2824
2919
|
processLines( mode ) {
|
|
2825
2920
|
|
|
2826
|
-
var
|
|
2921
|
+
var htmlCode = "";
|
|
2827
2922
|
this._blockCommentCache.length = 0;
|
|
2828
2923
|
|
|
2829
2924
|
// Reset all lines content
|
|
@@ -2843,16 +2938,20 @@ class CodeEditor {
|
|
|
2843
2938
|
{
|
|
2844
2939
|
const diff = Math.max( this.code.lines.length - this.visibleLinesViewport.y, 0 );
|
|
2845
2940
|
if( diff <= this.lineScrollMargin.y )
|
|
2941
|
+
{
|
|
2846
2942
|
this.visibleLinesViewport.y += diff;
|
|
2943
|
+
}
|
|
2847
2944
|
}
|
|
2848
2945
|
|
|
2946
|
+
this._scopeStack = [];
|
|
2947
|
+
|
|
2849
2948
|
// Process visible lines
|
|
2850
2949
|
for( let i = this.visibleLinesViewport.x; i < this.visibleLinesViewport.y; ++i )
|
|
2851
2950
|
{
|
|
2852
|
-
|
|
2951
|
+
htmlCode += this.processLine( i, true );
|
|
2853
2952
|
}
|
|
2854
2953
|
|
|
2855
|
-
this.code.innerHTML =
|
|
2954
|
+
this.code.innerHTML = htmlCode;
|
|
2856
2955
|
|
|
2857
2956
|
// Update scroll data
|
|
2858
2957
|
this.codeScroller.scrollTop = lastScrollTop;
|
|
@@ -2866,28 +2965,53 @@ class CodeEditor {
|
|
|
2866
2965
|
this.resize();
|
|
2867
2966
|
}
|
|
2868
2967
|
|
|
2869
|
-
processLine(
|
|
2968
|
+
processLine( lineNumber, force ) {
|
|
2870
2969
|
|
|
2871
2970
|
// Check if we are in block comment sections..
|
|
2872
|
-
if( !force && this._inBlockCommentSection(
|
|
2971
|
+
if( !force && this._inBlockCommentSection( lineNumber ) )
|
|
2873
2972
|
{
|
|
2874
2973
|
this.processLines();
|
|
2875
2974
|
return;
|
|
2876
2975
|
}
|
|
2877
2976
|
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2977
|
+
if( this._scopeStack )
|
|
2978
|
+
{
|
|
2979
|
+
this.lineScopes[ lineNumber ] = [ ...this._scopeStack ];
|
|
2980
|
+
}
|
|
2981
|
+
else
|
|
2982
|
+
{
|
|
2983
|
+
this._scopeStack = [ ...this.lineScopes[ lineNumber ] ];
|
|
2984
|
+
}
|
|
2881
2985
|
|
|
2882
|
-
const
|
|
2986
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
2987
|
+
const localLineNum = this.toLocalLine( lineNumber );
|
|
2988
|
+
const gutterLineHtml = `<span class='line-gutter'>${ lineNumber + 1 }</span>`;
|
|
2989
|
+
|
|
2990
|
+
const _updateLine = ( html ) => {
|
|
2883
2991
|
if( !force ) // Single line update
|
|
2884
2992
|
{
|
|
2993
|
+
_processNextLineIfNecessary();
|
|
2885
2994
|
this.code.childNodes[ localLineNum ].innerHTML = gutterLineHtml + html;
|
|
2886
|
-
this._setActiveLine(
|
|
2995
|
+
this._setActiveLine( lineNumber );
|
|
2887
2996
|
this._clearTmpVariables();
|
|
2888
2997
|
}
|
|
2889
2998
|
else // Update all lines at once
|
|
2890
|
-
|
|
2999
|
+
{
|
|
3000
|
+
return `<pre>${ gutterLineHtml + html }</pre>`;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
const _processNextLineIfNecessary = () => {
|
|
3005
|
+
// Only update scope stack if something changed when editing a single line
|
|
3006
|
+
if( this._scopeStack.toString() == this.lineScopes[ lineNumber + 1 ]?.toString() )
|
|
3007
|
+
return;
|
|
3008
|
+
|
|
3009
|
+
this.lineScopes[ lineNumber + 1 ] = [ ...this._scopeStack ];
|
|
3010
|
+
|
|
3011
|
+
if( this.code.lines.length > lineNumber + 1 )
|
|
3012
|
+
{
|
|
3013
|
+
this.processLine( lineNumber + 1 );
|
|
3014
|
+
}
|
|
2891
3015
|
}
|
|
2892
3016
|
|
|
2893
3017
|
// multi-line strings not supported by now
|
|
@@ -2895,7 +3019,7 @@ class CodeEditor {
|
|
|
2895
3019
|
delete this._pendingString;
|
|
2896
3020
|
delete this._markdownHeader;
|
|
2897
3021
|
|
|
2898
|
-
let
|
|
3022
|
+
let lineString = this.code.lines[ lineNumber ];
|
|
2899
3023
|
|
|
2900
3024
|
// Single line
|
|
2901
3025
|
if( !force )
|
|
@@ -2907,35 +3031,36 @@ class CodeEditor {
|
|
|
2907
3031
|
// Early out check for no highlighting languages
|
|
2908
3032
|
if( this.highlight == 'Plain Text' )
|
|
2909
3033
|
{
|
|
2910
|
-
const plainTextHtml =
|
|
2911
|
-
return
|
|
3034
|
+
const plainTextHtml = lineString.replaceAll('<', '<').replaceAll('>', '>');
|
|
3035
|
+
return _updateLine( plainTextHtml );
|
|
2912
3036
|
}
|
|
2913
3037
|
|
|
2914
|
-
this._currentLineNumber =
|
|
2915
|
-
this._currentLineString =
|
|
2916
|
-
|
|
2917
|
-
const tokensToEvaluate = this._getTokensFromLine( linestring );
|
|
3038
|
+
this._currentLineNumber = lineNumber;
|
|
3039
|
+
this._currentLineString = lineString;
|
|
2918
3040
|
|
|
3041
|
+
const tokensToEvaluate = this._getTokensFromLine( lineString );
|
|
2919
3042
|
if( !tokensToEvaluate.length )
|
|
2920
3043
|
{
|
|
2921
|
-
return
|
|
3044
|
+
return _updateLine( "" );
|
|
2922
3045
|
}
|
|
2923
3046
|
|
|
2924
|
-
|
|
3047
|
+
let lineInnerHtml = "";
|
|
2925
3048
|
|
|
2926
3049
|
// Process all tokens
|
|
2927
|
-
for(
|
|
3050
|
+
for( let i = 0; i < tokensToEvaluate.length; ++i )
|
|
2928
3051
|
{
|
|
2929
3052
|
let it = i - 1;
|
|
2930
3053
|
let prev = tokensToEvaluate[ it ];
|
|
2931
|
-
while( prev == ' ' )
|
|
3054
|
+
while( prev == ' ' )
|
|
3055
|
+
{
|
|
2932
3056
|
it--;
|
|
2933
3057
|
prev = tokensToEvaluate[ it ];
|
|
2934
3058
|
}
|
|
2935
3059
|
|
|
2936
3060
|
it = i + 1;
|
|
2937
3061
|
let next = tokensToEvaluate[ it ];
|
|
2938
|
-
while( next == ' ' || next == '"' )
|
|
3062
|
+
while( next == ' ' || next == '"' )
|
|
3063
|
+
{
|
|
2939
3064
|
it++;
|
|
2940
3065
|
next = tokensToEvaluate[ it ];
|
|
2941
3066
|
}
|
|
@@ -2946,7 +3071,13 @@ class CodeEditor {
|
|
|
2946
3071
|
{
|
|
2947
3072
|
const blockCommentsToken = ( lang.blockCommentsTokens ?? this.defaultBlockCommentTokens )[ 0 ];
|
|
2948
3073
|
if( token.substr( 0, blockCommentsToken.length ) == blockCommentsToken )
|
|
2949
|
-
this._buildingBlockComment =
|
|
3074
|
+
this._buildingBlockComment = lineNumber;
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
// Pop current scope if necessary
|
|
3078
|
+
if( token === "}" )
|
|
3079
|
+
{
|
|
3080
|
+
this._scopeStack.pop();
|
|
2950
3081
|
}
|
|
2951
3082
|
|
|
2952
3083
|
lineInnerHtml += this._evaluateToken( {
|
|
@@ -2960,20 +3091,49 @@ class CodeEditor {
|
|
|
2960
3091
|
isLastToken: (i == tokensToEvaluate.length - 1),
|
|
2961
3092
|
tokens: tokensToEvaluate
|
|
2962
3093
|
} );
|
|
3094
|
+
|
|
3095
|
+
// Store current scopes
|
|
3096
|
+
if( token === "{" )
|
|
3097
|
+
{
|
|
3098
|
+
// Get some context about the scope from previous lines
|
|
3099
|
+
const contextTokens = [
|
|
3100
|
+
...this._getTokensFromLine( this.code.lines[ lineNumber - 2 ] ),
|
|
3101
|
+
...this._getTokensFromLine( this.code.lines[ lineNumber - 1 ] ),
|
|
3102
|
+
...this._getTokensFromLine( this.code.lines[ lineNumber ].substring( 0, lineString.indexOf( token ) ) )
|
|
3103
|
+
].reverse().filter( v => v.length && v != ' ' );
|
|
3104
|
+
|
|
3105
|
+
let scopeType = contextTokens[ 1 ]; // This is the type of scope (function, class, enum, etc)
|
|
3106
|
+
|
|
3107
|
+
// Special cases
|
|
3108
|
+
if( scopeType === ":" ) // enum type specification
|
|
3109
|
+
{
|
|
3110
|
+
scopeType = contextTokens[ 3 ];
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
if( !this._mustHightlightWord( scopeType, CodeEditor.keywords, lang ) )
|
|
3114
|
+
{
|
|
3115
|
+
// If the scope type is not a keyword, use an empty string
|
|
3116
|
+
scopeType = "";
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
this._scopeStack.push( scopeType );
|
|
3120
|
+
}
|
|
2963
3121
|
}
|
|
2964
3122
|
|
|
2965
|
-
return
|
|
3123
|
+
return _updateLine( lineInnerHtml );
|
|
2966
3124
|
}
|
|
2967
3125
|
|
|
2968
|
-
_lineHasComment(
|
|
3126
|
+
_lineHasComment( lineString ) {
|
|
2969
3127
|
|
|
2970
|
-
const lang =
|
|
3128
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
2971
3129
|
|
|
2972
3130
|
if( !(lang.singleLineComments ?? true) )
|
|
3131
|
+
{
|
|
2973
3132
|
return;
|
|
3133
|
+
}
|
|
2974
3134
|
|
|
2975
3135
|
const singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
2976
|
-
const idx =
|
|
3136
|
+
const idx = lineString.indexOf( singleLineCommentToken );
|
|
2977
3137
|
|
|
2978
3138
|
if( idx > -1 )
|
|
2979
3139
|
{
|
|
@@ -2982,22 +3142,27 @@ class CodeEditor {
|
|
|
2982
3142
|
var err = false;
|
|
2983
3143
|
err |= stringKeys.some( function(v) {
|
|
2984
3144
|
var re = new RegExp( v, "g" );
|
|
2985
|
-
var matches = (
|
|
3145
|
+
var matches = (lineString.substring( 0, idx ).match( re ) || []);
|
|
2986
3146
|
return (matches.length % 2) !== 0;
|
|
2987
3147
|
} );
|
|
2988
3148
|
return err ? undefined : idx;
|
|
2989
3149
|
}
|
|
2990
3150
|
}
|
|
2991
3151
|
|
|
2992
|
-
_getTokensFromLine(
|
|
3152
|
+
_getTokensFromLine( lineString, skipNonWords ) {
|
|
3153
|
+
|
|
3154
|
+
if( !lineString || !lineString.length )
|
|
3155
|
+
{
|
|
3156
|
+
return [];
|
|
3157
|
+
}
|
|
2993
3158
|
|
|
2994
3159
|
// Check if line comment
|
|
2995
|
-
const ogLine =
|
|
2996
|
-
const hasCommentIdx = this._lineHasComment(
|
|
3160
|
+
const ogLine = lineString;
|
|
3161
|
+
const hasCommentIdx = this._lineHasComment( lineString );
|
|
2997
3162
|
|
|
2998
3163
|
if( hasCommentIdx != undefined )
|
|
2999
3164
|
{
|
|
3000
|
-
|
|
3165
|
+
lineString = ogLine.substring( 0, hasCommentIdx );
|
|
3001
3166
|
}
|
|
3002
3167
|
|
|
3003
3168
|
let tokensToEvaluate = []; // store in a temp array so we know prev and next tokens...
|
|
@@ -3013,25 +3178,25 @@ class CodeEditor {
|
|
|
3013
3178
|
charCounter += t.length;
|
|
3014
3179
|
};
|
|
3015
3180
|
|
|
3016
|
-
let iter =
|
|
3181
|
+
let iter = lineString.matchAll(/(<!--|-->|\*\/|\/\*|::|[\[\](){}<>.,;:*"'%@$!/= ])/g);
|
|
3017
3182
|
let subtokens = iter.next();
|
|
3018
3183
|
if( subtokens.value )
|
|
3019
3184
|
{
|
|
3020
3185
|
let idx = 0;
|
|
3021
3186
|
while( subtokens.value != undefined )
|
|
3022
3187
|
{
|
|
3023
|
-
const _pt =
|
|
3188
|
+
const _pt = lineString.substring(idx, subtokens.value.index);
|
|
3024
3189
|
if( _pt.length ) pushToken( _pt );
|
|
3025
3190
|
pushToken( subtokens.value[ 0 ] );
|
|
3026
3191
|
idx = subtokens.value.index + subtokens.value[ 0 ].length;
|
|
3027
3192
|
subtokens = iter.next();
|
|
3028
3193
|
if(!subtokens.value) {
|
|
3029
|
-
const _at =
|
|
3194
|
+
const _at = lineString.substring(idx);
|
|
3030
3195
|
if( _at.length ) pushToken( _at );
|
|
3031
3196
|
}
|
|
3032
3197
|
}
|
|
3033
3198
|
}
|
|
3034
|
-
else pushToken(
|
|
3199
|
+
else pushToken( lineString );
|
|
3035
3200
|
|
|
3036
3201
|
if( hasCommentIdx != undefined )
|
|
3037
3202
|
{
|
|
@@ -3061,6 +3226,22 @@ class CodeEditor {
|
|
|
3061
3226
|
// Scan for numbers again
|
|
3062
3227
|
return this._processTokens( tokens, idx );
|
|
3063
3228
|
}
|
|
3229
|
+
|
|
3230
|
+
const importantIdx = tokens.indexOf( 'important' );
|
|
3231
|
+
if( this.highlight == 'CSS' && importantIdx > -1 && tokens[ importantIdx - 1 ] === '!' )
|
|
3232
|
+
{
|
|
3233
|
+
tokens[ importantIdx - 1 ] = "!important";
|
|
3234
|
+
tokens.splice( importantIdx, 1 );
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
else if( this.highlight == 'PHP' )
|
|
3238
|
+
{
|
|
3239
|
+
const dollarIdx = tokens.indexOf( '$' );
|
|
3240
|
+
if( dollarIdx > -1 && tokens[ dollarIdx + 1 ] === 'this-' )
|
|
3241
|
+
{
|
|
3242
|
+
tokens[ dollarIdx ] = "$this";
|
|
3243
|
+
tokens[ dollarIdx + 1 ] = "-";
|
|
3244
|
+
}
|
|
3064
3245
|
}
|
|
3065
3246
|
|
|
3066
3247
|
return tokens;
|
|
@@ -3070,7 +3251,7 @@ class CodeEditor {
|
|
|
3070
3251
|
|
|
3071
3252
|
if( !lang )
|
|
3072
3253
|
{
|
|
3073
|
-
lang =
|
|
3254
|
+
lang = CodeEditor.languages[ this.highlight ];
|
|
3074
3255
|
}
|
|
3075
3256
|
|
|
3076
3257
|
let t = token;
|
|
@@ -3083,16 +3264,30 @@ class CodeEditor {
|
|
|
3083
3264
|
return kindArray[ this.highlight ] && kindArray[ this.highlight ][ t ] != undefined;
|
|
3084
3265
|
}
|
|
3085
3266
|
|
|
3267
|
+
_getTokenHighlighting( ctx, highlight ) {
|
|
3268
|
+
|
|
3269
|
+
const rules = [ ...HighlightRules.common, ...( HighlightRules[highlight] || [] ), ...HighlightRules.post_common ];
|
|
3270
|
+
|
|
3271
|
+
for( const rule of rules )
|
|
3272
|
+
{
|
|
3273
|
+
if( !rule.test( ctx, this ) )
|
|
3274
|
+
{
|
|
3275
|
+
continue;
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
const r = rule.action ? rule.action( ctx, this ) : undefined;
|
|
3279
|
+
if( rule.discard ) ctx.discardToken = r;
|
|
3280
|
+
return rule.className;
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
return null;
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3086
3286
|
_evaluateToken( ctxData ) {
|
|
3087
3287
|
|
|
3088
|
-
let token
|
|
3089
|
-
prev = ctxData.prev,
|
|
3090
|
-
next = ctxData.next,
|
|
3091
|
-
tokenIndex = ctxData.tokenIndex,
|
|
3092
|
-
isFirstToken = ctxData.isFirstToken,
|
|
3093
|
-
isLastToken = ctxData.isLastToken;
|
|
3288
|
+
let { token, prev, next, tokenIndex, isFirstToken, isLastToken } = ctxData;
|
|
3094
3289
|
|
|
3095
|
-
const lang =
|
|
3290
|
+
const lang = CodeEditor.languages[ this.highlight ],
|
|
3096
3291
|
highlight = this.highlight.replace( /\s/g, '' ).replaceAll( "+", "p" ).toLowerCase(),
|
|
3097
3292
|
customStringKeys = Object.assign( {}, this.stringKeys );
|
|
3098
3293
|
|
|
@@ -3114,14 +3309,14 @@ class CodeEditor {
|
|
|
3114
3309
|
|
|
3115
3310
|
if( usePreviousTokenToCheckString || ( this._buildingBlockComment === undefined && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) ) )
|
|
3116
3311
|
{
|
|
3117
|
-
const
|
|
3312
|
+
const _checkIfStringEnded = t => {
|
|
3118
3313
|
const idx = Object.values( customStringKeys ).indexOf( t );
|
|
3119
3314
|
this._stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
|
|
3120
3315
|
};
|
|
3121
3316
|
|
|
3122
3317
|
if( this._buildingString != undefined )
|
|
3123
3318
|
{
|
|
3124
|
-
|
|
3319
|
+
_checkIfStringEnded( usePreviousTokenToCheckString ? ctxData.nextWithSpaces : token );
|
|
3125
3320
|
}
|
|
3126
3321
|
else if( customStringKeys[ '@' + ( usePreviousTokenToCheckString ? ctxData.prevWithSpaces : token ) ] )
|
|
3127
3322
|
{
|
|
@@ -3131,77 +3326,25 @@ class CodeEditor {
|
|
|
3131
3326
|
// Check if string ended in same token using next...
|
|
3132
3327
|
if( usePreviousTokenToCheckString )
|
|
3133
3328
|
{
|
|
3134
|
-
|
|
3329
|
+
_checkIfStringEnded( ctxData.nextWithSpaces );
|
|
3135
3330
|
}
|
|
3136
3331
|
}
|
|
3137
3332
|
}
|
|
3138
3333
|
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
token_classname = "cm-com";
|
|
3148
|
-
|
|
3149
|
-
else if( this._buildingString != undefined )
|
|
3150
|
-
discardToken = this._appendStringToken( token );
|
|
3151
|
-
|
|
3152
|
-
else if( this._isKeyword( ctxData, lang ) )
|
|
3153
|
-
token_classname = "cm-kwd";
|
|
3154
|
-
|
|
3155
|
-
else if( this._mustHightlightWord( token, CodeEditor.builtIn, lang ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
|
|
3156
|
-
token_classname = "cm-bln";
|
|
3157
|
-
|
|
3158
|
-
else if( this._mustHightlightWord( token, CodeEditor.statementsAndDeclarations, lang ) )
|
|
3159
|
-
token_classname = "cm-std";
|
|
3160
|
-
|
|
3161
|
-
else if( this._mustHightlightWord( token, CodeEditor.symbols, lang ) )
|
|
3162
|
-
token_classname = "cm-sym";
|
|
3163
|
-
|
|
3164
|
-
else if( token.substr( 0, singleLineCommentToken.length ) == singleLineCommentToken )
|
|
3165
|
-
token_classname = "cm-com";
|
|
3166
|
-
|
|
3167
|
-
else if( this._isNumber( token ) || this._isNumber( token.replace(/[px]|[em]|%/g,'') ) )
|
|
3168
|
-
token_classname = "cm-dec";
|
|
3169
|
-
|
|
3170
|
-
else if( this._isCSSClass( ctxData ) )
|
|
3171
|
-
token_classname = "cm-kwd";
|
|
3172
|
-
|
|
3173
|
-
else if ( this._isType( ctxData, lang ) )
|
|
3174
|
-
token_classname = "cm-typ";
|
|
3175
|
-
|
|
3176
|
-
else if ( highlight == 'batch' && ( token == '@' || prev == ':' || prev == '@' ) )
|
|
3177
|
-
token_classname = "cm-kwd";
|
|
3178
|
-
|
|
3179
|
-
else if ( [ 'cpp', 'c', 'wgsl', 'glsl' ].indexOf( highlight ) > -1 && token.includes( '#' ) ) // C++ preprocessor
|
|
3180
|
-
token_classname = "cm-ppc";
|
|
3181
|
-
|
|
3182
|
-
else if ( highlight == 'cpp' && prev == '<' && (next == '>' || next == '*') ) // Defining template type in C++
|
|
3183
|
-
token_classname = "cm-typ";
|
|
3184
|
-
|
|
3185
|
-
else if ( highlight == 'cpp' && (next == '::' || prev == '::' && next != '(' )) // C++ Class
|
|
3186
|
-
token_classname = "cm-typ";
|
|
3187
|
-
|
|
3188
|
-
else if ( highlight == 'css' && prev == ':' && (next == ';' || next == '!important') ) // CSS value
|
|
3189
|
-
token_classname = "cm-str";
|
|
3190
|
-
|
|
3191
|
-
else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
|
|
3192
|
-
token_classname = "cm-typ";
|
|
3193
|
-
|
|
3194
|
-
else if ( this._markdownHeader || ( highlight == 'markdown' && isFirstToken && token.replaceAll('#', '').length != token.length ) ) // Header
|
|
3195
|
-
{
|
|
3196
|
-
token_classname = "cm-kwd";
|
|
3197
|
-
this._markdownHeader = true;
|
|
3198
|
-
}
|
|
3199
|
-
|
|
3200
|
-
else if ( token[ 0 ] != '@' && token[ 0 ] != ',' && next == '(' )
|
|
3201
|
-
token_classname = "cm-mtd";
|
|
3334
|
+
// Update context data for next tests
|
|
3335
|
+
ctxData.discardToken = false;
|
|
3336
|
+
ctxData.inBlockComment = this._buildingBlockComment;
|
|
3337
|
+
ctxData.markdownHeader = this._markdownHeader;
|
|
3338
|
+
ctxData.inString = this._buildingString;
|
|
3339
|
+
ctxData.singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
3340
|
+
ctxData.lang = lang;
|
|
3341
|
+
ctxData.scope = this._scopeStack[ this._scopeStack.length - 1 ];
|
|
3202
3342
|
|
|
3343
|
+
// Get highlighting class based on language common and specific rules
|
|
3344
|
+
let tokenClass = this._getTokenHighlighting( ctxData, highlight );
|
|
3203
3345
|
|
|
3204
|
-
|
|
3346
|
+
const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
|
|
3347
|
+
if( ( lang.blockComments ?? true ) && this._buildingBlockComment != undefined
|
|
3205
3348
|
&& token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
|
|
3206
3349
|
{
|
|
3207
3350
|
this._blockCommentCache.push( new LX.vec2( this._buildingBlockComment, this._currentLineNumber ) );
|
|
@@ -3212,34 +3355,36 @@ class CodeEditor {
|
|
|
3212
3355
|
if( this._buildingString && ( this._stringEnded || isLastToken ) )
|
|
3213
3356
|
{
|
|
3214
3357
|
token = this._getCurrentString();
|
|
3215
|
-
|
|
3216
|
-
discardToken = false;
|
|
3358
|
+
tokenClass = "cm-str";
|
|
3359
|
+
ctxData.discardToken = false;
|
|
3217
3360
|
}
|
|
3218
3361
|
|
|
3219
3362
|
// Update state
|
|
3220
3363
|
this._buildingString = this._stringEnded ? undefined : this._buildingString;
|
|
3221
3364
|
|
|
3222
|
-
if( discardToken )
|
|
3365
|
+
if( ctxData.discardToken )
|
|
3223
3366
|
{
|
|
3224
3367
|
return "";
|
|
3225
3368
|
}
|
|
3226
3369
|
|
|
3227
|
-
|
|
3228
|
-
token = token.replace( ">", ">" );
|
|
3370
|
+
// Replace html chars
|
|
3371
|
+
token = token.replace( "<", "<" ).replace( ">", ">" );
|
|
3229
3372
|
|
|
3230
3373
|
// No highlighting, no need to put it inside another span..
|
|
3231
|
-
if( !
|
|
3374
|
+
if( !tokenClass )
|
|
3232
3375
|
{
|
|
3233
3376
|
return token;
|
|
3234
3377
|
}
|
|
3235
3378
|
|
|
3236
|
-
return
|
|
3379
|
+
return `<span class="${ highlight } ${ tokenClass }">${ token }</span>`;
|
|
3237
3380
|
}
|
|
3238
3381
|
|
|
3239
3382
|
_appendStringToken( token ) {
|
|
3240
3383
|
|
|
3241
3384
|
if( !this._pendingString )
|
|
3385
|
+
{
|
|
3242
3386
|
this._pendingString = "";
|
|
3387
|
+
}
|
|
3243
3388
|
|
|
3244
3389
|
this._pendingString += token;
|
|
3245
3390
|
|
|
@@ -3285,11 +3430,9 @@ class CodeEditor {
|
|
|
3285
3430
|
return false;
|
|
3286
3431
|
}
|
|
3287
3432
|
|
|
3288
|
-
_isKeyword( ctxData
|
|
3433
|
+
_isKeyword( ctxData ) {
|
|
3289
3434
|
|
|
3290
|
-
const token = ctxData
|
|
3291
|
-
const tokenIndex = ctxData.tokenIndex;
|
|
3292
|
-
const tokens = ctxData.tokens;
|
|
3435
|
+
const { token, tokenIndex, tokens, lang } = ctxData;
|
|
3293
3436
|
|
|
3294
3437
|
let isKwd = this._mustHightlightWord( token, CodeEditor.keywords ) || this.highlight == 'XML';
|
|
3295
3438
|
|
|
@@ -3306,6 +3449,10 @@ class CodeEditor {
|
|
|
3306
3449
|
isKwd |= ( ctxData.tokens[ tokenIndex - 2 ] == '$' );
|
|
3307
3450
|
}
|
|
3308
3451
|
}
|
|
3452
|
+
if( this.highlight == 'Markdown' )
|
|
3453
|
+
{
|
|
3454
|
+
isKwd = ( this._markdownHeader !== undefined );
|
|
3455
|
+
}
|
|
3309
3456
|
else if( lang.tags )
|
|
3310
3457
|
{
|
|
3311
3458
|
isKwd &= ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) != undefined );
|
|
@@ -3315,24 +3462,14 @@ class CodeEditor {
|
|
|
3315
3462
|
return isKwd;
|
|
3316
3463
|
}
|
|
3317
3464
|
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
const token = ctxData.token;
|
|
3321
|
-
const prev = ctxData.prev;
|
|
3322
|
-
const next = ctxData.next;
|
|
3465
|
+
_isNumber( token ) {
|
|
3323
3466
|
|
|
3324
|
-
|
|
3467
|
+
const lang = CodeEditor.languages[ this.highlight ];
|
|
3468
|
+
if( !( lang.numbers ?? true ) )
|
|
3325
3469
|
{
|
|
3326
3470
|
return false;
|
|
3327
3471
|
}
|
|
3328
3472
|
|
|
3329
|
-
return ( prev == '.' || prev == '::'
|
|
3330
|
-
|| ( prev == ':' && next == '{' )
|
|
3331
|
-
|| ( token[ 0 ] == '#' && prev != ':' ) );
|
|
3332
|
-
}
|
|
3333
|
-
|
|
3334
|
-
_isNumber( token ) {
|
|
3335
|
-
|
|
3336
3473
|
const subToken = token.substring( 0, token.length - 1 );
|
|
3337
3474
|
|
|
3338
3475
|
if( this.highlight == 'C++' )
|
|
@@ -3364,53 +3501,24 @@ class CodeEditor {
|
|
|
3364
3501
|
return token.length && token != ' ' && !Number.isNaN( +token );
|
|
3365
3502
|
}
|
|
3366
3503
|
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
const token = ctxData.token;
|
|
3370
|
-
const prev = ctxData.prev;
|
|
3371
|
-
const next = ctxData.next;
|
|
3372
|
-
|
|
3373
|
-
// Common case
|
|
3374
|
-
if( this._mustHightlightWord( token, CodeEditor.types, lang ) )
|
|
3375
|
-
{
|
|
3376
|
-
return true;
|
|
3377
|
-
}
|
|
3504
|
+
_encloseSelectedWordWithKey( key, lidx, cursor ) {
|
|
3378
3505
|
|
|
3379
|
-
if(
|
|
3380
|
-
{
|
|
3381
|
-
return (prev == 'class' && next == '{') || (prev == 'new' && next == '(');
|
|
3382
|
-
}
|
|
3383
|
-
else if( this.highlight == 'C++' )
|
|
3506
|
+
if( !cursor.selection || ( cursor.selection.fromY != cursor.selection.toY ) )
|
|
3384
3507
|
{
|
|
3385
|
-
return
|
|
3386
|
-
}
|
|
3387
|
-
else if ( this.highlight == 'WGSL' )
|
|
3388
|
-
{
|
|
3389
|
-
const not_kwd = !this._mustHightlightWord( token, CodeEditor.keywords, lang );
|
|
3390
|
-
return (prev == 'struct' && next == '{') ||
|
|
3391
|
-
(not_kwd && prev == ':' && next == ';') ||
|
|
3392
|
-
( not_kwd &&
|
|
3393
|
-
( prev == ':' && next == ')' || prev == ':' && next == ',' || prev == '>' && next == '{'
|
|
3394
|
-
|| prev == '<' && next == ',' || prev == '<' && next == '>' || prev == '>' && token != ';' && !next ));
|
|
3508
|
+
return false;
|
|
3395
3509
|
}
|
|
3396
|
-
}
|
|
3397
|
-
|
|
3398
|
-
_encloseSelectedWordWithKey( key, lidx, cursor ) {
|
|
3399
|
-
|
|
3400
|
-
if( !cursor.selection || (cursor.selection.fromY != cursor.selection.toY) )
|
|
3401
|
-
return false;
|
|
3402
3510
|
|
|
3403
3511
|
cursor.selection.invertIfNecessary();
|
|
3404
3512
|
|
|
3405
3513
|
// Insert first..
|
|
3406
3514
|
this.code.lines[ lidx ] = [
|
|
3407
|
-
this.code.lines[ lidx ].slice(0, cursor.selection.fromX),
|
|
3515
|
+
this.code.lines[ lidx ].slice( 0, cursor.selection.fromX ),
|
|
3408
3516
|
key,
|
|
3409
|
-
this.code.lines[ lidx ].slice(cursor.selection.fromX)
|
|
3517
|
+
this.code.lines[ lidx ].slice( cursor.selection.fromX )
|
|
3410
3518
|
].join('');
|
|
3411
3519
|
|
|
3412
3520
|
// Go to the end of the word
|
|
3413
|
-
this.cursorToPosition(cursor, cursor.selection.toX + 1);
|
|
3521
|
+
this.cursorToPosition( cursor, cursor.selection.toX + 1 );
|
|
3414
3522
|
|
|
3415
3523
|
// Change next key?
|
|
3416
3524
|
switch(key)
|
|
@@ -3578,7 +3686,7 @@ class CodeEditor {
|
|
|
3578
3686
|
if( !key ) return;
|
|
3579
3687
|
|
|
3580
3688
|
cursor._left += this.charWidth;
|
|
3581
|
-
cursor.style.left =
|
|
3689
|
+
cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
|
|
3582
3690
|
cursor.position++;
|
|
3583
3691
|
|
|
3584
3692
|
this.restartBlink();
|
|
@@ -3598,7 +3706,7 @@ class CodeEditor {
|
|
|
3598
3706
|
|
|
3599
3707
|
cursor._left -= this.charWidth;
|
|
3600
3708
|
cursor._left = Math.max( cursor._left, 0 );
|
|
3601
|
-
cursor.style.left =
|
|
3709
|
+
cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
|
|
3602
3710
|
cursor.position--;
|
|
3603
3711
|
cursor.position = Math.max( cursor.position, 0 );
|
|
3604
3712
|
this.restartBlink();
|
|
@@ -3616,8 +3724,8 @@ class CodeEditor {
|
|
|
3616
3724
|
cursorToTop( cursor, resetLeft = false ) {
|
|
3617
3725
|
|
|
3618
3726
|
cursor._top -= this.lineHeight;
|
|
3619
|
-
cursor._top = Math.max(cursor._top, 0);
|
|
3620
|
-
cursor.style.top =
|
|
3727
|
+
cursor._top = Math.max( cursor._top, 0 );
|
|
3728
|
+
cursor.style.top = `calc(${ cursor._top }px)`;
|
|
3621
3729
|
this.restartBlink();
|
|
3622
3730
|
|
|
3623
3731
|
if( resetLeft )
|
|
@@ -3647,10 +3755,10 @@ class CodeEditor {
|
|
|
3647
3755
|
|
|
3648
3756
|
const currentScrollTop = this.getScrollTop();
|
|
3649
3757
|
const tabsHeight = this.tabs.root.getBoundingClientRect().height;
|
|
3650
|
-
const
|
|
3758
|
+
const statusPanelHeight = this.skipInfo ? 0 : this.statusPanel.root.getBoundingClientRect().height;
|
|
3651
3759
|
const scrollerHeight = this.codeScroller.offsetHeight;
|
|
3652
3760
|
|
|
3653
|
-
var lastLine = ( ( scrollerHeight - tabsHeight -
|
|
3761
|
+
var lastLine = ( ( scrollerHeight - tabsHeight - statusPanelHeight + currentScrollTop ) / this.lineHeight )|0;
|
|
3654
3762
|
if( cursor.line >= lastLine )
|
|
3655
3763
|
{
|
|
3656
3764
|
this.setScrollTop( currentScrollTop + this.lineHeight );
|
|
@@ -3660,7 +3768,9 @@ class CodeEditor {
|
|
|
3660
3768
|
cursorToString( cursor, text, reverse ) {
|
|
3661
3769
|
|
|
3662
3770
|
if( !text.length )
|
|
3771
|
+
{
|
|
3663
3772
|
return;
|
|
3773
|
+
}
|
|
3664
3774
|
|
|
3665
3775
|
for( let char of text )
|
|
3666
3776
|
{
|
|
@@ -3672,7 +3782,7 @@ class CodeEditor {
|
|
|
3672
3782
|
|
|
3673
3783
|
cursor.position = position;
|
|
3674
3784
|
cursor._left = position * this.charWidth;
|
|
3675
|
-
cursor.style.left =
|
|
3785
|
+
cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
|
|
3676
3786
|
}
|
|
3677
3787
|
|
|
3678
3788
|
cursorToLine( cursor, line, resetLeft = false ) {
|
|
@@ -3709,9 +3819,9 @@ class CodeEditor {
|
|
|
3709
3819
|
for( let cursor of this.cursors.children )
|
|
3710
3820
|
{
|
|
3711
3821
|
cursor._left = cursor.position * this.charWidth;
|
|
3712
|
-
cursor.style.left =
|
|
3822
|
+
cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
|
|
3713
3823
|
cursor._top = cursor.line * this.lineHeight;
|
|
3714
|
-
cursor.style.top =
|
|
3824
|
+
cursor.style.top = `calc(${ cursor._top }px)`;
|
|
3715
3825
|
}
|
|
3716
3826
|
}
|
|
3717
3827
|
|
|
@@ -3730,9 +3840,9 @@ class CodeEditor {
|
|
|
3730
3840
|
cursor.line = state.line ?? 0;
|
|
3731
3841
|
|
|
3732
3842
|
cursor._left = cursor.position * this.charWidth;
|
|
3733
|
-
cursor.style.left =
|
|
3843
|
+
cursor.style.left = `calc( ${ cursor._left }px + ${ this.xPadding } )`;
|
|
3734
3844
|
cursor._top = cursor.line * this.lineHeight;
|
|
3735
|
-
cursor.style.top =
|
|
3845
|
+
cursor.style.top = `calc(${ cursor._top }px)`;
|
|
3736
3846
|
|
|
3737
3847
|
if( state.selection )
|
|
3738
3848
|
{
|
|
@@ -3772,7 +3882,8 @@ class CodeEditor {
|
|
|
3772
3882
|
|
|
3773
3883
|
_addSpaceTabs( cursor, n ) {
|
|
3774
3884
|
|
|
3775
|
-
for( var i = 0; i < n; ++i )
|
|
3885
|
+
for( var i = 0; i < n; ++i )
|
|
3886
|
+
{
|
|
3776
3887
|
this.actions[ 'Tab' ].callback( cursor.line, cursor, null );
|
|
3777
3888
|
}
|
|
3778
3889
|
}
|
|
@@ -3789,13 +3900,17 @@ class CodeEditor {
|
|
|
3789
3900
|
}
|
|
3790
3901
|
|
|
3791
3902
|
_removeSpaces( cursor ) {
|
|
3903
|
+
|
|
3792
3904
|
const lidx = cursor.line;
|
|
3905
|
+
|
|
3793
3906
|
// Remove indentation
|
|
3794
3907
|
let lineStart = firstNonspaceIndex( this.code.lines[ lidx ] );
|
|
3795
3908
|
|
|
3796
3909
|
// Nothing to remove... we are at the start of the line
|
|
3797
3910
|
if( lineStart == 0 )
|
|
3911
|
+
{
|
|
3798
3912
|
return;
|
|
3913
|
+
}
|
|
3799
3914
|
|
|
3800
3915
|
// Only tabs/spaces in the line...
|
|
3801
3916
|
if( lineStart == -1 ) {
|
|
@@ -3835,7 +3950,7 @@ class CodeEditor {
|
|
|
3835
3950
|
|
|
3836
3951
|
setScrollLeft( value ) {
|
|
3837
3952
|
if( !this.codeScroller ) return;
|
|
3838
|
-
doAsync( () => {
|
|
3953
|
+
LX.doAsync( () => {
|
|
3839
3954
|
this.codeScroller.scrollLeft = value;
|
|
3840
3955
|
this.setScrollBarValue( 'horizontal', 0 );
|
|
3841
3956
|
}, 20 );
|
|
@@ -3843,7 +3958,7 @@ class CodeEditor {
|
|
|
3843
3958
|
|
|
3844
3959
|
setScrollTop( value ) {
|
|
3845
3960
|
if( !this.codeScroller ) return;
|
|
3846
|
-
doAsync( () => {
|
|
3961
|
+
LX.doAsync( () => {
|
|
3847
3962
|
this.codeScroller.scrollTop = value;
|
|
3848
3963
|
this.setScrollBarValue( 'vertical' );
|
|
3849
3964
|
}, 20 );
|
|
@@ -3858,13 +3973,13 @@ class CodeEditor {
|
|
|
3858
3973
|
const scrollWidth = maxLineLength * this.charWidth + CodeEditor.LINE_GUTTER_WIDTH;
|
|
3859
3974
|
|
|
3860
3975
|
const tabsHeight = this.tabs.root.getBoundingClientRect().height;
|
|
3861
|
-
const
|
|
3976
|
+
const statusPanelHeight = this.skipInfo ? 0 : this.statusPanel.root.getBoundingClientRect().height;
|
|
3862
3977
|
const scrollHeight = this.code.lines.length * this.lineHeight;
|
|
3863
3978
|
|
|
3864
3979
|
this._lastMaxLineLength = maxLineLength;
|
|
3865
3980
|
|
|
3866
3981
|
this.codeSizer.style.minWidth = scrollWidth + "px";
|
|
3867
|
-
this.codeSizer.style.minHeight = ( scrollHeight + tabsHeight +
|
|
3982
|
+
this.codeSizer.style.minHeight = ( scrollHeight + tabsHeight + statusPanelHeight ) + "px";
|
|
3868
3983
|
|
|
3869
3984
|
this.resizeScrollBars();
|
|
3870
3985
|
|
|
@@ -4034,7 +4149,7 @@ class CodeEditor {
|
|
|
4034
4149
|
return [ word, from, to ];
|
|
4035
4150
|
}
|
|
4036
4151
|
|
|
4037
|
-
_measureChar( char = "a",
|
|
4152
|
+
_measureChar( char = "a", useFloating = false, getBB = false ) {
|
|
4038
4153
|
const parentContainer = LX.makeContainer( null, "lexcodeeditor", "", document.body );
|
|
4039
4154
|
const container = LX.makeContainer( null, "code", "", parentContainer );
|
|
4040
4155
|
const line = document.createElement( "pre" );
|
|
@@ -4044,8 +4159,8 @@ class CodeEditor {
|
|
|
4044
4159
|
text.innerText = char;
|
|
4045
4160
|
var rect = text.getBoundingClientRect();
|
|
4046
4161
|
LX.deleteElement( parentContainer );
|
|
4047
|
-
const bb = [
|
|
4048
|
-
return
|
|
4162
|
+
const bb = [ useFloating ? rect.width : Math.floor( rect.width ), useFloating ? rect.height : Math.floor( rect.height ) ];
|
|
4163
|
+
return getBB ? bb : bb[ 0 ];
|
|
4049
4164
|
}
|
|
4050
4165
|
|
|
4051
4166
|
measureString( str ) {
|
|
@@ -4333,7 +4448,9 @@ class CodeEditor {
|
|
|
4333
4448
|
text = text ?? this._lastTextFound;
|
|
4334
4449
|
|
|
4335
4450
|
if( !text )
|
|
4451
|
+
{
|
|
4336
4452
|
return;
|
|
4453
|
+
}
|
|
4337
4454
|
|
|
4338
4455
|
let cursor = this._getCurrentCursor();
|
|
4339
4456
|
let cursorData = new LX.vec2( cursor.position, cursor.line );
|
|
@@ -4344,6 +4461,7 @@ class CodeEditor {
|
|
|
4344
4461
|
{
|
|
4345
4462
|
LX.deleteElement( this._lastResult.dom );
|
|
4346
4463
|
cursorData = this._lastResult.pos;
|
|
4464
|
+
cursorData.x += text.length * ( reverse ? -1 : 1 );
|
|
4347
4465
|
delete this._lastResult;
|
|
4348
4466
|
}
|
|
4349
4467
|
|
|
@@ -4389,10 +4507,12 @@ class CodeEditor {
|
|
|
4389
4507
|
}
|
|
4390
4508
|
}
|
|
4391
4509
|
|
|
4392
|
-
if( line == null)
|
|
4510
|
+
if( line == null )
|
|
4393
4511
|
{
|
|
4394
4512
|
if( !skipAlert )
|
|
4513
|
+
{
|
|
4395
4514
|
alert( "No results!" );
|
|
4515
|
+
}
|
|
4396
4516
|
|
|
4397
4517
|
const lastLine = this.code.lines.length - 1;
|
|
4398
4518
|
|
|
@@ -4400,6 +4520,7 @@ class CodeEditor {
|
|
|
4400
4520
|
'dom': this.searchResultSelections.lastChild,
|
|
4401
4521
|
'pos': reverse ? new LX.vec2( this.code.lines[ lastLine ].length, lastLine ) : new LX.vec2( 0, 0 )
|
|
4402
4522
|
};
|
|
4523
|
+
|
|
4403
4524
|
return;
|
|
4404
4525
|
}
|
|
4405
4526
|
|
|
@@ -4409,9 +4530,10 @@ class CodeEditor {
|
|
|
4409
4530
|
have to add the length of the substring (0, first_ocurrence)
|
|
4410
4531
|
*/
|
|
4411
4532
|
|
|
4412
|
-
|
|
4413
4533
|
if( !reverse )
|
|
4534
|
+
{
|
|
4414
4535
|
char += ( line == cursorData.y ? cursorData.x : 0 );
|
|
4536
|
+
}
|
|
4415
4537
|
|
|
4416
4538
|
|
|
4417
4539
|
// Text found..
|
|
@@ -4439,8 +4561,13 @@ class CodeEditor {
|
|
|
4439
4561
|
|
|
4440
4562
|
this._lastResult = {
|
|
4441
4563
|
'dom': this.searchResultSelections.lastChild,
|
|
4442
|
-
'pos': new LX.vec2( char
|
|
4564
|
+
'pos': new LX.vec2( char , line ),
|
|
4565
|
+
reverse
|
|
4443
4566
|
};
|
|
4567
|
+
|
|
4568
|
+
// Force focus back to search box
|
|
4569
|
+
const input = this.searchbox.querySelector( 'input' );
|
|
4570
|
+
input.focus();
|
|
4444
4571
|
}
|
|
4445
4572
|
|
|
4446
4573
|
showSearchLineBox() {
|
|
@@ -4527,13 +4654,16 @@ class CodeEditor {
|
|
|
4527
4654
|
|
|
4528
4655
|
number = number ?? this.state.activeLine;
|
|
4529
4656
|
|
|
4530
|
-
|
|
4657
|
+
const cursor = this._getCurrentCursor();
|
|
4658
|
+
this._updateDataInfoPanel( "@cursor-data", `Ln ${ number + 1 }, Col ${ cursor.position + 1 }` );
|
|
4531
4659
|
|
|
4532
|
-
const
|
|
4533
|
-
let line = this.code.childNodes[
|
|
4660
|
+
const oldLocal = this.toLocalLine( this.state.activeLine );
|
|
4661
|
+
let line = this.code.childNodes[ oldLocal ];
|
|
4534
4662
|
|
|
4535
4663
|
if( !line )
|
|
4664
|
+
{
|
|
4536
4665
|
return;
|
|
4666
|
+
}
|
|
4537
4667
|
|
|
4538
4668
|
line.classList.remove( 'active-line' );
|
|
4539
4669
|
|
|
@@ -4541,71 +4671,54 @@ class CodeEditor {
|
|
|
4541
4671
|
{
|
|
4542
4672
|
this.state.activeLine = number;
|
|
4543
4673
|
|
|
4544
|
-
const
|
|
4545
|
-
line = this.code.childNodes[
|
|
4674
|
+
const newLocal = this.toLocalLine( number );
|
|
4675
|
+
line = this.code.childNodes[ newLocal ];
|
|
4546
4676
|
if( line ) line.classList.add( 'active-line' );
|
|
4547
4677
|
}
|
|
4548
4678
|
}
|
|
4549
4679
|
|
|
4550
4680
|
_hideActiveLine() {
|
|
4551
|
-
|
|
4552
4681
|
this.code.querySelectorAll( '.active-line' ).forEach( e => e.classList.remove( 'active-line' ) );
|
|
4553
4682
|
}
|
|
4554
4683
|
|
|
4555
|
-
|
|
4556
|
-
|
|
4684
|
+
_setFontSize( size ) {
|
|
4557
4685
|
// Change font size
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
var pixels = parseInt( s.getPropertyValue( "--code-editor-font-size" ) );
|
|
4562
|
-
pixels = LX.clamp( pixels + 1, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE );
|
|
4563
|
-
r.style.setProperty( "--code-editor-font-size", pixels + "px" );
|
|
4686
|
+
this.fontSize = size;
|
|
4687
|
+
const r = document.querySelector( ':root' );
|
|
4688
|
+
r.style.setProperty( "--code-editor-font-size", `${ this.fontSize }px` );
|
|
4564
4689
|
this.charWidth = this._measureChar( "a", true );
|
|
4565
4690
|
|
|
4566
|
-
|
|
4691
|
+
window.localStorage.setItem( "lexcodeeditor-font-size", this.fontSize );
|
|
4567
4692
|
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4693
|
+
// Change row size
|
|
4694
|
+
const rowPixels = this.fontSize + 6;
|
|
4695
|
+
r.style.setProperty( "--code-editor-row-height", `${ rowPixels }px` );
|
|
4696
|
+
this.lineHeight = rowPixels;
|
|
4571
4697
|
|
|
4572
4698
|
// Relocate cursors
|
|
4573
|
-
|
|
4574
4699
|
this.relocateCursors();
|
|
4575
4700
|
|
|
4576
4701
|
// Resize the code area
|
|
4577
|
-
|
|
4578
4702
|
this.processLines();
|
|
4579
|
-
}
|
|
4580
|
-
|
|
4581
|
-
_decreaseFontSize() {
|
|
4582
|
-
|
|
4583
|
-
// Change font size
|
|
4584
4703
|
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
pixels = LX.clamp( pixels - 1, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE );
|
|
4589
|
-
r.style.setProperty( "--code-editor-font-size", pixels + "px" );
|
|
4590
|
-
this.charWidth = this._measureChar( "a", true );
|
|
4591
|
-
|
|
4592
|
-
// Change row size
|
|
4593
|
-
|
|
4594
|
-
var row_pixels = pixels + 6;
|
|
4595
|
-
r.style.setProperty( "--code-editor-row-height", row_pixels + "px" );
|
|
4596
|
-
this.lineHeight = row_pixels;
|
|
4597
|
-
|
|
4598
|
-
// Relocate cursors
|
|
4704
|
+
// Emit event
|
|
4705
|
+
LX.emit( "@font-size", this.fontSize );
|
|
4706
|
+
}
|
|
4599
4707
|
|
|
4600
|
-
|
|
4708
|
+
_applyFontSizeOffset( offset = 0 ) {
|
|
4709
|
+
const newFontSize = LX.clamp( this.fontSize + offset, CodeEditor.CODE_MIN_FONT_SIZE, CodeEditor.CODE_MAX_FONT_SIZE );
|
|
4710
|
+
this._setFontSize( newFontSize );
|
|
4711
|
+
}
|
|
4601
4712
|
|
|
4602
|
-
|
|
4713
|
+
_increaseFontSize() {
|
|
4714
|
+
this._applyFontSizeOffset( 1 );
|
|
4715
|
+
}
|
|
4603
4716
|
|
|
4604
|
-
|
|
4717
|
+
_decreaseFontSize() {
|
|
4718
|
+
this._applyFontSizeOffset( -1 );
|
|
4605
4719
|
}
|
|
4606
4720
|
|
|
4607
4721
|
_clearTmpVariables() {
|
|
4608
|
-
|
|
4609
4722
|
delete this._currentLineString;
|
|
4610
4723
|
delete this._currentLineNumber;
|
|
4611
4724
|
delete this._buildingString;
|
|
@@ -4613,13 +4726,35 @@ class CodeEditor {
|
|
|
4613
4726
|
delete this._buildingBlockComment;
|
|
4614
4727
|
delete this._markdownHeader;
|
|
4615
4728
|
delete this._lastResult;
|
|
4729
|
+
delete this._scopeStack;
|
|
4616
4730
|
}
|
|
4617
4731
|
}
|
|
4618
4732
|
|
|
4619
|
-
CodeEditor.
|
|
4733
|
+
CodeEditor.languages = {
|
|
4734
|
+
'Plain Text': { ext: 'txt', blockComments: false, singleLineComments: false, numbers: false },
|
|
4735
|
+
'JavaScript': { ext: 'js' },
|
|
4736
|
+
'TypeScript': { ext: 'ts' },
|
|
4737
|
+
'C': { ext: [ 'c', 'h' ], usePreprocessor: true },
|
|
4738
|
+
'C++': { ext: [ 'cpp', 'hpp' ], usePreprocessor: true },
|
|
4739
|
+
'CSS': { ext: 'css' },
|
|
4740
|
+
'CMake': { ext: 'cmake', singleLineCommentToken: '#', blockComments: false, ignoreCase: true },
|
|
4741
|
+
'GLSL': { ext: 'glsl', usePreprocessor: true },
|
|
4742
|
+
'WGSL': { ext: 'wgsl', usePreprocessor: true },
|
|
4743
|
+
'JSON': { ext: 'json', blockComments: false, singleLineComments: false },
|
|
4744
|
+
'XML': { ext: 'xml', tags: true },
|
|
4745
|
+
'Rust': { ext: 'rs' },
|
|
4746
|
+
'Python': { ext: 'py', singleLineCommentToken: '#' },
|
|
4747
|
+
'HTML': { ext: 'html', tags: true, singleLineComments: false, blockCommentsTokens: [ '<!--', '-->' ], numbers: false },
|
|
4748
|
+
'Batch': { ext: 'bat', blockComments: false, singleLineCommentToken: '::' },
|
|
4749
|
+
'Markdown': { ext: 'md', blockComments: false, singleLineCommentToken: '::', tags: true, numbers: false },
|
|
4750
|
+
'PHP': { ext: 'php' },
|
|
4751
|
+
};
|
|
4620
4752
|
|
|
4753
|
+
CodeEditor.keywords = {
|
|
4621
4754
|
'JavaScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'NaN', 'static', 'class', 'constructor', 'null', 'typeof', 'debugger', 'abstract',
|
|
4622
4755
|
'arguments', 'extends', 'instanceof', 'Infinity'],
|
|
4756
|
+
'TypeScript': ['var', 'let', 'const', 'this', 'in', 'of', 'true', 'false', 'new', 'function', 'class', 'extends', 'instanceof', 'Infinity', 'private', 'public', 'protected', 'interface',
|
|
4757
|
+
'enum', 'type'],
|
|
4623
4758
|
'C': ['int', 'float', 'double', 'long', 'short', 'char', 'const', 'void', 'true', 'false', 'auto', 'struct', 'typedef', 'signed', 'volatile', 'unsigned', 'static', 'extern', 'enum', 'register',
|
|
4624
4759
|
'union'],
|
|
4625
4760
|
'C++': ['int', 'float', 'double', 'bool', 'long', 'short', 'char', 'wchar_t', 'const', 'static_cast', 'dynamic_cast', 'new', 'delete', 'void', 'true', 'false', 'auto', 'class', 'struct', 'typedef', 'nullptr',
|
|
@@ -4630,10 +4765,10 @@ CodeEditor.keywords = {
|
|
|
4630
4765
|
'JSON': ['true', 'false'],
|
|
4631
4766
|
'GLSL': ['true', 'false', 'function', 'int', 'float', 'vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'struct'],
|
|
4632
4767
|
'CSS': ['body', 'html', 'canvas', 'div', 'input', 'span', '.'],
|
|
4633
|
-
'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
|
|
4768
|
+
'WGSL': ['var', 'let', 'true', 'false', 'fn', 'bool', 'u32', 'i32', 'f16', 'f32', 'vec2', 'vec3', 'vec4', 'vec2f', 'vec3f', 'vec4f', 'mat2x2f', 'mat3x3f', 'mat4x4f', 'array', 'atomic', 'struct',
|
|
4634
4769
|
'sampler', 'sampler_comparison', 'texture_depth_2d', 'texture_depth_2d_array', 'texture_depth_cube', 'texture_depth_cube_array', 'texture_depth_multisampled_2d',
|
|
4635
4770
|
'texture_external', 'texture_1d', 'texture_2d', 'texture_2d_array', 'texture_3d', 'texture_cube', 'texture_cube_array', 'texture_storage_1d', 'texture_storage_2d',
|
|
4636
|
-
'texture_storage_2d_array', 'texture_storage_3d', 'vec2u', 'vec3u', 'vec4u'],
|
|
4771
|
+
'texture_storage_2d_array', 'texture_storage_3d', 'vec2u', 'vec3u', 'vec4u', 'ptr'],
|
|
4637
4772
|
'Rust': ['as', 'const', 'crate', 'enum', 'extern', 'false', 'fn', 'impl', 'in', 'let', 'mod', 'move', 'mut', 'pub', 'ref', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true',
|
|
4638
4773
|
'type', 'unsafe', 'use', 'where', 'abstract', 'become', 'box', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual'],
|
|
4639
4774
|
'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
|
|
@@ -4641,10 +4776,10 @@ CodeEditor.keywords = {
|
|
|
4641
4776
|
'DRIVERQUERY', 'print', 'PRINT'],
|
|
4642
4777
|
'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
4643
4778
|
'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
|
|
4779
|
+
'PHP': ['const', 'function', 'array', 'new', 'int', 'string', '$this', 'public', 'null', 'private', 'protected', 'implements', 'class', 'use', 'namespace', 'abstract', 'clone', 'final'],
|
|
4644
4780
|
};
|
|
4645
4781
|
|
|
4646
4782
|
CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
|
|
4647
|
-
|
|
4648
4783
|
'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
|
|
4649
4784
|
'bind', 'prototype', 'length', 'assign', 'entries', 'values', 'concat', 'substring', 'substr', 'splice', 'slice', 'buffer', 'appendChild', 'createElement', 'prompt',
|
|
4650
4785
|
'alert'],
|
|
@@ -4656,42 +4791,45 @@ CodeEditor.utils = { // These ones don't have hightlight, used as suggestions to
|
|
|
4656
4791
|
};
|
|
4657
4792
|
|
|
4658
4793
|
CodeEditor.types = {
|
|
4659
|
-
|
|
4660
4794
|
'JavaScript': ['Object', 'String', 'Function', 'Boolean', 'Symbol', 'Error', 'Number', 'TextEncoder', 'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent',
|
|
4661
4795
|
'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element'],
|
|
4796
|
+
'TypeScript': ['arguments', 'constructor', 'null', 'typeof', 'debugger', 'abstract', 'Object', 'string', 'String', 'Function', 'Boolean', 'boolean', 'Error', 'Number', 'number', 'TextEncoder',
|
|
4797
|
+
'TextDecoder', 'Array', 'ArrayBuffer', 'InputEvent', 'MouseEvent', 'Int8Array', 'Int16Array', 'Int32Array', 'Float32Array', 'Float64Array', 'Element'],
|
|
4662
4798
|
'Rust': ['u128'],
|
|
4663
4799
|
'Python': ['int', 'type', 'float', 'map', 'list', 'ArithmeticError', 'AssertionError', 'AttributeError', 'Exception', 'EOFError', 'FloatingPointError', 'GeneratorExit',
|
|
4664
4800
|
'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError',
|
|
4665
4801
|
'OverflowError', 'ReferenceError', 'RuntimeError', 'StopIteration', 'SyntaxError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError',
|
|
4666
4802
|
'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError', 'UnicodeTranslateError', 'ValueError', 'ZeroDivisionError'],
|
|
4667
|
-
'C++': ['uint8_t', 'uint16_t', 'uint32_t']
|
|
4803
|
+
'C++': ['uint8_t', 'uint16_t', 'uint32_t'],
|
|
4804
|
+
'PHP': ['Exception', 'DateTime', 'JsonSerializable'],
|
|
4668
4805
|
};
|
|
4669
4806
|
|
|
4670
4807
|
CodeEditor.builtIn = {
|
|
4671
|
-
|
|
4672
4808
|
'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
|
|
4673
4809
|
'CSS': ['*', '!important'],
|
|
4674
4810
|
'C++': ['vector', 'list', 'map'],
|
|
4675
4811
|
'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
|
|
4676
4812
|
'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
|
|
4813
|
+
'PHP': ['echo', 'print'],
|
|
4677
4814
|
};
|
|
4678
4815
|
|
|
4679
4816
|
CodeEditor.statementsAndDeclarations = {
|
|
4680
|
-
|
|
4681
4817
|
'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
|
|
4818
|
+
'TypeScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await', 'as'],
|
|
4682
4819
|
'CSS': ['@', 'import'],
|
|
4683
4820
|
'C': ['for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'default', 'goto', 'do'],
|
|
4684
4821
|
'C++': ['std', 'for', 'if', 'else', 'return', 'continue', 'break', 'case', 'switch', 'while', 'using', 'glm', 'spdlog', 'default'],
|
|
4685
4822
|
'GLSL': ['for', 'if', 'else', 'return', 'continue', 'break'],
|
|
4686
|
-
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'read_write', 'uniform', 'function', 'workgroup'],
|
|
4823
|
+
'WGSL': ['const','for', 'if', 'else', 'return', 'continue', 'break', 'storage', 'read', 'read_write', 'uniform', 'function', 'workgroup', 'bitcast'],
|
|
4687
4824
|
'Rust': ['break', 'else', 'continue', 'for', 'if', 'loop', 'match', 'return', 'while', 'do', 'yield'],
|
|
4688
4825
|
'Python': ['if', 'raise', 'del', 'import', 'return', 'elif', 'try', 'else', 'while', 'as', 'except', 'with', 'assert', 'finally', 'yield', 'break', 'for', 'class', 'continue',
|
|
4689
4826
|
'global', 'pass', 'from'],
|
|
4690
|
-
'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT']
|
|
4827
|
+
'Batch': ['if', 'IF', 'for', 'FOR', 'in', 'IN', 'do', 'DO', 'call', 'CALL', 'goto', 'GOTO', 'exit', 'EXIT'],
|
|
4828
|
+
'PHP': ['declare', 'enddeclare', 'foreach', 'endforeach', 'if', 'else', 'elseif', 'endif', 'for', 'endfor', 'while', 'endwhile', 'switch', 'case', 'default', 'endswitch', 'return', 'break', 'continue',
|
|
4829
|
+
'try', 'catch', 'die', 'do', 'exit', 'finally'],
|
|
4691
4830
|
};
|
|
4692
4831
|
|
|
4693
4832
|
CodeEditor.symbols = {
|
|
4694
|
-
|
|
4695
4833
|
'JavaScript': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '??'],
|
|
4696
4834
|
'C': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '*', '-', '+'],
|
|
4697
4835
|
'C++': ['<', '>', '[', ']', '{', '}', '(', ')', ';', '=', '|', '||', '&', '&&', '?', '::', '*', '-', '+'],
|
|
@@ -4704,7 +4842,22 @@ CodeEditor.symbols = {
|
|
|
4704
4842
|
'Python': ['<', '>', '[', ']', '(', ')', '='],
|
|
4705
4843
|
'Batch': ['[', ']', '(', ')', '%'],
|
|
4706
4844
|
'HTML': ['<', '>', '/'],
|
|
4707
|
-
'XML': ['<', '>', '/']
|
|
4845
|
+
'XML': ['<', '>', '/'],
|
|
4846
|
+
'PHP': ['{', '}', '(', ')'],
|
|
4847
|
+
};
|
|
4848
|
+
|
|
4849
|
+
CodeEditor.REGISTER_LANGUAGE = function( name, options = {}, def, rules )
|
|
4850
|
+
{
|
|
4851
|
+
CodeEditor.languages[ name ] = options;
|
|
4852
|
+
|
|
4853
|
+
if( def?.keywords ) CodeEditor.keywords[ name ] = def.keywords.reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
4854
|
+
if( def?.utils ) CodeEditor.utils[ name ] = def.utils.reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
4855
|
+
if( def?.types ) CodeEditor.types[ name ] = def.types.reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
4856
|
+
if( def?.builtIn ) CodeEditor.builtIn[ name ] = def.builtIn.reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
4857
|
+
if( def?.statementsAndDeclarations ) CodeEditor.statementsAndDeclarations[ name ] = def.statementsAndDeclarations.reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
4858
|
+
if( def?.symbols ) CodeEditor.symbols[ name ] = def.symbols.reduce((a, v) => ({ ...a, [v]: true}), {});
|
|
4859
|
+
|
|
4860
|
+
if( rules ) HighlightRules[ name ] = rules;
|
|
4708
4861
|
};
|
|
4709
4862
|
|
|
4710
4863
|
LX.CodeEditor = CodeEditor;
|