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
package/build/lexgui.css
CHANGED
|
@@ -4449,7 +4449,7 @@ input[type=number] {
|
|
|
4449
4449
|
width: var(--thumb-height);
|
|
4450
4450
|
height: var(--thumb-height);
|
|
4451
4451
|
border-radius: 50%;
|
|
4452
|
-
background:
|
|
4452
|
+
background: var(--primary);
|
|
4453
4453
|
cursor: pointer;
|
|
4454
4454
|
box-shadow: 0px 0px 2px 1px var(--secondary);
|
|
4455
4455
|
}
|
|
@@ -4462,7 +4462,7 @@ input[type=number] {
|
|
|
4462
4462
|
width: var(--thumb-height);
|
|
4463
4463
|
height: var(--thumb-height);
|
|
4464
4464
|
border-radius: 50%;
|
|
4465
|
-
background:
|
|
4465
|
+
background: var(--primary);
|
|
4466
4466
|
cursor: pointer;
|
|
4467
4467
|
box-shadow: 0px 0px 2px 1px var(--secondary);
|
|
4468
4468
|
}
|
|
@@ -6198,6 +6198,16 @@ ul.lexassetscontent {
|
|
|
6198
6198
|
align-content: center;
|
|
6199
6199
|
}
|
|
6200
6200
|
}
|
|
6201
|
+
.lexcodeeditor .code-link {
|
|
6202
|
+
text-decoration: underline;
|
|
6203
|
+
text-underline-offset: 4px;
|
|
6204
|
+
pointer-events: auto;
|
|
6205
|
+
cursor: text;
|
|
6206
|
+
}
|
|
6207
|
+
.lexcodeeditor .code-link.hovered {
|
|
6208
|
+
color: var(--info);
|
|
6209
|
+
cursor: pointer;
|
|
6210
|
+
}
|
|
6201
6211
|
.lexcodeeditor ::-webkit-scrollbar {
|
|
6202
6212
|
width: 6px;
|
|
6203
6213
|
height: 6px;
|
package/build/lexgui.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
exports.LX = g$1.LX;
|
|
17
17
|
if (!exports.LX) {
|
|
18
18
|
exports.LX = {
|
|
19
|
-
version: '8.3.
|
|
19
|
+
version: '8.3.1',
|
|
20
20
|
ready: false,
|
|
21
21
|
extensions: [], // Store extensions used
|
|
22
22
|
extraCommandbarEntries: [], // User specific entries for command bar
|
|
@@ -9799,9 +9799,25 @@
|
|
|
9799
9799
|
* @param {String} str
|
|
9800
9800
|
*/
|
|
9801
9801
|
function toKebabCase(str) {
|
|
9802
|
-
return str
|
|
9802
|
+
return str
|
|
9803
|
+
.replace(/([A-Z])/g, '-$1')
|
|
9804
|
+
.replace(/[\s_]+/g, '-')
|
|
9805
|
+
.replace(/^-/, '')
|
|
9806
|
+
.toLowerCase();
|
|
9803
9807
|
}
|
|
9804
9808
|
exports.LX.toKebabCase = toKebabCase;
|
|
9809
|
+
/**
|
|
9810
|
+
* @method toSnakeCase
|
|
9811
|
+
* @param {String} str
|
|
9812
|
+
*/
|
|
9813
|
+
function toSnakeCase(str) {
|
|
9814
|
+
return str
|
|
9815
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
9816
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
|
|
9817
|
+
.replace(/[\s\-]+/g, '_')
|
|
9818
|
+
.toLowerCase();
|
|
9819
|
+
}
|
|
9820
|
+
exports.LX.toSnakeCase = toSnakeCase;
|
|
9805
9821
|
/**
|
|
9806
9822
|
* @method getSupportedDOMName
|
|
9807
9823
|
* @description Convert a text string to a valid DOM name
|
|
@@ -15595,6 +15611,21 @@
|
|
|
15595
15611
|
this._inputArea.addEventListener('blur', () => this._setFocused(false));
|
|
15596
15612
|
this.codeArea.root.addEventListener('mousedown', this._onMouseDown.bind(this));
|
|
15597
15613
|
this.codeArea.root.addEventListener('contextmenu', this._onMouseDown.bind(this));
|
|
15614
|
+
this.codeArea.root.addEventListener('mouseover', (e) => {
|
|
15615
|
+
const link = e.target.closest('.code-link');
|
|
15616
|
+
if (link && e.ctrlKey)
|
|
15617
|
+
link.classList.add('hovered');
|
|
15618
|
+
});
|
|
15619
|
+
this.codeArea.root.addEventListener('mouseout', (e) => {
|
|
15620
|
+
const link = e.target.closest('.code-link');
|
|
15621
|
+
if (link)
|
|
15622
|
+
link.classList.remove('hovered');
|
|
15623
|
+
});
|
|
15624
|
+
this.codeArea.root.addEventListener('mousemove', (e) => {
|
|
15625
|
+
const link = e.target.closest('.code-link');
|
|
15626
|
+
if (link)
|
|
15627
|
+
link.classList.toggle('hovered', e.ctrlKey);
|
|
15628
|
+
});
|
|
15598
15629
|
// Bottom status panel
|
|
15599
15630
|
this.statusPanel = this._createStatusPanel(options);
|
|
15600
15631
|
if (this.statusPanel) {
|
|
@@ -15682,18 +15713,148 @@
|
|
|
15682
15713
|
}
|
|
15683
15714
|
}
|
|
15684
15715
|
;
|
|
15685
|
-
setText(text) {
|
|
15716
|
+
setText(text, language, detectLang = false) {
|
|
15686
15717
|
if (!this.currentTab)
|
|
15687
15718
|
return;
|
|
15688
15719
|
this.doc.setText(text);
|
|
15689
15720
|
this.cursorSet.set(0, 0);
|
|
15690
15721
|
this.undoManager.clear();
|
|
15691
15722
|
this._lineStates = [];
|
|
15723
|
+
if (language) {
|
|
15724
|
+
this.setLanguage(language);
|
|
15725
|
+
}
|
|
15726
|
+
else if (detectLang) {
|
|
15727
|
+
const detected = this._detectLanguage(text);
|
|
15728
|
+
if (detected)
|
|
15729
|
+
this.setLanguage(detected);
|
|
15730
|
+
}
|
|
15692
15731
|
this._renderAllLines();
|
|
15693
15732
|
this._renderCursors();
|
|
15694
15733
|
this._renderSelections();
|
|
15695
15734
|
this.resize(true);
|
|
15696
15735
|
}
|
|
15736
|
+
_detectLanguage(text) {
|
|
15737
|
+
const scores = {};
|
|
15738
|
+
const add = (lang, pts) => { scores[lang] = (scores[lang] ?? 0) + pts; };
|
|
15739
|
+
// Score using reservedWords from each registered language
|
|
15740
|
+
const textWords = new Set(text.match(/\b\w+\b/g) ?? []);
|
|
15741
|
+
const totalWords = Math.max(textWords.size, 1);
|
|
15742
|
+
for (const langName of Tokenizer.getRegisteredLanguages()) {
|
|
15743
|
+
const langDef = Tokenizer.getLanguage(langName);
|
|
15744
|
+
if (!langDef?.reservedWords?.length)
|
|
15745
|
+
continue;
|
|
15746
|
+
let hits = 0;
|
|
15747
|
+
for (const word of langDef.reservedWords) {
|
|
15748
|
+
if (textWords.has(word))
|
|
15749
|
+
hits++;
|
|
15750
|
+
}
|
|
15751
|
+
if (hits > 0) {
|
|
15752
|
+
const vocabRatio = hits / langDef.reservedWords.length;
|
|
15753
|
+
const textRatio = hits / totalWords;
|
|
15754
|
+
add(langName, Math.round((vocabRatio + textRatio) * 40));
|
|
15755
|
+
}
|
|
15756
|
+
}
|
|
15757
|
+
// Add scores based on "important" structural words only
|
|
15758
|
+
// HTML
|
|
15759
|
+
if (/<!DOCTYPE\s+html/i.test(text))
|
|
15760
|
+
add('HTML', 20);
|
|
15761
|
+
if (/<html[\s>]/i.test(text))
|
|
15762
|
+
add('HTML', 15);
|
|
15763
|
+
if (/<\/?(div|span|body|head|script|style|meta)\b/i.test(text))
|
|
15764
|
+
add('HTML', 8);
|
|
15765
|
+
// JSON — must come before JS checks (starts with { or [)
|
|
15766
|
+
if (/^\s*[\[{]/.test(text) && /"\s*:\s*/.test(text) && !/function|=>|const|var|let/.test(text))
|
|
15767
|
+
add('JSON', 15);
|
|
15768
|
+
// CSS
|
|
15769
|
+
if (/[\w-]+\s*:\s*[\w#\d"'(]+.*;/.test(text) && /[{}]/.test(text) && !/<\w+/.test(text))
|
|
15770
|
+
add('CSS', 10);
|
|
15771
|
+
if (/@(media|keyframes|import|charset|font-face)\b/.test(text))
|
|
15772
|
+
add('CSS', 15);
|
|
15773
|
+
// WGSL
|
|
15774
|
+
if (/@(vertex|fragment|compute|group|binding|builtin)\b/.test(text))
|
|
15775
|
+
add('WGSL', 20);
|
|
15776
|
+
if (/\bfn\s+\w+\s*\(/.test(text) && /\bvar\b/.test(text))
|
|
15777
|
+
add('WGSL', 10);
|
|
15778
|
+
if (/\b(vec2f|vec3f|vec4f|mat4x4f|f32|u32|i32)\b/.test(text))
|
|
15779
|
+
add('WGSL', 12);
|
|
15780
|
+
// GLSL
|
|
15781
|
+
if (/\b(gl_Position|gl_FragColor|gl_FragCoord)\b/.test(text))
|
|
15782
|
+
add('GLSL', 20);
|
|
15783
|
+
if (/\b(uniform|varying|attribute)\s+\w/.test(text))
|
|
15784
|
+
add('GLSL', 10);
|
|
15785
|
+
if (/\b(vec2|vec3|vec4|mat4|sampler2D)\b/.test(text) && !/vec2f|vec3f/.test(text))
|
|
15786
|
+
add('GLSL', 8);
|
|
15787
|
+
// HLSL
|
|
15788
|
+
if (/\b(SV_Position|SV_Target|SV_Depth)\b/.test(text))
|
|
15789
|
+
add('HLSL', 20);
|
|
15790
|
+
if (/\b(cbuffer|tbuffer|float4|float3|float2|Texture2D)\b/.test(text))
|
|
15791
|
+
add('HLSL', 12);
|
|
15792
|
+
// Python
|
|
15793
|
+
if (/^\s*def\s+\w+\s*\(/m.test(text))
|
|
15794
|
+
add('Python', 15);
|
|
15795
|
+
if (/^\s*import\s+\w/m.test(text) && !/\bfrom\s+['"]/.test(text))
|
|
15796
|
+
add('Python', 8);
|
|
15797
|
+
if (/^\s*class\s+\w+(\s*\(.*\))?:/m.test(text))
|
|
15798
|
+
add('Python', 10);
|
|
15799
|
+
if (/\bprint\s*\(/.test(text) && /:\s*$/.test(text))
|
|
15800
|
+
add('Python', 5);
|
|
15801
|
+
if (/elif\b|lambda\b|self\.\w/.test(text))
|
|
15802
|
+
add('Python', 8);
|
|
15803
|
+
// Rust
|
|
15804
|
+
if (/\bfn\s+\w+\s*\(/.test(text) && /\blet\s+mut\b/.test(text))
|
|
15805
|
+
add('Rust', 15);
|
|
15806
|
+
if (/\b(impl|trait|enum|struct)\s+\w+/.test(text) && /\bfn\b/.test(text))
|
|
15807
|
+
add('Rust', 12);
|
|
15808
|
+
if (/use\s+std::|use\s+\w+::\w+/.test(text))
|
|
15809
|
+
add('Rust', 10);
|
|
15810
|
+
if (/println!\s*\(/.test(text))
|
|
15811
|
+
add('Rust', 8);
|
|
15812
|
+
// C++
|
|
15813
|
+
if (/#include\s*<[\w./]+>/.test(text))
|
|
15814
|
+
add('C++', 15);
|
|
15815
|
+
if (/\bstd::\w+/.test(text))
|
|
15816
|
+
add('C++', 12);
|
|
15817
|
+
if (/\b(class|template|namespace|nullptr|new\s+\w)\b/.test(text))
|
|
15818
|
+
add('C++', 8);
|
|
15819
|
+
if (/(::|->)\w+/.test(text))
|
|
15820
|
+
add('C++', 6);
|
|
15821
|
+
// C
|
|
15822
|
+
if (/#include\s*<[\w.]+\.h>/.test(text))
|
|
15823
|
+
add('C', 12);
|
|
15824
|
+
if (/\bint\s+main\s*\(/.test(text))
|
|
15825
|
+
add('C', 15);
|
|
15826
|
+
if (/\b(printf|scanf|malloc|free|sizeof)\s*\(/.test(text))
|
|
15827
|
+
add('C', 10);
|
|
15828
|
+
// TypeScript — before JS (is a superset)
|
|
15829
|
+
if (/:\s*(string|number|boolean|void|any|never|unknown)\b/.test(text))
|
|
15830
|
+
add('TypeScript', 12);
|
|
15831
|
+
if (/\binterface\s+\w+/.test(text))
|
|
15832
|
+
add('TypeScript', 12);
|
|
15833
|
+
if (/\btype\s+\w+\s*=/.test(text))
|
|
15834
|
+
add('TypeScript', 10);
|
|
15835
|
+
if (/\bas\s+(string|number|any|unknown)\b/.test(text))
|
|
15836
|
+
add('TypeScript', 8);
|
|
15837
|
+
if (/\benum\s+\w+\s*\{/.test(text))
|
|
15838
|
+
add('TypeScript', 8);
|
|
15839
|
+
// JavaScript
|
|
15840
|
+
if (/\b(const|let|var)\s+\w+\s*=/.test(text))
|
|
15841
|
+
add('JavaScript', 8);
|
|
15842
|
+
if (/=>\s*[\w{(]/.test(text))
|
|
15843
|
+
add('JavaScript', 6);
|
|
15844
|
+
if (/\b(import|export)\s+(default|{|\*)/.test(text))
|
|
15845
|
+
add('JavaScript', 8);
|
|
15846
|
+
if (/\bconsole\.(log|warn|error)\b/.test(text))
|
|
15847
|
+
add('JavaScript', 6);
|
|
15848
|
+
// Markdown
|
|
15849
|
+
if (/^#{1,6}\s+\S/m.test(text))
|
|
15850
|
+
add('Markdown', 12);
|
|
15851
|
+
if (/\*\*\w.*?\*\*/.test(text) || /\[.+\]\(.+\)/.test(text))
|
|
15852
|
+
add('Markdown', 8);
|
|
15853
|
+
if (/^```\w*/m.test(text))
|
|
15854
|
+
add('Markdown', 10);
|
|
15855
|
+
const best = Object.entries(scores).sort((a, b) => b[1] - a[1])[0];
|
|
15856
|
+
return best && best[1] >= 8 ? best[0] : null;
|
|
15857
|
+
}
|
|
15697
15858
|
appendText(text) {
|
|
15698
15859
|
const cursor = this.cursorSet.getPrimary();
|
|
15699
15860
|
const { line, col } = cursor.head;
|
|
@@ -16192,8 +16353,8 @@
|
|
|
16192
16353
|
: Tokenizer.initialState();
|
|
16193
16354
|
const lineText = this.doc.getLine(lineIndex);
|
|
16194
16355
|
const result = Tokenizer.tokenizeLine(lineText, this.language, prevState);
|
|
16195
|
-
// Build HTML
|
|
16196
16356
|
const langClass = this.language.name.toLowerCase().replace(/[^a-z]/g, '');
|
|
16357
|
+
const URL_REGEX = /(https?:\/\/[^\s"'<>)\]]+)/g;
|
|
16197
16358
|
let html = '';
|
|
16198
16359
|
for (const token of result.tokens) {
|
|
16199
16360
|
const cls = TOKEN_CLASS_MAP[token.type];
|
|
@@ -16201,11 +16362,15 @@
|
|
|
16201
16362
|
.replace(/&/g, '&')
|
|
16202
16363
|
.replace(/</g, '<')
|
|
16203
16364
|
.replace(/>/g, '>');
|
|
16365
|
+
// Wrap URLs in comment tokens with a clickable span
|
|
16366
|
+
const content = (token.type === 'comment')
|
|
16367
|
+
? escaped.replace(URL_REGEX, `<span class="code-link" data-url="$1">$1</span>`)
|
|
16368
|
+
: escaped;
|
|
16204
16369
|
if (cls) {
|
|
16205
|
-
html += `<span class="${cls} ${langClass}">${
|
|
16370
|
+
html += `<span class="${cls} ${langClass}">${content}</span>`;
|
|
16206
16371
|
}
|
|
16207
16372
|
else {
|
|
16208
|
-
html +=
|
|
16373
|
+
html += content;
|
|
16209
16374
|
}
|
|
16210
16375
|
}
|
|
16211
16376
|
return { html: html || ' ', endState: result.state, tokens: result.tokens };
|
|
@@ -17035,11 +17200,25 @@
|
|
|
17035
17200
|
const { line, col } = cursor.head;
|
|
17036
17201
|
const indent = this.doc.getIndent(line);
|
|
17037
17202
|
const spaces = ' '.repeat(indent);
|
|
17038
|
-
const
|
|
17039
|
-
this.
|
|
17040
|
-
|
|
17041
|
-
|
|
17042
|
-
|
|
17203
|
+
const charBefore = this.doc.getCharAt(line, col - 1);
|
|
17204
|
+
const charAfter = this.doc.getCharAt(line, col);
|
|
17205
|
+
const OPEN_CLOSE = { '{': '}', '[': ']', '(': ')' };
|
|
17206
|
+
if (charBefore && charAfter && OPEN_CLOSE[charBefore] === charAfter) {
|
|
17207
|
+
const innerSpaces = ' '.repeat(indent + this.tabSize);
|
|
17208
|
+
const insertion = '\n' + innerSpaces + '\n' + spaces;
|
|
17209
|
+
const op = this.doc.insert(line, col, insertion);
|
|
17210
|
+
this.undoManager.record(op, this.cursorSet.getCursorPositions());
|
|
17211
|
+
cursor.head = { line: line + 1, col: indent + this.tabSize };
|
|
17212
|
+
cursor.anchor = { ...cursor.head };
|
|
17213
|
+
this.cursorSet.adjustOthers(idx, line, col, 0, 2);
|
|
17214
|
+
}
|
|
17215
|
+
else {
|
|
17216
|
+
const op = this.doc.insert(line, col, '\n' + spaces);
|
|
17217
|
+
this.undoManager.record(op, this.cursorSet.getCursorPositions());
|
|
17218
|
+
cursor.head = { line: line + 1, col: indent };
|
|
17219
|
+
cursor.anchor = { ...cursor.head };
|
|
17220
|
+
this.cursorSet.adjustOthers(idx, line, col, 0, 1);
|
|
17221
|
+
}
|
|
17043
17222
|
}
|
|
17044
17223
|
this._rebuildLines();
|
|
17045
17224
|
this._afterCursorMove();
|
|
@@ -17053,8 +17232,36 @@
|
|
|
17053
17232
|
for (const idx of this.cursorSet.sortedIndicesBottomUp()) {
|
|
17054
17233
|
const cursor = this.cursorSet.cursors[idx];
|
|
17055
17234
|
const { line, col } = cursor.head;
|
|
17056
|
-
|
|
17057
|
-
|
|
17235
|
+
const anchorLine = cursor.anchor.line;
|
|
17236
|
+
// Multiline selection: indent/dedent all lines in the selection
|
|
17237
|
+
const startLine = Math.min(line, anchorLine);
|
|
17238
|
+
const endLine = Math.max(line, anchorLine);
|
|
17239
|
+
const isMultiline = startLine !== endLine;
|
|
17240
|
+
if (isMultiline) {
|
|
17241
|
+
for (let i = startLine; i <= endLine; i++) {
|
|
17242
|
+
if (shift) {
|
|
17243
|
+
const lineText = this.doc.getLine(i);
|
|
17244
|
+
let spacesToRemove = 0;
|
|
17245
|
+
while (spacesToRemove < this.tabSize && spacesToRemove < lineText.length && lineText[spacesToRemove] === ' ') {
|
|
17246
|
+
spacesToRemove++;
|
|
17247
|
+
}
|
|
17248
|
+
if (spacesToRemove > 0) {
|
|
17249
|
+
const op = this.doc.delete(i, 0, spacesToRemove);
|
|
17250
|
+
this.undoManager.record(op, this.cursorSet.getCursorPositions());
|
|
17251
|
+
}
|
|
17252
|
+
}
|
|
17253
|
+
else {
|
|
17254
|
+
const spaces = ' '.repeat(this.tabSize);
|
|
17255
|
+
const op = this.doc.insert(i, 0, spaces);
|
|
17256
|
+
this.undoManager.record(op, this.cursorSet.getCursorPositions());
|
|
17257
|
+
}
|
|
17258
|
+
}
|
|
17259
|
+
const delta = shift ? -this.tabSize : this.tabSize;
|
|
17260
|
+
cursor.head = { line, col: Math.max(0, col + delta) };
|
|
17261
|
+
cursor.anchor = { line: anchorLine, col: Math.max(0, cursor.anchor.col + delta) };
|
|
17262
|
+
}
|
|
17263
|
+
else if (shift) {
|
|
17264
|
+
// Single line dedent: remove up to tabSize spaces from start
|
|
17058
17265
|
const lineText = this.doc.getLine(line);
|
|
17059
17266
|
let spacesToRemove = 0;
|
|
17060
17267
|
while (spacesToRemove < this.tabSize && spacesToRemove < lineText.length && lineText[spacesToRemove] === ' ') {
|
|
@@ -17069,6 +17276,7 @@
|
|
|
17069
17276
|
}
|
|
17070
17277
|
}
|
|
17071
17278
|
else {
|
|
17279
|
+
// Single line indent: insert spaces at cursor
|
|
17072
17280
|
const spacesToAdd = this.tabSize - (col % this.tabSize);
|
|
17073
17281
|
const spaces = ' '.repeat(spacesToAdd);
|
|
17074
17282
|
const op = this.doc.insert(line, col, spaces);
|
|
@@ -17102,8 +17310,10 @@
|
|
|
17102
17310
|
this.cursorSet.adjustOthers(idx, start.line, start.col, -colDelta, -linesRemoved);
|
|
17103
17311
|
anyDeleted = true;
|
|
17104
17312
|
}
|
|
17105
|
-
if (anyDeleted)
|
|
17313
|
+
if (anyDeleted) {
|
|
17106
17314
|
this._rebuildLines();
|
|
17315
|
+
this._doHideAutocomplete();
|
|
17316
|
+
}
|
|
17107
17317
|
}
|
|
17108
17318
|
// Clipboard helpers:
|
|
17109
17319
|
_doCopy() {
|
|
@@ -17217,6 +17427,15 @@
|
|
|
17217
17427
|
return;
|
|
17218
17428
|
if (this.autocomplete && this.autocomplete.contains(e.target))
|
|
17219
17429
|
return;
|
|
17430
|
+
// Ctrl+click: open link if cursor is over a code-link span
|
|
17431
|
+
if (e.ctrlKey && e.button === 0) {
|
|
17432
|
+
const target = e.target;
|
|
17433
|
+
const link = target.closest('.code-link');
|
|
17434
|
+
if (link?.dataset.url) {
|
|
17435
|
+
window.open(link.dataset.url, '_blank');
|
|
17436
|
+
return;
|
|
17437
|
+
}
|
|
17438
|
+
}
|
|
17220
17439
|
e.preventDefault(); // Prevent browser from stealing focus from _inputArea
|
|
17221
17440
|
this._wasPaired = false;
|
|
17222
17441
|
// Calculate line and column from click position
|
|
@@ -17334,9 +17553,9 @@
|
|
|
17334
17553
|
}
|
|
17335
17554
|
const suggestions = [];
|
|
17336
17555
|
const added = new Set();
|
|
17337
|
-
const addSuggestion = (label, kind, scope, detail) => {
|
|
17556
|
+
const addSuggestion = (label, kind, scope, detail, insertText) => {
|
|
17338
17557
|
if (!added.has(label)) {
|
|
17339
|
-
suggestions.push({ label, kind, scope, detail });
|
|
17558
|
+
suggestions.push({ label, kind, scope, detail, insertText });
|
|
17340
17559
|
added.add(label);
|
|
17341
17560
|
}
|
|
17342
17561
|
};
|
|
@@ -17358,8 +17577,9 @@
|
|
|
17358
17577
|
const label = typeof suggestion === 'string' ? suggestion : suggestion.label;
|
|
17359
17578
|
const kind = typeof suggestion === 'object' ? suggestion.kind : undefined;
|
|
17360
17579
|
const detail = typeof suggestion === 'object' ? suggestion.detail : undefined;
|
|
17580
|
+
const insertText = typeof suggestion === 'object' ? suggestion.insertText : suggestion;
|
|
17361
17581
|
if (label.toLowerCase().startsWith(word.toLowerCase())) {
|
|
17362
|
-
addSuggestion(label, kind, undefined, detail);
|
|
17582
|
+
addSuggestion(label, kind, undefined, detail, insertText);
|
|
17363
17583
|
}
|
|
17364
17584
|
}
|
|
17365
17585
|
// Close autocomplete if no suggestions
|
|
@@ -17379,6 +17599,7 @@
|
|
|
17379
17599
|
// Render suggestions
|
|
17380
17600
|
suggestions.forEach((suggestion, index) => {
|
|
17381
17601
|
const item = document.createElement('pre');
|
|
17602
|
+
item.insertText = suggestion.insertText ?? suggestion.label;
|
|
17382
17603
|
if (index === this._selectedAutocompleteIndex)
|
|
17383
17604
|
item.classList.add('selected');
|
|
17384
17605
|
const currSuggestion = suggestion.label;
|
|
@@ -17497,8 +17718,8 @@
|
|
|
17497
17718
|
* Insert the selected autocomplete word at cursor.
|
|
17498
17719
|
*/
|
|
17499
17720
|
_doAutocompleteWord() {
|
|
17500
|
-
const
|
|
17501
|
-
if (!
|
|
17721
|
+
const text = this._getSelectedAutoCompleteWord();
|
|
17722
|
+
if (!text)
|
|
17502
17723
|
return;
|
|
17503
17724
|
const cursor = this.cursorSet.getPrimary().head;
|
|
17504
17725
|
const { start, end } = this._getWordAtCursor();
|
|
@@ -17508,8 +17729,14 @@
|
|
|
17508
17729
|
const deleteOp = this.doc.delete(line, start, end - start);
|
|
17509
17730
|
this.undoManager.record(deleteOp, cursorsBefore);
|
|
17510
17731
|
}
|
|
17511
|
-
const insertOp = this.doc.insert(line, start,
|
|
17512
|
-
|
|
17732
|
+
const insertOp = this.doc.insert(line, start, text);
|
|
17733
|
+
const insertedLines = text.split(/\r?\n/);
|
|
17734
|
+
if (insertedLines.length === 1) {
|
|
17735
|
+
this.cursorSet.set(line, start + text.length);
|
|
17736
|
+
}
|
|
17737
|
+
else {
|
|
17738
|
+
this.cursorSet.set(line + insertedLines.length - 1, insertedLines[insertedLines.length - 1].length);
|
|
17739
|
+
}
|
|
17513
17740
|
const cursorsAfter = this.cursorSet.getCursorPositions();
|
|
17514
17741
|
this.undoManager.record(insertOp, cursorsAfter);
|
|
17515
17742
|
this._rebuildLines();
|
|
@@ -17520,15 +17747,7 @@
|
|
|
17520
17747
|
if (!this.autocomplete || !this._isAutoCompleteActive)
|
|
17521
17748
|
return null;
|
|
17522
17749
|
const pre = this.autocomplete.childNodes[this._selectedAutocompleteIndex];
|
|
17523
|
-
|
|
17524
|
-
for (let childSpan of pre.childNodes) {
|
|
17525
|
-
const span = childSpan;
|
|
17526
|
-
if (span.constructor != HTMLSpanElement || span.classList.contains('kind')) {
|
|
17527
|
-
continue;
|
|
17528
|
-
}
|
|
17529
|
-
word += span.textContent;
|
|
17530
|
-
}
|
|
17531
|
-
return word;
|
|
17750
|
+
return pre.insertText;
|
|
17532
17751
|
}
|
|
17533
17752
|
_afterCursorMove() {
|
|
17534
17753
|
this._renderCursors();
|
|
@@ -17561,7 +17780,8 @@
|
|
|
17561
17780
|
_resetGutter() {
|
|
17562
17781
|
// Use cached value or compute if not available (e.g., on initial load)
|
|
17563
17782
|
const tabsHeight = this._cachedTabsHeight || (this.tabs?.root.getBoundingClientRect().height ?? 0);
|
|
17564
|
-
this.
|
|
17783
|
+
const statusPanelHeight = this._cachedStatusPanelHeight || (this.statusPanel?.root.getBoundingClientRect().height ?? 0);
|
|
17784
|
+
this.lineGutter.style.height = `calc(100% - ${tabsHeight + statusPanelHeight}px)`;
|
|
17565
17785
|
}
|
|
17566
17786
|
getMaxLineLength() {
|
|
17567
17787
|
if (!this.currentTab)
|