From c7e9fb799ddd1168d525ec95ed5ef4a19d855b51 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Sun, 12 Apr 2026 14:18:28 -0600 Subject: [PATCH] fix: adapter timeouts + bars now span full editor width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regression fixes from the previous commit: 1. Debug adapter timing out / general slowness — root cause was calling dapui.setup() on every M.enter() to "refresh layouts". setup is heavy (tears down windows, re-registers listeners, rebuilds state) and running it while the adapter handshake is in flight caused "Debug adapter didn't respond" timeouts. Reverted to one-time setup; layouts are computed once per nvim session. Trade-off: Console/ sidebar proportions may drift slightly if the terminal is resized between toggles, but that's vastly preferable to a broken adapter. 2. Console broken / unreachable — clear_console was calling buf_set_lines on a terminal buffer after dapui had made it a terminal, which leaves the terminal in an inconsistent state. Kept the config option but disabled by default (clear_console_on_start = false). Users who want it can opt in; the feature remains risky on terminal-backed Console buffers. Better design pending. 3. Bars not spanning full editor width — :topleft split and :botright split create full-width splits only when no competing vertical splits exist. dapui's sidebar creates a full-height vertical split AFTER our bars, which squeezes the bars into the non-sidebar column. Added force_bars_full_width() which runs `wincmd K` (on sbar) and `wincmd J` (on cbar) AFTER dapui.open(). These commands re-layout the window tree with the bar window spanning full editor width, pushing sidebar + source + tray into the middle rows. Co-Authored-By: Claude Opus 4.6 (1M context) --- lua/turbo-debug/config.lua | 9 ++- lua/turbo-debug/mode.lua | 113 ++++++++++++++++++++++++------------- 2 files changed, 81 insertions(+), 41 deletions(-) diff --git a/lua/turbo-debug/config.lua b/lua/turbo-debug/config.lua index d08f80e..2dcb66e 100644 --- a/lua/turbo-debug/config.lua +++ b/lua/turbo-debug/config.lua @@ -30,9 +30,12 @@ M.defaults = { -- it. Users can click/ to expand manually. collapsed_scopes = { "Registers" }, - -- Clear the Console pane on session start/restart so old output from a - -- previous run doesn't linger. Set false to preserve across runs. - clear_console_on_start = true, + -- Clear the Console pane on session start/restart. Disabled by default + -- because safe clearing is tricky: dapui's console buffer becomes a + -- terminal buffer once a session starts, and manipulating terminal + -- buffers mid-session destabilizes the adapter. Opt in explicitly if + -- you want it and accept the risk. + clear_console_on_start = false, -- modal keys (active only during debug mode, single-key) keys = { diff --git a/lua/turbo-debug/mode.lua b/lua/turbo-debug/mode.lua index 554bc12..2f5a471 100644 --- a/lua/turbo-debug/mode.lua +++ b/lua/turbo-debug/mode.lua @@ -472,8 +472,10 @@ end local function open_bars() -- Both bars are SPLITS, not floats. Splits consume real layout rows - -- so dapui and source windows tuck underneath them rather than - -- getting overlaid. The caller's original focus is preserved. + -- so dapui windows tuck between them. `wincmd K` / `wincmd J` is + -- applied after creation to ensure the bars span the FULL editor + -- width (across the sidebar column too) — otherwise dapui's later + -- vertical splits would constrain the bars to just the main column. local caller_win = vim.api.nvim_get_current_win() -- status bar (top) @@ -491,7 +493,6 @@ local function open_bars() bounce_out(sbar_buf) end - -- back to caller before creating bottom so topology is predictable if vim.api.nvim_win_is_valid(caller_win) then pcall(vim.api.nvim_set_current_win, caller_win) end @@ -511,7 +512,6 @@ local function open_bars() bounce_out(cbar_buf) end - -- restore caller focus if vim.api.nvim_win_is_valid(caller_win) then pcall(vim.api.nvim_set_current_win, caller_win) end @@ -519,6 +519,29 @@ local function open_bars() render_bars() end +-- After dapui has created its vertical splits (sidebar etc.), the bars +-- may be constrained to the non-sidebar column. `wincmd K` / `wincmd J` +-- re-layout the window tree with the bar window spanning full editor +-- width at the top / bottom respectively, pushing everything else into +-- the middle rows. +local function force_bars_full_width() + local caller_win = vim.api.nvim_get_current_win() + if sbar_win and vim.api.nvim_win_is_valid(sbar_win) then + pcall(vim.api.nvim_set_current_win, sbar_win) + pcall(vim.cmd, "wincmd K") + pcall(vim.api.nvim_win_set_height, sbar_win, 2) + end + if cbar_win and vim.api.nvim_win_is_valid(cbar_win) then + pcall(vim.api.nvim_set_current_win, cbar_win) + pcall(vim.cmd, "wincmd J") + pcall(vim.api.nvim_win_set_height, cbar_win, 2) + end + if vim.api.nvim_win_is_valid(caller_win) then + pcall(vim.api.nvim_set_current_win, caller_win) + end + render_bars() +end + local function close_bars() if sbar_win and vim.api.nvim_win_is_valid(sbar_win) then pcall(vim.api.nvim_win_close, sbar_win, true) @@ -595,26 +618,20 @@ local function schedule_console_redraw() end, config.opts.console_refresh_ms or 50) end --- Call dapui.setup each M.enter() so the layout is recomputed against --- current terminal dimensions AND against whatever splits our bars have --- pinned at top and bottom. Otherwise dapui caches first-call sizes and --- re-entering debug mode lays out against stale dimensions (Console --- ballooning, sidebar proportions drifting away from 25% each — the --- exact bug the user reported on second toggle). -local function refresh_dapui_layouts() +local function ensure_dapui() + -- One-time setup. Calling dapui.setup on every enter was tearing down + -- internal state (windows, event listeners, buffer refs) while the + -- adapter handshake was in flight — "Debug adapter didn't respond" + -- timeouts and overall slowness. dapui's layouts use the absolute + -- sizes computed once here; if the terminal resizes significantly + -- between toggles, proportions can drift slightly, but that's + -- acceptable compared to breaking the adapter. + if dapui_initialized then return end + dapui_initialized = true local dapui_opts = vim.deepcopy(config.opts.dapui) if not dapui_opts.layouts then dapui_opts.layouts = build_default_layouts() end require("dapui").setup(dapui_opts) patch_format_value() -end - -local function ensure_dapui() - if dapui_initialized then - refresh_dapui_layouts() - return - end - dapui_initialized = true - refresh_dapui_layouts() local dap = require("dap") dap.listeners.after.event_initialized["turbo-debug"] = function() @@ -698,22 +715,42 @@ local function ensure_dapui() dap.listeners.before.event_terminated["turbo-debug-ip"] = function() clear_ip() end dap.listeners.before.event_exited["turbo-debug-ip"] = function() clear_ip() end - -- Clear the Console pane on session start/restart. Runs BEFORE the - -- adapter emits output so the new session's log starts clean. + -- Clear the Console pane on session start/restart. + -- Strategy: delete the old console buffer and let dapui's own get_buf() + -- create a fresh one on next reference. Editing a terminal buffer in + -- place via nvim_buf_set_lines was breaking the terminal state and + -- making the Console window unresponsive. Swapping buffers is clean. + -- We also find any window still showing the old buffer and rehook it + -- to the new one so the user actually sees output in the pane. local function clear_console() if not config.opts.clear_console_on_start then return end local ok, dapui = pcall(require, "dapui") - if not ok then return end - local buf_ok, buf = pcall(function() return dapui.elements.console.buffer() end) - if not buf_ok or not buf or not vim.api.nvim_buf_is_valid(buf) then return end - pcall(function() - local prev = vim.bo[buf].modifiable - vim.bo[buf].modifiable = true - vim.api.nvim_buf_set_lines(buf, 0, -1, false, {}) - vim.bo[buf].modifiable = prev - end) + if not (ok and dapui and dapui.elements and dapui.elements.console) then return end + local old_ok, old_buf = pcall(dapui.elements.console.buffer) + if not old_ok or not old_buf or not vim.api.nvim_buf_is_valid(old_buf) then return end + + -- remember every window currently showing the old buffer + local wins = vim.fn.win_findbuf(old_buf) + + -- delete old buffer — dapui's cached ref becomes invalid and the + -- next `console.buffer()` call creates a fresh buffer + pcall(vim.api.nvim_buf_delete, old_buf, { force = true }) + + local new_ok, new_buf = pcall(dapui.elements.console.buffer) + if not new_ok or not new_buf or not vim.api.nvim_buf_is_valid(new_buf) then return end + + -- rehook the windows + for _, win in ipairs(wins) do + if vim.api.nvim_win_is_valid(win) then + pcall(vim.api.nvim_win_set_buf, win, new_buf) + end + end end - dap.listeners.before.event_initialized["turbo-debug-clear-console"] = clear_console + -- Fire BEFORE the launch/attach request so the old terminal state + -- from the previous run is gone before the new terminal opens. + dap.listeners.before.launch["turbo-debug-clear-console"] = clear_console + dap.listeners.before.attach["turbo-debug-clear-console"] = clear_console + dap.listeners.before.restart["turbo-debug-clear-console"] = clear_console -- Default-collapse scopes the user considers noisy (Registers on -- codelldb, for example). We intercept the scopes response and set @@ -1024,14 +1061,14 @@ function M.enter() end set_debug_chrome() - -- Bars BEFORE dapui.open so the sidebar and bottom tray compute their - -- proportions against the screen MINUS our bars, not against the full - -- screen. Calling ensure_dapui() here re-runs dapui.setup with freshly- - -- computed layout sizes based on current terminal dimensions — this is - -- what fixes the "Console balloons on second toggle" bug. - open_bars() ensure_dapui() require("dapui").open() + open_bars() + -- dapui creates full-height vertical splits for the sidebar. Without + -- this step the bars end up constrained to the non-sidebar column. + -- `wincmd K`/`J` rearranges the window tree so each bar spans full + -- editor width at the top/bottom, pushing everything else in-between. + force_bars_full_width() -- second install pass after dapui has created its buffers vim.schedule(function()