lexgui 8.3.0 → 8.3.1
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/core/Namespace.js +1 -1
- package/build/core/Namespace.js.map +1 -1
- package/build/extensions/CodeEditor.d.ts +2 -1
- package/build/extensions/CodeEditor.js +233 -29
- package/build/extensions/CodeEditor.js.map +1 -1
- package/build/extensions/Timeline.js.map +1 -1
- package/build/extensions/VideoEditor.js +1021 -1022
- package/build/extensions/VideoEditor.js.map +1 -1
- package/build/lexgui.all.js +252 -33
- package/build/lexgui.all.js.map +1 -1
- package/build/lexgui.all.min.js +1 -1
- package/build/lexgui.all.module.js +252 -33
- package/build/lexgui.all.module.js.map +1 -1
- package/build/lexgui.all.module.min.js +1 -1
- package/build/lexgui.css +12 -2
- package/build/lexgui.js +251 -31
- package/build/lexgui.js.map +1 -1
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +251 -31
- package/build/lexgui.module.js.map +1 -1
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +13 -1
- package/demo.js +3 -2
- package/examples/code-editor.html +9 -0
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ const g$2 = globalThis;
|
|
|
12
12
|
let LX = g$2.LX;
|
|
13
13
|
if (!LX) {
|
|
14
14
|
LX = {
|
|
15
|
-
version: '8.3.
|
|
15
|
+
version: '8.3.1',
|
|
16
16
|
ready: false,
|
|
17
17
|
extensions: [], // Store extensions used
|
|
18
18
|
extraCommandbarEntries: [], // User specific entries for command bar
|
|
@@ -9795,9 +9795,25 @@ LX.toTitleCase = toTitleCase;
|
|
|
9795
9795
|
* @param {String} str
|
|
9796
9796
|
*/
|
|
9797
9797
|
function toKebabCase(str) {
|
|
9798
|
-
return str
|
|
9798
|
+
return str
|
|
9799
|
+
.replace(/([A-Z])/g, '-$1')
|
|
9800
|
+
.replace(/[\s_]+/g, '-')
|
|
9801
|
+
.replace(/^-/, '')
|
|
9802
|
+
.toLowerCase();
|
|
9799
9803
|
}
|
|
9800
9804
|
LX.toKebabCase = toKebabCase;
|
|
9805
|
+
/**
|
|
9806
|
+
* @method toSnakeCase
|
|
9807
|
+
* @param {String} str
|
|
9808
|
+
*/
|
|
9809
|
+
function toSnakeCase(str) {
|
|
9810
|
+
return str
|
|
9811
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
9812
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
|
|
9813
|
+
.replace(/[\s\-]+/g, '_')
|
|
9814
|
+
.toLowerCase();
|
|
9815
|
+
}
|
|
9816
|
+
LX.toSnakeCase = toSnakeCase;
|
|
9801
9817
|
/**
|
|
9802
9818
|
* @method getSupportedDOMName
|
|
9803
9819
|
* @description Convert a text string to a valid DOM name
|
|
@@ -15591,6 +15607,21 @@ class CodeEditor {
|
|
|
15591
15607
|
this._inputArea.addEventListener('blur', () => this._setFocused(false));
|
|
15592
15608
|
this.codeArea.root.addEventListener('mousedown', this._onMouseDown.bind(this));
|
|
15593
15609
|
this.codeArea.root.addEventListener('contextmenu', this._onMouseDown.bind(this));
|
|
15610
|
+
this.codeArea.root.addEventListener('mouseover', (e) => {
|
|
15611
|
+
const link = e.target.closest('.code-link');
|
|
15612
|
+
if (link && e.ctrlKey)
|
|
15613
|
+
link.classList.add('hovered');
|
|
15614
|
+
});
|
|
15615
|
+
this.codeArea.root.addEventListener('mouseout', (e) => {
|
|
15616
|
+
const link = e.target.closest('.code-link');
|
|
15617
|
+
if (link)
|
|
15618
|
+
link.classList.remove('hovered');
|
|
15619
|
+
});
|
|
15620
|
+
this.codeArea.root.addEventListener('mousemove', (e) => {
|
|
15621
|
+
const link = e.target.closest('.code-link');
|
|
15622
|
+
if (link)
|
|
15623
|
+
link.classList.toggle('hovered', e.ctrlKey);
|
|
15624
|
+
});
|
|
15594
15625
|
// Bottom status panel
|
|
15595
15626
|
this.statusPanel = this._createStatusPanel(options);
|
|
15596
15627
|
if (this.statusPanel) {
|
|
@@ -15678,18 +15709,148 @@ class CodeEditor {
|
|
|
15678
15709
|
}
|
|
15679
15710
|
}
|
|
15680
15711
|
;
|
|
15681
|
-
setText(text) {
|
|
15712
|
+
setText(text, language, detectLang = false) {
|
|
15682
15713
|
if (!this.currentTab)
|
|
15683
15714
|
return;
|
|
15684
15715
|
this.doc.setText(text);
|
|
15685
15716
|
this.cursorSet.set(0, 0);
|
|
15686
15717
|
this.undoManager.clear();
|
|
15687
15718
|
this._lineStates = [];
|
|
15719
|
+
if (language) {
|
|
15720
|
+
this.setLanguage(language);
|
|
15721
|
+
}
|
|
15722
|
+
else if (detectLang) {
|
|
15723
|
+
const detected = this._detectLanguage(text);
|
|
15724
|
+
if (detected)
|
|
15725
|
+
this.setLanguage(detected);
|
|
15726
|
+
}
|
|
15688
15727
|
this._renderAllLines();
|
|
15689
15728
|
this._renderCursors();
|
|
15690
15729
|
this._renderSelections();
|
|
15691
15730
|
this.resize(true);
|
|
15692
15731
|
}
|
|
15732
|
+
_detectLanguage(text) {
|
|
15733
|
+
const scores = {};
|
|
15734
|
+
const add = (lang, pts) => { scores[lang] = (scores[lang] ?? 0) + pts; };
|
|
15735
|
+
// Score using reservedWords from each registered language
|
|
15736
|
+
const textWords = new Set(text.match(/\b\w+\b/g) ?? []);
|
|
15737
|
+
const totalWords = Math.max(textWords.size, 1);
|
|
15738
|
+
for (const langName of Tokenizer.getRegisteredLanguages()) {
|
|
15739
|
+
const langDef = Tokenizer.getLanguage(langName);
|
|
15740
|
+
if (!langDef?.reservedWords?.length)
|
|
15741
|
+
continue;
|
|
15742
|
+
let hits = 0;
|
|
15743
|
+
for (const word of langDef.reservedWords) {
|
|
15744
|
+
if (textWords.has(word))
|
|
15745
|
+
hits++;
|
|
15746
|
+
}
|
|
15747
|
+
if (hits > 0) {
|
|
15748
|
+
const vocabRatio = hits / langDef.reservedWords.length;
|
|
15749
|
+
const textRatio = hits / totalWords;
|
|
15750
|
+
add(langName, Math.round((vocabRatio + textRatio) * 40));
|
|
15751
|
+
}
|
|
15752
|
+
}
|
|
15753
|
+
// Add scores based on "important" structural words only
|
|
15754
|
+
// HTML
|
|
15755
|
+
if (/<!DOCTYPE\s+html/i.test(text))
|
|
15756
|
+
add('HTML', 20);
|
|
15757
|
+
if (/<html[\s>]/i.test(text))
|
|
15758
|
+
add('HTML', 15);
|
|
15759
|
+
if (/<\/?(div|span|body|head|script|style|meta)\b/i.test(text))
|
|
15760
|
+
add('HTML', 8);
|
|
15761
|
+
// JSON — must come before JS checks (starts with { or [)
|
|
15762
|
+
if (/^\s*[\[{]/.test(text) && /"\s*:\s*/.test(text) && !/function|=>|const|var|let/.test(text))
|
|
15763
|
+
add('JSON', 15);
|
|
15764
|
+
// CSS
|
|
15765
|
+
if (/[\w-]+\s*:\s*[\w#\d"'(]+.*;/.test(text) && /[{}]/.test(text) && !/<\w+/.test(text))
|
|
15766
|
+
add('CSS', 10);
|
|
15767
|
+
if (/@(media|keyframes|import|charset|font-face)\b/.test(text))
|
|
15768
|
+
add('CSS', 15);
|
|
15769
|
+
// WGSL
|
|
15770
|
+
if (/@(vertex|fragment|compute|group|binding|builtin)\b/.test(text))
|
|
15771
|
+
add('WGSL', 20);
|
|
15772
|
+
if (/\bfn\s+\w+\s*\(/.test(text) && /\bvar\b/.test(text))
|
|
15773
|
+
add('WGSL', 10);
|
|
15774
|
+
if (/\b(vec2f|vec3f|vec4f|mat4x4f|f32|u32|i32)\b/.test(text))
|
|
15775
|
+
add('WGSL', 12);
|
|
15776
|
+
// GLSL
|
|
15777
|
+
if (/\b(gl_Position|gl_FragColor|gl_FragCoord)\b/.test(text))
|
|
15778
|
+
add('GLSL', 20);
|
|
15779
|
+
if (/\b(uniform|varying|attribute)\s+\w/.test(text))
|
|
15780
|
+
add('GLSL', 10);
|
|
15781
|
+
if (/\b(vec2|vec3|vec4|mat4|sampler2D)\b/.test(text) && !/vec2f|vec3f/.test(text))
|
|
15782
|
+
add('GLSL', 8);
|
|
15783
|
+
// HLSL
|
|
15784
|
+
if (/\b(SV_Position|SV_Target|SV_Depth)\b/.test(text))
|
|
15785
|
+
add('HLSL', 20);
|
|
15786
|
+
if (/\b(cbuffer|tbuffer|float4|float3|float2|Texture2D)\b/.test(text))
|
|
15787
|
+
add('HLSL', 12);
|
|
15788
|
+
// Python
|
|
15789
|
+
if (/^\s*def\s+\w+\s*\(/m.test(text))
|
|
15790
|
+
add('Python', 15);
|
|
15791
|
+
if (/^\s*import\s+\w/m.test(text) && !/\bfrom\s+['"]/.test(text))
|
|
15792
|
+
add('Python', 8);
|
|
15793
|
+
if (/^\s*class\s+\w+(\s*\(.*\))?:/m.test(text))
|
|
15794
|
+
add('Python', 10);
|
|
15795
|
+
if (/\bprint\s*\(/.test(text) && /:\s*$/.test(text))
|
|
15796
|
+
add('Python', 5);
|
|
15797
|
+
if (/elif\b|lambda\b|self\.\w/.test(text))
|
|
15798
|
+
add('Python', 8);
|
|
15799
|
+
// Rust
|
|
15800
|
+
if (/\bfn\s+\w+\s*\(/.test(text) && /\blet\s+mut\b/.test(text))
|
|
15801
|
+
add('Rust', 15);
|
|
15802
|
+
if (/\b(impl|trait|enum|struct)\s+\w+/.test(text) && /\bfn\b/.test(text))
|
|
15803
|
+
add('Rust', 12);
|
|
15804
|
+
if (/use\s+std::|use\s+\w+::\w+/.test(text))
|
|
15805
|
+
add('Rust', 10);
|
|
15806
|
+
if (/println!\s*\(/.test(text))
|
|
15807
|
+
add('Rust', 8);
|
|
15808
|
+
// C++
|
|
15809
|
+
if (/#include\s*<[\w./]+>/.test(text))
|
|
15810
|
+
add('C++', 15);
|
|
15811
|
+
if (/\bstd::\w+/.test(text))
|
|
15812
|
+
add('C++', 12);
|
|
15813
|
+
if (/\b(class|template|namespace|nullptr|new\s+\w)\b/.test(text))
|
|
15814
|
+
add('C++', 8);
|
|
15815
|
+
if (/(::|->)\w+/.test(text))
|
|
15816
|
+
add('C++', 6);
|
|
15817
|
+
// C
|
|
15818
|
+
if (/#include\s*<[\w.]+\.h>/.test(text))
|
|
15819
|
+
add('C', 12);
|
|
15820
|
+
if (/\bint\s+main\s*\(/.test(text))
|
|
15821
|
+
add('C', 15);
|
|
15822
|
+
if (/\b(printf|scanf|malloc|free|sizeof)\s*\(/.test(text))
|
|
15823
|
+
add('C', 10);
|
|
15824
|
+
// TypeScript — before JS (is a superset)
|
|
15825
|
+
if (/:\s*(string|number|boolean|void|any|never|unknown)\b/.test(text))
|
|
15826
|
+
add('TypeScript', 12);
|
|
15827
|
+
if (/\binterface\s+\w+/.test(text))
|
|
15828
|
+
add('TypeScript', 12);
|
|
15829
|
+
if (/\btype\s+\w+\s*=/.test(text))
|
|
15830
|
+
add('TypeScript', 10);
|
|
15831
|
+
if (/\bas\s+(string|number|any|unknown)\b/.test(text))
|
|
15832
|
+
add('TypeScript', 8);
|
|
15833
|
+
if (/\benum\s+\w+\s*\{/.test(text))
|
|
15834
|
+
add('TypeScript', 8);
|
|
15835
|
+
// JavaScript
|
|
15836
|
+
if (/\b(const|let|var)\s+\w+\s*=/.test(text))
|
|
15837
|
+
add('JavaScript', 8);
|
|
15838
|
+
if (/=>\s*[\w{(]/.test(text))
|
|
15839
|
+
add('JavaScript', 6);
|
|
15840
|
+
if (/\b(import|export)\s+(default|{|\*)/.test(text))
|
|
15841
|
+
add('JavaScript', 8);
|
|
15842
|
+
if (/\bconsole\.(log|warn|error)\b/.test(text))
|
|
15843
|
+
add('JavaScript', 6);
|
|
15844
|
+
// Markdown
|
|
15845
|
+
if (/^#{1,6}\s+\S/m.test(text))
|
|
15846
|
+
add('Markdown', 12);
|
|
15847
|
+
if (/\*\*\w.*?\*\*/.test(text) || /\[.+\]\(.+\)/.test(text))
|
|
15848
|
+
add('Markdown', 8);
|
|
15849
|
+
if (/^```\w*/m.test(text))
|
|
15850
|
+
add('Markdown', 10);
|
|
15851
|
+
const best = Object.entries(scores).sort((a, b) => b[1] - a[1])[0];
|
|
15852
|
+
return best && best[1] >= 8 ? best[0] : null;
|
|
15853
|
+
}
|
|
15693
15854
|
appendText(text) {
|
|
15694
15855
|
const cursor = this.cursorSet.getPrimary();
|
|
15695
15856
|
const { line, col } = cursor.head;
|
|
@@ -16188,8 +16349,8 @@ class CodeEditor {
|
|
|
16188
16349
|
: Tokenizer.initialState();
|
|
16189
16350
|
const lineText = this.doc.getLine(lineIndex);
|
|
16190
16351
|
const result = Tokenizer.tokenizeLine(lineText, this.language, prevState);
|
|
16191
|
-
// Build HTML
|
|
16192
16352
|
const langClass = this.language.name.toLowerCase().replace(/[^a-z]/g, '');
|
|
16353
|
+
const URL_REGEX = /(https?:\/\/[^\s"'<>)\]]+)/g;
|
|
16193
16354
|
let html = '';
|
|
16194
16355
|
for (const token of result.tokens) {
|
|
16195
16356
|
const cls = TOKEN_CLASS_MAP[token.type];
|
|
@@ -16197,11 +16358,15 @@ class CodeEditor {
|
|
|
16197
16358
|
.replace(/&/g, '&')
|
|
16198
16359
|
.replace(/</g, '<')
|
|
16199
16360
|
.replace(/>/g, '>');
|
|
16361
|
+
// Wrap URLs in comment tokens with a clickable span
|
|
16362
|
+
const content = (token.type === 'comment')
|
|
16363
|
+
? escaped.replace(URL_REGEX, `<span class="code-link" data-url="$1">$1</span>`)
|
|
16364
|
+
: escaped;
|
|
16200
16365
|
if (cls) {
|
|
16201
|
-
html += `<span class="${cls} ${langClass}">${
|
|
16366
|
+
html += `<span class="${cls} ${langClass}">${content}</span>`;
|
|
16202
16367
|
}
|
|
16203
16368
|
else {
|
|
16204
|
-
html +=
|
|
16369
|
+
html += content;
|
|
16205
16370
|
}
|
|
16206
16371
|
}
|
|
16207
16372
|
return { html: html || ' ', endState: result.state, tokens: result.tokens };
|
|
@@ -17031,11 +17196,25 @@ class CodeEditor {
|
|
|
17031
17196
|
const { line, col } = cursor.head;
|
|
17032
17197
|
const indent = this.doc.getIndent(line);
|
|
17033
17198
|
const spaces = ' '.repeat(indent);
|
|
17034
|
-
const
|
|
17035
|
-
this.
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17199
|
+
const charBefore = this.doc.getCharAt(line, col - 1);
|
|
17200
|
+
const charAfter = this.doc.getCharAt(line, col);
|
|
17201
|
+
const OPEN_CLOSE = { '{': '}', '[': ']', '(': ')' };
|
|
17202
|
+
if (charBefore && charAfter && OPEN_CLOSE[charBefore] === charAfter) {
|
|
17203
|
+
const innerSpaces = ' '.repeat(indent + this.tabSize);
|
|
17204
|
+
const insertion = '\n' + innerSpaces + '\n' + spaces;
|
|
17205
|
+
const op = this.doc.insert(line, col, insertion);
|
|
17206
|
+
this.undoManager.record(op, this.cursorSet.getCursorPositions());
|
|
17207
|
+
cursor.head = { line: line + 1, col: indent + this.tabSize };
|
|
17208
|
+
cursor.anchor = { ...cursor.head };
|
|
17209
|
+
this.cursorSet.adjustOthers(idx, line, col, 0, 2);
|
|
17210
|
+
}
|
|
17211
|
+
else {
|
|
17212
|
+
const op = this.doc.insert(line, col, '\n' + spaces);
|
|
17213
|
+
this.undoManager.record(op, this.cursorSet.getCursorPositions());
|
|
17214
|
+
cursor.head = { line: line + 1, col: indent };
|
|
17215
|
+
cursor.anchor = { ...cursor.head };
|
|
17216
|
+
this.cursorSet.adjustOthers(idx, line, col, 0, 1);
|
|
17217
|
+
}
|
|
17039
17218
|
}
|
|
17040
17219
|
this._rebuildLines();
|
|
17041
17220
|
this._afterCursorMove();
|
|
@@ -17049,8 +17228,36 @@ class CodeEditor {
|
|
|
17049
17228
|
for (const idx of this.cursorSet.sortedIndicesBottomUp()) {
|
|
17050
17229
|
const cursor = this.cursorSet.cursors[idx];
|
|
17051
17230
|
const { line, col } = cursor.head;
|
|
17052
|
-
|
|
17053
|
-
|
|
17231
|
+
const anchorLine = cursor.anchor.line;
|
|
17232
|
+
// Multiline selection: indent/dedent all lines in the selection
|
|
17233
|
+
const startLine = Math.min(line, anchorLine);
|
|
17234
|
+
const endLine = Math.max(line, anchorLine);
|
|
17235
|
+
const isMultiline = startLine !== endLine;
|
|
17236
|
+
if (isMultiline) {
|
|
17237
|
+
for (let i = startLine; i <= endLine; i++) {
|
|
17238
|
+
if (shift) {
|
|
17239
|
+
const lineText = this.doc.getLine(i);
|
|
17240
|
+
let spacesToRemove = 0;
|
|
17241
|
+
while (spacesToRemove < this.tabSize && spacesToRemove < lineText.length && lineText[spacesToRemove] === ' ') {
|
|
17242
|
+
spacesToRemove++;
|
|
17243
|
+
}
|
|
17244
|
+
if (spacesToRemove > 0) {
|
|
17245
|
+
const op = this.doc.delete(i, 0, spacesToRemove);
|
|
17246
|
+
this.undoManager.record(op, this.cursorSet.getCursorPositions());
|
|
17247
|
+
}
|
|
17248
|
+
}
|
|
17249
|
+
else {
|
|
17250
|
+
const spaces = ' '.repeat(this.tabSize);
|
|
17251
|
+
const op = this.doc.insert(i, 0, spaces);
|
|
17252
|
+
this.undoManager.record(op, this.cursorSet.getCursorPositions());
|
|
17253
|
+
}
|
|
17254
|
+
}
|
|
17255
|
+
const delta = shift ? -this.tabSize : this.tabSize;
|
|
17256
|
+
cursor.head = { line, col: Math.max(0, col + delta) };
|
|
17257
|
+
cursor.anchor = { line: anchorLine, col: Math.max(0, cursor.anchor.col + delta) };
|
|
17258
|
+
}
|
|
17259
|
+
else if (shift) {
|
|
17260
|
+
// Single line dedent: remove up to tabSize spaces from start
|
|
17054
17261
|
const lineText = this.doc.getLine(line);
|
|
17055
17262
|
let spacesToRemove = 0;
|
|
17056
17263
|
while (spacesToRemove < this.tabSize && spacesToRemove < lineText.length && lineText[spacesToRemove] === ' ') {
|
|
@@ -17065,6 +17272,7 @@ class CodeEditor {
|
|
|
17065
17272
|
}
|
|
17066
17273
|
}
|
|
17067
17274
|
else {
|
|
17275
|
+
// Single line indent: insert spaces at cursor
|
|
17068
17276
|
const spacesToAdd = this.tabSize - (col % this.tabSize);
|
|
17069
17277
|
const spaces = ' '.repeat(spacesToAdd);
|
|
17070
17278
|
const op = this.doc.insert(line, col, spaces);
|
|
@@ -17098,8 +17306,10 @@ class CodeEditor {
|
|
|
17098
17306
|
this.cursorSet.adjustOthers(idx, start.line, start.col, -colDelta, -linesRemoved);
|
|
17099
17307
|
anyDeleted = true;
|
|
17100
17308
|
}
|
|
17101
|
-
if (anyDeleted)
|
|
17309
|
+
if (anyDeleted) {
|
|
17102
17310
|
this._rebuildLines();
|
|
17311
|
+
this._doHideAutocomplete();
|
|
17312
|
+
}
|
|
17103
17313
|
}
|
|
17104
17314
|
// Clipboard helpers:
|
|
17105
17315
|
_doCopy() {
|
|
@@ -17213,6 +17423,15 @@ class CodeEditor {
|
|
|
17213
17423
|
return;
|
|
17214
17424
|
if (this.autocomplete && this.autocomplete.contains(e.target))
|
|
17215
17425
|
return;
|
|
17426
|
+
// Ctrl+click: open link if cursor is over a code-link span
|
|
17427
|
+
if (e.ctrlKey && e.button === 0) {
|
|
17428
|
+
const target = e.target;
|
|
17429
|
+
const link = target.closest('.code-link');
|
|
17430
|
+
if (link?.dataset.url) {
|
|
17431
|
+
window.open(link.dataset.url, '_blank');
|
|
17432
|
+
return;
|
|
17433
|
+
}
|
|
17434
|
+
}
|
|
17216
17435
|
e.preventDefault(); // Prevent browser from stealing focus from _inputArea
|
|
17217
17436
|
this._wasPaired = false;
|
|
17218
17437
|
// Calculate line and column from click position
|
|
@@ -17330,9 +17549,9 @@ class CodeEditor {
|
|
|
17330
17549
|
}
|
|
17331
17550
|
const suggestions = [];
|
|
17332
17551
|
const added = new Set();
|
|
17333
|
-
const addSuggestion = (label, kind, scope, detail) => {
|
|
17552
|
+
const addSuggestion = (label, kind, scope, detail, insertText) => {
|
|
17334
17553
|
if (!added.has(label)) {
|
|
17335
|
-
suggestions.push({ label, kind, scope, detail });
|
|
17554
|
+
suggestions.push({ label, kind, scope, detail, insertText });
|
|
17336
17555
|
added.add(label);
|
|
17337
17556
|
}
|
|
17338
17557
|
};
|
|
@@ -17354,8 +17573,9 @@ class CodeEditor {
|
|
|
17354
17573
|
const label = typeof suggestion === 'string' ? suggestion : suggestion.label;
|
|
17355
17574
|
const kind = typeof suggestion === 'object' ? suggestion.kind : undefined;
|
|
17356
17575
|
const detail = typeof suggestion === 'object' ? suggestion.detail : undefined;
|
|
17576
|
+
const insertText = typeof suggestion === 'object' ? suggestion.insertText : suggestion;
|
|
17357
17577
|
if (label.toLowerCase().startsWith(word.toLowerCase())) {
|
|
17358
|
-
addSuggestion(label, kind, undefined, detail);
|
|
17578
|
+
addSuggestion(label, kind, undefined, detail, insertText);
|
|
17359
17579
|
}
|
|
17360
17580
|
}
|
|
17361
17581
|
// Close autocomplete if no suggestions
|
|
@@ -17375,6 +17595,7 @@ class CodeEditor {
|
|
|
17375
17595
|
// Render suggestions
|
|
17376
17596
|
suggestions.forEach((suggestion, index) => {
|
|
17377
17597
|
const item = document.createElement('pre');
|
|
17598
|
+
item.insertText = suggestion.insertText ?? suggestion.label;
|
|
17378
17599
|
if (index === this._selectedAutocompleteIndex)
|
|
17379
17600
|
item.classList.add('selected');
|
|
17380
17601
|
const currSuggestion = suggestion.label;
|
|
@@ -17493,8 +17714,8 @@ class CodeEditor {
|
|
|
17493
17714
|
* Insert the selected autocomplete word at cursor.
|
|
17494
17715
|
*/
|
|
17495
17716
|
_doAutocompleteWord() {
|
|
17496
|
-
const
|
|
17497
|
-
if (!
|
|
17717
|
+
const text = this._getSelectedAutoCompleteWord();
|
|
17718
|
+
if (!text)
|
|
17498
17719
|
return;
|
|
17499
17720
|
const cursor = this.cursorSet.getPrimary().head;
|
|
17500
17721
|
const { start, end } = this._getWordAtCursor();
|
|
@@ -17504,8 +17725,14 @@ class CodeEditor {
|
|
|
17504
17725
|
const deleteOp = this.doc.delete(line, start, end - start);
|
|
17505
17726
|
this.undoManager.record(deleteOp, cursorsBefore);
|
|
17506
17727
|
}
|
|
17507
|
-
const insertOp = this.doc.insert(line, start,
|
|
17508
|
-
|
|
17728
|
+
const insertOp = this.doc.insert(line, start, text);
|
|
17729
|
+
const insertedLines = text.split(/\r?\n/);
|
|
17730
|
+
if (insertedLines.length === 1) {
|
|
17731
|
+
this.cursorSet.set(line, start + text.length);
|
|
17732
|
+
}
|
|
17733
|
+
else {
|
|
17734
|
+
this.cursorSet.set(line + insertedLines.length - 1, insertedLines[insertedLines.length - 1].length);
|
|
17735
|
+
}
|
|
17509
17736
|
const cursorsAfter = this.cursorSet.getCursorPositions();
|
|
17510
17737
|
this.undoManager.record(insertOp, cursorsAfter);
|
|
17511
17738
|
this._rebuildLines();
|
|
@@ -17516,15 +17743,7 @@ class CodeEditor {
|
|
|
17516
17743
|
if (!this.autocomplete || !this._isAutoCompleteActive)
|
|
17517
17744
|
return null;
|
|
17518
17745
|
const pre = this.autocomplete.childNodes[this._selectedAutocompleteIndex];
|
|
17519
|
-
|
|
17520
|
-
for (let childSpan of pre.childNodes) {
|
|
17521
|
-
const span = childSpan;
|
|
17522
|
-
if (span.constructor != HTMLSpanElement || span.classList.contains('kind')) {
|
|
17523
|
-
continue;
|
|
17524
|
-
}
|
|
17525
|
-
word += span.textContent;
|
|
17526
|
-
}
|
|
17527
|
-
return word;
|
|
17746
|
+
return pre.insertText;
|
|
17528
17747
|
}
|
|
17529
17748
|
_afterCursorMove() {
|
|
17530
17749
|
this._renderCursors();
|
|
@@ -17557,7 +17776,8 @@ class CodeEditor {
|
|
|
17557
17776
|
_resetGutter() {
|
|
17558
17777
|
// Use cached value or compute if not available (e.g., on initial load)
|
|
17559
17778
|
const tabsHeight = this._cachedTabsHeight || (this.tabs?.root.getBoundingClientRect().height ?? 0);
|
|
17560
|
-
this.
|
|
17779
|
+
const statusPanelHeight = this._cachedStatusPanelHeight || (this.statusPanel?.root.getBoundingClientRect().height ?? 0);
|
|
17780
|
+
this.lineGutter.style.height = `calc(100% - ${tabsHeight + statusPanelHeight}px)`;
|
|
17561
17781
|
}
|
|
17562
17782
|
getMaxLineLength() {
|
|
17563
17783
|
if (!this.currentTab)
|
|
@@ -27585,8 +27805,7 @@ class VideoEditor {
|
|
|
27585
27805
|
}
|
|
27586
27806
|
else {
|
|
27587
27807
|
[videoArea, controlsArea] = area.split({ type: 'vertical',
|
|
27588
|
-
sizes: [controlsOptions.height ? `calc(100% - ${controlsOptions.height})` : '85%', null], minimizable: false,
|
|
27589
|
-
resize: false });
|
|
27808
|
+
sizes: [controlsOptions.height ? `calc(100% - ${controlsOptions.height})` : '85%', null], minimizable: false, resize: false });
|
|
27590
27809
|
}
|
|
27591
27810
|
controlsArea.root.classList.add('lexconstrolsarea');
|
|
27592
27811
|
this.cropArea = document.createElement('div');
|