@@ -4,12 +4,14 @@ local list = require('render-markdown.core.list')
44local str = require (' render-markdown.core.str' )
55
66--- @class render.md.data.Heading
7+ --- @field atx boolean
78--- @field level integer
89--- @field icon ? string
910--- @field sign ? string
1011--- @field foreground string
1112--- @field background string
1213--- @field heading_width render.md.heading.Width
14+ --- @field end_row integer
1315
1416--- @class render.md.render.Heading : render.md.Renderer
1517--- @field private heading render.md.Heading
@@ -33,80 +35,77 @@ function Render:setup()
3335 return false
3436 end
3537
36- local level = str .width (self .info .text )
38+ local atx , level = nil , nil
39+ if self .info .type == ' setext_heading' then
40+ atx , level = false , self .info :child (' setext_h1_underline' ) ~= nil and 1 or 2
41+ else
42+ atx , level = true , str .width (self .info .text )
43+ end
44+
3745 local heading_width = self .heading .width
3846 if type (heading_width ) == ' table' then
3947 heading_width = list .clamp (heading_width , level )
4048 end
49+
4150 self .data = {
51+ atx = atx ,
4252 level = level ,
4353 icon = list .cycle (self .heading .icons , level ),
4454 sign = list .cycle (self .heading .signs , level ),
4555 foreground = list .clamp (self .heading .foregrounds , level ),
4656 background = list .clamp (self .heading .backgrounds , level ),
4757 heading_width = heading_width ,
58+ end_row = self .info .end_row + (atx and 1 or 0 ),
4859 }
4960
5061 return true
5162end
5263
5364function Render :render ()
54- local icon_width = self :start_icon ( )
65+ local width = self :width ( self : icon () )
5566 if self .heading .sign then
5667 self :sign (self .data .sign , self .data .foreground )
5768 end
69+ self :background (width )
70+ self :border (width )
71+ self :left_pad ()
72+ self :conceal_underline ()
73+ end
5874
59- self .marks :add (true , self .info .start_row , 0 , {
60- end_row = self .info .end_row + 1 ,
61- end_col = 0 ,
62- hl_group = self .data .background ,
63- hl_eol = true ,
64- })
65-
66- local width = self :width (icon_width )
67- if self .data .heading_width == ' block' then
68- -- Overwrite anything beyond width with Normal
69- self .marks :add (true , self .info .start_row , 0 , {
70- priority = 0 ,
71- virt_text = { { str .spaces (vim .o .columns * 2 ), ' Normal' } },
72- virt_text_win_col = width ,
73- })
74- end
75-
76- if self .heading .border then
77- self :border (width )
78- end
79-
80- if self .heading .left_pad > 0 then
81- self .marks :add (false , self .info .start_row , 0 , {
82- priority = 0 ,
83- virt_text = { { str .spaces (self .heading .left_pad ), self .data .background } },
75+ --- @private
76+ --- @return integer
77+ function Render :icon ()
78+ if not self .data .atx then
79+ if self .data .icon == nil then
80+ return 0
81+ end
82+ local added = self .marks :add (true , self .info .start_row , self .info .start_col , {
83+ end_row = self .info .end_row ,
84+ end_col = self .info .end_col ,
85+ virt_text = { { self .data .icon , { self .data .foreground , self .data .background } } },
8486 virt_text_pos = ' inline' ,
8587 })
88+ return added and str .width (self .data .icon ) or 0
8689 end
87- end
8890
89- --- @private
90- --- @return integer
91- function Render :start_icon ()
92- -- Available width is level + 1 - concealed, where level = number of `#` characters, one
93- -- is added to account for the space after the last `#` but before the heading title,
94- -- and concealed text is subtracted since that space is not usable
91+ -- For atx headings available width is level + 1 - concealed, where level = number of
92+ -- `#` characters, one is added to account for the space after the last `#` but before
93+ -- the heading title, and concealed text is subtracted since that space is not usable
9594 local width = self .data .level + 1 - self .context :concealed (self .info )
9695 if self .data .icon == nil then
9796 return width
9897 end
9998
10099 local padding = width - str .width (self .data .icon )
101100 if self .heading .position == ' inline' or padding < 0 then
102- self .marks :add (true , self .info .start_row , self .info .start_col , {
101+ local added = self .marks :add (true , self .info .start_row , self .info .start_col , {
103102 end_row = self .info .end_row ,
104103 end_col = self .info .end_col ,
105104 virt_text = { { self .data .icon , { self .data .foreground , self .data .background } } },
106105 virt_text_pos = ' inline' ,
107106 conceal = ' ' ,
108107 })
109- return str .width (self .data .icon )
108+ return added and str .width (self .data .icon ) or width
110109 else
111110 self .marks :add (true , self .info .start_row , self .info .start_col , {
112111 end_row = self .info .end_row ,
@@ -123,20 +122,50 @@ end
123122--- @return integer
124123function Render :width (icon_width )
125124 if self .data .heading_width == ' block' then
126- local width = self .heading .left_pad + icon_width + self .heading .right_pad
127- local content = self .info :sibling (' inline' )
128- if content ~= nil then
129- width = width + str .width (content .text ) + self .context :get_offset (content ) - self .context :concealed (content )
125+ local width = nil
126+ if self .data .atx then
127+ width = icon_width + self .context :width (self .info :sibling (' inline' ))
128+ else
129+ -- Account for icon in first row
130+ local widths = vim .tbl_map (str .width , self .info :lines ())
131+ widths [1 ] = widths [1 ] + icon_width
132+ width = vim .fn .max (widths )
130133 end
134+ width = self .heading .left_pad + width + self .heading .right_pad
131135 return math.max (width , self .heading .min_width )
132136 else
133137 return self .context :get_width ()
134138 end
135139end
136140
141+ --- @private
142+ --- @param width integer
143+ function Render :background (width )
144+ for row = self .info .start_row , self .data .end_row - 1 do
145+ self .marks :add (true , row , 0 , {
146+ end_row = row + 1 ,
147+ hl_group = self .data .background ,
148+ hl_eol = true ,
149+ })
150+ if self .data .heading_width == ' block' then
151+ -- Overwrite anything beyond width with Normal
152+ self .marks :add (true , row , 0 , {
153+ priority = 0 ,
154+ virt_text = { { str .spaces (vim .o .columns * 2 ), ' Normal' } },
155+ virt_text_win_col = width ,
156+ })
157+ end
158+ end
159+ end
160+
137161--- @private
138162--- @param width integer
139163function Render :border (width )
164+ -- Only atx headings support borders
165+ if not self .heading .border or not self .data .atx then
166+ return
167+ end
168+
140169 local background = colors .inverse (self .data .background )
141170 local prefix = self .heading .border_prefix and self .data .level or 0
142171
@@ -175,4 +204,34 @@ function Render:border(width)
175204 end
176205end
177206
207+ --- @private
208+ function Render :left_pad ()
209+ if self .heading .left_pad <= 0 then
210+ return
211+ end
212+ for row = self .info .start_row , self .data .end_row - 1 do
213+ self .marks :add (false , row , 0 , {
214+ priority = 0 ,
215+ virt_text = { { str .spaces (self .heading .left_pad ), self .data .background } },
216+ virt_text_pos = ' inline' ,
217+ })
218+ end
219+ end
220+
221+ --- @private
222+ function Render :conceal_underline ()
223+ if self .data .atx then
224+ return
225+ end
226+ local info = self .info :child (string.format (' setext_h%d_underline' , self .data .level ))
227+ if info == nil then
228+ return
229+ end
230+ self .marks :add (true , info .start_row , info .start_col , {
231+ end_row = info .end_row ,
232+ end_col = info .end_col ,
233+ conceal = ' ' ,
234+ })
235+ end
236+
178237return Render
0 commit comments