An open API service indexing awesome lists of open source software.

https://github.com/jamylak/cplug.nvim

<leader>c to compile and debug, CMake utilities and other helpful defaults
https://github.com/jamylak/cplug.nvim

cmake debug neovim nvim plugin vim

Last synced: 1 day ago
JSON representation

<leader>c to compile and debug, CMake utilities and other helpful defaults

Awesome Lists containing this project

README

          

# cplug.nvim

## 🚧 Work In Progress

This plugin is still a work in progress and is still being tested.

`cplug.nvim` aims to make `c` the compile-and-debug entrypoint for projects.

It started as C debug, CMake, and related C/C++ tooling inside my older `jamylak/nvimconf` setup.
The goal here is to pull that work out into a cleaner reusable plugin in its own repo, so the debug workflow can stay separate and easier to maintain.

![screenshot1.png](assets/screenshot1.png)

This project currently has:

- a public `setup()` API
- a `:CPlugCompileDebug` user command
- a `:CPlugAttach` user command
- a `:CPlugGenerateAttach` user command
- a `:CPlugCMakeConfigure` user command
- a `:CPlugCMakeBuildOnce` user command
- a `:CPlugCMakeBuildAndRun` user command
- a `:CPlugCMakeRun` user command
- optional default keymaps
- a `:checkhealth cplug` healthcheck
- a core orchestration pipeline for detect -> optional scaffold -> build -> launch resolution
- a shared backend contract for future language implementations
- shared `.vscode/launch.json` resolution in the main pipeline
- a CMake backend for existing, source-only, and empty C/C++ projects
- an existing-project Cargo backend for Rust
- a minimal Python backend for existing projects
- `nvim-dap` / `nvim-dap-ui` startup from the resolved launch config
- automatic common DAP adapter registration for generated C/C++/Rust LLDB and Python debugpy configs
- automatic `nvim-dap-disasm` wiring for the default low-level UI when it is installed
- optional default DAP stepping and breakpoint keymaps

The remaining work is language expansion and adapter-specific polish.

Current backend scope:

- C/C++ detection for existing `CMakeLists.txt` projects, automatic scaffolding for source-only repos, and empty-repo bootstrapping into a minimal C++ project by default
- missing C/C++ `launch.json` generation from built CMake executables
- Rust detection for existing `Cargo.toml` projects with debug builds in `target/debug`
- missing Rust `launch.json` generation from the first Cargo binary target
- Python detection for existing projects via `pyproject.toml`, `requirements.txt`, or discovered `*.py` files in common source layouts
- Python launch generation with interpreter defaults from `.venv`, `venv`, `python3`, or `python`
- automatic Python debug environment bootstrapping with `debugpy`
- attach config generation for Python and native process attach flows
- debug launch resolution through `.vscode/launch.json`
- no Python scaffolding yet

## Setup

```lua
require("cplug").setup()
```

Disable the built-in keymaps if you want to own lazy-loading or mappings yourself:

```lua
require("cplug").setup({
default_keymaps = false,
})
```

Customize the built-in keymaps per action. Each entry accepts a single lhs, a
list of lhs values for aliases, or `false` to skip that action entirely:

```lua
require("cplug").setup({
keymaps = {
compile_debug = { "c", "gj" },
continue = { "gc", "c" },
restart = false,
},
})
```

Select a specific VS Code launch configuration by name:

```lua
require("cplug").setup({
launch = {
configuration = "Debug current file",
},
})
```

Control how cplug chooses among compatible launch configurations:

```lua
require("cplug").setup({
launch = {
select = "auto", -- "auto" | "first" | "picker"
},
})
```

`launch.json` is generated automatically by default. To change that policy:

```lua
require("cplug").setup({
launch = {
on_missing = "prompt", -- or "never"
},
})
```

Control automatic project scaffolding:

```lua
require("cplug").setup({
scaffold = {
on_missing = "always",
},
c_family = {
empty_project_language = "cpp",
},
})
```

Disable automatic `dap-ui` opening:

```lua
require("cplug").setup({
dap = {
open_ui = false,
},
})
```

Pick a default managed DAP UI layout:

```lua
require("cplug").setup({
dap = {
layout = "code_repl",
},
})
```

Let cplug own the default `dapui` layout setup explicitly:

```lua
require("cplug").setup({
dap = {
manage_ui_layout = true,
},
})
```

Disable the default disassembly pane in low-level debug layouts:

```lua
require("cplug").setup({
dap = {
disassembly = {
enabled = false,
},
},
})
```

If you disable cplug-managed UI layouts, you own the `dapui` layout and must
add `disassembly` yourself if you still want that pane:

