dbl
Debug launcher. Pronounced "tubel"
Type dbl <command> in your editor's terminal and it launches that command in a debug session. Works with Neovim and VSCode.
dbl ./build/app --flag value
dbl bazel run --config=dbg //pkg:target -- --arg
dbl bazel test --config=dbg //pkg:test -- --arg
dbl cargo run -- --arg
dbl cargo test --test my_test -- --arg
dbl cmake run -B build -DCMAKE_BUILD_TYPE=Debug --target myapp -- --arg
dbl cmake test -B build -DCMAKE_BUILD_TYPE=Debug --target mytest -- --arg
dbl ctest --test-dir build -R mytest -- --arg
dbl python script.py arg
dbl --debugger=codelldb ./app # override adapter detection
Prerequisites
- Bazel (for building dbl from source)
- One of:
Install
CLI
Homebrew (macOS / Linux)
brew install dav1d-wright/dbl/dbl
From source (Cargo)
cargo install --path cli
From source (Bazel)
bazel run //cli:install
This builds the CLI binary and copies it to ~/.local/bin/dbl. Ensure ~/.local/bin is on your PATH.
Neovim plugin
Install this repo as a Neovim plugin. The lua/ and plugin/ directories are at the repo root, so any plugin manager works directly.
With lazy.nvim (GitLab):
{ url = "https://gitlab.com/david_wright/dbl.git", config = function() require("dbl").setup({}) end }
GitHub mirror:
{ "dav1d-wright/dbl", config = function() require("dbl").setup({}) end }
VSCode extension
Build and install the extension from the vscode/ directory:
cd vscode
npm ci && npm run build
npx vsce package --no-dependencies
code --install-extension dbl-0.1.0.vsix
The extension automatically sets $DBL in all integrated terminals. No additional configuration is needed for the default adapter mappings.
Override adapters or enable stop-on-entry in VSCode settings:
{
"dbl.adapters": { "c": "cppdbg" },
"dbl.stopOnEntry": false
}
Adapter configuration
dbl detects the language of your program and maps it to a DAP adapter. The defaults cover common setups:
| Language |
Adapter |
| C, C++, Rust, Zig, Objective-C, native binaries |
codelldb |
| Python |
debugpy |
| Go |
delve |
Override or extend mappings in setup():
require("dbl").setup({
adapters = {
c = "cppdbg", -- use cppdbg instead of codelldb for C
java = "java-dbg", -- add a new language
},
})
The --debugger flag bypasses detection entirely:
dbl --debugger=lldb-dap ./myapp
Usage
Run dbl from your editor's integrated terminal. In Neovim, you can also use the :Dbl command:
:Dbl bazel run --config=dbg //pkg:target
:Dbl runs dbl in the background and reports errors via vim.notify. It accepts the same arguments as the CLI.
Plain binaries
dbl ./build/myapp --flag value
Resolves the binary to an absolute path and passes remaining arguments through.
Bazel targets
dbl bazel run --config=dbg //pkg:target -- --user-arg
dbl bazel test --config=dbg //pkg:test -- --user-arg
Replaces run/test with build, forwards your flags to bazel cquery and bazel build, and resolves the binary. Arguments after -- are passed to the debugged program, not to bazel. Pass --config=dbg or --compilation_mode=dbg to build with debug symbols.
bazel test targets get the test environment variables that bazel's test runner normally sets: TEST_TMPDIR, TEST_SRCDIR, BAZEL_TEST, TEST_TARGET, and HOME. This allows test binaries that depend on runfiles or temp directories to work correctly under the debugger.
Cargo targets
dbl cargo run -- --user-arg
dbl cargo test --test my_test -- --user-arg
Replaces run/test with build, forwards your flags to cargo build, and resolves the binary using --message-format=json. Arguments after -- are passed to the debugged program, not to cargo.
CMake targets
dbl cmake run -B build -DCMAKE_BUILD_TYPE=Debug --target myapp -- --user-arg
dbl cmake test -B build -DCMAKE_BUILD_TYPE=Debug --target mytest -- --user-arg
cmake run and cmake test are dbl subcommands (cmake has no native run/test for individual targets). Uses cmake's own flags: -B sets the build directory (defaults to cwd), --target/-t selects the target. -D flags are forwarded to cmake configure; all other flags are forwarded to cmake --build. Arguments after -- are passed to the debugged program. Uses the CMake File API to discover the artifact path. Auto-configures if the build directory is not set up.
CTest
dbl ctest --test-dir build -R mytest -- --user-arg
All standard ctest filtering flags work: -R (regex match), -E (regex exclude), -L/-LE (label match/exclude), -C (configuration), -I (range). Requires exactly one test match. Arguments after -- are appended to the test command.
Python
dbl python3 script.py --arg
Resolves the interpreter path and keeps the script and args separate. debugpy receives the correct interpreter path.
Options
| Flag |
Description |
--debugger=<name> |
Override adapter detection. Uses the given adapter name directly. |
Examples
The tests/projects/ directory contains small programs you can debug with dbl. Open nvim, set breakpoints in the test projects, then run from :terminal or via :Dbl:
# C (Bazel)
dbl bazel run --config=dbg //tests/projects/bazel_app:app -- arg1
dbl bazel test --config=dbg //tests/projects/bazel_app:app_test
# Rust (Bazel)
dbl bazel run --config=dbg //tests/projects/bazel_rs_app:app
dbl bazel test --config=dbg //tests/projects/bazel_rs_app:app_test
# Python (Bazel)
dbl bazel run //tests/projects/bazel_py_app:app
dbl bazel test //tests/projects/bazel_py_app:app_test
# Rust (Cargo)
cd tests/projects/cargo_app
dbl cargo run
dbl cargo test --test cargo_app_test
# Python (direct)
dbl python3 tests/projects/python_app/app.py
# C (CMake)
dbl cmake run -B tests/projects/cmake_app/build \
-S tests/projects/cmake_app -DCMAKE_BUILD_TYPE=Debug --target cmake_app
dbl cmake test -B tests/projects/cmake_app/build \
-S tests/projects/cmake_app -DCMAKE_BUILD_TYPE=Debug --target cmake_app_test
Or equivalently from nvim's command line:
:Dbl bazel run --config=dbg //tests/projects/bazel_app:app -- arg1
:Dbl python3 tests/projects/python_app/app.py
Dogfooding
Debug dbl with dbl. From nvim's :terminal:
dbl bazel run --config=dbg //cli:dbl -- --help
This builds the CLI with debug symbols and launches a codelldb debug session. Set breakpoints in the Rust source before running.
Roadmap
- [ ] Environment variable forwarding.
FOO=bar dbl ./app currently assigns FOO=bar to the dbl process, not the debugged program. A --env flag or -- separator for env vars would fix this.
- [ ] Custom tool paths. dbl finds
cmake, bazel, python/python3 via $PATH. A --cmake= / --bazel= flag would allow overriding.
- [ ] Execution-phase flag passthrough. dbl replaces
run/test with build, so flags that affect execution (e.g. bazel's --run_under) are silently ignored.
- [ ] Multi-target debugging. Each invocation debugs one program. Wildcard patterns like
//pkg/... or //pkg:all are rejected.
Build and test
bazel build //cli:dbl # build the CLI binary
bazel test //... # run all tests (CLI, nvim plugin, integration)
cd vscode && npm test # run VSCode extension tests
Design
terminal Editor
┌──────────┐ dbl/launch ┌────────────┐
│ dbl CLI │ ──────────────────► │ dbl plugin │ ──► debug session
│ (Rust) │ via $DBL socket │ │
└──────────┘ └────────────┘
The CLI sends a JSON launch request to the editor plugin over a Unix socket ($DBL) using msgpack-rpc with method dbl/launch. The plugin handles adapter selection, validation, and launching. The protocol is editor-agnostic; each editor plugin runs its own socket server.
The CLI resolves a freeform shell command into a structured launch request in three stages:
Launch resolution. Determines how to obtain the program binary. Dispatches by command shape: bazel targets are built via cquery + build, cmake targets use the CMake File API, ctest uses --show-only=json-v1, python commands resolve the interpreter path, and plain commands resolve via canonicalize.
Kind detection. Analyzes the resolved file to determine its language using content analysis (hyperpolyglot, a Rust port of GitHub Linguist) and binary section scanning (goblin) for compiled languages like Go.
Request assembly. Combines the resolved program, detected kind, and optional --debugger override into a launch request sent to the plugin.
Repo layout
MODULE.bazel Bazel module definition
cli/ Rust CLI binary
src/
main.rs entry point, arg parsing
resolve.rs command resolution, kind detection
rpc.rs editor RPC client (msgpack)
lua/dbl/ Neovim plugin (at repo root for plugin managers)
init.lua setup(), socket server, launch(), adapter mapping
config.lua defaults and user config
plugin/dbl.lua :Dbl command registration
vscode/ VSCode extension
src/
extension.ts activate/deactivate, $DBL injection
server.ts Unix socket server, msgpack-rpc handling
launch.ts LaunchRequest to DebugConfiguration mapping
tests/ unit, plugin, and integration tests
License
MIT. See LICENSE.