wasm-idle 0.4.1 → 0.6.0
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/README.md +94 -3
- package/dist/clang/app.d.ts +4 -0
- package/dist/clang/app.js +315 -12
- package/dist/clang/index.d.ts +4 -0
- package/dist/clang/index.js +151 -8
- package/dist/clangd/worker.js +21 -5
- package/dist/debug/controller.svelte.d.ts +43 -0
- package/dist/debug/controller.svelte.js +268 -0
- package/dist/debug/editor/index.d.ts +2 -1
- package/dist/debug/editor/index.js +1 -1
- package/dist/debug/editor/monaco.d.ts +8 -0
- package/dist/debug/editor/monaco.js +23 -0
- package/dist/debug/expression.d.ts +24 -0
- package/dist/debug/expression.js +307 -138
- package/dist/debug/index.d.ts +4 -1
- package/dist/debug/index.js +2 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.js +4 -3
- package/dist/lsp/cpp/server.d.ts +2 -0
- package/dist/lsp/cpp/server.js +57 -0
- package/dist/lsp/index.d.ts +5 -0
- package/dist/lsp/index.js +4 -0
- package/dist/lsp/python/main.worker.d.ts +1 -0
- package/dist/lsp/python/main.worker.js +92 -0
- package/dist/lsp/python/package/arcturus_python_lsp/__init__.py +3 -0
- package/dist/lsp/python/package/arcturus_python_lsp/server.py +462 -0
- package/dist/lsp/python/server.d.ts +2 -0
- package/dist/lsp/python/server.js +54 -0
- package/dist/lsp/registry.d.ts +2 -0
- package/dist/lsp/registry.js +12 -0
- package/dist/lsp/runtime.d.ts +3 -0
- package/dist/lsp/runtime.js +30 -0
- package/dist/lsp/types.d.ts +15 -0
- package/dist/lsp/types.js +1 -0
- package/dist/playground/assets.d.ts +5 -0
- package/dist/playground/assets.js +8 -0
- package/dist/playground/clang.d.ts +5 -0
- package/dist/playground/clang.js +45 -2
- package/dist/playground/index.d.ts +5 -5
- package/dist/playground/index.js +45 -5
- package/dist/playground/options.d.ts +9 -0
- package/dist/playground/python.d.ts +1 -0
- package/dist/playground/python.js +12 -0
- package/dist/playground/rust.d.ts +32 -0
- package/dist/playground/rust.js +175 -0
- package/dist/playground/sandbox.d.ts +18 -4
- package/dist/playground/wasmRustVersion.d.ts +1 -0
- package/dist/playground/wasmRustVersion.js +1 -0
- package/dist/playground/worker/clang.js +12 -4
- package/dist/playground/worker/rust.d.ts +1 -0
- package/dist/playground/worker/rust.js +159 -0
- package/dist/terminal/Terminal.svelte +78 -25
- package/dist/terminal/Terminal.svelte.d.ts +2 -0
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +3 -0
- package/package.json +15 -9
package/dist/clang/index.js
CHANGED
|
@@ -137,6 +137,8 @@ export default class Clang {
|
|
|
137
137
|
debugPauseOnEntry = false;
|
|
138
138
|
debugBuffer;
|
|
139
139
|
debugInterruptBuffer;
|
|
140
|
+
debugWatchBuffer;
|
|
141
|
+
debugWatchResultBuffer;
|
|
140
142
|
onDebugEvent;
|
|
141
143
|
debugVariableMetadata = {};
|
|
142
144
|
debugGlobalMetadata = [];
|
|
@@ -185,7 +187,7 @@ export default class Clang {
|
|
|
185
187
|
this.traceStartedAt = Date.now();
|
|
186
188
|
}
|
|
187
189
|
trace(message) {
|
|
188
|
-
if (!this.debug)
|
|
190
|
+
if (!this.debug || !this.log)
|
|
189
191
|
return;
|
|
190
192
|
const elapsed = Date.now() - this.traceStartedAt;
|
|
191
193
|
this.stdout(`\x1b[2m[debug +${elapsed}ms] ${message}\x1b[0m\n`);
|
|
@@ -231,11 +233,98 @@ export default class Clang {
|
|
|
231
233
|
let currentFunctionContainers = new Map();
|
|
232
234
|
let inBlockComment = false;
|
|
233
235
|
let pendingFunctionHeader;
|
|
236
|
+
const debugStructTypes = new Map();
|
|
237
|
+
let structName = '';
|
|
238
|
+
let structFields = [];
|
|
239
|
+
let structInBlockComment = false;
|
|
240
|
+
for (const rawLine of lines) {
|
|
241
|
+
let structLine = rawLine;
|
|
242
|
+
if (structInBlockComment) {
|
|
243
|
+
const commentEnd = structLine.indexOf('*/');
|
|
244
|
+
if (commentEnd === -1)
|
|
245
|
+
continue;
|
|
246
|
+
structLine = structLine.slice(commentEnd + 2);
|
|
247
|
+
structInBlockComment = false;
|
|
248
|
+
}
|
|
249
|
+
const commentStart = structLine.indexOf('/*');
|
|
250
|
+
if (commentStart !== -1) {
|
|
251
|
+
const commentEnd = structLine.indexOf('*/', commentStart + 2);
|
|
252
|
+
if (commentEnd === -1) {
|
|
253
|
+
structInBlockComment = true;
|
|
254
|
+
structLine = structLine.slice(0, commentStart);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
structLine =
|
|
258
|
+
structLine.slice(0, commentStart) + structLine.slice(commentEnd + 2);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const lineComment = structLine.indexOf('//');
|
|
262
|
+
if (lineComment !== -1)
|
|
263
|
+
structLine = structLine.slice(0, lineComment);
|
|
264
|
+
const normalizedStructLine = structLine.trim();
|
|
265
|
+
if (!structName) {
|
|
266
|
+
const structMatch = normalizedStructLine.match(/^struct\s+([A-Za-z_]\w*)\s*\{$/);
|
|
267
|
+
if (structMatch?.[1]) {
|
|
268
|
+
structName = structMatch[1];
|
|
269
|
+
structFields = [];
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (normalizedStructLine === '};') {
|
|
274
|
+
let structOffset = 0;
|
|
275
|
+
let structAlignment = 1;
|
|
276
|
+
const resolvedFields = [];
|
|
277
|
+
for (const field of structFields) {
|
|
278
|
+
const fieldSize = field.kind === 'double'
|
|
279
|
+
? 8
|
|
280
|
+
: field.kind === 'bool' || field.kind === 'char'
|
|
281
|
+
? 1
|
|
282
|
+
: 4;
|
|
283
|
+
if (structOffset % fieldSize !== 0) {
|
|
284
|
+
structOffset += fieldSize - (structOffset % fieldSize);
|
|
285
|
+
}
|
|
286
|
+
resolvedFields.push({
|
|
287
|
+
name: field.name,
|
|
288
|
+
kind: field.kind,
|
|
289
|
+
offset: structOffset
|
|
290
|
+
});
|
|
291
|
+
structOffset += fieldSize;
|
|
292
|
+
structAlignment = Math.max(structAlignment, fieldSize);
|
|
293
|
+
}
|
|
294
|
+
if (structOffset % structAlignment !== 0) {
|
|
295
|
+
structOffset += structAlignment - (structOffset % structAlignment);
|
|
296
|
+
}
|
|
297
|
+
debugStructTypes.set(structName, {
|
|
298
|
+
fields: resolvedFields,
|
|
299
|
+
size: Math.max(structOffset, 1)
|
|
300
|
+
});
|
|
301
|
+
structName = '';
|
|
302
|
+
structFields = [];
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const structFieldMatch = normalizedStructLine.match(/^(?:const\s+)?(?:(?:unsigned|signed)\s+)?(?:(?:short|long long|long)\s+)?(int|float|double|bool|char)\s+(.+);$/);
|
|
306
|
+
if (!structFieldMatch)
|
|
307
|
+
continue;
|
|
308
|
+
for (const declaration of structFieldMatch[2].split(',')) {
|
|
309
|
+
const declarator = declaration.split('=')[0]?.trim() || '';
|
|
310
|
+
if (!declarator || /[*&\[]/.test(declarator))
|
|
311
|
+
continue;
|
|
312
|
+
const name = declarator.match(/([A-Za-z_]\w*)\s*$/)?.[1];
|
|
313
|
+
if (!name)
|
|
314
|
+
continue;
|
|
315
|
+
structFields.push({
|
|
316
|
+
name,
|
|
317
|
+
kind: structFieldMatch[1]
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
234
321
|
this.debugVariableMetadata = {};
|
|
235
322
|
this.debugGlobalMetadata = [];
|
|
236
323
|
this.debugFunctionMetadata = {};
|
|
237
324
|
const globalInitialization = [];
|
|
238
325
|
const instrumented = [
|
|
326
|
+
'#include <cstdio>',
|
|
327
|
+
'#include <iostream>',
|
|
239
328
|
'#include <map>',
|
|
240
329
|
'#include <set>',
|
|
241
330
|
'#include <string>',
|
|
@@ -388,6 +477,29 @@ export default class Clang {
|
|
|
388
477
|
globalInitialization.push(`${kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(0, ${slot}, ${name});`);
|
|
389
478
|
}
|
|
390
479
|
}
|
|
480
|
+
const globalStructArrayMatch = isTopLevelDeclarationContext &&
|
|
481
|
+
normalized.match(/^(?:const\s+)?([A-Za-z_]\w*)\s+([A-Za-z_]\w*)\s*\[(\d+)\]\s*(?:=.*)?;$/);
|
|
482
|
+
if (globalStructArrayMatch) {
|
|
483
|
+
const structType = debugStructTypes.get(globalStructArrayMatch[1]);
|
|
484
|
+
if (structType) {
|
|
485
|
+
const slot = nextVariableSlot++;
|
|
486
|
+
this.debugGlobalMetadata = [
|
|
487
|
+
...this.debugGlobalMetadata,
|
|
488
|
+
{
|
|
489
|
+
slot,
|
|
490
|
+
name: globalStructArrayMatch[2],
|
|
491
|
+
kind: 'array',
|
|
492
|
+
length: Number(globalStructArrayMatch[3]),
|
|
493
|
+
dimensions: [Number(globalStructArrayMatch[3])],
|
|
494
|
+
structFields: structType.fields,
|
|
495
|
+
structSize: structType.size,
|
|
496
|
+
fromLine: index + 1,
|
|
497
|
+
toLine: Number.MAX_SAFE_INTEGER
|
|
498
|
+
}
|
|
499
|
+
];
|
|
500
|
+
globalInitialization.push(`__wasm_idle_debug_value_addr(0, ${slot}, (int)((unsigned long long)(${globalStructArrayMatch[2]})));`);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
391
503
|
if (inFunctionBody &&
|
|
392
504
|
normalized &&
|
|
393
505
|
!normalized.startsWith('#') &&
|
|
@@ -522,7 +634,7 @@ export default class Clang {
|
|
|
522
634
|
slot,
|
|
523
635
|
kind,
|
|
524
636
|
fromLine: index + 1,
|
|
525
|
-
toLine:
|
|
637
|
+
toLine: Number.MAX_SAFE_INTEGER
|
|
526
638
|
});
|
|
527
639
|
this.debugVariableMetadata[currentFunctionId] = [
|
|
528
640
|
...(this.debugVariableMetadata[currentFunctionId] || []),
|
|
@@ -531,7 +643,7 @@ export default class Clang {
|
|
|
531
643
|
name,
|
|
532
644
|
kind,
|
|
533
645
|
fromLine: index + 1,
|
|
534
|
-
toLine:
|
|
646
|
+
toLine: Number.MAX_SAFE_INTEGER
|
|
535
647
|
}
|
|
536
648
|
];
|
|
537
649
|
}
|
|
@@ -551,10 +663,23 @@ export default class Clang {
|
|
|
551
663
|
continue;
|
|
552
664
|
if (new RegExp(`(?:^|[^\\w])(?:\\+\\+|--)\\s*${escapedName}\\b`).test(normalized) ||
|
|
553
665
|
new RegExp(`\\b${escapedName}\\s*(?:[+\\-*/%]?=|\\+\\+|--)`).test(normalized) ||
|
|
554
|
-
new RegExp(`&\\s*${escapedName}\\b`).test(normalized)
|
|
666
|
+
new RegExp(`&\\s*${escapedName}\\b`).test(normalized) ||
|
|
667
|
+
new RegExp(`\\b(?:cin|std::cin)\\b[^;]*>>\\s*${escapedName}\\b`).test(normalized)) {
|
|
555
668
|
trailingInstrumentation.push(`${indent}${variable.kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(${currentFunctionId}, ${variable.slot}, ${name});`);
|
|
556
669
|
}
|
|
557
670
|
}
|
|
671
|
+
for (const [name, variable] of globalVariables) {
|
|
672
|
+
if (currentFunctionVariables.has(name) ||
|
|
673
|
+
currentFunctionContainers.has(name))
|
|
674
|
+
continue;
|
|
675
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
676
|
+
if (new RegExp(`(?:^|[^\\w])(?:\\+\\+|--)\\s*${escapedName}\\b`).test(normalized) ||
|
|
677
|
+
new RegExp(`\\b${escapedName}\\s*(?:[+\\-*/%]?=|\\+\\+|--)`).test(normalized) ||
|
|
678
|
+
new RegExp(`&\\s*${escapedName}\\b`).test(normalized) ||
|
|
679
|
+
new RegExp(`\\b(?:cin|std::cin)\\b[^;]*>>\\s*${escapedName}\\b`).test(normalized)) {
|
|
680
|
+
trailingInstrumentation.push(`${indent}${variable.kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(0, ${variable.slot}, ${name});`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
558
683
|
}
|
|
559
684
|
if (/^return\b/.test(normalized))
|
|
560
685
|
leadingInstrumentation.push(`${indent}__wasm_idle_debug_leave(${currentFunctionId});`);
|
|
@@ -618,6 +743,7 @@ export default class Clang {
|
|
|
618
743
|
const initSource = segments[0].trim();
|
|
619
744
|
const updateSource = segments[2].trim();
|
|
620
745
|
const initHooks = [];
|
|
746
|
+
const conditionHooks = [];
|
|
621
747
|
const updateHooks = [];
|
|
622
748
|
const initLooksLikeDeclaration = /^(?:const\s+)?(?:(?:unsigned|signed)\s+)?(?:(?:short|long long|long)\s+)?(?:int|float|double|bool|char)\b/.test(initSource);
|
|
623
749
|
for (const [name, variable] of currentFunctionVariables) {
|
|
@@ -627,6 +753,10 @@ export default class Clang {
|
|
|
627
753
|
mutationPattern.test(initSource)) {
|
|
628
754
|
initHooks.push(`${variable.kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(${currentFunctionId}, ${variable.slot}, ${name})`);
|
|
629
755
|
}
|
|
756
|
+
if (initLooksLikeDeclaration &&
|
|
757
|
+
mutationPattern.test(initSource)) {
|
|
758
|
+
conditionHooks.push(`${variable.kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(${currentFunctionId}, ${variable.slot}, ${name})`);
|
|
759
|
+
}
|
|
630
760
|
if (mutationPattern.test(updateSource)) {
|
|
631
761
|
updateHooks.push(`${variable.kind === 'bool' ? '__wasm_idle_debug_value_bool' : '__wasm_idle_debug_value_num'}(${currentFunctionId}, ${variable.slot}, ${name})`);
|
|
632
762
|
}
|
|
@@ -639,7 +769,7 @@ export default class Clang {
|
|
|
639
769
|
: segments[2];
|
|
640
770
|
rewrittenLine =
|
|
641
771
|
line.slice(0, openIndex + 1) +
|
|
642
|
-
`${instrumentedInit}; (__wasm_idle_debug_line(${currentFunctionId}, ${index + 1}), (${segments[1].trim()})); ${instrumentedUpdate}` +
|
|
772
|
+
`${instrumentedInit}; (${conditionHooks.length ? `${conditionHooks.join(', ')}, ` : ''}__wasm_idle_debug_line(${currentFunctionId}, ${index + 1}), (${segments[1].trim()})); ${instrumentedUpdate}` +
|
|
643
773
|
line.slice(closeIndex);
|
|
644
774
|
}
|
|
645
775
|
}
|
|
@@ -680,6 +810,12 @@ export default class Clang {
|
|
|
680
810
|
currentFunctionVariables = new Map();
|
|
681
811
|
currentFunctionContainers = new Map();
|
|
682
812
|
instrumented.push(`${indent} __wasm_idle_debug_enter(${currentFunctionId}, ${index + 1});`);
|
|
813
|
+
if (functionName === 'main') {
|
|
814
|
+
instrumented.push(`${indent} std::cout.setf(std::ios::unitbuf);`);
|
|
815
|
+
instrumented.push(`${indent} std::cerr.setf(std::ios::unitbuf);`);
|
|
816
|
+
instrumented.push(`${indent} setvbuf(stdout, nullptr, _IONBF, 0);`);
|
|
817
|
+
instrumented.push(`${indent} setvbuf(stderr, nullptr, _IONBF, 0);`);
|
|
818
|
+
}
|
|
683
819
|
const parameterSource = startsInlineFunctionBody
|
|
684
820
|
? normalized.slice(normalized.indexOf('(') + 1, normalized.lastIndexOf(')'))
|
|
685
821
|
: pendingFunctionHeader?.parameters || '';
|
|
@@ -847,7 +983,10 @@ export default class Clang {
|
|
|
847
983
|
app.debugSession = {
|
|
848
984
|
buffer: this.debugBuffer,
|
|
849
985
|
interruptBuffer: this.debugInterruptBuffer,
|
|
986
|
+
watchBuffer: this.debugWatchBuffer,
|
|
987
|
+
watchResultBuffer: this.debugWatchResultBuffer,
|
|
850
988
|
breakpoints: new Set(this.debugBreakpoints),
|
|
989
|
+
breakpointVersion: 0,
|
|
851
990
|
pauseOnEntry: this.debugPauseOnEntry,
|
|
852
991
|
stepArmed: this.debugPauseOnEntry,
|
|
853
992
|
nextLineArmed: false,
|
|
@@ -878,13 +1017,15 @@ export default class Clang {
|
|
|
878
1017
|
return stillRunning ? app : null;
|
|
879
1018
|
}
|
|
880
1019
|
async compileLink(code, options = {}) {
|
|
881
|
-
const { language = 'CPP', args = [], compileArgs = args, debug = false, breakpoints = [], pauseOnEntry = false, cppVersion, cVersion, debugBuffer, interruptBuffer } = options;
|
|
1020
|
+
const { language = 'CPP', args = [], compileArgs = args, debug = false, breakpoints = [], pauseOnEntry = false, cppVersion, cVersion, debugBuffer, interruptBuffer, watchBuffer, watchResultBuffer } = options;
|
|
882
1021
|
const input = language === 'C' ? `test.c` : `test.cc`, obj = `test.o`, wasm = `test.wasm`;
|
|
883
1022
|
this.beginTrace(debug);
|
|
884
1023
|
this.debugBreakpoints = new Set(debug ? breakpoints : []);
|
|
885
1024
|
this.debugPauseOnEntry = debug && pauseOnEntry;
|
|
886
1025
|
this.debugBuffer = debugBuffer;
|
|
887
1026
|
this.debugInterruptBuffer = interruptBuffer;
|
|
1027
|
+
this.debugWatchBuffer = watchBuffer;
|
|
1028
|
+
this.debugWatchResultBuffer = watchResultBuffer;
|
|
888
1029
|
const buildKey = JSON.stringify({
|
|
889
1030
|
code,
|
|
890
1031
|
language,
|
|
@@ -913,7 +1054,7 @@ export default class Clang {
|
|
|
913
1054
|
return (this.wasm = await this.hostLogAsync(`Compiling ${wasm}`, WebAssembly.compile(wasmBytes)));
|
|
914
1055
|
}
|
|
915
1056
|
async compileLinkRun(code, options = {}) {
|
|
916
|
-
const { language = 'CPP', args = [], compileArgs = args, programArgs = [], debug = false, breakpoints = [], pauseOnEntry = false, cppVersion, cVersion, debugBuffer, interruptBuffer } = options;
|
|
1057
|
+
const { language = 'CPP', args = [], compileArgs = args, programArgs = [], debug = false, breakpoints = [], pauseOnEntry = false, cppVersion, cVersion, debugBuffer, interruptBuffer, watchBuffer, watchResultBuffer } = options;
|
|
917
1058
|
this.debug = debug;
|
|
918
1059
|
return await this.run(await this.compileLink(code, {
|
|
919
1060
|
language,
|
|
@@ -924,7 +1065,9 @@ export default class Clang {
|
|
|
924
1065
|
cppVersion,
|
|
925
1066
|
cVersion,
|
|
926
1067
|
debugBuffer,
|
|
927
|
-
interruptBuffer
|
|
1068
|
+
interruptBuffer,
|
|
1069
|
+
watchBuffer,
|
|
1070
|
+
watchResultBuffer
|
|
928
1071
|
}), true, `test.wasm`, ...programArgs);
|
|
929
1072
|
}
|
|
930
1073
|
}
|
package/dist/clangd/worker.js
CHANGED
|
@@ -26,6 +26,7 @@ const stdinReady = async () => {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
let writer = null;
|
|
29
|
+
let clangdRuntime = null;
|
|
29
30
|
const stdout = (charCode) => {
|
|
30
31
|
const json = jsonStream.insert(charCode);
|
|
31
32
|
if (json && writer)
|
|
@@ -36,7 +37,22 @@ const onAbort = () => {
|
|
|
36
37
|
writer?.end();
|
|
37
38
|
self.reportError?.('clangd aborted');
|
|
38
39
|
};
|
|
40
|
+
const syncWorkspaceFile = (filePath) => {
|
|
41
|
+
if (!clangdRuntime)
|
|
42
|
+
return;
|
|
43
|
+
const normalizedFilePath = filePath.startsWith(CLANGD_WORKSPACE_PATH)
|
|
44
|
+
? filePath
|
|
45
|
+
: `${CLANGD_WORKSPACE_PATH}/${filePath.replace(/^\/+/, '')}`;
|
|
46
|
+
const lastSlash = normalizedFilePath.lastIndexOf('/');
|
|
47
|
+
const directoryPath = lastSlash > 0 ? normalizedFilePath.slice(0, lastSlash) : CLANGD_WORKSPACE_PATH;
|
|
48
|
+
clangdRuntime.FS.mkdirTree(directoryPath);
|
|
49
|
+
clangdRuntime.FS.writeFile(normalizedFilePath, '');
|
|
50
|
+
};
|
|
39
51
|
self.addEventListener('message', async (event) => {
|
|
52
|
+
if (event.data?.type === 'sync-file' && typeof event.data?.name === 'string') {
|
|
53
|
+
syncWorkspaceFile(event.data.name);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
40
56
|
if (event.data?.type !== 'init')
|
|
41
57
|
return;
|
|
42
58
|
const baseUrl = normalizeClangdBaseUrl(event.data.baseUrl);
|
|
@@ -91,7 +107,7 @@ self.addEventListener('message', async (event) => {
|
|
|
91
107
|
const wasmBlob = new Blob([wasmBytes.buffer], { type: 'application/wasm' });
|
|
92
108
|
const wasmDataUrl = URL.createObjectURL(wasmBlob);
|
|
93
109
|
const { default: Clangd } = await jsModule;
|
|
94
|
-
|
|
110
|
+
clangdRuntime = await Clangd({
|
|
95
111
|
thisProgram: '/usr/bin/clangd',
|
|
96
112
|
mainScriptUrlOrBlob: jsDataUrl,
|
|
97
113
|
locateFile: (path, prefix) => path.endsWith('.wasm') ? wasmDataUrl : `${prefix}${path}`,
|
|
@@ -102,14 +118,14 @@ self.addEventListener('message', async (event) => {
|
|
|
102
118
|
onExit: onAbort,
|
|
103
119
|
onAbort
|
|
104
120
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
121
|
+
clangdRuntime.FS.mkdirTree(CLANGD_WORKSPACE_PATH);
|
|
122
|
+
syncWorkspaceFile(CLANGD_CPP_FILE_PATH);
|
|
123
|
+
clangdRuntime.FS.writeFile(`${CLANGD_WORKSPACE_PATH}/.clangd`, JSON.stringify({
|
|
108
124
|
CompileFlags: {
|
|
109
125
|
Add: createClangdCompileFlags()
|
|
110
126
|
}
|
|
111
127
|
}));
|
|
112
|
-
|
|
128
|
+
clangdRuntime.callMain([]);
|
|
113
129
|
writer = new BrowserMessageWriter(self);
|
|
114
130
|
const reader = new BrowserMessageReader(self);
|
|
115
131
|
reader.listen((data) => {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { DebugLanguageAdapter } from './language';
|
|
2
|
+
import type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable } from '../playground/options';
|
|
3
|
+
import type { TerminalControl } from '../terminal';
|
|
4
|
+
export type DebugWatchValue = {
|
|
5
|
+
expression: string;
|
|
6
|
+
value: string;
|
|
7
|
+
};
|
|
8
|
+
export type DebugSessionControllerOptions = {
|
|
9
|
+
terminal?: TerminalControl;
|
|
10
|
+
adapter?: DebugLanguageAdapter | null;
|
|
11
|
+
breakpoints?: number[];
|
|
12
|
+
cursorLine?: number | null;
|
|
13
|
+
syncBreakpointsWhile?: boolean | (() => boolean);
|
|
14
|
+
};
|
|
15
|
+
export declare function createDebugSessionController(options?: DebugSessionControllerOptions): {
|
|
16
|
+
readonly active: boolean;
|
|
17
|
+
readonly paused: boolean;
|
|
18
|
+
readonly pausedLine: number | null;
|
|
19
|
+
readonly locals: DebugVariable[];
|
|
20
|
+
readonly callStack: DebugFrame[];
|
|
21
|
+
readonly runToCursorLine: number | null;
|
|
22
|
+
readonly breakpoints: number[];
|
|
23
|
+
readonly effectiveBreakpoints: number[];
|
|
24
|
+
readonly cursorLine: number | null;
|
|
25
|
+
readonly canRunToCursor: boolean;
|
|
26
|
+
watchInput: string;
|
|
27
|
+
readonly watchExpressions: string[];
|
|
28
|
+
readonly watchValues: DebugWatchValue[];
|
|
29
|
+
begin: () => void;
|
|
30
|
+
reset: () => void;
|
|
31
|
+
handleEvent: (event: DebugSessionEvent) => void;
|
|
32
|
+
setTerminal: (terminal?: TerminalControl) => void;
|
|
33
|
+
setAdapter: (adapter: DebugLanguageAdapter | null) => void;
|
|
34
|
+
setBreakpoints: (lines: number[]) => void;
|
|
35
|
+
setCursorLine: (line: number | null) => void;
|
|
36
|
+
sendCommand: (command: DebugCommand) => Promise<boolean>;
|
|
37
|
+
runToCursor: (targetLine?: number | null) => Promise<boolean>;
|
|
38
|
+
stop: () => Promise<boolean>;
|
|
39
|
+
addWatchExpression: (expression?: string) => boolean;
|
|
40
|
+
removeWatchExpression: (expression: string) => void;
|
|
41
|
+
clearWatches: () => void;
|
|
42
|
+
};
|
|
43
|
+
export type DebugSessionController = ReturnType<typeof createDebugSessionController>;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { fromStore, get, writable } from 'svelte/store';
|
|
2
|
+
export function createDebugSessionController(options = {}) {
|
|
3
|
+
const activeStore = writable(false);
|
|
4
|
+
const pausedStore = writable(false);
|
|
5
|
+
const pausedLineStore = writable(null);
|
|
6
|
+
const localsStore = writable([]);
|
|
7
|
+
const callStackStore = writable([]);
|
|
8
|
+
const runToCursorLineStore = writable(null);
|
|
9
|
+
const watchInputStore = writable('');
|
|
10
|
+
const watchExpressionsStore = writable([]);
|
|
11
|
+
const watchValuesStore = writable([]);
|
|
12
|
+
const breakpointsStore = writable([...(options.breakpoints || [])]);
|
|
13
|
+
const cursorLineStore = writable(options.cursorLine ?? null);
|
|
14
|
+
const terminalStore = writable(options.terminal);
|
|
15
|
+
const adapterStore = writable(options.adapter ?? null);
|
|
16
|
+
const activeState = fromStore(activeStore);
|
|
17
|
+
const pausedState = fromStore(pausedStore);
|
|
18
|
+
const pausedLineState = fromStore(pausedLineStore);
|
|
19
|
+
const localsState = fromStore(localsStore);
|
|
20
|
+
const callStackState = fromStore(callStackStore);
|
|
21
|
+
const runToCursorLineState = fromStore(runToCursorLineStore);
|
|
22
|
+
const watchInputState = fromStore(watchInputStore);
|
|
23
|
+
const watchExpressionsState = fromStore(watchExpressionsStore);
|
|
24
|
+
const watchValuesState = fromStore(watchValuesStore);
|
|
25
|
+
const breakpointsState = fromStore(breakpointsStore);
|
|
26
|
+
const cursorLineState = fromStore(cursorLineStore);
|
|
27
|
+
const terminalState = fromStore(terminalStore);
|
|
28
|
+
const adapterState = fromStore(adapterStore);
|
|
29
|
+
let watchRequestVersion = 0;
|
|
30
|
+
function shouldSyncBreakpoints() {
|
|
31
|
+
if (typeof options.syncBreakpointsWhile === 'function') {
|
|
32
|
+
return options.syncBreakpointsWhile();
|
|
33
|
+
}
|
|
34
|
+
if (typeof options.syncBreakpointsWhile === 'boolean') {
|
|
35
|
+
return options.syncBreakpointsWhile;
|
|
36
|
+
}
|
|
37
|
+
return get(activeStore);
|
|
38
|
+
}
|
|
39
|
+
function getEffectiveBreakpoints() {
|
|
40
|
+
const lines = [...get(breakpointsStore)];
|
|
41
|
+
const runToCursorLine = get(runToCursorLineStore);
|
|
42
|
+
if (runToCursorLine !== null && !lines.includes(runToCursorLine))
|
|
43
|
+
lines.push(runToCursorLine);
|
|
44
|
+
return lines.sort((left, right) => left - right);
|
|
45
|
+
}
|
|
46
|
+
function refreshWatchValues() {
|
|
47
|
+
const expressions = [...get(watchExpressionsStore)];
|
|
48
|
+
const adapter = get(adapterStore);
|
|
49
|
+
const localVariables = [...get(localsStore)];
|
|
50
|
+
const paused = get(pausedStore);
|
|
51
|
+
const terminal = get(terminalStore);
|
|
52
|
+
const version = ++watchRequestVersion;
|
|
53
|
+
if (!expressions.length) {
|
|
54
|
+
watchValuesStore.set([]);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (paused && terminal?.debugEvaluate) {
|
|
58
|
+
watchValuesStore.set(expressions.map((expression) => ({ expression, value: '...' })));
|
|
59
|
+
(async () => {
|
|
60
|
+
const resolved = [];
|
|
61
|
+
for (const expression of expressions) {
|
|
62
|
+
resolved.push({
|
|
63
|
+
expression,
|
|
64
|
+
value: await terminal.debugEvaluate(expression)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (version === watchRequestVersion)
|
|
68
|
+
watchValuesStore.set(resolved);
|
|
69
|
+
})().catch(() => {
|
|
70
|
+
if (version === watchRequestVersion) {
|
|
71
|
+
watchValuesStore.set(expressions.map((expression) => ({ expression, value: 'error' })));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
watchValuesStore.set(expressions.map((expression) => {
|
|
77
|
+
try {
|
|
78
|
+
return {
|
|
79
|
+
expression,
|
|
80
|
+
value: adapter ? adapter.evaluateExpression(expression, localVariables) : 'error'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return {
|
|
85
|
+
expression,
|
|
86
|
+
value: error instanceof Error && error.message === 'unavailable' ? '?' : 'error'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
function syncBreakpoints() {
|
|
92
|
+
const terminal = get(terminalStore);
|
|
93
|
+
if (!shouldSyncBreakpoints() || !terminal?.setBreakpoints)
|
|
94
|
+
return;
|
|
95
|
+
void terminal.setBreakpoints(getEffectiveBreakpoints());
|
|
96
|
+
}
|
|
97
|
+
function clearPauseState() {
|
|
98
|
+
runToCursorLineStore.set(null);
|
|
99
|
+
pausedLineStore.set(null);
|
|
100
|
+
localsStore.set([]);
|
|
101
|
+
callStackStore.set([]);
|
|
102
|
+
pausedStore.set(false);
|
|
103
|
+
}
|
|
104
|
+
function reset() {
|
|
105
|
+
activeStore.set(false);
|
|
106
|
+
clearPauseState();
|
|
107
|
+
refreshWatchValues();
|
|
108
|
+
}
|
|
109
|
+
function begin() {
|
|
110
|
+
activeStore.set(true);
|
|
111
|
+
clearPauseState();
|
|
112
|
+
refreshWatchValues();
|
|
113
|
+
syncBreakpoints();
|
|
114
|
+
}
|
|
115
|
+
function handleEvent(event) {
|
|
116
|
+
if (event.type === 'pause') {
|
|
117
|
+
activeStore.set(true);
|
|
118
|
+
runToCursorLineStore.set(null);
|
|
119
|
+
pausedLineStore.set(event.line);
|
|
120
|
+
localsStore.set(event.locals);
|
|
121
|
+
callStackStore.set(event.callStack);
|
|
122
|
+
pausedStore.set(true);
|
|
123
|
+
refreshWatchValues();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (event.type === 'resume') {
|
|
127
|
+
pausedStore.set(false);
|
|
128
|
+
pausedLineStore.set(null);
|
|
129
|
+
localsStore.set([]);
|
|
130
|
+
callStackStore.set([]);
|
|
131
|
+
refreshWatchValues();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
reset();
|
|
135
|
+
}
|
|
136
|
+
function setTerminal(terminal) {
|
|
137
|
+
terminalStore.set(terminal);
|
|
138
|
+
refreshWatchValues();
|
|
139
|
+
syncBreakpoints();
|
|
140
|
+
}
|
|
141
|
+
function setAdapter(adapter) {
|
|
142
|
+
adapterStore.set(adapter);
|
|
143
|
+
refreshWatchValues();
|
|
144
|
+
}
|
|
145
|
+
function setBreakpoints(lines) {
|
|
146
|
+
breakpointsStore.set([...lines]);
|
|
147
|
+
syncBreakpoints();
|
|
148
|
+
}
|
|
149
|
+
function setCursorLine(line) {
|
|
150
|
+
cursorLineStore.set(line);
|
|
151
|
+
}
|
|
152
|
+
function addWatchExpression(expression) {
|
|
153
|
+
const watchInput = get(watchInputStore);
|
|
154
|
+
const watchExpressions = get(watchExpressionsStore);
|
|
155
|
+
const nextExpression = (expression || watchInput).trim();
|
|
156
|
+
if (!nextExpression || watchExpressions.includes(nextExpression))
|
|
157
|
+
return false;
|
|
158
|
+
watchExpressionsStore.set([...watchExpressions, nextExpression]);
|
|
159
|
+
watchInputStore.set('');
|
|
160
|
+
refreshWatchValues();
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
function removeWatchExpression(expression) {
|
|
164
|
+
watchExpressionsStore.set(get(watchExpressionsStore).filter((entry) => entry !== expression));
|
|
165
|
+
refreshWatchValues();
|
|
166
|
+
}
|
|
167
|
+
function clearWatches() {
|
|
168
|
+
watchInputStore.set('');
|
|
169
|
+
watchExpressionsStore.set([]);
|
|
170
|
+
refreshWatchValues();
|
|
171
|
+
}
|
|
172
|
+
async function sendCommand(command) {
|
|
173
|
+
const terminal = get(terminalStore);
|
|
174
|
+
if (!terminal || !get(pausedStore))
|
|
175
|
+
return false;
|
|
176
|
+
await terminal.debugCommand(command);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
async function runToCursor(targetLine = get(cursorLineStore)) {
|
|
180
|
+
const terminal = get(terminalStore);
|
|
181
|
+
const breakpoints = get(breakpointsStore);
|
|
182
|
+
if (!terminal ||
|
|
183
|
+
!get(pausedStore) ||
|
|
184
|
+
!targetLine ||
|
|
185
|
+
targetLine === get(pausedLineStore) ||
|
|
186
|
+
!terminal.setBreakpoints) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
const nextBreakpoints = breakpoints.includes(targetLine)
|
|
190
|
+
? [...breakpoints]
|
|
191
|
+
: [...breakpoints, targetLine].sort((left, right) => left - right);
|
|
192
|
+
runToCursorLineStore.set(breakpoints.includes(targetLine) ? null : targetLine);
|
|
193
|
+
await terminal.setBreakpoints(nextBreakpoints);
|
|
194
|
+
await terminal.debugCommand('continue');
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
async function stop() {
|
|
198
|
+
const terminal = get(terminalStore);
|
|
199
|
+
if (!terminal)
|
|
200
|
+
return false;
|
|
201
|
+
reset();
|
|
202
|
+
await terminal.stop?.();
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
get active() {
|
|
207
|
+
return activeState.current;
|
|
208
|
+
},
|
|
209
|
+
get paused() {
|
|
210
|
+
return pausedState.current;
|
|
211
|
+
},
|
|
212
|
+
get pausedLine() {
|
|
213
|
+
return pausedLineState.current;
|
|
214
|
+
},
|
|
215
|
+
get locals() {
|
|
216
|
+
return localsState.current;
|
|
217
|
+
},
|
|
218
|
+
get callStack() {
|
|
219
|
+
return callStackState.current;
|
|
220
|
+
},
|
|
221
|
+
get runToCursorLine() {
|
|
222
|
+
return runToCursorLineState.current;
|
|
223
|
+
},
|
|
224
|
+
get breakpoints() {
|
|
225
|
+
return breakpointsState.current;
|
|
226
|
+
},
|
|
227
|
+
get effectiveBreakpoints() {
|
|
228
|
+
return getEffectiveBreakpoints();
|
|
229
|
+
},
|
|
230
|
+
get cursorLine() {
|
|
231
|
+
return cursorLineState.current;
|
|
232
|
+
},
|
|
233
|
+
get canRunToCursor() {
|
|
234
|
+
const cursorLine = cursorLineState.current;
|
|
235
|
+
const terminal = terminalState.current;
|
|
236
|
+
return (pausedState.current &&
|
|
237
|
+
cursorLine !== null &&
|
|
238
|
+
cursorLine > 0 &&
|
|
239
|
+
cursorLine !== pausedLineState.current &&
|
|
240
|
+
!!terminal?.setBreakpoints);
|
|
241
|
+
},
|
|
242
|
+
get watchInput() {
|
|
243
|
+
return watchInputState.current;
|
|
244
|
+
},
|
|
245
|
+
set watchInput(value) {
|
|
246
|
+
watchInputStore.set(value);
|
|
247
|
+
},
|
|
248
|
+
get watchExpressions() {
|
|
249
|
+
return watchExpressionsState.current;
|
|
250
|
+
},
|
|
251
|
+
get watchValues() {
|
|
252
|
+
return watchValuesState.current;
|
|
253
|
+
},
|
|
254
|
+
begin,
|
|
255
|
+
reset,
|
|
256
|
+
handleEvent,
|
|
257
|
+
setTerminal,
|
|
258
|
+
setAdapter,
|
|
259
|
+
setBreakpoints,
|
|
260
|
+
setCursorLine,
|
|
261
|
+
sendCommand,
|
|
262
|
+
runToCursor,
|
|
263
|
+
stop,
|
|
264
|
+
addWatchExpression,
|
|
265
|
+
removeWatchExpression,
|
|
266
|
+
clearWatches
|
|
267
|
+
};
|
|
268
|
+
}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { MonacoDebugView } from './monaco';
|
|
1
|
+
export { MonacoDebugView, attachMonacoDebugActions } from './monaco';
|
|
2
|
+
export type { MonacoDebugActionsOptions } from './monaco';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { MonacoDebugView } from './monaco';
|
|
1
|
+
export { MonacoDebugView, attachMonacoDebugActions } from './monaco';
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { DebugVariable } from '../../playground/options';
|
|
2
2
|
import type { DebugLanguageAdapter } from '../language';
|
|
3
3
|
import type monaco from 'monaco-editor';
|
|
4
|
+
export type MonacoDebugActionsOptions = {
|
|
5
|
+
runToCursorLabel?: string;
|
|
6
|
+
onCursorLineChange?: (line: number | null) => void;
|
|
7
|
+
onRunToCursor?: (line: number | null) => void;
|
|
8
|
+
};
|
|
4
9
|
export declare class MonacoDebugView {
|
|
5
10
|
Monaco: typeof monaco;
|
|
6
11
|
editor: monaco.editor.IStandaloneCodeEditor;
|
|
@@ -16,3 +21,6 @@ export declare class MonacoDebugView {
|
|
|
16
21
|
setPauseState(pausedLine: number | null, locals: DebugVariable[], adapter: DebugLanguageAdapter | null): void;
|
|
17
22
|
dispose(): void;
|
|
18
23
|
}
|
|
24
|
+
export declare function attachMonacoDebugActions(editor: monaco.editor.IStandaloneCodeEditor, options?: MonacoDebugActionsOptions): {
|
|
25
|
+
dispose(): void;
|
|
26
|
+
};
|