There have been some recent updates to the SWI-Prolog DAP server implementation.
The latest version is available for installation by running:
Title: Debug Adapter Protocol package for SWI-Prolog
Installed version: 0.2.13
Notable changes include:
autotools dependency to ease packaging and installation
- Fix issue with debugging non-module source files
next request to skip over a goal
stepOut request to skip to the end of a goal
Here’s an example of using
debug_adapter to step through code in various directions:
Please let me know if you hit any issues stepping around you’re code. SWI-Prolog is complicated and I’d like to collect some test cases
Comments and requests are welcome. There are still several desirable features to be implemented, specifically I intend to tackle breakpoints of all sorts next.
Is this simply orthogonal to the LSP (Language Server Protocol) as done my @jamesnvc ?
The stuff missing shouldn’t be too hard to add. Just ask if you can’t figure out how to perform the required low-level stuff quickly.
Yes, the way I see it LSP and DAP serve different and complementary goals - an LSP server is in charge of exposing semantic analysis of a language (Prolog) to the IDE, while the DAP server is in charge of facilitating debugging code.
So a DAP server essentially handles runtime notions where LSP may rely on static analysis for most of its operations.
And thanks you! I’ll definitely ask for help if/when I get stuck. Up to this point
xpce source code was a great resource.
Note that wrong behavior may well not be your mistake. Verify how the built-in debugger works first. Notably unifications immediately following the head, the => rules and $ are not all correctly handled by the debugger.
can you explain this a bit more – and give an example of a semantic analysis – if possible.
Basic examples would include finding references to some defined predicate and deciding whether a certain term in a body of a clause written in a source file represents “code” or “data”, and providing these insights to the IDE.
LSP is described here Official page for Language Server Protocol and DAP here Official page for Debug Adapter Protocol
Added support for basic breakpoints functionality in ADD: Initial support for breakpoints with `setBreakpoints` request · eshelyaron/debug_adapter@b7e48c5 · GitHub.
Here’s an example of setting a breakpoint on the fly and using
(c)ontinue from inside
test_noun1/2 to run until backtracking and hitting the breakpoint inside
Now, DAP also defines conditional breakpoints where the user submits a piece of code to act as a condition for entering the tracer while setting a breakpoint. This feature should be straightforward to support with
break_hook/6 (in fact this is the first use case listed in this hook’s docs), and
dap-mode implements support for it on the client-side for Emacs (certainly also VSCode has it, but I haven’t checked), so I think it can be a nice addition in the next version, although I’m not sure how often it’ll actually come useful.
re: conditional breakpoints
I use it often when i debug more complex use cases that have to generate all kinds of structures first, before the one is reached that requires debugging.
Usually, i then place an (Arg = ‘known value’ → trace ; true) into the code to conditionally start the debugger exactly there …
Conditional breakpoints are now available in version 0.4.5 of the
debug_adapter, along with DAP “log points” which are a similar feature to the familiar trace points SWI-Prolog provides via
Here’s an illustration:
Each breakpoint can be associated with a condition, which is a Prolog goal that will be executed in the context of the frame where the breakpoint was hit, including variables bindings.
Also visible in the above screenshot is the “Hit Condition” feature, which is a simple skip count that can be set per breakpoint using e.g.
M-x dap-breakpoint-hit-condition with
dap-mode - requesting that the breakpoint will be skipped for the specified number of visits periodically, so in the example above the first breakpoint only fires every other time.
Lastly, one can also turn a breakpoint into a logpoint (with e.g.
M-x dap-breakpoint-log-message), causing the specified message to be logged whenever the breakpoint is hit, instead of breaking the program run, as exemplified with the
print me! message printed in the above example.
Logpoints also respect breakpoint conditions and hit conditions, so the message is only logged when
X = 2 in the screenshot.