@joinezco/codeblock 0.0.8 → 0.0.10
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/dist/editor.d.ts +30 -3
- package/dist/editor.js +416 -43
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/lsps/index.d.ts +5 -0
- package/dist/lsps/index.js +9 -2
- package/dist/lsps/typescript.d.ts +3 -1
- package/dist/lsps/typescript.js +8 -17
- package/dist/panels/settings.d.ts +22 -0
- package/dist/panels/settings.js +267 -0
- package/dist/panels/terminal.d.ts +3 -0
- package/dist/panels/terminal.js +76 -0
- package/dist/panels/toolbar.d.ts +53 -3
- package/dist/panels/toolbar.js +1336 -164
- package/dist/panels/toolbar.test.js +20 -14
- package/dist/rpc/transport.d.ts +2 -11
- package/dist/rpc/transport.js +19 -35
- package/dist/themes/index.js +226 -13
- package/dist/themes/vscode.js +3 -2
- package/dist/types.d.ts +5 -0
- package/dist/utils/fs.d.ts +22 -3
- package/dist/utils/fs.js +126 -21
- package/dist/utils/lsp.d.ts +26 -15
- package/dist/utils/lsp.js +79 -44
- package/dist/utils/search.d.ts +2 -0
- package/dist/utils/search.js +13 -4
- package/dist/utils/typescript-defaults.d.ts +57 -0
- package/dist/utils/typescript-defaults.js +208 -0
- package/dist/utils/typescript-defaults.test.d.ts +1 -0
- package/dist/utils/typescript-defaults.test.js +197 -0
- package/dist/workers/fs.worker.d.ts +4 -8
- package/dist/workers/fs.worker.js +30 -60
- package/dist/workers/javascript.worker.js +11 -9
- package/package.json +8 -4
- package/dist/assets/clike-C8IJ2oj_.js +0 -1
- package/dist/assets/cmake-BQqOBYOt.js +0 -1
- package/dist/assets/dockerfile-C_y-rIpk.js +0 -1
- package/dist/assets/fs.worker-BwEqZcql.ts +0 -109
- package/dist/assets/go-CTD25R5P.js +0 -1
- package/dist/assets/haskell-BWDZoCOh.js +0 -1
- package/dist/assets/index-9HdhmM_Y.js +0 -1
- package/dist/assets/index-C-QhPFHP.js +0 -3
- package/dist/assets/index-C3BnE2cG.js +0 -222
- package/dist/assets/index-CGx5MZO7.js +0 -6
- package/dist/assets/index-CIuq3uTk.js +0 -1
- package/dist/assets/index-CXFONXS8.js +0 -1
- package/dist/assets/index-D5Z27j1C.js +0 -1
- package/dist/assets/index-DWOBdRjn.js +0 -1
- package/dist/assets/index-Dvu-FFzd.js +0 -1
- package/dist/assets/index-Dx_VuNNd.js +0 -1
- package/dist/assets/index-I0dlv-r3.js +0 -1
- package/dist/assets/index-MGle_v2x.js +0 -1
- package/dist/assets/index-N-GE7HTU.js +0 -1
- package/dist/assets/index-aEsF5o-7.js +0 -2
- package/dist/assets/index-as7ELo0J.js +0 -1
- package/dist/assets/index-gUUzXNuP.js +0 -1
- package/dist/assets/index-pGm0qkrJ.js +0 -13
- package/dist/assets/javascript.worker-C1zGArKk.js +0 -527
- package/dist/assets/lua-BgMRiT3U.js +0 -1
- package/dist/assets/perl-CdXCOZ3F.js +0 -1
- package/dist/assets/process-Dw9K5EnD.js +0 -1357
- package/dist/assets/properties-C78fOPTZ.js +0 -1
- package/dist/assets/ruby-B2Rjki9n.js +0 -1
- package/dist/assets/shell-CjFT_Tl9.js +0 -1
- package/dist/assets/swift-BzpIVaGY.js +0 -1
- package/dist/assets/toml-BXUEaScT.js +0 -1
- package/dist/assets/vb-CmGdzxic.js +0 -1
- package/dist/e2e/example.spec.d.ts +0 -5
- package/dist/e2e/example.spec.js +0 -44
- package/dist/index.html +0 -16
- package/dist/resources/config.json +0 -13
- package/dist/snapshot.bin +0 -0
- package/dist/styles.css +0 -7
package/dist/utils/fs.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Comlink from "comlink";
|
|
2
2
|
import { watchOptionsTransferHandler, asyncGeneratorTransferHandler } from "../rpc/serde";
|
|
3
3
|
import { FileType } from '@volar/language-service';
|
|
4
|
+
import { TopLevelFs } from "@joinezco/jswasi/filesystem";
|
|
4
5
|
import { constants } from "@joinezco/jswasi";
|
|
5
6
|
Comlink.transferHandlers.set("asyncGenerator", asyncGeneratorTransferHandler);
|
|
6
7
|
Comlink.transferHandlers.set("watchOptions", watchOptionsTransferHandler);
|
|
@@ -58,7 +59,6 @@ export var Vfs;
|
|
|
58
59
|
let cur = "/";
|
|
59
60
|
for (const part of parts) {
|
|
60
61
|
cur = cur === "/" ? `/${part}` : `${cur}/${part}`;
|
|
61
|
-
console.log('creating', { cur, abs });
|
|
62
62
|
const exists = await this.exists(cur);
|
|
63
63
|
if (exists)
|
|
64
64
|
continue;
|
|
@@ -115,13 +115,25 @@ export var Vfs;
|
|
|
115
115
|
type: toVolarFileType(filestat.filetype),
|
|
116
116
|
};
|
|
117
117
|
},
|
|
118
|
+
async unlink(path) {
|
|
119
|
+
const abs = ensureAbs(path);
|
|
120
|
+
const res = await jswasiFs.removeEntry(abs, false);
|
|
121
|
+
if (res !== WASI_ESUCCESS) {
|
|
122
|
+
throw new Error(`unlink failed (${res}) for ${abs}`);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
118
125
|
};
|
|
119
126
|
};
|
|
120
|
-
// TODO: this is incorrect, fs is a Comlink proxy
|
|
121
127
|
Vfs.fromMemfs = (fs) => {
|
|
122
128
|
return {
|
|
123
129
|
async readFile(path) {
|
|
124
|
-
|
|
130
|
+
const result = await fs.promises.readFile(path, { encoding: "utf-8" });
|
|
131
|
+
// memfs may return a Buffer — ensure we return a string
|
|
132
|
+
if (typeof result === 'string')
|
|
133
|
+
return result;
|
|
134
|
+
if (result && typeof result.toString === 'function')
|
|
135
|
+
return result.toString('utf-8');
|
|
136
|
+
return String(result ?? '');
|
|
125
137
|
},
|
|
126
138
|
async writeFile(path, data) {
|
|
127
139
|
await fs.promises.writeFile(path, data);
|
|
@@ -178,7 +190,10 @@ export var Vfs;
|
|
|
178
190
|
catch (err) {
|
|
179
191
|
return null;
|
|
180
192
|
}
|
|
181
|
-
}
|
|
193
|
+
},
|
|
194
|
+
async unlink(path) {
|
|
195
|
+
await fs.promises.unlink(path);
|
|
196
|
+
},
|
|
182
197
|
};
|
|
183
198
|
};
|
|
184
199
|
Vfs.fromNodelike = (fs) => {
|
|
@@ -246,9 +261,25 @@ export var Vfs;
|
|
|
246
261
|
catch (err) {
|
|
247
262
|
return null;
|
|
248
263
|
}
|
|
249
|
-
}
|
|
264
|
+
},
|
|
265
|
+
async unlink(path) {
|
|
266
|
+
await fs.unlink(path);
|
|
267
|
+
},
|
|
250
268
|
};
|
|
251
269
|
};
|
|
270
|
+
/**
|
|
271
|
+
* Create an FSA (File System Access / OPFS) backed filesystem.
|
|
272
|
+
* Data persists across page reloads via the browser's Origin Private File System.
|
|
273
|
+
*
|
|
274
|
+
* @param name - Unique name for the FSA storage bucket (default: 'codeblock')
|
|
275
|
+
*/
|
|
276
|
+
Vfs.fsa = async (name = 'codeblock') => {
|
|
277
|
+
const topFs = new TopLevelFs();
|
|
278
|
+
await topFs.addMount(
|
|
279
|
+
// @ts-ignore - TopLevelFs.addMount typing
|
|
280
|
+
undefined, "", undefined, "/", "fsa", 0n, { name, keepMetadata: "true", create: "true" });
|
|
281
|
+
return Vfs.fromJswasiFs(topFs);
|
|
282
|
+
};
|
|
252
283
|
/**
|
|
253
284
|
* Create a filesystem worker with optional snapshot data.
|
|
254
285
|
*
|
|
@@ -257,27 +288,22 @@ export var Vfs;
|
|
|
257
288
|
* for better performance with large files.
|
|
258
289
|
*/
|
|
259
290
|
Vfs.worker = async (bufferOrUrl) => {
|
|
260
|
-
|
|
261
|
-
const worker = new SharedWorker(url, { type: 'module' });
|
|
291
|
+
// TODO: fix this for non-Vite consumers
|
|
292
|
+
const worker = new SharedWorker(new URL('../workers/fs.worker.js', import.meta.url), { type: 'module' });
|
|
262
293
|
worker.port.start();
|
|
263
294
|
const proxy = Comlink.wrap(worker.port);
|
|
264
|
-
let
|
|
295
|
+
let vfs;
|
|
265
296
|
if (!bufferOrUrl) {
|
|
266
|
-
|
|
267
|
-
({ fs } = await proxy.mount({ mountPoint: '/' }));
|
|
297
|
+
vfs = await proxy.mount({ mountPoint: '/' });
|
|
268
298
|
}
|
|
269
299
|
else if (typeof bufferOrUrl === 'string') {
|
|
270
|
-
|
|
271
|
-
({ fs } = await proxy.mountFromUrl({
|
|
272
|
-
url: bufferOrUrl,
|
|
273
|
-
mountPoint: '/'
|
|
274
|
-
}));
|
|
300
|
+
vfs = await proxy.mountFromUrl({ url: bufferOrUrl, mountPoint: '/' });
|
|
275
301
|
}
|
|
276
302
|
else {
|
|
277
|
-
|
|
278
|
-
({ fs } = await proxy.mount(Comlink.transfer({ buffer: bufferOrUrl, mountPoint: "/" }, [bufferOrUrl])));
|
|
303
|
+
vfs = await proxy.mount(Comlink.transfer({ buffer: bufferOrUrl, mountPoint: "/" }, [bufferOrUrl]));
|
|
279
304
|
}
|
|
280
|
-
|
|
305
|
+
console.debug('Filesystem worker mounted');
|
|
306
|
+
return vfs;
|
|
281
307
|
};
|
|
282
308
|
async function* walk(fs, path) {
|
|
283
309
|
const files = await fs.readDir(path);
|
|
@@ -295,16 +321,95 @@ export var Vfs;
|
|
|
295
321
|
})(Vfs || (Vfs = {}));
|
|
296
322
|
export class VolarFs {
|
|
297
323
|
#fs;
|
|
324
|
+
#fileCache = new Map();
|
|
325
|
+
#statCache = new Map();
|
|
326
|
+
#dirCache = new Map();
|
|
298
327
|
constructor(fs) {
|
|
299
328
|
this.#fs = fs;
|
|
300
329
|
}
|
|
301
|
-
|
|
330
|
+
/**
|
|
331
|
+
* Synchronously populate the cache from a pre-resolved map of path → content.
|
|
332
|
+
* This bypasses async VFS reads entirely, ensuring TypeScript gets lib files
|
|
333
|
+
* immediately on first program creation.
|
|
334
|
+
*/
|
|
335
|
+
preloadFromMap(files) {
|
|
336
|
+
const now = Date.now();
|
|
337
|
+
for (const [path, content] of Object.entries(files)) {
|
|
338
|
+
this.#fileCache.set(path, content);
|
|
339
|
+
this.#statCache.set(path, {
|
|
340
|
+
type: FileType.File,
|
|
341
|
+
ctime: now,
|
|
342
|
+
mtime: now,
|
|
343
|
+
size: content.length,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
// Build directory tree from file paths
|
|
347
|
+
const dirChildren = new Map();
|
|
348
|
+
for (const path of Object.keys(files)) {
|
|
349
|
+
let dir = path;
|
|
350
|
+
let child = '';
|
|
351
|
+
while (true) {
|
|
352
|
+
const lastSlash = dir.lastIndexOf('/');
|
|
353
|
+
if (lastSlash < 0)
|
|
354
|
+
break;
|
|
355
|
+
child = dir.substring(lastSlash + 1);
|
|
356
|
+
dir = dir.substring(0, lastSlash) || '/';
|
|
357
|
+
if (!dirChildren.has(dir)) {
|
|
358
|
+
dirChildren.set(dir, new Map());
|
|
359
|
+
}
|
|
360
|
+
const children = dirChildren.get(dir);
|
|
361
|
+
// First encounter of this child — it's the file itself
|
|
362
|
+
if (!children.has(child)) {
|
|
363
|
+
// If we've already seen this as a parent dir, it's a Directory
|
|
364
|
+
children.set(child, dirChildren.has(dir === '/' ? `/${child}` : `${dir}/${child}`) ? FileType.Directory : FileType.File);
|
|
365
|
+
}
|
|
366
|
+
if (dir === '/')
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Update directory type for children that are actually directories
|
|
371
|
+
for (const [dirPath, children] of dirChildren) {
|
|
372
|
+
for (const [name] of children) {
|
|
373
|
+
const fullPath = dirPath === '/' ? `/${name}` : `${dirPath}/${name}`;
|
|
374
|
+
if (dirChildren.has(fullPath)) {
|
|
375
|
+
children.set(name, FileType.Directory);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// Cache directory listings and stats
|
|
380
|
+
for (const [dirPath, children] of dirChildren) {
|
|
381
|
+
this.#dirCache.set(dirPath, [...children.entries()]);
|
|
382
|
+
this.#statCache.set(dirPath, {
|
|
383
|
+
type: FileType.Directory,
|
|
384
|
+
ctime: now,
|
|
385
|
+
mtime: now,
|
|
386
|
+
size: 0,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
stat(uri) {
|
|
391
|
+
const cached = this.#statCache.get(uri.path);
|
|
392
|
+
if (cached)
|
|
393
|
+
return cached;
|
|
302
394
|
return this.#fs.stat(uri.path);
|
|
303
395
|
}
|
|
304
|
-
|
|
396
|
+
readDirectory(uri) {
|
|
397
|
+
// Only use dirCache for node_modules subtree (stable, preloaded).
|
|
398
|
+
// Root and user directories must go through live VFS to pick up new files.
|
|
399
|
+
if (uri.path.startsWith('/node_modules/')) {
|
|
400
|
+
const cached = this.#dirCache.get(uri.path);
|
|
401
|
+
if (cached)
|
|
402
|
+
return cached;
|
|
403
|
+
}
|
|
305
404
|
return this.#fs.readDir(uri.path);
|
|
306
405
|
}
|
|
307
|
-
|
|
406
|
+
readFile(uri) {
|
|
407
|
+
const cached = this.#fileCache.get(uri.path);
|
|
408
|
+
if (cached !== undefined)
|
|
409
|
+
return cached;
|
|
308
410
|
return this.#fs.readFile(uri.path);
|
|
309
411
|
}
|
|
412
|
+
getCacheSize() {
|
|
413
|
+
return this.#fileCache.size;
|
|
414
|
+
}
|
|
310
415
|
}
|
package/dist/utils/lsp.d.ts
CHANGED
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
import { VfsInterface } from "../types";
|
|
2
|
-
import { LanguageServerClient } from "@marimo-team/codemirror-languageserver";
|
|
3
2
|
import { Extension } from "@codemirror/state";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
3
|
+
export declare const FileChangeType: {
|
|
4
|
+
readonly Created: 1;
|
|
5
|
+
readonly Changed: 2;
|
|
6
|
+
readonly Deleted: 3;
|
|
7
|
+
};
|
|
8
|
+
export interface LspLogEntry {
|
|
9
|
+
timestamp: number;
|
|
10
|
+
level: 'error' | 'warn' | 'info' | 'log';
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export declare namespace LspLog {
|
|
14
|
+
function entries(): readonly LspLogEntry[];
|
|
15
|
+
function push(level: LspLogEntry['level'], message: string): void;
|
|
16
|
+
function clear(): void;
|
|
17
|
+
function subscribe(fn: () => void): () => void;
|
|
18
|
+
}
|
|
9
19
|
export type ClientOptions = {
|
|
10
|
-
view: EditorView;
|
|
11
20
|
language: string;
|
|
12
21
|
path: string;
|
|
13
22
|
fs: VfsInterface;
|
|
23
|
+
libFiles?: Record<string, string>;
|
|
14
24
|
};
|
|
15
|
-
export declare const languageServerFactory: Map<string, (args: {
|
|
16
|
-
fs: VfsInterface;
|
|
17
|
-
}) => Promise<{
|
|
18
|
-
server: LanguageServer;
|
|
19
|
-
}>>;
|
|
20
25
|
export declare const lspWorkers: Map<string, SharedWorker>;
|
|
21
26
|
export declare namespace LSP {
|
|
22
|
-
function worker(language: string, fs: VfsInterface): Promise<{
|
|
27
|
+
function worker(language: string, fs: VfsInterface, libFiles?: Record<string, string>): Promise<{
|
|
23
28
|
worker: SharedWorker;
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
lspPort: MessagePort;
|
|
30
|
+
} | null>;
|
|
31
|
+
function client({ fs, language, path, libFiles }: ClientOptions): Promise<Extension | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Notify all connected LSP clients that a file was created, changed, or deleted.
|
|
34
|
+
* This sends workspace/didChangeWatchedFiles so the server re-evaluates the project.
|
|
35
|
+
*/
|
|
36
|
+
function notifyFileChanged(path: string, type?: number): void;
|
|
26
37
|
}
|
package/dist/utils/lsp.js
CHANGED
|
@@ -1,74 +1,109 @@
|
|
|
1
1
|
import * as Comlink from 'comlink';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { HighlightStyle } from "@codemirror/language";
|
|
5
|
-
import { languageSupportCompartment, renderMarkdownCode } from "../editor";
|
|
6
|
-
import markdownit from 'markdown-it';
|
|
7
|
-
import { vscodeLightDark } from "../themes/vscode";
|
|
2
|
+
import { LSPClient, languageServerExtensions } from "@codemirror/lsp-client";
|
|
3
|
+
import { messagePortTransport } from "../rpc/transport";
|
|
8
4
|
const clients = new Map();
|
|
9
|
-
//
|
|
10
|
-
export const
|
|
5
|
+
// FileChangeType from LSP spec
|
|
6
|
+
export const FileChangeType = { Created: 1, Changed: 2, Deleted: 3 };
|
|
7
|
+
const MAX_LOG_ENTRIES = 200;
|
|
8
|
+
const lspLogBuffer = [];
|
|
9
|
+
const lspLogListeners = new Set();
|
|
10
|
+
export var LspLog;
|
|
11
|
+
(function (LspLog) {
|
|
12
|
+
function entries() {
|
|
13
|
+
return lspLogBuffer;
|
|
14
|
+
}
|
|
15
|
+
LspLog.entries = entries;
|
|
16
|
+
function push(level, message) {
|
|
17
|
+
lspLogBuffer.push({ timestamp: Date.now(), level, message });
|
|
18
|
+
if (lspLogBuffer.length > MAX_LOG_ENTRIES) {
|
|
19
|
+
lspLogBuffer.splice(0, lspLogBuffer.length - MAX_LOG_ENTRIES);
|
|
20
|
+
}
|
|
21
|
+
for (const listener of lspLogListeners)
|
|
22
|
+
listener();
|
|
23
|
+
}
|
|
24
|
+
LspLog.push = push;
|
|
25
|
+
function clear() {
|
|
26
|
+
lspLogBuffer.length = 0;
|
|
27
|
+
for (const listener of lspLogListeners)
|
|
28
|
+
listener();
|
|
29
|
+
}
|
|
30
|
+
LspLog.clear = clear;
|
|
31
|
+
function subscribe(fn) {
|
|
32
|
+
lspLogListeners.add(fn);
|
|
33
|
+
return () => { lspLogListeners.delete(fn); };
|
|
34
|
+
}
|
|
35
|
+
LspLog.subscribe = subscribe;
|
|
36
|
+
})(LspLog || (LspLog = {}));
|
|
37
|
+
// Cached factory (Comlink-wrapped) and LSP port per language
|
|
38
|
+
const languageServerFactory = new Map();
|
|
39
|
+
const lspPorts = new Map();
|
|
11
40
|
export const lspWorkers = new Map();
|
|
12
41
|
export var LSP;
|
|
13
42
|
(function (LSP) {
|
|
14
|
-
async function worker(language, fs) {
|
|
15
|
-
let factory
|
|
16
|
-
|
|
43
|
+
async function worker(language, fs, libFiles) {
|
|
44
|
+
let factory;
|
|
45
|
+
let worker;
|
|
17
46
|
switch (language) {
|
|
18
47
|
case 'javascript':
|
|
19
48
|
case 'typescript':
|
|
20
49
|
factory = languageServerFactory.get('javascript');
|
|
21
50
|
worker = lspWorkers.get('javascript');
|
|
22
|
-
console.debug('got worker', { worker, factory });
|
|
23
51
|
if (!factory) {
|
|
24
52
|
worker = new SharedWorker(new URL('../workers/javascript.worker.js', import.meta.url), { type: 'module' });
|
|
25
53
|
worker.port.start();
|
|
26
54
|
lspWorkers.set('javascript', worker);
|
|
27
|
-
const
|
|
28
|
-
factory = createLanguageServer;
|
|
55
|
+
const wrapped = Comlink.wrap(worker.port);
|
|
56
|
+
factory = wrapped.createLanguageServer;
|
|
29
57
|
languageServerFactory.set('javascript', factory);
|
|
30
58
|
}
|
|
31
59
|
break;
|
|
60
|
+
default:
|
|
61
|
+
return null;
|
|
32
62
|
}
|
|
33
|
-
|
|
34
|
-
|
|
63
|
+
// fs is proxied (has methods), libFiles is plain data (structured clone)
|
|
64
|
+
// The factory returns a MessagePort for the LSP connection (separate from Comlink's port)
|
|
65
|
+
const lspPort = await factory(Comlink.proxy(fs), libFiles);
|
|
66
|
+
lspPort.start();
|
|
67
|
+
lspPorts.set(language, lspPort);
|
|
68
|
+
return { worker: worker, lspPort };
|
|
35
69
|
}
|
|
36
70
|
LSP.worker = worker;
|
|
37
|
-
async function client({ fs, language, path,
|
|
71
|
+
async function client({ fs, language, path, libFiles }) {
|
|
38
72
|
let client = clients.get(language);
|
|
39
|
-
let clientExtension;
|
|
40
73
|
const uri = `file:///${path}`;
|
|
41
74
|
if (!client) {
|
|
42
|
-
const
|
|
43
|
-
if (!
|
|
75
|
+
const result = await LSP.worker(language, fs, libFiles);
|
|
76
|
+
if (!result)
|
|
44
77
|
return null;
|
|
45
|
-
|
|
46
|
-
client = new
|
|
47
|
-
transport: new MessagePortTransport(worker.port),
|
|
78
|
+
const { lspPort } = result;
|
|
79
|
+
client = new LSPClient({
|
|
48
80
|
rootUri: 'file:///',
|
|
49
|
-
|
|
81
|
+
extensions: languageServerExtensions(),
|
|
82
|
+
notificationHandlers: {
|
|
83
|
+
"window/logMessage": (_client, params) => {
|
|
84
|
+
const level = params.type === 1 ? 'error' : params.type === 2 ? 'warn' : params.type === 3 ? 'info' : 'log';
|
|
85
|
+
LspLog.push(level, params.message);
|
|
86
|
+
return false; // fall through to default handler (console)
|
|
87
|
+
}
|
|
88
|
+
},
|
|
50
89
|
});
|
|
90
|
+
client.connect(messagePortTransport(lspPort));
|
|
91
|
+
clients.set(language, client);
|
|
51
92
|
}
|
|
52
|
-
|
|
53
|
-
clientExtension = { client, extension: [] };
|
|
54
|
-
clientExtension.extension = languageServerWithClient({
|
|
55
|
-
client: clientExtension.client,
|
|
56
|
-
documentUri: uri,
|
|
57
|
-
languageId: language,
|
|
58
|
-
allowHTMLContent: true,
|
|
59
|
-
markdownRenderer(markdown) {
|
|
60
|
-
const support = languageSupportCompartment.get(view.state);
|
|
61
|
-
const highlighter = vscodeLightDark[1].find(item => item.value instanceof HighlightStyle)?.value;
|
|
62
|
-
const parser = support.language?.parser;
|
|
63
|
-
const md = markdownit({
|
|
64
|
-
highlight: (str) => {
|
|
65
|
-
return renderMarkdownCode(str, parser, highlighter);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
return md.render(markdown);
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
return clientExtension;
|
|
93
|
+
return client.plugin(uri, language);
|
|
72
94
|
}
|
|
73
95
|
LSP.client = client;
|
|
96
|
+
/**
|
|
97
|
+
* Notify all connected LSP clients that a file was created, changed, or deleted.
|
|
98
|
+
* This sends workspace/didChangeWatchedFiles so the server re-evaluates the project.
|
|
99
|
+
*/
|
|
100
|
+
function notifyFileChanged(path, type = FileChangeType.Changed) {
|
|
101
|
+
const uri = `file:///${path}`;
|
|
102
|
+
for (const client of clients.values()) {
|
|
103
|
+
client.notification("workspace/didChangeWatchedFiles", {
|
|
104
|
+
changes: [{ uri, type }]
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
LSP.notifyFileChanged = notifyFileChanged;
|
|
74
109
|
})(LSP || (LSP = {}));
|
package/dist/utils/search.d.ts
CHANGED
|
@@ -15,7 +15,9 @@ export type HighlightedSearch = SearchResult & {
|
|
|
15
15
|
};
|
|
16
16
|
export declare class SearchIndex {
|
|
17
17
|
index: MiniSearch;
|
|
18
|
+
savePath?: string;
|
|
18
19
|
constructor(index: MiniSearch);
|
|
20
|
+
add(path: string): void;
|
|
19
21
|
search(...params: Parameters<MiniSearch['search']>): HighlightedSearch[];
|
|
20
22
|
/**
|
|
21
23
|
*
|
package/dist/utils/search.js
CHANGED
|
@@ -7,9 +7,16 @@ export const defaultFilter = (path) => {
|
|
|
7
7
|
};
|
|
8
8
|
export class SearchIndex {
|
|
9
9
|
index;
|
|
10
|
+
/// The VFS path this index was loaded from / saved to, if known.
|
|
11
|
+
savePath;
|
|
10
12
|
constructor(index) {
|
|
11
13
|
this.index = index;
|
|
12
14
|
}
|
|
15
|
+
add(path) {
|
|
16
|
+
if (!this.index.has(path)) {
|
|
17
|
+
this.index.add({ path });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
13
20
|
search(...params) {
|
|
14
21
|
const results = this.index.search(...params);
|
|
15
22
|
const highlights = this.highlight(results);
|
|
@@ -48,10 +55,12 @@ export class SearchIndex {
|
|
|
48
55
|
return new SearchIndex(index);
|
|
49
56
|
}
|
|
50
57
|
static async get(fs, path, fields = defaultFields) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
SearchIndex.from(
|
|
54
|
-
SearchIndex.build(fs, { fields, idField: 'path' }).then(
|
|
58
|
+
const data = await fs.exists(path) ? await fs.readFile(path) : null;
|
|
59
|
+
let index = data
|
|
60
|
+
? SearchIndex.from(data, fields)
|
|
61
|
+
: await SearchIndex.build(fs, { fields, idField: 'path' }).then(idx => idx.save(fs, path));
|
|
62
|
+
index.savePath = path;
|
|
63
|
+
return index;
|
|
55
64
|
}
|
|
56
65
|
static async build(fs, { filter = defaultFilter, ...rest }) {
|
|
57
66
|
const index = new MiniSearch({ ...rest });
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { VfsInterface } from "../types";
|
|
2
|
+
export type TypescriptDefaultsConfig = {
|
|
3
|
+
/** ES target, determines which lib files are needed. Default: "ES2020" */
|
|
4
|
+
target?: string;
|
|
5
|
+
/** Additional lib names to load beyond the ES target libs.
|
|
6
|
+
* Defaults to TypeScript's `.full` environment libs (dom, dom.iterable,
|
|
7
|
+
* dom.asynciterable, webworker.importscripts, scripthost).
|
|
8
|
+
* Pass `[]` to disable. */
|
|
9
|
+
additionalLibs?: string[];
|
|
10
|
+
/** Custom tsconfig compilerOptions merged with defaults */
|
|
11
|
+
compilerOptions?: Record<string, any>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Returns the list of TypeScript lib file names required for a given ES target,
|
|
15
|
+
* plus any additional libs (DOM by default).
|
|
16
|
+
* Names are without the `lib.` prefix and `.d.ts` suffix (e.g. "es5", "dom").
|
|
17
|
+
*/
|
|
18
|
+
export declare function getRequiredLibs(target?: string, additionalLibs?: string[]): string[];
|
|
19
|
+
/**
|
|
20
|
+
* Returns all individual lib names for the tsconfig `lib` field.
|
|
21
|
+
*
|
|
22
|
+
* Lists every individual lib file (e.g. "es5", "es2015.promise", "dom") instead of
|
|
23
|
+
* just the top-level entry (e.g. "ES2020"). This is critical for browser-based
|
|
24
|
+
* TypeScript via Volar: the virtual filesystem is async, so each
|
|
25
|
+
* `/// <reference lib="..." />` chain level requires a separate async round-trip.
|
|
26
|
+
* By listing all libs explicitly, TypeScript loads them all in a single pass.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getLibFieldForTarget(target?: string, additionalLibs?: string[]): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Returns the cached lib file contents from the last prefill, if available.
|
|
31
|
+
* These are keyed by full path (e.g. "/node_modules/typescript/lib/lib.es5.d.ts").
|
|
32
|
+
* Includes the tsconfig.json content as well.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getCachedLibFiles(): Record<string, string> | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Pre-fills the virtual filesystem with TypeScript default lib definitions and tsconfig.
|
|
37
|
+
* Writes to `/node_modules/typescript/lib/` where Volar's TypeScript language server
|
|
38
|
+
* expects to find them in a browser environment.
|
|
39
|
+
*
|
|
40
|
+
* - Skips files that already exist on the filesystem
|
|
41
|
+
* - Only runs once per session (subsequent calls are no-ops)
|
|
42
|
+
* - Should be called lazily when a TypeScript file is first opened
|
|
43
|
+
*
|
|
44
|
+
* Returns a map of file paths to their contents for direct use by the LSP worker,
|
|
45
|
+
* bypassing the need for the worker to read through nested Comlink proxies.
|
|
46
|
+
*
|
|
47
|
+
* @param fs - Virtual filesystem to write to
|
|
48
|
+
* @param resolveLib - Function that resolves a lib name to its `.d.ts` content.
|
|
49
|
+
* Receives names like "es5", "es2015.collection".
|
|
50
|
+
* In Vite, use `import.meta.glob('typescript/lib/*.d.ts', { query: '?raw' })`.
|
|
51
|
+
* @param config - Optional target and tsconfig overrides
|
|
52
|
+
*/
|
|
53
|
+
export declare function prefillTypescriptDefaults(fs: VfsInterface, resolveLib: (name: string) => Promise<string>, config?: TypescriptDefaultsConfig): Promise<Record<string, string>>;
|
|
54
|
+
/**
|
|
55
|
+
* Resets the prefilled state. Primarily useful for testing.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resetPrefillState(): void;
|