input-form.nvim
A small Neovim plugin for building bordered, keyboard-navigable forms in a
floating window. Create a single window containing multiple typed inputs
(single-line text, multiline text, select dropdowns), collect results via an
on_submit callback.
Features
- Bordered floating window with optional title
- Keyboard-navigable:
<Tab>/<S-Tab>to move between inputs - Input types:
text,multiline,select - Select dropdowns open with
<CR>; arrows navigate;<CR>confirms - Submit with
<C-s>— results delivered as a{ [name] = value }table - Cancel with
<Esc> - Lazy:
create_formbuilds the form;:show()renders it when you want :hide()/:show()re-open a form while preserving in-progress values- Fully configurable keymaps, border, width, title
- Auto-generated help doc (
:h input-form) - Tested with
mini.test
Installation
lazy.nvim
{
"chenasraf/input-form.nvim",
config = function()
require("input-form").setup()
end,
}
packer.nvim
use({
"chenasraf/input-form.nvim",
config = function()
require("input-form").setup()
end,
})
vim-plug
Plug 'chenasraf/input-form.nvim'
lua require('input-form').setup()
Usage
local f = require("input-form")
local form = f.create_form({
inputs = {
{ name = "id", label = "Enter ID", type = "text", default = "sample ID" },
{
name = "choice",
label = "Select an option",
type = "select",
options = {
{ id = "opt1", label = "Option 1" },
{ id = "opt2", label = "Option 2" },
},
},
{ name = "body", label = "Enter multiline text", type = "multiline" },
},
on_submit = function(results)
vim.print(results) -- { id = "...", choice = "opt1", body = "..." }
end,
on_cancel = function()
vim.notify("cancelled")
end,
})
-- Create once, show on demand:
form:show()
create_form returns a form object. Nothing is rendered until you call
form:show(). This lets you construct the form in one place and open it from a
keymap, autocommand, or anywhere else:
vim.keymap.set("n", "<leader>xf", function()
form:show()
end)
Form methods
| Method | Description |
|---|---|
form:show() |
Open the form. No-op if already visible. |
form:hide() |
Close windows but keep values so :show() resumes where you left off. |
form:close() |
Permanently tear down the form. |
form:submit() |
Gather values, close, and invoke on_submit(results). |
form:cancel() |
Close and invoke on_cancel() if provided. |
form:results() |
Return { [name] = value } without closing. |
Input spec reference
All inputs share name (string, required — the key in the result table) and
label (string, shown above the field).
text
{ name = "id", label = "Enter ID", type = "text", default = "sample ID" }
multiline
{ name = "body", label = "Notes", type = "multiline", default = "", height = 5 }
height(optional) — number of rows for the input; falls back toconfig.multiline.height.
select
{
name = "choice",
label = "Pick one",
type = "select",
default = "opt1", -- optional; defaults to first option's id
options = {
{ id = "opt1", label = "Option 1" },
{ id = "opt2", label = "Option 2" },
},
}
value() returns the selected id (not the label).
Configuration
Defaults:
require("input-form").setup({
window = {
border = "rounded", -- any nvim_open_win border
width = 60, -- number of columns; <= 1 treated as ratio
title = " Form ",
title_pos = "center",
winblend = 0,
padding = 0, -- cells between the outer border and inputs (all sides)
gap = 0, -- blank rows between adjacent inputs
},
keymaps = {
next = "<Tab>",
prev = "<S-Tab>",
submit = "<C-s>",
cancel = "<Esc>",
open_select = "<CR>",
},
select = {
max_height = 10,
},
multiline = {
height = 5,
},
})
Per-form overrides: pass title and/or width in the create_form spec.
Help
Help tags are registered automatically on the first require('input-form'),
so setup() is not required for them either:
:h input-form
For plugin developers — using input-form.nvim as a dependency
You can depend on input-form.nvim from another plugin without forcing your
users to call setup(). The module is safe to use immediately after require:
-- In your plugin's code:
local ok, input_form = pcall(require, 'input-form')
if not ok then
vim.notify('my-plugin: input-form.nvim is required', vim.log.levels.ERROR)
return
end
input_form.create_form({
inputs = { ... },
on_submit = function(results) ... end,
}):show()
Key points:
- No
setup()required. Defaults are loaded at module-load time andcreate_form/form:show()work on a barerequire('input-form'). End users of your plugin don't need to know input-form.nvim exists. - Per-form overrides. Pass
title,width,on_cancel, etc. directly in thecreate_formspec — no need to mutate global config for one-off tweaks. - Baseline config. If your plugin wants a different baseline (say, a
non-default border style for all forms it opens), call
require('input-form').setup({ ... })once during your plugin's own initialization. This is idempotent and safe to call even if the end user has already called setup — later calls deep-merge over earlier ones. - Respect the user. Prefer per-form overrides over global
setup()when possible so you don't stomp on a user who has configured input-form.nvim for their own keymaps or other plugins that use it. - Declaring the dep. With lazy.nvim, add it to your
dependencies:{ 'your-name/your-plugin.nvim', dependencies = { 'chenasraf/input-form.nvim' }, }
Contributing & development
make deps # install mini.nvim into deps/
make test # run the test suite (mini.test)
make documentation # regenerate doc/input-form.txt (mini.doc)
make lint # stylua check
License
MIT — see LICENSE.