dape icon indicating copy to clipboard operation
dape copied to clipboard

Invoke `dape` from elisp directly.

Open Vighnesh-V opened this issue 11 months ago • 6 comments

Hi!

Apologies first off, because I am not super competent with elisp. I am working on a small function to invoke dape automatically after performing some additional parsing. When I invoke dape interactively (M-x dape), I can then type myTargetName :args [... some overrided args] and it launches dape. However, when I run dape in my elisp function like so:

(defun test ()
  (interactive)
  ;; Do some work to get some parameters 
  (dape `(myTargetName :args ["... some overrided args using the above parameter"]))
)

I get an error saying that the argument I passed is not a plist. Does there exist an easy way to call dape from elisp with the above arguments directly?

Vighnesh-V avatar Jan 13 '25 21:01 Vighnesh-V

Hello! Somewhat confusingly the interactive input to dape i.e. "Run adapter: ..." differs a bit form non interactive.

With interactive use (when you invoke dape as a command) Dape takes the first symbol and looks up an config i dape-configs and takes the rest of the input and applies on to that configuration that resulting object is the PLIST config which (dape config) is then called with (note that this mechanism does a lot of heavy lifting, like resolving functions and symbols).

So if you wanted to start debugging a go application in "~/go-project" you would need to evaluate:

(dape '( command "dlv"
         command-args ("dap" "--listen" "127.0.0.1::autoport")
         command-cwd "~/go-project"
         command-insert-stderr t
         port :autoport
         :request "launch"
         :type "debug"
         :cwd "."
         :program "."))

It's not optimal that these formats have diverged and it might be improved in the future.

svaante avatar Jan 15 '25 22:01 svaante

Hey @svaante,

Thanks for dape, it is awesome!

I want to write a wrapper function which calls dape with a specified config and reads an additional argument, and then puts that into :args. Can you help me do it?

This is my attempt:

(defun debug-my-thing (argument)
  (interactive "sDebug my thing for argument: ")
  (dape '(command "python"
          command-args ("-m" "debugpy.adapter")
          :type "executable"
          :request "launch"
          :module "my_thing"
          :cwd "/home/.../mything"
          :args (vector "-c" "config.yaml" "-e" "env_file.yaml" "-l" argument)
          )))

It is not working because (vector) doesn't seem to be right:

-->

* Welcome to Dape REPL! *
Available Dape commands: debug, next, continue, pause, step, out, up, down, threads, stack, modules, sources, breakpoints, scope, watch, restart, kill, disconnect, quit
Empty input will rerun last command.

* Invalid message: "args"[0] must be str *
* Adapter connection shutdown without successfully initializing *
* Session terminated *
> 

and If I write:

:args [... argument]

then I get Error running timer: (wrong-type-argument json-value-p argument)

bastianbeischer avatar Mar 08 '25 11:03 bastianbeischer

Thanks for the kind words, and sorry for the delay!

This should work:

(defun debug-my-thing (argument)
  (interactive "sDebug my thing for argument: ")
  (dape `(command "python"
          command-args ("-m" "debugpy.adapter")
          :type "executable"
          :request "launch"
          :module "my_thing"
          :cwd "/home/.../mything"
          :args ["-c" "config.yaml" "-e" "env_file.yaml" "-l" ,argument]
          )))

See backquote

But I recommend you use the default interface for this kind of thing, together with M-r and savehist-mode you can enter it once in the minibuffer (and be save for future sessions with savehist-mode) and then with dape + M-r you can search through your history and find it again and then just use dape-restart to run again, etc.

But the beauty of Emacs is also that you can mold it to whatever you want!

svaante avatar Mar 19 '25 23:03 svaante

That kind of approach wouldn't work because basically argument is unique every time I want to debug. What would work is, if I would be able to interactively edit the :args, and indeed I can override it when invoking interactively from M-x dape, but because there are various other arguments I would have to type the whole args list again, changing only argument. What would be cool is if :args <TAB> would fill in the :args that was given when the config was defined, and still allowed you to edit it...

bastianbeischer avatar Mar 30 '25 16:03 bastianbeischer

This is how I have configured dape to work with the Remote Ruby Debugger (rdbg) in a ruby/rails and minitest environment.

I have one dape Rails config and one dape minitest config. I can run my/dape and it will pick the correct configuration based on whether I am currently in a test file or not.

Here is how it is set up:

When I start the Rails server, it runs a ruby debugger server on port 3020. This process stays running until I stop the rails server. The server runs normally, unless I put a debugger statement in my code. If I do that and make a request, the code stops and I run my/dape to enter the debugger.

