Skip to content

Commit 1641b43

Browse files
feat: heading icon eol position
## Details Request: #593 Adds new `eol` value for `heading.position`. This is very similar to the existing `right` value, but rather than being right after the existing text it gets placed all the way at the right most column. Some notes: - Much like `right` alignment the '#'s on the left are concealed - This requires neovim >= `0.11.0` as it uses a newer `virt_text_pos` - The look may not pair well with `block` width, however I don't think there is much overlap between people using `block` width and people who would use this feature
1 parent 8a71090 commit 1641b43

8 files changed

Lines changed: 113 additions & 74 deletions

File tree

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,10 @@ require('render-markdown').setup({
352352
-- | string[] | `cycle(value, context.level)` |
353353
icons = { '󰲡 ', '󰲣 ', '󰲥 ', '󰲧 ', '󰲩 ', '󰲫 ' },
354354
-- Determines how icons fill the available space.
355-
-- | right | '#'s are concealed and icon is appended to right side |
356-
-- | inline | '#'s are concealed and icon is inlined on left side |
357-
-- | overlay | icon is left padded with spaces and inserted on left hiding additional '#' |
355+
-- | eol | '#'s are concealed and icon is placed at right most column |
356+
-- | right | '#'s are concealed and icon is appended to right side |
357+
-- | inline | '#'s are concealed and icon is inlined on left side |
358+
-- | overlay | icon is left padded with spaces and overlayed hiding all '#' |
358359
position = 'overlay',
359360
-- Added to the sign column if enabled.
360361
-- Output is evaluated by `cycle(value, context.level)`.
@@ -1027,9 +1028,10 @@ require('render-markdown').setup({
10271028
-- | string[] | `cycle(value, context.level)` |
10281029
icons = { '󰲡 ', '󰲣 ', '󰲥 ', '󰲧 ', '󰲩 ', '󰲫 ' },
10291030
-- Determines how icons fill the available space.
1030-
-- | right | '#'s are concealed and icon is appended to right side |
1031-
-- | inline | '#'s are concealed and icon is inlined on left side |
1032-
-- | overlay | icon is left padded with spaces and inserted on left hiding additional '#' |
1031+
-- | eol | '#'s are concealed and icon is placed at right most column |
1032+
-- | right | '#'s are concealed and icon is appended to right side |
1033+
-- | inline | '#'s are concealed and icon is inlined on left side |
1034+
-- | overlay | icon is left padded with spaces and overlayed hiding all '#' |
10331035
position = 'overlay',
10341036
-- Added to the sign column if enabled.
10351037
-- Output is evaluated by `cycle(value, context.level)`.

doc/custom-handlers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Each handler must conform to the following interface:
3333
---@class render.md.mark.Opts: vim.api.keyset.set_extmark
3434
---@field hl_mode? 'replace'|'combine'|'blend'
3535
---@field virt_text? render.md.mark.Line
36-
---@field virt_text_pos? 'eol'|'inline'|'overlay'
36+
---@field virt_text_pos? 'eol'|'eol_right_align'|'inline'|'overlay'
3737
---@field virt_lines? render.md.mark.Line[]
3838

3939
---@alias render.md.mark.Line render.md.mark.Text[]

doc/render-markdown.txt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,10 @@ Default Configuration ~
418418
-- | string[] | `cycle(value, context.level)` |
419419
icons = { '󰲡 ', '󰲣 ', '󰲥 ', '󰲧 ', '󰲩 ', '󰲫 ' },
420420
-- Determines how icons fill the available space.
421-
-- | right | '#'s are concealed and icon is appended to right side |
422-
-- | inline | '#'s are concealed and icon is inlined on left side |
423-
-- | overlay | icon is left padded with spaces and inserted on left hiding additional '#' |
421+
-- | eol | '#'s are concealed and icon is placed at right most column |
422+
-- | right | '#'s are concealed and icon is appended to right side |
423+
-- | inline | '#'s are concealed and icon is inlined on left side |
424+
-- | overlay | icon is left padded with spaces and overlayed hiding all '#' |
424425
position = 'overlay',
425426
-- Added to the sign column if enabled.
426427
-- Output is evaluated by `cycle(value, context.level)`.
@@ -1091,9 +1092,10 @@ Heading Configuration ~
10911092
-- | string[] | `cycle(value, context.level)` |
10921093
icons = { '󰲡 ', '󰲣 ', '󰲥 ', '󰲧 ', '󰲩 ', '󰲫 ' },
10931094
-- Determines how icons fill the available space.
1094-
-- | right | '#'s are concealed and icon is appended to right side |
1095-
-- | inline | '#'s are concealed and icon is inlined on left side |
1096-
-- | overlay | icon is left padded with spaces and inserted on left hiding additional '#' |
1095+
-- | eol | '#'s are concealed and icon is placed at right most column |
1096+
-- | right | '#'s are concealed and icon is appended to right side |
1097+
-- | inline | '#'s are concealed and icon is inlined on left side |
1098+
-- | overlay | icon is left padded with spaces and overlayed hiding all '#' |
10971099
position = 'overlay',
10981100
-- Added to the sign column if enabled.
10991101
-- Output is evaluated by `cycle(value, context.level)`.

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.11'
9+
M.version = '8.11.12'
1010

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

lua/render-markdown/lib/marks.lua

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ local str = require('render-markdown.lib.str')
1414
---@class render.md.mark.Opts: vim.api.keyset.set_extmark
1515
---@field hl_mode? 'replace'|'combine'|'blend'
1616
---@field virt_text? render.md.mark.Line
17-
---@field virt_text_pos? 'eol'|'inline'|'overlay'
17+
---@field virt_text_pos? 'eol'|'eol_right_align'|'inline'|'overlay'
1818
---@field virt_lines? render.md.mark.Line[]
1919

2020
---@alias render.md.mark.Line render.md.mark.Text[]
@@ -90,8 +90,8 @@ function Marks:add(config, conceal, start_row, start_col, opts)
9090
start_col = start_col,
9191
opts = opts,
9292
}
93-
local valid, feature, min_version = self:validate(mark.opts)
94-
if not valid then
93+
local feature, min_version = self:validate(mark.opts)
94+
if feature and min_version then
9595
local message = feature .. ' requires neovim >= ' .. min_version
9696
log.add('error', 'Mark', message, mark)
9797
return false
@@ -106,18 +106,25 @@ end
106106

107107
---@private
108108
---@param opts render.md.mark.Opts
109-
---@return boolean, string, string
109+
---@return string?, string?
110110
function Marks:validate(opts)
111-
if opts.virt_text_pos == 'inline' and not compat.has_10 then
112-
return false, "virt_text_pos = 'inline'", '0.10.0'
111+
if not compat.has_10 then
112+
if opts.virt_text_pos == 'inline' then
113+
return "virt_text_pos = 'inline'", '0.10.0'
114+
end
115+
if opts.virt_text_repeat_linebreak then
116+
return 'virt_text_repeat_linebreak', '0.10.0'
117+
end
113118
end
114-
if opts.virt_text_repeat_linebreak and not compat.has_10 then
115-
return false, 'virt_text_repeat_linebreak', '0.10.0'
119+
if not compat.has_11 then
120+
if opts.virt_text_pos == 'eol_right_align' then
121+
return "virt_text_pos = 'eol_right_align'", '0.11.0'
122+
end
123+
if opts.conceal_lines then
124+
return 'conceal_lines', '0.11.0'
125+
end
116126
end
117-
if opts.conceal_lines and not compat.has_11 then
118-
return false, 'conceal_lines', '0.11.0'
119-
end
120-
return true, '', ''
127+
return nil, nil
121128
end
122129

123130
---@private

lua/render-markdown/render/markdown/heading.lua

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -119,73 +119,76 @@ end
119119
---@return integer
120120
function Render:marker()
121121
local icon = self.data.icon
122+
local position = self.config.position
122123
local highlight = {} ---@type string[]
123-
if self.data.fg then
124-
highlight[#highlight + 1] = self.data.fg
125-
end
126-
if self.data.bg then
127-
highlight[#highlight + 1] = self.data.bg
128-
end
124+
highlight[#highlight + 1] = self.data.fg
125+
highlight[#highlight + 1] = self.data.bg
126+
129127
if self.data.atx then
130128
local node = self.data.marker
131-
-- add 1 to account for space after last '#'
129+
130+
-- add 1 to right to account for space after last '#'
132131
local width = self.context:width(node) + 1
132+
local offset = { 0, 0, 0, 1 } ---@type Range4
133+
133134
if not icon or #highlight == 0 then
134135
return width
135136
end
136-
if self.config.position == 'right' then
137-
self.marks:over(self.config, true, node, {
138-
conceal = '',
139-
}, { 0, 0, 0, 1 })
137+
138+
if position == 'eol' or position == 'right' then
139+
self.marks:over(self.config, true, node, { conceal = '' }, offset)
140140
self.marks:start(self.config, 'head_icon', node, {
141141
priority = 1000,
142142
virt_text = { { icon, highlight } },
143-
virt_text_pos = 'eol',
143+
virt_text_pos = position == 'eol' and 'eol_right_align'
144+
or 'eol',
144145
})
145-
return 1 + str.width(icon)
146-
else
147-
local padding = width - str.width(icon)
148-
if self.config.position == 'inline' or padding < 0 then
149-
local added = self.marks:over(self.config, 'head_icon', node, {
150-
virt_text = { { icon, highlight } },
151-
virt_text_pos = 'inline',
152-
conceal = '',
153-
}, { 0, 0, 0, 1 })
154-
return added and str.width(icon) or width
155-
else
156-
self.marks:over(self.config, 'head_icon', node, {
157-
virt_text = { { str.pad(padding) .. icon, highlight } },
158-
virt_text_pos = 'overlay',
159-
})
160-
return width
161-
end
146+
return position == 'eol' and 0 or 1 + str.width(icon)
147+
end
148+
149+
local space = width - str.width(icon)
150+
if position == 'inline' or space < 0 then
151+
local added = self.marks:over(self.config, 'head_icon', node, {
152+
virt_text = { { icon, highlight } },
153+
virt_text_pos = 'inline',
154+
conceal = '',
155+
}, offset)
156+
return added and str.width(icon) or width
162157
end
158+
159+
self.marks:over(self.config, 'head_icon', node, {
160+
virt_text = { { str.pad(space) .. icon, highlight } },
161+
virt_text_pos = 'overlay',
162+
})
163+
return width
163164
else
164165
local node = self.node
166+
165167
if not icon or #highlight == 0 then
166168
return 0
167169
end
168-
if self.config.position == 'right' then
170+
171+
if position == 'eol' or position == 'right' then
169172
self.marks:start(self.config, 'head_icon', node, {
170173
priority = 1000,
171174
virt_text = { { icon, highlight } },
172-
virt_text_pos = 'eol',
175+
virt_text_pos = position == 'eol' and 'eol_right_align'
176+
or 'eol',
177+
})
178+
return position == 'eol' and 0 or 1 + str.width(icon)
179+
end
180+
181+
local added = self.marks:start(self.config, 'head_icon', node, {
182+
virt_text = { { icon, highlight } },
183+
virt_text_pos = 'inline',
184+
})
185+
for row = node.start_row + 1, node.end_row - 1 do
186+
self.marks:add(self.config, 'head_icon', row, node.start_col, {
187+
virt_text = { { str.pad(str.width(icon)), highlight } },
188+
virt_text_pos = 'inline',
173189
})
174-
return 1 + str.width(icon)
175-
else
176-
local col = node.start_col
177-
local added = true
178-
for row = node.start_row, node.end_row - 1 do
179-
local start = row == node.start_row
180-
local text = start and icon or str.pad(str.width(icon))
181-
added = added
182-
and self.marks:add(self.config, 'head_icon', row, col, {
183-
virt_text = { { text, highlight } },
184-
virt_text_pos = 'inline',
185-
})
186-
end
187-
return added and str.width(icon) or 0
188190
end
191+
return added and str.width(icon) or 0
189192
end
190193
end
191194

lua/render-markdown/settings.lua

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,7 @@ M.heading.position = {
806806
overlay = 'overlay',
807807
inline = 'inline',
808808
right = 'right',
809+
eol = 'eol',
809810
}
810811

811812
---@enum render.md.heading.Width
@@ -842,9 +843,10 @@ M.heading.default = {
842843
-- | string[] | `cycle(value, context.level)` |
843844
icons = { '󰲡 ', '󰲣 ', '󰲥 ', '󰲧 ', '󰲩 ', '󰲫 ' },
844845
-- Determines how icons fill the available space.
845-
-- | right | '#'s are concealed and icon is appended to right side |
846-
-- | inline | '#'s are concealed and icon is inlined on left side |
847-
-- | overlay | icon is left padded with spaces and inserted on left hiding additional '#' |
846+
-- | eol | '#'s are concealed and icon is placed at right most column |
847+
-- | right | '#'s are concealed and icon is appended to right side |
848+
-- | inline | '#'s are concealed and icon is inlined on left side |
849+
-- | overlay | icon is left padded with spaces and overlayed hiding all '#' |
848850
position = 'overlay',
849851
-- Added to the sign column if enabled.
850852
-- Output is evaluated by `cycle(value, context.level)`.

tests/heading_spec.lua

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,27 @@ describe('heading', function()
269269
' Ext Heading 2 Line 2',
270270
})
271271
end)
272+
273+
it('eol', function()
274+
util.setup.text(lines, {
275+
heading = { position = 'eol' },
276+
})
277+
util.assert_screen({
278+
'',
279+
'󰫎 Head 1 󰲡',
280+
'',
281+
'󰫎 Head 2 󰲣',
282+
'󰫎 H3 󰲥',
283+
'󰫎 H4 󰲧',
284+
'',
285+
'󰫎 Head 5 󰲩',
286+
'',
287+
'󰫎 Head 6 󰲫',
288+
'',
289+
'󰫎 Ext Heading 󰲡',
290+
'',
291+
'󰫎 Ext Heading 2 󰲣',
292+
' Ext Heading 2 Line 2',
293+
})
294+
end)
272295
end)

0 commit comments

Comments
 (0)