4 Commits
Author SHA1 Message Date
swaits 20dec9bc7b chore: release 0.2.2
Bug-fix release: Python program output now shows in the Console pane, and
breakpoints restore for files opened on the command line.

### Fixed

- **Python `print()` output appears in the Console pane.** The debugpy
  launch config now sets `console = "integratedTerminal"`, so debugpy
  routes stdout/stderr via `RunInTerminalRequest` into dapui's Console
  buffer (which is already registered as
  `dap.defaults.fallback.terminal_win_cmd`). Previously debugpy defaulted
  to `internalConsole` and emitted `OutputEvent`s that nvim-dap forwards
  to the REPL buffer — which turbo-debug's layout deliberately omits, so
  program output vanished. Interactive `input()` now also works.
- **Breakpoints restore for files opened on the command line.** `nvim
  foo.py` fires `BufReadPost` for `foo.py` before turbo-debug's deferred
  `setup()` runs, so `persistent-breakpoints.nvim`'s `BufReadPost`
  autocmd missed the startup buffer and saved breakpoints never came
  back. After setup, turbo-debug now sweeps already-loaded buffers once
  via `load_breakpoints()` (idempotent; placed after `sign_define` so
  the first paint uses turbo-debug's emoji signs instead of dap's
  default `B`).
2026-04-12 21:44:54 -06:00
swaits 5118c03f83 fix: python console output and startup breakpoint restoration
adapters/python: set `console = "integratedTerminal"` so debugpy routes stdout/stderr via RunInTerminalRequest into dapui's Console pane. Under the default (internalConsole), debugpy emits DAP OutputEvents that nvim-dap forwards to the REPL buffer — which turbo-debug's layout deliberately omits, so print() output vanished.

breakpoints: sweep already-loaded buffers with load_breakpoints() right after sign_define. plugin/turbo-debug.lua defers setup via vim.schedule so deps register first, which means BufReadPost has already fired for any file opened on the command line — persistent-breakpoints' autocmd misses it. The one-shot sweep is idempotent; placing it after sign_define avoids a first-paint flicker of dap's default 'B' sign.
2026-04-12 21:35:50 -06:00
swaits 31daa1582c chore: release 0.2.1
Language coverage release. 10 new DAP adapters bring built-in support from
12 to 22 languages, prioritized by language popularity and DAP adapter
maturity.

ADDED
- Dart (with optional Flutter) — auto-detects `flutter` on PATH and adds
  a "Launch Flutter app" configuration alongside the plain Dart file
  launch. Uses `dart debug_adapter` / `flutter debug_adapter`.
- Bash / Sh — `bash-debug-adapter` with automatic `bashdb_dir` probing
  from the mason package path.
- R — `vscDebugger` R-package integration. Preflights the package via
  `Rscript` and silently skips registration if it's missing.
- Elixir — `elixir-ls-debugger` with `mix_task` launch (default task
  `test`). Falls back to the mason package `debugger.sh` if the wrapper
  binary isn't on PATH.
- Haskell — `haskell-debug-adapter`.
- OCaml — `ocamlearlybird`.
- Zig / Nim / Crystal — routed through the existing `codelldb` adapter
  via filetype extension (no new adapter; just added filetypes to the
  existing codelldb configuration).
- F# — routed through the existing `netcoredbg` adapter (same
  `.dll`-launch flow as C#).

INTERNAL
- Extended `tests/test_adapters.lua` to cover the three new modular
  adapter modules (`dart`, `bash`, `r`).
- All new registrations follow the existing `find_executable` +
  `loader.register` pattern and silently skip when the required binary
  isn't found — matches the "no noise when prereqs missing" invariant of
  the previously-shipped adapters.

See CHANGELOG.md for the full 0.2.1 entry.
2026-04-12 20:44:47 -06:00
swaits f8cce8da37 feat: add 10 new language adapters
Adds Dart (with optional Flutter auto-detection), Bash (bash-debug-adapter with mason bashdb_dir probing), R (vscDebugger package preflight), Elixir (mix_task), Haskell, OCaml (ocamlearlybird), Zig/Nim/Crystal (via codelldb filetype extension), and F# (via netcoredbg filetype extension). Expands built-in adapter coverage from 12 to 22 languages.
2026-04-12 20:42:49 -06:00
11 changed files with 317 additions and 13 deletions

No files matched your search