```lua
require("cplug").setup({
dap = {
manage_ui_layout = false,
disassembly = {
enabled = true,
},
},
})

require("dapui").setup({
layouts = {
{
elements = {
{ id = "scopes", size = 0.25 },
{ id = "breakpoints", size = 0.25 },
{ id = "stacks", size = 0.25 },
{ id = "watches", size = 0.25 },
},
position = "left",
size = 40,
},
{
elements = {
{ id = "disassembly", size = 0.7 },
{ id = "repl", size = 0.3 },
},
position = "bottom",
size = 16,
},
},
})
```

Override the default CMake build directory:

```lua
require("cplug").setup({
c_family = {
build_dir = "build",
},
})
```

Skip `.clang-format` generation during C/C++ scaffolding:

```lua
require("cplug").setup({
c_family = {
generate_clang_format = false,
},
})
```

Default scaffolded C/C++ templates can seed `CPLUG_WARNINGS_AS_ERRORS`:

```lua
require("cplug").setup({
c_family = {
warnings_as_errors = true,
},
})
```

Bootstrap Git during C/C++ scaffolding:

```lua
require("cplug").setup({
c_family = {
bootstrap_git = false,
},
})
```

Keep the `:CPlugCMakeBuildOnce` terminal open after a successful build:

```lua
require("cplug").setup({
c_family = {
keep_build_terminal_open = true,
},
})
```

Keep the `:CPlugCMakeConfigure` terminal open after a successful configure:

```lua
require("cplug").setup({
c_family = {
keep_configure_terminal_open = true,
},
})
```

Override the Python interpreter used in generated launch configs:

```lua
require("cplug").setup({
python = {
interpreter = "/path/to/python",
},
})
```

By default, Python projects get a local `.venv` with `debugpy` when no usable
project environment exists. cplug prefers `uv` for this and falls back to
`python -m venv` plus `pip`. Configure the `uv` invocation if your environment
needs specific transport or index behavior:

```lua
require("cplug").setup({
python = {
console = "internalConsole",
redirect_output = true,
uv = {
native_tls = true,
extra_args = { "--index-url", "https://example.invalid/simple" },
env = {
UV_NATIVE_TLS = "true",
},
},
},
})
```

Default keymaps:

- `c` compile and debug
- `gj` compile and debug
- `gg` toggle debug UI
- `gl` pick debug UI layout
- `gc` continue
- `gx` terminate
- `gn` step over
- `gi` step into
- `go` step out
- `gb` toggle breakpoint
- `gr` run to cursor
- `gq` restart
- `ge` evaluate expression

## lazy.nvim

```lua
{
"jamylak/cplug.nvim",
dependencies = {
"mfussenegger/nvim-dap",
"nvim-neotest/nvim-nio",
"rcarriga/nvim-dap-ui",
"Jorenar/nvim-dap-disasm",
},
keys = {
{
"c",
function()
require("cplug").compile_and_debug()
end,
desc = "Compile and debug",
},
},
config = function()
require("cplug").setup({
default_keymaps = false,
})
end,
}
```

## Health

Run:

```vim
:checkhealth cplug
```

The healthcheck warns when `nvim-dap`, `nvim-dap-ui`, or `nvim-dap-disasm` are missing from `runtimepath`.

## Tests

Run the current regression scripts with:

```sh
make test
```

Try the plugin locally against a toy or empty C++ project with:

```sh
sh scripts/run-cpp-demo.sh toy
sh scripts/run-cpp-demo.sh empty
```

The demo runner sets `` to ``, enables automatic project and
launch scaffolding, and will also add local `nvim-dap`, `nvim-dap-ui`, and
`nvim-dap-disasm` installations plus an LLDB adapter when they are already
available on your machine. That lets you use `c`, `gj`, and the
default `g...` debug mappings directly in the demo session, including
`gl` for the layout picker.

The demo also exposes `:CPlugAttach` and `:CPlugGenerateAttach` so you can try
attach flows after starting a local process yourself.

For a repeatable attach demo, use the dedicated `target` and `editor` scripts:

```sh
sh scripts/demo/python-attach-target.sh
sh scripts/demo/python-attach-editor.sh

sh scripts/demo/cpp-attach-target.sh
sh scripts/demo/cpp-attach-editor.sh
```

The `target` script starts something attachable. The `editor` script opens
Neovim in the matching demo project. Then run `:CPlugAttach` inside Neovim.

## DAP Startup

Once a backend and launch config are resolved, cplug passes the selected configuration to `nvim-dap` and opens `nvim-dap-ui` by default. cplug manages named `dapui` layout presets unless you set `dap.manage_ui_layout = false`.

By default `dap.layout = "auto"`:

