如何在Monaco Editor中添加运行调试功能
Monaco Editor 逐行代码调试实现方案
核心依赖与基础逻辑
Monaco Editor 原生支持基于 Debug Adapter Protocol (DAP) 的调试能力,核心是通过调试适配器(Debug Adapter)连接编辑器与目标代码运行环境,实现断点管理、执行控制、数据同步等功能。如果是JavaScript、Python等常见语言,可直接复用开源调试适配器;自定义语言则需要自行实现符合DAP的适配器。
核心功能实现步骤
1. 初始化编辑器与调试模块
先完成Monaco Editor的基础初始化,再注册调试适配器工厂,建立编辑器与调试服务的连接:
import * as monaco from 'monaco-editor'; // 以JS调试为例,引入对应调试客户端 import { DebugClient } from 'vscode-debugadapter'; // 创建编辑器实例 const editor = monaco.editor.create(document.getElementById('editor-container'), { value: 'function add(a, b) { return a + b; }\nadd(1, 2);', language: 'javascript', lineNumbers: 'on', gutter: 'on' }); // 注册调试适配器工厂 monaco.debug.registerDebugAdapterDescriptorFactory('javascript', { createAdapter: () => new DebugClient('node', './node_modules/vscode-js-debug/out/src/debugAdapter.js') });
2. 断点管理实现
监听编辑器 gutter 的点击事件,同步断点到调试适配器,并维护断点状态:
// 监听断点变更事件 monaco.debug.onDidChangeBreakpoints(({ added, removed }) => { // 新增断点 added.forEach(bp => { debugClient.setBreakpointsRequest({ source: { path: bp.source?.path || 'inline-code' }, breakpoints: [{ line: bp.lineNumber }] }); }); // 删除断点 removed.forEach(bp => { debugClient.setBreakpointsRequest({ source: { path: bp.source?.path || 'inline-code' }, breakpoints: [] }); }); });
3. 逐行调试控制逻辑
实现启动、暂停、步进(步过/步入/步出)等调试控制按钮,绑定对应的DAP命令:
// 启动调试会话 document.getElementById('btn-start').addEventListener('click', async () => { const session = monaco.debug.startDebugSession({ type: 'javascript', request: 'launch', program: '/path/to/target-code.js', console: 'integratedTerminal' }); }); // 步过(下一步) document.getElementById('btn-step-over').addEventListener('click', () => { debugClient.nextRequest(); }); // 步入 document.getElementById('btn-step-in').addEventListener('click', () => { debugClient.stepInRequest(); }); // 步出 document.getElementById('btn-step-out').addEventListener('click', () => { debugClient.stepOutRequest(); }); // 暂停调试 document.getElementById('btn-pause').addEventListener('click', () => { debugClient.pauseRequest({ threadId: activeThreadId }); });
4. 运行结果与状态同步
监听调试适配器的stopped事件,高亮当前执行行,并获取栈帧、变量数据展示:
// 监听调试停止事件(命中断点/步进触发) debugClient.on('stopped', async (event) => { const threadId = event.threadId; // 获取当前栈帧 const stackResp = await debugClient.stackTraceRequest({ threadId }); const currentFrame = stackResp.stackFrames[0]; // 高亮当前执行行 editor.setDecorations('debug-current-line', [{ range: new monaco.Range(currentFrame.line, 1, currentFrame.line, 1), options: { className: 'debug-highlight-line' } }]); // 获取当前作用域变量 const scope = currentFrame.scopes[0]; const varsResp = await debugClient.variablesRequest({ variablesReference: scope.variablesReference }); // 渲染变量面板(需自行实现DOM更新逻辑) renderVariablePanel(varsResp.variables); });
关键注意事项
- 跨域处理:若调试适配器部署在独立服务,需配置CORS允许编辑器域名的请求
- 自定义语言适配:需自行实现符合DAP的调试适配器,处理断点验证、执行控制、数据序列化等逻辑
- 状态一致性:调试过程中若编辑器代码修改,需同步更新调试环境的代码(如热重载),避免调试状态不一致
- 性能优化:批量处理断点变更请求,减少编辑器与适配器的通信次数,避免卡顿
内容的提问来源于stack exchange,提问作者Syed Irshad
相关产品推荐
相关产品推荐

