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.
@@ -10,7 +10,7 @@ const g = globalThis;
10
10
  let LX = g.LX;
11
11
  if (!LX) {
12
12
  LX = {
13
- version: '8.3.0',
13
+ version: '8.3.1',
14
14
  ready: false,
15
15
  extensions: [], // Store extensions used
16
16
  extraCommandbarEntries: [], // User specific entries for command bar
@@ -1 +1 @@
1
- {"version":3,"file":"Namespace.js","sources":["../../src/core/Namespace.ts"],"sourcesContent":["// Namespace.ts @jxarco\r\n\r\n/**\r\n * Main namespace\r\n * @namespace LX\r\n */\r\n\r\nconst g = globalThis as any;\r\n\r\n// Update global namespace if not present (Loading module)\r\n// Extension scripts rely on LX being globally available\r\nlet LX: any = g.LX;\r\n\r\nif ( !LX )\r\n{\r\n LX = {\r\n version: '8.3.0',\r\n ready: false,\r\n extensions: [], // Store extensions used\r\n extraCommandbarEntries: [], // User specific entries for command bar\r\n signals: {}, // Events and triggers\r\n activeDraggable: null, // Watch for the current active draggable\r\n\r\n spacingMode: 'default',\r\n layoutMode: 'app',\r\n\r\n MOUSE_LEFT_CLICK: 0,\r\n MOUSE_MIDDLE_CLICK: 1,\r\n MOUSE_RIGHT_CLICK: 2,\r\n\r\n MOUSE_DOUBLE_CLICK: 2,\r\n MOUSE_TRIPLE_CLICK: 3,\r\n\r\n CURVE_MOVEOUT_CLAMP: 0,\r\n CURVE_MOVEOUT_DELETE: 1,\r\n\r\n DRAGGABLE_Z_INDEX: 101\r\n };\r\n\r\n g.LX = LX;\r\n}\r\n\r\nexport { LX };\r\n"],"names":[],"mappings":";AAAA;AAEA;;;AAGG;AAEH,MAAM,CAAC,GAAG,UAAiB;AAE3B;AACA;AACA,IAAI,EAAE,GAAQ,CAAC,CAAC;AAEhB,IAAK,CAAC,EAAE,EACR;AACI,IAAA,EAAE,GAAG;AACD,QAAA,OAAO,EAAE,OAAO;AAChB,QAAA,KAAK,EAAE,KAAK;QACZ,UAAU,EAAE,EAAE;QACd,sBAAsB,EAAE,EAAE;QAC1B,OAAO,EAAE,EAAE;QACX,eAAe,EAAE,IAAI;AAErB,QAAA,WAAW,EAAE,SAAS;AACtB,QAAA,UAAU,EAAE,KAAK;AAEjB,QAAA,gBAAgB,EAAE,CAAC;AACnB,QAAA,kBAAkB,EAAE,CAAC;AACrB,QAAA,iBAAiB,EAAE,CAAC;AAEpB,QAAA,kBAAkB,EAAE,CAAC;AACrB,QAAA,kBAAkB,EAAE,CAAC;AAErB,QAAA,mBAAmB,EAAE,CAAC;AACtB,QAAA,oBAAoB,EAAE,CAAC;AAEvB,QAAA,iBAAiB,EAAE;KACtB;AAED,IAAA,CAAC,CAAC,EAAE,GAAG,EAAE;AACb;;;;"}
1
+ {"version":3,"file":"Namespace.js","sources":["../../src/core/Namespace.ts"],"sourcesContent":["// Namespace.ts @jxarco\r\n\r\n/**\r\n * Main namespace\r\n * @namespace LX\r\n */\r\n\r\nconst g = globalThis as any;\r\n\r\n// Update global namespace if not present (Loading module)\r\n// Extension scripts rely on LX being globally available\r\nlet LX: any = g.LX;\r\n\r\nif ( !LX )\r\n{\r\n LX = {\r\n version: '8.3.1',\r\n ready: false,\r\n extensions: [], // Store extensions used\r\n extraCommandbarEntries: [], // User specific entries for command bar\r\n signals: {}, // Events and triggers\r\n activeDraggable: null, // Watch for the current active draggable\r\n\r\n spacingMode: 'default',\r\n layoutMode: 'app',\r\n\r\n MOUSE_LEFT_CLICK: 0,\r\n MOUSE_MIDDLE_CLICK: 1,\r\n MOUSE_RIGHT_CLICK: 2,\r\n\r\n MOUSE_DOUBLE_CLICK: 2,\r\n MOUSE_TRIPLE_CLICK: 3,\r\n\r\n CURVE_MOVEOUT_CLAMP: 0,\r\n CURVE_MOVEOUT_DELETE: 1,\r\n\r\n DRAGGABLE_Z_INDEX: 101\r\n };\r\n\r\n g.LX = LX;\r\n}\r\n\r\nexport { LX };\r\n"],"names":[],"mappings":";AAAA;AAEA;;;AAGG;AAEH,MAAM,CAAC,GAAG,UAAiB;AAE3B;AACA;AACA,IAAI,EAAE,GAAQ,CAAC,CAAC;AAEhB,IAAK,CAAC,EAAE,EACR;AACI,IAAA,EAAE,GAAG;AACD,QAAA,OAAO,EAAE,OAAO;AAChB,QAAA,KAAK,EAAE,KAAK;QACZ,UAAU,EAAE,EAAE;QACd,sBAAsB,EAAE,EAAE;QAC1B,OAAO,EAAE,EAAE;QACX,eAAe,EAAE,IAAI;AAErB,QAAA,WAAW,EAAE,SAAS;AACtB,QAAA,UAAU,EAAE,KAAK;AAEjB,QAAA,gBAAgB,EAAE,CAAC;AACnB,QAAA,kBAAkB,EAAE,CAAC;AACrB,QAAA,iBAAiB,EAAE,CAAC;AAEpB,QAAA,kBAAkB,EAAE,CAAC;AACrB,QAAA,kBAAkB,EAAE,CAAC;AAErB,QAAA,mBAAmB,EAAE,CAAC;AACtB,QAAA,oBAAoB,EAAE,CAAC;AAEvB,QAAA,iBAAiB,EAAE;KACtB;AAED,IAAA,CAAC,CAAC,EAAE,GAAG,EAAE;AACb;;;;"}
@@ -349,7 +349,8 @@ export declare class CodeEditor {
349
349
  private _init;
350
350
  clear(): void;
351
351
  addExplorerItem(item: any): void;
352
- setText(text: string): void;
352
+ setText(text: string, language?: string, detectLang?: boolean): void;
353
+ private _detectLanguage;
353
354
  appendText(text: string): void;
354
355
  getText(): string;
355
356
  setLanguage(name: string, extension?: string): void;
@@ -2137,6 +2137,21 @@ class CodeEditor {
2137
2137
  this._inputArea.addEventListener('blur', () => this._setFocused(false));
2138
2138
  this.codeArea.root.addEventListener('mousedown', this._onMouseDown.bind(this));
2139
2139
  this.codeArea.root.addEventListener('contextmenu', this._onMouseDown.bind(this));
2140
+ this.codeArea.root.addEventListener('mouseover', (e) => {
2141
+ const link = e.target.closest('.code-link');
2142
+ if (link && e.ctrlKey)
2143
+ link.classList.add('hovered');
2144
+ });
2145
+ this.codeArea.root.addEventListener('mouseout', (e) => {
2146
+ const link = e.target.closest('.code-link');
2147
+ if (link)
2148
+ link.classList.remove('hovered');
2149
+ });
2150
+ this.codeArea.root.addEventListener('mousemove', (e) => {
2151
+ const link = e.target.closest('.code-link');
2152
+ if (link)
2153
+ link.classList.toggle('hovered', e.ctrlKey);
2154
+ });
2140
2155
  // Bottom status panel
2141
2156
  this.statusPanel = this._createStatusPanel(options);
2142
2157
  if (this.statusPanel) {
@@ -2224,18 +2239,148 @@ class CodeEditor {
2224
2239
  }
2225
2240
  }
2226
2241
  ;
2227
- setText(text) {
2242
+ setText(text, language, detectLang = false) {
2228
2243
  if (!this.currentTab)
2229
2244
  return;
2230
2245
  this.doc.setText(text);
2231
2246
  this.cursorSet.set(0, 0);
2232
2247
  this.undoManager.clear();
2233
2248
  this._lineStates = [];
2249
+ if (language) {
2250
+ this.setLanguage(language);
2251
+ }
2252
+ else if (detectLang) {
2253
+ const detected = this._detectLanguage(text);
2254
+ if (detected)
2255
+ this.setLanguage(detected);
2256
+ }
2234
2257
  this._renderAllLines();
2235
2258
  this._renderCursors();
2236
2259
  this._renderSelections();
2237
2260
  this.resize(true);
2238
2261
  }
2262
+ _detectLanguage(text) {
2263
+ const scores = {};
2264
+ const add = (lang, pts) => { scores[lang] = (scores[lang] ?? 0) + pts; };
2265
+ // Score using reservedWords from each registered language
2266
+ const textWords = new Set(text.match(/\b\w+\b/g) ?? []);
2267
+ const totalWords = Math.max(textWords.size, 1);
2268
+ for (const langName of Tokenizer.getRegisteredLanguages()) {
2269
+ const langDef = Tokenizer.getLanguage(langName);
2270
+ if (!langDef?.reservedWords?.length)
2271
+ continue;
2272
+ let hits = 0;
2273
+ for (const word of langDef.reservedWords) {
2274
+ if (textWords.has(word))
2275
+ hits++;
2276
+ }
2277
+ if (hits > 0) {
2278
+ const vocabRatio = hits / langDef.reservedWords.length;
2279
+ const textRatio = hits / totalWords;
2280
+ add(langName, Math.round((vocabRatio + textRatio) * 40));
2281
+ }
2282
+ }
2283
+ // Add scores based on "important" structural words only
2284
+ // HTML
2285
+ if (/<!DOCTYPE\s+html/i.test(text))
2286
+ add('HTML', 20);
2287
+ if (/<html[\s>]/i.test(text))
2288
+ add('HTML', 15);
2289
+ if (/<\/?(div|span|body|head|script|style|meta)\b/i.test(text))
2290
+ add('HTML', 8);
2291
+ // JSON — must come before JS checks (starts with { or [)
2292
+ if (/^\s*[\[{]/.test(text) && /"\s*:\s*/.test(text) && !/function|=>|const|var|let/.test(text))
2293
+ add('JSON', 15);
2294
+ // CSS
2295
+ if (/[\w-]+\s*:\s*[\w#\d"'(]+.*;/.test(text) && /[{}]/.test(text) && !/<\w+/.test(text))
2296
+ add('CSS', 10);
2297
+ if (/@(media|keyframes|import|charset|font-face)\b/.test(text))
2298
+ add('CSS', 15);
2299
+ // WGSL
2300
+ if (/@(vertex|fragment|compute|group|binding|builtin)\b/.test(text))
2301
+ add('WGSL', 20);
2302
+ if (/\bfn\s+\w+\s*\(/.test(text) && /\bvar\b/.test(text))
2303
+ add('WGSL', 10);
2304
+ if (/\b(vec2f|vec3f|vec4f|mat4x4f|f32|u32|i32)\b/.test(text))
2305
+ add('WGSL', 12);
2306
+ // GLSL
2307
+ if (/\b(gl_Position|gl_FragColor|gl_FragCoord)\b/.test(text))
2308
+ add('GLSL', 20);
2309
+ if (/\b(uniform|varying|attribute)\s+\w/.test(text))
2310
+ add('GLSL', 10);
2311
+ if (/\b(vec2|vec3|vec4|mat4|sampler2D)\b/.test(text) && !/vec2f|vec3f/.test(text))
2312
+ add('GLSL', 8);
2313
+ // HLSL
2314
+ if (/\b(SV_Position|SV_Target|SV_Depth)\b/.test(text))
2315
+ add('HLSL', 20);
2316
+ if (/\b(cbuffer|tbuffer|float4|float3|float2|Texture2D)\b/.test(text))
2317
+ add('HLSL', 12);
2318
+ // Python
2319
+ if (/^\s*def\s+\w+\s*\(/m.test(text))
2320
+ add('Python', 15);
2321
+ if (/^\s*import\s+\w/m.test(text) && !/\bfrom\s+['"]/.test(text))
2322
+ add('Python', 8);
2323
+ if (/^\s*class\s+\w+(\s*\(.*\))?:/m.test(text))
2324
+ add('Python', 10);
2325
+ if (/\bprint\s*\(/.test(text) && /:\s*$/.test(text))
2326
+ add('Python', 5);
2327
+ if (/elif\b|lambda\b|self\.\w/.test(text))
2328
+ add('Python', 8);
2329
+ // Rust
2330
+ if (/\bfn\s+\w+\s*\(/.test(text) && /\blet\s+mut\b/.test(text))
2331
+ add('Rust', 15);
2332
+ if (/\b(impl|trait|enum|struct)\s+\w+/.test(text) && /\bfn\b/.test(text))
2333
+ add('Rust', 12);
2334
+ if (/use\s+std::|use\s+\w+::\w+/.test(text))
2335
+ add('Rust', 10);
2336
+ if (/println!\s*\(/.test(text))
2337
+ add('Rust', 8);
2338
+ // C++
2339
+ if (/#include\s*<[\w./]+>/.test(text))
2340
+ add('C++', 15);
2341
+ if (/\bstd::\w+/.test(text))
2342
+ add('C++', 12);
2343
+ if (/\b(class|template|namespace|nullptr|new\s+\w)\b/.test(text))
2344
+ add('C++', 8);
2345
+ if (/(::|->)\w+/.test(text))
2346
+ add('C++', 6);
2347
+ // C
2348
+ if (/#include\s*<[\w.]+\.h>/.test(text))
2349
+ add('C', 12);
2350
+ if (/\bint\s+main\s*\(/.test(text))
2351
+ add('C', 15);
2352
+ if (/\b(printf|scanf|malloc|free|sizeof)\s*\(/.test(text))
2353
+ add('C', 10);
2354
+ // TypeScript — before JS (is a superset)
2355
+ if (/:\s*(string|number|boolean|void|any|never|unknown)\b/.test(text))
2356
+ add('TypeScript', 12);
2357
+ if (/\binterface\s+\w+/.test(text))
2358
+ add('TypeScript', 12);
2359
+ if (/\btype\s+\w+\s*=/.test(text))
2360
+ add('TypeScript', 10);
2361
+ if (/\bas\s+(string|number|any|unknown)\b/.test(text))
2362
+ add('TypeScript', 8);
2363
+ if (/\benum\s+\w+\s*\{/.test(text))
2364
+ add('TypeScript', 8);
2365
+ // JavaScript
2366
+ if (/\b(const|let|var)\s+\w+\s*=/.test(text))
2367
+ add('JavaScript', 8);
2368
+ if (/=>\s*[\w{(]/.test(text))
2369
+ add('JavaScript', 6);
2370
+ if (/\b(import|export)\s+(default|{|\*)/.test(text))
2371
+ add('JavaScript', 8);
2372
+ if (/\bconsole\.(log|warn|error)\b/.test(text))
2373
+ add('JavaScript', 6);
2374
+ // Markdown
2375
+ if (/^#{1,6}\s+\S/m.test(text))
2376
+ add('Markdown', 12);
2377
+ if (/\*\*\w.*?\*\*/.test(text) || /\[.+\]\(.+\)/.test(text))
2378
+ add('Markdown', 8);
2379
+ if (/^```\w*/m.test(text))
2380
+ add('Markdown', 10);
2381
+ const best = Object.entries(scores).sort((a, b) => b[1] - a[1])[0];
2382
+ return best && best[1] >= 8 ? best[0] : null;
2383
+ }
2239
2384
  appendText(text) {
2240
2385
  const cursor = this.cursorSet.getPrimary();
2241
2386
  const { line, col } = cursor.head;
@@ -2734,8 +2879,8 @@ class CodeEditor {
2734
2879
  : Tokenizer.initialState();
2735
2880
  const lineText = this.doc.getLine(lineIndex);
2736
2881
  const result = Tokenizer.tokenizeLine(lineText, this.language, prevState);
2737
- // Build HTML
2738
2882
  const langClass = this.language.name.toLowerCase().replace(/[^a-z]/g, '');
2883
+ const URL_REGEX = /(https?:\/\/[^\s"'<>)\]]+)/g;
2739
2884
  let html = '';
2740
2885
  for (const token of result.tokens) {
2741
2886
  const cls = TOKEN_CLASS_MAP[token.type];
@@ -2743,11 +2888,15 @@ class CodeEditor {
2743
2888
  .replace(/&/g, '&amp;')
2744
2889
  .replace(/</g, '&lt;')
2745
2890
  .replace(/>/g, '&gt;');
2891
+ // Wrap URLs in comment tokens with a clickable span
2892
+ const content = (token.type === 'comment')
2893
+ ? escaped.replace(URL_REGEX, `<span class="code-link" data-url="$1">$1</span>`)
2894
+ : escaped;
2746
2895
  if (cls) {
2747
- html += `<span class="${cls} ${langClass}">${escaped}</span>`;
2896
+ html += `<span class="${cls} ${langClass}">${content}</span>`;
2748
2897
  }
2749
2898
  else {
2750
- html += escaped;
2899
+ html += content;
2751
2900
  }
2752
2901
  }
2753
2902
  return { html: html || '&nbsp;', endState: result.state, tokens: result.tokens };
@@ -3577,11 +3726,25 @@ class CodeEditor {
3577
3726
  const { line, col } = cursor.head;
3578
3727
  const indent = this.doc.getIndent(line);
3579
3728
  const spaces = ' '.repeat(indent);
3580
- const op = this.doc.insert(line, col, '\n' + spaces);
3581
- this.undoManager.record(op, this.cursorSet.getCursorPositions());
3582
- cursor.head = { line: line + 1, col: indent };
3583
- cursor.anchor = { ...cursor.head };
3584
- this.cursorSet.adjustOthers(idx, line, col, 0, 1);
3729
+ const charBefore = this.doc.getCharAt(line, col - 1);
3730
+ const charAfter = this.doc.getCharAt(line, col);
3731
+ const OPEN_CLOSE = { '{': '}', '[': ']', '(': ')' };
3732
+ if (charBefore && charAfter && OPEN_CLOSE[charBefore] === charAfter) {
3733
+ const innerSpaces = ' '.repeat(indent + this.tabSize);
3734
+ const insertion = '\n' + innerSpaces + '\n' + spaces;
3735
+ const op = this.doc.insert(line, col, insertion);
3736
+ this.undoManager.record(op, this.cursorSet.getCursorPositions());
3737
+ cursor.head = { line: line + 1, col: indent + this.tabSize };
3738
+ cursor.anchor = { ...cursor.head };
3739
+ this.cursorSet.adjustOthers(idx, line, col, 0, 2);
3740
+ }
3741
+ else {
3742
+ const op = this.doc.insert(line, col, '\n' + spaces);
3743
+ this.undoManager.record(op, this.cursorSet.getCursorPositions());
3744
+ cursor.head = { line: line + 1, col: indent };
3745
+ cursor.anchor = { ...cursor.head };
3746
+ this.cursorSet.adjustOthers(idx, line, col, 0, 1);
3747
+ }
3585
3748
  }
3586
3749
  this._rebuildLines();
3587
3750
  this._afterCursorMove();
@@ -3595,8 +3758,36 @@ class CodeEditor {
3595
3758
  for (const idx of this.cursorSet.sortedIndicesBottomUp()) {
3596
3759
  const cursor = this.cursorSet.cursors[idx];
3597
3760
  const { line, col } = cursor.head;
3598
- if (shift) {
3599
- // Dedent: remove up to tabSize spaces from start
3761
+ const anchorLine = cursor.anchor.line;
3762
+ // Multiline selection: indent/dedent all lines in the selection
3763
+ const startLine = Math.min(line, anchorLine);
3764
+ const endLine = Math.max(line, anchorLine);
3765
+ const isMultiline = startLine !== endLine;
3766
+ if (isMultiline) {
3767
+ for (let i = startLine; i <= endLine; i++) {
3768
+ if (shift) {
3769
+ const lineText = this.doc.getLine(i);
3770
+ let spacesToRemove = 0;
3771
+ while (spacesToRemove < this.tabSize && spacesToRemove < lineText.length && lineText[spacesToRemove] === ' ') {
3772
+ spacesToRemove++;
3773
+ }
3774
+ if (spacesToRemove > 0) {
3775
+ const op = this.doc.delete(i, 0, spacesToRemove);
3776
+ this.undoManager.record(op, this.cursorSet.getCursorPositions());
3777
+ }
3778
+ }
3779
+ else {
3780
+ const spaces = ' '.repeat(this.tabSize);
3781
+ const op = this.doc.insert(i, 0, spaces);
3782
+ this.undoManager.record(op, this.cursorSet.getCursorPositions());
3783
+ }
3784
+ }
3785
+ const delta = shift ? -this.tabSize : this.tabSize;
3786
+ cursor.head = { line, col: Math.max(0, col + delta) };
3787
+ cursor.anchor = { line: anchorLine, col: Math.max(0, cursor.anchor.col + delta) };
3788
+ }
3789
+ else if (shift) {
3790
+ // Single line dedent: remove up to tabSize spaces from start
3600
3791
  const lineText = this.doc.getLine(line);
3601
3792
  let spacesToRemove = 0;
3602
3793
  while (spacesToRemove < this.tabSize && spacesToRemove < lineText.length && lineText[spacesToRemove] === ' ') {
@@ -3611,6 +3802,7 @@ class CodeEditor {
3611
3802
  }
3612
3803
  }
3613
3804
  else {
3805
+ // Single line indent: insert spaces at cursor
3614
3806
  const spacesToAdd = this.tabSize - (col % this.tabSize);
3615
3807
  const spaces = ' '.repeat(spacesToAdd);
3616
3808
  const op = this.doc.insert(line, col, spaces);
@@ -3644,8 +3836,10 @@ class CodeEditor {
3644
3836
  this.cursorSet.adjustOthers(idx, start.line, start.col, -colDelta, -linesRemoved);
3645
3837
  anyDeleted = true;
3646
3838
  }
3647
- if (anyDeleted)
3839
+ if (anyDeleted) {
3648
3840
  this._rebuildLines();
3841
+ this._doHideAutocomplete();
3842
+ }
3649
3843
  }
3650
3844
  // Clipboard helpers:
3651
3845
  _doCopy() {
@@ -3759,6 +3953,15 @@ class CodeEditor {
3759
3953
  return;
3760
3954
  if (this.autocomplete && this.autocomplete.contains(e.target))
3761
3955
  return;
3956
+ // Ctrl+click: open link if cursor is over a code-link span
3957
+ if (e.ctrlKey && e.button === 0) {
3958
+ const target = e.target;
3959
+ const link = target.closest('.code-link');
3960
+ if (link?.dataset.url) {
3961
+ window.open(link.dataset.url, '_blank');
3962
+ return;
3963
+ }
3964
+ }
3762
3965
  e.preventDefault(); // Prevent browser from stealing focus from _inputArea
3763
3966
  this._wasPaired = false;
3764
3967
  // Calculate line and column from click position
@@ -3876,9 +4079,9 @@ class CodeEditor {
3876
4079
  }
3877
4080
  const suggestions = [];
3878
4081
  const added = new Set();
3879
- const addSuggestion = (label, kind, scope, detail) => {
4082
+ const addSuggestion = (label, kind, scope, detail, insertText) => {
3880
4083
  if (!added.has(label)) {
3881
- suggestions.push({ label, kind, scope, detail });
4084
+ suggestions.push({ label, kind, scope, detail, insertText });
3882
4085
  added.add(label);
3883
4086
  }
3884
4087
  };
@@ -3900,8 +4103,9 @@ class CodeEditor {
3900
4103
  const label = typeof suggestion === 'string' ? suggestion : suggestion.label;
3901
4104
  const kind = typeof suggestion === 'object' ? suggestion.kind : undefined;
3902
4105
  const detail = typeof suggestion === 'object' ? suggestion.detail : undefined;
4106
+ const insertText = typeof suggestion === 'object' ? suggestion.insertText : suggestion;
3903
4107
  if (label.toLowerCase().startsWith(word.toLowerCase())) {
3904
- addSuggestion(label, kind, undefined, detail);
4108
+ addSuggestion(label, kind, undefined, detail, insertText);
3905
4109
  }
3906
4110
  }
3907
4111
  // Close autocomplete if no suggestions
@@ -3921,6 +4125,7 @@ class CodeEditor {
3921
4125
  // Render suggestions
3922
4126
  suggestions.forEach((suggestion, index) => {
3923
4127
  const item = document.createElement('pre');
4128
+ item.insertText = suggestion.insertText ?? suggestion.label;
3924
4129
  if (index === this._selectedAutocompleteIndex)
3925
4130
  item.classList.add('selected');
3926
4131
  const currSuggestion = suggestion.label;
@@ -4039,8 +4244,8 @@ class CodeEditor {
4039
4244
  * Insert the selected autocomplete word at cursor.
4040
4245
  */
4041
4246
  _doAutocompleteWord() {
4042
- const word = this._getSelectedAutoCompleteWord();
4043
- if (!word)
4247
+ const text = this._getSelectedAutoCompleteWord();
4248
+ if (!text)
4044
4249
  return;
4045
4250
  const cursor = this.cursorSet.getPrimary().head;
4046
4251
  const { start, end } = this._getWordAtCursor();
@@ -4050,8 +4255,14 @@ class CodeEditor {
4050
4255
  const deleteOp = this.doc.delete(line, start, end - start);
4051
4256
  this.undoManager.record(deleteOp, cursorsBefore);
4052
4257
  }
4053
- const insertOp = this.doc.insert(line, start, word);
4054
- this.cursorSet.set(line, start + word.length);
4258
+ const insertOp = this.doc.insert(line, start, text);
4259
+ const insertedLines = text.split(/\r?\n/);
4260
+ if (insertedLines.length === 1) {
4261
+ this.cursorSet.set(line, start + text.length);
4262
+ }
4263
+ else {
4264
+ this.cursorSet.set(line + insertedLines.length - 1, insertedLines[insertedLines.length - 1].length);
4265
+ }
4055
4266
  const cursorsAfter = this.cursorSet.getCursorPositions();
4056
4267
  this.undoManager.record(insertOp, cursorsAfter);
4057
4268
  this._rebuildLines();
@@ -4062,15 +4273,7 @@ class CodeEditor {
4062
4273
  if (!this.autocomplete || !this._isAutoCompleteActive)
4063
4274
  return null;
4064
4275
  const pre = this.autocomplete.childNodes[this._selectedAutocompleteIndex];
4065
- var word = '';
4066
- for (let childSpan of pre.childNodes) {
4067
- const span = childSpan;
4068
- if (span.constructor != HTMLSpanElement || span.classList.contains('kind')) {
4069
- continue;
4070
- }
4071
- word += span.textContent;
4072
- }
4073
- return word;
4276
+ return pre.insertText;
4074
4277
  }
4075
4278
  _afterCursorMove() {
4076
4279
  this._renderCursors();
@@ -4103,7 +4306,8 @@ class CodeEditor {
4103
4306
  _resetGutter() {
4104
4307
  // Use cached value or compute if not available (e.g., on initial load)
4105
4308
  const tabsHeight = this._cachedTabsHeight || (this.tabs?.root.getBoundingClientRect().height ?? 0);
4106
- this.lineGutter.style.height = `calc(100% - ${tabsHeight}px)`;
4309
+ const statusPanelHeight = this._cachedStatusPanelHeight || (this.statusPanel?.root.getBoundingClientRect().height ?? 0);
4310
+ this.lineGutter.style.height = `calc(100% - ${tabsHeight + statusPanelHeight}px)`;
4107
4311
  }
4108
4312
  getMaxLineLength() {
4109
4313
  if (!this.currentTab)