Telescope-powered script runner & build system for Neovim 0.10+
Fuzzy find scripts or binaries in your project, pick one, and run it in a terminal buffer. Configure, build, and pick targets from CMake, Make, and Meson projects. Generate new C/C++ projects with a single command.

vim.ui.input before execution:ExecutionerRerun re-runs the last script without the picker:CreateProject wizard generates build files, source stubs, and proper directory layoutq in the terminal buffer to close itOptional:
terminal.type = "toggleterm"cmake, make, meson, ninja — for build system integrationgit — for :CreateProject git init{
"sektant1/executioner.nvim",
cmd = { "Executioner", "ExecutionerRerun",
"ExecutionerConfigure", "ExecutionerBuild",
"ExecutionerBuildLast", "CreateProject" },
keys = {
{ "<leader>er", function() require("executioner").run_scripts() end, desc = "Executioner: run script" },
{ "<leader>eR", function() require("executioner").rerun() end, desc = "Executioner: rerun last script" },
{ "<leader>ec", function() require("executioner").configure() end, desc = "Executioner: configure project" },
{ "<leader>eb", function() require("executioner").build() end, desc = "Executioner: build target" },
{ "<leader>eB", function() require("executioner").build_last() end, desc = "Executioner: rerun last build" },
{ "<leader>ep", function() require("executioner").create_project() end, desc = "Executioner: create project" },
},
dependencies = {
"nvim-telescope/telescope.nvim",
"nvim-lua/plenary.nvim",
},
opts = {},
config = function(_, opts)
require("executioner").setup(opts)
require("telescope").load_extension("executioner")
end,
}
use {
"sektant1/executioner.nvim",
requires = {
"nvim-telescope/telescope.nvim",
"nvim-lua/plenary.nvim",
},
config = function()
require("executioner").setup()
require("telescope").load_extension("executioner")
end,
}
:Rocks install executioner.nvim
:Executioner
Or from Lua:
require("executioner").run_scripts()
Select a script, optionally enter arguments, and it runs in a terminal buffer.
The args prompt is pre-filled with the last-used arguments for that script.
Press q in the terminal buffer (normal mode) to close it.
To re-run the last script without the picker:
:ExecutionerRerun
executioner.nvim auto-detects CMake, Make, and Meson projects by looking for
CMakeLists.txt, Makefile (or makefile, GNUmakefile), and meson.build
in the current working directory.
Configure (CMake / Meson):
:ExecutionerConfigure
Build a target (opens a Telescope picker with discovered targets):
:ExecutionerBuild
You can also pass a target directly (with tab-completion):
:ExecutionerBuild myapp
Re-run the last build:
:ExecutionerBuildLast
| Command | Lua API | Description |
|---|---|---|
:Executioner |
require("executioner").run_scripts() |
Pick and run a script |
:ExecutionerRerun |
require("executioner").rerun() |
Rerun last script |
:ExecutionerConfigure |
require("executioner").configure() |
Configure project |
:ExecutionerBuild [target] |
require("executioner").build(nil, target) |
Build target (picker or direct) |
:ExecutionerBuildLast |
require("executioner").build_last() |
Rerun last build |
:CreateProject |
require("executioner").create_project() |
Generate a new project |
| Build system | Method |
|---|---|
| CMake | Parsed from cmake --build <dir> --target help |
| Make | Parsed from .PHONY declarations and rule lines in the Makefile |
| Meson | Parsed from meson introspect --targets <dir> (JSON) |
Create a new C or C++ project with an interactive wizard:
:CreateProject
Or from Lua:
require("executioner").create_project()
The wizard walks you through these steps:
| Step | Options |
|---|---|
| Project name | Free text input |
| Build system | CMake / Make / Meson |
| Language | C / C++ |
| Project type | Executable, Static Library, Shared Library, Header-only Library, Library + Executable |
| Standard | C: c11 c17 c23 — C++: c++17 c++20 c++23 |
| .gitignore | Yes / No |
| Git init | Yes / No |
A directory with the project name is created in the current working directory
containing the build file, source stubs, and proper src/ / include/
directories where appropriate.
| Project type | Directories | Files |
|---|---|---|
| Executable | src/ |
build file + src/main.c|cpp |
| Static Library | src/ include/ |
build file + src/name.c|cpp + include/name.h|hpp |
| Shared Library | src/ include/ |
build file + src/name.c|cpp + include/name.h|hpp |
| Header-only Library | include/ |
build file + include/name.h|hpp |
| Library + Executable | src/ include/ |
build file + src/name.c|cpp + src/main.c|cpp + include/name.h|hpp |
For example, :CreateProject with name myapp, CMake, C++, Library + Executable produces:
myapp/
├── CMakeLists.txt
├── include/
│ └── myapp.hpp
└── src/
├── main.cpp
└── myapp.cpp
Build files reference the correct paths — CMake uses
${CMAKE_CURRENT_SOURCE_DIR}/include, Make appends -Iinclude to compiler
flags, and Meson uses include_directories('include').
After creation, executioner offers to cd into the new project directory.
Defaults are shown below.
require("executioner").setup({
scripts_dir = ".", -- string or function(): string
recursive = true,
max_depth = 3,
ignore = { "node_modules", ".git", ".venv", "target", "dist" },
include_executables = true,
always_prompt_args = true,
extensions = {
sh = "bash",
bash = "bash",
zsh = "zsh",
fish = "fish",
py = "python3",
ps1 = "pwsh",
lua = "nvim -l",
js = "node",
ts = "tsx",
rb = "ruby",
pl = "perl",
bat = "cmd /c",
cmd = "cmd /c",
},
build = {
cmake = {
build_dir = "build", -- relative to cwd or absolute
generator = nil, -- e.g. "Ninja" (nil = cmake default)
configure_args = {}, -- extra args for cmake -B … -S …
build_args = {}, -- extra args for cmake --build …
},
make = {
args = {}, -- extra args passed before the target
},
meson = {
build_dir = "builddir", -- relative to cwd or absolute
setup_args = {}, -- extra args for meson setup
compile_args = {}, -- extra args for meson compile
},
},
terminal = {
type = "split", -- "split" | "float" | "toggleterm"
split = {
direction = "belowright",
size = 15,
vertical = false,
},
float = {
width = 0.8,
height = 0.8,
border = "rounded",
title = " Executioner ",
},
auto_close = false,
start_insert = true,
},
telescope = {
theme = "dropdown",
preview = true,
},
keymaps = { run = false }, -- set to e.g. "<leader>er" for a global mapping
on_exit = nil, -- function(code, script_path)
})
scripts/ directoryrequire("executioner").setup({
scripts_dir = "scripts",
recursive = true,
})
require("executioner").setup({
terminal = { type = "split", split = { size = 20 } },
})
require("executioner").setup({
terminal = { auto_close = true },
})
require("executioner").setup({
extensions = { rs = "cargo run --" },
})
require("executioner").setup({
scripts_dir = function()
return vim.fn.getcwd() .. "/bin"
end,
})
require("executioner").setup({
always_prompt_args = false,
})
require("executioner").setup({
build = {
cmake = {
generator = "Ninja",
configure_args = { "-DCMAKE_BUILD_TYPE=Release" },
build_args = { "-j8" },
},
},
})
require("executioner").setup({
build = {
make = { args = { "-j$(nproc)" } },
},
})
require("executioner").setup({
build = {
meson = {
setup_args = { "--buildtype=release" },
},
},
})
:checkhealth executioner
Verifies dependencies (telescope, plenary, toggleterm), Neovim version,
scripts_dir existence, common interpreter availability, and build tool
detection (cmake, make, meson, ninja).
Q: How does executioner decide which interpreter to use?
A: First it checks for a shebang (#!) + executable bit — if both exist, the file runs directly. Otherwise it looks up the file extension in the extensions map. Bare executables without a shebang or known extension also run directly.
Q: Can I use this without Telescope? A: No. Telescope is a required dependency for the picker UI.
Q: Does it work on Windows?
A: The bat and cmd extensions map to cmd /c. Path handling uses vim.fn.fnamemodify. It should work for basic cases but is primarily tested on Unix.
Q: How do I add support for a new language?
A: Add the extension to the extensions table: extensions = { go = "go run" }.
Q: How does build system detection work?
A: executioner looks for CMakeLists.txt, meson.build, or Makefile (also makefile and GNUmakefile) in the current working directory. Priority is CMake > Meson > Make.
Q: Do I need to run :ExecutionerConfigure before :ExecutionerBuild?
A: For CMake and Meson, yes — the project must be configured first so the build directory exists. For Make, there is no configure step.
Q: What does :CreateProject generate for a header-only library?
A: For C++ it generates a header with inline functions in include/. For C it generates an stb-style single-header with an IMPLEMENTATION guard in include/ — define PROJECTNAME_IMPLEMENTATION in exactly one .c file before including.
Q: What directory layout does :CreateProject use?
A: Sources go in src/, public headers go in include/. Executables get only src/, header-only libraries get only include/, and all other library types get both. The build file sits at the project root.
make test or nvim --headless -u tests/minimal_init.lua -c "PlenaryBustedDirectory tests/ { minimal_init = 'tests/minimal_init.lua' }"stylua passesMIT