@@ -13,7 +13,7 @@ type HeadingConfig struct {
1313}
1414
1515// SetOption implements SetOptioner.
16- func (b * HeadingConfig ) SetOption (name OptionName , _ interface {} ) {
16+ func (b * HeadingConfig ) SetOption (name OptionName , _ any ) {
1717 switch name {
1818 case optAutoHeadingID :
1919 b .AutoHeadingID = true
@@ -98,69 +98,48 @@ func (b *atxHeadingParser) Open(parent ast.Node, reader text.Reader, pc Context)
9898 if l == 0 {
9999 return nil , NoChildren
100100 }
101- start := i + l
102- if start >= len (line ) {
103- start = len (line ) - 1
104- }
105- origstart := start
106- stop := len (line ) - util .TrimRightSpaceLength (line )
107101
102+ start := min (i + l , len (line )- 1 )
108103 node := ast .NewHeading (level )
109- parsed := false
110- if b .Attribute { // handles special case like ### heading ### {#id}
111- start --
112- closureClose := - 1
113- closureOpen := - 1
114- for j := start ; j < stop ; {
115- c := line [j ]
116- if util .IsEscapedPunctuation (line , j ) {
117- j += 2
118- } else if util .IsSpace (c ) && j < stop - 1 && line [j + 1 ] == '#' {
119- closureOpen = j + 1
120- k := j + 1
121- for ; k < stop && line [k ] == '#' ; k ++ {
122- }
123- closureClose = k
124- break
125- } else {
126- j ++
127- }
104+ hl := text .NewSegment (
105+ segment .Start + start - segment .Padding ,
106+ segment .Start + len (line )- segment .Padding )
107+ hl = hl .TrimRightSpace (reader .Source ())
108+ if hl .Len () == 0 {
109+ reader .AdvanceToEOL ()
110+ return node , NoChildren
111+ }
112+
113+ if b .Attribute {
114+ node .Lines ().Append (hl )
115+ parseLastLineAttributes (node , reader , pc )
116+ hl = node .Lines ().At (0 )
117+ node .Lines ().Clear ()
118+ }
119+
120+ // handle closing sequence of '#' characters
121+ line = hl .Value (reader .Source ())
122+ stop := len (line )
123+ if stop == 0 { // empty headings like '##[space]'
124+ stop = 0
125+ } else {
126+ i = stop - 1
127+ for ; line [i ] == '#' && i > 0 ; i -- {
128128 }
129- if closureClose > 0 {
130- reader .Advance (closureClose )
131- attrs , ok := ParseAttributes (reader )
132- rest , _ := reader .PeekLine ()
133- parsed = ok && util .IsBlank (rest )
134- if parsed {
135- for _ , attr := range attrs {
136- node .SetAttribute (attr .Name , attr .Value )
137- }
138- node .Lines ().Append (text .NewSegment (
139- segment .Start + start + 1 - segment .Padding ,
140- segment .Start + closureOpen - segment .Padding ))
141- }
129+ if i == 0 { // empty headings like '### ###'
130+ reader .AdvanceToEOL ()
131+ return node , NoChildren
142132 }
143- }
144- if ! parsed {
145- start = origstart
146- stop := len (line ) - util .TrimRightSpaceLength (line )
147- if stop <= start { // empty headings like '##[space]'
148- stop = start
149- } else {
150- i = stop - 1
151- for ; line [i ] == '#' && i >= start ; i -- {
152- }
153- if i != stop - 1 && ! util .IsSpace (line [i ]) {
154- i = stop - 1
155- }
156- i ++
133+ if i == 0 || util .IsSpace (line [i ]) {
157134 stop = i
158- }
135+ stop -= util . TrimRightSpaceLength ( line [ 0 : stop ])
159136
160- if len (util .TrimRight (line [start :stop ], []byte {'#' })) != 0 { // empty heading like '### ###'
161- node .Lines ().Append (text .NewSegment (segment .Start + start - segment .Padding , segment .Start + stop - segment .Padding ))
162137 }
163138 }
139+ hl .Stop = hl .Start + stop
140+ node .Lines ().Append (hl )
141+ reader .AdvanceToEOL ()
142+
164143 return node , NoChildren
165144}
166145
@@ -169,13 +148,6 @@ func (b *atxHeadingParser) Continue(node ast.Node, reader text.Reader, pc Contex
169148}
170149
171150func (b * atxHeadingParser ) Close (node ast.Node , reader text.Reader , pc Context ) {
172- if b .Attribute {
173- _ , ok := node .AttributeString ("id" )
174- if ! ok {
175- parseLastLineAttributes (node , reader , pc )
176- }
177- }
178-
179151 if b .AutoHeadingID {
180152 id , ok := node .AttributeString ("id" )
181153 if ! ok {
@@ -205,44 +177,44 @@ func generateAutoHeadingID(node *ast.Heading, reader text.Reader, pc Context) {
205177 node .SetAttribute (attrNameID , headingID )
206178}
207179
208- func parseLastLineAttributes (node ast.Node , reader text.Reader , pc Context ) {
180+ func parseLastLineAttributes (node ast.Node , reader text.Reader , _ Context ) {
209181 lastIndex := node .Lines ().Len () - 1
210182 if lastIndex < 0 { // empty headings
211183 return
212184 }
213185 lastLine := node .Lines ().At (lastIndex )
214186 line := lastLine .Value (reader .Source ())
215187 lr := text .NewReader (line )
216- var attrs Attributes
217- var ok bool
218188 var start text.Segment
219189 var sl int
220- var end text.Segment
221190 for {
222191 c := lr .Peek ()
223- if c == text .EOF {
192+ if c == text .EOF || c == '\n' {
224193 break
225194 }
226195 if c == '\\' {
227196 lr .Advance (1 )
228- if lr .Peek () == '{' {
197+ if util . IsPunct ( lr .Peek ()) {
229198 lr .Advance (1 )
230199 }
231200 continue
232201 }
233202 if c == '{' {
234203 sl , start = lr .Position ()
235- attrs , ok = ParseAttributes (lr )
236- _ , end = lr .Position ()
204+ attrs , ok := ParseAttributes (lr )
205+ if ok {
206+ if nl , _ := lr .PeekLine (); nl == nil || util .IsBlank (nl ) {
207+ for _ , attr := range attrs {
208+ node .SetAttribute (attr .Name , attr .Value )
209+ }
210+ lastLine .Stop = lastLine .Start + start .Start
211+ lastLine = lastLine .TrimRightSpace (reader .Source ())
212+ node .Lines ().Set (lastIndex , lastLine )
213+ return
214+ }
215+ }
237216 lr .SetPosition (sl , start )
238217 }
239218 lr .Advance (1 )
240219 }
241- if ok && util .IsBlank (line [end .Start :]) {
242- for _ , attr := range attrs {
243- node .SetAttribute (attr .Name , attr .Value )
244- }
245- lastLine .Stop = lastLine .Start + start .Start
246- node .Lines ().Set (lastIndex , lastLine )
247- }
248220}
0 commit comments