Skip to content
jixiuf edited this page Jul 15, 2024 · 25 revisions

You will find documentation and adapter installation details in the README.org.

dape-eval-in-repl

(defun dape-eval-in-repl()
  (interactive)
  (let (e )
    (if (region-active-p)
        (setq e (buffer-substring (region-beginning)
                                  (region-end)))
      (setq e (thing-at-point 'symbol)))
    (when current-prefix-arg
      (setq e (read-string "Evaluate: " e)))
    (dape-repl)
    (with-current-buffer (get-buffer "*dape-repl*")
      (goto-char (point-max))
      (insert e)
      (comint-send-input))))

dape-dwim

(defun compare-vectors-prefix (vec1 vec2)
  (let ((min-length (min (length vec1) (length vec2))))
    (cl-loop for i below min-length
             always (equal (aref vec1 i) (aref vec2 i)))))

;;;###autoload
(defun dape-dwim()
  "If a DAP (Debug Adapter Protocol) session is active, terminate the session.
If there's no active DAP session, start a new session with default configuration.
When prefix argument is given, invoke `dape' interactively instead.

This function uses `dape' related functions to manage debug sessions for Emacs.
It also handles session configuration by looking up the appropriate settings
based on the current context and previous history."
  (interactive)
  (require 'dape)
  (if (dape--live-connection 'parent t)
      (progn
        (call-interactively #'dape-quit)
        (message "dape quit now!"))
    (if current-prefix-arg
        (call-interactively #'dape)
      (let* ((cfg (car (cl-loop for (key . config) in dape-configs
                                when (and (dape--config-mode-p config)
                                          (dape--config-ensure config))
                                collect (dape--config-eval key config))))
             (suggested-configs
              (cl-loop for (key . config) in dape-configs
                       when (and (dape--config-mode-p config)
                                 (dape--config-ensure config))
                       collect (dape--config-to-string key nil)))
             (hist (seq-find (lambda (str)
                               (ignore-errors
                                 (member (thread-first (dape--config-from-string str)
                                                       (car)
                                                       (dape--config-to-string nil))
                                         suggested-configs)))
                             dape-history)))
        (when hist
          (setq hist (nth 1 (dape--config-from-string hist t)))
          (when (and
                 (equal (plist-get  hist 'command-cwd) (plist-get  cfg 'command-cwd))
                 (equal (plist-get  hist 'command) (plist-get  cfg 'command))
                 (compare-vectors-prefix (plist-get  hist ':args) (plist-get  cfg ':args))
                 (equal (plist-get  hist ':program) (plist-get  cfg ':program)))
            (setq cfg hist))
          )
        (when (plist-get  cfg 'command-cwd)
          (call-process "find" nil nil nil (plist-get  cfg 'command-cwd)
                        "-maxdepth" "1" "-type" "f" "-name" "__debug_bin*" "-exec" "rm" "{}" ";"))
        (when cfg (dape cfg)))
      (message "spc-dd: toggle breakpoint spc-dk:clear breakpints M-h:info H-r:run-or-stop"))))


Debug Configurations

On this page you will find useful dape configurations.

Feel free to add useful configurations!

Ruby - rdbg

Remote attach via docker-compose

NB: Once you have the configuration implemented as defined below, you can trigger dape from within the container within the context of a TRAMP connection. Use C-x C-f /docker:<container_name>:/path/to/project to enter the container, then run M-x dape.

Rails Server (with Puma)

Ensure your target ruby service is setup like so in your docker-compose.yml:

rails-app:
 image: rails-app:latest
 command: rdbg --port 5678 -c -- rails server -b 0.0.0.0
 environment:
   - RUBY_DEBUG_OPEN=true
   - RUBY_DEBUG_HOST=0.0.0.0
   # Allows rails to initialise, only stopping when a breakpoint is hit
   - RUBY_DEBUG_NONSTOP=true
   # Needed to stop puma making workers which all attempt to open rdbg 
   # on the same port
   - WEB_CONCURRENCY=0 
 ports:
   - "5678:5678" # For remote debugging RAILS with debug.rb

Use the following dape-config to attach to the process:

(add-to-list 'dape-configs
             `(rdbg-attach-local-source
               prefix-local "~/code/ruby-app/"
               prefix-remote "/usr/app/"
               port 5678
               :request "attach"
               :localfs t))

RSpec (with rspec-mode)

In order to setup rspec-mode correctly, it is necessary to run rdbg on a different port to the rails server instance. By using a custom wrapper function, we can add in the necessary environment variables, and spin up a fresh instance for the Rspec run:

(defun jjh/rspec--compose-default-wrapper (_compose compose-service command)
  "Function for wrapping a command for execution inside a compose
environment. By adding the port manually here, we keep it out of the
rails service - keeping it free for just rspec. We also name it so
it's easy to find."
  (format "docker-compose -f %s run -it --rm --name ruby-app-rspec -e 'RUBY_DEBUG_PORT=5680' -p 5680:5680 %s sh -c \"%s\""
          rspec-docker-file-name compose-service command))

(setq rspec-docker-wrapper-fn #'jjh/rspec--compose-default-wrapper)

It’s important to set the debug gem to require: false so we can control when the port is opened. Update the Gemfile:

gem "debug", require: false

In the spec_helper.rb, require the debug gem:

# For debugging
require "debug/open_nonstop"

You can then trigger the rspec tests with a custom dape-config entry:

(add-to-list 'dape-configs
             `(rdbg-attach-rspec
               prefix-local "~/code/app/"
               prefix-remote "/usr/app/"
               port 5680
               :request "attach"
               :localfs t))

Python - debugpy

Remote attach

Make sure that remote application imports and starts debugpy.

import debugpy
# Port does not matter just needs to match port in config
# dape debugpy-remote-attach
debugpy.listen(("0.0.0.0", 5678))
(add-to-list 'dape-configs
 `(debugpy-remote-attach
   modes (python-mode python-ts-mode)
   host (lambda () (read-string "Host: " "localhost"))
   port (lambda () (read-number "Port: "))
   :request "attach"
   :type "python"
   :pathMappings [(:localRoot (lambda ()
                                (read-directory-name "Local source directory: "
                                                     (funcall dape-cwd-fn)))
                   :remoteRoot (lambda ()
                                 (read-string "Remote source directory: ")))]
   :justMyCode nil
   :showReturnValue t))

Thanks to @Gavinok for contributing.

Go - dlv

Unit test at point

(add-to-list 'dape-configs
             `(delve
               modes (go-mode go-ts-mode)
               ensure dape-ensure-command
               fn (dape-config-autoport dape-config-tramp)
               command "dlv"
               command-args ("dap" "--listen" "127.0.0.1::autoport")
               command-cwd (lambda()(if (string-suffix-p "_test.go" (buffer-name))
                                     default-directory (dape-cwd)))
               port :autoport
               :type "debug"
               :request "launch"
               :mode (lambda() (if (string-suffix-p "_test.go" (buffer-name)) "test" "debug"))
               :program "."
               :cwd "."
               :args (lambda()
                       (require 'which-func)
                       (if (string-suffix-p "_test.go" (buffer-name))
                           (when-let* ((test-name (which-function))
                                       (test-regexp (concat "^" test-name "$")))
                             (if test-name `["-test.run" ,test-regexp]
                               (error "No test selected")))
                         []))))

The point can be anywhere in function TestDemo, then call dape and “Run adapter: delve-unit-test” it will execute go test -test.run TestDemo.

func TestDemo(t *testing.T) {
   t.Error("hello")
}

Thanks to @jixiuf for contributing.

Swift IOS

Setup

Follow the steps in the README.org on how to install codelldb in section “C, C++ and Rust - codelldb”

Add the following configuration to your init.el:

(add-to-list 'dape-configs
             `(ios
               modes (swift-mode)
               command-cwd dape-command-cwd
               command ,(file-name-concat dape-adapter-dir
                                          "codelldb"
                                          "extension"
                                          "adapter"
                                          "codelldb")
               command-args ("--port" :autoport
                             "--settings" "{\"sourceLanguages\":[\"swift\"]}"
                             "--liblldb" "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB")
               port :autoport
               simulator-id "iPhone 15 Pro"
               app-bundle-id "com.yourcompany.ProductName"
               fn (dape-config-autoport
                   ,(lambda (config)
                      (with-temp-buffer
                        (let* ((command
                                (format "xcrun simctl launch --wait-for-debugger --terminate-running-process %S %S"
                                        (plist-get config 'simulator-id)
                                        (plist-get config 'app-bundle-id)))
                               (code (call-process-shell-command command nil (current-buffer))))
                          (dape--repl-message (format "* Running: %S *" command))
                          (dape--repl-message (buffer-string))
                          (save-match-data
                            (if (and (zerop code)
                                     (progn (goto-char (point-min))
                                            (search-forward-regexp "[[:digit:]]+" nil t)))
                                (plist-put config :pid (string-to-number (match-string 0)))
                              (dape--repl-message (format "* Running: %S *" command))
                              (dape--repl-message (format "Failed to start simulator:\n%s" (buffer-string)))
                              (user-error "Failed to start simulator")))))
                      config))
               :type "lldb"
               :request "attach"
               :cwd "."))

Debug

Start simulator:

open -n "Simulator"

Compile program, see xcodebuild (could use compile flag in dape)

Debug Jest unit test

(contributed by @timcharper)

The following configuration can be used to run an entire file of jest tests (works as of dape 0.5.0).

(defun dape-jest/find-file-buffer-default ()
  "Read filename at project root, defaulting to current buffer. Return vector of jest args to run said file"
  (let ((file (dape-buffer-default)))
    (if file
        `["--runInBand" "--no-coverage" ,file]
      (user-error "No file found"))))

(defun dape-jest/ensure (config)
  "Ensure node is available, jest is installed, that the dapDebugServer is installed"

  (dape-ensure-command config)
  (let ((cwd (dape-cwd))
        (js-debug-file (expand-file-name
                        (dape--config-eval-value (car (plist-get config 'command-args)))
                        (dape--config-eval-value (plist-get config 'command-cwd))))
        (node-jest-file (expand-file-name
                        (dape--config-eval-value (plist-get config :program))
                        (dape--config-eval-value (plist-get config :cwd)))))
    (unless (file-exists-p js-debug-file)
      (user-error "Debug server file %S does not exist" js-debug-file))
    (unless (file-exists-p node-jest-file)
      (user-error "Jest executable not found at %S" node-jest-file))))


(add-to-list 'dape-configs
             `(jest
               modes (js-mode js-ts-mode typescript-mode)
               ensure dape-jest/ensure
               command "node"
               command-cwd dape-command-cwd
               command-args (,(expand-file-name
                               (file-name-concat dape-adapter-dir
                                                 "js-debug"
                                                 "src"
                                                 "dapDebugServer.js"))
                             :autoport)
               port :autoport
               fn dape-config-autoport
               :type "pwa-node"
               :cwd dape-cwd
               :env (:VAR1 "some value" :VAR2 "another value")
               :program "node_modules/.bin/jest"
               :args dape-jest/find-file-buffer-default
               :outputCapture "console"
               :sourceMapRenames t
               :pauseForSourceMap nil
               :autoAttachChildProcesses t
               :console "internalConsole"
               :outputCapture "std"
               :killBehavior "forceful"))

Embedded jlink debugging with cortex-debug

https://github.com/svaante/dape-cortex-debug