A simple but extensive task runner for Neovim
- Task Providers: Auto-discover tasks from justfiles, npm scripts, Cargo (via rust-analyzer LSP)
- Task Bundles: Group multiple tasks to run together
- Output Processing: Parse build errors with vim's errorformat; configure processors per-task for multi-language projects
- Quickfix Integration: Errors populate quickfix list; opens in trouble.nvim if installed
- Floating Terminal: View task output in floating windows
- Toggle Terminal: Built-in toggle terminal (
:ForgeTerm) - Lualine Component: Show running task status in your statusline
-- lazy.nvim
{
"ten3roberts/forge.nvim",
opts = {}
}require("forge").setup({
providers = {
require("forge.provider.just"),
require("forge.provider.npm"),
require("forge.provider.lsp.cargo"),
},
terminal = {
kind = "float",
height = { 0.6, 40 },
width = { 0.8, 120 },
border = "rounded",
},
keymaps = { -- Set to false to disable all keymaps
tasks = { "<leader>sr", "<leader>ss" },
stop_all = "<leader>sx",
new = "<leader>sn",
},
default_processors = { "errorformat" }, -- Format any errors to quickfix
quickfix = {
auto_open = false,
},
job_dispose_timeout = 300, -- seconds before completed jobs are removed
})Forge is built around two core concepts: tasks and jobs.
-
Task: A definition of something you can run (e.g.,
cargo build,npm test)- Auto-discovered by providers from
justfile,package.json, Cargo LSP, etc. - Create custom tasks with
:Forge new
- Auto-discovered by providers from
-
Job: A running instance of a task
- Monitor, restart, or stop running jobs
- Same task can have multiple concurrent jobs
Basic workflow:
- Open the task picker with
:Forge(or<leader>sr) - Select a task to run - output appears in a floating terminal
- Use
:Forge jobs(or<C-o>in the picker) to see running jobs - Stop jobs with
<C-x>or:Forge stop - Errors are parsed to quickfix for easy navigation
| Command | Description |
|---|---|
:Forge |
Open task picker |
:Forge run [task] |
Run a task (or open picker) |
:Forge jobs [task] |
Show running jobs |
:Forge stop [task] |
Stop jobs (all or by task) |
:Forge new |
Create a new task or bundle |
:Forge edit <task> |
Edit an existing task |
:ForgeTerm [nr] |
Toggle terminal |
Global keymaps:
| Action | Default | Description |
|---|---|---|
tasks |
<leader>sr, <leader>ss |
Open task picker |
stop_all |
<leader>sx |
Stop all jobs |
new |
<leader>sn |
Create new task/bundle |
Task picker keymaps (keymaps.picker):
| Action | Default | Description |
|---|---|---|
restart |
<C-r> |
Restart task |
stop |
<C-x> |
Stop all instances |
jobs |
<C-o> |
Show jobs for task |
edit |
<C-e> |
Edit task |
preview |
<C-f> |
Preview terminal |
background |
<C-s> |
Run in background |
tab |
<C-t> |
Run/focus in tab |
split |
<C-v> |
Run/focus in split |
Jobs picker keymaps (keymaps.jobs_picker):
| Action | Default | Description |
|---|---|---|
stop |
<C-x> |
Stop job |
restart |
<C-r> |
Restart job |
preview |
<C-f> |
Preview terminal |
dismiss |
<C-d> |
Dismiss completed job |
Keymaps can be customized or disabled:
keymaps = {
tasks = "<leader>ft", -- Custom global keymap
stop_all = false, -- Disable this keymap
picker = {
restart = "<C-y>", -- Custom picker keymap
edit = false, -- Disable edit in picker
},
}
-- Disable all keymaps
keymaps = falseTasks can specify processor configuration for proper error parsing:
{
name = "build",
cmd = "cargo build",
processors = {
errorformat = { compiler = "cargo" }, -- uses cargo's errorformat
},
}Processors can be specified as simple strings (use defaults) or tables with config:
-- Use default errorformat (generic patterns)
processors = { "errorformat" }
-- Use specific compiler's errorformat
processors = { errorformat = { compiler = "cargo" } }
-- Multiple processors
processors = { "cargo", errorformat = { compiler = "gcc" } }require("lualine").setup({
sections = {
lualine_x = {
require("forge.lualine"),
},
},
})