fix: deferred dep loading + idempotent setup
Two related bugs surfaced when the user tried adding `setup({...})` to
their init.lua alongside our auto-setup:
1. **`inmemory.bps = nil` crash.** When a user's `init.lua` does
`vim.pack.add(..., turbo-debug)` and then calls `setup({...})`
synchronously right after, the nested `vim.pack.add` calls inside
our own `plugin/turbo-debug.lua` (for dap/dapui/persistent-
breakpoints) are DEFERRED by nvim's pack module — so the user's
follow-up `require('persistent-breakpoints')` throws "module not
found". On the previous `initialized = true` guard, the error set
the flag but left setup half-complete; any later call short-
circuited and `inmemory.bps` stayed `nil`, exploding the first
breakpoint toggle (`api.lua:15: attempt to index field 'bps'`).
2. **Colorscheme override ignored.** Same root cause: user's
`setup({ colorscheme = "nightfox" })` errored before reaching
`config.merge`, so `config.opts.colorscheme` stayed `nil` and
`M.enter` never switched themes.
Three changes that together fix both:
- **`init.lua` `setup()` self-defers** when `require('persistent-
breakpoints')` fails, `vim.schedule`s itself once deps can load.
- **`plugin/turbo-debug.lua` auto-setup is now `vim.schedule`d** and
skips entirely if the user's explicit `setup({...})` already ran
(tracked via `M._setup_called`) — no more stomping user opts with
a second `config.merge(nil)`.
- **`breakpoints.setup` is now fully idempotent**: dropped the
`initialized` flag, since every call inside it
(`persistent-breakpoints.setup`, `sign_define`, `nvim_set_hl`,
`vim.keymap.set`) is safe to re-run.
This commit is contained in:
3 files changed
+49
-7
No files matched your search
@@ -2,12 +2,26 @@ local config = require("turbo-debug.config")
|
||||
|
||||
local M = {}
|
||||
|
||||
local initialized = false
|
||||
|
||||
-- This function is idempotent — every operation inside is safe to re-run.
|
||||
-- That property matters because turbo-debug.setup() can get called twice in
|
||||
-- practice:
|
||||
-- (1) Deferred auto-setup from plugin/turbo-debug.lua, which runs after
|
||||
-- vim.pack.add has finished registering all deps in runtimepath. The
|
||||
-- defer is necessary — calling setup synchronously during
|
||||
-- vim.pack.add would try to `require('persistent-breakpoints.api')`
|
||||
-- before the plugin's lua/ directory is on the runtimepath and
|
||||
-- crash with "module not found".
|
||||
-- (2) User's explicit setup({...}) in their init.lua with config
|
||||
-- overrides. It runs immediately (before the deferred auto-setup).
|
||||
--
|
||||
-- Previously a single `initialized` flag guarded this function so it only
|
||||
-- ran once. That guard was fragile: if the first call errored partway
|
||||
-- (case above), the flag was set but the setup was incomplete, and the
|
||||
-- second call skipped the retry. We now let every call do its full work;
|
||||
-- persistent-breakpoints.setup re-reads its bps file, sign_define overrides
|
||||
-- a previous definition, nvim_set_hl overrides a previous link, and
|
||||
-- vim.keymap.set overrides a previous binding.
|
||||
function M.setup()
|
||||
if initialized then return end
|
||||
initialized = true
|
||||
|
||||
require("persistent-breakpoints").setup({
|
||||
load_breakpoints_event = { "BufReadPost" },
|
||||
})
|
||||
|
||||
@@ -4,6 +4,21 @@ local mode = require("turbo-debug.mode")
|
||||
local M = {}
|
||||
|
||||
function M.setup(opts)
|
||||
-- Self-defer if dependencies aren't yet on the runtimepath. This happens
|
||||
-- when the user calls `setup({...})` from their init.lua right after
|
||||
-- `vim.pack.add(..., turbo-debug)`: our plugin/turbo-debug.lua fires
|
||||
-- nested `vim.pack.add` calls for dap/dapui/persistent-breakpoints, but
|
||||
-- those nested adds only register the paths on the NEXT event loop tick
|
||||
-- — so a synchronous follow-up `require('persistent-breakpoints')`
|
||||
-- crashes with "module not found". `vim.schedule` lets the pack module
|
||||
-- finalize before we retry. `_setup_called` still gets set on the retry,
|
||||
-- so plugin/turbo-debug.lua's own deferred auto-setup correctly skips.
|
||||
if not pcall(require, "persistent-breakpoints") then
|
||||
vim.schedule(function() M.setup(opts) end)
|
||||
return
|
||||
end
|
||||
|
||||
M._setup_called = true
|
||||
config.merge(opts)
|
||||
require("turbo-debug.breakpoints").setup()
|
||||
require("turbo-debug.adapters").setup()
|
||||
|
||||
+15
-2
@@ -16,5 +16,18 @@ pcall(function()
|
||||
require("which-key").add({ { "<leader>d", group = "debug" } })
|
||||
end)
|
||||
|
||||
-- setup immediately (idempotent — user can call setup() again with overrides)
|
||||
require("turbo-debug").setup()
|
||||
-- Deferred auto-setup. Must be deferred (not called synchronously) because
|
||||
-- the vim.pack.add calls above might not have finished registering the
|
||||
-- dep modules in runtimepath by the time this script's top-level runs —
|
||||
-- a synchronous `require('persistent-breakpoints.api')` inside setup would
|
||||
-- crash with "module not found". vim.schedule guarantees we run AFTER the
|
||||
-- current event loop tick, at which point pack has finalized the paths.
|
||||
--
|
||||
-- If the user calls setup({...}) themselves (typically from init.lua after
|
||||
-- vim.pack.add), `M._setup_called` flips true and this deferred call
|
||||
-- becomes a no-op — otherwise we'd overwrite their user opts with defaults.
|
||||
vim.schedule(function()
|
||||
local td = require("turbo-debug")
|
||||
if td._setup_called then return end
|
||||
pcall(td.setup)
|
||||
end)
|
||||
Reference in new issue
Block a user