Configuring Dape: Emacs' new DAP client

April 30, 2024

A while ago eglot, an LSP client, was introduced to emacs core, turning it more or less into the emacs’ official LSP client, to the detriment of lsp-mode. I love eglot, it’s easy to use, configure and it just works, and its integration with other emacs packages and components like company and flycheck is seamless. But, if there’s an aspect of development that emacs has struggled with, that is, in my opinion, debugging. (Although the C/C++ development experience in emacs remains uncontested).

dape

The developers of the aforementioned lsp-mode also mantain dap-mode, a DAP (LSPs but for debuggers) client for emacs. But as you can already guess, it needs lsp-mode, much to the chagrin of eglot users.

But now we have a competitor to dap-mode in the same way that eglot is a competitor to lsp-mode. Dape (which isn’t that new to be honest, with its first commit dating back to september 29th, 2023).

Unlike dap-mode, it strives to be a minimalist dap client, and it doesn’t make use of a launch.json file, relying instead on configurations written totally in elisp.

In this post, I’ll show an example configuration to use dape with python and flask, but I plan on updating it with more configurations. Dape is an extremely interesting project and the fact that it’s compatible with ruby’s rdbg debug gem catched my eye: emacs can’t compete right now with vs code as the development environment for ruby, mainly because of debugging. I haven’t had luck configuring dap-mode’s ruby debug mode and it doesn’t use rdbg, which is the newest, most up to date, and overall, best gem for debugging ruby. I also would like to check debugging vanilla javascript.

But anyway, reading dape’s source code we come across this:

command "python"
               command-args ("-m" "debugpy.adapter" "--host" "0.0.0.0" "--port" :autoport)
               port :autoport
               :request "launch"
               :type "python"
               :cwd dape-cwd))
            (common
             `(:args []
               :justMyCode nil
               :console "integratedTerminal"
               :showReturnValue t
               :stopOnEntry nil))

This is the python debugpy configuration. When you install dape, and run it with M-x dape, you can select this config. And you can assign all the variables you need in order to debug your flask project, mainly, :args and :module. But it’s not practical to type all of that everytime you wish to debug your project, so let’s jump to your init.file, and write the following:

(require 'dape)
;; Dape configs
(add-to-list 'dape-configs
	     `(debugpy-flask
	       modes (python-mode jinja2-mode)
	       command "python"
	       command-args ["-m" "debugpy.adapter" "--host" "0.0.0.0" "--port" :autoport]
	       port :autoport
	       :type "python"
	       :request "launch"
	       :module "flask"
	       :args ["--app" "src" "run" "--no-debugger" "--no-reload"]
	       :console "integratedTerminal"
	       :showReturnValue t
	       :justMyCode nil
	       :jinja t
	       :cwd dape-cwd-fn))

First, we load dape, and then, add another config to the dape-configs list, defined in dape.el. As you can see, it is kind of a shameless copy paste of dape’s base debugpy config, but with every variable set for flask development, like the module to be used (flask) and the CLI arguments it uses (–app, run, etc.) There’s an issue though, as you can see, the args array defines my app’s name as “src”, I tried to refer to my app’s name as a variable, so it could be used for different projects, but alas, I’m not too familiar with dape, or elisp for that matter. A possible solution is just to copy this to a .dir-locals.el file for a project wide configuration.