Skip to content

Commit e724a49

Browse files
feat: improve code language border for different conceal settings
## Details When adding a thin border ignore whether the delimiter node is fully concealed or not. Since we're doing an `overlay` it'll hide the underlying text anyway, so whether it is concealed or not makes no difference. Instead we just do a check that the delimiter node exists. When computing leading spaces offset use the difference between the number of leading spaces and the concealed width of the delimiter node. This is a more generic version of the previous logic, which gated adding the spaces behind the node being fully concealed. Update unit test to handle more cases of code blocks.
1 parent 00cace9 commit e724a49

File tree

6 files changed

+118
-113
lines changed

6 files changed

+118
-113
lines changed

lua/render-markdown/health.lua

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

77
---@private
8-
M.version = '8.1.14'
8+
M.version = '8.1.15'
99

1010
function M.check()
1111
M.start('version')

lua/render-markdown/lib/str.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ end
4242
---@param n integer
4343
---@return string
4444
function M.pad(n)
45+
if n <= 0 then
46+
return ''
47+
end
4548
return string.rep(' ', n)
4649
end
4750

lua/render-markdown/render/code.lua

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function Render:render()
9999

100100
local icon = self:language()
101101
local start_row, end_row = self.node.start_row, self.node.end_row - 1
102-
self:border(start_row, true, not icon and self:concealed(self.data.code_node))
102+
self:border(start_row, true, not icon and self.context:width(self.data.code_node) == 0)
103103
self:border(end_row, false, true)
104104
if background then
105105
self:background(start_row + 1, end_row - 1)
@@ -114,8 +114,9 @@ function Render:language()
114114
return false
115115
end
116116

117+
local delim = self.node:child('fenced_code_block_delimiter', self.node.start_row)
117118
local node, padding = self.data.language_node, self.data.language_padding
118-
if node == nil then
119+
if delim == nil or node == nil then
119120
return false
120121
end
121122

@@ -142,13 +143,25 @@ function Render:language()
142143
table.insert(highlight, self.code.highlight)
143144
end
144145

