Show HN:Dap-mux – 将你的编辑器和 REPL 连接到同一个调试会话

3作者: YesJustWolf24 天前
我编写代码已有四十多年,使用过多种语言,参与过许多项目(如果您还记得的话,包括 Firefox、Final Cut Pro、Newton 和 Fullwrite Professional;所有这些都使用了我的“旧名字”)。 我写了一个小而简单的东西来解决一个痛点。这就是 UNIX 的哲学:小巧的“一招鲜”工具,每个工具都*非常*擅长它唯一的功能,然后用户可以将它们连接起来解决实际问题。我是一个命令行(CLI)用户,对于几乎所有事情,我已经实现了这一点。但调试不行。我解决的痛点是那个能够实现调试这一哲学理念的连接器。那个东西就是 dap-mux。一个 DAP 多路复用器,将一个一对一的协议转换成一个协作的会话,其中包含您完成工作所需的任意数量的工具! 它的起源:对我而言是 Helix 和 Python(有时是 IPython),而我的团队其他成员使用 PyCharm(我一直很喜欢!)。我团队的问题是他们想要 PyCharm 的调试器,因此必须满足于 JetBrains 编辑器。*我的*问题是我可以拥有一个功能齐全的调试器*或者*我可以拥有 IPython*或者*我可以拥有 Helix(有时是 Helix 和调试器的不令人满意组合)。这就是我的“痛点”。 DAP(Debug Adapter Protocol,调试适配器协议)是诱人的答案,但又不是。DAP 是编辑器(那些不想编写自己的调试器)开始采用的标准。DAP 的问题在于它是一对一的。一个编辑器连接到一个调试器。就这么简单。这并不是我问题的解决方案。然后突然间,它*就*成了解决方案。我意识到一个 DAP 多路复用器可以让你将任何支持 DAP 的编辑器连接到任何语言的任何调试器,同时还可以连接到一个 REPL,或者编辑器的另一个会话(或者另一个编辑器)!附带的好处是,现在,就像 screen 或 tmux 一样,由于每个进程都是独立的:会话是持久的。只需重启崩溃的任何东西,你就能回到原来的状态! 其中有一些困难的部分:序列化、延迟加入者、状态管理。不同的端点以不同的顺序处理不同的操作,但使用相同的消息 ID。我通过类似 NAT 的方式解决了这些问题。不过,我不是在翻译网络地址,而是在将每个客户端的序列号转换为全局有序的内容,然后将回复正确地路由回等待它们的端点,同时将这些回复的序列号映射回该端点的空间。了解调试器的当前状态,并将其作为消息序列重放给延迟加入者,可以让你以任意顺序启动/连接客户端。我选择了 Python:asyncio 非常适合 I/O 路由模式,并且它允许 IPython 扩展在进程内运行,而不是通过 IPC。 还有一些问题尚未解决:例如,我认为客户端中的配置和/或启动序列过于复杂。但它能正常工作!我得到了我想要的! 我每天使用的组合:Python + debugpy + Helix + IPython,所有这些同时连接。使用 `%n` 或 `%s` 进行单步调试,使用 `%eval` 评估表达式,观察 Helix 实时跟踪当前行。Rust 与 codelldb 是第二个已确认的组合——我使用 Helix 和一个第三方 DAP 观察工具调试了一个 Dijkstra 实现,这两个工具都连接到同一个 codelldb 会话。一位社区成员 Sean Perry 已经构建了 [dap-observer](https://github.com/shaleh/dap-observer),它可以将当前帧的变量渲染成一个可导航的终端树。*这*正是我的梦想!小巧、专注、可连接的工具协同工作! 还有很多可以尝试的:其他编辑器、其他调试适配器、Windows、其他语言。这些都还没有涉及。现在最有帮助的是人们用他们自己的设置进行测试,并报告他们发现的问题。是时候玩起来了! 对于 Python + IPython,请使用 `uv tool install 'dap-mux[ipython]'`。对于任何语言和适配器的无头使用,请使用 `uv tool install dap-mux`。无需 Python 生态系统的任何部分。 https://github.com/dap-mux/dap-mux
查看原文
I have been coding over four decades, in many languages, on many projects (including Firefox, Final Cut Pro, the Newton, and Fullwrite Professional if you can remember that far back; all these using my &quot;dead-name&quot;).<p>I wrote something small and simple to scratch an itch. It&#x27;s the UNIX philosophy: small &quot;one-trick ponies&quot;, each *really* good at their one trick, then the user can hook them together to solve actual problems. I&#x27;m a CLI guy, and for almost everything, I already have this. But not for debugging. The itch I scratched was the connector that enables this philosophy for debugging. That thing is dap-mux. A DAP multiplexer turning a one-to-one protocol into a cooperating session of as many tools as you need to get it done!<p>How it started: Helix and Python for me (and sometimes IPython), with the rest of my team using PyCharm (which I have long loved!). My team&#x27;s problem is that they want the PyCharm debugger, and so must be satisfied with the JetBrains editor. *My* problem was I could use a full-blown debugger *or* I could have IPython *or* I could have Helix (or sometimes an unsatisfying combination of Helix and the debugger). That was my &quot;itch&quot;.<p>DAP (Debug Adapter Protocol) is the tantalizing answer, except it isn&#x27;t. DAP is what editors (that don&#x27;t want to write their own debuggers) are starting to adopt. The problem with DAP is it&#x27;s one-to-one. One editor connects to one debugger. Done. Not a solution to my problem. And then suddenly, it *was* the solution. I realized that a DAP multiplexer would let you connect any DAP-aware editor to any debugger for any language, and simultaneously to a REPL, another session of your editor (or a different editor)! With the side benefit that now, like screen or tmux, since each process is its own thing: sessions are durable. Just restart whatever crashed and you&#x27;re back where you were!<p>There were hard parts: sequencing, late joiners, state management. Different end-points working on different actions in different sequences but with the same message ids. I solved these problems something like how NAT works. Instead of translating network addresses, though, I&#x27;m translating the sequence numbers of each client into something global and ordered, then correctly routing replies back to the end-point awaiting them, while mapping the sequence numbers for those replies back into the space of that end-point. Knowing the current state of the debugger, and replaying that as a message sequence to late joiners lets you start&#x2F;connect the clients in any order. I chose Python: asyncio fits the I&#x2F;O-router pattern perfectly, and it lets the IPython extension run in-process rather than over IPC.<p>There are problems not yet solved: for instance, I think configuration in the clients and&#x2F;or the startup sequence is too complicated. But it functions! I got what I wanted!<p>The combination I use every day: Python + debugpy + Helix + IPython, all connected simultaneously. Step with `%n` or `%s`, evaluate expressions with `%eval`, watch Helix track the current line in real time. Rust with codelldb is the second confirmed combination — I debugged a Dijkstra implementation with Helix and a third-party DAP observer tool both connected to the same codelldb session. A community member, Sean Perry, has already built [dap-observer](<a href="https:&#x2F;&#x2F;github.com&#x2F;shaleh&#x2F;dap-observer" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;shaleh&#x2F;dap-observer</a>), which renders the current frame&#x27;s variables as a navigable terminal tree. *This* was my exact dream! Small, focused, connectable tools all playing together!<p>There&#x27;s so much left to try: other editors, other debug adapters, Windows, other languages. None of this has been touched yet. The most helpful thing now is people testing it with their own setup and reporting what they find. It&#x27;s time to play!<p>`uv tool install &#x27;dap-mux[ipython]&#x27;` for Python + IPython. `uv tool install dap-mux` for headless use with any language and adapter. No need for any part of the Python ecosystem.<p><a href="https:&#x2F;&#x2F;github.com&#x2F;dap-mux&#x2F;dap-mux" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;dap-mux&#x2F;dap-mux</a>