When I run a minitest test, it runs a second, temporary ruby debugger server on port 3021 for the duration of the test run. The test runs normally, unless I put a debugger statement in my code. If I do that, and run the test, the test will stop, and I can run my/dape to enter the debugger.

The Ruby debugger server is started in a rails initializer config/initializers/debugger.rb on the port set by the environment variable RUBY_DEBUG_PORT. It runs for both rails server and minitest tests:

# start ruby debugger server in the test environment (for running test scripts)
# start ruby debugger server in the development environment, but only when the server is also running
# (this prevents the debugger server from starting when you run `rails console`)
# The server will be configured to run on the port set in the environment variable RUBY_DEBUG_PORT
if Rails.env.test? || (defined?(Rails::Server) && Rails.env.development?)
  require "debug/open_nonstop"
end

I can start the rails server using:

RUBY_DEBUG_PORT=3020 bin/rails server

However, I usually start the rails server within emacs using Prodigy:

(use-package prodigy)
(setq prodigy-services
      '((:name "My Rails Projdct"
         :command "bundle"
         :args ("exec" "rails" "server")
         :cwd "~/projects/the-rails-project"
         :env (("RUBY_DEBUG_PORT" "3020")))))

I use minitest-emacs to run my minitest tests, and it's not easy to get it to set an environment variable when it runs a test. So I set a project-wide environment variable using mise in my mise.toml file:

RUBY_DEBUG_PORT = "3021"

I configured dape so that it can be run in the normal way (M-x dape), and I can pick either the rdbg-rails config or the rdbg-minitest config.

But I also can run M-x my/dape which will pick the correct config for me depending on whether I'm currently editing a test file or not:

(setq dape-rdbg-rails-config
      `(
        modes (ruby-mode ruby-ts-mode)
        command "rdbg"
        command-args ("-A 3020")
        port 3020
        :type "Ruby"
        :request "attach"
        )
      )

(add-to-list 'dape-configs (push 'rdbg-rails dape-rdbg-rails-config))

(setq dape-rdbg-minitest-config
    `(
        modes (ruby-mode ruby-ts-mode)
        command "rdbg"
        command-args ("-A 3021")
        port 3021
        :type "Ruby"
        :request "attach"
        )
      )

(add-to-list 'dape-configs (push 'rdbg-minitest dape-rdbg-rails-config))

(defun my/dape ()
  (interactive)
  (if (string-match "_test.rb" buffer-file-name)
      (dape dape-rdbg-minitest-config)
    (dape dape-rdbg-rails-config)
    ))

It took me a while to figure out how to create and manipulate the elisp data structures for the dape config so they work interactively via M-x dape and also via elisp (dape config) - hopefully someone else finds this useful!

mgraham avatar Apr 23 '25 20:04 mgraham

@mgraham I have also just gone through this and can simplify your setup a bit 😄

You don't need the require, you can take care of it with env vars or a config file (either or, I use env vars in my Procfile) listed here

For the port, I use a WEB_DEBUG_PORT env var on each project in my .env.development.local (the convention we use for local env vars) which gets pulled in when I do bin/dev -e .env.development.local and the rails line looks like

web: RUBY_DEBUG_OPEN=true RUBY_DEBUG_PORT=${WEB_DEBUG_PORT} RUN_SIDEKIQ_IN_TEST_MODE=false bin/rails server -p 3002

On the Emacs side, I have a dape config for attatching against the configured port and a bound command for calling rspec:

  (setq dape-configs
        (cons
         `(rdbg-rails
           modes (ruby-mode ruby-ts-mode web-mode)
           :type "ruby"
           :request "attach"
           port (if (file-exists-p (expand-file-name ".env.development.local" (project-root (project-current))))
                    (string-to-number (dotenv-get "WEB_DEBUG_PORT" (expand-file-name ".env.development.local" (project-root (project-current)))))
                  :autoport)
           :localfs t)
         (cl-remove-if (lambda (cfg) (eq (car-safe cfg) 'rdbg-rails))
                       dape-configs)))

 (defun lkn/rspec-debug ()
    (interactive)
    (let* ((cmd (format "RAILS_ENV=test bundle exec rspec %s:%d"
                        (expand-file-name (buffer-file-name))
                        (line-number-at-pos))))
      (dape (dape--config-eval 'rdbg `(-c ,cmd)))))

Hope this helps 😄

elken avatar Apr 30 '25 07:04 elken