146+
-- TODO(0.11): handle conceal_lines
147+
-- - Use self.context:hidden(node) to determine if a node is hidden
148+
-- - Default highlights remove the fenced code block delimiter lines along with
149+
-- any extmarks we add there.
150+
-- - To account for this we'll need add back the lines, likely using virt_lines.
151+
-- - For top delimiter
152+
-- - Add extmark above the second row with virt_lines_above = true
153+
-- - By doing this we'll add a line just above the fenced code block
154+
-- - We likely need to handle the sign column here as well
155+
-- - For bottom delimiter
156+
-- - Add extmark below the second to last row with virt_lines_above = false
157+
-- - By doing this we'll add a line just below the fenced code block
158+
145159
if self.code.position == 'left' then
146160
text = Str.pad(padding) .. text
147-
if self:concealed(node) then
148-
-- Code blocks can pick up varying amounts of leading white space.
149-
-- This is lumped into the delimiter node and needs to be handled.
150-
text = Str.pad(Str.spaces('start', self.node.text)) .. text
151-
end
161+
-- Code blocks can pick up varying amounts of leading white space.
162+
-- This is lumped into the delimiter node and needs to be handled.
163+
local spaces = Str.spaces('start', delim.text)
164+
text = Str.pad(spaces - self.context:width(delim)) .. text
152165
return self.marks:add('code_language', node.start_row, node.start_col, {
153166
virt_text = { { text, highlight } },
154167
virt_text_pos = 'inline',
@@ -173,10 +186,12 @@ function Render:border(row, above, empty)
173186
if self.code.border == 'none' then
174187
return
175188
end
176-
local delim = self.node:child('fenced_code_block_delimiter', row)
189+
if self.node:child('fenced_code_block_delimiter', row) == nil then
190+
return
191+
end
177192
local width, highlight = self.data.width - self.data.col, self.code.highlight
178193
local border = above and self.code.above or self.code.below
179-
if self.code.border == 'thin' and empty and self:concealed(delim) then
194+
if self.code.border == 'thin' and empty then
180195
local line = { { border:rep(width), colors.bg_to_fg(highlight) } }
181196
self.marks:add('code_border', row, self.data.col, {
182197
virt_text = line,
@@ -187,28 +202,6 @@ function Render:border(row, above, empty)
187202
end
188203
end
189204

190-
---@private
191-
---@param node? render.md.Node
192-
---@return boolean
193-
function Render:concealed(node)
194-
-- TODO(0.11): handle conceal_lines
195-
-- - Use self.context:hidden(node) to determine if a node is hidden
196-
-- - Default highlights remove the fenced code block delimiter lines along with
197-
-- any extmarks we add there.
198-
-- - To account for this we'll need add back the lines, likely using virt_lines.
199-
-- - For top delimiter
200-
-- - Add extmark above the top row with virt_lines_above = false
201-
-- - By doing this we'll add a line just above the fenced code block
202-
-- - We likely need to handle the sign column here as well
203-
-- - For bottom delimiter
204-
-- - Add extmark below the bottom row with virt_lines_above = true
205-
-- - By doing this we'll add a line just below the fenced code block
206-
-- - For both of these we'll need to do something that does anti_conceal via an
207-
-- offset such that the cursor going over the concealed line naturally shows
208-
-- the raw text and the virtual line disappears
209-
return self.context:width(node) == 0
210-
end
211-
212205
---@private
213206
---@param start_row integer
214207
---@param end_row integer

tests/code_spec.lua

Lines changed: 82 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,19 @@ describe('code.md', function()
2929
:add(row:get(), nil, 5, nil, util.code.icon('py'))
3030
:add(row:get(), row:inc(), 2, 0, util.code.bg())
3131
:add(row:get(), row:inc(), 2, 0, util.code.bg())
32+
:add(row:get(), nil, 0, nil, util.padding(2, 1000))
33+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
3234
:add(row:get(), row:inc(), 2, 0, util.code.bg())
3335
:add(row:get(), nil, 2, nil, util.code.border(false, vim.o.columns - 2))
3436

35-
marks:add(row:inc(2), row:get(), 0, 2, util.bullet(1))
36-
3737
marks
38-
:add(row:inc(2), nil, 2, nil, util.code.sign('lua'))
39-
:add(row:get(), nil, 5, nil, util.code.icon('lua'))
40-
:add(row:get(), row:inc(), 2, 0, util.code.bg())
41-
:add(row:get(), row:inc(), 2, 0, util.code.bg())
42-
:add(row:get(), nil, 0, nil, util.padding(2, 1000))
38+
:add(row:inc(4), nil, 0, nil, util.code.sign('lua'))
39+
:add(row:get(), nil, 5, nil, util.code.icon('lua', 2))
4340
:add(row:get(), row:inc(), 0, 0, util.code.bg())
44-
:add(row:get(), row:inc(), 2, 0, util.code.bg())
45-
:add(row:get(), nil, 2, nil, util.code.border(false, vim.o.columns - 2))
41+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
42+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
43+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
44+
:add(row:get(), nil, 0, nil, util.code.border(false, vim.o.columns))
4645

4746
marks:add(row:inc(2), row:get(), 0, 2, util.bullet(1))
4847

@@ -60,26 +59,27 @@ describe('code.md', function()
6059
' 6 }',
6160
' 7 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
6261
' 8',
63-
' 9 ● Nested code',
62+
' 9 ● List Divider',
6463
' 10',
6564
'󰌠 11 󰌠 py',
6665
' 12 print("hello")',
67-
' 13 print("world")',
68-
' 14 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
69-
' 15',
70-
' 16 ● Nested code with blank',
71-
' 17',
72-
'󰢱 18 󰢱 lua',
73-
" 19 print('hello')",
74-
' 20',
75-
" 21 print('world')",
76-
' 22 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
77-
' 23',
78-
' 24 ● No language',
79-
' 25',
80-
' 26 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄',
81-
" 27 print('Hello, World!')",
82-
' 28 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
66+
' 13',
67+
' 14 print("world")',
68+
' 15 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
69+
' 16',
70+
' 17 Paragraph Divider',
71+
' 18',
72+
'󰢱 19 󰢱 lua',
73+
" 20 print('hello')",
74+
' 21',
75+
" 22 print('world')",
76+
' 23 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
77+
' 24',
78+
' 25 ● List Divider',
79+
' 26',
80+
' 27 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄',
81+
" 28 print('Hello, World!')",
82+
' 29 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
8383
})
8484
end)
8585

@@ -101,12 +101,16 @@ describe('code.md', function()
101101
:add(row:get(), nil, 3, nil, util.code.icon('rust'))
102102
:add(row:get(), nil, 0, nil, util.code.hide(width_1))
103103
:add(row:get(), row:inc(), 0, 0, util.code.bg())
104-
for _ = 1, 3 do
105-
marks:add(row:get(), nil, 0, nil, util.padding(2, 0, 'RmCode'))
106-
marks:add(row:get(), nil, 0, nil, util.code.hide(width_1))
107-
marks:add(row:get(), row:inc(), 0, 0, util.code.bg())
108-
end
109-
marks:add(row:get(), nil, 0, nil, util.code.border(false, width_1))
104+
:add(row:get(), nil, 0, nil, util.padding(2, 0, 'RmCode'))
105+
:add(row:get(), nil, 0, nil, util.code.hide(width_1))
106+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
107+
:add(row:get(), nil, 0, nil, util.padding(2, 0, 'RmCode'))
108+
:add(row:get(), nil, 0, nil, util.code.hide(width_1))
109+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
110+
:add(row:get(), nil, 0, nil, util.padding(2, 0, 'RmCode'))
111+
:add(row:get(), nil, 0, nil, util.code.hide(width_1))
112+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
113+
:add(row:get(), nil, 0, nil, util.code.border(false, width_1))
110114

111115
marks:add(row:inc(2), row:get(), 0, 2, util.bullet(1))
112116

@@ -116,36 +120,37 @@ describe('code.md', function()
116120
:add(row:get(), nil, 5, nil, util.code.icon('py'))
117121
:add(row:get(), nil, 2, nil, util.code.hide(width_2))
118122
:add(row:get(), row:inc(), 2, 0, util.code.bg())
119-
for _ = 1, 2 do
120-
marks
121-
:add(row:get(), nil, 2, nil, util.padding(2, 1000, 'RmCode'))
122-
:add(row:get(), nil, 2, nil, util.code.hide(width_2))
123-
:add(row:get(), row:inc(), 2, 0, util.code.bg())
124-
end
125-
marks:add(row:get(), nil, 2, nil, util.code.border(false, width_2 - 2))
126-
127-
marks:add(row:inc(2), row:get(), 0, 2, util.bullet(1))
123+
:add(row:get(), nil, 2, nil, util.padding(2, 1000, 'RmCode'))
124+
:add(row:get(), nil, 2, nil, util.code.hide(width_2))
125+
:add(row:get(), row:inc(), 2, 0, util.code.bg())
126+
:add(row:get(), nil, 0, nil, {
127+
priority = 1000,
128+
virt_text = { { ' ', 'Normal' }, { ' ', 'RmCode' } },
129+
virt_text_pos = 'inline',
130+
})
131+
:add(row:get(), nil, 0, nil, util.code.hide(width_2))
132+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
133+
:add(row:get(), nil, 2, nil, util.padding(2, 1000, 'RmCode'))
134+
:add(row:get(), nil, 2, nil, util.code.hide(width_2))
135+
:add(row:get(), row:inc(), 2, 0, util.code.bg())
136+
:add(row:get(), nil, 2, nil, util.code.border(false, width_2 - 2))
128137

129138
local width_3 = 20
130139
marks
131-
:add(row:inc(2), nil, 2, nil, util.code.sign('lua'))
132-
:add(row:get(), nil, 5, nil, util.code.icon('lua'))
133-
:add(row:get(), nil, 2, nil, util.code.hide(width_3))
134-
:add(row:get(), row:inc(), 2, 0, util.code.bg())
135-
for _, col in ipairs({ 2, 0, 2 }) do
136-
if col == 0 then
137-
marks:add(row:get(), nil, col, nil, {
138-
priority = 1000,
139-
virt_text = { { ' ', 'Normal' }, { ' ', 'RmCode' } },
140-
virt_text_pos = 'inline',
141-
})
142-
else
143-
marks:add(row:get(), nil, col, nil, util.padding(2, 1000, 'RmCode'))
144-
end
145-
marks:add(row:get(), nil, col, nil, util.code.hide(width_3))
146-
marks:add(row:get(), row:inc(), col, 0, util.code.bg())
147-
end
148-
marks:add(row:get(), nil, 2, nil, util.code.border(false, width_3 - 2))
140+
:add(row:inc(4), nil, 0, nil, util.code.sign('lua'))
141+
:add(row:get(), nil, 5, nil, util.code.icon('lua', 2))
142+
:add(row:get(), nil, 0, nil, util.code.hide(width_3))
143+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
144+
:add(row:get(), nil, 0, nil, util.padding(2, 0, 'RmCode'))
145+
:add(row:get(), nil, 0, nil, util.code.hide(width_3))
146+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
147+
:add(row:get(), nil, 0, nil, util.padding(2, 0, 'RmCode'))
148+
:add(row:get(), nil, 0, nil, util.code.hide(width_3))
149+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
150+
:add(row:get(), nil, 0, nil, util.padding(2, 0, 'RmCode'))
151+
:add(row:get(), nil, 0, nil, util.code.hide(width_3))
152+
:add(row:get(), row:inc(), 0, 0, util.code.bg())
153+
:add(row:get(), nil, 0, nil, util.code.border(false, width_3))
149154

150155
marks:add(row:inc(2), row:get(), 0, 2, util.bullet(1))
151156

@@ -166,26 +171,27 @@ describe('code.md', function()
166171
' 6 }',
167172
' 7 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
168173
' 8',
169-
' 9 ● Nested code',
174+
' 9 ● List Divider',
170175
' 10',
171176
'󰌠 11 󰌠 py',
172177
' 12 print("hello")',
173-
' 13 print("world")',
174-
' 14 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
175-
' 15',
176-
' 16 ● Nested code with blank',
177-
' 17',
178-
'󰢱 18 󰢱 lua',
179-
" 19 print('hello')",
180-
' 20',
181-
" 21 print('world')",
182-
' 22 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
183-
' 23',
184-
' 24 ● No language',
185-
' 25',
186-
' 26 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄',
187-
" 27 print('Hello, World!')",
188-
' 28 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
178+
' 13',
179+
' 14 print("world")',
180+
' 15 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
181+
' 16',
182+
' 17 Paragraph Divider',
183+
' 18',
184+
'󰢱 19 󰢱 lua',
185+
" 20 print('hello')",
186+
' 21',
187+
" 22 print('world')",
188+
' 23 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
189+
' 24',
190+
' 25 ● List Divider',
191+
' 26',
192+
' 27 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄',
193+
" 28 print('Hello, World!')",
194+
' 29 ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
189195
})
190196
end)
191197
end)

tests/data/code.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,23 @@ fn main() {
66
}
77
```
88

9-
- Nested code
9+
- List Divider
1010

1111
```py
1212
print("hello")
13+
1314
print("world")
1415
```
1516

16-
- Nested code with blank
17+
Paragraph Divider
1718

1819
```lua
1920
print('hello')
2021

2122
print('world')
2223
```
2324

24-
- No language
25+
- List Divider
2526

2627
```
2728
print('Hello, World!')

tests/util.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,9 @@ function M.code.sign(name)
203203
end
204204

205205
---@param name 'python'|'py'|'rust'|'rs'|'lua'
206+
---@param padding? integer
206207
---@return vim.api.keyset.set_extmark
207-
function M.code.icon(name)
208+
function M.code.icon(name, padding)
208209
local icon, highlight
209210
if name == 'python' or name == 'py' then
210211
icon, highlight = '󰌠 ', 'MiniIconsYellow'
@@ -213,9 +214,10 @@ function M.code.icon(name)
213214
elseif name == 'lua' then
214215
icon, highlight = '󰢱 ', 'MiniIconsAzure'
215216
end
217+
local prefix = string.rep(' ', padding or 0)
216218
---@type vim.api.keyset.set_extmark
217219
return {
218-
virt_text = { { icon .. name, highlight .. ':' .. 'RmCode' } },
220+
virt_text = { { prefix .. icon .. name, highlight .. ':' .. 'RmCode' } },
219221
virt_text_pos = 'inline',
220222
}
221223
end

0 commit comments

Comments
 (0)