Skip to content

Breakpoint synchronization

Breakpoints are shared between the editor and the debugger through one file. This page explains how, and what the sharing does and does not guarantee.

One file, two readers

<root>/.bugsaur/
├── config.toml        the project — shared, committed
├── breakpoints.json   your breakpoints — personal, ignored by git
└── .gitignore         created on the first write

Neovim writes breakpoints.json; the debugger reads it. That is the whole mechanism, and it gives three properties:

  • They survive a Neovim restart. The set is restored when you next open the file. You do not re-create breakpoints after restarting your editor.
  • The terminal and the editor agree. bugsaur run --project . with no --break starts with exactly the breakpoints :DebugStart would use.
  • They survive a move. Paths are stored relative to the project root, so moving or renaming the project directory does not invalidate the set.

Personal, not shared

On the first write, a .bugsaur/.gitignore is created next to the file, which hides breakpoints.json from git. config.toml is the project's; breakpoints are yours.

An existing .gitignore is left alone — if you have your own, add the breakpoints.json line by hand.

Line numbers are stored as written

The line number is stored literally. If a file changes outside the editor — a rebase, a git checkout, a code formatter run while Neovim is closed — the breakpoint stays on its old line number and you will have to move it.

Files outside any project

A file that is not under any .bugsaur/ root has nowhere to be saved. Its breakpoints live until the end of the editor session and are then gone. That is a consequence of the storage being per project, not a bug.

The command line can override the file

bugsaur gui --project . --break src/main.rs:15

An explicit --break replaces the saved set entirely, rather than adding to it. Without --break, the saved set is used as-is.

:DebugTest behaves the same way as an explicit set: a test run uses only the breakpoints of that request, not the saved ones.

The three states of a breakpoint

The sign in the gutter shows what the adapter said:

Sign State Meaning
verified the adapter accepted it at that line
pending no answer yet
rejected the adapter refused it

The adapter is allowed to move a breakpoint off the line you asked for. When it does, the requested line gets no confirmation and stays pending — the state reflects the answer to your request, not the presence of a breakpoint somewhere nearby.

The same states appear as circles in the window's Breakpoints panel.

Reacting to changes from a plugin

Any change to the set fires a User autocommand:

vim.api.nvim_create_autocmd("User", {
  pattern = "BugsaurBreakpointsChanged",
  callback = function(event)
    -- event.data.paths — the affected files
  end,
})

This fires for changes from :ToggleBreakpoint, from the Lua API, and from reading a file's set off disk. Full API: Commands.