@@ -4,12 +4,14 @@ local list = require('render-markdown.core.list')
4
4
local str = require (' render-markdown.core.str' )
5
5
6
6
--- @class render.md.data.Heading
7
+ --- @field atx boolean
7
8
--- @field level integer
8
9
--- @field icon ? string
9
10
--- @field sign ? string
10
11
--- @field foreground string
11
12
--- @field background string
12
13
--- @field heading_width render.md.heading.Width
14
+ --- @field end_row integer
13
15
14
16
--- @class render.md.render.Heading : render.md.Renderer
15
17
--- @field private heading render.md.Heading
@@ -33,80 +35,77 @@ function Render:setup()
33
35
return false
34
36
end
35
37
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
+
37
45
local heading_width = self .heading .width
38
46
if type (heading_width ) == ' table' then
39
47
heading_width = list .clamp (heading_width , level )
40
48
end
49
+
41
50
self .data = {
51
+ atx = atx ,
42
52
level = level ,
43
53
icon = list .cycle (self .heading .icons , level ),
44
54
sign = list .cycle (self .heading .signs , level ),
45
55
foreground = list .clamp (self .heading .foregrounds , level ),
46
56
background = list .clamp (self .heading .backgrounds , level ),
47
57
heading_width = heading_width ,
58
+ end_row = self .info .end_row + (atx and 1 or 0 ),
48
59
}
49
60
50
61
return true
51
62
end
52
63
53
64
function Render :render ()
54
- local icon_width = self :start_icon ( )
65
+ local width = self :width ( self : icon () )
55
66
if self .heading .sign then
56
67
self :sign (self .data .sign , self .data .foreground )
57
68
end
69
+ self :background (width )
70
+ self :border (width )
71
+ self :left_pad ()
72
+ self :conceal_underline ()
73
+ end
58
74
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 } } },
84
86
virt_text_pos = ' inline' ,
85
87
})
88
+ return added and str .width (self .data .icon ) or 0
86
89
end
87
- end
88
90
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
95
94
local width = self .data .level + 1 - self .context :concealed (self .info )
96
95
if self .data .icon == nil then
97
96
return width
98
97
end
99
98
100
99
local padding = width - str .width (self .data .icon )
101
100
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 , {
103
102
end_row = self .info .end_row ,
104
103
end_col = self .info .end_col ,
105
104
virt_text = { { self .data .icon , { self .data .foreground , self .data .background } } },
106
105
virt_text_pos = ' inline' ,
107
106
conceal = ' ' ,
108
107
})
109
- return str .width (self .data .icon )
108
+ return added and str .width (self .data .icon ) or width
110
109
else
111
110
self .marks :add (true , self .info .start_row , self .info .start_col , {
112
111
end_row = self .info .end_row ,
@@ -123,20 +122,50 @@ end
123
122
--- @return integer
124
123
function Render :width (icon_width )
125
124
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 )
130
133
end
134
+ width = self .heading .left_pad + width + self .heading .right_pad
131
135
return math.max (width , self .heading .min_width )
132
136
else
133
137
return self .context :get_width ()
134
138
end
135
139
end
136
140
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
+
137
161
--- @private
138
162
--- @param width integer
139
163
function 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
+
140
169
local background = colors .inverse (self .data .background )
141
170
local prefix = self .heading .border_prefix and self .data .level or 0
142
171
@@ -175,4 +204,34 @@ function Render:border(width)
175
204
end
176
205
end
177
206
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
+
178
237
return Render
0 commit comments