Markdown & CSV table editor for Neovim — structural editing, pure text, always valid.
Supports Markdown and CSV out of the box.
Edit tables anywhere — including arbitrary text files via the Tir-embedded format.

Edit Markdown tables structurally.
Raw → aligned → edit → back to raw (lossless).

Raw → aligned → edit → back to raw (lossless).
Edit tables embedded inside source code, configuration files, or plain text documents without changing the surrounding text.
For example, tirenvi can edit tables embedded in Python source code:
# | Name | City |
# | ----- | ------ |
# | Alice | Tokyo |
# | Bob | Osaka |
print("Hello Tir")
Use :Tir toggle to switch between the original text and the structured table view.
For details about the embedded table format, see the tir-embedded documentation.
Demo coming soon.
Markdown and CSV are text — but they represent structure.
Tirenvi lets you edit tabular data structurally, while staying in Vim’s native editing model.
You edit text. Tirenvi preserves structure.
At the center is: TIR — Tabular Intermediate Representation
flat (gfm markdown, csv, tsv, ...)
↓ external parser
TIR (intermediate representation)
↓ tirenvi
tir-buf (structured buffer view)
Editing takes place tir-buf format.
On save:
tir-buf → TIR → original flat format
Key principles:
Edit tables directly in Vim while preserving a structured internal model.
Tirenvi maintains a structured internal table model while allowing temporary buffer inconsistencies.
In deferred repair mode:
:Tir redraw synchronizes the buffer with the internal modelIn immediate repair mode:
Tirenvi respects Vim’s undo tree.
Historical undo states are not rewritten, even if they contain temporary structural inconsistencies.
{
"kibi2/tirenvi.nvim",
dependencies = {
{ "kibi2/tir-csv", build = "pip install ." },
{ "kibi2/tir-gfm-lite", build = "pip install ." },
{ "kibi2/tir-embedded", build = "pip install ." },
{ "tpope/vim-repeat" }, -- Optional: enables '.' repeat for column width operations
},
config = function()
require("tirenvi").setup {}
end,
}
The required parser packages are installed automatically when using lazy.nvim.
Plug 'kibi2/tirenvi.nvim'
" Required parsers
Plug 'kibi2/tir-csv' { 'do': 'pip install .' }
Plug 'kibi2/tir-gfm-lite' { 'do': 'pip install .' }
Plug 'kibi2/tir-embedded' { 'do': 'pip install .' }
" Optional: enables '.' repeat for column width operations
Plug 'tpope/vim-repeat'
PukiWiki support is optional.
pip install tir-pukiwiki
Automatically activates based on filetype (via parser_map):
csvtsvmarkdownpukiwikiFor embedded tables, use :Tir toggle to switch
between the original text and the structured table view.
Custom parser mapping:
require("tirenvi").setup({
parser_map = {
csv = { executable = "tir-csv", required_version = "0.1.4" },
tsv = { executable = "tir-csv", options = { "--delimiter", "\t" }, required_version = "0.1.4" },
markdown = { executable = "tir-gfm-lite", allow_plain = true, required_version = "0.1.6" },
["*"] = { executable = "tir-embedded", allow_plain = true, embedded = true },
pukiwiki = { executable = "tir-pukiwiki", allow_plain = true, required_version = "0.1.1" },
}
})
| Command | Description |
|---|---|
:Tir width{=+-}[count] |
Adjust column width by count (=: set, +/-: increment/decrement) |
:Tir width? |
Show the current table width settings. |
:Tir fit{=+-}[count] |
Adjust table span by count (=: set, +/-: increment/decrement) |
:Tir wrap |
Toggle nowrap ↔ wrap|auto width mode |
:Tir redraw |
Redraw and reformat dirty tables |
:Tir toggle |
Toggle raw ↔ structured table view |
:Tir repair enable |
Enable automatic structural repair |
:Tir repair disable |
Disable automatic structural repair |
:Tir repair toggle |
Toggle automatic structural repair |
:Tir repair |
Deprecated. Will be removed in v0.6.Use :Tir redraw instead |
Operations that would break structure are automatically corrected.
Column width operations support . repeat when
vim-repeat is installed.
Native Vim editing commands work as usual.
dd, yy, p, D, o, R, J, and moreNo special editing mode.
Use:
:Tir width?
Example output:
mode=nowrap span=23 col=1/3 header="Name" widths=[5*,3,11]
This displays the current width mode, table span, selected column, column widths, and other table information.
Tirenvi supports three width modes:
D at the beginning of a table row to split the table.D at the beginning of the second column to clear the cell contents.Columns are structural units.
To modify a column:
vil, val, v3al)d, p, etc.)require("tirenvi").setup({
textobj = {
column = "l",
cell = "c",
},
})
Cells are structural units within a table.
To modify a cell:
vic, vac, v3ac)d, y, p, etc.)vic selects the cell content without the left border, while
vac also includes the left border.
Wrapped cells are selected across multiple lines.
Navigate through the logical structure of a TIR buffer.
vim.keymap.set({ 'n', 'o', 'x' }, '<C-h>', require('tirenvi').motion.cell_prev,
{ expr = true, desc = 'TirEnvi: previous cell' })
vim.keymap.set({ 'n', 'o', 'x' }, '<C-l>', require('tirenvi').motion.cell_next,
{ expr = true, desc = 'TirEnvi: next cell' })
vim.keymap.set({ 'n', 'o', 'x' }, '<C-k>', require('tirenvi').motion.block_top,
{ expr = true, desc = 'TirEnvi: block top' })
vim.keymap.set({ 'n', 'o', 'x' }, '<C-j>', require('tirenvi').motion.block_bottom,
{ expr = true, desc = 'TirEnvi: block bottom' })
Ctrl-h / Ctrl-l: previous / next cellCtrl-k / Ctrl-j: top / bottom of blockThese motions support counts and Visual mode.
Navigate horizontally by table separators.
vim.keymap.set({ 'n', 'o', 'x' }, '<leader>tf', require('tirenvi').motion.f, { expr = true })
vim.keymap.set({ 'n', 'o', 'x' }, '<leader>tF', require('tirenvi').motion.F, { expr = true })
vim.keymap.set({ 'n', 'o', 'x' }, '<leader>tt', require('tirenvi').motion.t, { expr = true })
vim.keymap.set({ 'n', 'o', 'x' }, '<leader>tT', require('tirenvi').motion.T, { expr = true })
They behave like Vim's f/F/t/T,
but target table separators.
; and , continue to repeat as usual.
Note: There are two types of pipes (continuation and non-continuation), so motions may behave differently across lines.
It is a structured text editor layer.
sum, count, ave, max, min, sequence numbers)hydra.nvim| Feature | Tirenvi | csv.vim | Spreadsheet tools |
|---|---|---|---|
| Native Vim editing | ✅ | ⚠️ | ❌ |
| Always structurally valid | ✅ | ❌ | ⚠️ |
| No file format change | ✅ | ❌ | ❌ |
| No custom buffer type | ✅ | ❌ | ❌ |
| Toggle raw view | ✅ | ❌ | ❌ |
| Automatic wrapping | ✅ | ❌ | ⚠️ |
| Grid-aware join | ✅ | ❌ | ❌ |
| Markdown | ✅ | ❌ | ❌ |
| Embedded table | ✅ | ❌ | ❌ |
Tirenvi prioritizes structural safety with Vim purity.
The architecture centers around:
Large changes should respect this separation.
Please open an issue before major design proposals.
MIT License.