wasm-idle 0.2.5 → 0.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/dist/clang/app.d.ts +44 -0
- package/dist/clang/app.js +270 -2
- package/dist/clang/index.d.ts +40 -9
- package/dist/clang/index.js +603 -15
- package/dist/clang/memory/memFs.d.ts +3 -1
- package/dist/clang/memory/memFs.js +12 -1
- package/dist/clang/memory/memory.d.ts +8 -4
- package/dist/clang/memory/memory.js +14 -0
- package/dist/clang/memory/tar.d.ts +1 -1
- package/dist/clang/memory/tar.js +1 -1
- package/dist/clang/wasm.d.ts +1 -1
- package/dist/clang/wasm.js +13 -4
- package/dist/clangd/config.d.ts +18 -0
- package/dist/clangd/config.js +14 -0
- package/dist/clangd/jsonStream.d.ts +9 -0
- package/dist/clangd/jsonStream.js +48 -0
- package/dist/clangd/session.d.ts +14 -0
- package/dist/clangd/session.js +94 -0
- package/dist/clangd/worker.d.ts +1 -0
- package/dist/clangd/worker.js +130 -0
- package/dist/debug/editor/index.d.ts +1 -0
- package/dist/debug/editor/index.js +1 -0
- package/dist/debug/editor/monaco.d.ts +18 -0
- package/dist/debug/editor/monaco.js +105 -0
- package/dist/debug/expression.d.ts +2 -0
- package/dist/debug/expression.js +262 -0
- package/dist/debug/index.d.ts +5 -0
- package/dist/debug/index.js +4 -0
- package/dist/debug/language/cpp.d.ts +2 -0
- package/dist/debug/language/cpp.js +7 -0
- package/dist/debug/language/index.d.ts +2 -0
- package/dist/debug/language/index.js +1 -0
- package/dist/debug/language/types.d.ts +6 -0
- package/dist/debug/language/types.js +1 -0
- package/dist/debug/locals.d.ts +2 -0
- package/dist/debug/locals.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/playground/clang.d.ts +8 -2
- package/dist/playground/clang.js +25 -4
- package/dist/playground/options.d.ts +39 -0
- package/dist/playground/options.js +1 -0
- package/dist/playground/python.d.ts +10 -2
- package/dist/playground/python.js +6 -3
- package/dist/playground/sandbox.d.ts +15 -2
- package/dist/playground/worker/clang.js +21 -4
- package/dist/playground/worker/python.js +196 -3
- package/dist/terminal/Terminal.svelte +35 -11
- package/dist/terminal/Terminal.svelte.d.ts +12 -9
- package/dist/terminal/index.d.ts +2 -0
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +13 -0
- package/dist/terminal/types.js +1 -0
- package/package.json +42 -33
|
@@ -6,6 +6,7 @@ interface MemFsOptions {
|
|
|
6
6
|
stdinStr?: string;
|
|
7
7
|
path: string;
|
|
8
8
|
progress?: Writable<number>;
|
|
9
|
+
trace?: (message: string) => void;
|
|
9
10
|
}
|
|
10
11
|
export default class MemFS {
|
|
11
12
|
ready: Promise<void>;
|
|
@@ -14,6 +15,7 @@ export default class MemFS {
|
|
|
14
15
|
stdinStr: string;
|
|
15
16
|
stdin: () => string;
|
|
16
17
|
stdout: (str: string) => void;
|
|
18
|
+
trace: (message: string) => void;
|
|
17
19
|
instance: WebAssembly.Instance;
|
|
18
20
|
exports: any;
|
|
19
21
|
out: boolean;
|
|
@@ -22,7 +24,7 @@ export default class MemFS {
|
|
|
22
24
|
setStdinStr(str: string): void;
|
|
23
25
|
addDirectory(path: string): void;
|
|
24
26
|
addFile(path: string, contents: string | ArrayBuffer | Uint8Array): void;
|
|
25
|
-
getFileContents(path: string): Uint8Array<
|
|
27
|
+
getFileContents(path: string): Uint8Array<ArrayBufferLike>;
|
|
26
28
|
abort(): void;
|
|
27
29
|
host_write(fd: number, iovs: number, iovs_len: number, nwritten_out: number): number;
|
|
28
30
|
host_read(fd: number, iovs: number, iovs_len: number, nread: number): number;
|
|
@@ -4,6 +4,7 @@ import { AbortError, assert } from '../error';
|
|
|
4
4
|
import { compile } from '../wasm';
|
|
5
5
|
import { memfsUrl } from '../url';
|
|
6
6
|
const ESUCCESS = 0;
|
|
7
|
+
const previewText = (text) => JSON.stringify(text.length > 96 ? text.slice(0, 93) + '...' : text);
|
|
7
8
|
export default class MemFS {
|
|
8
9
|
ready;
|
|
9
10
|
mem = null;
|
|
@@ -11,6 +12,7 @@ export default class MemFS {
|
|
|
11
12
|
stdinStr;
|
|
12
13
|
stdin;
|
|
13
14
|
stdout;
|
|
15
|
+
trace;
|
|
14
16
|
instance = null;
|
|
15
17
|
exports;
|
|
16
18
|
out = true;
|
|
@@ -18,6 +20,7 @@ export default class MemFS {
|
|
|
18
20
|
this.stdin = options.stdin;
|
|
19
21
|
this.stdout = options.stdout;
|
|
20
22
|
this.stdinStr = options.stdinStr || '';
|
|
23
|
+
this.trace = options.trace || (() => { });
|
|
21
24
|
const env = bindNew(this, 'abort', 'host_write', 'host_read', 'memfs_log', 'copy_in', 'copy_out');
|
|
22
25
|
this.ready = compile(memfsUrl(options.path), options.progress)
|
|
23
26
|
.then((module) => WebAssembly.instantiate(module, { env }))
|
|
@@ -57,6 +60,7 @@ export default class MemFS {
|
|
|
57
60
|
return new Uint8Array(this.mem.buffer, addr, size);
|
|
58
61
|
}
|
|
59
62
|
abort() {
|
|
63
|
+
this.trace('abort()');
|
|
60
64
|
throw new AbortError();
|
|
61
65
|
}
|
|
62
66
|
host_write(fd, iovs, iovs_len, nwritten_out) {
|
|
@@ -73,6 +77,7 @@ export default class MemFS {
|
|
|
73
77
|
size += len;
|
|
74
78
|
}
|
|
75
79
|
this.hostMem_.write32(nwritten_out, size);
|
|
80
|
+
this.trace(`host_write(fd=${fd}, bytes=${size}, data=${previewText(str)})`);
|
|
76
81
|
if (this.out)
|
|
77
82
|
this.stdout(str);
|
|
78
83
|
return ESUCCESS;
|
|
@@ -91,18 +96,24 @@ export default class MemFS {
|
|
|
91
96
|
const lenToWrite = Math.min(len, this.stdinStr.length);
|
|
92
97
|
if (lenToWrite === 0)
|
|
93
98
|
break;
|
|
99
|
+
const chunk = this.stdinStr.substring(0, lenToWrite);
|
|
94
100
|
this.hostMem_.write(buf, this.stdinStr.substring(0, lenToWrite));
|
|
95
101
|
this.stdinStr = this.stdinStr.substring(lenToWrite);
|
|
96
102
|
size += lenToWrite;
|
|
103
|
+
this.trace(`host_read(fd=${fd}, bytes=${lenToWrite}, data=${previewText(chunk)})`);
|
|
97
104
|
if (lenToWrite !== len)
|
|
98
105
|
break;
|
|
99
106
|
}
|
|
100
107
|
this.hostMem_.write32(nread, size);
|
|
108
|
+
if (size === 0)
|
|
109
|
+
this.trace(`host_read(fd=${fd}, bytes=0)`);
|
|
101
110
|
return ESUCCESS;
|
|
102
111
|
}
|
|
103
112
|
memfs_log(buf, len) {
|
|
104
113
|
this.mem.check();
|
|
105
|
-
|
|
114
|
+
const message = this.mem.readStr(buf, len);
|
|
115
|
+
this.trace(`memfs_log(${previewText(message)})`);
|
|
116
|
+
console.log(message);
|
|
106
117
|
}
|
|
107
118
|
copy_out(clang_dst, memfs_src, size) {
|
|
108
119
|
this.hostMem_.check();
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
export default class Memory {
|
|
2
|
-
memory:
|
|
3
|
-
|
|
2
|
+
memory: WebAssembly.Memory;
|
|
3
|
+
view: DataView;
|
|
4
|
+
buffer: ArrayBufferLike;
|
|
4
5
|
u8: Uint8Array;
|
|
5
6
|
u32: Uint32Array;
|
|
6
|
-
constructor(memory:
|
|
7
|
+
constructor(memory: WebAssembly.Memory);
|
|
7
8
|
check(): void;
|
|
8
9
|
read8(o: number): number;
|
|
9
10
|
read32(o: number): number;
|
|
11
|
+
readInt32(o: number): number;
|
|
12
|
+
readFloat32(o: number): number;
|
|
13
|
+
readFloat64(o: number): number;
|
|
10
14
|
readStr(o: number, len: number): string;
|
|
11
15
|
readStrR(o: number, len: number): string;
|
|
12
16
|
write8(o: number, v: number): void;
|
|
@@ -14,5 +18,5 @@ export default class Memory {
|
|
|
14
18
|
write64(o: number, vlo: number, vhi?: number): void;
|
|
15
19
|
writeStr(o: number, str: string): number;
|
|
16
20
|
writeUint8(o: number, arr: ArrayLike<number>): number;
|
|
17
|
-
write(o: number, buf:
|
|
21
|
+
write(o: number, buf: ArrayBufferLike | string | Uint8Array): number;
|
|
18
22
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { readStr, readStrR } from '../encode';
|
|
2
2
|
export default class Memory {
|
|
3
3
|
memory;
|
|
4
|
+
view;
|
|
4
5
|
buffer;
|
|
5
6
|
u8;
|
|
6
7
|
u32;
|
|
7
8
|
constructor(memory) {
|
|
8
9
|
this.memory = memory;
|
|
9
10
|
this.buffer = memory.buffer;
|
|
11
|
+
this.view = new DataView(this.buffer);
|
|
10
12
|
this.u8 = new Uint8Array(this.buffer);
|
|
11
13
|
this.u32 = new Uint32Array(this.buffer);
|
|
12
14
|
}
|
|
13
15
|
check() {
|
|
14
16
|
if (this.buffer.byteLength === 0) {
|
|
15
17
|
this.buffer = this.memory.buffer;
|
|
18
|
+
this.view = new DataView(this.buffer);
|
|
16
19
|
this.u8 = new Uint8Array(this.buffer);
|
|
17
20
|
this.u32 = new Uint32Array(this.buffer);
|
|
18
21
|
}
|
|
@@ -23,6 +26,15 @@ export default class Memory {
|
|
|
23
26
|
read32(o) {
|
|
24
27
|
return this.u32[o >> 2];
|
|
25
28
|
}
|
|
29
|
+
readInt32(o) {
|
|
30
|
+
return this.view.getInt32(o, true);
|
|
31
|
+
}
|
|
32
|
+
readFloat32(o) {
|
|
33
|
+
return this.view.getFloat32(o, true);
|
|
34
|
+
}
|
|
35
|
+
readFloat64(o) {
|
|
36
|
+
return this.view.getFloat64(o, true);
|
|
37
|
+
}
|
|
26
38
|
readStr(o, len) {
|
|
27
39
|
return readStr(this.u8, o, len);
|
|
28
40
|
}
|
|
@@ -51,6 +63,8 @@ export default class Memory {
|
|
|
51
63
|
write(o, buf) {
|
|
52
64
|
if (buf instanceof ArrayBuffer)
|
|
53
65
|
return this.writeUint8(o, new Uint8Array(buf));
|
|
66
|
+
if (buf instanceof SharedArrayBuffer)
|
|
67
|
+
return this.writeUint8(o, new Uint8Array(buf));
|
|
54
68
|
else if (typeof buf === 'string')
|
|
55
69
|
return this.writeUint8(o, buf.split('').map((x) => x.charCodeAt(0)));
|
|
56
70
|
else
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { MemFS } from './';
|
|
2
|
-
export default function untar(buffer:
|
|
2
|
+
export default function untar(buffer: Uint8Array | ArrayBufferLike, memfs: MemFS): void;
|
package/dist/clang/memory/tar.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readStr, readOct } from '../encode';
|
|
2
2
|
import { assert } from '../error';
|
|
3
3
|
function* readEntry(buffer) {
|
|
4
|
-
const u8 = new Uint8Array(buffer);
|
|
4
|
+
const u8 = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
5
5
|
let offset = 0;
|
|
6
6
|
const str = (len) => {
|
|
7
7
|
offset += len;
|
package/dist/clang/wasm.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Writable } from 'svelte/store';
|
|
2
|
-
export declare const readBuffer: (name: string, progress?: Writable<number>) => Promise<Uint8Array<
|
|
2
|
+
export declare const readBuffer: (name: string, progress?: Writable<number>) => Promise<Uint8Array<ArrayBuffer>>;
|
|
3
3
|
export declare function compile(filename: string, progress?: Writable<number>): Promise<any>;
|
|
4
4
|
export declare function getInstance(module: WebAssembly.Module, imports: WebAssembly.Imports): Promise<WebAssembly.Instance>;
|
package/dist/clang/wasm.js
CHANGED
|
@@ -2,17 +2,24 @@ import * as zip from '@zip.js/zip.js';
|
|
|
2
2
|
const store = {};
|
|
3
3
|
export const readBuffer = async (name, progress) => {
|
|
4
4
|
const response = await fetch(name);
|
|
5
|
+
const contentLength = +(response.headers.get('Content-Length') || 0);
|
|
6
|
+
if (!response.body) {
|
|
7
|
+
progress?.set?.(1);
|
|
8
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
9
|
+
}
|
|
5
10
|
const r = response.body.getReader();
|
|
6
|
-
const contentLength = +response.headers.get('Content-Length');
|
|
7
11
|
let receivedLength = 0;
|
|
8
|
-
|
|
12
|
+
const chunks = [];
|
|
9
13
|
while (true) {
|
|
10
14
|
const { done, value } = await r.read();
|
|
11
15
|
if (done)
|
|
12
16
|
break;
|
|
13
|
-
|
|
17
|
+
if (!value)
|
|
18
|
+
continue;
|
|
19
|
+
chunks.push(Uint8Array.from(value));
|
|
14
20
|
receivedLength += value.length;
|
|
15
|
-
|
|
21
|
+
if (contentLength > 0)
|
|
22
|
+
progress?.set?.(receivedLength / contentLength);
|
|
16
23
|
}
|
|
17
24
|
progress?.set?.(1);
|
|
18
25
|
const chunksAll = new Uint8Array(receivedLength); // (4.1)
|
|
@@ -24,6 +31,8 @@ export const readBuffer = async (name, progress) => {
|
|
|
24
31
|
const reader = new zip.ZipReader(new zip.Uint8ArrayReader(chunksAll));
|
|
25
32
|
const entries = await reader.getEntries();
|
|
26
33
|
for (const entry of entries) {
|
|
34
|
+
if (!('getData' in entry))
|
|
35
|
+
continue;
|
|
27
36
|
const data = await entry.getData(new zip.Uint8ArrayWriter());
|
|
28
37
|
return data;
|
|
29
38
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const CLANGD_WORKSPACE_PATH = "/workspace";
|
|
2
|
+
export declare const CLANGD_WORKSPACE_URI = "file:///workspace";
|
|
3
|
+
export declare const CLANGD_CPP_FILE_PATH = "/workspace/main.cpp";
|
|
4
|
+
export declare const CLANGD_CPP_FILE_URI = "file:///workspace/main.cpp";
|
|
5
|
+
export type ClangdStatus = {
|
|
6
|
+
state: 'disabled';
|
|
7
|
+
} | {
|
|
8
|
+
state: 'loading';
|
|
9
|
+
loaded?: number;
|
|
10
|
+
total?: number;
|
|
11
|
+
} | {
|
|
12
|
+
state: 'ready';
|
|
13
|
+
} | {
|
|
14
|
+
state: 'error';
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const normalizeClangdBaseUrl: (baseUrl: string) => string;
|
|
18
|
+
export declare const createClangdCompileFlags: () => string[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const CLANGD_WORKSPACE_PATH = '/workspace';
|
|
2
|
+
export const CLANGD_WORKSPACE_URI = `file://${CLANGD_WORKSPACE_PATH}`;
|
|
3
|
+
export const CLANGD_CPP_FILE_PATH = `${CLANGD_WORKSPACE_PATH}/main.cpp`;
|
|
4
|
+
export const CLANGD_CPP_FILE_URI = `file://${CLANGD_CPP_FILE_PATH}`;
|
|
5
|
+
export const normalizeClangdBaseUrl = (baseUrl) => baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
6
|
+
export const createClangdCompileFlags = () => [
|
|
7
|
+
'-std=gnu++2a',
|
|
8
|
+
'-xc++',
|
|
9
|
+
'--target=wasm32-wasi',
|
|
10
|
+
'-isystem/usr/include/c++/v1',
|
|
11
|
+
'-isystem/usr/include/wasm32-wasi/c++/v1',
|
|
12
|
+
'-isystem/usr/include',
|
|
13
|
+
'-isystem/usr/include/wasm32-wasi'
|
|
14
|
+
];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const QUOTE = 34;
|
|
2
|
+
const LEFT_BRACE = 123;
|
|
3
|
+
const RIGHT_BRACE = 125;
|
|
4
|
+
const BACKSLASH = 92;
|
|
5
|
+
export class JsonStream {
|
|
6
|
+
inJson = false;
|
|
7
|
+
rawText = [];
|
|
8
|
+
unbalancedBraces = 0;
|
|
9
|
+
inString = false;
|
|
10
|
+
inEscape = 0;
|
|
11
|
+
textDecoder = new TextDecoder();
|
|
12
|
+
insert(charCode) {
|
|
13
|
+
if (!this.inJson && charCode === LEFT_BRACE) {
|
|
14
|
+
this.inJson = true;
|
|
15
|
+
this.rawText = [];
|
|
16
|
+
}
|
|
17
|
+
if (!this.inJson)
|
|
18
|
+
return null;
|
|
19
|
+
this.rawText.push(charCode);
|
|
20
|
+
if (this.inString) {
|
|
21
|
+
if (this.inEscape) {
|
|
22
|
+
if (charCode === 75)
|
|
23
|
+
this.inEscape += 4;
|
|
24
|
+
this.inEscape -= 1;
|
|
25
|
+
}
|
|
26
|
+
else if (charCode === BACKSLASH) {
|
|
27
|
+
this.inEscape = 1;
|
|
28
|
+
}
|
|
29
|
+
else if (charCode === QUOTE) {
|
|
30
|
+
this.inString = false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (charCode === LEFT_BRACE) {
|
|
34
|
+
this.unbalancedBraces += 1;
|
|
35
|
+
}
|
|
36
|
+
else if (charCode === RIGHT_BRACE) {
|
|
37
|
+
this.unbalancedBraces -= 1;
|
|
38
|
+
if (this.unbalancedBraces === 0) {
|
|
39
|
+
this.inJson = false;
|
|
40
|
+
return this.textDecoder.decode(new Uint8Array(this.rawText));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (charCode === QUOTE) {
|
|
44
|
+
this.inString = true;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MonacoLanguageClient } from '@hancomac/monaco-languageclient';
|
|
2
|
+
import type * as Monaco from 'monaco-editor';
|
|
3
|
+
import { type ClangdStatus } from './config';
|
|
4
|
+
export declare class ClangdSession {
|
|
5
|
+
Monaco: typeof Monaco;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
onStatus?: (status: ClangdStatus) => void;
|
|
8
|
+
worker: Worker | null;
|
|
9
|
+
languageClient: MonacoLanguageClient | null;
|
|
10
|
+
constructor(MonacoModule: typeof Monaco, baseUrl: string, onStatus?: (status: ClangdStatus) => void);
|
|
11
|
+
createModel(value: string): Monaco.editor.ITextModel;
|
|
12
|
+
start(): Promise<void>;
|
|
13
|
+
dispose(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { CloseAction, ErrorAction, MonacoLanguageClient, MonacoServices } from '@hancomac/monaco-languageclient';
|
|
2
|
+
import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-jsonrpc/browser';
|
|
3
|
+
import { CLANGD_CPP_FILE_URI, CLANGD_WORKSPACE_URI, normalizeClangdBaseUrl } from './config';
|
|
4
|
+
import ClangdWorker from './worker?worker';
|
|
5
|
+
let servicesInstalled = false;
|
|
6
|
+
export class ClangdSession {
|
|
7
|
+
Monaco;
|
|
8
|
+
baseUrl;
|
|
9
|
+
onStatus;
|
|
10
|
+
worker = null;
|
|
11
|
+
languageClient = null;
|
|
12
|
+
constructor(MonacoModule, baseUrl, onStatus) {
|
|
13
|
+
this.Monaco = MonacoModule;
|
|
14
|
+
this.onStatus = onStatus;
|
|
15
|
+
this.baseUrl = normalizeClangdBaseUrl(baseUrl);
|
|
16
|
+
if (!servicesInstalled) {
|
|
17
|
+
MonacoServices.install(MonacoModule);
|
|
18
|
+
servicesInstalled = true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
createModel(value) {
|
|
22
|
+
const uri = this.Monaco.Uri.parse(CLANGD_CPP_FILE_URI);
|
|
23
|
+
this.Monaco.editor.getModel(uri)?.dispose();
|
|
24
|
+
return this.Monaco.editor.createModel(value, 'cpp', uri);
|
|
25
|
+
}
|
|
26
|
+
async start() {
|
|
27
|
+
if (this.worker)
|
|
28
|
+
return;
|
|
29
|
+
this.onStatus?.({ state: 'loading' });
|
|
30
|
+
const worker = new ClangdWorker();
|
|
31
|
+
this.worker = worker;
|
|
32
|
+
await new Promise((resolve, reject) => {
|
|
33
|
+
const cleanup = () => {
|
|
34
|
+
worker.removeEventListener('message', handleMessage);
|
|
35
|
+
worker.removeEventListener('error', handleError);
|
|
36
|
+
};
|
|
37
|
+
const handleMessage = (event) => {
|
|
38
|
+
const { type, value, max, message } = event.data || {};
|
|
39
|
+
if (type === 'progress') {
|
|
40
|
+
this.onStatus?.({ state: 'loading', loaded: value, total: max });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (type === 'ready') {
|
|
44
|
+
cleanup();
|
|
45
|
+
resolve();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (type === 'error') {
|
|
49
|
+
cleanup();
|
|
50
|
+
reject(new Error(message || 'clangd failed to start'));
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const handleError = (event) => {
|
|
54
|
+
cleanup();
|
|
55
|
+
reject(event.error || new Error(event.message || 'clangd worker failed'));
|
|
56
|
+
};
|
|
57
|
+
worker.addEventListener('message', handleMessage);
|
|
58
|
+
worker.addEventListener('error', handleError);
|
|
59
|
+
worker.postMessage({ type: 'init', baseUrl: this.baseUrl });
|
|
60
|
+
});
|
|
61
|
+
const reader = new BrowserMessageReader(worker);
|
|
62
|
+
const writer = new BrowserMessageWriter(worker);
|
|
63
|
+
this.languageClient = new MonacoLanguageClient({
|
|
64
|
+
name: 'wasm-idle clangd',
|
|
65
|
+
clientOptions: {
|
|
66
|
+
documentSelector: ['cpp'],
|
|
67
|
+
errorHandler: {
|
|
68
|
+
error: () => ({ action: ErrorAction.Continue }),
|
|
69
|
+
closed: () => ({ action: CloseAction.DoNotRestart })
|
|
70
|
+
},
|
|
71
|
+
workspaceFolder: {
|
|
72
|
+
index: 0,
|
|
73
|
+
name: 'workspace',
|
|
74
|
+
uri: this.Monaco.Uri.parse(CLANGD_WORKSPACE_URI)
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
connectionProvider: {
|
|
78
|
+
get: async () => ({ reader, writer })
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
this.languageClient.start();
|
|
82
|
+
reader.onClose(() => {
|
|
83
|
+
this.languageClient?.stop();
|
|
84
|
+
this.onStatus?.({ state: 'disabled' });
|
|
85
|
+
});
|
|
86
|
+
this.onStatus?.({ state: 'ready' });
|
|
87
|
+
}
|
|
88
|
+
dispose() {
|
|
89
|
+
this.languageClient?.stop();
|
|
90
|
+
this.languageClient = null;
|
|
91
|
+
this.worker?.terminate();
|
|
92
|
+
this.worker = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/// <reference lib="WebWorker" />
|
|
2
|
+
import { CLANGD_CPP_FILE_PATH, CLANGD_WORKSPACE_PATH, createClangdCompileFlags, normalizeClangdBaseUrl } from './config';
|
|
3
|
+
import { JsonStream } from './jsonStream';
|
|
4
|
+
import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-jsonrpc/browser';
|
|
5
|
+
const textEncoder = new TextEncoder();
|
|
6
|
+
const jsonStream = new JsonStream();
|
|
7
|
+
let resolveStdinReady = () => { };
|
|
8
|
+
const stdinChunks = [];
|
|
9
|
+
const currentStdinChunk = [];
|
|
10
|
+
const stdin = () => {
|
|
11
|
+
if (currentStdinChunk.length === 0) {
|
|
12
|
+
if (stdinChunks.length === 0)
|
|
13
|
+
return null;
|
|
14
|
+
const nextChunk = stdinChunks.shift();
|
|
15
|
+
if (!nextChunk)
|
|
16
|
+
return null;
|
|
17
|
+
currentStdinChunk.push(...textEncoder.encode(nextChunk), null);
|
|
18
|
+
}
|
|
19
|
+
return currentStdinChunk.shift() ?? null;
|
|
20
|
+
};
|
|
21
|
+
const stdinReady = async () => {
|
|
22
|
+
if (stdinChunks.length === 0) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
resolveStdinReady = resolve;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
let writer = null;
|
|
29
|
+
const stdout = (charCode) => {
|
|
30
|
+
const json = jsonStream.insert(charCode);
|
|
31
|
+
if (json && writer)
|
|
32
|
+
writer.write(JSON.parse(json));
|
|
33
|
+
};
|
|
34
|
+
const stderr = () => { };
|
|
35
|
+
const onAbort = () => {
|
|
36
|
+
writer?.end();
|
|
37
|
+
self.reportError?.('clangd aborted');
|
|
38
|
+
};
|
|
39
|
+
self.addEventListener('message', async (event) => {
|
|
40
|
+
if (event.data?.type !== 'init')
|
|
41
|
+
return;
|
|
42
|
+
const baseUrl = normalizeClangdBaseUrl(event.data.baseUrl);
|
|
43
|
+
try {
|
|
44
|
+
const jsResponse = await fetch(`${baseUrl}/clangd.js`);
|
|
45
|
+
if (!jsResponse.ok) {
|
|
46
|
+
throw new Error(`Failed to load clangd.js: ${jsResponse.status}`);
|
|
47
|
+
}
|
|
48
|
+
const jsSource = await jsResponse.text();
|
|
49
|
+
const jsDataUrl = URL.createObjectURL(new Blob([jsSource], { type: 'text/javascript;charset=utf-8' }));
|
|
50
|
+
const jsModule = import(/* @vite-ignore */ jsDataUrl);
|
|
51
|
+
const wasmResponse = await fetch(`${baseUrl}/clangd.wasm.gz`);
|
|
52
|
+
if (!wasmResponse.ok) {
|
|
53
|
+
throw new Error(`Failed to load clangd.wasm.gz: ${wasmResponse.status}`);
|
|
54
|
+
}
|
|
55
|
+
const wasmSize = +(wasmResponse.headers.get('Content-Length') || 0);
|
|
56
|
+
const wasmReader = wasmResponse.body?.getReader();
|
|
57
|
+
const chunks = [];
|
|
58
|
+
let receivedLength = 0;
|
|
59
|
+
if (wasmReader) {
|
|
60
|
+
while (true) {
|
|
61
|
+
const { done, value } = await wasmReader.read();
|
|
62
|
+
if (done)
|
|
63
|
+
break;
|
|
64
|
+
if (!value)
|
|
65
|
+
continue;
|
|
66
|
+
chunks.push(Uint8Array.from(value));
|
|
67
|
+
receivedLength += value.length;
|
|
68
|
+
self.postMessage({
|
|
69
|
+
type: 'progress',
|
|
70
|
+
value: receivedLength,
|
|
71
|
+
...(wasmSize > 0 ? { max: wasmSize } : {})
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const wasmBytes = new Uint8Array(await wasmResponse.arrayBuffer());
|
|
77
|
+
chunks.push(wasmBytes);
|
|
78
|
+
receivedLength = wasmBytes.byteLength;
|
|
79
|
+
self.postMessage({
|
|
80
|
+
type: 'progress',
|
|
81
|
+
value: receivedLength,
|
|
82
|
+
...(wasmSize > 0 ? { max: wasmSize } : {})
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const wasmBytes = new Uint8Array(receivedLength);
|
|
86
|
+
let offset = 0;
|
|
87
|
+
for (const chunk of chunks) {
|
|
88
|
+
wasmBytes.set(chunk, offset);
|
|
89
|
+
offset += chunk.length;
|
|
90
|
+
}
|
|
91
|
+
const wasmBlob = new Blob([wasmBytes.buffer], { type: 'application/wasm' });
|
|
92
|
+
const wasmDataUrl = URL.createObjectURL(wasmBlob);
|
|
93
|
+
const { default: Clangd } = await jsModule;
|
|
94
|
+
const clangd = await Clangd({
|
|
95
|
+
thisProgram: '/usr/bin/clangd',
|
|
96
|
+
mainScriptUrlOrBlob: jsDataUrl,
|
|
97
|
+
locateFile: (path, prefix) => path.endsWith('.wasm') ? wasmDataUrl : `${prefix}${path}`,
|
|
98
|
+
stdinReady,
|
|
99
|
+
stdin,
|
|
100
|
+
stdout,
|
|
101
|
+
stderr,
|
|
102
|
+
onExit: onAbort,
|
|
103
|
+
onAbort
|
|
104
|
+
});
|
|
105
|
+
clangd.FS.mkdirTree(CLANGD_WORKSPACE_PATH);
|
|
106
|
+
clangd.FS.writeFile(CLANGD_CPP_FILE_PATH, '');
|
|
107
|
+
clangd.FS.writeFile(`${CLANGD_WORKSPACE_PATH}/.clangd`, JSON.stringify({
|
|
108
|
+
CompileFlags: {
|
|
109
|
+
Add: createClangdCompileFlags()
|
|
110
|
+
}
|
|
111
|
+
}));
|
|
112
|
+
clangd.callMain([]);
|
|
113
|
+
writer = new BrowserMessageWriter(self);
|
|
114
|
+
const reader = new BrowserMessageReader(self);
|
|
115
|
+
reader.listen((data) => {
|
|
116
|
+
const body = JSON.stringify(data).replace(/[\u007F-\uFFFF]/g, (character) => {
|
|
117
|
+
return '\\u' + character.codePointAt(0)?.toString(16).padStart(4, '0');
|
|
118
|
+
});
|
|
119
|
+
stdinChunks.push(`Content-Length: ${body.length}\r\n`, '\r\n', body);
|
|
120
|
+
resolveStdinReady();
|
|
121
|
+
});
|
|
122
|
+
self.postMessage({ type: 'ready', value: receivedLength });
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
self.postMessage({
|
|
126
|
+
type: 'error',
|
|
127
|
+
message: error instanceof Error ? error.message : String(error)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { MonacoDebugView } from './monaco';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { MonacoDebugView } from './monaco';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DebugVariable } from '../../playground/options';
|
|
2
|
+
import type { DebugLanguageAdapter } from '../language';
|
|
3
|
+
import type monaco from 'monaco-editor';
|
|
4
|
+
export declare class MonacoDebugView {
|
|
5
|
+
Monaco: typeof monaco;
|
|
6
|
+
editor: monaco.editor.IStandaloneCodeEditor;
|
|
7
|
+
breakpointDecorations: monaco.editor.IEditorDecorationsCollection;
|
|
8
|
+
inlineValueDecorations: monaco.editor.IEditorDecorationsCollection;
|
|
9
|
+
pausedLineWidget: monaco.editor.IContentWidget | null;
|
|
10
|
+
pausedLineWidgetNode: HTMLDivElement | null;
|
|
11
|
+
mouseHandler: monaco.IDisposable | null;
|
|
12
|
+
breakpoints: number[];
|
|
13
|
+
onBreakpointsChange?: (lines: number[]) => void;
|
|
14
|
+
constructor(MonacoModule: typeof monaco, editor: monaco.editor.IStandaloneCodeEditor, onBreakpointsChange?: (lines: number[]) => void);
|
|
15
|
+
setBreakpoints(lines: number[]): void;
|
|
16
|
+
setPauseState(pausedLine: number | null, locals: DebugVariable[], adapter: DebugLanguageAdapter | null): void;
|
|
17
|
+
dispose(): void;
|
|
18
|
+
}
|