+59
View File
@@ -1,5 +1,64 @@
# Changelog # Changelog
## 0.2.2 — 2026-04-12
Bug-fix release: Python program output now shows in the Console pane, and
breakpoints restore for files opened on the command line.
### Fixed
- **Python `print()` output appears in the Console pane.** The debugpy
launch config now sets `console = "integratedTerminal"`, so debugpy
routes stdout/stderr via `RunInTerminalRequest` into dapui's Console
buffer (which is already registered as
`dap.defaults.fallback.terminal_win_cmd`). Previously debugpy defaulted
to `internalConsole` and emitted `OutputEvent`s that nvim-dap forwards
to the REPL buffer — which turbo-debug's layout deliberately omits, so
program output vanished. Interactive `input()` now also works.
- **Breakpoints restore for files opened on the command line.** `nvim
foo.py` fires `BufReadPost` for `foo.py` before turbo-debug's deferred
`setup()` runs, so `persistent-breakpoints.nvim`'s `BufReadPost`
autocmd missed the startup buffer and saved breakpoints never came
back. After setup, turbo-debug now sweeps already-loaded buffers once
via `load_breakpoints()` (idempotent; placed after `sign_define` so
the first paint uses turbo-debug's emoji signs instead of dap's
default `B`).
## 0.2.1 — 2026-04-12
Language coverage release. 10 new DAP adapters bring built-in support from
12 to 22 languages, prioritized by language popularity and DAP adapter
maturity.
### Added
- **Dart** (with optional **Flutter**) — auto-detects `flutter` on PATH and
adds a "Launch Flutter app" configuration alongside the plain Dart file
launch. Uses `dart debug_adapter` / `flutter debug_adapter`.
- **Bash / Sh** — `bash-debug-adapter` with automatic `bashdb_dir` probing
from the mason package path.
- **R** — `vscDebugger` R-package integration. Preflights the package via
`Rscript` and silently skips registration if it's missing.
- **Elixir** — `elixir-ls-debugger` with `mix_task` launch (default task
`test`). Falls back to the mason package `debugger.sh` if the wrapper
binary isn't on PATH.
- **Haskell** — `haskell-debug-adapter`.
- **OCaml** — `ocamlearlybird`.
- **Zig / Nim / Crystal** — routed through the existing `codelldb` adapter
via filetype extension (no new adapter; just added filetypes to the
existing codelldb configuration).
- **F#** — routed through the existing `netcoredbg` adapter (same
`.dll`-launch flow as C#).
### Internal
- Extended `tests/test_adapters.lua` to cover the three new modular adapter
modules (`dart`, `bash`, `r`).
- All new registrations follow the existing `find_executable` +
`loader.register` pattern and silently skip when the required binary
isn't found — matches the "no noise when prereqs missing" invariant of
the previously-shipped adapters.
## 0.2.0 — 2026-04-12 ## 0.2.0 — 2026-04-12
Major UI rework and stability release. Two floating chrome bars replace the Major UI rework and stability release. Two floating chrome bars replace the
+20 -8
View File
@@ -180,21 +180,33 @@ Both bars:
## Supported Languages ## Supported Languages
turbo-debug ships adapter configs for 12 languages. You install the adapter turbo-debug ships adapter configs for 22 languages. You install the adapter
binary (via [mason.nvim](https://github.com/williamboman/mason.nvim), your binary (via [mason.nvim](https://github.com/williamboman/mason.nvim), your
package manager, or manually) — turbo-debug handles all the configuration. package manager, or manually) — turbo-debug handles all the configuration.
| Language | Adapter | Language | Adapter | | Language | Adapter | Language | Adapter |
|----------|---------|----------|---------| |----------|---------|----------|---------|
| Python | debugpy | C# | netcoredbg | | Python | debugpy | C# | netcoredbg |
| JavaScript / TS | js-debug | Java | java-debug-adapter | | JavaScript / TS | js-debug | F# | netcoredbg |
| Go | delve | Kotlin | kotlin-debug | | Go | delve | Java | java-debug-adapter |
| C / C++ | codelldb | PHP | php-debug | | C / C++ | codelldb | Kotlin | kotlin-debug |
| Rust | codelldb | Ruby | rdbg | | Rust | codelldb | PHP | php-debug |
| Swift | codelldb | Lua (Neovim) | osv | | Swift | codelldb | Ruby | rdbg |
| Zig | codelldb | Elixir | elixir-ls |
| Nim | codelldb | Haskell | haskell-debug-adapter |
| Crystal | codelldb | OCaml | ocamlearlybird |
| Dart / Flutter | dart-debug | R | vscDebugger |
| Bash / Sh | bash-debug-adapter | Lua (Neovim) | osv |
Rust gets special treatment: pressing `c` runs `cargo build` automatically Special treatment:
and picks the binary via `vim.ui.select`. - **Rust**: pressing `c` runs `cargo build` automatically and picks the
binary via `vim.ui.select`.
- **Python**: auto-detects virtualenvs (`VIRTUAL_ENV`, `CONDA_PREFIX`,
`.venv`, `venv`, `env`, `.env`).
- **Dart**: if `flutter` is on PATH, a "Launch Flutter app" configuration
is added alongside the plain Dart file launch.
- **R**: preflights that the `vscDebugger` R package is installed; silently
skips registration if missing.
## What This Plugin Actually Does ## What This Plugin Actually Does
+21 -1
View File
@@ -32,7 +32,7 @@ Features:
- Floating REPL eval (|E|) - Floating REPL eval (|E|)
- Floating status bar + control bar chrome (independent of any statusline - Floating status bar + control bar chrome (independent of any statusline
plugin) plugin)
- Zero-config adapters for 12 languages - Zero-config adapters for 22 languages
- Theme-agnostic colors (every highlight links to a standard group) - Theme-agnostic colors (every highlight links to a standard group)
============================================================================== ==============================================================================
@@ -332,11 +332,22 @@ Built-in adapter support (adapter binary must be installed):
C/C++ codelldb codelldb C/C++ codelldb codelldb
Rust codelldb codelldb Rust codelldb codelldb
Swift codelldb codelldb Swift codelldb codelldb
Zig codelldb codelldb
Nim codelldb codelldb
Crystal codelldb codelldb
C# netcoredbg netcoredbg C# netcoredbg netcoredbg
F# netcoredbg netcoredbg
Java java-debug java-debug-adapter Java java-debug java-debug-adapter
Kotlin kotlin-debug kotlin-debug-adapter Kotlin kotlin-debug kotlin-debug-adapter
PHP php-debug php-debug-adapter PHP php-debug php-debug-adapter
Ruby rdbg rdbg Ruby rdbg rdbg
Dart dart-debug dart debug_adapter
Flutter flutter-debug flutter debug_adapter
Bash / Sh bash-debug bash-debug-adapter
Elixir elixir-ls elixir-ls-debugger
Haskell haskell-debug haskell-debug-adapter
OCaml earlybird ocamlearlybird
R vscDebugger R (with vscDebugger pkg)
Lua (Neovim) osv one-small-step-for-vimkind Lua (Neovim) osv one-small-step-for-vimkind
Path resolution order: Path resolution order:
@@ -350,6 +361,15 @@ and picks the binary via `vim.ui.select` if multiple artifacts exist.
Python auto-detects virtualenvs: `VIRTUAL_ENV`, `CONDA_PREFIX`, then the Python auto-detects virtualenvs: `VIRTUAL_ENV`, `CONDA_PREFIX`, then the
common directories `.venv`, `venv`, `env`, `.env` relative to `cwd`. common directories `.venv`, `venv`, `env`, `.env` relative to `cwd`.
Dart auto-detects Flutter: if `flutter` is on PATH alongside `dart`, a
"Launch Flutter app" configuration is added to the `dart` filetype in
addition to the plain Dart file launch.
R requires the `vscDebugger` R package. turbo-debug preflights it via
`Rscript` and silently skips registration if the package is missing. Install
with `install.packages("vscDebugger")` or
`remotes::install_github("ManuelHentschel/vscDebugger")`.
Adapter binaries are the user's responsibility to install (mason.nvim is Adapter binaries are the user's responsibility to install (mason.nvim is
the easiest route). the easiest route).
+39
View File
@@ -0,0 +1,39 @@
local loader = require("turbo-debug.adapters.init")
local M = {}
function M.register(dap)
local exe = loader.find_executable({ "bash-debug-adapter" })
if not exe then return end
local bashdb_dir = vim.fn.stdpath("data") .. "/mason/packages/bash-debug-adapter/extension/bashdb_dir"
if vim.fn.isdirectory(bashdb_dir) == 0 then bashdb_dir = nil end
loader.register(dap, "bashdb", {
type = "executable",
command = exe,
name = "bashdb",
}, {
{
type = "bashdb",
request = "launch",
name = "Launch file",
showDebugOutput = true,
pathBashdb = bashdb_dir and (bashdb_dir .. "/bashdb") or "bashdb",
pathBashdbLib = bashdb_dir,
trace = true,
file = "${file}",
program = "${file}",
cwd = "${workspaceFolder}",
pathCat = "cat",
pathBash = "/bin/bash",
pathMkfifo = "mkfifo",
pathPkill = "pkill",
args = {},
env = {},
terminalKind = "integrated",
},
}, { "sh", "bash" })
end
return M
+1 -1
View File
@@ -62,7 +62,7 @@ function M.register(dap)
cwd = "${workspaceFolder}", cwd = "${workspaceFolder}",
}, },
} }
for _, ft in ipairs({ "c", "cpp", "swift" }) do for _, ft in ipairs({ "c", "cpp", "swift", "zig", "nim", "crystal" }) do
dap.configurations[ft] = dap.configurations[ft] or prompt_config dap.configurations[ft] = dap.configurations[ft] or prompt_config
end end
+46
View File
@@ -0,0 +1,46 @@
local loader = require("turbo-debug.adapters.init")
local M = {}
function M.register(dap)
local dart = loader.find_executable({ "dart" })
if not dart then return end
local configs = {
{
type = "dart",
request = "launch",
name = "Launch Dart file",
program = "${file}",
cwd = "${workspaceFolder}",
toolArgs = { "--enable-vm-service" },
},
}
local flutter = loader.find_executable({ "flutter" })
if flutter then
if not dap.adapters.flutter then
dap.adapters.flutter = {
type = "executable",
command = flutter,
args = { "debug_adapter" },
}
end
table.insert(configs, {
type = "flutter",
request = "launch",
name = "Launch Flutter app",
program = "lib/main.dart",
cwd = "${workspaceFolder}",
flutterMode = "debug",
})
end
loader.register(dap, "dart", {
type = "executable",
command = dart,
args = { "debug_adapter" },
}, configs, { "dart" })
end
return M
+73 -2
View File
@@ -38,7 +38,7 @@ local function register_simple_adapters(dap)
program = function() return vim.fn.input("Path to DLL: ", vim.fn.getcwd() .. "/bin/Debug/", "file") end, program = function() return vim.fn.input("Path to DLL: ", vim.fn.getcwd() .. "/bin/Debug/", "file") end,
}, },
}, },
{ "cs" }, { "cs", "fsharp" },
}, },
{ {
@@ -119,6 +119,77 @@ local function register_simple_adapters(dap)
}, },
{ "ruby" }, { "ruby" },
}, },
{
"mix_task",
{
"elixir-ls-debugger",
"elixir-ls-debug-adapter",
vim.fn.stdpath("data") .. "/mason/packages/elixir-ls/debugger.sh",
},
{
type = "executable",
},
{
{
type = "mix_task",
request = "launch",
name = "mix test",
task = "test",
taskArgs = { "--trace" },
startApps = true,
projectDir = "${workspaceFolder}",
requireFiles = { "test/**/test_helper.exs", "test/**/*_test.exs" },
},
},
{ "elixir" },
},
{
"haskell",
{ "haskell-debug-adapter" },
{
type = "executable",
},
{
{
type = "haskell",
request = "launch",
name = "Launch file",
workspace = "${workspaceFolder}",
startup = "${file}",
stopOnEntry = true,
logFile = vim.fn.stdpath("data") .. "/haskell-dap.log",
logLevel = "WARNING",
ghciEnv = vim.empty_dict(),
ghciPrompt = "H>>= ",
ghciInitialPrompt = "Prelude>",
ghciCmd = "cabal exec -- ghci-dap --interactive -i -i${workspaceFolder}",
},
},
{ "haskell" },
},
{
"ocamlearlybird",
{ "ocamlearlybird" },
{
type = "executable",
args = { "debug" },
},
{
{
type = "ocamlearlybird",
request = "launch",
name = "Launch bytecode",
program = function()
return vim.fn.input("Path to bytecode: ", vim.fn.getcwd() .. "/_build/default/", "file")
end,
stopOnEntry = false,
},
},
{ "ocaml" },
},
} }
for _, s in ipairs(simple) do for _, s in ipairs(simple) do
@@ -153,7 +224,7 @@ function M.setup()
local dap = require("dap") local dap = require("dap")
-- load adapters with non-trivial logic from separate files -- load adapters with non-trivial logic from separate files
for _, name in ipairs({ "python", "go", "c", "javascript" }) do for _, name in ipairs({ "python", "go", "c", "javascript", "dart", "bash", "r" }) do
local ok, adapter_mod = pcall(require, "turbo-debug.adapters." .. name) local ok, adapter_mod = pcall(require, "turbo-debug.adapters." .. name)
if ok and adapter_mod.register then adapter_mod.register(dap) end if ok and adapter_mod.register then adapter_mod.register(dap) end
end end
+8
View File
@@ -36,6 +36,14 @@ function M.register(dap)
name = "Launch file", name = "Launch file",
program = "${file}", program = "${file}",
pythonPath = function() return find_python() end, pythonPath = function() return find_python() end,
-- integratedTerminal routes the program via RunInTerminalRequest,
-- which nvim-dap spawns in a terminal buffer. dapui registers its
-- Console element as `dap.defaults.fallback.terminal_win_cmd`, so
-- stdout/stderr land in the visible Console pane. Default
-- (internalConsole) sends OutputEvents that nvim-dap routes to the
-- REPL buffer — which turbo-debug's layout deliberately omits, so
-- print() output vanishes. `input()` also works under this mode.
console = "integratedTerminal",
}, },
}, { "python" }) }, { "python" })
end end
+39
View File
@@ -0,0 +1,39 @@
local loader = require("turbo-debug.adapters.init")
local M = {}
-- vscDebugger is an R package, not a standalone binary. It must be installed
-- in the user's R library (install.packages("vscDebugger") or remotes::install_github).
-- Silently skip registration if it's missing — matches the "no noise when
-- prereqs missing" invariant used by the other adapters.
local function has_vsc_debugger()
if vim.fn.executable("Rscript") ~= 1 then return false end
vim.fn.system({
"Rscript",
"-e",
"if (!requireNamespace('vscDebugger', quietly=TRUE)) quit(status=1)",
})
return vim.v.shell_error == 0
end
function M.register(dap)
local R = loader.find_executable({ "R" })
if not R then return end
if not has_vsc_debugger() then return end
loader.register(dap, "r", {
type = "executable",
command = R,
args = { "-e", "vscDebugger::.vsc.listenForDAP()" },
}, {
{
type = "r",
request = "launch",
name = "Launch R file",
program = "${file}",
cwd = "${workspaceFolder}",
},
}, { "r", "rmd" })
end
return M
+10
View File
@@ -61,6 +61,16 @@ function M.setup()
vim.fn.sign_define(s[1], spec) vim.fn.sign_define(s[1], spec)
end end
-- persistent-breakpoints.setup() only installs its BufReadPost autocmd —
-- for files opened from the command line (`nvim foo.py`), BufReadPost has
-- already fired before our deferred plugin/turbo-debug.lua setup runs, so
-- the autocmd misses the startup buffer and saved breakpoints never
-- reappear. load_breakpoints() iterates all listed buffers and is
-- idempotent, so one post-sign_define sweep catches the missed ones
-- without a flicker of dap's default 'B' sign. Placed after sign_define
-- so the restored breakpoints render with our emoji from the first paint.
pcall(require("persistent-breakpoints.api").load_breakpoints)
-- unobtrusive inline virtual text: italic, comment-colored -- unobtrusive inline virtual text: italic, comment-colored
vim.api.nvim_set_hl(0, "NvimDapVirtualText", { default = true, link = "Comment", italic = true }) vim.api.nvim_set_hl(0, "NvimDapVirtualText", { default = true, link = "Comment", italic = true })
vim.api.nvim_set_hl(0, "NvimDapVirtualTextChanged", { default = true, link = "DiagnosticWarn", italic = true }) vim.api.nvim_set_hl(0, "NvimDapVirtualTextChanged", { default = true, link = "DiagnosticWarn", italic = true })
+1 -1
View File
@@ -9,7 +9,7 @@ assert_eq(loader.find_executable({ "totally-fake-binary-xyz" }), nil, "non-exist
assert_eq(loader.find_executable({ "sh" }) ~= nil, true, "find_executable finds sh") assert_eq(loader.find_executable({ "sh" }) ~= nil, true, "find_executable finds sh")
-- all adapter modules load and have register() -- all adapter modules load and have register()
for _, name in ipairs({ "python", "go", "c", "javascript" }) do for _, name in ipairs({ "python", "go", "c", "javascript", "dart", "bash", "r" }) do
assert_eq(type(require("turbo-debug.adapters." .. name).register), "function", name .. " has register()") assert_eq(type(require("turbo-debug.adapters." .. name).register), "function", name .. " has register()")
end end