2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:24:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00
2026-04-05 00:22:58 +03:00

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_form builds 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 to config.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 and create_form / form:show() work on a bare require('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 the create_form spec — 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.

Description
No description provided
Readme MIT 470 KiB
Languages
Lua 98.2%
Makefile 1.8%