https://github.com/artemave/vigun
Unclutter your test diet
https://github.com/artemave/vigun
electron-mocha mocha testing vim vim-plugin
Last synced: about 1 month ago
JSON representation
Unclutter your test diet
- Host: GitHub
- URL: https://github.com/artemave/vigun
- Owner: artemave
- License: mit
- Created: 2016-12-15T21:54:45.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2026-04-23T10:29:28.000Z (2 months ago)
- Last Synced: 2026-04-23T12:22:29.586Z (2 months ago)
- Topics: electron-mocha, mocha, testing, vim, vim-plugin
- Language: Lua
- Size: 162 KB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# vigun
Unclutter your test diet.
## What is this?
Neovim plugin to run tests outside of Neovim. Out of the box it dispatches to a separate tmux window; other executors can be plugged in (see [Executors](#executors)).
## Installation
Using [lazy](https://lazy.folke.io/):
```lua
require("lazy").setup({
{ "artemave/vigun" }
})
```
## Usage
Vigun comes with no mappings, but it does add the following commands:
#### VigunRun
Run test(s): `:VigunRun `. The `` is any name you define in your runner’s `commands` table (see Configuration).
If invoked from a non‑test file, `:VigunRun ` will attempt to run the last command.
#### VigunToggleTestWindowToPane
Ask the active executor to toggle its layout — for the tmux executor, that means joining the test window into the current vim window as a pane (or splitting it back out if it's already a pane). Errors if the active executor doesn't implement a layout toggle.
#### VigunShowSpecIndex
Open quickfix window to quickly navigate between the tests.
#### VigunCurrentTestBefore
Fold everything, except current test and all relevant setup code (e.g. before/beforeEach blocks).
#### VigunToggleOnly
Toggle `.only` for a current test/context/describe.
### Example bindings
Example bindings for user‑defined modes (here: `file`, `focus`, `debug-focus`):
```vim script
au FileType {ruby,javascript,typescript,cucumber} nnoremap t :VigunRun file
au FileType {ruby,javascript,typescript,cucumber} nnoremap T :VigunRun focus
au FileType {ruby,javascript,typescript,cucumber} nnoremap d :VigunRun debug-focus
```
## Configuration
### Treesitter
Vigun relies on Treesitter for test discovery and folding. Ensure Neovim has relevant parsers installed for your languages. Example:
```
:TSInstall javascript typescript ruby python
```
### Lua setup()
Configure frameworks with Lua. Call `setup()` once or multiple times; each call merges into previous configuration.
Example enabling rspec, pytest, and minitest_rails under the `runners` key with top‑level options. Command names are arbitrary and user‑defined:
```lua
require('vigun').setup({
tmux_window_name = 'test',
tmux_pane_orientation = 'vertical',
remember_last_command = true,
runners = {
rspec = {
enabled = function()
return vim.fn.expand('%'):match('_spec%.rb$') ~= nil
end,
test_nodes = { 'it', 'xit' },
context_nodes = { 'describe', 'context' },
commands = {
file = function(_)
return 'rspec ' .. vim.fn.expand('%')
end,
focus = function(_)
return 'rspec ' .. vim.fn.expand('%') .. ':' .. vim.fn.line('.')
end,
},
},
pytest = {
enabled = function()
return vim.fn.expand('%'):match('_test%.py$') ~= nil
end,
test_nodes = function(node, name)
return node and node:type() == 'function_definition' and type(name) == 'string' and name:match('^test_') ~= nil
end,
context_nodes = function(node, _)
return node and node:type() == 'class_definition'
end,
commands = {
file = function(_)
return 'pytest -s ' .. vim.fn.expand('%')
end,
focus = function(info)
local quoted = vim.fn.shellescape(info.test_title)
return 'pytest -k ' .. quoted .. ' -s ' .. vim.fn.expand('%')
end,
['debug-file'] = function(_)
return 'pytest -vv -s ' .. vim.fn.expand('%')
end,
['debug-focus'] = function(info)
local quoted = vim.fn.shellescape(info.test_title)
return 'pytest -vv -k ' .. quoted .. ' -s ' .. vim.fn.expand('%')
end,
},
},
minitest_rails = {
enabled = function()
return vim.fn.expand('%'):match('_test%.rb$') ~= nil
end,
commands = {
file = function(_)
return 'rails test ' .. vim.fn.expand('%')
end,
focus = function(_)
return 'rails test ' .. vim.fn.expand('%') .. ':' .. vim.fn.line('.')
end,
},
},
}
})
```
You can call `setup()` again (e.g., from a project `.exrc`) to override or add commands. Options are top‑level; runners live under `runners`:
```lua
require('vigun').setup({
runners = {
mocha = {
commands = {
file = function(_)
return 'electron-mocha --renderer ' .. vim.fn.expand('%')
end,
},
},
}})
```
### Options
Options are top‑level keys of `setup()`:
- `executor`: which executor to dispatch commands through (default: `'tmux'`). See [Executors](#executors).
- `tmux_window_name`: name of the tmux window for tests (default: `test`). Tmux executor only.
- `tmux_pane_orientation`: `vertical` or `horizontal` for `:VigunToggleTestWindowToPane` (default: `vertical`). Tmux executor only.
- `hop_role`: role to pass to `hop run --role` (default: `'test'`). Hop executor only.
- `remember_last_command`: rerun last command if no matching command is found (default: `true`).
### Executors
An executor is the adapter that takes a shell command and makes it run somewhere observable. Select one via the `executor` option:
```lua
require('vigun').setup({ executor = 'tmux' }) -- default
require('vigun').setup({ executor = 'hop' })
```
Built‑in executors:
- **`tmux`** — sends commands to a dedicated tmux window (`tmux_window_name`, default `test`). Supports layout toggling via `:VigunToggleTestWindowToPane`.
- **`hop`** — shells out to [`hop`](https://github.com/artemave/hop) as `hop run --focus --role `.
You can also pass a table with your own `run(cmd, on_done?)` (and optional `toggle_layout()`):
```lua
require('vigun').setup({
executor = {
run = function(cmd, on_done)
-- fire-and-forget when on_done is nil;
-- otherwise invoke on_done(output_string) when the command completes.
end,
},
})
```
### on_result callback
Attach a per‑runner `on_result` to react to a finished run.
`on_result` takes an `info` argument with the following fields:
- `command`: exact command dispatched to the executor
- `mode`: the exact mode name you invoked (whatever you defined)
- `file`: current buffer filename at start
- `output`: output of the command
- `started_at`, `ended_at`: timestamps
Example: populate diagnostics from failures
```lua
require('vigun').setup({
runners = {
rspec = {
-- ... other rspec config ...
on_result = function(info)
local ns = vim.api.nvim_create_namespace('vigun.tests')
vim.diagnostic.reset(ns)
local by_buf = {}
for path, line, msg in info.output:gmatch("([%./%w%-%_%/]+):(%d+):%s*(.-)\n") do
local bufnr = vim.fn.bufnr(vim.fn.fnamemodify(path, ':p'), true)
by_buf[bufnr] = by_buf[bufnr] or {}
table.insert(by_buf[bufnr], {
lnum = tonumber(line) - 1,
col = 0,
message = msg ~= '' and msg or 'Test failure',
severity = vim.diagnostic.severity.ERROR,
source = 'vigun',
})
end
for bufnr, items in pairs(by_buf) do
vim.diagnostic.set(ns, bufnr, items, { underline = true, virtual_text = true })
end
end,
},
},
})
```
You can adapt the parser to your runner’s format (RSpec, Minitest, PyTest, etc.) or populate quickfix/loclist instead of diagnostics.
## Running Plugin Tests
- `./run_vader_test` — run Vader specs.
- `./run_vader_test test/run_ruby_tests.vader` — run a specific Vader file.
- `./run_vader_test 'test/regressions/*.vader'` — run a subset via glob.
- `./run_lua_tests` — run Lua tests (Plenary/Busted).
- `./run_tests` — run both suites in sequence.