svaante / dape

Debug Adapter Protocol for Emacs
GNU General Public License v3.0
433 stars 24 forks source link

Customizable API for setting debug target #31

Open mohkale opened 7 months ago

mohkale commented 7 months ago

Hi there,

Thanks for this wonderful package. I've been wanting a DAP client in Emacs that isn't lsp-mode dependent :sunglasses:.

One other thing I've been wanting for a while is a way to debug individual build targets easily. For example in a C++ project using CMake you can setup multiple test binaries as possible targets and run them through CMake directly. I'm the maintainer of an Emacs package called projection and because projection interfaces with CMake I can expose something to read available test targets from it.

Currently it looks like dap just asks you which executable you want to debug with find-file. What are your thoughts about a more customizable interface for this that can plug in build system related metadata my projection package can provide.

Originally I was thinking a hook and I can just prefix a projection-executable-target command to it and you keep calling functions in the hook until it returns a non-nil variable. But I'm not sure how flexible that would be with the different requirements of each debugger. go-delve for example seems to accept a package argument instead of an executable. Maybe the hooks should be tailored based on the kind of target the dap client requires so it should be an alist :thinking:. What are youur thoughts?

svaante commented 7 months ago

Sounds interesting!

I don't really grok, maybe I am a bit slow.

Could you help me out with what your dream API would look like!

Is it something like(defun dape-start-debug (language binary) ...)?

mohkale commented 6 months ago

Hi there,

Sorry for taking so long to get back to you. I wanted to experiment a bit and then propose something. I've added a new projection-artifacts-read-debug-targets function which can source available programs for debugging from (currently just) a projects go modules or a CMake projects CMake and CTest metadata.

Screenshot_20231231_153222

Screenshot_20231231_153302

Now by default this function just matches all possible project types for the current project, queries debug targets and then gives you back an alist of properties for that target. For a CTest target it gives back:

((name . "projection-demo-test")
 (category . "CTest")
 (arg0 .
       "/home/mohkale/Develop/repos/projection-demo/build/tests/Debug/projection-demo-test")
 (argv "foobar")
 (type . executable)
 (debuggable . t)
 (working-directory . "/home/mohkale/Develop/repos/projection-demo/build/tests")
 (environment ("FOO" . "1")))

For a golang project it gives back:

((name . "cmd/guru")
 (type . go-package)
 (category . "Go package")
 (go-package . "golang.org/x/tools/cmd/guru")
 (debuggable . t))

Some of these properties are just internal but the common theme is a name and a type. Then based on that type different options for the target which I think you could map to an appropriate debugger and then invoke. What do you think about this? A function which returns an alist is as simple as it gets and you can call this, use the type to narrow down a series of debuggers, then if there's multiple interactively ask the user to select one of those as well. So rather than going from debugger -> debug target, we go from debug target -> debugger. I think that would be a nicer interface than asking for a debugger and then asking what to debug because it presents you all possible targets upfront. Thoughts?

mohkale commented 6 months ago

I've written a rudimentary projection-dape+ wrapper command that reads a debug target of the form I've defined and then converts the alist into the plist style format dape expects. I'm having some issues figuring out how to the interpret the environment-variable setting but everything else seems to work as expected. I can load a project, compile, start a debugger for a specific target, then set breakpoints from my editor. Kick ass :partying_face:.

(use-package projection
  :commands projection-dape+
  :config
  (defun projection-dape--run-debugger+ (config)
    (thread-last
      (thread-first
        (let-alist config
          ;; TODO: Support .environment property for debug invocation.
          (pcase .type
            ('executable
             `(lldb-vscode ,@(when .working-directory
                               (list :cwd .working-directory))
                           :program ,.arg0
                           ,@(when .argv
                               (list :command-args (apply #'vector .argv)))))
            ('go-package `(dlv :program .go-package))
            (_ (error "Don't know how to derive debugger config from %S" config))))
        (prin1-to-string)
        (substring 1 -1)
        (dape--config-from-string))
      (apply 'dape--config-eval)
      (dape)))

  (defun projection-dape+ (debug-target)
    (interactive (list (projection-artifacts-read-debug-targets)))
    (projection-dape--run-debugger+ debug-target))

  :leader
  ("lv" 'projection-dape+
   "vv" 'projection-dape+))

I'll refine this a little more and maybe turn into an extension package for projection itself.

svaante commented 6 months ago

Looks pretty sweet. No need tho to convert from config to string (if not something has changed on master that I can't remember). Maybe it would be good to mark `dape-config-eval' as an stable api.

(defun projection-dape--run-debugger+ (config)
  (let-alist config
    ;; TODO: Support .environment property for debug invocation.
    (dape (pcase .type
            ('executable
             (dape--config-eval 'lldb-vscode
                                (append (list :program .arg0)
                                        (when .working-directory
                                          (list :cwd .working-directory))
                                        (when .argv
                                          (list :args (apply #'vector .argv))))))
            (_ (error "Don't know how to derive debugger config from %S" config))))))

(projection-dape--run-debugger+ '((type . executable)
                                  (arg0 . "/Users/dpettersson/Workspace/c_test/a.out")))

For both go and lldb environment is passed in as an plist (:MY_ENV_1 "MY_ENV_VAL_1")etc.

1925381584 commented 5 months ago

Hi,there i like this package too. Thanks for your job. I think target -> debugger. is better than debugger->target when debug Scripts like Python. I hope there is an option that can achieve debugging from the current file by default, similar to vscode

svaante commented 5 months ago

@1925381584 I am not sure I follow, the default configuration is to debug the current buffer and I don't understand what target -> debugger means? Are you referencing that you need to write debugpy :program "main.py" instead of :program "main.py" debugpy?

1925381584 commented 5 months ago

I mean when I open a file like 1.py, then I call dape, Just start debugging 1.py directly, instead of going to the run adapter panel