- non-low-level adapters use the `standard` layout
- low-level adapters use the `native` layout

Available managed presets:

- `standard`
- `native`
- `code_repl`
- `stack_focus`
- `repl_only`

You can switch layouts at runtime with `:CPlugLayout` or the default
`gl` keymap. `:CPlugLayout` with no argument opens a built-in floating
picker with fuzzy filtering.
Low-level sessions use the disassembly pane in the `native` layout when
`Jorenar/nvim-dap-disasm` is installed; disable that with
`dap.disassembly.enabled = false`.

## DAP Keymaps

When `default_keymaps = true`, cplug also registers a small set of DAP action mappings:

- `compile and debug` on `c` and `gj`
- `toggle debug UI` on `gg`
- `pick debug UI layout` on `gl`
- `continue` on `gc`
- `terminate` on `gx`
- `step over` on `gn`
- `step into` on `gi`
- `step out` on `go`
- `toggle breakpoint` on `gb`
- `run to cursor` on `gr`
- `restart` on `gq`
- `evaluate expression` on `ge`

If you want extra convenience bindings without fully opting out, set
`keymaps.` to a list of lhs values. If you want complete control, set
`default_keymaps = false` and define your own mappings outside the plugin.

## Launch Configs

The shared launch layer expects `.vscode/launch.json` by default, can select a named configuration via `opts.launch.configuration`, and can handle missing files with `opts.launch.on_missing`:

- `"prompt"` prompts before generating a backend-specific launch file
- `"always"` generates automatically and is the default
- `"never"` returns an error

Generated launch files are written as pretty multi-line JSON. By default,
`dap.auto_adapter = "auto"` also registers the common generated adapters when
possible: `lldb` from `lldb-dap`, `codelldb`, or `lldb-vscode`, and `python`
through the resolved interpreter running `debugpy.adapter`. For Python, cplug
also ensures `debugpy` is installed in the project env unless
`python.bootstrap_debugpy = false` or you set an explicit `python.interpreter`.
Set `dap.auto_adapter = "none"` if you want to own adapter registration
yourself.

Project scaffolding uses `opts.scaffold.on_missing` with the same modes, and
defaults to `"always"` so `c` will generate missing C/C++ project
files automatically.

`:CPlugAttach` uses the same `launch.on_missing` policy, but resolves attach
configurations first and can auto-generate an attach config when the backend
provides one and `.vscode/launch.json` is missing.

`:CPlugGenerateAttach` writes or updates an attach configuration for the
current backend without starting DAP. Existing launch entries are preserved
unless a generated attach configuration replaces one with the same name.

## Attach

Run:

```vim
:CPlugAttach
```

This detects the current backend, resolves the selected attach configuration,
and starts `nvim-dap` without entering cplug's build or scaffold path.

Run:

```vim
:CPlugGenerateAttach
```

This writes a backend-appropriate attach configuration into
`.vscode/launch.json` without starting a debug session.

Current attach templates:

- Python: `debugpy`-style server attach on `127.0.0.1:5678`
- C/C++ and Rust: LLDB process attach using `${command:pickProcess}`

## CMake Configure

Run:

```vim
:CPlugCMakeConfigure
```

This reuses the CMake backend detection and scaffolding flow, then runs a Debug configure step into the configured build directory without starting DAP.
It opens the configure step in a terminal split, keeps failures visible, and closes the terminal automatically when configuration succeeds unless `c_family.keep_configure_terminal_open = true`.

## CMake Build Once

Run:

```vim
:CPlugCMakeBuildOnce
```

This reuses the same CMake detection and scaffolding flow, then performs a one-off Debug build into the configured build directory without starting DAP.
It opens the build in a terminal split, keeps failures visible, and closes the terminal automatically when the build succeeds unless `c_family.keep_build_terminal_open = true`.

## CMake Build And Run

Run:

```vim
:CPlugCMakeBuildAndRun
```

This reuses the same CMake detection and scaffolding flow, performs a Debug build, and runs the first built executable without starting DAP.
It now runs the build in a terminal first, then starts the executable in a second terminal only after the build succeeds.

## CMake Run

Run:

```vim
:CPlugCMakeRun
```

This runs the first built executable from the configured CMake build directory without rebuilding first.
It opens the executable in a terminal split, focuses that window, and enters insert mode so live output stays visible while the process runs.

## Layout Picker

Run:

```vim
:CPlugLayout
```

With no argument, this opens the managed DAP layout picker through
the built-in floating picker. Start typing to fuzzy-filter the available
layouts, then press `` to switch.

You can also set a layout directly:

```vim
:CPlugLayout code_repl
:CPlugLayout native
:CPlugLayout auto
```