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.
Files changed (56) hide show
  1. package/README.md +94 -3
  2. package/dist/clang/app.d.ts +4 -0
  3. package/dist/clang/app.js +315 -12
  4. package/dist/clang/index.d.ts +4 -0
  5. package/dist/clang/index.js +151 -8
  6. package/dist/clangd/worker.js +21 -5
  7. package/dist/debug/controller.svelte.d.ts +43 -0
  8. package/dist/debug/controller.svelte.js +268 -0
  9. package/dist/debug/editor/index.d.ts +2 -1
  10. package/dist/debug/editor/index.js +1 -1
  11. package/dist/debug/editor/monaco.d.ts +8 -0
  12. package/dist/debug/editor/monaco.js +23 -0
  13. package/dist/debug/expression.d.ts +24 -0
  14. package/dist/debug/expression.js +307 -138
  15. package/dist/debug/index.d.ts +4 -1
  16. package/dist/debug/index.js +2 -1
  17. package/dist/index.d.ts +7 -3
  18. package/dist/index.js +4 -3
  19. package/dist/lsp/cpp/server.d.ts +2 -0
  20. package/dist/lsp/cpp/server.js +57 -0
  21. package/dist/lsp/index.d.ts +5 -0
  22. package/dist/lsp/index.js +4 -0
  23. package/dist/lsp/python/main.worker.d.ts +1 -0
  24. package/dist/lsp/python/main.worker.js +92 -0
  25. package/dist/lsp/python/package/arcturus_python_lsp/__init__.py +3 -0
  26. package/dist/lsp/python/package/arcturus_python_lsp/server.py +462 -0
  27. package/dist/lsp/python/server.d.ts +2 -0
  28. package/dist/lsp/python/server.js +54 -0
  29. package/dist/lsp/registry.d.ts +2 -0
  30. package/dist/lsp/registry.js +12 -0
  31. package/dist/lsp/runtime.d.ts +3 -0
  32. package/dist/lsp/runtime.js +30 -0
  33. package/dist/lsp/types.d.ts +15 -0
  34. package/dist/lsp/types.js +1 -0
  35. package/dist/playground/assets.d.ts +5 -0
  36. package/dist/playground/assets.js +8 -0
  37. package/dist/playground/clang.d.ts +5 -0
  38. package/dist/playground/clang.js +45 -2
  39. package/dist/playground/index.d.ts +5 -5
  40. package/dist/playground/index.js +45 -5
  41. package/dist/playground/options.d.ts +9 -0
  42. package/dist/playground/python.d.ts +1 -0
  43. package/dist/playground/python.js +12 -0
  44. package/dist/playground/rust.d.ts +32 -0
  45. package/dist/playground/rust.js +175 -0
  46. package/dist/playground/sandbox.d.ts +18 -4
  47. package/dist/playground/wasmRustVersion.d.ts +1 -0
  48. package/dist/playground/wasmRustVersion.js +1 -0
  49. package/dist/playground/worker/clang.js +12 -4
  50. package/dist/playground/worker/rust.d.ts +1 -0
  51. package/dist/playground/worker/rust.js +159 -0
  52. package/dist/terminal/Terminal.svelte +78 -25
  53. package/dist/terminal/Terminal.svelte.d.ts +2 -0
  54. package/dist/terminal/theme.js +2 -2
  55. package/dist/terminal/types.d.ts +3 -0
  56. package/package.json +15 -9
@@ -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: index + 1
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: index + 1
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
  }
@@ -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
- const clangd = await Clangd({
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
- clangd.FS.mkdirTree(CLANGD_WORKSPACE_PATH);
106
- clangd.FS.writeFile(CLANGD_CPP_FILE_PATH, '');
107
- clangd.FS.writeFile(`${CLANGD_WORKSPACE_PATH}/.clangd`, JSON.stringify({
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
- clangd.callMain([]);
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
+ };