Skip to content

Commit ee58093

Browse files
authored
v0.2.0 - multiline improved (#37)
Fixed-width columns: Support for org-style alignment tags (<l30>, <c10>, <r20>) Tags specify alignment (left/center/right) and column width Automatic text wrapping for cells exceeding column width Automatic multiline row detection: detects if a table has multiline rows by checking for separator patterns Enabled when multiline option is set to 'auto' (now the default) Multiline formatting options: New multiline_format option with values: 'align', 'wrap', 'block_align', 'block_wrap' (default), 'paragraph_wrap'
1 parent 593cfe6 commit ee58093

23 files changed

Lines changed: 721 additions & 323 deletions

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,38 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [v0.2.0] - 2026-02-19
6+
7+
### Added
8+
- **Fixed-width columns**: Support for org-style alignment tags (`<l30>`, `<c10>`, `<r20>`)
9+
- Tags specify alignment (left/center/right) and column width
10+
- Automatic text wrapping for cells exceeding column width
11+
- **Automatic multiline row detection**:
12+
- detects if a table has multiline rows by checking for separator patterns
13+
- Enabled when `multiline` option is set to `'auto'` (now the default)
14+
- **Multiline formatting options**:
15+
- New `multiline_format` option with values: `'align'`, `'wrap'`, `'block_align'`, `'block_wrap'` (default), `'paragraph_wrap'`
16+
- Cell editor now automatically fills missing multiline rows when editing cells
17+
18+
### Fixed
19+
- Sort functionality now works correctly with right-aligned columns
20+
- Org-style alignment detection no longer triggers false positives on empty lines
21+
- Improved table parser to handle column bounds for visual block operations
22+
- Fixed cleanup in table drawing lines to clear removed data
23+
- Fixed alignment row identification when placement align_sep_id is unset
24+
25+
### Changed
26+
- **BREAKING**: Changed `multiline` option default from `v:false` to `'auto'`
27+
- Now auto-detects multiline rows based on separator patterns in the table
28+
- Set to `v:true` or `v:false` for explicit control
29+
- **BREAKING**: Replaced `preserve_indentation` with `multiline_format` option
30+
- `:TableOption` command renamed to `:TableConfig`.
31+
- org-style alignment tags take precedence over alignment header
32+
33+
### Deprecated
34+
- `:TableOption` command is deprecated in favor of `:TableConfig`
35+
- `:TableOption` still works but shows a deprecation warning
36+
537
## [v0.1.1] - 2026-02-15
638

739
### Fixed

README.md

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# table.vim
22

3-
Your one-stop shop for all your table editing needs in Vim and Neovim. Supports
4-
multi-line rows and features a cell editing window for editing cell content in a
5-
separate buffer (Neovim users get a seamless floating window)
3+
Advanced table editing for Vim and Neovim. Easily create, edit, and format
4+
tables with support for multi-line rows, fixed-width columns, sorting, and
5+
various table styles. Edit cell content in a separate window for enhanced
6+
control. Highly configurable and extensible.
67

78
## Quick Start
89

910
Create tables using pipes `|` and dashes `-`. The table is aligned and redrawn
10-
automatically on pipe insertion. Table style is configurable.
11+
automatically on pipe insertion. Table style is configurable, "double" is shown
12+
here:
1113

1214
```
1315
|Header 1| Header 2|Header 3| ║ Header 1 ║ Header 2 ║ Header 3 ║
@@ -26,7 +28,7 @@ And may be completed to:
2628
```
2729

2830
- Use `:Table <action>` to perform table actions like completion, sorting, and style conversion.
29-
- Use `:TableOption <option>` to view and change configuration at runtime, such as enabling multiline rows or changing the table style.
31+
- Use `:TableConfig <option>` to view and change configuration at runtime, such as enabling multiline rows or changing the table style.
3032

3133
See [`:help table.txt`](doc/table.txt) for complete documentation.
3234

@@ -35,9 +37,16 @@ See [`:help table.txt`](doc/table.txt) for complete documentation.
3537
- Vim 8.1 or later
3638
- Neovim 0.11.5 or later
3739

40+
## Upgrading from v0.1.x
41+
42+
- **Multiline rows** are now auto-detected by default. Set `multiline` to `true` or `false` to override.
43+
- **`:TableOption`** is deprecated. Use **`:TableConfig`** instead.
44+
- If you used `preserve_indentation`, switch to `multiline_format` (see `:help table-configuration`). Default behavior is unchanged.
45+
3846
## Features
3947

40-
- **Multiline rows** - must be enabled in your configuration
48+
- **Multiline rows** - auto-detected by default
49+
- **Fixed-width columns** - hard-wrap columns with alignment tags
4150
- **Cell editing window** - edit in a floating window, hooks provided (split window in Vim)
4251
- **Sorting** - sort rows and columns by any column/row
4352
- **Table navigation** - move between cells even if the table is not yet aligned
@@ -46,26 +55,27 @@ See [`:help table.txt`](doc/table.txt) for complete documentation.
4655

4756
## Demo
4857

58+
Note: This demo is from an older version, options have changed.
4959
https://github.com/user-attachments/assets/352e23b0-33ba-4f9d-9fa0-e2aee5fd16cc
5060

5161
## Configuration (optional)
5262

5363
Configuration is **buffer-local**. Set defaults in your vimrc, customize
54-
per-filetype in after/ftplugin files, or change at runtime with `:TableOption`.
64+
per-filetype in after/ftplugin files, or change at runtime with `:TableConfig`.
5565

5666
```vim
5767
" .vimrc - set defaults for all buffers (overridden by ftplugins)
5868
call table#Setup({
5969
\ 'style': 'default',
60-
\ 'options': {'multiline': v:true}
70+
\ 'options': {'multiline': 'auto', 'multiline_format': 'block_wrap'}
6171
\ })
6272
```
6373

6474
```lua
6575
-- init.lua - set defaults for all buffers (overridden by ftplugins)
6676
require('table_vim').setup({
6777
style = 'default',
68-
options = { multiline = true }
78+
options = { multiline = 'auto', multiline_format = 'block_wrap' }
6979
})
7080
```
7181

@@ -93,7 +103,7 @@ keybindings work normally.
93103

94104
### Navigation
95105

96-
- `<Tab>` / `<S-Tab>` - Next/previous cell (wraps rows)
106+
- `<Tab>` / `<S-Tab>` - Next/previous cell
97107
- `<C-h>` / `<C-j>` / `<C-k>` / `<C-l>` - Navigate left/down/up/right
98108

99109
### Text Objects
@@ -123,7 +133,7 @@ nnoremap <leader>te <Plug>(table_cell_edit)
123133

124134
## Commands
125135

126-
Two top level commands are defined, `:Table` and `:TableOption`. Tab-completion
136+
Two top level commands are defined, `:Table` and `:TableConfig`. Tab-completion
127137
is available for all subcommands and arguments.
128138

129139
### `:Table` - Table Actions
@@ -138,19 +148,21 @@ is available for all subcommands and arguments.
138148
:Table ToStyle {style} " Convert to specified style and update buffer style
139149
```
140150

141-
### `:TableOption` - Runtime Configuration
151+
### `:TableConfig` - Runtime Configuration
142152

143153
Runtime configuration for the current buffer. Use without arguments to show
144154
current configuration.
145155

146156
```vim
147-
:TableOption " Show all current settings
148-
:TableOption Style [name] " Get/set style
149-
:TableOption Option [key] [value] " Get/set option
150-
:TableOption StyleOption [key] [value] " Get/set style option
151-
:TableOption RegisterStyle [name] " Register current style (session only)
157+
:TableConfig " Show all current settings
158+
:TableConfig Style [name] " Get/set style
159+
:TableConfig Option [key] [value] " Get/set option
160+
:TableConfig StyleOption [key] [value] " Get/set style option
161+
:TableConfig RegisterStyle [name] " Register current style (session only)
152162
```
153163

164+
**Note:** `:TableOption` is deprecated, use `:TableConfig` instead.
165+
154166
**Note:** Style registration is only for the current session. Add the
155167
registration to your vimrc/init.lua for persistence.
156168

@@ -166,6 +178,38 @@ Sort table rows by a specific column or sort columns by a specific row:
166178

167179
See `:help :Table-SortRows` and `:help :Table-SortCols`
168180

181+
## Fixed-Width Columns
182+
183+
Set column widths using alignment tags. Tags use the format
184+
`<[alignment][width]>` where alignment is `l` (left), `c` (center), or `r`
185+
(right), and width is a positive integer. Columns are padded to the specified
186+
width, and, if `multiline` is enabled and a `multiline_format` wrap option is
187+
set, long columns are also hard-wrapped to the specified width.
188+
189+
```vim
190+
| Description | Column 1 width |
191+
|--------------------|---------------------|
192+
| <l30> | <c10> |
193+
| Next col will wrap | This cell will wrap |
194+
195+
196+
| Description | Column 1 |
197+
| | width |
198+
|--------------------|-----------|
199+
| <l30> | <c10> |
200+
| Next col will wrap | This cell |
201+
| | will wrap |
202+
```
203+
204+
Enable wrapping in configuration:
205+
```vim
206+
call table#Setup({
207+
\ 'options': {'multiline': 'auto', 'multiline_format': 'block_wrap'}
208+
\ })
209+
```
210+
211+
See `:help table-fixed-width` for details.
212+
169213
## Chunk Processing
170214

171215
For performance, the align action (auto-align and `:Table Align`) only processes

TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
* table caching is disables in favor of chunk processing. re-enable later if needed.
2+
* validate config option values

autoload/table.vim

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
let s:save_cpo = &cpo
22
set cpo&vim
33

4-
let g:table_version = '0.1.1'
4+
let g:table_version = '0.2.0'
55

66
function! table#Version() abort
77
return g:table_version
@@ -31,11 +31,21 @@ function! table#AlignIfNotEscaped() abort
3131
if char_before_pipe ==# '\'
3232
return
3333
else
34-
let coord = s:GetCursorLineCoord()
34+
let cur_pos = getpos('.')[1:2]
35+
let cfg_opts = table#config#Config(bufnr('%')).options
36+
let table = table#table#Get(cur_pos[0], cfg_opts.chunk_size)
37+
if !table.valid
38+
return
39+
endif
40+
let coord = table#cursor#GetCoord(table, cur_pos)
41+
if coord.type ==# 'alignment'
42+
let coord = table#cursor#GetCoord(table, cur_pos, {'type_override': 'separator'})
43+
endif
44+
" if char under cursor before insertion is a pipe, offset by one for correct coordinate
3545
let char_under_cursor = getline('.')[col('.') - 1]
3646
let coord.coord[-1] -= ( char_under_cursor ==# '|' )? 1 : 0
37-
call table#Align(line('.'))
38-
call s:SetCursorLineCoord(coord)
47+
let table = table#draw#CurrentlyPlaced(table)
48+
call table#cursor#SetCoord(table, coord)
3949
endif
4050
endfunction
4151

@@ -85,12 +95,13 @@ endfunction
8595

8696
function! table#CycleCursor(dir, count1) abort
8797
let curpos = getpos('.')[1:2]
88-
let table = table#table#Get(curpos[0], [0,0])
98+
let table = table#table#Get(curpos[0], [-1,0])
8999
if !table.valid
90100
return
91101
endif
92102
let coord = table#cursor#GetCoord(table, getpos('.')[1:2], {'dir': a:dir})
93103
if coord.type ==# 'separator'
104+
let table = table#table#Get(curpos[0], [0,0])
94105
let coord = table#cursor#GetCoord(table, getpos('.')[1:2], {'type_override': 'cell'})
95106
endif
96107
for _ in range(a:count1)
@@ -119,27 +130,6 @@ function! table#Sort(linenr, dim_kind, id, flags) abort
119130
call table#draw#CurrentlyPlaced(table)
120131
endfunction
121132

122-
function! s:GetCursorLineCoord() abort
123-
let table = table#table#Get(line('.'), [0,0])
124-
return table#cursor#GetCoord(table, getpos('.')[1:2])
125-
endfunction
126-
127-
function! s:SetCursorLineCoord(coord) abort
128-
let table = table#table#Get(line('.'), [0,0])
129-
if a:coord.type ==# 'cell'
130-
let a:coord.coord[0] = 0
131-
else
132-
let sep_id = (table.placement.bounds[0] - line('.')) < 0? 0 : -1
133-
if a:coord.type ==# 'alignment'
134-
let a:coord.type= 'separator'
135-
let a:coord.coord = [ sep_id, (a:coord.coord[0]+1)/2 ]
136-
elseif a:coord.type ==# 'separator'
137-
let a:coord.coord[0] = sep_id
138-
endif
139-
endif
140-
call table#cursor#SetCoord(table, a:coord)
141-
endfunction
142-
143133
function! s:UpdateOnCycleWrapCell(table, dir, coord) abort
144134
let new_table = a:table
145135
let new_coord = a:coord
@@ -176,7 +166,8 @@ endfunction
176166
function! s:CycleCursor(table, dir, coord) abort
177167
let step = (a:dir ==# 'forward') ? 1 : -1
178168
if a:coord.type ==# 'alignment'
179-
let n = 2*len(a:table.col_align)
169+
let align_id = a:table.placement.align_sep_id
170+
let n = 2*( len(a:table.placement.positions[align_id]['separator_pos']) - 1 )
180171
let a:coord.coord[0] = (a:coord.coord[0] + step + n) % n
181172
elseif a:coord.type ==# 'cell'
182173
let row_bound = a:table.RowCount()

autoload/table/cell_editor.vim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ function! s:OnWinClosed(tbl_id) abort
105105
endif
106106

107107
call s:UpdateCell(tbl, cell_id, bufnr)
108-
call table#draw#CurrentlyPlaced(tbl)
108+
call table#draw#CurrentlyPlaced(tbl, {'fill_multiline_gaps': v:true})
109109
augroup table.vim
110110
autocmd!
111111
augroup END

autoload/table/commands.vim

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ function! table#commands#TableComplete(ArgLead, CmdLine, CursorPos) abort
5858
return []
5959
endfunction
6060

61-
" :TableOption command - for configuration
62-
function! table#commands#TableOptionCommand(...) abort
61+
" :TableConfig command - for configuration
62+
function! table#commands#TableConfigCommand(...) abort
6363
if a:0 == 0
6464
let subcommands = ['Option', 'StyleOption', 'Style', 'RegisterStyle']
65-
echomsg 'TableOption subcommands: ' .. join(subcommands, ', ')
65+
echomsg 'TableConfig subcommands: ' .. join(subcommands, ', ')
6666
echomsg ' '
6767
echomsg 'table.vim ' .. table#Version()
6868
echomsg 'Configuration for buffer ' .. (bufname('%') !=# '' ? bufname('%') : bufnr('%'))
@@ -87,12 +87,12 @@ function! table#commands#TableOptionCommand(...) abort
8787
call s:RegisterStyle(args)
8888
else
8989
echohl ErrorMsg
90-
echomsg "TableOption: unknown subcommand '" .. subcommand .. "'"
90+
echomsg "TableConfig: unknown subcommand '" .. subcommand .. "'"
9191
echohl None
9292
endif
9393
endfunction
9494

95-
function! table#commands#TableOptionComplete(ArgLead, CmdLine, CursorPos) abort
95+
function! table#commands#TableConfigComplete(ArgLead, CmdLine, CursorPos) abort
9696
let parts = split(a:CmdLine, '\s\+', 1)
9797
let num_args = len(parts) - 1
9898

@@ -236,8 +236,10 @@ function! s:CompleteOption(ArgLead, CmdLine, CursorPos) abort
236236
let option_key = parts[1]
237237
if option_key ==# 'default_alignment'
238238
return filter(['left', 'center', 'right'], 'v:val =~? "^" .. a:ArgLead')
239-
elseif option_key =~# 'multiline\|indentation'
240-
return filter(['true', 'false'], 'v:val =~? "^" .. a:ArgLead')
239+
elseif option_key ==# 'multiline'
240+
return filter(['auto', 'true', 'false'], 'v:val =~? "^" .. a:ArgLead')
241+
elseif option_key ==# 'multiline_format'
242+
return filter(['align', 'wrap', 'block_align', 'block_wrap', 'paragraph_wrap'], 'v:val =~? "^" .. a:ArgLead')
241243
endif
242244
endif
243245
return []
@@ -315,5 +317,11 @@ function! s:ParseSortArgs(type, bang, args) abort
315317
return sort_args
316318
endfunction
317319

320+
function! table#commands#TableOptionCommand(...) abort
321+
echom 'TableOption command is deprecated, use TableConfig instead'
322+
echom ''
323+
call call('table#commands#TableConfigCommand', a:000)
324+
endfunction
325+
318326
let &cpo = s:save_cpo
319327
unlet s:save_cpo

autoload/table/compat.vim

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,10 @@ function! table#compat#virtcol2col(winid, lnum, col) abort
3131
return virtcol2col(a:winid, a:lnum, a:col)
3232
endif
3333

34-
" input col and output byte are 1-based, internal computations are 0-based
3534
let bufnr = winbufnr(a:winid)
3635
let line = table#compat#getbufoneline(bufnr, a:lnum)
37-
let byte = 0
38-
let vcol = 0
39-
let idx = 0
40-
while byte < len(line) && vcol < a:col-1
41-
let char = strcharpart(line, idx, 1)
42-
let idx += 1
43-
let vcol += strdisplaywidth(char, vcol)
44-
if vcol > a:col-1
45-
break
46-
endif
47-
let byte += len(char)
48-
endwhile
49-
return byte+1
36+
" input col and output byte are 1-based, internal computations are 0-based
37+
return table#util#DisplayWidthToByteWidth(line, a:col-1) + 1
5038
endfunction
5139

5240
let &cpo = s:save_cpo

0 commit comments

Comments
 (0)