lexgui 0.1.16 → 0.1.17
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 +754 -644
- package/build/lexgui.css +41 -42
- package/build/lexgui.js +1 -1
- package/build/lexgui.module.js +1 -1
- package/examples/code_editor.html +1 -1
- package/package.json +1 -1
|
@@ -13,23 +13,23 @@ function flushCss(element) {
|
|
|
13
13
|
element.offsetHeight;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function swapElements
|
|
16
|
+
function swapElements( obj, a, b ) {
|
|
17
17
|
[obj[a], obj[b]] = [obj[b], obj[a]];
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function swapArrayElements
|
|
20
|
+
function swapArrayElements( array, id0, id1 ) {
|
|
21
21
|
[array[id0], array[id1]] = [array[id1], array[id0]];
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
function sliceChar(str, idx) {
|
|
24
|
+
function sliceChar( str, idx ) {
|
|
25
25
|
return str.substr(0, idx) + str.substr(idx + 1);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function firstNonspaceIndex(str) {
|
|
28
|
+
function firstNonspaceIndex( str ) {
|
|
29
29
|
return str.search(/\S|$/);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function deleteElement(el) {
|
|
32
|
+
function deleteElement( el ) {
|
|
33
33
|
if(el) el.remove();
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -42,9 +42,9 @@ function doAsync( fn, ms ) {
|
|
|
42
42
|
fn();
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
class
|
|
45
|
+
class CodeSelection {
|
|
46
46
|
|
|
47
|
-
constructor(editor, ix, iy) {
|
|
47
|
+
constructor( editor, ix, iy ) {
|
|
48
48
|
|
|
49
49
|
this.editor = editor;
|
|
50
50
|
this.chars = 0;
|
|
@@ -66,25 +66,83 @@ class ISelection {
|
|
|
66
66
|
swapElements(this, 'fromY', 'toY');
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
selectInline(x, y, width) {
|
|
69
|
+
selectInline( x, y, width ) {
|
|
70
70
|
|
|
71
71
|
this.chars = width / this.editor.charWidth;
|
|
72
72
|
this.fromX = x;
|
|
73
73
|
this.toX = x + this.chars;
|
|
74
74
|
this.fromY = this.toY = y;
|
|
75
75
|
|
|
76
|
-
var domEl = document.createElement('div');
|
|
76
|
+
var domEl = document.createElement( 'div' );
|
|
77
77
|
domEl.className = "lexcodeselection";
|
|
78
78
|
|
|
79
|
-
domEl._top =
|
|
80
|
-
domEl.style.top =
|
|
79
|
+
domEl._top = y * this.editor.lineHeight;
|
|
80
|
+
domEl.style.top = domEl._top + "px";
|
|
81
81
|
domEl._left = x * this.editor.charWidth;
|
|
82
|
-
domEl.style.left = "calc(" +
|
|
82
|
+
domEl.style.left = "calc(" + domEl._left + "px + " + this.editor.xPadding + ")";
|
|
83
83
|
domEl.style.width = width + "px";
|
|
84
84
|
this.editor.selections.appendChild(domEl);
|
|
85
85
|
}
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
+
class ScrollBar {
|
|
89
|
+
|
|
90
|
+
static SCROLLBAR_VERTICAL = 1;
|
|
91
|
+
static SCROLLBAR_HORIZONTAL = 2;
|
|
92
|
+
|
|
93
|
+
constructor( editor, type ) {
|
|
94
|
+
|
|
95
|
+
this.editor = editor;
|
|
96
|
+
this.type = type;
|
|
97
|
+
|
|
98
|
+
this.root = document.createElement( 'div' );
|
|
99
|
+
this.root.className = "lexcodescrollbar";
|
|
100
|
+
if( type & ScrollBar.SCROLLBAR_HORIZONTAL )
|
|
101
|
+
this.root.classList.add( 'horizontal' );
|
|
102
|
+
|
|
103
|
+
this.thumb = document.createElement( 'div' );
|
|
104
|
+
this.thumb._top = 0;
|
|
105
|
+
this.thumb._left = 0;
|
|
106
|
+
this.root.appendChild( this.thumb );
|
|
107
|
+
|
|
108
|
+
this.thumb.addEventListener( "mousedown", inner_mousedown );
|
|
109
|
+
|
|
110
|
+
this.lastPosition = new LX.vec2( 0, 0 );
|
|
111
|
+
|
|
112
|
+
let that = this;
|
|
113
|
+
|
|
114
|
+
function inner_mousedown( e )
|
|
115
|
+
{
|
|
116
|
+
var doc = editor.root.ownerDocument;
|
|
117
|
+
doc.addEventListener( "mousemove",inner_mousemove );
|
|
118
|
+
doc.addEventListener( "mouseup",inner_mouseup );
|
|
119
|
+
that.lastPosition.set( e.x, e.y );
|
|
120
|
+
e.stopPropagation();
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function inner_mousemove( e )
|
|
125
|
+
{
|
|
126
|
+
var dt = that.lastPosition.sub( new LX.vec2( e.x, e.y ) );
|
|
127
|
+
if( that.type & ScrollBar.SCROLLBAR_VERTICAL )
|
|
128
|
+
editor.updateVerticalScrollFromScrollBar( dt.y )
|
|
129
|
+
else
|
|
130
|
+
editor.updateHorizontalScrollFromScrollBar( dt.x )
|
|
131
|
+
that.lastPosition.set( e.x, e.y );
|
|
132
|
+
e.stopPropagation();
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function inner_mouseup( e )
|
|
137
|
+
{
|
|
138
|
+
var doc = editor.root.ownerDocument;
|
|
139
|
+
doc.removeEventListener( "mousemove", inner_mousemove );
|
|
140
|
+
doc.removeEventListener( "mouseup", inner_mouseup );
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
}
|
|
145
|
+
|
|
88
146
|
/**
|
|
89
147
|
* @class CodeEditor
|
|
90
148
|
*/
|
|
@@ -96,6 +154,9 @@ class CodeEditor {
|
|
|
96
154
|
static CURSOR_LEFT = 1;
|
|
97
155
|
static CURSOR_TOP = 2;
|
|
98
156
|
|
|
157
|
+
static KEEP_VISIBLE_LINES = 1;
|
|
158
|
+
static UPDATE_VISIBLE_LINES = 2;
|
|
159
|
+
|
|
99
160
|
static WORD_TYPE_METHOD = 0;
|
|
100
161
|
static WORD_TYPE_CLASS = 1;
|
|
101
162
|
|
|
@@ -106,35 +167,14 @@ class CodeEditor {
|
|
|
106
167
|
|
|
107
168
|
constructor( area, options = {} ) {
|
|
108
169
|
|
|
109
|
-
// var a = [];
|
|
110
|
-
|
|
111
|
-
// var map = {};
|
|
112
|
-
|
|
113
|
-
// for( var i = 0; i < 1000000; ++i )
|
|
114
|
-
// map[i] = 1;
|
|
115
|
-
|
|
116
|
-
// const start = performance.now();
|
|
117
|
-
|
|
118
|
-
// for( var i = 0; i < 3000; ++i ) {
|
|
119
|
-
// const b = map[Math.floor( Math.random() * 1000000 )];
|
|
120
|
-
// }
|
|
121
|
-
|
|
122
|
-
// console.log( performance.now() - start );
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// debugger;
|
|
126
|
-
|
|
127
|
-
|
|
128
170
|
window.editor = this;
|
|
129
171
|
|
|
130
172
|
CodeEditor.__instances.push( this );
|
|
131
173
|
|
|
132
|
-
var that = this;
|
|
133
|
-
|
|
134
174
|
this.base_area = area;
|
|
135
175
|
this.area = new LX.Area( { className: "lexcodeeditor", height: "auto", no_append: true } );
|
|
136
176
|
|
|
137
|
-
this.tabs = this.area.addTabs( { onclose: (name) => delete this.openedTabs[name] } );
|
|
177
|
+
this.tabs = this.area.addTabs( { onclose: (name) => delete this.openedTabs[ name ] } );
|
|
138
178
|
this.tabs.root.addEventListener( 'dblclick', (e) => {
|
|
139
179
|
if( options.allow_add_scripts ?? true ) {
|
|
140
180
|
e.preventDefault();
|
|
@@ -142,10 +182,11 @@ class CodeEditor {
|
|
|
142
182
|
}
|
|
143
183
|
} );
|
|
144
184
|
|
|
185
|
+
// Full editor
|
|
145
186
|
area.root.classList.add('codebasearea');
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
area.
|
|
187
|
+
|
|
188
|
+
// Code area
|
|
189
|
+
this.tabs.area.root.classList.add( 'codetabsarea' );
|
|
149
190
|
|
|
150
191
|
this.root = this.area.root;
|
|
151
192
|
this.root.tabIndex = -1;
|
|
@@ -156,9 +197,9 @@ class CodeEditor {
|
|
|
156
197
|
|
|
157
198
|
if( !this.disableEdition )
|
|
158
199
|
{
|
|
159
|
-
this.root.addEventListener( 'keydown', this.processKey.bind(this), true);
|
|
160
|
-
this.root.addEventListener( 'focus', this.processFocus.bind(this, true) );
|
|
161
|
-
this.root.addEventListener( 'focusout', this.processFocus.bind(this, false) );
|
|
200
|
+
this.root.addEventListener( 'keydown', this.processKey.bind( this), true );
|
|
201
|
+
this.root.addEventListener( 'focus', this.processFocus.bind( this, true ) );
|
|
202
|
+
this.root.addEventListener( 'focusout', this.processFocus.bind( this, false ) );
|
|
162
203
|
}
|
|
163
204
|
|
|
164
205
|
this.root.addEventListener( 'mousedown', this.processMouse.bind(this) );
|
|
@@ -167,130 +208,119 @@ class CodeEditor {
|
|
|
167
208
|
this.root.addEventListener( 'click', this.processMouse.bind(this) );
|
|
168
209
|
this.root.addEventListener( 'contextmenu', this.processMouse.bind(this) );
|
|
169
210
|
|
|
170
|
-
// Take into account the scrollbar..
|
|
171
|
-
this.tabs.area.root.classList.add( 'codetabsarea' );
|
|
172
|
-
|
|
173
211
|
// Cursors and selection
|
|
174
212
|
|
|
175
|
-
this.cursors = document.createElement('div');
|
|
213
|
+
this.cursors = document.createElement( 'div' );
|
|
176
214
|
this.cursors.className = 'cursors';
|
|
177
|
-
this.tabs.area.attach(this.cursors);
|
|
215
|
+
this.tabs.area.attach( this.cursors );
|
|
178
216
|
|
|
179
|
-
this.selections = document.createElement('div');
|
|
217
|
+
this.selections = document.createElement( 'div' );
|
|
180
218
|
this.selections.className = 'selections';
|
|
181
|
-
this.tabs.area.attach(this.selections);
|
|
219
|
+
this.tabs.area.attach( this.selections );
|
|
182
220
|
|
|
183
221
|
// Css char synchronization
|
|
184
|
-
this.xPadding = "
|
|
222
|
+
this.xPadding = "48px";
|
|
185
223
|
|
|
186
224
|
// Add main cursor
|
|
187
225
|
{
|
|
188
|
-
var cursor = document.createElement('div');
|
|
226
|
+
var cursor = document.createElement( 'div' );
|
|
189
227
|
cursor.className = "cursor";
|
|
190
228
|
cursor.innerHTML = " ";
|
|
191
229
|
cursor._left = 0;
|
|
192
230
|
cursor.style.left = this.xPadding;
|
|
193
|
-
cursor._top =
|
|
194
|
-
cursor.style.top = "
|
|
231
|
+
cursor._top = 0;
|
|
232
|
+
cursor.style.top = cursor._top + "px";
|
|
195
233
|
cursor.position = 0;
|
|
196
|
-
cursor.
|
|
197
|
-
cursor.print = (function() { console.log( this.line, this.position ) }).bind(cursor);
|
|
198
|
-
|
|
234
|
+
cursor._line = 0;
|
|
235
|
+
cursor.print = (function() { console.log( this.line, this.position ) }).bind( cursor );
|
|
236
|
+
|
|
237
|
+
Object.defineProperty( cursor, 'line', {
|
|
238
|
+
get: (v) => { return this._line },
|
|
239
|
+
set: (v) => { this._line = v; }
|
|
240
|
+
} );
|
|
241
|
+
|
|
242
|
+
this.cursors.appendChild( cursor );
|
|
199
243
|
}
|
|
200
244
|
|
|
201
|
-
//
|
|
245
|
+
// Scroll stuff
|
|
202
246
|
{
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
this.
|
|
206
|
-
|
|
247
|
+
this.codeScroller = this.tabs.area.root;
|
|
248
|
+
this.viewportRangeStart = 0;
|
|
249
|
+
this.lineScrollMargin = new LX.vec2( 20, 20 ); // [ mUp, mDown ]
|
|
250
|
+
window.scroller = this.codeScroller;
|
|
207
251
|
|
|
208
|
-
|
|
209
|
-
this.
|
|
210
|
-
this.scrollbarThumb._top = 0;
|
|
211
|
-
scrollbar.appendChild(scrollbarThumb);
|
|
252
|
+
let lastScrollTopValue = -1;
|
|
253
|
+
this.codeScroller.addEventListener( 'scroll', (e) => {
|
|
212
254
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
{
|
|
219
|
-
var doc = that.root.ownerDocument;
|
|
220
|
-
doc.addEventListener("mousemove",inner_mousemove);
|
|
221
|
-
doc.addEventListener("mouseup",inner_mouseup);
|
|
222
|
-
last_pos = e.y;
|
|
223
|
-
e.stopPropagation();
|
|
224
|
-
e.preventDefault();
|
|
225
|
-
}
|
|
255
|
+
if( this._discardScroll )
|
|
256
|
+
{
|
|
257
|
+
this._discardScroll = false;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
226
260
|
|
|
227
|
-
|
|
228
|
-
{
|
|
229
|
-
var dt = (last_pos - e.y);
|
|
230
|
-
|
|
231
|
-
that.applyVerticalScrollFromScrollBar( that.scrollbarThumb._top - dt )
|
|
261
|
+
this.setScrollBarValue( 'vertical' );
|
|
232
262
|
|
|
233
|
-
|
|
234
|
-
e.stopPropagation();
|
|
235
|
-
e.preventDefault();
|
|
236
|
-
}
|
|
263
|
+
const scrollTop = this.getScrollTop();
|
|
237
264
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
doc.removeEventListener("mouseup", inner_mouseup);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
265
|
+
// Scroll down...
|
|
266
|
+
if( scrollTop > lastScrollTopValue )
|
|
267
|
+
{
|
|
268
|
+
const scrollDownBoundary = (this.viewportRangeStart + this.lineScrollMargin.y) * this.lineHeight;
|
|
245
269
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
270
|
+
if( scrollTop > scrollDownBoundary )
|
|
271
|
+
{
|
|
272
|
+
this.code.style.top = (scrollDownBoundary - this.lineScrollMargin.x * this.lineHeight) + "px";
|
|
273
|
+
this.processLines( CodeEditor.UPDATE_VISIBLE_LINES );
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Scroll up...
|
|
277
|
+
else
|
|
278
|
+
{
|
|
279
|
+
const scrollUpBoundary = (this.viewportRangeStart + this.lineScrollMargin.x) * this.lineHeight;
|
|
280
|
+
// console.log(scrollTop, scrollUpBoundary)
|
|
281
|
+
|
|
282
|
+
if( scrollTop <= scrollUpBoundary )
|
|
283
|
+
{
|
|
284
|
+
this.viewportRangeStart -= this.lineScrollMargin.x;
|
|
285
|
+
this.viewportRangeStart = Math.max( this.viewportRangeStart, 0 );
|
|
252
286
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
287
|
+
this.code.style.top = this.viewportRangeStart == 0 ? "0px" : (scrollUpBoundary - this.lineScrollMargin.x * this.lineHeight) + "px";
|
|
288
|
+
|
|
289
|
+
this.processLines( CodeEditor.KEEP_VISIBLE_LINES );
|
|
290
|
+
}
|
|
291
|
+
}
|
|
257
292
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
var last_pos = 0;
|
|
261
|
-
|
|
262
|
-
function inner_mousedown(e)
|
|
263
|
-
{
|
|
264
|
-
var doc = that.root.ownerDocument;
|
|
265
|
-
doc.addEventListener("mousemove",inner_mousemove);
|
|
266
|
-
doc.addEventListener("mouseup",inner_mouseup);
|
|
267
|
-
last_pos = e.x;
|
|
268
|
-
e.stopPropagation();
|
|
269
|
-
e.preventDefault();
|
|
270
|
-
}
|
|
293
|
+
lastScrollTopValue = scrollTop;
|
|
294
|
+
});
|
|
271
295
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
296
|
+
this.codeScroller.addEventListener( 'wheel', (e) => {
|
|
297
|
+
const dX = (e.deltaY > 0.0 ? 10.0 : -10.0) * ( e.shiftKey ? 1.0 : 0.0 );
|
|
298
|
+
if( dX != 0.0 ) this.setScrollBarValue( 'horizontal', dX );
|
|
299
|
+
});
|
|
300
|
+
}
|
|
277
301
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
302
|
+
// This is only the container, line numbers are in the same line div
|
|
303
|
+
{
|
|
304
|
+
this.gutter = document.createElement( 'div' );
|
|
305
|
+
this.gutter.className = "lexcodegutter";
|
|
306
|
+
area.attach( this.gutter );
|
|
307
|
+
}
|
|
282
308
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
309
|
+
// Add custom vertical scroll bar
|
|
310
|
+
{
|
|
311
|
+
this.vScrollbar = new ScrollBar( this, ScrollBar.SCROLLBAR_VERTICAL );
|
|
312
|
+
area.attach(this.vScrollbar.root);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Add custom horizontal scroll bar
|
|
316
|
+
{
|
|
317
|
+
this.hScrollbar = new ScrollBar( this, ScrollBar.SCROLLBAR_HORIZONTAL );
|
|
318
|
+
area.attach(this.hScrollbar.root);
|
|
289
319
|
}
|
|
290
320
|
|
|
291
321
|
// Add autocomplete box
|
|
292
322
|
{
|
|
293
|
-
var box = document.createElement('div');
|
|
323
|
+
var box = document.createElement( 'div' );
|
|
294
324
|
box.className = "autocomplete";
|
|
295
325
|
this.autocomplete = box;
|
|
296
326
|
this.tabs.area.attach(box);
|
|
@@ -298,10 +328,21 @@ class CodeEditor {
|
|
|
298
328
|
this.isAutoCompleteActive = false;
|
|
299
329
|
}
|
|
300
330
|
|
|
331
|
+
// Add code-sizer
|
|
332
|
+
{
|
|
333
|
+
this.codeSizer = document.createElement( 'div' );
|
|
334
|
+
this.codeSizer.className = "code-sizer";
|
|
335
|
+
|
|
336
|
+
// Append all childs
|
|
337
|
+
while( this.codeScroller.firstChild )
|
|
338
|
+
this.codeSizer.appendChild( this.codeScroller.firstChild );
|
|
339
|
+
|
|
340
|
+
this.codeScroller.appendChild( this.codeSizer );
|
|
341
|
+
}
|
|
342
|
+
|
|
301
343
|
// State
|
|
302
344
|
|
|
303
345
|
this.state = {
|
|
304
|
-
overwrite: false,
|
|
305
346
|
focused: false,
|
|
306
347
|
selectingText: false
|
|
307
348
|
}
|
|
@@ -335,7 +376,7 @@ class CodeEditor {
|
|
|
335
376
|
};
|
|
336
377
|
|
|
337
378
|
// Scan tokens..
|
|
338
|
-
setInterval( this.scanWordSuggestions.bind(this), 2000 );
|
|
379
|
+
// setInterval( this.scanWordSuggestions.bind( this ), 2000 );
|
|
339
380
|
|
|
340
381
|
this.languages = {
|
|
341
382
|
'Plain Text': { },
|
|
@@ -426,64 +467,67 @@ class CodeEditor {
|
|
|
426
467
|
|
|
427
468
|
// Action keys
|
|
428
469
|
|
|
429
|
-
this.action('Escape', false, ( ln, cursor, e ) => {
|
|
470
|
+
this.action( 'Escape', false, ( ln, cursor, e ) => {
|
|
430
471
|
this.hideAutoCompleteBox();
|
|
431
472
|
});
|
|
432
473
|
|
|
433
|
-
this.action('Backspace', false, ( ln, cursor, e ) => {
|
|
474
|
+
this.action( 'Backspace', false, ( ln, cursor, e ) => {
|
|
434
475
|
|
|
435
|
-
this._addUndoStep(cursor);
|
|
476
|
+
this._addUndoStep( cursor );
|
|
436
477
|
|
|
437
|
-
if(this.selection) {
|
|
438
|
-
this.deleteSelection(cursor);
|
|
478
|
+
if( this.selection ) {
|
|
479
|
+
this.deleteSelection( cursor );
|
|
439
480
|
// Remove entire line when selecting with triple click
|
|
440
|
-
if(this.code.lines[ln] && !this.code.lines[ln].length)
|
|
441
|
-
|
|
481
|
+
if(this.code.lines[ ln ] != undefined && !this.code.lines[ ln ].length)
|
|
482
|
+
{
|
|
483
|
+
this.actions['Backspace'].callback( ln, cursor, e );
|
|
484
|
+
this.lineDown( cursor, true );
|
|
485
|
+
}
|
|
442
486
|
}
|
|
443
487
|
else {
|
|
444
488
|
var letter = this.getCharAtPos( cursor, -1 );
|
|
445
|
-
if(letter) {
|
|
446
|
-
this.code.lines[ln] = sliceChar( this.code.lines[ln], cursor.position - 1 );
|
|
489
|
+
if( letter ) {
|
|
490
|
+
this.code.lines[ ln ] = sliceChar( this.code.lines[ ln ], cursor.position - 1 );
|
|
447
491
|
this.cursorToLeft( letter );
|
|
448
|
-
this.processLine(ln);
|
|
492
|
+
this.processLine( ln );
|
|
449
493
|
if( this.useAutoComplete )
|
|
450
494
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
451
495
|
}
|
|
452
|
-
else if(this.code.lines[ln - 1] != undefined) {
|
|
496
|
+
else if( this.code.lines[ ln - 1 ] != undefined ) {
|
|
453
497
|
this.lineUp();
|
|
454
|
-
this.actions['End'].callback(cursor.line, cursor, e);
|
|
498
|
+
this.actions[ 'End' ].callback( cursor.line, cursor, e );
|
|
455
499
|
// Move line on top
|
|
456
|
-
this.code.lines[ln - 1] += this.code.lines[ln];
|
|
457
|
-
this.code.lines.splice(ln, 1);
|
|
458
|
-
this.processLines(
|
|
500
|
+
this.code.lines[ ln - 1 ] += this.code.lines[ ln ];
|
|
501
|
+
this.code.lines.splice( ln, 1 );
|
|
502
|
+
this.processLines();
|
|
459
503
|
}
|
|
460
504
|
}
|
|
461
505
|
});
|
|
462
506
|
|
|
463
|
-
this.action('Delete', false, ( ln, cursor, e ) => {
|
|
507
|
+
this.action( 'Delete', false, ( ln, cursor, e ) => {
|
|
464
508
|
|
|
465
509
|
this._addUndoStep( cursor );
|
|
466
510
|
|
|
467
511
|
if(this.selection) {
|
|
468
512
|
// Use 'Backspace' as it's the same callback...
|
|
469
|
-
this.actions['Backspace'].callback(ln, cursor, e);
|
|
513
|
+
this.actions['Backspace'].callback( ln, cursor, e );
|
|
470
514
|
}
|
|
471
515
|
else
|
|
472
516
|
{
|
|
473
517
|
var letter = this.getCharAtPos( cursor );
|
|
474
|
-
if(letter) {
|
|
475
|
-
this.code.lines[ln] = sliceChar( this.code.lines[ln], cursor.position );
|
|
476
|
-
this.processLine(ln);
|
|
518
|
+
if( letter ) {
|
|
519
|
+
this.code.lines[ ln ] = sliceChar( this.code.lines[ ln ], cursor.position );
|
|
520
|
+
this.processLine( ln );
|
|
477
521
|
}
|
|
478
|
-
else if(this.code.lines[ln + 1] != undefined) {
|
|
479
|
-
this.code.lines[ln] += this.code.lines[ln + 1];
|
|
480
|
-
this.code.lines.splice(ln + 1, 1);
|
|
481
|
-
this.processLines(
|
|
522
|
+
else if(this.code.lines[ ln + 1 ] != undefined) {
|
|
523
|
+
this.code.lines[ ln ] += this.code.lines[ ln + 1 ];
|
|
524
|
+
this.code.lines.splice( ln + 1, 1 );
|
|
525
|
+
this.processLines();
|
|
482
526
|
}
|
|
483
527
|
}
|
|
484
528
|
});
|
|
485
529
|
|
|
486
|
-
this.action('Tab', true, ( ln, cursor, e ) => {
|
|
530
|
+
this.action( 'Tab', true, ( ln, cursor, e ) => {
|
|
487
531
|
|
|
488
532
|
if( this.isAutoCompleteActive )
|
|
489
533
|
{
|
|
@@ -494,53 +538,53 @@ class CodeEditor {
|
|
|
494
538
|
}
|
|
495
539
|
});
|
|
496
540
|
|
|
497
|
-
this.action('Home', false, ( ln, cursor, e ) => {
|
|
541
|
+
this.action( 'Home', false, ( ln, cursor, e ) => {
|
|
498
542
|
|
|
499
|
-
let idx = firstNonspaceIndex(this.code.lines[ln]);
|
|
543
|
+
let idx = firstNonspaceIndex( this.code.lines[ ln ] );
|
|
500
544
|
|
|
501
545
|
// We already are in the first non space index...
|
|
502
546
|
if(idx == cursor.position) idx = 0;
|
|
503
547
|
|
|
504
|
-
const prestring = this.code.lines[ln].substring(0, idx);
|
|
505
|
-
let
|
|
548
|
+
const prestring = this.code.lines[ ln ].substring( 0, idx );
|
|
549
|
+
let lastX = cursor.position;
|
|
506
550
|
|
|
507
551
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
508
|
-
if(idx > 0) this.cursorToString(cursor, prestring);
|
|
509
|
-
this._refreshCodeInfo(cursor.line, cursor.position);
|
|
552
|
+
if(idx > 0) this.cursorToString( cursor, prestring );
|
|
553
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
510
554
|
this.setScrollLeft( 0 );
|
|
511
555
|
|
|
512
556
|
if( e.shiftKey && !e.cancelShift )
|
|
513
557
|
{
|
|
514
558
|
// Get last selection range
|
|
515
|
-
if(this.selection)
|
|
516
|
-
|
|
559
|
+
if( this.selection )
|
|
560
|
+
lastX += this.selection.chars;
|
|
517
561
|
|
|
518
|
-
this.startSelection(cursor);
|
|
519
|
-
var string = this.code.lines[ln].substring(idx,
|
|
520
|
-
this.selection.selectInline(idx, cursor.line, this.measureString(string));
|
|
562
|
+
this.startSelection( cursor );
|
|
563
|
+
var string = this.code.lines[ ln ].substring( idx, lastX );
|
|
564
|
+
this.selection.selectInline( idx, cursor.line, this.measureString( string ) );
|
|
521
565
|
} else if( !e.keepSelection )
|
|
522
566
|
this.endSelection();
|
|
523
567
|
});
|
|
524
568
|
|
|
525
|
-
this.action('End', false, ( ln, cursor, e ) => {
|
|
569
|
+
this.action( 'End', false, ( ln, cursor, e ) => {
|
|
526
570
|
|
|
527
571
|
if( e.shiftKey || e._shiftKey ) {
|
|
528
572
|
|
|
529
|
-
var string = this.code.lines[ln].substring(cursor.position);
|
|
530
|
-
if(!this.selection)
|
|
531
|
-
this.startSelection(cursor);
|
|
573
|
+
var string = this.code.lines[ ln ].substring(cursor.position);
|
|
574
|
+
if( !this.selection )
|
|
575
|
+
this.startSelection( cursor );
|
|
532
576
|
this.selection.selectInline(cursor.position, cursor.line, this.measureString(string));
|
|
533
577
|
} else
|
|
534
578
|
this.endSelection();
|
|
535
579
|
|
|
536
580
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
537
|
-
this.cursorToString( cursor, this.code.lines[ln] );
|
|
581
|
+
this.cursorToString( cursor, this.code.lines[ ln ] );
|
|
538
582
|
|
|
539
583
|
const last_char = (this.code.clientWidth / this.charWidth)|0;
|
|
540
584
|
this.setScrollLeft( cursor.position >= last_char ? (cursor.position - last_char) * this.charWidth : 0 );
|
|
541
585
|
});
|
|
542
586
|
|
|
543
|
-
this.action('Enter', true, ( ln, cursor, e ) => {
|
|
587
|
+
this.action( 'Enter', true, ( ln, cursor, e ) => {
|
|
544
588
|
|
|
545
589
|
// Add word
|
|
546
590
|
if( this.isAutoCompleteActive )
|
|
@@ -549,63 +593,63 @@ class CodeEditor {
|
|
|
549
593
|
return;
|
|
550
594
|
}
|
|
551
595
|
|
|
552
|
-
if(e.ctrlKey)
|
|
596
|
+
if( e.ctrlKey )
|
|
553
597
|
{
|
|
554
598
|
this.onrun( this.getText() );
|
|
555
599
|
return;
|
|
556
600
|
}
|
|
557
601
|
|
|
558
|
-
this._addUndoStep(cursor);
|
|
602
|
+
this._addUndoStep( cursor );
|
|
559
603
|
|
|
560
604
|
var _c0 = this.getCharAtPos( cursor, -1 );
|
|
561
605
|
var _c1 = this.getCharAtPos( cursor );
|
|
562
606
|
|
|
563
|
-
this.code.lines.splice(cursor.line + 1, 0, "");
|
|
564
|
-
this.code.lines[cursor.line + 1] = this.code.lines[ln].substr( cursor.position ); // new line (below)
|
|
565
|
-
this.code.lines[ln] = this.code.lines[ln].substr( 0, cursor.position ); // line above
|
|
566
|
-
this.lineDown(cursor, true);
|
|
607
|
+
this.code.lines.splice( cursor.line + 1, 0, "" );
|
|
608
|
+
this.code.lines[cursor.line + 1] = this.code.lines[ ln ].substr( cursor.position ); // new line (below)
|
|
609
|
+
this.code.lines[ ln ] = this.code.lines[ ln ].substr( 0, cursor.position ); // line above
|
|
610
|
+
this.lineDown( cursor, true );
|
|
567
611
|
|
|
568
612
|
// Check indentation
|
|
569
|
-
var spaces = firstNonspaceIndex(this.code.lines[ln]);
|
|
613
|
+
var spaces = firstNonspaceIndex( this.code.lines[ ln ]);
|
|
570
614
|
var tabs = Math.floor( spaces / this.tabSpaces );
|
|
571
615
|
|
|
572
616
|
if( _c0 == '{' && _c1 == '}' ) {
|
|
573
|
-
this.code.lines.splice(cursor.line, 0, "");
|
|
574
|
-
this.addSpaceTabs(tabs + 1);
|
|
575
|
-
this.code.lines[cursor.line + 1] = " ".repeat(spaces) + this.code.lines[cursor.line + 1];
|
|
617
|
+
this.code.lines.splice( cursor.line, 0, "" );
|
|
618
|
+
this.addSpaceTabs( tabs + 1 );
|
|
619
|
+
this.code.lines[ cursor.line + 1 ] = " ".repeat(spaces) + this.code.lines[ cursor.line + 1 ];
|
|
576
620
|
} else {
|
|
577
|
-
this.addSpaceTabs(tabs);
|
|
621
|
+
this.addSpaceTabs( tabs );
|
|
578
622
|
}
|
|
579
623
|
|
|
580
|
-
this.processLines(
|
|
624
|
+
this.processLines();
|
|
581
625
|
});
|
|
582
626
|
|
|
583
|
-
this.action('ArrowUp', false, ( ln, cursor, e ) => {
|
|
627
|
+
this.action( 'ArrowUp', false, ( ln, cursor, e ) => {
|
|
584
628
|
|
|
585
629
|
// Move cursor..
|
|
586
630
|
if( !this.isAutoCompleteActive )
|
|
587
631
|
{
|
|
588
632
|
if( e.shiftKey ) {
|
|
589
|
-
if(!this.selection)
|
|
590
|
-
this.startSelection(cursor);
|
|
633
|
+
if( !this.selection )
|
|
634
|
+
this.startSelection( cursor );
|
|
591
635
|
|
|
592
|
-
this.selection.toY = (this.selection.toY > 0) ? (this.selection.toY - 1) : 0;
|
|
593
|
-
this.cursorToLine(cursor, this.selection.toY);
|
|
636
|
+
this.selection.toY = ( this.selection.toY > 0 ) ? ( this.selection.toY - 1 ) : 0;
|
|
637
|
+
this.cursorToLine( cursor, this.selection.toY );
|
|
594
638
|
|
|
595
639
|
var letter = this.getCharAtPos( cursor );
|
|
596
|
-
if(!letter) {
|
|
597
|
-
this.selection.toX = this.code.lines[cursor.line].length;
|
|
598
|
-
this.cursorToPosition(cursor, this.selection.toX);
|
|
640
|
+
if( !letter ) {
|
|
641
|
+
this.selection.toX = this.code.lines[ cursor.line ].length;
|
|
642
|
+
this.cursorToPosition( cursor, this.selection.toX );
|
|
599
643
|
}
|
|
600
644
|
|
|
601
|
-
this.processSelection(null, true);
|
|
645
|
+
this.processSelection( null, true );
|
|
602
646
|
|
|
603
647
|
} else {
|
|
604
648
|
this.endSelection();
|
|
605
649
|
this.lineUp();
|
|
606
650
|
// Go to end of line if out of line
|
|
607
651
|
var letter = this.getCharAtPos( cursor );
|
|
608
|
-
if(!letter) this.actions['End'].callback(cursor.line, cursor, e);
|
|
652
|
+
if( !letter ) this.actions['End'].callback( cursor.line, cursor, e );
|
|
609
653
|
}
|
|
610
654
|
}
|
|
611
655
|
// Move up autocomplete selection
|
|
@@ -615,34 +659,34 @@ class CodeEditor {
|
|
|
615
659
|
}
|
|
616
660
|
});
|
|
617
661
|
|
|
618
|
-
this.action('ArrowDown', false, ( ln, cursor, e ) => {
|
|
662
|
+
this.action( 'ArrowDown', false, ( ln, cursor, e ) => {
|
|
619
663
|
|
|
620
664
|
// Move cursor..
|
|
621
665
|
if( !this.isAutoCompleteActive )
|
|
622
666
|
{
|
|
623
667
|
if( e.shiftKey ) {
|
|
624
|
-
if(!this.selection)
|
|
625
|
-
this.startSelection(cursor);
|
|
668
|
+
if( !this.selection )
|
|
669
|
+
this.startSelection( cursor );
|
|
626
670
|
|
|
627
671
|
this.selection.toY = this.selection.toY < this.code.lines.length - 1 ? this.selection.toY + 1 : this.code.lines.length - 1;
|
|
628
|
-
this.cursorToLine(cursor, this.selection.toY);
|
|
672
|
+
this.cursorToLine( cursor, this.selection.toY );
|
|
629
673
|
|
|
630
674
|
var letter = this.getCharAtPos( cursor );
|
|
631
|
-
if(!letter) {
|
|
632
|
-
this.selection.toX = Math.max(this.code.lines[cursor.line].length - 1, 0);
|
|
675
|
+
if( !letter ) {
|
|
676
|
+
this.selection.toX = Math.max(this.code.lines[ cursor.line ].length - 1, 0);
|
|
633
677
|
this.cursorToPosition(cursor, this.selection.toX);
|
|
634
678
|
}
|
|
635
679
|
|
|
636
|
-
this.processSelection(null, true);
|
|
680
|
+
this.processSelection( null, true );
|
|
637
681
|
} else {
|
|
638
682
|
|
|
639
683
|
if( this.code.lines[ ln + 1 ] == undefined )
|
|
640
684
|
return;
|
|
641
685
|
this.endSelection();
|
|
642
|
-
this.lineDown();
|
|
686
|
+
this.lineDown( cursor );
|
|
643
687
|
// Go to end of line if out of line
|
|
644
688
|
var letter = this.getCharAtPos( cursor );
|
|
645
|
-
if(!letter) this.actions['End'].callback(cursor.line, cursor, e);
|
|
689
|
+
if( !letter ) this.actions['End'].callback(cursor.line, cursor, e);
|
|
646
690
|
}
|
|
647
691
|
}
|
|
648
692
|
// Move down autocomplete selection
|
|
@@ -652,40 +696,40 @@ class CodeEditor {
|
|
|
652
696
|
}
|
|
653
697
|
});
|
|
654
698
|
|
|
655
|
-
this.action('ArrowLeft', false, ( ln, cursor, e ) => {
|
|
699
|
+
this.action( 'ArrowLeft', false, ( ln, cursor, e ) => {
|
|
656
700
|
|
|
657
|
-
if(e.metaKey) { // Apple devices (Command)
|
|
701
|
+
if( e.metaKey ) { // Apple devices (Command)
|
|
658
702
|
e.preventDefault();
|
|
659
703
|
this.actions[ 'Home' ].callback( ln, cursor, e );
|
|
660
704
|
}
|
|
661
|
-
else if(e.ctrlKey) {
|
|
705
|
+
else if( e.ctrlKey ) {
|
|
662
706
|
// Get next word
|
|
663
707
|
const [word, from, to] = this.getWordAtPos( cursor, -1 );
|
|
664
708
|
var diff = Math.max(cursor.position - from, 1);
|
|
665
709
|
var substr = word.substr(0, diff);
|
|
666
710
|
// Selections...
|
|
667
|
-
if( e.shiftKey ) if(!this.selection) this.startSelection(cursor);
|
|
711
|
+
if( e.shiftKey ) if( !this.selection ) this.startSelection( cursor );
|
|
668
712
|
this.cursorToString(cursor, substr, true);
|
|
669
713
|
if( e.shiftKey ) this.processSelection();
|
|
670
714
|
}
|
|
671
715
|
else {
|
|
672
716
|
var letter = this.getCharAtPos( cursor, -1 );
|
|
673
|
-
if(letter) {
|
|
717
|
+
if( letter ) {
|
|
674
718
|
if( e.shiftKey ) {
|
|
675
|
-
if(!this.selection) this.startSelection(cursor);
|
|
676
|
-
if( ((cursor.position - 1) < this.selection.fromX) && this.selection.sameLine() )
|
|
719
|
+
if( !this.selection ) this.startSelection( cursor );
|
|
720
|
+
if( ( ( cursor.position - 1 ) < this.selection.fromX ) && this.selection.sameLine() )
|
|
677
721
|
this.selection.fromX--;
|
|
678
|
-
else if( (cursor.position - 1) == this.selection.fromX && this.selection.sameLine() ) {
|
|
722
|
+
else if( ( cursor.position - 1 ) == this.selection.fromX && this.selection.sameLine() ) {
|
|
679
723
|
this.cursorToLeft( letter, cursor );
|
|
680
724
|
this.endSelection();
|
|
681
725
|
return;
|
|
682
726
|
}
|
|
683
727
|
else this.selection.toX--;
|
|
684
728
|
this.cursorToLeft( letter, cursor );
|
|
685
|
-
this.processSelection(null, true);
|
|
729
|
+
this.processSelection( null, true );
|
|
686
730
|
}
|
|
687
731
|
else {
|
|
688
|
-
if(!this.selection) {
|
|
732
|
+
if( !this.selection ) {
|
|
689
733
|
this.cursorToLeft( letter, cursor );
|
|
690
734
|
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
691
735
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
@@ -693,8 +737,8 @@ class CodeEditor {
|
|
|
693
737
|
else {
|
|
694
738
|
this.selection.invertIfNecessary();
|
|
695
739
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
|
|
696
|
-
this.cursorToLine(cursor, this.selection.fromY, true);
|
|
697
|
-
this.cursorToPosition(cursor, this.selection.fromX);
|
|
740
|
+
this.cursorToLine( cursor, this.selection.fromY, true );
|
|
741
|
+
this.cursorToPosition( cursor, this.selection.fromX );
|
|
698
742
|
this.endSelection();
|
|
699
743
|
}
|
|
700
744
|
}
|
|
@@ -702,7 +746,7 @@ class CodeEditor {
|
|
|
702
746
|
else if( cursor.line > 0 ) {
|
|
703
747
|
|
|
704
748
|
if( e.shiftKey ) {
|
|
705
|
-
if(!this.selection) this.startSelection(cursor);
|
|
749
|
+
if( !this.selection ) this.startSelection( cursor );
|
|
706
750
|
}
|
|
707
751
|
|
|
708
752
|
this.lineUp( cursor );
|
|
@@ -711,34 +755,34 @@ class CodeEditor {
|
|
|
711
755
|
if( e.shiftKey ) {
|
|
712
756
|
this.selection.toX = cursor.position;
|
|
713
757
|
this.selection.toY--;
|
|
714
|
-
this.processSelection(null, true);
|
|
758
|
+
this.processSelection( null, true );
|
|
715
759
|
}
|
|
716
760
|
}
|
|
717
761
|
}
|
|
718
762
|
});
|
|
719
763
|
|
|
720
|
-
this.action('ArrowRight', false, ( ln, cursor, e ) => {
|
|
764
|
+
this.action( 'ArrowRight', false, ( ln, cursor, e ) => {
|
|
721
765
|
|
|
722
|
-
if(e.metaKey) { // Apple devices (Command)
|
|
766
|
+
if( e.metaKey ) { // Apple devices (Command)
|
|
723
767
|
e.preventDefault();
|
|
724
768
|
this.actions[ 'End' ].callback( ln, cursor );
|
|
725
|
-
} else if(e.ctrlKey) {
|
|
769
|
+
} else if( e.ctrlKey ) {
|
|
726
770
|
// Get next word
|
|
727
|
-
const [word, from, to] = this.getWordAtPos( cursor );
|
|
771
|
+
const [ word, from, to ] = this.getWordAtPos( cursor );
|
|
728
772
|
var diff = cursor.position - from;
|
|
729
|
-
var substr = word.substr(diff);
|
|
773
|
+
var substr = word.substr( diff );
|
|
730
774
|
// Selections...
|
|
731
|
-
if( e.shiftKey ) if(!this.selection) this.startSelection(cursor);
|
|
732
|
-
this.cursorToString(cursor, substr);
|
|
775
|
+
if( e.shiftKey ) if( !this.selection ) this.startSelection( cursor );
|
|
776
|
+
this.cursorToString( cursor, substr);
|
|
733
777
|
if( e.shiftKey ) this.processSelection();
|
|
734
778
|
} else {
|
|
735
779
|
var letter = this.getCharAtPos( cursor );
|
|
736
|
-
if(letter) {
|
|
780
|
+
if( letter ) {
|
|
737
781
|
if( e.shiftKey ) {
|
|
738
|
-
if(!this.selection) this.startSelection(cursor);
|
|
782
|
+
if( !this.selection ) this.startSelection( cursor );
|
|
739
783
|
var keep_range = false;
|
|
740
784
|
if( cursor.position == this.selection.fromX ) {
|
|
741
|
-
if( (cursor.position + 1) == this.selection.toX && this.selection.sameLine() ) {
|
|
785
|
+
if( ( cursor.position + 1 ) == this.selection.toX && this.selection.sameLine() ) {
|
|
742
786
|
this.cursorToRight( letter, cursor );
|
|
743
787
|
this.endSelection();
|
|
744
788
|
return;
|
|
@@ -748,9 +792,9 @@ class CodeEditor {
|
|
|
748
792
|
} else this.selection.toX++;
|
|
749
793
|
}
|
|
750
794
|
this.cursorToRight( letter, cursor );
|
|
751
|
-
this.processSelection(null, keep_range);
|
|
795
|
+
this.processSelection( null, keep_range );
|
|
752
796
|
}else{
|
|
753
|
-
if(!this.selection) {
|
|
797
|
+
if( !this.selection ) {
|
|
754
798
|
this.cursorToRight( letter, cursor );
|
|
755
799
|
if( this.useAutoComplete && this.isAutoCompleteActive )
|
|
756
800
|
this.showAutoCompleteBox( 'foo', cursor );
|
|
@@ -759,8 +803,8 @@ class CodeEditor {
|
|
|
759
803
|
{
|
|
760
804
|
this.selection.invertIfNecessary();
|
|
761
805
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
|
|
762
|
-
this.cursorToLine(cursor, this.selection.toY);
|
|
763
|
-
this.cursorToPosition(cursor, this.selection.toX);
|
|
806
|
+
this.cursorToLine( cursor, this.selection.toY );
|
|
807
|
+
this.cursorToPosition( cursor, this.selection.toX );
|
|
764
808
|
this.endSelection();
|
|
765
809
|
}
|
|
766
810
|
}
|
|
@@ -768,18 +812,18 @@ class CodeEditor {
|
|
|
768
812
|
else if( this.code.lines[ cursor.line + 1 ] !== undefined ) {
|
|
769
813
|
|
|
770
814
|
if( e.shiftKey ) {
|
|
771
|
-
if(!this.selection) this.startSelection(cursor);
|
|
815
|
+
if( !this.selection ) this.startSelection( cursor );
|
|
772
816
|
e.cancelShift = true;
|
|
773
817
|
e.keepSelection = true;
|
|
774
818
|
}
|
|
775
819
|
|
|
776
820
|
this.lineDown( cursor );
|
|
777
|
-
this.actions['Home'].callback(cursor.line, cursor, e);
|
|
821
|
+
this.actions['Home'].callback( cursor.line, cursor, e );
|
|
778
822
|
|
|
779
823
|
if( e.shiftKey ) {
|
|
780
824
|
this.selection.toX = cursor.position;
|
|
781
825
|
this.selection.toY++;
|
|
782
|
-
this.processSelection(null, true);
|
|
826
|
+
this.processSelection( null, true );
|
|
783
827
|
}
|
|
784
828
|
|
|
785
829
|
this.hideAutoCompleteBox();
|
|
@@ -813,14 +857,14 @@ class CodeEditor {
|
|
|
813
857
|
// This can be used to empty all text...
|
|
814
858
|
setText( text = "", lang ) {
|
|
815
859
|
|
|
816
|
-
let new_lines = text.split('\n');
|
|
817
|
-
this.code.lines = [].concat(new_lines);
|
|
860
|
+
let new_lines = text.split( '\n' );
|
|
861
|
+
this.code.lines = [].concat( new_lines );
|
|
818
862
|
|
|
819
|
-
let cursor = this.cursors.children[0];
|
|
863
|
+
let cursor = this.cursors.children[ 0 ];
|
|
820
864
|
let lastLine = new_lines.pop();
|
|
821
865
|
|
|
822
|
-
this.cursorToLine(cursor, new_lines.length); // Already substracted 1
|
|
823
|
-
this.cursorToPosition(cursor, lastLine.length);
|
|
866
|
+
this.cursorToLine( cursor, new_lines.length ); // Already substracted 1
|
|
867
|
+
this.cursorToPosition( cursor, lastLine.length );
|
|
824
868
|
this.processLines();
|
|
825
869
|
|
|
826
870
|
if( lang )
|
|
@@ -831,64 +875,64 @@ class CodeEditor {
|
|
|
831
875
|
|
|
832
876
|
appendText( text ) {
|
|
833
877
|
|
|
834
|
-
let cursor = this.cursors.children[0];
|
|
878
|
+
let cursor = this.cursors.children[ 0 ];
|
|
835
879
|
let lidx = cursor.line;
|
|
836
880
|
|
|
837
881
|
if( this.selection ) {
|
|
838
|
-
this.deleteSelection(cursor);
|
|
882
|
+
this.deleteSelection( cursor );
|
|
839
883
|
lidx = cursor.line;
|
|
840
884
|
}
|
|
841
885
|
|
|
842
886
|
this.endSelection();
|
|
843
887
|
|
|
844
|
-
const new_lines = text.split('\n');
|
|
888
|
+
const new_lines = text.split( '\n' );
|
|
845
889
|
|
|
846
890
|
// Pasting Multiline...
|
|
847
|
-
if(new_lines.length != 1)
|
|
891
|
+
if( new_lines.length != 1 )
|
|
848
892
|
{
|
|
849
893
|
let num_lines = new_lines.length;
|
|
850
|
-
console.assert(num_lines > 0);
|
|
894
|
+
console.assert( num_lines > 0 );
|
|
851
895
|
const first_line = new_lines.shift();
|
|
852
896
|
num_lines--;
|
|
853
897
|
|
|
854
|
-
const remaining = this.code.lines[lidx].slice(cursor.position);
|
|
898
|
+
const remaining = this.code.lines[ lidx ].slice( cursor.position );
|
|
855
899
|
|
|
856
900
|
// Add first line
|
|
857
|
-
this.code.lines[lidx] = [
|
|
858
|
-
this.code.lines[lidx].slice(0, cursor.position),
|
|
901
|
+
this.code.lines[ lidx ] = [
|
|
902
|
+
this.code.lines[ lidx ].slice( 0, cursor.position ),
|
|
859
903
|
first_line
|
|
860
904
|
].join('');
|
|
861
905
|
|
|
862
|
-
this.cursorToPosition(cursor, (cursor.position + first_line.length));
|
|
906
|
+
this.cursorToPosition( cursor, ( cursor.position + first_line.length ) );
|
|
863
907
|
|
|
864
908
|
// Enter next lines...
|
|
865
909
|
|
|
866
910
|
let _text = null;
|
|
867
911
|
|
|
868
912
|
for( var i = 0; i < new_lines.length; ++i ) {
|
|
869
|
-
_text = new_lines[i];
|
|
870
|
-
this.cursorToLine(cursor, cursor.line++, true);
|
|
913
|
+
_text = new_lines[ i ];
|
|
914
|
+
this.cursorToLine( cursor, cursor.line++, true );
|
|
871
915
|
// Add remaining...
|
|
872
916
|
if( i == (new_lines.length - 1) )
|
|
873
917
|
_text += remaining;
|
|
874
|
-
this.code.lines.splice( 1 + lidx + i, 0, _text);
|
|
918
|
+
this.code.lines.splice( 1 + lidx + i, 0, _text );
|
|
875
919
|
}
|
|
876
920
|
|
|
877
|
-
if(_text) this.cursorToPosition(cursor, _text.length);
|
|
878
|
-
this.cursorToLine(cursor, cursor.line + num_lines);
|
|
879
|
-
this.processLines(
|
|
921
|
+
if( _text ) this.cursorToPosition( cursor, _text.length );
|
|
922
|
+
this.cursorToLine( cursor, cursor.line + num_lines );
|
|
923
|
+
this.processLines();
|
|
880
924
|
}
|
|
881
925
|
// Pasting one line...
|
|
882
926
|
else
|
|
883
927
|
{
|
|
884
|
-
this.code.lines[lidx] = [
|
|
885
|
-
this.code.lines[lidx].slice(0, cursor.position),
|
|
886
|
-
new_lines[0],
|
|
887
|
-
this.code.lines[lidx].slice(cursor.position)
|
|
928
|
+
this.code.lines[ lidx ] = [
|
|
929
|
+
this.code.lines[ lidx ].slice( 0, cursor.position ),
|
|
930
|
+
new_lines[ 0 ],
|
|
931
|
+
this.code.lines[ lidx ].slice( cursor.position )
|
|
888
932
|
].join('');
|
|
889
933
|
|
|
890
|
-
this.cursorToPosition(cursor, (cursor.position + new_lines[0].length));
|
|
891
|
-
this.processLine(lidx);
|
|
934
|
+
this.cursorToPosition( cursor, ( cursor.position + new_lines[ 0 ].length ) );
|
|
935
|
+
this.processLine( lidx );
|
|
892
936
|
}
|
|
893
937
|
}
|
|
894
938
|
|
|
@@ -898,18 +942,18 @@ class CodeEditor {
|
|
|
898
942
|
const existing = this.addTab(name, true, title);
|
|
899
943
|
if( !existing )
|
|
900
944
|
{
|
|
901
|
-
text = text.replaceAll('\r', '');
|
|
902
|
-
this.code.lines = text.split('\n');
|
|
903
|
-
this._changeLanguageFromExtension( LX.getExtension(name) );
|
|
945
|
+
text = text.replaceAll( '\r', '' );
|
|
946
|
+
this.code.lines = text.split( '\n' );
|
|
947
|
+
this._changeLanguageFromExtension( LX.getExtension( name ) );
|
|
904
948
|
}
|
|
905
949
|
};
|
|
906
950
|
|
|
907
|
-
if(file.constructor == String)
|
|
951
|
+
if( file.constructor == String )
|
|
908
952
|
{
|
|
909
953
|
let filename = file;
|
|
910
954
|
LX.request({ url: filename, success: text => {
|
|
911
955
|
|
|
912
|
-
const name = filename.substring(filename.lastIndexOf('/') + 1);
|
|
956
|
+
const name = filename.substring(filename.lastIndexOf( '/' ) + 1);
|
|
913
957
|
inner_add_tab( text, name, filename );
|
|
914
958
|
} });
|
|
915
959
|
}
|
|
@@ -926,18 +970,18 @@ class CodeEditor {
|
|
|
926
970
|
|
|
927
971
|
_addUndoStep( cursor ) {
|
|
928
972
|
|
|
929
|
-
var cursor = cursor ?? this.cursors.children[0];
|
|
973
|
+
var cursor = cursor ?? this.cursors.children[ 0 ];
|
|
930
974
|
|
|
931
975
|
this.code.undoSteps.push( {
|
|
932
|
-
lines: LX.deepCopy(this.code.lines),
|
|
933
|
-
cursor: this.saveCursor(cursor),
|
|
976
|
+
lines: LX.deepCopy( this.code.lines ),
|
|
977
|
+
cursor: this.saveCursor( cursor ),
|
|
934
978
|
line: cursor.line
|
|
935
979
|
} );
|
|
936
980
|
}
|
|
937
981
|
|
|
938
982
|
_changeLanguage( lang ) {
|
|
939
983
|
|
|
940
|
-
this.code.
|
|
984
|
+
this.code.language = lang;
|
|
941
985
|
this.highlight = lang;
|
|
942
986
|
this._refreshCodeInfo();
|
|
943
987
|
this.processLines();
|
|
@@ -946,23 +990,23 @@ class CodeEditor {
|
|
|
946
990
|
_changeLanguageFromExtension( ext ) {
|
|
947
991
|
|
|
948
992
|
if( !ext )
|
|
949
|
-
return this._changeLanguage( this.code.
|
|
950
|
-
|
|
951
|
-
switch(ext.toLowerCase())
|
|
952
|
-
{
|
|
953
|
-
case 'js': return this._changeLanguage('JavaScript');
|
|
954
|
-
case 'cpp': return this._changeLanguage('C++');
|
|
955
|
-
case 'h': return this._changeLanguage('C++');
|
|
956
|
-
case 'glsl': return this._changeLanguage('GLSL');
|
|
957
|
-
case 'css': return this._changeLanguage('CSS');
|
|
958
|
-
case 'json': return this._changeLanguage('JSON');
|
|
959
|
-
case 'xml': return this._changeLanguage('XML');
|
|
960
|
-
case 'wgsl': return this._changeLanguage('WGSL');
|
|
961
|
-
case 'py': return this._changeLanguage('Python');
|
|
962
|
-
case 'bat': return this._changeLanguage('Batch');
|
|
993
|
+
return this._changeLanguage( this.code.language );
|
|
994
|
+
|
|
995
|
+
switch( ext.toLowerCase() )
|
|
996
|
+
{
|
|
997
|
+
case 'js': return this._changeLanguage( 'JavaScript' );
|
|
998
|
+
case 'cpp': return this._changeLanguage( 'C++' );
|
|
999
|
+
case 'h': return this._changeLanguage( 'C++' );
|
|
1000
|
+
case 'glsl': return this._changeLanguage( 'GLSL' );
|
|
1001
|
+
case 'css': return this._changeLanguage( 'CSS' );
|
|
1002
|
+
case 'json': return this._changeLanguage( 'JSON' );
|
|
1003
|
+
case 'xml': return this._changeLanguage( 'XML' );
|
|
1004
|
+
case 'wgsl': return this._changeLanguage( 'WGSL' );
|
|
1005
|
+
case 'py': return this._changeLanguage( 'Python' );
|
|
1006
|
+
case 'bat': return this._changeLanguage( 'Batch' );
|
|
963
1007
|
case 'txt':
|
|
964
1008
|
default:
|
|
965
|
-
this._changeLanguage('Plain Text');
|
|
1009
|
+
this._changeLanguage( 'Plain Text' );
|
|
966
1010
|
}
|
|
967
1011
|
}
|
|
968
1012
|
|
|
@@ -974,15 +1018,15 @@ class CodeEditor {
|
|
|
974
1018
|
panel.ln = 0;
|
|
975
1019
|
panel.col = 0;
|
|
976
1020
|
|
|
977
|
-
this._refreshCodeInfo = (ln = panel.ln, col = panel.col) => {
|
|
1021
|
+
this._refreshCodeInfo = ( ln = panel.ln, col = panel.col ) => {
|
|
978
1022
|
panel.ln = ln + 1;
|
|
979
1023
|
panel.col = col + 1;
|
|
980
1024
|
panel.clear();
|
|
981
1025
|
panel.sameLine();
|
|
982
|
-
panel.addLabel(this.code.title, { float: 'right' });
|
|
983
|
-
panel.addLabel("Ln " + panel.ln, { width: "64px" });
|
|
984
|
-
panel.addLabel("Col " + panel.col, { width: "64px" });
|
|
985
|
-
panel.addButton("<b>{ }</b>", this.highlight, (value, event) => {
|
|
1026
|
+
panel.addLabel( this.code.title, { float: 'right' });
|
|
1027
|
+
panel.addLabel( "Ln " + panel.ln, { width: "64px" });
|
|
1028
|
+
panel.addLabel( "Col " + panel.col, { width: "64px" });
|
|
1029
|
+
panel.addButton( "<b>{ }</b>", this.highlight, ( value, event ) => {
|
|
986
1030
|
LX.addContextMenu( "Language", event, m => {
|
|
987
1031
|
for( const lang of Object.keys(this.languages) )
|
|
988
1032
|
m.add( lang, this._changeLanguage.bind(this) );
|
|
@@ -1003,8 +1047,8 @@ class CodeEditor {
|
|
|
1003
1047
|
|
|
1004
1048
|
// Change css a little bit...
|
|
1005
1049
|
this.gutter.style.height = "calc(100% - 38px)";
|
|
1006
|
-
this.root.querySelectorAll('.code').forEach( e => e.style.height = "calc(100% - 6px)" );
|
|
1007
|
-
this.root.querySelector('.lexareatabscontent').style.height = "calc(100% - 23px)";
|
|
1050
|
+
this.root.querySelectorAll( '.code' ).forEach( e => e.style.height = "calc(100% - 6px)" );
|
|
1051
|
+
this.root.querySelector( '.lexareatabscontent' ).style.height = "calc(100% - 23px)";
|
|
1008
1052
|
|
|
1009
1053
|
}, 100);
|
|
1010
1054
|
}
|
|
@@ -1015,61 +1059,46 @@ class CodeEditor {
|
|
|
1015
1059
|
this.processFocus(false);
|
|
1016
1060
|
|
|
1017
1061
|
LX.addContextMenu( null, e, m => {
|
|
1018
|
-
m.add( "Create", this.addTab.bind(this, "unnamed.js", true) );
|
|
1019
|
-
m.add( "Load", this.loadTab.bind(this, "unnamed.js", true) );
|
|
1062
|
+
m.add( "Create", this.addTab.bind( this, "unnamed.js", true ) );
|
|
1063
|
+
m.add( "Load", this.loadTab.bind( this, "unnamed.js", true ) );
|
|
1020
1064
|
});
|
|
1021
1065
|
}
|
|
1022
1066
|
|
|
1023
1067
|
addTab(name, selected, title) {
|
|
1024
1068
|
|
|
1025
|
-
if(this.openedTabs[name])
|
|
1069
|
+
if(this.openedTabs[ name ])
|
|
1026
1070
|
{
|
|
1027
1071
|
this.tabs.select( this.code.tabName );
|
|
1028
1072
|
return true;
|
|
1029
1073
|
}
|
|
1030
1074
|
|
|
1031
1075
|
// Create code content
|
|
1032
|
-
let code = document.createElement('div');
|
|
1076
|
+
let code = document.createElement( 'div' );
|
|
1033
1077
|
code.className = 'code';
|
|
1034
|
-
code.lines = [""];
|
|
1035
|
-
code.
|
|
1078
|
+
code.lines = [ "" ];
|
|
1079
|
+
code.language = "Plain Text";
|
|
1036
1080
|
code.cursorState = {};
|
|
1037
1081
|
code.undoSteps = [];
|
|
1038
1082
|
code.tabName = name;
|
|
1039
1083
|
code.title = title ?? name;
|
|
1040
1084
|
code.tokens = {};
|
|
1041
|
-
code.customScroll = new LX.vec2;
|
|
1042
1085
|
|
|
1043
|
-
code.addEventListener('dragenter', function(e) {
|
|
1086
|
+
code.addEventListener( 'dragenter', function(e) {
|
|
1044
1087
|
e.preventDefault();
|
|
1045
|
-
this.parentElement.classList.add('dragging');
|
|
1088
|
+
this.parentElement.classList.add( 'dragging' );
|
|
1046
1089
|
});
|
|
1047
|
-
code.addEventListener('dragleave', function(e) {
|
|
1090
|
+
code.addEventListener( 'dragleave', function(e) {
|
|
1048
1091
|
e.preventDefault();
|
|
1049
|
-
this.parentElement.remove('dragging');
|
|
1092
|
+
this.parentElement.remove( 'dragging' );
|
|
1050
1093
|
});
|
|
1051
|
-
code.addEventListener('drop',
|
|
1094
|
+
code.addEventListener( 'drop', e => {
|
|
1052
1095
|
e.preventDefault();
|
|
1053
|
-
code.parentElement.classList.remove('dragging');
|
|
1096
|
+
code.parentElement.classList.remove( 'dragging' );
|
|
1054
1097
|
for( let i = 0; i < e.dataTransfer.files.length; ++i )
|
|
1055
|
-
this.loadFile( e.dataTransfer.files[i] );
|
|
1056
|
-
});
|
|
1057
|
-
code.addEventListener('wheel', (e) => {
|
|
1058
|
-
|
|
1059
|
-
// Get scroll data
|
|
1060
|
-
|
|
1061
|
-
const dX = (e.deltaY > 0.0 ? 1.0 : -1.0) * 20.0 * ( e.shiftKey ? 1.0 : 0.0 );
|
|
1062
|
-
const dY = (e.deltaY > 0.0 ? 1.0 : -1.0) * 40.0 * ( e.shiftKey ? 0.0 : 1.0 );
|
|
1063
|
-
|
|
1064
|
-
var new_scroll = code.customScroll.add( new LX.vec2( dX, dY ), new LX.vec2() );
|
|
1065
|
-
|
|
1066
|
-
// Update state
|
|
1067
|
-
|
|
1068
|
-
if( new_scroll.x != this.getScrollLeft()) this.setScrollLeft( new_scroll.x );
|
|
1069
|
-
if( new_scroll.y != this.getScrollTop()) this.setScrollTop( new_scroll.y );
|
|
1098
|
+
this.loadFile( e.dataTransfer.files[ i ] );
|
|
1070
1099
|
});
|
|
1071
1100
|
|
|
1072
|
-
this.openedTabs[name] = code;
|
|
1101
|
+
this.openedTabs[ name ] = code;
|
|
1073
1102
|
|
|
1074
1103
|
this.tabs.add(name, code, { 'selected': selected, 'fixed': (name === '+') , 'title': code.title, 'onSelect': (e, tabname) => {
|
|
1075
1104
|
|
|
@@ -1079,38 +1108,37 @@ class CodeEditor {
|
|
|
1079
1108
|
return;
|
|
1080
1109
|
}
|
|
1081
1110
|
|
|
1082
|
-
var cursor = cursor ?? this.cursors.children[0];
|
|
1083
|
-
this.saveCursor(cursor, this.code.cursorState);
|
|
1084
|
-
this.code = this.openedTabs[tabname];
|
|
1085
|
-
this.restoreCursor(cursor, this.code.cursorState);
|
|
1111
|
+
var cursor = cursor ?? this.cursors.children[ 0 ];
|
|
1112
|
+
this.saveCursor( cursor, this.code.cursorState );
|
|
1113
|
+
this.code = this.openedTabs[ tabname ];
|
|
1114
|
+
this.restoreCursor( cursor, this.code.cursorState );
|
|
1086
1115
|
this.endSelection();
|
|
1087
|
-
this._changeLanguageFromExtension( LX.getExtension(tabname) );
|
|
1088
|
-
this._refreshCodeInfo(cursor.line, cursor.position);
|
|
1089
|
-
|
|
1090
|
-
// Restore scroll
|
|
1091
|
-
this.gutter.scrollLeft = this.getScrollLeft();
|
|
1092
|
-
this.gutter.scrollTop = this.getScrollTop();
|
|
1116
|
+
this._changeLanguageFromExtension( LX.getExtension( tabname ) );
|
|
1117
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
1093
1118
|
}});
|
|
1119
|
+
|
|
1120
|
+
// Move into the sizer..
|
|
1121
|
+
this.codeSizer.appendChild( code );
|
|
1094
1122
|
|
|
1095
1123
|
this.endSelection();
|
|
1096
1124
|
|
|
1097
1125
|
if( selected )
|
|
1098
1126
|
{
|
|
1099
1127
|
this.code = code;
|
|
1100
|
-
this.resetCursorPos(CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP);
|
|
1128
|
+
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
|
|
1101
1129
|
this.processLines();
|
|
1102
|
-
doAsync( () => this._refreshCodeInfo(0, 0), 50 );
|
|
1130
|
+
doAsync( () => this._refreshCodeInfo( 0, 0 ), 50 );
|
|
1103
1131
|
}
|
|
1104
1132
|
}
|
|
1105
1133
|
|
|
1106
1134
|
loadTab() {
|
|
1107
|
-
const input = document.createElement('input');
|
|
1135
|
+
const input = document.createElement( 'input' );
|
|
1108
1136
|
input.type = 'file';
|
|
1109
|
-
document.body.appendChild(input);
|
|
1137
|
+
document.body.appendChild( input );
|
|
1110
1138
|
input.click();
|
|
1111
|
-
input.addEventListener('change',
|
|
1112
|
-
if (e.target.files[0]) {
|
|
1113
|
-
this.loadFile( e.target.files[0] );
|
|
1139
|
+
input.addEventListener('change', e => {
|
|
1140
|
+
if (e.target.files[ 0 ]) {
|
|
1141
|
+
this.loadFile( e.target.files[ 0 ] );
|
|
1114
1142
|
}
|
|
1115
1143
|
input.remove();
|
|
1116
1144
|
});
|
|
@@ -1131,7 +1159,7 @@ class CodeEditor {
|
|
|
1131
1159
|
if( !e.target.classList.contains('code') ) return;
|
|
1132
1160
|
if( !this.code ) return;
|
|
1133
1161
|
|
|
1134
|
-
var cursor = this.cursors.children[0];
|
|
1162
|
+
var cursor = this.cursors.children[ 0 ];
|
|
1135
1163
|
var code_rect = this.code.getBoundingClientRect();
|
|
1136
1164
|
var mouse_pos = [(e.clientX - code_rect.x), (e.clientY - code_rect.y)];
|
|
1137
1165
|
|
|
@@ -1139,7 +1167,7 @@ class CodeEditor {
|
|
|
1139
1167
|
if( e.type != 'contextmenu' )
|
|
1140
1168
|
{
|
|
1141
1169
|
var ln = (mouse_pos[1] / this.lineHeight)|0;
|
|
1142
|
-
if(this.code.lines[ln] == undefined) return;
|
|
1170
|
+
if(this.code.lines[ ln ] == undefined) return;
|
|
1143
1171
|
}
|
|
1144
1172
|
|
|
1145
1173
|
if( e.type == 'mousedown' )
|
|
@@ -1231,36 +1259,35 @@ class CodeEditor {
|
|
|
1231
1259
|
}
|
|
1232
1260
|
}
|
|
1233
1261
|
|
|
1234
|
-
processClick(e, skip_refresh = false) {
|
|
1262
|
+
processClick( e, skip_refresh = false ) {
|
|
1235
1263
|
|
|
1236
|
-
var
|
|
1237
|
-
var
|
|
1238
|
-
var
|
|
1264
|
+
var cursor = this.cursors.children[ 0 ];
|
|
1265
|
+
var code_rect = this.codeScroller.getBoundingClientRect();
|
|
1266
|
+
var position = [( e.clientX - code_rect.x ) + this.getScrollLeft(), (e.clientY - code_rect.y) + this.getScrollTop()];
|
|
1267
|
+
var ln = (position[ 1 ] / this.lineHeight)|0;
|
|
1239
1268
|
|
|
1240
|
-
if(this.code.lines[ln] == undefined)
|
|
1269
|
+
if( this.code.lines[ ln ] == undefined )
|
|
1270
|
+
return;
|
|
1241
1271
|
|
|
1242
|
-
|
|
1243
|
-
cursor.line = ln;
|
|
1244
|
-
|
|
1245
|
-
this.cursorToLine(cursor, ln, true);
|
|
1272
|
+
this.cursorToLine( cursor, ln, true );
|
|
1246
1273
|
|
|
1247
|
-
var ch = (position[0] / this.charWidth)|0;
|
|
1248
|
-
var string = this.code.lines[ln].slice(0, ch);
|
|
1249
|
-
this.cursorToPosition(cursor, string.length);
|
|
1274
|
+
var ch = ( ( position[ 0 ] - parseInt( this.xPadding ) + 3) / this.charWidth )|0;
|
|
1275
|
+
var string = this.code.lines[ ln ].slice( 0, ch );
|
|
1276
|
+
this.cursorToPosition( cursor, string.length );
|
|
1250
1277
|
|
|
1251
1278
|
this.hideAutoCompleteBox();
|
|
1252
1279
|
|
|
1253
|
-
if(!skip_refresh)
|
|
1280
|
+
if( !skip_refresh )
|
|
1254
1281
|
this._refreshCodeInfo( ln, cursor.position );
|
|
1255
1282
|
}
|
|
1256
1283
|
|
|
1257
1284
|
processSelection( e, keep_range ) {
|
|
1258
1285
|
|
|
1259
|
-
var cursor = this.cursors.children[0];
|
|
1286
|
+
var cursor = this.cursors.children[ 0 ];
|
|
1260
1287
|
|
|
1261
|
-
if(e) this.processClick(e, true);
|
|
1288
|
+
if(e) this.processClick( e, true );
|
|
1262
1289
|
if( !this.selection )
|
|
1263
|
-
this.startSelection(cursor);
|
|
1290
|
+
this.startSelection( cursor );
|
|
1264
1291
|
|
|
1265
1292
|
// Update selection
|
|
1266
1293
|
if(!keep_range)
|
|
@@ -1291,7 +1318,7 @@ class CodeEditor {
|
|
|
1291
1318
|
let domEl = this.selections.childNodes[sId];
|
|
1292
1319
|
if(!domEl)
|
|
1293
1320
|
{
|
|
1294
|
-
domEl = document.createElement('div');
|
|
1321
|
+
domEl = document.createElement( 'div' );
|
|
1295
1322
|
domEl.className = "lexcodeselection";
|
|
1296
1323
|
this.selections.appendChild( domEl );
|
|
1297
1324
|
}
|
|
@@ -1302,22 +1329,21 @@ class CodeEditor {
|
|
|
1302
1329
|
if(sId == 0) // First line 2 cases (single line, multiline)
|
|
1303
1330
|
{
|
|
1304
1331
|
const reverse = fromX > toX;
|
|
1305
|
-
if(deltaY == 0) string = !reverse ? this.code.lines[i].substring(fromX, toX) : this.code.lines[i].substring(toX, fromX);
|
|
1306
|
-
else string = this.code.lines[i].substr(fromX);
|
|
1307
|
-
const pixels = (
|
|
1332
|
+
if(deltaY == 0) string = !reverse ? this.code.lines[ i ].substring(fromX, toX) : this.code.lines[ i ].substring(toX, fromX);
|
|
1333
|
+
else string = this.code.lines[ i ].substr(fromX);
|
|
1334
|
+
const pixels = (reverse && deltaY == 0 ? toX : fromX) * this.charWidth;
|
|
1308
1335
|
domEl.style.left = "calc(" + pixels + "px + " + this.xPadding + ")";
|
|
1309
1336
|
}
|
|
1310
1337
|
else
|
|
1311
1338
|
{
|
|
1312
|
-
string = (i == toY) ? this.code.lines[i].substring(0, toX) : this.code.lines[i]; // Last line, any multiple line...
|
|
1313
|
-
|
|
1314
|
-
domEl.style.left = "calc(" + pixels + "px + " + this.xPadding + ")";
|
|
1339
|
+
string = (i == toY) ? this.code.lines[ i ].substring(0, toX) : this.code.lines[ i ]; // Last line, any multiple line...
|
|
1340
|
+
domEl.style.left = this.xPadding;
|
|
1315
1341
|
}
|
|
1316
1342
|
|
|
1317
1343
|
const stringWidth = this.measureString(string);
|
|
1318
1344
|
domEl.style.width = (stringWidth || 8) + "px";
|
|
1319
|
-
domEl._top =
|
|
1320
|
-
domEl.style.top =
|
|
1345
|
+
domEl._top = i * this.lineHeight;
|
|
1346
|
+
domEl.style.top = domEl._top + "px";
|
|
1321
1347
|
this.selection.chars += stringWidth / this.charWidth;
|
|
1322
1348
|
}
|
|
1323
1349
|
}
|
|
@@ -1334,7 +1360,7 @@ class CodeEditor {
|
|
|
1334
1360
|
let domEl = this.selections.childNodes[sId];
|
|
1335
1361
|
if(!domEl)
|
|
1336
1362
|
{
|
|
1337
|
-
domEl = document.createElement('div');
|
|
1363
|
+
domEl = document.createElement( 'div' );
|
|
1338
1364
|
domEl.className = "lexcodeselection";
|
|
1339
1365
|
this.selections.appendChild( domEl );
|
|
1340
1366
|
}
|
|
@@ -1344,20 +1370,20 @@ class CodeEditor {
|
|
|
1344
1370
|
|
|
1345
1371
|
if(sId == 0)
|
|
1346
1372
|
{
|
|
1347
|
-
string = this.code.lines[i].substr(toX);
|
|
1348
|
-
const pixels =
|
|
1373
|
+
string = this.code.lines[ i ].substr(toX);
|
|
1374
|
+
const pixels = toX * this.charWidth;
|
|
1349
1375
|
domEl.style.left = "calc(" + pixels + "px + " + this.xPadding + ")";
|
|
1350
1376
|
}
|
|
1351
1377
|
else
|
|
1352
1378
|
{
|
|
1353
|
-
string = (i == fromY) ? this.code.lines[i].substring(0, fromX) : this.code.lines[i]; // Last line, any multiple line...
|
|
1354
|
-
domEl.style.left =
|
|
1379
|
+
string = (i == fromY) ? this.code.lines[ i ].substring(0, fromX) : this.code.lines[ i ]; // Last line, any multiple line...
|
|
1380
|
+
domEl.style.left = this.xPadding;
|
|
1355
1381
|
}
|
|
1356
1382
|
|
|
1357
1383
|
const stringWidth = this.measureString(string);
|
|
1358
1384
|
domEl.style.width = (stringWidth || 8) + "px";
|
|
1359
|
-
domEl._top =
|
|
1360
|
-
domEl.style.top =
|
|
1385
|
+
domEl._top = i * this.lineHeight;
|
|
1386
|
+
domEl.style.top = domEl._top + "px";
|
|
1361
1387
|
this.selection.chars += stringWidth / this.charWidth;
|
|
1362
1388
|
}
|
|
1363
1389
|
}
|
|
@@ -1376,9 +1402,9 @@ class CodeEditor {
|
|
|
1376
1402
|
if( key.length > 1 && this.specialKeys.indexOf(key) == -1 )
|
|
1377
1403
|
return;
|
|
1378
1404
|
|
|
1379
|
-
let cursor = this.cursors.children[0];
|
|
1405
|
+
let cursor = this.cursors.children[ 0 ];
|
|
1380
1406
|
let lidx = cursor.line;
|
|
1381
|
-
this.code.lines[lidx] = this.code.lines[lidx] ?? "";
|
|
1407
|
+
this.code.lines[ lidx ] = this.code.lines[ lidx ] ?? "";
|
|
1382
1408
|
|
|
1383
1409
|
// Check combinations
|
|
1384
1410
|
|
|
@@ -1388,22 +1414,22 @@ class CodeEditor {
|
|
|
1388
1414
|
case 'a': // select all
|
|
1389
1415
|
e.preventDefault();
|
|
1390
1416
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT | CodeEditor.CURSOR_TOP );
|
|
1391
|
-
this.startSelection(cursor);
|
|
1417
|
+
this.startSelection( cursor );
|
|
1392
1418
|
const nlines = this.code.lines.length - 1;
|
|
1393
|
-
this.selection.toX = this.code.lines[nlines].length;
|
|
1419
|
+
this.selection.toX = this.code.lines[ nlines ].length;
|
|
1394
1420
|
this.selection.toY = nlines;
|
|
1395
|
-
this.processSelection(null, true);
|
|
1396
|
-
this.cursorToPosition(cursor, this.selection.toX);
|
|
1397
|
-
this.cursorToLine(cursor, this.selection.toY);
|
|
1421
|
+
this.processSelection( null, true );
|
|
1422
|
+
this.cursorToPosition( cursor, this.selection.toX );
|
|
1423
|
+
this.cursorToLine( cursor, this.selection.toY );
|
|
1398
1424
|
break;
|
|
1399
1425
|
case 'c': // copy
|
|
1400
1426
|
this._copyContent();
|
|
1401
1427
|
return;
|
|
1402
1428
|
case 'd': // duplicate line
|
|
1403
1429
|
e.preventDefault();
|
|
1404
|
-
this.code.lines.splice(lidx, 0, this.code.lines[lidx]);
|
|
1430
|
+
this.code.lines.splice( lidx, 0, this.code.lines[ lidx ] );
|
|
1405
1431
|
this.lineDown( cursor );
|
|
1406
|
-
this.processLines(
|
|
1432
|
+
this.processLines();
|
|
1407
1433
|
return;
|
|
1408
1434
|
case 's': // save
|
|
1409
1435
|
e.preventDefault();
|
|
@@ -1424,8 +1450,6 @@ class CodeEditor {
|
|
|
1424
1450
|
this.restoreCursor( cursor, step.cursor );
|
|
1425
1451
|
this.processLines();
|
|
1426
1452
|
return;
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
1453
|
}
|
|
1430
1454
|
}
|
|
1431
1455
|
|
|
@@ -1509,14 +1533,14 @@ class CodeEditor {
|
|
|
1509
1533
|
// Append key
|
|
1510
1534
|
|
|
1511
1535
|
const isPairKey = (Object.values( this.pairKeys ).indexOf( key ) > -1) && !this.wasKeyPaired;
|
|
1512
|
-
const sameKeyNext = isPairKey && (this.code.lines[lidx][cursor.position] === key);
|
|
1536
|
+
const sameKeyNext = isPairKey && (this.code.lines[ lidx ][cursor.position] === key);
|
|
1513
1537
|
|
|
1514
1538
|
if( !sameKeyNext )
|
|
1515
1539
|
{
|
|
1516
|
-
this.code.lines[lidx] = [
|
|
1517
|
-
this.code.lines[lidx].slice(0, cursor.position),
|
|
1540
|
+
this.code.lines[ lidx ] = [
|
|
1541
|
+
this.code.lines[ lidx ].slice(0, cursor.position),
|
|
1518
1542
|
key,
|
|
1519
|
-
this.code.lines[lidx].slice(cursor.position)
|
|
1543
|
+
this.code.lines[ lidx ].slice(cursor.position)
|
|
1520
1544
|
].join('');
|
|
1521
1545
|
}
|
|
1522
1546
|
|
|
@@ -1556,11 +1580,11 @@ class CodeEditor {
|
|
|
1556
1580
|
|
|
1557
1581
|
async _copyContent() {
|
|
1558
1582
|
|
|
1559
|
-
let cursor = this.cursors.children[0];
|
|
1583
|
+
let cursor = this.cursors.children[ 0 ];
|
|
1560
1584
|
let text_to_copy = "";
|
|
1561
1585
|
|
|
1562
1586
|
if( !this.selection ) {
|
|
1563
|
-
text_to_copy = "\n" + this.code.lines[cursor.line];
|
|
1587
|
+
text_to_copy = "\n" + this.code.lines[ cursor.line ];
|
|
1564
1588
|
}
|
|
1565
1589
|
else {
|
|
1566
1590
|
const separator = "_NEWLINE_";
|
|
@@ -1570,7 +1594,7 @@ class CodeEditor {
|
|
|
1570
1594
|
let index = 0;
|
|
1571
1595
|
|
|
1572
1596
|
for(let i = 0; i <= this.selection.fromY; i++)
|
|
1573
|
-
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
|
|
1597
|
+
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[ i ].length);
|
|
1574
1598
|
|
|
1575
1599
|
index += this.selection.fromY * separator.length;
|
|
1576
1600
|
const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
|
|
@@ -1584,14 +1608,14 @@ class CodeEditor {
|
|
|
1584
1608
|
|
|
1585
1609
|
async _cutContent() {
|
|
1586
1610
|
|
|
1587
|
-
let cursor = this.cursors.children[0];
|
|
1611
|
+
let cursor = this.cursors.children[ 0 ];
|
|
1588
1612
|
let lidx = cursor.line;
|
|
1589
1613
|
let text_to_cut = "";
|
|
1590
1614
|
|
|
1591
1615
|
if( !this.selection ) {
|
|
1592
|
-
text_to_cut = "\n" + this.code.lines[cursor.line];
|
|
1593
|
-
this.code.lines.splice(lidx, 1);
|
|
1594
|
-
this.processLines(
|
|
1616
|
+
text_to_cut = "\n" + this.code.lines[ cursor.line ];
|
|
1617
|
+
this.code.lines.splice( lidx, 1 );
|
|
1618
|
+
this.processLines();
|
|
1595
1619
|
this.resetCursorPos( CodeEditor.CURSOR_LEFT );
|
|
1596
1620
|
}
|
|
1597
1621
|
else {
|
|
@@ -1602,7 +1626,7 @@ class CodeEditor {
|
|
|
1602
1626
|
let index = 0;
|
|
1603
1627
|
|
|
1604
1628
|
for(let i = 0; i <= this.selection.fromY; i++)
|
|
1605
|
-
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
|
|
1629
|
+
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[ i ].length);
|
|
1606
1630
|
|
|
1607
1631
|
index += this.selection.fromY * separator.length;
|
|
1608
1632
|
const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
|
|
@@ -1631,70 +1655,117 @@ class CodeEditor {
|
|
|
1631
1655
|
for( let i = 0; i < this.code.lines.length; ++i )
|
|
1632
1656
|
{
|
|
1633
1657
|
const linestring = this.code.lines[ i ];
|
|
1634
|
-
const tokens = this.
|
|
1658
|
+
const tokens = this._getTokensFromLine( linestring, true );
|
|
1635
1659
|
tokens.forEach( t => this.code.tokens[ t ] = 1 );
|
|
1636
1660
|
}
|
|
1637
1661
|
}
|
|
1638
1662
|
|
|
1639
|
-
|
|
1663
|
+
toLocalLine( line ) {
|
|
1640
1664
|
|
|
1641
|
-
const
|
|
1665
|
+
const d = Math.max( this.viewportRangeStart - this.lineScrollMargin.x, 0 );
|
|
1666
|
+
return Math.min( Math.max( line - d, 0 ), this.code.lines.length - 1 );
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
getMaxLineLength() {
|
|
1670
|
+
|
|
1671
|
+
return Math.max(...this.code.lines.map( v => v.length ));
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
processLines( mode ) {
|
|
1675
|
+
|
|
1676
|
+
mode = mode ?? CodeEditor.KEEP_VISIBLE_LINES;
|
|
1642
1677
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1678
|
+
// console.clear();
|
|
1679
|
+
console.log("--------------------------------------------");
|
|
1645
1680
|
|
|
1646
|
-
this.
|
|
1681
|
+
const lastScrollTop = this.getScrollTop();
|
|
1682
|
+
const start = performance.now();
|
|
1683
|
+
|
|
1684
|
+
var gutter_html = "", code_html = "";
|
|
1647
1685
|
|
|
1648
1686
|
this.code.innerHTML = "";
|
|
1649
|
-
this.gutter.innerHTML = "";
|
|
1650
1687
|
|
|
1651
1688
|
// Get info about lines in viewport
|
|
1652
|
-
const
|
|
1653
|
-
|
|
1654
|
-
const totalLinesInViewport = ((this.
|
|
1689
|
+
const firstLineInViewport = mode & CodeEditor.UPDATE_VISIBLE_LINES ?
|
|
1690
|
+
( (lastScrollTop / this.lineHeight)|0 ) : this.viewportRangeStart;
|
|
1691
|
+
const totalLinesInViewport = ((this.codeScroller.offsetHeight - 36) / this.lineHeight)|0;
|
|
1692
|
+
this.viewportRangeStart = firstLineInViewport;
|
|
1693
|
+
|
|
1655
1694
|
const viewportRange = new LX.vec2(
|
|
1656
|
-
Math.max( firstLineInViewport -
|
|
1657
|
-
Math.min( firstLineInViewport + totalLinesInViewport +
|
|
1695
|
+
Math.max( firstLineInViewport - this.lineScrollMargin.x, 0 ),
|
|
1696
|
+
Math.min( firstLineInViewport + totalLinesInViewport + this.lineScrollMargin.y, this.code.lines.length )
|
|
1658
1697
|
);
|
|
1659
1698
|
|
|
1699
|
+
// Add remaining lines if we are near the end of the scroll
|
|
1700
|
+
{
|
|
1701
|
+
const diff = Math.max( this.code.lines.length - viewportRange.y, 0 );
|
|
1702
|
+
if( diff < ( totalLinesInViewport + this.lineScrollMargin.y ) )
|
|
1703
|
+
viewportRange.y += diff;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1660
1706
|
for( let i = viewportRange.x; i < viewportRange.y; ++i )
|
|
1661
1707
|
{
|
|
1662
1708
|
gutter_html += "<span>" + (i + 1) + "</span>";
|
|
1663
1709
|
code_html += this.processLine( i, true );
|
|
1664
1710
|
}
|
|
1665
|
-
|
|
1666
|
-
this.code.innerHTML = code_html;
|
|
1667
|
-
this.gutter.innerHTML = gutter_html;
|
|
1668
1711
|
|
|
1669
|
-
|
|
1712
|
+
this.code.innerHTML = code_html;
|
|
1713
|
+
|
|
1714
|
+
console.log("RANGE:", viewportRange);
|
|
1715
|
+
console.log( "Num lines processed:", (viewportRange.y - viewportRange.x), performance.now() - start );
|
|
1716
|
+
console.log("--------------------------------------------");
|
|
1717
|
+
|
|
1718
|
+
this.codeScroller.scrollTop = lastScrollTop;
|
|
1719
|
+
|
|
1720
|
+
setTimeout( () => {
|
|
1721
|
+
|
|
1722
|
+
// Update max viewport
|
|
1723
|
+
const scrollWidth = this.getMaxLineLength() * this.charWidth;
|
|
1724
|
+
const scrollHeight = this.code.lines.length * this.lineHeight + 10; // scrollbar offset
|
|
1725
|
+
|
|
1726
|
+
this.codeSizer.style.minWidth = scrollWidth + "px";
|
|
1727
|
+
this.codeSizer.style.minHeight = scrollHeight + "px";
|
|
1728
|
+
|
|
1729
|
+
this.resizeScrollBars( totalLinesInViewport );
|
|
1730
|
+
|
|
1731
|
+
}, 10 );
|
|
1670
1732
|
}
|
|
1671
1733
|
|
|
1672
1734
|
processLine( linenum, force ) {
|
|
1673
1735
|
|
|
1674
|
-
|
|
1736
|
+
const local_line_num = this.toLocalLine( linenum );
|
|
1737
|
+
const gutter_line = "<span class='line-gutter'>" + (linenum + 1) + "</span>";
|
|
1738
|
+
|
|
1739
|
+
const UPDATE_LINE = ( html ) => {
|
|
1740
|
+
if( !force ) // Single line update
|
|
1741
|
+
this.code.childNodes[ local_line_num ].innerHTML = gutter_line + html;
|
|
1742
|
+
else // Update all lines at once
|
|
1743
|
+
return "<pre>" + ( gutter_line + html ) + "</pre>";
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// multi-line strings not supported by now
|
|
1747
|
+
delete this._buildingString;
|
|
1748
|
+
delete this._pendingString;
|
|
1675
1749
|
|
|
1676
|
-
// It's allowed to process only 1 line to optimize
|
|
1677
1750
|
let linestring = this.code.lines[ linenum ];
|
|
1678
1751
|
|
|
1752
|
+
// Single line
|
|
1679
1753
|
if( !force )
|
|
1680
1754
|
{
|
|
1681
|
-
|
|
1755
|
+
deleteElement( this.code.childNodes[ local_line_num ] );
|
|
1756
|
+
this.code.insertChildAtIndex( document.createElement( 'pre' ), local_line_num );
|
|
1757
|
+
}
|
|
1682
1758
|
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
// Gutter
|
|
1688
|
-
deleteElement( this.gutter.childNodes[ linenum ] );
|
|
1689
|
-
var linenumspan = document.createElement('span');
|
|
1690
|
-
linenumspan.innerHTML = (linenum + 1);
|
|
1691
|
-
this.gutter.insertChildAtIndex( linenumspan, linenum );
|
|
1759
|
+
// Early out check for no highlighting languages
|
|
1760
|
+
if( this.highlight == 'Plain Text' )
|
|
1761
|
+
{
|
|
1762
|
+
return UPDATE_LINE( linestring );
|
|
1692
1763
|
}
|
|
1693
1764
|
|
|
1694
|
-
const tokensToEvaluate = this.
|
|
1765
|
+
const tokensToEvaluate = this._getTokensFromLine( linestring );
|
|
1695
1766
|
|
|
1696
1767
|
if( !tokensToEvaluate.length )
|
|
1697
|
-
return "<pre></pre>";
|
|
1768
|
+
return "<pre><span class='line-gutter'>" + linenum + "</span></pre>";
|
|
1698
1769
|
|
|
1699
1770
|
var line_inner_html = "";
|
|
1700
1771
|
|
|
@@ -1702,58 +1773,49 @@ class CodeEditor {
|
|
|
1702
1773
|
for( var i = 0; i < tokensToEvaluate.length; ++i )
|
|
1703
1774
|
{
|
|
1704
1775
|
let it = i - 1;
|
|
1705
|
-
let prev = tokensToEvaluate[it];
|
|
1776
|
+
let prev = tokensToEvaluate[ it ];
|
|
1706
1777
|
while( prev == ' ' ) {
|
|
1707
1778
|
it--;
|
|
1708
|
-
prev = tokensToEvaluate[it];
|
|
1779
|
+
prev = tokensToEvaluate[ it ];
|
|
1709
1780
|
}
|
|
1710
1781
|
|
|
1711
1782
|
it = i + 1;
|
|
1712
|
-
let next = tokensToEvaluate[it];
|
|
1783
|
+
let next = tokensToEvaluate[ it ];
|
|
1713
1784
|
while( next == ' ' || next == '"' ) {
|
|
1714
1785
|
it++;
|
|
1715
|
-
next = tokensToEvaluate[it];
|
|
1786
|
+
next = tokensToEvaluate[ it ];
|
|
1716
1787
|
}
|
|
1717
1788
|
|
|
1718
|
-
const token = tokensToEvaluate[i];
|
|
1789
|
+
const token = tokensToEvaluate[ i ];
|
|
1719
1790
|
|
|
1720
1791
|
if( this.languages[ this.highlight ].blockComments ?? true )
|
|
1721
1792
|
{
|
|
1722
|
-
if( token.substr(0, 2) == '/*' )
|
|
1793
|
+
if( token.substr( 0, 2 ) == '/*' )
|
|
1723
1794
|
this._buildingBlockComment = true;
|
|
1724
|
-
if( token.substr(token.length - 2) == '*/' )
|
|
1795
|
+
if( token.substr( token.length - 2 ) == '*/' )
|
|
1725
1796
|
delete this._buildingBlockComment;
|
|
1726
1797
|
}
|
|
1727
1798
|
|
|
1728
|
-
line_inner_html += this.evaluateToken(token, prev, next);
|
|
1799
|
+
line_inner_html += this.evaluateToken( token, prev, next, (i == tokensToEvaluate.length - 1) );
|
|
1729
1800
|
}
|
|
1730
1801
|
|
|
1731
|
-
|
|
1732
|
-
if( !force )
|
|
1733
|
-
{
|
|
1734
|
-
this.code.childNodes[ linenum ].innerHTML = line_inner_html;
|
|
1735
|
-
}
|
|
1736
|
-
// Update all lines at once
|
|
1737
|
-
else
|
|
1738
|
-
{
|
|
1739
|
-
return "<pre>" + line_inner_html + "</pre>";
|
|
1740
|
-
}
|
|
1802
|
+
return UPDATE_LINE( line_inner_html );
|
|
1741
1803
|
}
|
|
1742
1804
|
|
|
1743
1805
|
_processTokens( tokens, offset = 0 ) {
|
|
1744
1806
|
|
|
1745
1807
|
if( this.highlight == 'C++' )
|
|
1746
1808
|
{
|
|
1747
|
-
var idx = tokens.slice(offset).findIndex( (value, index) => this.isNumber(value) );
|
|
1809
|
+
var idx = tokens.slice( offset ).findIndex( ( value, index ) => this.isNumber( value ) );
|
|
1748
1810
|
if( idx > -1 )
|
|
1749
1811
|
{
|
|
1750
1812
|
idx += offset; // Add offset to compute within the whole array of tokens
|
|
1751
|
-
let data = tokens[idx] + tokens[++idx];
|
|
1813
|
+
let data = tokens[ idx ] + tokens[ ++idx ];
|
|
1752
1814
|
while( this.isNumber( data ) )
|
|
1753
1815
|
{
|
|
1754
1816
|
tokens[ idx - 1 ] += tokens[ idx ];
|
|
1755
1817
|
tokens.splice( idx, 1 );
|
|
1756
|
-
data += tokens[idx];
|
|
1818
|
+
data += tokens[ idx ];
|
|
1757
1819
|
}
|
|
1758
1820
|
// Scan for numbers again
|
|
1759
1821
|
return this._processTokens( tokens, idx );
|
|
@@ -1763,16 +1825,43 @@ class CodeEditor {
|
|
|
1763
1825
|
return tokens;
|
|
1764
1826
|
}
|
|
1765
1827
|
|
|
1766
|
-
|
|
1828
|
+
_lineHasComment( linestring ) {
|
|
1767
1829
|
|
|
1768
|
-
// Check if line comment
|
|
1769
1830
|
const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
1770
|
-
const
|
|
1771
|
-
const has_comment = linestring.split(singleLineCommentToken);
|
|
1772
|
-
linestring = ( has_comment.length > 1 ) ? has_comment[0] : linestring;
|
|
1773
|
-
|
|
1774
|
-
// const tokens = linestring.split(' ').join('¬ ¬').split('¬'); // trick to split without losing spaces
|
|
1831
|
+
const idx = linestring.indexOf( singleLineCommentToken );
|
|
1775
1832
|
|
|
1833
|
+
if( idx > -1 )
|
|
1834
|
+
{
|
|
1835
|
+
const stringKeys = Object.values( this.stringKeys );
|
|
1836
|
+
// Count times we started a string BEFORE the comment
|
|
1837
|
+
var err = false;
|
|
1838
|
+
err |= stringKeys.some( function(v) {
|
|
1839
|
+
var re = new RegExp( v, "g" );
|
|
1840
|
+
var matches = (linestring.substring( 0, idx ).match( re ) || []);
|
|
1841
|
+
return (matches.length % 2) !== 0;
|
|
1842
|
+
} );
|
|
1843
|
+
err |= stringKeys.some( function(v) {
|
|
1844
|
+
var re = new RegExp( v, "g" );
|
|
1845
|
+
var matches = (linestring.substring( idx ).match( re ) || []);
|
|
1846
|
+
return (matches.length % 2) !== 0;
|
|
1847
|
+
} );
|
|
1848
|
+
return err ? undefined : idx;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
_getTokensFromLine( linestring, skipNonWords ) {
|
|
1853
|
+
|
|
1854
|
+
// Check if line comment
|
|
1855
|
+
const ogLine = linestring;
|
|
1856
|
+
const hasCommentIdx = this._lineHasComment( linestring );
|
|
1857
|
+
|
|
1858
|
+
if( hasCommentIdx != undefined )
|
|
1859
|
+
{
|
|
1860
|
+
linestring = ogLine.substring( 0, hasCommentIdx );
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
|
|
1864
|
+
|
|
1776
1865
|
let tokensToEvaluate = []; // store in a temp array so we know prev and next tokens...
|
|
1777
1866
|
|
|
1778
1867
|
const pushToken = function( t ) {
|
|
@@ -1790,8 +1879,8 @@ class CodeEditor {
|
|
|
1790
1879
|
{
|
|
1791
1880
|
const _pt = linestring.substring(idx, subtokens.value.index);
|
|
1792
1881
|
if( _pt.length ) pushToken( _pt );
|
|
1793
|
-
pushToken( subtokens.value[0] );
|
|
1794
|
-
idx = subtokens.value.index + subtokens.value[0].length;
|
|
1882
|
+
pushToken( subtokens.value[ 0 ] );
|
|
1883
|
+
idx = subtokens.value.index + subtokens.value[ 0 ].length;
|
|
1795
1884
|
subtokens = iter.next();
|
|
1796
1885
|
if(!subtokens.value) {
|
|
1797
1886
|
const _at = linestring.substring(idx);
|
|
@@ -1821,10 +1910,10 @@ class CodeEditor {
|
|
|
1821
1910
|
// if( block ) continue;
|
|
1822
1911
|
// }
|
|
1823
1912
|
|
|
1824
|
-
if(
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1913
|
+
if( hasCommentIdx != undefined )
|
|
1914
|
+
{
|
|
1915
|
+
pushToken( ogLine.substring( hasCommentIdx ) );
|
|
1916
|
+
}
|
|
1828
1917
|
|
|
1829
1918
|
return this._processTokens( tokensToEvaluate );
|
|
1830
1919
|
}
|
|
@@ -1834,45 +1923,51 @@ class CodeEditor {
|
|
|
1834
1923
|
return kindArray[this.highlight] && kindArray[this.highlight][token] != undefined;
|
|
1835
1924
|
}
|
|
1836
1925
|
|
|
1837
|
-
evaluateToken( token, prev, next ) {
|
|
1926
|
+
evaluateToken( token, prev, next, isLastToken ) {
|
|
1927
|
+
|
|
1928
|
+
const highlight = this.highlight.replace(/\s/g, '').replaceAll("+", "p").toLowerCase();
|
|
1929
|
+
const customStringKeys = Object.assign( {}, this.stringKeys );
|
|
1930
|
+
|
|
1931
|
+
if( highlight == 'cpp' && prev && prev.includes('#') ) // preprocessor code..
|
|
1932
|
+
{
|
|
1933
|
+
customStringKeys['@<'] = '>';
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// Manage strings
|
|
1937
|
+
this._stringEnded = false;
|
|
1938
|
+
if( this._buildingString != undefined )
|
|
1939
|
+
{
|
|
1940
|
+
const idx = Object.values(customStringKeys).indexOf( token );
|
|
1941
|
+
this._stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
|
|
1942
|
+
}
|
|
1943
|
+
else if( customStringKeys[ '@' + token ] )
|
|
1944
|
+
{
|
|
1945
|
+
// Start new string
|
|
1946
|
+
this._buildingString = token;
|
|
1947
|
+
}
|
|
1838
1948
|
|
|
1839
1949
|
if(token == ' ')
|
|
1840
1950
|
{
|
|
1951
|
+
if( this._buildingString != undefined )
|
|
1952
|
+
{
|
|
1953
|
+
this.appendStringToken( token );
|
|
1954
|
+
return "";
|
|
1955
|
+
}
|
|
1841
1956
|
return token;
|
|
1842
1957
|
}
|
|
1843
1958
|
else
|
|
1844
1959
|
{
|
|
1845
|
-
let stringEnded = false;
|
|
1846
|
-
let highlight = this.highlight.replace(/\s/g, '').replaceAll("+", "p").toLowerCase();
|
|
1847
|
-
|
|
1848
1960
|
const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
|
|
1849
1961
|
const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
if( highlight == 'cpp' && prev && prev.includes('#') ) // preprocessor code..
|
|
1853
|
-
{
|
|
1854
|
-
customStringKeys['@<'] = '>';
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
// Manage strings
|
|
1858
|
-
if( this._buildingString != undefined )
|
|
1859
|
-
{
|
|
1860
|
-
const idx = Object.values(customStringKeys).indexOf( token );
|
|
1861
|
-
stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
|
|
1862
|
-
}
|
|
1863
|
-
else if( customStringKeys[ '@' + token ] )
|
|
1864
|
-
{
|
|
1865
|
-
// Start new string
|
|
1866
|
-
this._buildingString = token;
|
|
1867
|
-
}
|
|
1868
|
-
|
|
1962
|
+
|
|
1869
1963
|
let token_classname = "";
|
|
1964
|
+
let discardToken = false;
|
|
1870
1965
|
|
|
1871
1966
|
if( this._buildingBlockComment != undefined )
|
|
1872
1967
|
token_classname = "cm-com";
|
|
1873
1968
|
|
|
1874
1969
|
else if( this._buildingString != undefined )
|
|
1875
|
-
|
|
1970
|
+
discardToken = this.appendStringToken( token );
|
|
1876
1971
|
|
|
1877
1972
|
else if( this._mustHightlightWord( token, this.keywords ) )
|
|
1878
1973
|
token_classname = "cm-kwd";
|
|
@@ -1922,15 +2017,49 @@ class CodeEditor {
|
|
|
1922
2017
|
else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
|
|
1923
2018
|
token_classname = "cm-typ";
|
|
1924
2019
|
|
|
1925
|
-
else if ( token[0] != '@' && next == '(' )
|
|
2020
|
+
else if ( token[ 0 ] != '@' && token[ 0 ] != ',' && next == '(' )
|
|
1926
2021
|
token_classname = "cm-mtd";
|
|
1927
2022
|
|
|
1928
|
-
|
|
2023
|
+
|
|
2024
|
+
// We finished constructing a string
|
|
2025
|
+
if( this._buildingString && ( this._stringEnded || isLastToken ) )
|
|
2026
|
+
{
|
|
2027
|
+
token = this.getCurrentString();
|
|
2028
|
+
token_classname = "cm-str";
|
|
2029
|
+
discardToken = false;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
// Update state
|
|
2033
|
+
this._buildingString = this._stringEnded ? undefined : this._buildingString;
|
|
2034
|
+
|
|
2035
|
+
if( discardToken )
|
|
2036
|
+
return "";
|
|
2037
|
+
|
|
2038
|
+
// No highlighting, no need to put it inside another span..
|
|
2039
|
+
if( !token_classname.length )
|
|
2040
|
+
return token;
|
|
1929
2041
|
|
|
1930
2042
|
return "<span class='" + highlight + " " + token_classname + "'>" + token + "</span>";
|
|
1931
2043
|
}
|
|
1932
2044
|
}
|
|
1933
2045
|
|
|
2046
|
+
appendStringToken( token ) {
|
|
2047
|
+
|
|
2048
|
+
if( !this._pendingString )
|
|
2049
|
+
this._pendingString = "";
|
|
2050
|
+
|
|
2051
|
+
this._pendingString += token;
|
|
2052
|
+
|
|
2053
|
+
return true;
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
getCurrentString() {
|
|
2057
|
+
|
|
2058
|
+
const chars = this._pendingString;
|
|
2059
|
+
delete this._pendingString;
|
|
2060
|
+
return chars;
|
|
2061
|
+
}
|
|
2062
|
+
|
|
1934
2063
|
isCSSClass( token, prev, next ) {
|
|
1935
2064
|
return this.highlight == 'CSS' && prev == '.';
|
|
1936
2065
|
}
|
|
@@ -1980,10 +2109,10 @@ class CodeEditor {
|
|
|
1980
2109
|
this.selection.invertIfNecessary();
|
|
1981
2110
|
|
|
1982
2111
|
// Insert first..
|
|
1983
|
-
this.code.lines[lidx] = [
|
|
1984
|
-
this.code.lines[lidx].slice(0, this.selection.fromX),
|
|
2112
|
+
this.code.lines[ lidx ] = [
|
|
2113
|
+
this.code.lines[ lidx ].slice(0, this.selection.fromX),
|
|
1985
2114
|
key,
|
|
1986
|
-
this.code.lines[lidx].slice(this.selection.fromX)
|
|
2115
|
+
this.code.lines[ lidx ].slice(this.selection.fromX)
|
|
1987
2116
|
].join('');
|
|
1988
2117
|
|
|
1989
2118
|
// Go to the end of the word
|
|
@@ -2000,10 +2129,10 @@ class CodeEditor {
|
|
|
2000
2129
|
}
|
|
2001
2130
|
|
|
2002
2131
|
// Insert the other
|
|
2003
|
-
this.code.lines[lidx] = [
|
|
2004
|
-
this.code.lines[lidx].slice(0, cursor.position),
|
|
2132
|
+
this.code.lines[ lidx ] = [
|
|
2133
|
+
this.code.lines[ lidx ].slice(0, cursor.position),
|
|
2005
2134
|
key,
|
|
2006
|
-
this.code.lines[lidx].slice(cursor.position)
|
|
2135
|
+
this.code.lines[ lidx ].slice(cursor.position)
|
|
2007
2136
|
].join('');
|
|
2008
2137
|
|
|
2009
2138
|
// Recompute and reposition current selection
|
|
@@ -2018,19 +2147,19 @@ class CodeEditor {
|
|
|
2018
2147
|
return true;
|
|
2019
2148
|
}
|
|
2020
2149
|
|
|
2021
|
-
lineUp(cursor, resetLeft) {
|
|
2150
|
+
lineUp( cursor, resetLeft ) {
|
|
2022
2151
|
|
|
2023
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2152
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2024
2153
|
cursor.line--;
|
|
2025
|
-
cursor.line = Math.max(0, cursor.line);
|
|
2026
|
-
this.cursorToTop(cursor, resetLeft);
|
|
2154
|
+
cursor.line = Math.max( 0, cursor.line );
|
|
2155
|
+
this.cursorToTop( cursor, resetLeft );
|
|
2027
2156
|
}
|
|
2028
2157
|
|
|
2029
|
-
lineDown(cursor, resetLeft) {
|
|
2158
|
+
lineDown( cursor, resetLeft ) {
|
|
2030
2159
|
|
|
2031
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2160
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2032
2161
|
cursor.line++;
|
|
2033
|
-
this.cursorToBottom(cursor, resetLeft);
|
|
2162
|
+
this.cursorToBottom( cursor, resetLeft );
|
|
2034
2163
|
}
|
|
2035
2164
|
|
|
2036
2165
|
restartBlink() {
|
|
@@ -2057,40 +2186,40 @@ class CodeEditor {
|
|
|
2057
2186
|
this.selections.classList.add('show');
|
|
2058
2187
|
|
|
2059
2188
|
// Create new selection instance
|
|
2060
|
-
this.selection = new
|
|
2189
|
+
this.selection = new CodeSelection(this, cursor.position, cursor.line);
|
|
2061
2190
|
}
|
|
2062
2191
|
|
|
2063
2192
|
deleteSelection( cursor ) {
|
|
2064
2193
|
|
|
2065
2194
|
// I think it's not necessary but...
|
|
2066
|
-
if(this.disableEdition)
|
|
2195
|
+
if( this.disableEdition )
|
|
2067
2196
|
return;
|
|
2068
2197
|
|
|
2069
2198
|
// Some selections don't depend on mouse up..
|
|
2070
|
-
if(this.selection) this.selection.invertIfNecessary();
|
|
2199
|
+
if( this.selection ) this.selection.invertIfNecessary();
|
|
2071
2200
|
|
|
2072
2201
|
const separator = "_NEWLINE_";
|
|
2073
|
-
let code = this.code.lines.join(separator);
|
|
2202
|
+
let code = this.code.lines.join( separator );
|
|
2074
2203
|
|
|
2075
2204
|
// Get linear start index
|
|
2076
2205
|
let index = 0;
|
|
2077
2206
|
for(let i = 0; i <= this.selection.fromY; i++)
|
|
2078
|
-
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[i].length);
|
|
2207
|
+
index += (i == this.selection.fromY ? this.selection.fromX : this.code.lines[ i ].length);
|
|
2079
2208
|
|
|
2080
2209
|
index += this.selection.fromY * separator.length;
|
|
2081
2210
|
|
|
2082
2211
|
const num_chars = this.selection.chars + (this.selection.toY - this.selection.fromY) * separator.length;
|
|
2083
|
-
const pre = code.slice(0, index);
|
|
2084
|
-
const post = code.slice(index + num_chars);
|
|
2212
|
+
const pre = code.slice( 0, index );
|
|
2213
|
+
const post = code.slice( index + num_chars );
|
|
2085
2214
|
|
|
2086
|
-
this.code.lines = (pre + post).split(separator);
|
|
2087
|
-
this.processLines(
|
|
2215
|
+
this.code.lines = ( pre + post ).split( separator );
|
|
2216
|
+
this.processLines();
|
|
2088
2217
|
|
|
2089
|
-
this.cursorToLine(cursor, this.selection.fromY, true);
|
|
2090
|
-
this.cursorToPosition(cursor, this.selection.fromX);
|
|
2218
|
+
this.cursorToLine( cursor, this.selection.fromY, true );
|
|
2219
|
+
this.cursorToPosition( cursor, this.selection.fromX );
|
|
2220
|
+
|
|
2091
2221
|
this.endSelection();
|
|
2092
|
-
|
|
2093
|
-
this._refreshCodeInfo(cursor.line, cursor.position);
|
|
2222
|
+
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
2094
2223
|
}
|
|
2095
2224
|
|
|
2096
2225
|
endSelection() {
|
|
@@ -2102,10 +2231,10 @@ class CodeEditor {
|
|
|
2102
2231
|
|
|
2103
2232
|
cursorToRight( key, cursor ) {
|
|
2104
2233
|
|
|
2105
|
-
if(!key) return;
|
|
2106
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2234
|
+
if( !key ) return;
|
|
2235
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2107
2236
|
cursor._left += this.charWidth;
|
|
2108
|
-
cursor.style.left = "calc(" +
|
|
2237
|
+
cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
|
|
2109
2238
|
cursor.position++;
|
|
2110
2239
|
this.restartBlink();
|
|
2111
2240
|
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
@@ -2113,7 +2242,7 @@ class CodeEditor {
|
|
|
2113
2242
|
// Add horizontal scroll
|
|
2114
2243
|
|
|
2115
2244
|
doAsync(() => {
|
|
2116
|
-
var last_char = ((this.
|
|
2245
|
+
var last_char = ((this.codeScroller.clientWidth + this.getScrollLeft()) / this.charWidth)|0;
|
|
2117
2246
|
if( cursor.position >= last_char )
|
|
2118
2247
|
this.setScrollLeft( this.getScrollLeft() + this.charWidth );
|
|
2119
2248
|
});
|
|
@@ -2122,10 +2251,10 @@ class CodeEditor {
|
|
|
2122
2251
|
cursorToLeft( key, cursor ) {
|
|
2123
2252
|
|
|
2124
2253
|
if(!key) return;
|
|
2125
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2254
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2126
2255
|
cursor._left -= this.charWidth;
|
|
2127
2256
|
cursor._left = Math.max(cursor._left, 0);
|
|
2128
|
-
cursor.style.left = "calc(" +
|
|
2257
|
+
cursor.style.left = "calc( " + cursor._left + "px + " + this.xPadding + " )";
|
|
2129
2258
|
cursor.position--;
|
|
2130
2259
|
cursor.position = Math.max(cursor.position, 0);
|
|
2131
2260
|
this.restartBlink();
|
|
@@ -2142,10 +2271,10 @@ class CodeEditor {
|
|
|
2142
2271
|
|
|
2143
2272
|
cursorToTop( cursor, resetLeft = false ) {
|
|
2144
2273
|
|
|
2145
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2274
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2146
2275
|
cursor._top -= this.lineHeight;
|
|
2147
|
-
cursor._top = Math.max(cursor._top,
|
|
2148
|
-
cursor.style.top = "calc(" +
|
|
2276
|
+
cursor._top = Math.max(cursor._top, 0);
|
|
2277
|
+
cursor.style.top = "calc(" + cursor._top + "px)";
|
|
2149
2278
|
this.restartBlink();
|
|
2150
2279
|
|
|
2151
2280
|
if(resetLeft)
|
|
@@ -2162,9 +2291,9 @@ class CodeEditor {
|
|
|
2162
2291
|
|
|
2163
2292
|
cursorToBottom( cursor, resetLeft = false ) {
|
|
2164
2293
|
|
|
2165
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2294
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2166
2295
|
cursor._top += this.lineHeight;
|
|
2167
|
-
cursor.style.top = "calc(" +
|
|
2296
|
+
cursor.style.top = "calc(" + cursor._top + "px)";
|
|
2168
2297
|
this.restartBlink();
|
|
2169
2298
|
|
|
2170
2299
|
if(resetLeft)
|
|
@@ -2173,7 +2302,7 @@ class CodeEditor {
|
|
|
2173
2302
|
this._refreshCodeInfo( cursor.line, cursor.position );
|
|
2174
2303
|
|
|
2175
2304
|
doAsync(() => {
|
|
2176
|
-
var last_line = ((this.
|
|
2305
|
+
var last_line = ((this.codeScroller.offsetHeight + this.getScrollTop()) / this.lineHeight)|0;
|
|
2177
2306
|
if( cursor.line >= last_line )
|
|
2178
2307
|
this.setScrollTop( this.getScrollTop() + this.lineHeight );
|
|
2179
2308
|
});
|
|
@@ -2181,7 +2310,7 @@ class CodeEditor {
|
|
|
2181
2310
|
|
|
2182
2311
|
cursorToString( cursor, text, reverse ) {
|
|
2183
2312
|
|
|
2184
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2313
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2185
2314
|
for( let char of text )
|
|
2186
2315
|
reverse ? this.cursorToLeft(char) : this.cursorToRight(char);
|
|
2187
2316
|
}
|
|
@@ -2190,20 +2319,20 @@ class CodeEditor {
|
|
|
2190
2319
|
|
|
2191
2320
|
cursor.position = position;
|
|
2192
2321
|
cursor._left = position * this.charWidth;
|
|
2193
|
-
cursor.style.left = "calc(" +
|
|
2322
|
+
cursor.style.left = "calc(" + cursor._left + "px + " + this.xPadding + ")";
|
|
2194
2323
|
}
|
|
2195
2324
|
|
|
2196
2325
|
cursorToLine( cursor, line, resetLeft = false ) {
|
|
2197
2326
|
|
|
2198
2327
|
cursor.line = line;
|
|
2199
|
-
cursor._top =
|
|
2200
|
-
cursor.style.top =
|
|
2328
|
+
cursor._top = this.lineHeight * line;
|
|
2329
|
+
cursor.style.top = cursor._top + "px";
|
|
2201
2330
|
if(resetLeft) this.resetCursorPos( CodeEditor.CURSOR_LEFT, cursor );
|
|
2202
2331
|
}
|
|
2203
2332
|
|
|
2204
2333
|
saveCursor( cursor, state = {} ) {
|
|
2205
2334
|
|
|
2206
|
-
var cursor = cursor ?? this.cursors.children[0];
|
|
2335
|
+
var cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2207
2336
|
state.top = cursor._top;
|
|
2208
2337
|
state.left = cursor._left;
|
|
2209
2338
|
state.line = cursor.line;
|
|
@@ -2213,19 +2342,19 @@ class CodeEditor {
|
|
|
2213
2342
|
|
|
2214
2343
|
restoreCursor( cursor, state ) {
|
|
2215
2344
|
|
|
2216
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2345
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2217
2346
|
cursor.line = state.line ?? 0;
|
|
2218
2347
|
cursor.position = state.charPos ?? 0;
|
|
2219
2348
|
|
|
2220
2349
|
cursor._left = state.left ?? 0;
|
|
2221
2350
|
cursor.style.left = "calc(" + (cursor._left - this.getScrollLeft()) + "px + " + this.xPadding + ")";
|
|
2222
|
-
cursor._top = state.top ??
|
|
2351
|
+
cursor._top = state.top ?? 0;
|
|
2223
2352
|
cursor.style.top = "calc(" + (cursor._top - this.getScrollTop()) + "px)";
|
|
2224
2353
|
}
|
|
2225
2354
|
|
|
2226
2355
|
resetCursorPos( flag, cursor ) {
|
|
2227
2356
|
|
|
2228
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2357
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2229
2358
|
|
|
2230
2359
|
if( flag & CodeEditor.CURSOR_LEFT )
|
|
2231
2360
|
{
|
|
@@ -2236,7 +2365,7 @@ class CodeEditor {
|
|
|
2236
2365
|
|
|
2237
2366
|
if( flag & CodeEditor.CURSOR_TOP )
|
|
2238
2367
|
{
|
|
2239
|
-
cursor._top =
|
|
2368
|
+
cursor._top = 0;
|
|
2240
2369
|
cursor.style.top = (cursor._top - this.getScrollTop()) + "px";
|
|
2241
2370
|
cursor.line = 0;
|
|
2242
2371
|
}
|
|
@@ -2261,160 +2390,141 @@ class CodeEditor {
|
|
|
2261
2390
|
|
|
2262
2391
|
getScrollLeft() {
|
|
2263
2392
|
|
|
2264
|
-
if(!this.
|
|
2265
|
-
return this.
|
|
2393
|
+
if(!this.codeScroller) return 0;
|
|
2394
|
+
return this.codeScroller.scrollLeft;
|
|
2266
2395
|
}
|
|
2267
2396
|
|
|
2268
2397
|
getScrollTop() {
|
|
2269
2398
|
|
|
2270
|
-
if(!this.
|
|
2271
|
-
return this.
|
|
2399
|
+
if(!this.codeScroller) return 0;
|
|
2400
|
+
return this.codeScroller.scrollTop;
|
|
2272
2401
|
}
|
|
2273
2402
|
|
|
2274
|
-
setScrollLeft( value
|
|
2275
|
-
|
|
2276
|
-
if(!this.code) return;
|
|
2403
|
+
setScrollLeft( value ) {
|
|
2277
2404
|
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
value = LX.UTILS.clamp( value, 0, maxWidth );
|
|
2282
|
-
|
|
2283
|
-
this.code.style.marginLeft = (-value) + "px";
|
|
2284
|
-
|
|
2285
|
-
if( !keepScrollBar )
|
|
2286
|
-
{
|
|
2287
|
-
const scrollWidth = this.hScrollbarThumb.parentElement.offsetWidth;
|
|
2288
|
-
const scrollBarWidth = this.hScrollbarThumb.offsetWidth;
|
|
2289
|
-
this.setScrollBarValue( ( scrollWidth - scrollBarWidth ) * ( value / maxWidth ), 'horizontal' );
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
// Update cursor
|
|
2293
|
-
var cursor = this.cursors.children[0];
|
|
2294
|
-
cursor.style.left = "calc( " + (cursor._left - value) + "px + " + this.xPadding + ")";
|
|
2295
|
-
|
|
2296
|
-
// Update selection
|
|
2297
|
-
for( let s of this.selections.childNodes ) {
|
|
2298
|
-
s.style.left = "calc( " + (s._left - value) + "px + " + this.xPadding + ")";
|
|
2299
|
-
}
|
|
2300
|
-
|
|
2301
|
-
this.code.customScroll.x = value;
|
|
2405
|
+
if(!this.codeScroller) return;
|
|
2406
|
+
this.codeScroller.scrollLeft = value;
|
|
2407
|
+
this.setScrollBarValue( 'horizontal', 0 );
|
|
2302
2408
|
}
|
|
2303
2409
|
|
|
2304
|
-
setScrollTop( value
|
|
2305
|
-
|
|
2306
|
-
if(!this.code) return;
|
|
2307
|
-
|
|
2308
|
-
const realClientHeight = this.code.parentElement.offsetHeight - 36;
|
|
2309
|
-
const maxHeight = Math.max( this.code.scrollHeight - realClientHeight, 0 );
|
|
2310
|
-
|
|
2311
|
-
value = LX.UTILS.clamp( value, 0, maxHeight );
|
|
2410
|
+
setScrollTop( value ) {
|
|
2312
2411
|
|
|
2313
|
-
this.
|
|
2314
|
-
|
|
2315
|
-
this.
|
|
2316
|
-
|
|
2317
|
-
if( !keepScrollBar )
|
|
2318
|
-
{
|
|
2319
|
-
const scrollHeight = this.scrollbarThumb.parentElement.offsetHeight;
|
|
2320
|
-
const scrollBarHeight = this.scrollbarThumb.offsetHeight;
|
|
2321
|
-
// this.setScrollBarValue( ( scrollHeight - scrollBarHeight ) * ( value / maxHeight ) )
|
|
2322
|
-
const firstLineInViewport = (this.getScrollTop() / this.lineHeight)|0;
|
|
2323
|
-
this.setScrollBarValue( ( scrollHeight - scrollBarHeight ) * ( firstLineInViewport / this.code.lines.length ) )
|
|
2324
|
-
}
|
|
2325
|
-
|
|
2326
|
-
// Update cursor
|
|
2327
|
-
var cursor = this.cursors.children[0];
|
|
2328
|
-
cursor.style.top = (cursor._top - value) + "px";
|
|
2329
|
-
|
|
2330
|
-
// Update selection
|
|
2331
|
-
for( let s of this.selections.childNodes ) {
|
|
2332
|
-
s.style.top = (s._top - value) + "px";
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2335
|
-
this.code.customScroll.y = value;
|
|
2412
|
+
if(!this.codeScroller) return;
|
|
2413
|
+
this.codeScroller.scrollTop = value;
|
|
2414
|
+
this.setScrollBarValue( 'vertical' );
|
|
2336
2415
|
}
|
|
2337
2416
|
|
|
2338
|
-
resizeScrollBars() {
|
|
2339
|
-
|
|
2340
|
-
const numViewportLines = Math.floor( (this.code.parentElement.offsetHeight - 36) / this.lineHeight );
|
|
2417
|
+
resizeScrollBars( numViewportLines ) {
|
|
2341
2418
|
|
|
2342
2419
|
if( numViewportLines > this.code.lines.length )
|
|
2343
2420
|
{
|
|
2344
|
-
this.
|
|
2345
|
-
this.
|
|
2421
|
+
this.codeScroller.classList.remove( 'with-vscrollbar' );
|
|
2422
|
+
this.vScrollbar.root.classList.add( 'scrollbar-unused' );
|
|
2346
2423
|
}
|
|
2347
2424
|
else
|
|
2348
2425
|
{
|
|
2349
|
-
this.
|
|
2350
|
-
this.
|
|
2351
|
-
this.
|
|
2352
|
-
this.
|
|
2426
|
+
this.codeScroller.classList.add( 'with-vscrollbar' );
|
|
2427
|
+
this.vScrollbar.root.classList.remove( 'scrollbar-unused' );
|
|
2428
|
+
this.vScrollbar.thumb.size = (numViewportLines / this.code.lines.length);
|
|
2429
|
+
this.vScrollbar.thumb.style.height = (this.vScrollbar.thumb.size * 100.0) + "%";
|
|
2353
2430
|
}
|
|
2354
2431
|
|
|
2355
|
-
const numViewportChars = Math.floor( this.
|
|
2432
|
+
const numViewportChars = Math.floor( this.codeScroller.clientWidth / this.charWidth );
|
|
2356
2433
|
const line_lengths = this.code.lines.map( value => value.length );
|
|
2357
2434
|
const maxLineLength = Math.max(...line_lengths);
|
|
2358
2435
|
|
|
2359
2436
|
if( numViewportChars > maxLineLength )
|
|
2360
2437
|
{
|
|
2361
|
-
this.
|
|
2362
|
-
this.
|
|
2438
|
+
this.codeScroller.classList.remove( 'with-hscrollbar' );
|
|
2439
|
+
this.hScrollbar.root.classList.add( 'scrollbar-unused' );
|
|
2363
2440
|
}
|
|
2364
2441
|
else
|
|
2365
2442
|
{
|
|
2366
|
-
this.
|
|
2367
|
-
this.
|
|
2368
|
-
this.
|
|
2369
|
-
this.
|
|
2443
|
+
this.codeScroller.classList.add( 'with-hscrollbar' );
|
|
2444
|
+
this.hScrollbar.root.classList.remove( 'scrollbar-unused' );
|
|
2445
|
+
this.hScrollbar.thumb.size = (numViewportChars / maxLineLength);
|
|
2446
|
+
this.hScrollbar.thumb.style.width = (this.hScrollbar.thumb.size * 100.0) + "%";
|
|
2370
2447
|
}
|
|
2371
2448
|
}
|
|
2372
2449
|
|
|
2373
|
-
setScrollBarValue(
|
|
2450
|
+
setScrollBarValue( type = 'vertical', value ) {
|
|
2374
2451
|
|
|
2375
2452
|
if( type == 'vertical' )
|
|
2376
2453
|
{
|
|
2377
|
-
const
|
|
2378
|
-
const
|
|
2454
|
+
const scrollBarHeight = this.vScrollbar.thumb.parentElement.offsetHeight;
|
|
2455
|
+
const scrollThumbHeight = this.vScrollbar.thumb.offsetHeight;
|
|
2379
2456
|
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
this.
|
|
2457
|
+
const scrollHeight = this.codeScroller.scrollHeight - this.codeScroller.clientHeight;
|
|
2458
|
+
const currentScroll = this.codeScroller.scrollTop;
|
|
2459
|
+
|
|
2460
|
+
this.vScrollbar.thumb._top = ( currentScroll / scrollHeight ) * ( scrollBarHeight - scrollThumbHeight );
|
|
2461
|
+
this.vScrollbar.thumb.style.top = this.vScrollbar.thumb._top + "px";
|
|
2384
2462
|
}
|
|
2385
2463
|
else
|
|
2386
2464
|
{
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
this.
|
|
2393
|
-
|
|
2465
|
+
this.codeScroller.scrollLeft += value;
|
|
2466
|
+
|
|
2467
|
+
const scrollBarWidth = this.hScrollbar.thumb.parentElement.offsetWidth;
|
|
2468
|
+
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
2469
|
+
|
|
2470
|
+
const scrollWidth = this.codeScroller.scrollWidth - this.codeScroller.clientWidth;
|
|
2471
|
+
const currentScroll = this.codeScroller.scrollLeft;
|
|
2472
|
+
|
|
2473
|
+
this.hScrollbar.thumb._left = ( currentScroll / scrollWidth ) * ( scrollBarWidth - scrollThumbWidth );
|
|
2474
|
+
this.hScrollbar.thumb.style.left = this.hScrollbar.thumb._left + "px";
|
|
2394
2475
|
}
|
|
2395
2476
|
}
|
|
2396
2477
|
|
|
2397
|
-
|
|
2478
|
+
updateHorizontalScrollFromScrollBar( value ) {
|
|
2479
|
+
|
|
2480
|
+
value = this.hScrollbar.thumb._left - value;
|
|
2481
|
+
|
|
2482
|
+
// Move scrollbar thumb
|
|
2483
|
+
|
|
2484
|
+
const scrollBarWidth = this.hScrollbar.thumb.parentElement.offsetWidth;
|
|
2485
|
+
const scrollThumbWidth = this.hScrollbar.thumb.offsetWidth;
|
|
2486
|
+
|
|
2487
|
+
this.hScrollbar.thumb._left = LX.UTILS.clamp( value, 0, ( scrollBarWidth - scrollThumbWidth ) );
|
|
2488
|
+
this.hScrollbar.thumb.style.left = this.hScrollbar.thumb._left + "px";
|
|
2398
2489
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2490
|
+
// Scroll code
|
|
2491
|
+
|
|
2492
|
+
const scrollWidth = this.codeScroller.scrollWidth - this.codeScroller.clientWidth;
|
|
2493
|
+
const currentScroll = (this.hScrollbar.thumb._left * scrollWidth) / ( scrollBarWidth - scrollThumbWidth );
|
|
2494
|
+
this.codeScroller.scrollLeft = currentScroll;
|
|
2495
|
+
|
|
2496
|
+
|
|
2497
|
+
this._discardScroll = true;
|
|
2401
2498
|
}
|
|
2402
2499
|
|
|
2403
|
-
|
|
2500
|
+
updateVerticalScrollFromScrollBar( value ) {
|
|
2501
|
+
|
|
2502
|
+
value = this.vScrollbar.thumb._top - value;
|
|
2503
|
+
|
|
2504
|
+
// Move scrollbar thumb
|
|
2505
|
+
|
|
2506
|
+
const scrollBarHeight = this.vScrollbar.thumb.parentElement.offsetHeight;
|
|
2507
|
+
const scrollThumbHeight = this.vScrollbar.thumb.offsetHeight;
|
|
2508
|
+
|
|
2509
|
+
this.vScrollbar.thumb._top = LX.UTILS.clamp( value, 0, ( scrollBarHeight - scrollThumbHeight ) );
|
|
2510
|
+
this.vScrollbar.thumb.style.top = this.vScrollbar.thumb._top + "px";
|
|
2511
|
+
|
|
2512
|
+
// Scroll code
|
|
2404
2513
|
|
|
2405
|
-
this.
|
|
2406
|
-
|
|
2514
|
+
const scrollHeight = this.codeScroller.scrollHeight - this.codeScroller.clientHeight;
|
|
2515
|
+
const currentScroll = (this.vScrollbar.thumb._top * scrollHeight) / ( scrollBarHeight - scrollThumbHeight );
|
|
2516
|
+
this.codeScroller.scrollTop = currentScroll;
|
|
2407
2517
|
}
|
|
2408
2518
|
|
|
2409
2519
|
getCharAtPos( cursor, offset = 0 ) {
|
|
2410
2520
|
|
|
2411
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2412
|
-
return this.code.lines[cursor.line][cursor.position + offset];
|
|
2521
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2522
|
+
return this.code.lines[ cursor.line ][cursor.position + offset];
|
|
2413
2523
|
}
|
|
2414
2524
|
|
|
2415
2525
|
getWordAtPos( cursor, offset = 0 ) {
|
|
2416
2526
|
|
|
2417
|
-
cursor = cursor ?? this.cursors.children[0];
|
|
2527
|
+
cursor = cursor ?? this.cursors.children[ 0 ];
|
|
2418
2528
|
const col = cursor.line;
|
|
2419
2529
|
const words = this.code.lines[col];
|
|
2420
2530
|
|
|
@@ -2449,7 +2559,7 @@ class CodeEditor {
|
|
|
2449
2559
|
var rect = test.getBoundingClientRect();
|
|
2450
2560
|
deleteElement( test );
|
|
2451
2561
|
const bb = [Math.floor(rect.width), Math.floor(rect.height)];
|
|
2452
|
-
return get_bb ? bb : bb[0];
|
|
2562
|
+
return get_bb ? bb : bb[ 0 ];
|
|
2453
2563
|
}
|
|
2454
2564
|
|
|
2455
2565
|
measureString( str ) {
|
|
@@ -2462,10 +2572,10 @@ class CodeEditor {
|
|
|
2462
2572
|
var script = document.createElement('script');
|
|
2463
2573
|
script.type = 'module';
|
|
2464
2574
|
script.innerHTML = code;
|
|
2465
|
-
// script.src = url[i] + ( version ? "?version=" + version : "" );
|
|
2575
|
+
// script.src = url[ i ] + ( version ? "?version=" + version : "" );
|
|
2466
2576
|
script.async = false;
|
|
2467
2577
|
// script.onload = function(e) { };
|
|
2468
|
-
document.getElementsByTagName('head')[0].appendChild(script);
|
|
2578
|
+
document.getElementsByTagName('head')[ 0 ].appendChild(script);
|
|
2469
2579
|
}
|
|
2470
2580
|
|
|
2471
2581
|
toJSONFormat( text ) {
|
|
@@ -2473,19 +2583,19 @@ class CodeEditor {
|
|
|
2473
2583
|
let params = text.split(":");
|
|
2474
2584
|
|
|
2475
2585
|
for(let i = 0; i < params.length; i++) {
|
|
2476
|
-
let key = params[i].split(',');
|
|
2586
|
+
let key = params[ i ].split(',');
|
|
2477
2587
|
if(key.length > 1) {
|
|
2478
2588
|
if(key[key.length-1].includes("]"))
|
|
2479
2589
|
continue;
|
|
2480
2590
|
key = key[key.length-1];
|
|
2481
2591
|
}
|
|
2482
|
-
else if(key[0].includes("}"))
|
|
2592
|
+
else if(key[ 0 ].includes("}"))
|
|
2483
2593
|
continue;
|
|
2484
2594
|
else
|
|
2485
|
-
key = key[0];
|
|
2595
|
+
key = key[ 0 ];
|
|
2486
2596
|
key = key.replaceAll(/[{}\n\r]/g,"").replaceAll(" ","")
|
|
2487
|
-
if(key[0] != '"' && key[key.length - 1] != '"') {
|
|
2488
|
-
params[i] = params[i].replace(key, '"' + key + '"');
|
|
2597
|
+
if(key[ 0 ] != '"' && key[key.length - 1] != '"') {
|
|
2598
|
+
params[ i ] = params[ i ].replace(key, '"' + key + '"');
|
|
2489
2599
|
}
|
|
2490
2600
|
}
|
|
2491
2601
|
|
|
@@ -2622,7 +2732,7 @@ class CodeEditor {
|
|
|
2622
2732
|
|
|
2623
2733
|
for( let i = 0; i < this.autocomplete.childElementCount; ++i )
|
|
2624
2734
|
{
|
|
2625
|
-
const child = this.autocomplete.childNodes[i];
|
|
2735
|
+
const child = this.autocomplete.childNodes[ i ];
|
|
2626
2736
|
if( child.classList.contains('selected') )
|
|
2627
2737
|
{
|
|
2628
2738
|
var word = "";
|