Metals supports the DAP Protocol which allows to debug Scala code in Emacs. By default all the setup is done to use code lenses to start an instance of the program to debug it, set breakpoints inspect variables, all the good stuff to expect from a debugger. It is not as obvious how to debug a remote Scala process however as this requires a bit of setup.
Setting up the Scala process
Scala, as running on the JVM, allows to be run with the well know flags to be debugged namely
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
When using sbt this can be simplified to running as
sbt -jvm-debug 5005
which launches an sbt shell exposing a debug port 5005 on localhost. It is good practice to always use localhost and if truly remote to use SSH port forwarding, as the debugger has no authentication or any security in place.
Attaching the debugger
In dap-mode in Emacs uses debug-templates to determine what to run, in the case of scala and metals we need to provide the connection information as well as the lsp-project to use as the source for the code to set breakpoints and in general browse the code. The project name to use can normally be found in the build.sbt file this means for a project like the one associated with the FP Tower Foundations Course the setup looks like
(dap-register-debug-template
"Scala Attach Foundations (localhost:5005)"
'(:type "scala"
:request "attach"
:name "Scala Attach Foundations (localhost:5005)"
:hostName "localhost"
:port 5005
:targets [(:uri "file:///Users/pfehre/source/foundations?id=foundation")]))
important is the targets uri in this case as the default template for scala attach sets the project name to root resulting in the invalid target file:///Users/pfehre/source/foundations?id=root. With this setup and evaluated the commad dap-debug will now contain a Scala Attach Foundations (localhost:5005) target to use to attach to the process.
As this is project specific setup it makes sense to add it to the .dir-locals.el file so it automatically gets registered when visiting the project folder.
((nil . ((eval . (dap-register-debug-template
"Scala Attach Foundations (localhost:5005)"
'(:type "scala"
:request "attach"
:name "Scala Attach Foundations (localhost:5005)"
:hostName "localhost"
:port 5005
:targets [(:uri "file:///Users/pfehre/source/foundations?id=foundation")]))))))
would make this work.