Edit chezmoi templates in Neovim, preview their output, and bring static target changes back into source while preserving expressions.
This project is in alpha. APIs and behavior may change.
Template-aware diff. Compare rendered output with the actual file, transfer hunks in both directions, and choose insertion positions. MP4
Static write-back. Bring target changes into the template in bulk while preserving expressions. MP4
Live preview. Edit unsaved templates with synchronized scrolling, branch changes, undo, and error recovery. MP4
Requires Neovim 0.11+ and chezmoi 2.x. The gotmpl Tree-sitter parser is recommended for full template highlighting,
precise syntax-aware completion, template-reference navigation, and template block matching. Without it, an experimental
fallback still supports preview, diff, static write-back, and basic completion: action detection uses line-local regular
expressions and variable completion may include names from unrelated scopes. Chezmoi still validates template expressions.
Host-language parsers are optional; rendered highlighting uses Vim syntax when no host parser is available. blink.cmp is
optional unless you want Vimoi's completion source. Tested on Linux.
With lazy.nvim:
{
"dirichy/vimoi.nvim",
lazy = false,
opts = {},
}With another plugin manager, install the plugin and call require("vimoi").setup().
Set vim.g.mapleader first, then run :checkhealth vimoi after installation.
The defaults work without extra options. For example, customize lazy.nvim's opts:
opts = {
split = "horizontal", -- Stacked windows
keymaps = { readd = false }, -- Disable the default ReAdd mapping
}See :help vimoi-setup for all options. Configure the plugin once at startup;
restart Neovim after changing options.
Open a chezmoi-managed source or target, then use these commands and default mappings:
| Command | Key | Purpose |
|---|---|---|
:VimoiFiles |
<leader>cf |
Pick a managed file and open its source |
:VimoiToggle |
<leader>ct |
Switch source / target, preserving unsaved edits |
:VimoiPreview |
<leader>cp |
Preview the current template as you type, without saving |
:VimoiDiff |
<leader>cd |
Compare rendered output with the actual target |
:VimoiReAdd |
<leader>ca |
Bring target changes back into source in bulk |
:VimoiCancel |
<leader>cx |
Cancel slow tasks |
:VimoiFiles lists managed files and symlinks through vim.ui.select, reusing
your configured picker UI. Search by target paths such as .config/nvim/init.lua;
select an entry to edit its source. Use :VimoiToggle to switch to its target.
To search by source names instead, set files = { path = "source" } in setup()
(default: "target"). The list is refreshed each time and respects chezmoi's ignore
rules. Cancel a slow query with :VimoiCancel.
Path arguments are also available; see :help vimoi-commands.
Preview supports unnamed buffers and template fragments. Failed renders retain the previous
output and mark it stale. Close either window to end a diff/preview.
Use [c / ]c to navigate changes and zo / zc to show or hide context.
In a template diff:
| Current window | do: get into this window |
dp: put into the other window |
|---|---|---|
| Source template | Write target changes back to the template | Put rendered output into the target |
| Target | Get rendered template output | Write target changes back to the template |
Use the plugin's do / dp mappings or :VimoiDiffGet / :VimoiDiffPut;
native :diffget / :diffput copy template source text. Plain files retain native do / dp behavior.
:VimoiReAdd merges identifiable static changes in bulk while preserving expressions;
ambiguous positions prompt for a choice. Dynamic output needs manual source edits.
For plain text files, it replaces source content with the entire current target buffer,
including unsaved edits. Use u to undo.
Write-back changes buffers without saving or applying. Review and save with :write,
then run chezmoi apply to deploy.
Template and host-language highlighting are enabled by default; inactive branches use comment colors.
Rendered mode reads Tree-sitter or Vim syntax colors from complete output and maps them onto the template.
Set highlight.mode = "injection" to highlight host fragments directly, or "disabled"
to keep only template highlighting. Customize action backgrounds with highlight.template_bg.
In injection mode, missing host parsers use Vim syntax directly on the template as a best-effort fallback.
Full-resolution highlighting demo (MP4)
Injection highlighting (MP4) · Host highlighting disabled (MP4)
Template errors are checked as you edit. Host diagnostics reuse your enabled Neovim LSP
or nvim-lint configuration, checking rendered output and mapping errors back to source.
nvim-lint supports stdin linters only. Set diagnostics.host = false for template diagnostics only.
Set render.debounce to change the default 500ms wait for structural or rendering-context changes;
literal-only edits reuse the cached skeleton immediately.
See :help vimoi-highlighting and :help vimoi-diagnostics.
Full-resolution diagnostics demo (MP4)
Register the source in your blink.cmp configuration:
require("blink.cmp").setup({
completion = {
accept = {
auto_brackets = {
blocked_filetypes = { "gotmpl" },
},
},
},
sources = {
default = { "lsp", "path", "snippets", "buffer", "vimoi" },
providers = {
vimoi = {
name = "Vimoi",
module = "vimoi.blink",
score_offset = 100,
},
},
},
})Completes functions, data fields, local variables, paths, and block snippets. With no gotmpl parser, completion falls
back to line-local action detection and buffer-wide variable-name matching, so the menu may contain extra candidates.
blocked_filetypes prevents automatic parentheses on template function completions.
Control value masking in vimoi's setup options:
require("vimoi").setup({
completion = {
mask = function(key)
return key:lower():match("password") ~= nil or key:lower():match("token") ~= nil
end,
},
})completion.mask defaults to "hint": all values are masked, with a short
See :help vimoi-completion-mask hint. Set true to mask without the hint, or
false to show all values. A function receives the full field path without a
leading dot (for example account.token); returning true masks that value
without the hint. Masked values use [ masked], including in object previews.
Masking a parent also masks all its descendants. Lists use one-based paths such
as accounts.1.token. Previews are bounded and truncated; field-name masking
only covers paths matched by your function.
The cached data view replaces runtime-only fields before exposing it to consumers. Completion makes a private copy
and fills fields that can be determined from the current managed buffer. Value documentation comes from
that sanitized view; runtime fields such as
.chezmoi.command, .chezmoi.args, .chezmoi.sourceFile, and .chezmoi.targetFile
are shown as [runtime value] because they can differ during template execution.
Full-resolution completion demo (MP4)
In templates and symlink sources, gf follows includes, shared templates, globs, and symlink paths;
gF also supports path:line[:column]. Multiple matches open a picker; managed targets open their source preferentially.
Optional vim-matchup, with Tree-sitter matching enabled,
provides % for template blocks. See :help vimoi-navigation for path rules.
Navigation and switching recording (MP4)
Background tasks show progress after 1000ms. Literal edits rebuild from the cached
skeleton immediately; structural or rendering-context changes use a 500ms debounce.
Cancel running tasks with :VimoiCancel or <leader>cx, even before progress appears.
In a focused waiting float, use q, Esc, or Ctrl-C. Canceled pending write-back does not continue.
Task cancellation recording (MP4)
Data, configuration, and discovered shared-template changes refresh automatically.
Press <leader>cr if results remain stale. Template execution may invoke password helpers;
waits can be canceled and have no default timeout.
Full-resolution dependency refresh demo (MP4)
require("vimoi.api").diff_buffers(source_buf, target_buf, { filetype = "yaml" })
compares two loaded buffers, including unnamed and scratch buffers, with the same hunk operations and ReAdd.
See :help vimoi-api for the full API.
Buffer API and horizontal splits recording (MP4)
Both support direct source editing, live preview, template and host highlighting, blink completion, and template navigation. Their focus differs. This comparison uses chezmoi-template.nvim source, checked on 2026-10-25.
| Feature | vimoi.nvim | chezmoi-template.nvim |
|---|---|---|
| Compare and write back | Template-aware native diff, bidirectional hunks, static ReAdd | Preview annotations against the deployed file |
| Host highlighting | Rendered output projection by default, optional injection | Tree-sitter injection |
| Diagnostics | Template checks as you edit, plus LSP / nvim-lint host checks | Template execution checks on save |
| Completion | Functions, data fields, locals, paths, block snippets; optional filtered data value docs | Functions, data fields with value docs, sensitive-field masking, block snippets |
| Template formatting, target icons, transparent encryption | Not provided | Conform, mini.icons, optional decrypt/encrypt |
| File selection and deployment | Managed-file picker and source / target toggle; deploy with the CLI | Multiple file pickers, apply commands, apply on save by default |
| Default mappings | Global shortcuts, can be disabled | Opt-in source-buffer mappings |
| Neovim requirement | 0.11+ | 0.10+ |
Consider it for formatting, encryption, and deployment integrations. vimoi focuses on comparing rendered output and bringing static changes from actual files back into templates. See the implementation comparison and historical research (Chinese).
- Changes to dynamic or repeated output and
define/blockbodies usually need manual source edits. - Custom template delimiters and templates producing different results under
chezmoi catandapplyare unsupported. - Encrypted files, symlinks, and script sources are unsupported by plain text ReAdd.
- Formatting, icons, file browsing, and apply commands are not provided; deploy with the chezmoi CLI.
- Transparent editing of chezmoi-encrypted source is not provided. For repository encryption, we recommend git-crypt: unlocked templates are directly editable, and the local working tree is plaintext. See chezmoi transparent encryption.
See :help vimoi-limitations for further constraints.
Run :checkhealth vimoi to check your setup, or :help vimoi.nvim for the full manual.
Recording demos · Development notes · Roadmap
MIT, Copyright (c) 2026 dirichy.










