A standalone graphical DAP debugger with Neovim integration.
About · Screenshots · Quickstart · Install · Build
English · Русский
Bugsaur is a graphical debugger that runs on its own. It is language-agnostic: it talks to debug adapters over the Debug Adapter Protocol, and Neovim acts as a source frontend rather than as the debugger.
The architectural invariant: Neovim does not own the debug session. Bugsaur is the only DAP client and the authoritative owner of state. It starts and works without Neovim, and Neovim is a replaceable frontend for showing source code.
| Neovim — source frontend | Bugsaur core | Bugsaur GUI |
|---|---|---|
| Code editing | DAP session | Variables |
| Source navigation | Execution control | Threads |
| Breakpoint UX | Runtime state | Call stack |
| Watches, Evaluate |
Breakpoints survive an editor restart: they live in
<root>/.bugsaur/breakpoints.json and are visible both to the terminal debugger
and to Neovim.
Languages today: PHP, Go, Rust, Python. A TOML catalog extends or overrides the built-in adapter catalog, so anything with a DAP adapter can be added.
PHP is the one language Bugsaur does not delegate. It ships its own native
DBGp adapter, php-dbgp-adapter, built from this workspace. It replaces the
usual DAP↔DBGp bridge and depends on no Node.js, npm, VS Code or
vscode-php-debug — at runtime only bugsaur, php-dbgp-adapter and Xdebug
are involved. See ADR-008.
Bugsaur is available in both light and dark themes. The two variants are shown side by side below.
| Light theme | Dark theme |
![]() |
![]() |
# 1. Build the debugger
make build
# 2. Make sure the adapter for your language is on PATH
command -v codelldb # Rust, C, C++
command -v dlv # Go
python3 -m debugpy --version # Python (usually in the project venv)
# 3. In your project: describe what to debug
target/debug/bugsaur init # writes <project>/.bugsaur/config.toml
# 4. Debug it
target/debug/bugsaur gui --project /path/to/project --break src/main.rs:15
target/debug/bugsaur gui --project /path/to/project --test-at tests/test_app.py:12bugsaur init detects the language by Cargo.toml, go.mod, composer.json
or pyproject.toml and writes a config you then edit by hand:
version = 1
default = "app"
[profiles.app]
adapter = "codelldb"
program = "target/debug/app"The config is read by the debugger, not by the editor, so bugsaur from a
terminal and :DebugStart from Neovim see exactly the same setup. The project
root is the nearest directory up the tree that contains .bugsaur/. Pick a
profile with :DebugStart <name>, otherwise default is used.
Relative paths resolve from the project root, and so does the debuggee's working
directory unless a profile sets cwd. Environment comes from env_file and
env; arguments from args.
Full format, resolution rules and worked examples for every language: docs/CONFIG.md. See also docs/NEOVIM.md §3 and docs/SPEC.md §38.1.
| Rust | only for a source build: the toolchain pinned in rust-toolchain.toml (1.92), installed with rustup |
| Platform | macOS or Linux |
| Adapter | at least one: codelldb (Rust, C, C++), dlv (Go), debugpy (Python — a pip install debugpy away), or the bundled php-dbgp-adapter (PHP) |
| Neovim | optional — only for the source frontend |
Adapters are resolved through PATH, so they must be visible to the debugger.
If yours came from Mason:
export PATH="$HOME/.local/share/nvim/mason/bin:$PATH"Download the archive for your platform from
GitHub Releases, extract it,
and put bugsaur and the bundled php-dbgp-adapter on your PATH. Archives
are published for Linux x86-64, macOS Intel and macOS Apple Silicon, and also
contain the optional Neovim frontend under lua/.
To build from source instead:
git clone https://github.com/real420og/bugsaur.git
cd bugsaur
make buildThe binary lands in target/debug/bugsaur. Copy or symlink it into a directory
on your PATH to call it as bugsaur.
The adapter is a Python module, so it is installed into the interpreter that runs your code:
python3 -m pip install debugpy # or: .venv/bin/pip install debugpybugsaur init looks for .venv/bin/python and venv/bin/python in the project
root. When it finds one, the generated profile points both the adapter and the
debuggee at that interpreter:
[profiles.main]
adapter = "debugpy"
adapter_command = "${root}/.venv/bin/python"
program = "main.py"
cwd = "."
[profiles.main.launch_arguments]
python = "${root}/.venv/bin/python"Without a virtualenv both fall back to python3 from PATH, which then needs
debugpy installed. Entry points are found by name — main.py, app.py,
manage.py, __main__.py in the root and src/<pkg>/__main__.py — one profile
each; anything else you write by hand.
PHP needs no external adapter: release archives include php-dbgp-adapter, and
the same make build produces it next to bugsaur. Put their directory on
PATH so the catalog can find it:
export PATH="$PWD:$PATH" # extracted release archive
export PATH="$PWD/target/debug:$PATH" # source buildPoint a profile at it and describe the Xdebug side:
[profiles.docker]
adapter = "php"
program = "."
[profiles.docker.launch_arguments]
port = 9003
sessionMode = "server" # "cli" for a one-shot script
pathMappings = { "/app" = "${root}" } # path in the container -> host pathIn the PHP environment itself:
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003server is for an already running PHP-FPM, built-in server or web container;
cli is for a script that starts and exits. Session modes, running two PHP
projects side by side and the Docker fixture: docs/NEOVIM.md §7.
The Lua source frontend lives in lua/bugsaur/init.lua and connects to the
backend over a Unix socket. With lazy.nvim:
{
dir = "/absolute/path/to/bugsaur",
name = "bugsaur",
config = function()
require("bugsaur").setup({
binary = "/absolute/path/to/bugsaur/target/debug/bugsaur",
gui = true,
})
end,
}setup() only carries editor and window options — adapters, programs and launch
arguments live in the project config. Then add a breakpoint with
:ToggleBreakpoint and run :DebugStart. Full command and keymap reference:
docs/NEOVIM.md.
Build through make, not through a bare cargo: the Makefile resolves the
toolchain directory from rust-toolchain.toml and puts it first on PATH, so a
stray older cargo cannot change the result.
| Command | What it does |
|---|---|
make build |
build the workspace and bugsaur |
make check |
type-check the workspace |
make test |
run the offline test suite |
make test-one |
run one target: PKG=<crate> [TEST=<file>] [NAME=<filter>] |
make fmt |
check formatting |
make clippy |
clippy with -D warnings |
make gui |
build the Rust fixture and launch the GUI |
make help |
list every target |
Live adapter tests, acceptance scripts and the development process: docs/DEVELOPMENT.md.
The full documentation lives at real420og.github.io/bugsaur — also available in Russian.
| Getting Started | install it, run it, hit your first breakpoint |
| Debugger UI | every panel of the window, explained |
| Languages | Rust, C/C++, Go, Python, PHP |
| Neovim | commands, keymaps, breakpoint sync |
| Configuration | profiles, environment, path mappings, UI |
| Guides | cargo test, pytest, PHP in Docker, and more |
| Troubleshooting | when it does not work |
| Reference | CLI, config keys, keyboard shortcuts |
Working on Bugsaur itself: Development · docs/SPEC.md · docs/ROADMAP.md · docs/MIGRATION.md · docs/adr/

