Markdown & CSV table editor for Neovim — structural editing, pure text, always valid.
Supports Markdown and CSV out of the box, and can be extended to other formats via custom parsers.
Edit Markdown tables structurally.
Raw → aligned → edit → back to raw (lossless).
Raw → aligned → edit → back to raw (lossless).
- Vim-first
- Text-only buffer
- Structurally safe
- Fully reversible transformation
- No hidden metadata
- Always valid
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 happens in tir-buf format.
On save:
tir-buf → TIR → original flat format
Key principles:
- The buffer contains only text
- No hidden shadow buffer
- No custom buffer type
- Transformations are reversible
- TIR is the source of truth
- Buffer structure can temporarily diverge during editing
- Structural repair is explicit and controllable
Edit tables directly in Vim while preserving a structured internal model.
- Render CSV/TSV/GFM into an aligned structured view
- Preserve original file format on save
- Toggle raw ↔ structured view
- Immediate or deferred structural repair
- Dirty line tracking and highlighting
- Multiline cell support with preserved line breaks
- Column width control with wrapping support
- Grid-aware join that preserves column structure
- Column text objects for structural editing
- Works with native Vim motions and operators
- External parser architecture (extensible)
- No learning curve
Tirenvi maintains a structured internal table model while allowing temporary buffer inconsistencies.
In deferred repair mode:
- Invalid table edits are allowed temporarily
- Dirty lines are tracked incrementally
- Internal attrs/model state remains structurally valid
- Buffer structure may temporarily diverge from the model
:Tir repairsynchronizes the buffer with the internal model
In immediate repair mode:
- Invalid structural edits are repaired automatically
- Normal mode edits are repaired immediately
- Insert mode edits are repaired after leaving Insert
Tirenvi respects Vim’s undo tree.
Historical undo states are not rewritten, even if they contain temporary structural inconsistencies.
{
"kibi2/tirenvi.nvim",
dependencies = {
"tpope/vim-repeat", -- optional: enables '.' repeat for column width operations
},
config = function()
require("tirenvi").setup {}
end,
}Plug 'kibi2/tirenvi.nvim'
" Optional: enables '.' repeat for column width operations
Plug 'tpope/vim-repeat'- Neovim >= 0.9
- UTF-8 environment
Install parsers:
pip install tir-gfm-lite
pip install tir-csv
pip install tir-pukiwikiAutomatically activates based on filetype (via parser_map):
csvtsvmarkdownpukiwiki
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" },
pukiwiki = { executable = "tir-pukiwiki", allow_plain = true, required_version = "0.1.1" },
}
})| Command | Description |
|---|---|
:Tir repair |
Repair and reformat dirty tables |
:Tir repair enable |
Enable automatic structural repair |
:Tir repair disable |
Disable automatic structural repair |
:Tir repair toggle |
Toggle automatic structural repair |
:Tir toggle |
Switch raw ↔ structured table view |
:Tir width[=+-][count] |
Adjust column width by count (=: set, +/-: increment/decrement) |
:Tir redraw |
Deprecated. Will be removed in v0.5. Use :Tir repair instead |
All native Vim editing works.
dd,yy,p,D,o,R,J, and more- Command-line command
- Visual mode command
No special editing mode.
Columns are structural units.
To modify a column:
- Select a column using text objects (
vil,val,v3al) - Apply standard operators (
d,p, etc.)
require("tirenvi").setup({
textobj = {
column = "l",
},
})Operations that would break structure are automatically corrected.
Column width operations support . repeat when
vim-repeat is installed.
Fast horizontal navigation across cells.
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.
- Not a spreadsheet
- Not a new editing mode
- Not a hidden AST editor
- Not a file-format converter
It is a structured text editor layer.
- Text objects (table, row, column, cell)
- Column formatting presets
- Outline mode
| Feature | Tirenvi | csv.vim | Spreadsheet tools |
|---|---|---|---|
| Native Vim editing | ✅ | ❌ | |
| Always structurally valid | ✅ | ❌ | |
| No file format change | ✅ | ❌ | ❌ |
| No custom buffer type | ✅ | ❌ | ❌ |
| Toggle raw view | ✅ | ❌ | ❌ |
| Markdown | ✅ | ❌ | ❌ |
| Automatic wrapping | ✅ | ❌ | |
| Grid-aware join | ✅ | ❌ | ❌ |
Tirenvi prioritizes structural safety with Vim purity.
The architecture centers around:
- flat ↔ TIR (external)
- TIR ↔ tir-buf (internal)
Large changes should respect this separation.
Please open an issue before major design proposals.
MIT License.

