Skip to content

Commit fcea077

Browse files
chore(refactor): separate table delimiter from overall column metadata
1 parent 9075055 commit fcea077

4 files changed

Lines changed: 74 additions & 76 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
[b3efd64](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/b3efd6408e4e4d66d6caaee0579e72b579bc0884)
2525
- handle rendering empty buffers [#597](https://github.com/MeanderingProgrammer/render-markdown.nvim/pull/597)
2626
[1c95813](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/1c958131c083c8557ea499fdb08c88b8afb05c4e)
27+
- remove pipe_table.filler config and RenderMarkdownTableFill highlight [9075055](https://github.com/MeanderingProgrammer/render-markdown.nvim/commit/907505549edc2f90c82fc429348af03ee8c3a825)
2728

2829
### Collaborator Shoutouts
2930

doc/render-markdown.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*render-markdown.txt* For NVIM v0.11.6 Last change: 2026 March 05
1+
*render-markdown.txt* For NVIM v0.11.6 Last change: 2026 March 07
22

33
==============================================================================
44
Table of Contents *render-markdown-table-of-contents*

lua/render-markdown/health.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ local state = require('render-markdown.state')
66
local M = {}
77

88
---@private
9-
M.version = '8.11.14'
9+
M.version = '8.11.15'
1010

1111
function M.check()
1212
M.start('versions')

lua/render-markdown/render/markdown/table.lua

Lines changed: 71 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@ local log = require('render-markdown.core.log')
44
local str = require('render-markdown.lib.str')
55

66
---@class render.md.table.Data
7-
---@field delim render.md.table.delim.Row
8-
---@field rows render.md.table.body.Row[]
7+
---@field delim render.md.Node
8+
---@field cols render.md.table.Col[]
9+
---@field rows render.md.table.Row[]
910

10-
---@class render.md.table.delim.Row
11-
---@field node render.md.Node
12-
---@field cols render.md.table.delim.Col[]
13-
14-
---@class render.md.table.delim.Col
11+
---@class render.md.table.Col
1512
---@field width integer
1613
---@field alignment render.md.table.Alignment
1714

@@ -23,12 +20,12 @@ local Alignment = {
2320
default = 'default',
2421
}
2522

26-
---@class render.md.table.body.Row
23+
---@class render.md.table.Row
2724
---@field node render.md.Node
2825
---@field pipes render.md.Node[]
29-
---@field cols render.md.table.body.Col[]
26+
---@field cells render.md.table.Cell[]
3027

31-
---@class render.md.table.body.Col
28+
---@class render.md.table.Cell
3229
---@field node render.md.Node
3330
---@field width integer
3431
---@field space render.md.table.Space
@@ -37,7 +34,7 @@ local Alignment = {
3734
---@field left integer
3835
---@field right integer
3936

40-
---@class render.md.table.Row
37+
---@class render.md.table.row.Parts
4138
---@field pipes render.md.Node[]
4239
---@field cells render.md.Node[]
4340

@@ -56,7 +53,7 @@ function Render:setup()
5653
end
5754

5855
-- ensure delimiter and rows exist
59-
local delim_node = nil ---@type render.md.Node?
56+
local delim = nil ---@type render.md.Node?
6057
local row_nodes = {} ---@type render.md.Node[]
6158
local types = {
6259
delim = 'pipe_table_delimiter_row',
@@ -65,7 +62,7 @@ function Render:setup()
6562
}
6663
self.node:for_each_child(function(node)
6764
if node.type == types.delim then
68-
delim_node = node
65+
delim = node
6966
elseif self.context.view:overlaps(node:get()) then
7067
if vim.tbl_contains(types.row, node.type) then
7168
row_nodes[#row_nodes + 1] = node
@@ -74,21 +71,19 @@ function Render:setup()
7471
end
7572
end
7673
end)
77-
if not delim_node or #row_nodes == 0 then
74+
if not delim or #row_nodes == 0 then
7875
return false
7976
end
8077

81-
-- double check delimiter exists after parsing
82-
local delim = self:parse_delim_row(delim_node)
83-
if not delim then
78+
local cols = self:parse_cols(delim)
79+
if not cols then
8480
return false
8581
end
8682

87-
-- double check rows exist after parsing
88-
local rows = {} ---@type render.md.table.body.Row[]
83+
local rows = {} ---@type render.md.table.Row[]
8984
table.sort(row_nodes)
9085
for _, row_node in ipairs(row_nodes) do
91-
local row = self:parse_body_row(row_node, #delim.cols)
86+
local row = self:parse_row(row_node, #cols)
9287
if row then
9388
rows[#rows + 1] = row
9489
end
@@ -97,41 +92,40 @@ function Render:setup()
9792
return false
9893
end
9994

100-
-- store the max width in the delimiter
95+
-- store the max width in cols
10196
for _, row in ipairs(rows) do
102-
for i, col in ipairs(row.cols) do
103-
local space = col.space.left + col.space.right
97+
for i, cell in ipairs(row.cells) do
98+
local space = cell.space.left + cell.space.right
10499
local available = space - (2 * self.config.padding)
105100
-- if we don't have enough space for padding add it to the width
106-
local width = col.width
101+
local width = cell.width
107102
if available < 0 then
108103
width = width - available
109104
end
110105
if self.config.cell == 'trimmed' then
111106
width = width - math.max(available, 0)
112107
end
113-
local delim_col = delim.cols[i]
114-
delim_col.width = math.max(delim_col.width, width)
108+
cols[i].width = math.max(cols[i].width, width)
115109
end
116110
end
117111

118-
self.data = { delim = delim, rows = rows }
112+
self.data = { delim = delim, cols = cols, rows = rows }
119113

120114
return true
121115
end
122116

123117
---@private
124118
---@param node render.md.Node
125-
---@return render.md.table.delim.Row?
126-
function Render:parse_delim_row(node)
127-
local row = self:parse_row(node, 'pipe_table_delimiter_cell')
128-
if not row then
119+
---@return render.md.table.Col[]?
120+
function Render:parse_cols(node)
121+
local parts = self:parse_row_parts(node, 'pipe_table_delimiter_cell')
122+
if not parts then
129123
return nil
130124
end
131-
local cols = {} ---@type render.md.table.delim.Col[]
132-
for i, cell in ipairs(row.cells) do
133-
local start_col = row.pipes[i].end_col
134-
local end_col = row.pipes[i + 1].start_col
125+
local cols = {} ---@type render.md.table.Col[]
126+
for i, cell in ipairs(parts.cells) do
127+
local start_col = parts.pipes[i].end_col
128+
local end_col = parts.pipes[i + 1].start_col
135129
local width = end_col - start_col
136130
assert(width >= 0, 'invalid table layout')
137131
if self.config.cell == 'padded' then
@@ -144,8 +138,7 @@ function Render:parse_delim_row(node)
144138
alignment = Render.alignment(cell),
145139
}
146140
end
147-
---@type render.md.table.delim.Row
148-
return { node = node, cols = cols }
141+
return cols
149142
end
150143

151144
---@private
@@ -168,23 +161,23 @@ end
168161
---@private
169162
---@param node render.md.Node
170163
---@param num_cols integer
171-
---@return render.md.table.body.Row?
172-
function Render:parse_body_row(node, num_cols)
173-
local row = self:parse_row(node, 'pipe_table_cell')
174-
if not row or #row.cells ~= num_cols then
164+
---@return render.md.table.Row?
165+
function Render:parse_row(node, num_cols)
166+
local parts = self:parse_row_parts(node, 'pipe_table_cell')
167+
if not parts or #parts.cells ~= num_cols then
175168
return nil
176169
end
177-
local cols = {} ---@type render.md.table.body.Col[]
178-
for i, cell in ipairs(row.cells) do
170+
local cells = {} ---@type render.md.table.Cell[]
171+
for i, cell in ipairs(parts.cells) do
179172
-- account for double width glyphs by replacing cell range with width
180-
local start_col = row.pipes[i].end_col
181-
local end_col = row.pipes[i + 1].start_col
173+
local start_col = parts.pipes[i].end_col
174+
local end_col = parts.pipes[i + 1].start_col
182175
local width = (end_col - start_col)
183176
- (cell.end_col - cell.start_col)
184177
+ self.context:width(cell)
185178
+ self.config.cell_offset({ node = cell:get() })
186179
assert(width >= 0, 'invalid table layout')
187-
cols[#cols + 1] = {
180+
cells[#cells + 1] = {
188181
node = cell,
189182
width = width,
190183
space = {
@@ -195,15 +188,15 @@ function Render:parse_body_row(node, num_cols)
195188
},
196189
}
197190
end
198-
---@type render.md.table.body.Row
199-
return { node = node, pipes = row.pipes, cols = cols }
191+
---@type render.md.table.Row
192+
return { node = node, pipes = parts.pipes, cells = cells }
200193
end
201194

202195
---@private
203196
---@param node render.md.Node
204197
---@param cell_type string
205-
---@return render.md.table.Row?
206-
function Render:parse_row(node, cell_type)
198+
---@return render.md.table.row.Parts?
199+
function Render:parse_row_parts(node, cell_type)
207200
local pipes = {} ---@type render.md.Node[]
208201
local cells = {} ---@type render.md.Node[]
209202
node:for_each_child(function(child)
@@ -220,7 +213,7 @@ function Render:parse_row(node, cell_type)
220213
end
221214
table.sort(pipes)
222215
table.sort(cells)
223-
---@type render.md.table.Row
216+
---@type render.md.table.row.Parts
224217
return { pipes = pipes, cells = cells }
225218
end
226219

@@ -237,10 +230,12 @@ end
237230

238231
---@private
239232
function Render:delimiter()
240-
local delim, border = self.data.delim, self.config.border
233+
local delim = self.data.delim
234+
local border = self.config.border
235+
local indicator = self.config.alignment_indicator
241236

242-
local indicator, icon = self.config.alignment_indicator, border[11]
243-
local parts = iter.list.map(delim.cols, function(col)
237+
local icon = border[11]
238+
local parts = iter.list.map(self.data.cols, function(col)
244239
-- must have enough space to put the alignment indicator
245240
-- alignment indicator must be exactly one character wide
246241
-- do not put an indicator for default alignment
@@ -261,17 +256,17 @@ function Render:delimiter()
261256
local delimiter = border[4] .. table.concat(parts, border[5]) .. border[6]
262257

263258
local line = self:line()
264-
line:pad(str.spaces('start', delim.node.text))
259+
line:pad(str.spaces('start', delim.text))
265260
line:text(delimiter, self.config.head)
266-
line:pad(str.width(delim.node.text) - line:width())
267-
self.marks:over(self.config, 'table_border', delim.node, {
261+
line:pad(str.width(delim.text) - line:width())
262+
self.marks:over(self.config, 'table_border', delim, {
268263
virt_text = line:get(),
269264
virt_text_pos = 'overlay',
270265
})
271266
end
272267

273268
---@private
274-
---@param row render.md.table.body.Row
269+
---@param row render.md.table.Row
275270
function Render:row(row)
276271
local icon = self.config.border[10]
277272
local header = row.node.type == 'pipe_table_header'
@@ -287,25 +282,25 @@ function Render:row(row)
287282
end
288283

289284
if vim.tbl_contains({ 'trimmed', 'padded' }, self.config.cell) then
290-
for i, col in ipairs(row.cols) do
291-
local delim = self.data.delim.cols[i]
292-
local node = col.node
293-
local space = col.space
294-
local fill = delim.width - col.width
285+
for i, cell in ipairs(row.cells) do
286+
local col = self.data.cols[i]
287+
local node = cell.node
288+
local space = cell.space
289+
local fill = col.width - cell.width
295290
-- delim(20) : --------------------
296291
-- col(4,7,2): ----XXXXXXX--
297292
-- fill(7) : _______
298293
if not self.context.conceal:enabled() then
299294
-- result: ----XXXXXXX--_______
300295
-- without concealing it is impossible to do full alignment
301296
self:shift(node, 'right', fill)
302-
elseif delim.alignment == Alignment.center then
297+
elseif col.alignment == Alignment.center then
303298
-- (7 + 2 - 4) // 2 = 5 // 2 = 2 -> move two spaces to the right
304299
-- result: __----XXXXXXX--_____
305300
local shift = math.floor((fill + space.right - space.left) / 2)
306301
self:shift(node, 'left', shift)
307302
self:shift(node, 'right', fill - shift)
308-
elseif delim.alignment == Alignment.right then
303+
elseif col.alignment == Alignment.right then
309304
-- 2 - 1 = 1 -> conceal one space on right side
310305
-- result: -_______----XXXXXXX-
311306
local shift = space.right - self.config.padding
@@ -356,29 +351,30 @@ function Render:border()
356351
local rows = self.data.rows
357352
local border = self.config.border
358353

359-
---@param row render.md.table.body.Row
354+
---@param row render.md.table.Row
360355
---@return boolean
361356
local function width_equal(row)
362357
if vim.tbl_contains({ 'trimmed', 'padded' }, self.config.cell) then
363358
-- assume table was modified to match
364359
return true
365360
elseif self.config.cell == 'raw' then
366361
-- want the computed widths to match
367-
for i, col in ipairs(row.cols) do
368-
if delim.cols[i].width ~= col.width then
362+
for i, cell in ipairs(row.cells) do
363+
if cell.width ~= self.data.cols[i].width then
369364
return false
370365
end
371366
end
372367
return true
373368
elseif self.config.cell == 'overlay' then
374369
-- want the underlying text widths to match
375-
return str.width(delim.node.text) == str.width(row.node.text)
370+
return str.width(row.node.text) == str.width(delim.text)
376371
else
377372
return false
378373
end
379374
end
380375

381-
local first, last = rows[1], rows[#rows]
376+
local first = rows[1]
377+
local last = rows[#rows]
382378
if not width_equal(first) or not width_equal(last) then
383379
return
384380
end
@@ -391,21 +387,22 @@ function Render:border()
391387
end
392388

393389
local first_node = first.node
394-
local last_node = #rows == 1 and delim.node or last.node
390+
local last_node = #rows == 1 and delim or last.node
395391
local spaces = get_spaces(first_node)
396392
if spaces ~= get_spaces(last_node) then
397393
return
398394
end
399395

400-
local sections = iter.list.map(delim.cols, function(col)
401-
return border[11]:rep(col.width)
396+
local icon = border[11]
397+
local parts = iter.list.map(self.data.cols, function(col)
398+
return icon:rep(col.width)
402399
end)
403400

404401
---@param node render.md.Node
405402
---@param above boolean
406403
---@param chars [string, string, string]
407404
local function table_border(node, above, chars)
408-
local text = chars[1] .. table.concat(sections, chars[2]) .. chars[3]
405+
local text = chars[1] .. table.concat(parts, chars[2]) .. chars[3]
409406
local highlight = above and self.config.head or self.config.row
410407
local line = self:line():pad(spaces):text(text, highlight)
411408

0 commit comments

Comments
 (0)