Skip to content

Repository files navigation

nvim-dapper

A debugging environment for Neovim, built on top of nvim-dap, targeting JavaScript/TypeScript first via vscode-js-debug, with optional LLM-assisted investigation as a layered feature.

Status: Phase 1 in progress — session manager, extmark namespace pool, and main panel frame landed.

Requirements

  • Neovim 0.10+
  • nvim-dap ≥ 0.8.0 (minimum version verified against commit d5b09c2, 2024-01; the vscode-js-debug smoke test is the canary for upstream drift)
  • plenary.nvim (test runner)
  • vscode-js-debug (smoke tests and JS/TS debugging)

Session Manager

lua/nvim-dapper/session/ is the first shipped capability. It is the single source of truth for live nvim-dap session state:

  • Typed event streamsession_started, session_terminated, stopped, continued, thread_changed, frame_changed, focus_changed, output, adapter_failed, attach_failed.
  • Subscriber-keyed subscriptionssubscribe(id, handler) / unsubscribe(id). Teardown is atomic per subscriber id, making mount/unmount listener-leak-free.
  • Pull accessorscurrent_session(), current_thread(), current_frame(), is_paused(), is_running(). Updated before each dispatch so handlers see the new state synchronously.
  • Multi-session aware — observes all live nvim-dap sessions, follows nvim-dap's own focus policy (most recent stopped wins), emits focus_changed when focus shifts.
  • Infrastructure-failure eventsadapter_failed and attach_failed with discriminating reason fields; no silent failure.

See docs/session-manager.md for the full API, event payload shape, and the thread focus model.

Main Panel

lua/nvim-dapper/panel/main/ owns the single Neovim window that every Phase 1 view (variables, stack, breakpoints, watches, REPL, output) will register into via panel.register_view(spec). Views never create their own windows or scratch buffers — that constraint is CI-enforced. See docs/main-panel.md for the registration API, the mount-on-first-focus lifecycle, the on_session_event re-dispatch contract, the four user-facing commands (:DebugPanel, :DebugPanelToggle, :DebugPanelClose, :DebugView), and the winbar highlight groups.

Extmark Namespace Pool

lua/nvim-dapper/extmark_pool/ is the sole allocator of vim.api.nvim_create_namespace in the plugin. It distinguishes concern-scoped (plugin-lifetime) namespaces from instance-scoped (owner-lifecycle) namespaces, tracks which buffers have received marks under each namespace, and bulk-clears O(tracked buffers) instead of scanning every buffer. See docs/extmark-pool.md for the API and the rules a Phase 1 view must follow.

Usage

local session = require("nvim-dapper.session")

-- Mount
session.subscribe("my-view", function(event)
  if event.kind == "stopped" then
    local frame = session.current_frame()  -- pull state inside handler
    -- render ...
  elseif event.kind == "session_terminated" then
    -- clear ...
  end
end)

-- Unmount
session.unsubscribe("my-view")

Keybinds

Every Phase 1 panel view ships view-local default keymaps on its scratch buffer (<CR> jumps to source, l/h expand/collapse, r retries, ]p pages, etc.). The full table — plus the override snippet for each layer — lives in docs/keymaps.md.

Global debugger keymaps (<F5> continue, <F10> step over, <leader>de evaluate, …) are opt-in. Enable them with:

require("nvim-dapper").setup({
  keymaps = { global = { enabled = true } },
})

When using Dapper panels with step/continue commands, enable Dapper's stopped-frame source targeting so nvim-dap opens the stopped source in an editing window instead of replacing the focused panel buffer:

require("nvim-dapper").setup({
  stopped_frame_source_targeting = true,
  keymaps = { global = { enabled = true } },
})

This option installs Dapper's managed nvim-dap switchbuf function. It reuses an eligible editing window for stopped source, including unopened source buffers, and opens a split only when no editing window is available. When enabled, Dapper also applies the managed policy to adapter-specific dap.defaults.<type>.switchbuf values for active sessions and restores the prior values when disabled. If nvim-dap loads after setup(), Dapper keeps the opt-in pending and installs the policy once dap is available. Plain setup({}) leaves any existing nvim-dap switchbuf value intact.

See docs/keymaps.md for the full default table, override grammar, and the require("nvim-dapper.keymaps").actions() facade for building your own bindings.

Downstream Rules (CI-enforced)

  • Never import dap.listeners or call dap.session() from outside lua/nvim-dapper/session/.
  • Never read event.data from a dapper.SessionEvent outside lua/nvim-dapper/session/.
  • Never call blocking primitives (vim.fn.system, io.read, …) from any function reachable from a handler.

See docs/contributing.md for details.

Running Tests

# Static lint (dap-isolation, event-data, blocking-handler,
# namespace-isolation, extmark-pool-imports, window-ownership, panel-no-dap)
nvim -l scripts/lint.lua lua

# Unit tests (no adapter required)
nvim --headless \
  -u tests/minimal_init.lua \
  -c "lua require('plenary.test_harness').test_directory('tests/session/', { sequential = true })" \
  -c "qa!"

nvim --headless \
  -u tests/minimal_init.lua \
  -c "lua require('plenary.test_harness').test_directory('tests/extmark_pool/', { sequential = true })" \
  -c "qa!"

nvim --headless \
  -u tests/minimal_init.lua \
  -c "lua require('plenary.test_harness').test_directory('tests/panel/main/', { sequential = true })" \
  -c "qa!"

# Smoke test (requires vscode-js-debug)
VSCODEJSDEBUG=/path/to/ms-vscode.js-debug/extension \
nvim --headless \
  -u tests/minimal_init.lua \
  -c "lua require('plenary.test_harness').test_directory('tests/session/', { sequential = true, pattern = 'smoke_spec' })" \
  -c "qa!"

Roadmap

  • Phase 1: Session manager ✓ → main panel → variables view → stack view → inline virtual text values
  • Phase 2: Agent tool registry, attach_and_pause_at primitive, multi-session controller

About

No description, website, or topics provided.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages