GitHunt
LL

llawn/save-my-breakpoints.nvim

Neovim nvim-dap extension for persistent breakpoints

Save My Breakpoints

GitHub License
GitHub repo size
GitHub Tag
Neovim Version

save-my-breakpoints is a memory-efficient Lua plugin for Neovim that saves
nvim-dap breakpoints to a file and
automatically restores them based on your current working directory.

Inspired by persistent-breakpoints.nvim

This version uses Base64URL encoding to create unique, reversible save
files for every project and uses an in-memory cache to ensure high
performance without constant disk I/O.

It comes with an optional Telescope menu for viewing and managing breakpoints:

smb-telescope-example

Install

lazy.nvim

{
  "llawn/save-my-breakpoints",
  dependencies = {
    "mfussenegger/nvim-dap",
    "nvim-telescope/telescope.nvim" -- Optional for telescope integration
    "nvim-tree/nvim-web-devicons" -- Optional for filetype icons in telescope_menu
  },
}

Using vim.pack (Neovim 0.12+)

-- In your init.lua
vim.pack.add({
  "mfussenegger/nvim-dap",
})

vim.pack.add({
  "nvim-telescope/telescope.nvim", -- Optional for telescope integration
})

vim.pack.add({
  "nvim-tree/nvim-web-devicons", -- Optional for filetype icons
})

vim.pack.add({
  "llawn/save-my-breakpoints.nvim",
  config = function()
    require('save-my-breakpoints').setup({
      telescope_menu = true,
      load_on_buffer_open = true,
      save_on_exit = true,
    })
  end,
})

Dependencies

  • Optional: jq to format JSON files in save_dir

Setup

Basic Setup

{
  "llawn/save-my-breakpoints",
  dependencies = {
    "mfussenegger/nvim-dap",
    "nvim-telescope/telescope.nvim" -- Optional for telescope integration
    "nvim-tree/nvim-web-devicons" -- Optional for filetype icons in telescope_menu
  },
  config = function()
    require('save-my-breakpoints').setup({
      save_dir = vim.fn.stdpath('data') .. '/save_my_breakpoints',
      load_on_start = true,
      load_on_buffer_open = true,
      save_on_exit = true,
      telescope_menu = true, -- Enable telescope integration
    })
  end
}

Complete DAP Setup Example

This example shows how to integrate save-my-breakpoints with a full nvim-dap configuration (Lazy):

return {
  {
    "mfussenegger/nvim-dap",
    dependencies = {
      "rcarriga/nvim-dap-ui",
      "theHamsta/nvim-dap-virtual-text",
      "nvim-neotest/nvim-nio",
      "jay-babu/mason-nvim-dap.nvim",
      "williamboman/mason.nvim",
      "llawn/save-my-breakpoints.nvim",
    },
    config = function()
      local dap = require("dap")
      local dapui = require("dapui")

      -- Mason DAP Setup
      require("mason-nvim-dap").setup({
        automatic_installation = false,
        ensure_installed = {},
        handlers = {
          function(config)
            require('mason-nvim-dap').default_setup(config)
          end,
        },
      })

      -- UI & Virtual Text Setup
      dapui.setup()
      require("nvim-dap-virtual-text").setup({
        commented = true,
      })

      -- Breakpoint Signs
      local signs = {
        DapBreakpoint          = { text = "●", texthl = "DapBreakpoint", linehl = "", numhl = "" },
        DapBreakpointCondition = { text = "●", texthl = "DapBreakpointCondition", linehl = "", numhl = "" },
        DapLogPoint            = { text = "", texthl = "DapLogPoint", linehl = "", numhl = "" },
        DapStopped             = { text = "󰁕", texthl = "DapStopped", linehl = "DapStopped", numhl = "DapStopped" },
      }

      vim.api.nvim_set_hl(0, 'DapBreakpoint', { fg = '#ff0800', bg = 'NONE' })
      vim.api.nvim_set_hl(0, 'DapBreakpointCondition', { fg = '#da9100', bg = 'NONE' })
      vim.api.nvim_set_hl(0, 'DapLogPoint', { fg = '#6cb4ee', bg = 'NONE' })
      vim.api.nvim_set_hl(0, 'DapStopped', { fg = '#93c572', bg = '#353839', bold = true })

      for name, sign in pairs(signs) do
        vim.fn.sign_define(name, sign)
      end

      -- DAP Listeners
      dap.listeners.after.event_initialized["dapui_config"] = function()
        dapui.open()
      end
      dap.listeners.before.event_terminated["dapui_config"] = function()
        dapui.close()
      end
      dap.listeners.before.event_exited["dapui_config"] = function()
        dapui.close()
      end

      -- Keymaps
      local set = vim.keymap.set
      set("n", "<F5>", dap.continue, { desc = "Debug: Start/Continue" })
      set("n", "<F6>", dap.step_over, { desc = "Debug: Step Over" })
      set("n", "<F7>", dap.step_into, { desc = "Debug: Step Into" })
      set("n", "<F8>", dap.step_out, { desc = "Debug: Step Out" })
      set("n", "<Leader>b", require('save-my-breakpoints').toggle_breakpoint, { desc = "Toggle Breakpoint" })

      -- Save My Breakpoints Setup
      require('save-my-breakpoints').setup({
        save_dir = vim.fn.stdpath('data') .. '/breakpoints',
        load_on_buffer_open = true,
        save_on_exit = true,
        telescope_menu = true,
      })
    end,
  },
}

Default Configuration

Option Default Description
save_dir vim.fn.stdpath('data') .. /save_my_breakpoints Path to store the encoded JSON files.
load_on_start true Load project breakpoints on startup.
load_on_buffer_open true Load breakpoints for a file as soon as its buffer is opened.
save_on_exit true Flush the memory cache to disk when Neovim closes.
telescope_menu false Enable telescope integration for breakpoint management.

Usage

User Commands

Command Description
:SMBToggleBreakpoint Toggles a breakpoint and syncs memory (Delete Line Breakpoint / Add default Breakpoint)
:SMBSetConditionalBreakpoint Prompts for a condition and sets a breakpoint
:SMBSetLogPoint Prompts for a log message and sets a breakpoint
:SMBClearAllBreakpoints Clears all breakpoints and deletes the project save file
:SMBLoadBufferBreakpoints Load all breakpoints from current buffer
:SMBLoadAllBreakpoints Load all breakpoints from current project
:TelescopeBreakpoints Opens a telescope picker to view and delete breakpoints (requires telescope_menu = true)
local smb = require('save-my-breakpoints')

vim.keymap.set("n", "<leader>bb", smb.toggle_breakpoint, { desc = "DAP Toggle Breakpoint" })
vim.keymap.set("n", "<leader>bc", smb.set_conditional_breakpoint, { desc = "DAP Conditional Breakpoint" })
vim.keymap.set("n", "<leader>bp", smb.set_log_point, { desc = "DAP Log Breakpoint" })
vim.keymap.set("n", "<leader>bX", smb.clear_all_breakpoints, { desc = "DAP Clear All Breakpoints" })
vim.keymap.set("n", "<leader>bl", smb.load_bps_for_buf, { desc = "DAP Load Buffer Breakpoints" })
vim.keymap.set("n", "<leader>bL", smb.load_all_breakpoints, { desc = "DAP Load All Breakpoints" })
vim.keymap.set("n", "<leader>bt", smb.show_breakpoints, { desc = "DAP Show Breakpoints (Telescope)" })

How it Works

  1. Efficiency: Breakpoints are stored in an in-memory session cache. Disk writes only happen when you call flush_to_disk (automatically on exit).
  2. Path Encoding: The plugin uses Base64URL to convert your current working directory into a safe filename. This allows you to have different breakpoints for the same file in different projects.
  3. Automatic Cleanup: If you clear all breakpoints in a project, the corresponding JSON file is automatically deleted from your disk.
  4. Telescope Integration: When enabled, provides a convenient telescope picker for viewing and managing breakpoints with delete functionality using <C-d>.

License

MIT License - see LICENSE

llawn/save-my-breakpoints.nvim | GitHunt