• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yuin / goldmark / 14774131132

01 May 2025 10:44AM UTC coverage: 82.826% (-0.03%) from 82.856%
14774131132

push

github

yuin
Cleanup codes

18 of 21 new or added lines in 3 files covered. (85.71%)

6202 of 7488 relevant lines covered (82.83%)

477254.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.46
/parser/parser.go
1
// Package parser contains stuff that are related to parsing a Markdown text.
2
package parser
3

4
import (
5
        "fmt"
6
        "strings"
7
        "sync"
8

9
        "github.com/yuin/goldmark/ast"
10
        "github.com/yuin/goldmark/text"
11
        "github.com/yuin/goldmark/util"
12
)
13

14
// A Reference interface represents a link reference in Markdown text.
15
type Reference interface {
16
        // String implements Stringer.
17
        String() string
18

19
        // Label returns a label of the reference.
20
        Label() []byte
21

22
        // Destination returns a destination(URL) of the reference.
23
        Destination() []byte
24

25
        // Title returns a title of the reference.
26
        Title() []byte
27
}
28

29
type reference struct {
30
        label       []byte
31
        destination []byte
32
        title       []byte
33
}
34

35
// NewReference returns a new Reference.
36
func NewReference(label, destination, title []byte) Reference {
525✔
37
        return &reference{label, destination, title}
525✔
38
}
525✔
39

40
func (r *reference) Label() []byte {
525✔
41
        return r.label
525✔
42
}
525✔
43

44
func (r *reference) Destination() []byte {
438✔
45
        return r.destination
438✔
46
}
438✔
47

48
func (r *reference) Title() []byte {
438✔
49
        return r.title
438✔
50
}
438✔
51

52
func (r *reference) String() string {
×
53
        return fmt.Sprintf("Reference{Label:%s, Destination:%s, Title:%s}", r.label, r.destination, r.title)
×
54
}
×
55

56
// An IDs interface is a collection of the element ids.
57
type IDs interface {
58
        // Generate generates a new element id.
59
        Generate(value []byte, kind ast.NodeKind) []byte
60

61
        // Put puts a given element id to the used ids table.
62
        Put(value []byte)
63
}
64

65
type ids struct {
66
        values map[string]bool
67
}
68

69
func newIDs() IDs {
4,530✔
70
        return &ids{
4,530✔
71
                values: map[string]bool{},
4,530✔
72
        }
4,530✔
73
}
4,530✔
74

75
func (s *ids) Generate(value []byte, kind ast.NodeKind) []byte {
210✔
76
        value = util.TrimLeftSpace(value)
210✔
77
        value = util.TrimRightSpace(value)
210✔
78
        result := []byte{}
210✔
79
        for i := 0; i < len(value); {
1,509✔
80
                v := value[i]
1,299✔
81
                l := util.UTF8Len(v)
1,299✔
82
                i += int(l)
1,299✔
83
                if l != 1 {
1,299✔
84
                        continue
×
85
                }
86
                if util.IsAlphaNumeric(v) {
2,304✔
87
                        if 'A' <= v && v <= 'Z' {
1,110✔
88
                                v += 'a' - 'A'
105✔
89
                        }
105✔
90
                        result = append(result, v)
1,005✔
91
                } else if util.IsSpace(v) || v == '-' || v == '_' {
372✔
92
                        result = append(result, '-')
78✔
93
                }
78✔
94
        }
95
        if len(result) == 0 {
222✔
96
                if kind == ast.KindHeading {
24✔
97
                        result = []byte("heading")
12✔
98
                } else {
12✔
99
                        result = []byte("id")
×
100
                }
×
101
        }
102
        if _, ok := s.values[util.BytesToReadOnlyString(result)]; !ok {
369✔
103
                s.values[util.BytesToReadOnlyString(result)] = true
159✔
104
                return result
159✔
105
        }
159✔
106
        for i := 1; ; i++ {
144✔
107
                newResult := fmt.Sprintf("%s-%d", result, i)
93✔
108
                if _, ok := s.values[newResult]; !ok {
144✔
109
                        s.values[newResult] = true
51✔
110
                        return []byte(newResult)
51✔
111
                }
51✔
112

113
        }
114
}
115

116
func (s *ids) Put(value []byte) {
24✔
117
        s.values[util.BytesToReadOnlyString(value)] = true
24✔
118
}
24✔
119

120
// ContextKey is a key that is used to set arbitrary values to the context.
121
type ContextKey int
122

123
// ContextKeyMax is a maximum value of the ContextKey.
124
var ContextKeyMax ContextKey
125

126
// NewContextKey return a new ContextKey value.
127
func NewContextKey() ContextKey {
96✔
128
        ContextKeyMax++
96✔
129
        return ContextKeyMax
96✔
130
}
96✔
131

132
// A Context interface holds a information that are necessary to parse
133
// Markdown text.
134
type Context interface {
135
        // String implements Stringer.
136
        String() string
137

138
        // Get returns a value associated with the given key.
139
        Get(ContextKey) interface{}
140

141
        // ComputeIfAbsent computes a value if a value associated with the given key is absent and returns the value.
142
        ComputeIfAbsent(ContextKey, func() interface{}) interface{}
143

144
        // Set sets the given value to the context.
145
        Set(ContextKey, interface{})
146

147
        // AddReference adds the given reference to this context.
148
        AddReference(Reference)
149

150
        // Reference returns (a reference, true) if a reference associated with
151
        // the given label exists, otherwise (nil, false).
152
        Reference(label string) (Reference, bool)
153

154
        // References returns a list of references.
155
        References() []Reference
156

157
        // IDs returns a collection of the element ids.
158
        IDs() IDs
159

160
        // BlockOffset returns a first non-space character position on current line.
161
        // This value is valid only for BlockParser.Open.
162
        // BlockOffset returns -1 if current line is blank.
163
        BlockOffset() int
164

165
        // BlockOffset sets a first non-space character position on current line.
166
        // This value is valid only for BlockParser.Open.
167
        SetBlockOffset(int)
168

169
        // BlockIndent returns an indent width on current line.
170
        // This value is valid only for BlockParser.Open.
171
        // BlockIndent returns -1 if current line is blank.
172
        BlockIndent() int
173

174
        // BlockIndent sets an indent width on current line.
175
        // This value is valid only for BlockParser.Open.
176
        SetBlockIndent(int)
177

178
        // FirstDelimiter returns a first delimiter of the current delimiter list.
179
        FirstDelimiter() *Delimiter
180

181
        // LastDelimiter returns a last delimiter of the current delimiter list.
182
        LastDelimiter() *Delimiter
183

184
        // PushDelimiter appends the given delimiter to the tail of the current
185
        // delimiter list.
186
        PushDelimiter(delimiter *Delimiter)
187

188
        // RemoveDelimiter removes the given delimiter from the current delimiter list.
189
        RemoveDelimiter(d *Delimiter)
190

191
        // ClearDelimiters clears the current delimiter list.
192
        ClearDelimiters(bottom ast.Node)
193

194
        // OpenedBlocks returns a list of nodes that are currently in parsing.
195
        OpenedBlocks() []Block
196

197
        // SetOpenedBlocks sets a list of nodes that are currently in parsing.
198
        SetOpenedBlocks([]Block)
199

200
        // LastOpenedBlock returns a last node that is currently in parsing.
201
        LastOpenedBlock() Block
202

203
        // IsInLinkLabel returns true if current position seems to be in link label.
204
        IsInLinkLabel() bool
205
}
206

207
// A ContextConfig struct is a data structure that holds configuration of the Context.
208
type ContextConfig struct {
209
        IDs IDs
210
}
211

212
// An ContextOption is a functional option type for the Context.
213
type ContextOption func(*ContextConfig)
214

215
// WithIDs is a functional option for the Context.
216
func WithIDs(ids IDs) ContextOption {
3✔
217
        return func(c *ContextConfig) {
6✔
218
                c.IDs = ids
3✔
219
        }
3✔
220
}
221

222
type parseContext struct {
223
        store         []interface{}
224
        ids           IDs
225
        refs          map[string]Reference
226
        blockOffset   int
227
        blockIndent   int
228
        delimiters    *Delimiter
229
        lastDelimiter *Delimiter
230
        openedBlocks  []Block
231
}
232

233
// NewContext returns a new Context.
234
func NewContext(options ...ContextOption) Context {
4,530✔
235
        cfg := &ContextConfig{
4,530✔
236
                IDs: newIDs(),
4,530✔
237
        }
4,530✔
238
        for _, option := range options {
4,533✔
239
                option(cfg)
3✔
240
        }
3✔
241

242
        return &parseContext{
4,530✔
243
                store:         make([]interface{}, ContextKeyMax+1),
4,530✔
244
                refs:          map[string]Reference{},
4,530✔
245
                ids:           cfg.IDs,
4,530✔
246
                blockOffset:   -1,
4,530✔
247
                blockIndent:   -1,
4,530✔
248
                delimiters:    nil,
4,530✔
249
                lastDelimiter: nil,
4,530✔
250
                openedBlocks:  []Block{},
4,530✔
251
        }
4,530✔
252
}
253

254
func (p *parseContext) Get(key ContextKey) interface{} {
1,689,192✔
255
        return p.store[key]
1,689,192✔
256
}
1,689,192✔
257

258
func (p *parseContext) ComputeIfAbsent(key ContextKey, f func() interface{}) interface{} {
3✔
259
        v := p.store[key]
3✔
260
        if v == nil {
6✔
261
                v = f()
3✔
262
                p.store[key] = v
3✔
263
        }
3✔
264
        return v
3✔
265
}
266

267
func (p *parseContext) Set(key ContextKey, value interface{}) {
926,331✔
268
        p.store[key] = value
926,331✔
269
}
926,331✔
270

271
func (p *parseContext) IDs() IDs {
240✔
272
        return p.ids
240✔
273
}
240✔
274

275
func (p *parseContext) BlockOffset() int {
2,241✔
276
        return p.blockOffset
2,241✔
277
}
2,241✔
278

279
func (p *parseContext) SetBlockOffset(v int) {
12,291✔
280
        p.blockOffset = v
12,291✔
281
}
12,291✔
282

283
func (p *parseContext) BlockIndent() int {
90✔
284
        return p.blockIndent
90✔
285
}
90✔
286

287
func (p *parseContext) SetBlockIndent(v int) {
12,291✔
288
        p.blockIndent = v
12,291✔
289
}
12,291✔
290

291
func (p *parseContext) LastDelimiter() *Delimiter {
465,336✔
292
        return p.lastDelimiter
465,336✔
293
}
465,336✔
294

295
func (p *parseContext) FirstDelimiter() *Delimiter {
1,113✔
296
        return p.delimiters
1,113✔
297
}
1,113✔
298

299
func (p *parseContext) PushDelimiter(d *Delimiter) {
2,778✔
300
        if p.delimiters == nil {
3,975✔
301
                p.delimiters = d
1,197✔
302
                p.lastDelimiter = d
1,197✔
303
        } else {
2,778✔
304
                l := p.lastDelimiter
1,581✔
305
                p.lastDelimiter = d
1,581✔
306
                l.NextDelimiter = d
1,581✔
307
                d.PreviousDelimiter = l
1,581✔
308
        }
1,581✔
309
}
310

311
func (p *parseContext) RemoveDelimiter(d *Delimiter) {
2,778✔
312
        if d.PreviousDelimiter == nil {
4,800✔
313
                p.delimiters = d.NextDelimiter
2,022✔
314
        } else {
2,778✔
315
                d.PreviousDelimiter.NextDelimiter = d.NextDelimiter
756✔
316
                if d.NextDelimiter != nil {
1,215✔
317
                        d.NextDelimiter.PreviousDelimiter = d.PreviousDelimiter
459✔
318
                }
459✔
319
        }
320
        if d.NextDelimiter == nil {
4,272✔
321
                p.lastDelimiter = d.PreviousDelimiter
1,494✔
322
        }
1,494✔
323
        if p.delimiters != nil {
4,359✔
324
                p.delimiters.PreviousDelimiter = nil
1,581✔
325
        }
1,581✔
326
        if p.lastDelimiter != nil {
4,359✔
327
                p.lastDelimiter.NextDelimiter = nil
1,581✔
328
        }
1,581✔
329
        d.NextDelimiter = nil
2,778✔
330
        d.PreviousDelimiter = nil
2,778✔
331
        if d.Length != 0 {
3,621✔
332
                ast.MergeOrReplaceTextSegment(d.Parent(), d, d.Segment)
843✔
333
        } else {
2,778✔
334
                d.Parent().RemoveChild(d.Parent(), d)
1,935✔
335
        }
1,935✔
336
}
337

338
func (p *parseContext) ClearDelimiters(bottom ast.Node) {
1,275✔
339
        if p.lastDelimiter == nil {
2,055✔
340
                return
780✔
341
        }
780✔
342
        var c ast.Node
495✔
343
        for c = p.lastDelimiter; c != nil && c != bottom; {
1,479✔
344
                prev := c.PreviousSibling()
984✔
345
                if d, ok := c.(*Delimiter); ok {
1,572✔
346
                        p.RemoveDelimiter(d)
588✔
347
                }
588✔
348
                c = prev
984✔
349
        }
350
}
351

352
func (p *parseContext) AddReference(ref Reference) {
525✔
353
        key := util.ToLinkReference(ref.Label())
525✔
354
        if _, ok := p.refs[key]; !ok {
1,038✔
355
                p.refs[key] = ref
513✔
356
        }
513✔
357
}
358

359
func (p *parseContext) Reference(label string) (Reference, bool) {
807✔
360
        v, ok := p.refs[label]
807✔
361
        return v, ok
807✔
362
}
807✔
363

364
func (p *parseContext) References() []Reference {
×
365
        ret := make([]Reference, 0, len(p.refs))
×
366
        for _, v := range p.refs {
×
367
                ret = append(ret, v)
×
368
        }
×
369
        return ret
×
370
}
371

372
func (p *parseContext) String() string {
×
373
        refs := []string{}
×
374
        for _, r := range p.refs {
×
375
                refs = append(refs, r.String())
×
376
        }
×
377

378
        return fmt.Sprintf("Context{Store:%#v, Refs:%s}", p.store, strings.Join(refs, ","))
×
379
}
380

381
func (p *parseContext) OpenedBlocks() []Block {
28,122✔
382
        return p.openedBlocks
28,122✔
383
}
28,122✔
384

385
func (p *parseContext) SetOpenedBlocks(v []Block) {
21,390✔
386
        p.openedBlocks = v
21,390✔
387
}
21,390✔
388

389
func (p *parseContext) LastOpenedBlock() Block {
36,558✔
390
        if l := len(p.openedBlocks); l != 0 {
52,494✔
391
                return p.openedBlocks[l-1]
15,936✔
392
        }
15,936✔
393
        return Block{}
20,622✔
394
}
395

396
func (p *parseContext) IsInLinkLabel() bool {
7,530✔
397
        tlist := p.Get(linkLabelStateKey)
7,530✔
398
        return tlist != nil
7,530✔
399
}
7,530✔
400

401
// State represents parser's state.
402
// State is designed to use as a bit flag.
403
type State int
404

405
const (
406
        // None is a default value of the [State].
407
        None State = 1 << iota
408

409
        // Continue indicates parser can continue parsing.
410
        Continue
411

412
        // Close indicates parser cannot parse anymore.
413
        Close
414

415
        // HasChildren indicates parser may have child blocks.
416
        HasChildren
417

418
        // NoChildren indicates parser does not have child blocks.
419
        NoChildren
420

421
        // RequireParagraph indicates parser requires that the last node
422
        // must be a paragraph and is not converted to other nodes by
423
        // ParagraphTransformers.
424
        RequireParagraph
425
)
426

427
// A Config struct is a data structure that holds configuration of the Parser.
428
type Config struct {
429
        Options               map[OptionName]interface{}
430
        BlockParsers          util.PrioritizedSlice /*<BlockParser>*/
431
        InlineParsers         util.PrioritizedSlice /*<InlineParser>*/
432
        ParagraphTransformers util.PrioritizedSlice /*<ParagraphTransformer>*/
433
        ASTTransformers       util.PrioritizedSlice /*<ASTTransformer>*/
434
        EscapedSpace          bool
435
}
436

437
// NewConfig returns a new Config.
438
func NewConfig() *Config {
2,139✔
439
        return &Config{
2,139✔
440
                Options:               map[OptionName]interface{}{},
2,139✔
441
                BlockParsers:          util.PrioritizedSlice{},
2,139✔
442
                InlineParsers:         util.PrioritizedSlice{},
2,139✔
443
                ParagraphTransformers: util.PrioritizedSlice{},
2,139✔
444
                ASTTransformers:       util.PrioritizedSlice{},
2,139✔
445
        }
2,139✔
446
}
2,139✔
447

448
// An Option interface is a functional option type for the Parser.
449
type Option interface {
450
        SetParserOption(*Config)
451
}
452

453
// OptionName is a name of parser options.
454
type OptionName string
455

456
// Attribute is an option name that spacify attributes of elements.
457
const optAttribute OptionName = "Attribute"
458

459
type withAttribute struct {
460
}
461

462
func (o *withAttribute) SetParserOption(c *Config) {
1,959✔
463
        c.Options[optAttribute] = true
1,959✔
464
}
1,959✔
465

466
// WithAttribute is a functional option that enables custom attributes.
467
func WithAttribute() Option {
1,959✔
468
        return &withAttribute{}
1,959✔
469
}
1,959✔
470

471
// A Parser interface parses Markdown text into AST nodes.
472
type Parser interface {
473
        // Parse parses the given Markdown text into AST nodes.
474
        Parse(reader text.Reader, opts ...ParseOption) ast.Node
475

476
        // AddOption adds the given option to this parser.
477
        AddOptions(...Option)
478
}
479

480
// A SetOptioner interface sets the given option to the object.
481
type SetOptioner interface {
482
        // SetOption sets the given option to the object.
483
        // Unacceptable options may be passed.
484
        // Thus implementations must ignore unacceptable options.
485
        SetOption(name OptionName, value interface{})
486
}
487

488
// A BlockParser interface parses a block level element like Paragraph, List,
489
// Blockquote etc.
490
type BlockParser interface {
491
        // Trigger returns a list of characters that triggers Parse method of
492
        // this parser.
493
        // If Trigger returns a nil, Open will be called with any lines.
494
        Trigger() []byte
495

496
        // Open parses the current line and returns a result of parsing.
497
        //
498
        // Open must not parse beyond the current line.
499
        // If Open has been able to parse the current line, Open must advance a reader
500
        // position by consumed byte length.
501
        //
502
        // If Open has not been able to parse the current line, Open should returns
503
        // (nil, NoChildren). If Open has been able to parse the current line, Open
504
        // should returns a new Block node and returns HasChildren or NoChildren.
505
        Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State)
506

507
        // Continue parses the current line and returns a result of parsing.
508
        //
509
        // Continue must not parse beyond the current line.
510
        // If Continue has been able to parse the current line, Continue must advance
511
        // a reader position by consumed byte length.
512
        //
513
        // If Continue has not been able to parse the current line, Continue should
514
        // returns Close. If Continue has been able to parse the current line,
515
        // Continue should returns (Continue | NoChildren) or
516
        // (Continue | HasChildren)
517
        Continue(node ast.Node, reader text.Reader, pc Context) State
518

519
        // Close will be called when the parser returns Close.
520
        Close(node ast.Node, reader text.Reader, pc Context)
521

522
        // CanInterruptParagraph returns true if the parser can interrupt paragraphs,
523
        // otherwise false.
524
        CanInterruptParagraph() bool
525

526
        // CanAcceptIndentedLine returns true if the parser can open new node when
527
        // the given line is being indented more than 3 spaces.
528
        CanAcceptIndentedLine() bool
529
}
530

531
// An InlineParser interface parses an inline level element like CodeSpan, Link etc.
532
type InlineParser interface {
533
        // Trigger returns a list of characters that triggers Parse method of
534
        // this parser.
535
        // Trigger characters must be a punctuation or a halfspace.
536
        // Halfspaces triggers this parser when character is any spaces characters or
537
        // a head of line
538
        Trigger() []byte
539

540
        // Parse parse the given block into an inline node.
541
        //
542
        // Parse can parse beyond the current line.
543
        // If Parse has been able to parse the current line, it must advance a reader
544
        // position by consumed byte length.
545
        Parse(parent ast.Node, block text.Reader, pc Context) ast.Node
546
}
547

548
// A CloseBlocker interface is a callback function that will be
549
// called when block is closed in the inline parsing.
550
type CloseBlocker interface {
551
        // CloseBlock will be called when a block is closed.
552
        CloseBlock(parent ast.Node, block text.Reader, pc Context)
553
}
554

555
// A ParagraphTransformer transforms parsed Paragraph nodes.
556
// For example, link references are searched in parsed Paragraphs.
557
type ParagraphTransformer interface {
558
        // Transform transforms the given paragraph.
559
        Transform(node *ast.Paragraph, reader text.Reader, pc Context)
560
}
561

562
// ASTTransformer transforms entire Markdown document AST tree.
563
type ASTTransformer interface {
564
        // Transform transforms the given AST tree.
565
        Transform(node *ast.Document, reader text.Reader, pc Context)
566
}
567

568
// DefaultBlockParsers returns a new list of default BlockParsers.
569
// Priorities of default BlockParsers are:
570
//
571
//        SetextHeadingParser, 100
572
//        ThematicBreakParser, 200
573
//        ListParser, 300
574
//        ListItemParser, 400
575
//        CodeBlockParser, 500
576
//        ATXHeadingParser, 600
577
//        FencedCodeBlockParser, 700
578
//        BlockquoteParser, 800
579
//        HTMLBlockParser, 900
580
//        ParagraphParser, 1000
581
func DefaultBlockParsers() []util.PrioritizedValue {
2,139✔
582
        return []util.PrioritizedValue{
2,139✔
583
                util.Prioritized(NewSetextHeadingParser(), 100),
2,139✔
584
                util.Prioritized(NewThematicBreakParser(), 200),
2,139✔
585
                util.Prioritized(NewListParser(), 300),
2,139✔
586
                util.Prioritized(NewListItemParser(), 400),
2,139✔
587
                util.Prioritized(NewCodeBlockParser(), 500),
2,139✔
588
                util.Prioritized(NewATXHeadingParser(), 600),
2,139✔
589
                util.Prioritized(NewFencedCodeBlockParser(), 700),
2,139✔
590
                util.Prioritized(NewBlockquoteParser(), 800),
2,139✔
591
                util.Prioritized(NewHTMLBlockParser(), 900),
2,139✔
592
                util.Prioritized(NewParagraphParser(), 1000),
2,139✔
593
        }
2,139✔
594
}
2,139✔
595

596
// DefaultInlineParsers returns a new list of default InlineParsers.
597
// Priorities of default InlineParsers are:
598
//
599
//        CodeSpanParser, 100
600
//        LinkParser, 200
601
//        AutoLinkParser, 300
602
//        RawHTMLParser, 400
603
//        EmphasisParser, 500
604
func DefaultInlineParsers() []util.PrioritizedValue {
2,139✔
605
        return []util.PrioritizedValue{
2,139✔
606
                util.Prioritized(NewCodeSpanParser(), 100),
2,139✔
607
                util.Prioritized(NewLinkParser(), 200),
2,139✔
608
                util.Prioritized(NewAutoLinkParser(), 300),
2,139✔
609
                util.Prioritized(NewRawHTMLParser(), 400),
2,139✔
610
                util.Prioritized(NewEmphasisParser(), 500),
2,139✔
611
        }
2,139✔
612
}
2,139✔
613

614
// DefaultParagraphTransformers returns a new list of default ParagraphTransformers.
615
// Priorities of default ParagraphTransformers are:
616
//
617
//        LinkReferenceParagraphTransformer, 100
618
func DefaultParagraphTransformers() []util.PrioritizedValue {
2,139✔
619
        return []util.PrioritizedValue{
2,139✔
620
                util.Prioritized(LinkReferenceParagraphTransformer, 100),
2,139✔
621
        }
2,139✔
622
}
2,139✔
623

624
// A Block struct holds a node and correspond parser pair.
625
type Block struct {
626
        // Node is a BlockNode.
627
        Node ast.Node
628
        // Parser is a BlockParser.
629
        Parser BlockParser
630
}
631

632
type parser struct {
633
        options               map[OptionName]interface{}
634
        blockParsers          [256][]BlockParser
635
        freeBlockParsers      []BlockParser
636
        inlineParsers         [256][]InlineParser
637
        closeBlockers         []CloseBlocker
638
        paragraphTransformers []ParagraphTransformer
639
        astTransformers       []ASTTransformer
640
        escapedSpace          bool
641
        config                *Config
642
        initSync              sync.Once
643
}
644

645
type withBlockParsers struct {
646
        value []util.PrioritizedValue
647
}
648

649
func (o *withBlockParsers) SetParserOption(c *Config) {
6,069✔
650
        c.BlockParsers = append(c.BlockParsers, o.value...)
6,069✔
651
}
6,069✔
652

653
// WithBlockParsers is a functional option that allow you to add
654
// BlockParsers to the parser.
655
func WithBlockParsers(bs ...util.PrioritizedValue) Option {
6,069✔
656
        return &withBlockParsers{bs}
6,069✔
657
}
6,069✔
658

659
type withInlineParsers struct {
660
        value []util.PrioritizedValue
661
}
662

663
func (o *withInlineParsers) SetParserOption(c *Config) {
15,870✔
664
        c.InlineParsers = append(c.InlineParsers, o.value...)
15,870✔
665
}
15,870✔
666

667
// WithInlineParsers is a functional option that allow you to add
668
// InlineParsers to the parser.
669
func WithInlineParsers(bs ...util.PrioritizedValue) Option {
15,870✔
670
        return &withInlineParsers{bs}
15,870✔
671
}
15,870✔
672

673
type withParagraphTransformers struct {
674
        value []util.PrioritizedValue
675
}
676

677
func (o *withParagraphTransformers) SetParserOption(c *Config) {
6,087✔
678
        c.ParagraphTransformers = append(c.ParagraphTransformers, o.value...)
6,087✔
679
}
6,087✔
680

681
// WithParagraphTransformers is a functional option that allow you to add
682
// ParagraphTransformers to the parser.
683
func WithParagraphTransformers(ps ...util.PrioritizedValue) Option {
6,087✔
684
        return &withParagraphTransformers{ps}
6,087✔
685
}
6,087✔
686

687
type withASTTransformers struct {
688
        value []util.PrioritizedValue
689
}
690

691
func (o *withASTTransformers) SetParserOption(c *Config) {
5,919✔
692
        c.ASTTransformers = append(c.ASTTransformers, o.value...)
5,919✔
693
}
5,919✔
694

695
// WithASTTransformers is a functional option that allow you to add
696
// ASTTransformers to the parser.
697
func WithASTTransformers(ps ...util.PrioritizedValue) Option {
5,919✔
698
        return &withASTTransformers{ps}
5,919✔
699
}
5,919✔
700

701
type withEscapedSpace struct {
702
}
703

704
func (o *withEscapedSpace) SetParserOption(c *Config) {
6✔
705
        c.EscapedSpace = true
6✔
706
}
6✔
707

708
// WithEscapedSpace is a functional option indicates that a '\' escaped half-space(0x20) should not trigger parsers.
709
func WithEscapedSpace() Option {
6✔
710
        return &withEscapedSpace{}
6✔
711
}
6✔
712

713
type withOption struct {
714
        name  OptionName
715
        value interface{}
716
}
717

718
func (o *withOption) SetParserOption(c *Config) {
×
719
        c.Options[o.name] = o.value
×
720
}
×
721

722
// WithOption is a functional option that allow you to set
723
// an arbitrary option to the parser.
724
func WithOption(name OptionName, value interface{}) Option {
×
725
        return &withOption{name, value}
×
726
}
×
727

728
// NewParser returns a new Parser with given options.
729
func NewParser(options ...Option) Parser {
2,139✔
730
        config := NewConfig()
2,139✔
731
        for _, opt := range options {
8,556✔
732
                opt.SetParserOption(config)
6,417✔
733
        }
6,417✔
734

735
        p := &parser{
2,139✔
736
                options: map[OptionName]interface{}{},
2,139✔
737
                config:  config,
2,139✔
738
        }
2,139✔
739

2,139✔
740
        return p
2,139✔
741
}
742

743
func (p *parser) AddOptions(opts ...Option) {
21,618✔
744
        for _, opt := range opts {
53,073✔
745
                opt.SetParserOption(p.config)
31,455✔
746
        }
31,455✔
747
}
748

749
func (p *parser) addBlockParser(v util.PrioritizedValue, options map[OptionName]interface{}) {
27,165✔
750
        bp, ok := v.Value.(BlockParser)
27,165✔
751
        if !ok {
27,165✔
752
                panic(fmt.Sprintf("%v is not a BlockParser", v.Value))
×
753
        }
754
        tcs := bp.Trigger()
27,165✔
755
        so, ok := v.Value.(SetOptioner)
27,165✔
756
        if ok {
31,419✔
757
                for oname, ovalue := range options {
12,096✔
758
                        so.SetOption(oname, ovalue)
7,842✔
759
                }
7,842✔
760
        }
761
        if tcs == nil {
31,419✔
762
                p.freeBlockParsers = append(p.freeBlockParsers, bp)
4,254✔
763
        } else {
27,165✔
764
                for _, tc := range tcs {
105,378✔
765
                        if p.blockParsers[tc] == nil {
128,937✔
766
                                p.blockParsers[tc] = []BlockParser{}
46,470✔
767
                        }
46,470✔
768
                        p.blockParsers[tc] = append(p.blockParsers[tc], bp)
82,467✔
769
                }
770
        }
771
}
772

773
func (p *parser) addInlineParser(v util.PrioritizedValue, options map[OptionName]interface{}) {
24,366✔
774
        ip, ok := v.Value.(InlineParser)
24,366✔
775
        if !ok {
24,366✔
776
                panic(fmt.Sprintf("%v is not a InlineParser", v.Value))
×
777
        }
778
        tcs := ip.Trigger()
24,366✔
779
        so, ok := v.Value.(SetOptioner)
24,366✔
780
        if ok {
30,255✔
781
                for oname, ovalue := range options {
17,625✔
782
                        so.SetOption(oname, ovalue)
11,736✔
783
                }
11,736✔
784
        }
785
        if cb, ok := ip.(CloseBlocker); ok {
26,493✔
786
                p.closeBlockers = append(p.closeBlockers, cb)
2,127✔
787
        }
2,127✔
788
        for _, tc := range tcs {
88,470✔
789
                if p.inlineParsers[tc] == nil {
96,675✔
790
                        p.inlineParsers[tc] = []InlineParser{}
32,571✔
791
                }
32,571✔
792
                p.inlineParsers[tc] = append(p.inlineParsers[tc], ip)
64,104✔
793
        }
794
}
795

796
func (p *parser) addParagraphTransformer(v util.PrioritizedValue, options map[OptionName]interface{}) {
6,075✔
797
        pt, ok := v.Value.(ParagraphTransformer)
6,075✔
798
        if !ok {
6,075✔
799
                panic(fmt.Sprintf("%v is not a ParagraphTransformer", v.Value))
×
800
        }
801
        so, ok := v.Value.(SetOptioner)
6,075✔
802
        if ok {
6,075✔
803
                for oname, ovalue := range options {
×
804
                        so.SetOption(oname, ovalue)
×
805
                }
×
806
        }
807
        p.paragraphTransformers = append(p.paragraphTransformers, pt)
6,075✔
808
}
809

810
func (p *parser) addASTTransformer(v util.PrioritizedValue, options map[OptionName]interface{}) {
5,919✔
811
        at, ok := v.Value.(ASTTransformer)
5,919✔
812
        if !ok {
5,919✔
813
                panic(fmt.Sprintf("%v is not a ASTTransformer", v.Value))
×
814
        }
815
        so, ok := v.Value.(SetOptioner)
5,919✔
816
        if ok {
5,919✔
817
                for oname, ovalue := range options {
×
818
                        so.SetOption(oname, ovalue)
×
819
                }
×
820
        }
821
        p.astTransformers = append(p.astTransformers, at)
5,919✔
822
}
823

824
// A ParseConfig struct is a data structure that holds configuration of the Parser.Parse.
825
type ParseConfig struct {
826
        Context Context
827
}
828

829
// A ParseOption is a functional option type for the Parser.Parse.
830
type ParseOption func(c *ParseConfig)
831

832
// WithContext is a functional option that allow you to override
833
// a default context.
834
func WithContext(context Context) ParseOption {
3✔
835
        return func(c *ParseConfig) {
6✔
836
                c.Context = context
3✔
837
        }
3✔
838
}
839

840
func (p *parser) Parse(reader text.Reader, opts ...ParseOption) ast.Node {
4,530✔
841
        p.initSync.Do(func() {
6,657✔
842
                p.config.BlockParsers.Sort()
2,127✔
843
                for _, v := range p.config.BlockParsers {
29,292✔
844
                        p.addBlockParser(v, p.config.Options)
27,165✔
845
                }
27,165✔
846
                for i := range p.blockParsers {
546,639✔
847
                        if p.blockParsers[i] != nil {
590,982✔
848
                                p.blockParsers[i] = append(p.blockParsers[i], p.freeBlockParsers...)
46,470✔
849
                        }
46,470✔
850
                }
851

852
                p.config.InlineParsers.Sort()
2,127✔
853
                for _, v := range p.config.InlineParsers {
26,493✔
854
                        p.addInlineParser(v, p.config.Options)
24,366✔
855
                }
24,366✔
856
                p.config.ParagraphTransformers.Sort()
2,127✔
857
                for _, v := range p.config.ParagraphTransformers {
8,202✔
858
                        p.addParagraphTransformer(v, p.config.Options)
6,075✔
859
                }
6,075✔
860
                p.config.ASTTransformers.Sort()
2,127✔
861
                for _, v := range p.config.ASTTransformers {
8,046✔
862
                        p.addASTTransformer(v, p.config.Options)
5,919✔
863
                }
5,919✔
864
                p.escapedSpace = p.config.EscapedSpace
2,127✔
865
                p.config = nil
2,127✔
866
        })
867
        c := &ParseConfig{}
4,530✔
868
        for _, opt := range opts {
4,533✔
869
                opt(c)
3✔
870
        }
3✔
871
        if c.Context == nil {
9,057✔
872
                c.Context = NewContext()
4,527✔
873
        }
4,527✔
874
        pc := c.Context
4,530✔
875
        root := ast.NewDocument()
4,530✔
876
        p.parseBlocks(root, reader, pc)
4,530✔
877

4,530✔
878
        blockReader := text.NewBlockReader(reader.Source(), nil)
4,530✔
879
        p.walkBlock(root, func(node ast.Node) {
18,657✔
880
                p.parseBlock(blockReader, node, pc)
14,127✔
881
        })
14,127✔
882
        for _, at := range p.astTransformers {
10,497✔
883
                at.Transform(root, reader, pc)
5,967✔
884
        }
5,967✔
885

886
        // root.Dump(reader.Source(), 0)
887
        return root
4,530✔
888
}
889

890
func (p *parser) transformParagraph(node *ast.Paragraph, reader text.Reader, pc Context) bool {
5,415✔
891
        for _, pt := range p.paragraphTransformers {
15,069✔
892
                pt.Transform(node, reader, pc)
9,654✔
893
                if node.Parent() == nil {
10,173✔
894
                        return true
519✔
895
                }
519✔
896
        }
897
        return false
4,896✔
898
}
899

900
func (p *parser) closeBlocks(from, to int, reader text.Reader, pc Context) {
7,317✔
901
        blocks := pc.OpenedBlocks()
7,317✔
902
        for i := from; i >= to; i-- {
16,512✔
903
                node := blocks[i].Node
9,195✔
904
                paragraph, ok := node.(*ast.Paragraph)
9,195✔
905
                if ok && node.Parent() != nil {
14,436✔
906
                        p.transformParagraph(paragraph, reader, pc)
5,241✔
907
                }
5,241✔
908
                if node.Parent() != nil { // closes only if node has not been transformed
17,868✔
909
                        blocks[i].Parser.Close(blocks[i].Node, reader, pc)
8,673✔
910
                }
8,673✔
911
        }
912
        if from == len(blocks)-1 {
13,302✔
913
                blocks = blocks[0:to]
5,985✔
914
        } else {
7,317✔
915
                blocks = append(blocks[0:to], blocks[from+1:]...)
1,332✔
916
        }
1,332✔
917
        pc.SetOpenedBlocks(blocks)
7,317✔
918
}
919

920
type blockOpenResult int
921

922
const (
923
        paragraphContinuation blockOpenResult = iota + 1
924
        newBlocksOpened
925
        noBlocksOpened
926
)
927

928
func (p *parser) openBlocks(parent ast.Node, blankLine bool, reader text.Reader, pc Context) blockOpenResult {
10,047✔
929
        result := blockOpenResult(noBlocksOpened)
10,047✔
930
        continuable := false
10,047✔
931
        lastBlock := pc.LastOpenedBlock()
10,047✔
932
        if lastBlock.Node != nil {
14,517✔
933
                continuable = ast.IsParagraph(lastBlock.Node)
4,470✔
934
        }
4,470✔
935
retry:
936
        var bps []BlockParser
12,291✔
937
        line, _ := reader.PeekLine()
12,291✔
938
        w, pos := util.IndentWidth(line, reader.LineOffset())
12,291✔
939
        if w >= len(line) {
12,345✔
940
                pc.SetBlockOffset(-1)
54✔
941
                pc.SetBlockIndent(-1)
54✔
942
        } else {
12,291✔
943
                pc.SetBlockOffset(pos)
12,237✔
944
                pc.SetBlockIndent(w)
12,237✔
945
        }
12,237✔
946
        if line == nil || line[0] == '\n' {
13,977✔
947
                goto continuable
1,686✔
948
        }
949
        bps = p.freeBlockParsers
10,605✔
950
        if pos < len(line) {
21,210✔
951
                bps = p.blockParsers[line[pos]]
10,605✔
952
                if bps == nil {
15,375✔
953
                        bps = p.freeBlockParsers
4,770✔
954
                }
4,770✔
955
        }
956
        if bps == nil {
10,605✔
957
                goto continuable
×
958
        }
959

960
        for _, bp := range bps {
35,184✔
961
                if continuable && result == noBlocksOpened && !bp.CanInterruptParagraph() {
27,027✔
962
                        continue
2,448✔
963
                }
964
                if w > 3 && !bp.CanAcceptIndentedLine() {
22,344✔
965
                        continue
213✔
966
                }
967
                lastBlock = pc.LastOpenedBlock()
21,918✔
968
                last := lastBlock.Node
21,918✔
969
                node, state := bp.Open(parent, reader, pc)
21,918✔
970
                if node != nil {
31,293✔
971
                        // Parser requires last node to be a paragraph.
9,375✔
972
                        // With table extension:
9,375✔
973
                        //
9,375✔
974
                        //     0
9,375✔
975
                        //     -:
9,375✔
976
                        //     -
9,375✔
977
                        //
9,375✔
978
                        // '-' on 3rd line seems a Setext heading because 1st and 2nd lines
9,375✔
979
                        // are being paragraph when the Settext heading parser tries to parse the 3rd
9,375✔
980
                        // line.
9,375✔
981
                        // But 1st line and 2nd line are a table. Thus this paragraph will be transformed
9,375✔
982
                        // by a paragraph transformer. So this text should be converted to a table and
9,375✔
983
                        // an empty list.
9,375✔
984
                        if state&RequireParagraph != 0 {
9,555✔
985
                                if last == parent.LastChild() {
354✔
986
                                        // Opened paragraph may be transformed by ParagraphTransformers in
174✔
987
                                        // closeBlocks().
174✔
988
                                        lastBlock.Parser.Close(last, reader, pc)
174✔
989
                                        blocks := pc.OpenedBlocks()
174✔
990
                                        pc.SetOpenedBlocks(blocks[0 : len(blocks)-1])
174✔
991
                                        if p.transformParagraph(last.(*ast.Paragraph), reader, pc) {
180✔
992
                                                // Paragraph has been transformed.
6✔
993
                                                // So this parser is considered as failing.
6✔
994
                                                continuable = false
6✔
995
                                                goto retry
6✔
996
                                        }
997
                                }
998
                        }
999
                        node.SetBlankPreviousLines(blankLine)
9,369✔
1000
                        if last != nil && last.Parent() == nil {
9,369✔
1001
                                lastPos := len(pc.OpenedBlocks()) - 1
×
1002
                                p.closeBlocks(lastPos, lastPos, reader, pc)
×
1003
                        }
×
1004
                        parent.AppendChild(parent, node)
9,369✔
1005
                        result = newBlocksOpened
9,369✔
1006
                        be := Block{node, bp}
9,369✔
1007
                        pc.SetOpenedBlocks(append(pc.OpenedBlocks(), be))
9,369✔
1008
                        if state&HasChildren != 0 {
11,607✔
1009
                                parent = node
2,238✔
1010
                                goto retry // try child block
2,238✔
1011
                        }
1012
                        break // no children, can not open more blocks on this line
7,131✔
1013
                }
1014
        }
1015

1016
continuable:
1017
        if result == noBlocksOpened && continuable {
12,429✔
1018
                state := lastBlock.Parser.Continue(lastBlock.Node, reader, pc)
2,382✔
1019
                if state&Continue != 0 {
3,594✔
1020
                        result = paragraphContinuation
1,212✔
1021
                }
1,212✔
1022
        }
1023
        return result
10,047✔
1024
}
1025

1026
type lineStat struct {
1027
        lineNum int
1028
        level   int
1029
        isBlank bool
1030
}
1031

1032
func isBlankLine(lineNum, level int, stats []lineStat) bool {
4,470✔
1033
        l := len(stats)
4,470✔
1034
        if l == 0 {
4,470✔
NEW
1035
                return true
×
NEW
1036
        }
×
1037
        for i := l - 1 - level; i >= 0; i-- {
10,995✔
1038
                s := stats[i]
6,525✔
1039
                if s.lineNum == lineNum && s.level <= level {
8,571✔
1040
                        return s.isBlank
2,046✔
1041
                } else if s.lineNum < lineNum {
6,525✔
NEW
1042
                        break
×
1043
                }
1044
        }
1045
        return false
2,424✔
1046
}
1047

1048
func (p *parser) parseBlocks(parent ast.Node, reader text.Reader, pc Context) {
4,530✔
1049
        pc.SetOpenedBlocks(nil)
4,530✔
1050
        blankLines := make([]lineStat, 0, 128)
4,530✔
1051
        for { // process blocks separated by blank lines
10,266✔
1052
                _, _, ok := reader.SkipBlankLines()
5,736✔
1053
                if !ok {
5,895✔
1054
                        return
159✔
1055
                }
159✔
1056
                // first, we try to open blocks
1057
                if p.openBlocks(parent, true, reader, pc) != newBlocksOpened {
5,577✔
1058
                        return
×
1059
                }
×
1060
                reader.AdvanceLine()
5,577✔
1061
                blankLines = blankLines[0:0]
5,577✔
1062
                for { // process opened blocks line by line
16,839✔
1063
                        openedBlocks := pc.OpenedBlocks()
11,262✔
1064
                        l := len(openedBlocks)
11,262✔
1065
                        if l == 0 {
12,468✔
1066
                                break
1,206✔
1067
                        }
1068
                        lastIndex := l - 1
10,056✔
1069
                        for i := 0; i < l; i++ {
22,938✔
1070
                                be := openedBlocks[i]
12,882✔
1071
                                line, _ := reader.PeekLine()
12,882✔
1072
                                if line == nil {
17,253✔
1073
                                        p.closeBlocks(lastIndex, 0, reader, pc)
4,371✔
1074
                                        reader.AdvanceLine()
4,371✔
1075
                                        return
4,371✔
1076
                                }
4,371✔
1077
                                lineNum, _ := reader.Position()
8,511✔
1078
                                blankLines = append(blankLines, lineStat{lineNum, i, util.IsBlank(line)})
8,511✔
1079
                                // If node is a paragraph, p.openBlocks determines whether it is continuable.
8,511✔
1080
                                // So we do not process paragraphs here.
8,511✔
1081
                                if !ast.IsParagraph(be.Node) {
14,361✔
1082
                                        state := be.Parser.Continue(be.Node, reader, pc)
5,850✔
1083
                                        if state&Continue != 0 {
10,203✔
1084
                                                // When current node is a container block and has no children,
4,353✔
1085
                                                // we try to open new child nodes
4,353✔
1086
                                                if state&HasChildren != 0 && i == lastIndex {
4,665✔
1087
                                                        isBlank := isBlankLine(lineNum-1, i+1, blankLines)
312✔
1088
                                                        p.openBlocks(be.Node, isBlank, reader, pc)
312✔
1089
                                                        break
312✔
1090
                                                }
1091
                                                continue
4,041✔
1092
                                        }
1093
                                }
1094
                                // current node may be closed or lazy continuation
1095
                                isBlank := isBlankLine(lineNum-1, i, blankLines)
4,158✔
1096
                                thisParent := parent
4,158✔
1097
                                if i != 0 {
5,265✔
1098
                                        thisParent = openedBlocks[i-1].Node
1,107✔
1099
                                }
1,107✔
1100
                                lastNode := openedBlocks[lastIndex].Node
4,158✔
1101
                                result := p.openBlocks(thisParent, isBlank, reader, pc)
4,158✔
1102
                                if result != paragraphContinuation {
7,104✔
1103
                                        // lastNode is a paragraph and was transformed by the paragraph
2,946✔
1104
                                        // transformers.
2,946✔
1105
                                        if openedBlocks[lastIndex].Node != lastNode {
3,120✔
1106
                                                lastIndex--
174✔
1107
                                        }
174✔
1108
                                        p.closeBlocks(lastIndex, i, reader, pc)
2,946✔
1109
                                }
1110
                                break
4,158✔
1111
                        }
1112

1113
                        reader.AdvanceLine()
5,685✔
1114
                }
1115
        }
1116
}
1117

1118
func (p *parser) walkBlock(block ast.Node, cb func(node ast.Node)) {
14,127✔
1119
        for c := block.FirstChild(); c != nil; c = c.NextSibling() {
23,724✔
1120
                p.walkBlock(c, cb)
9,597✔
1121
        }
9,597✔
1122
        cb(block)
14,127✔
1123
}
1124

1125
const (
1126
        lineBreakHard uint8 = 1 << iota
1127
        lineBreakSoft
1128
        lineBreakVisible
1129
)
1130

1131
func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context) {
14,127✔
1132
        if parent.IsRaw() {
15,114✔
1133
                return
987✔
1134
        }
987✔
1135
        escaped := false
13,140✔
1136
        source := block.Source()
13,140✔
1137
        block.Reset(parent.Lines())
13,140✔
1138
        for {
30,495✔
1139
        retry:
17,355✔
1140
                line, _ := block.PeekLine()
473,481✔
1141
                if line == nil {
486,621✔
1142
                        break
13,140✔
1143
                }
1144
                lineLength := len(line)
460,341✔
1145
                var lineBreakFlags uint8
460,341✔
1146
                hasNewLine := line[lineLength-1] == '\n'
460,341✔
1147
                if ((lineLength >= 3 && line[lineLength-2] == '\\' &&
460,341✔
1148
                        line[lineLength-3] != '\\') || (lineLength == 2 && line[lineLength-2] == '\\')) && hasNewLine { // ends with \\n
460,413✔
1149
                        lineLength -= 2
72✔
1150
                        lineBreakFlags |= lineBreakHard | lineBreakVisible
72✔
1151
                } else if ((lineLength >= 4 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r' &&
460,341✔
1152
                        line[lineLength-4] != '\\') || (lineLength == 3 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r')) &&
460,269✔
1153
                        hasNewLine { // ends with \\r\n
460,272✔
1154
                        lineLength -= 3
3✔
1155
                        lineBreakFlags |= lineBreakHard | lineBreakVisible
3✔
1156
                } else if lineLength >= 3 && line[lineLength-3] == ' ' && line[lineLength-2] == ' ' &&
460,269✔
1157
                        hasNewLine { // ends with [space][space]\n
460,314✔
1158
                        lineLength -= 3
48✔
1159
                        lineBreakFlags |= lineBreakHard
48✔
1160
                } else if lineLength >= 4 && line[lineLength-4] == ' ' && line[lineLength-3] == ' ' &&
460,266✔
1161
                        line[lineLength-2] == '\r' && hasNewLine { // ends with [space][space]\r\n
460,221✔
1162
                        lineLength -= 4
3✔
1163
                        lineBreakFlags |= lineBreakHard
3✔
1164
                } else if hasNewLine {
461,442✔
1165
                        // If the line ends with a newline character, but it is not a hardlineBreak, then it is a softLinebreak
1,224✔
1166
                        // If the line ends with a hardlineBreak, then it cannot end with a softLinebreak
1,224✔
1167
                        // See https://spec.commonmark.org/0.30/#soft-line-breaks
1,224✔
1168
                        lineBreakFlags |= lineBreakSoft
1,224✔
1169
                }
1,224✔
1170

1171
                l, startPosition := block.Position()
460,341✔
1172
                n := 0
460,341✔
1173
                for i := 0; i < lineLength; i++ {
4,558,950✔
1174
                        c := line[i]
4,098,609✔
1175
                        if c == '\n' {
4,099,335✔
1176
                                break
726✔
1177
                        }
1178
                        isSpace := util.IsSpace(c) && c != '\r' && c != '\n'
4,097,883✔
1179
                        isPunct := util.IsPunct(c)
4,097,883✔
1180
                        if (isPunct && !escaped) || isSpace && !(escaped && p.escapedSpace) || i == 0 {
7,117,569✔
1181
                                parserChar := c
3,019,686✔
1182
                                if isSpace || (i == 0 && !isPunct) {
4,079,313✔
1183
                                        parserChar = ' '
1,059,627✔
1184
                                }
1,059,627✔
1185
                                ips := p.inlineParsers[parserChar]
3,019,686✔
1186
                                if ips != nil {
4,530,588✔
1187
                                        block.Advance(n)
1,510,902✔
1188
                                        n = 0
1,510,902✔
1189
                                        savedLine, savedPosition := block.Position()
1,510,902✔
1190
                                        if i != 0 {
2,865,915✔
1191
                                                _, currentPosition := block.Position()
1,355,013✔
1192
                                                ast.MergeOrAppendTextSegment(parent, startPosition.Between(currentPosition))
1,355,013✔
1193
                                                _, startPosition = block.Position()
1,355,013✔
1194
                                        }
1,355,013✔
1195
                                        var inlineNode ast.Node
1,510,902✔
1196
                                        for _, ip := range ips {
3,627,618✔
1197
                                                inlineNode = ip.Parse(parent, block, pc)
2,116,716✔
1198
                                                if inlineNode != nil {
2,572,842✔
1199
                                                        break
456,126✔
1200
                                                }
1201
                                                block.SetPosition(savedLine, savedPosition)
1,660,590✔
1202
                                        }
1203
                                        if inlineNode != nil {
1,967,028✔
1204
                                                parent.AppendChild(parent, inlineNode)
456,126✔
1205
                                                goto retry
456,126✔
1206
                                        }
1207
                                }
1208
                        }
1209
                        if escaped {
3,642,279✔
1210
                                escaped = false
522✔
1211
                                n++
522✔
1212
                                continue
522✔
1213
                        }
1214

1215
                        if c == '\\' {
3,641,775✔
1216
                                escaped = true
540✔
1217
                                n++
540✔
1218
                                continue
540✔
1219
                        }
1220

1221
                        escaped = false
3,640,695✔
1222
                        n++
3,640,695✔
1223
                }
1224
                if n != 0 {
8,313✔
1225
                        block.Advance(n)
4,098✔
1226
                }
4,098✔
1227
                currentL, currentPosition := block.Position()
4,215✔
1228
                if l != currentL {
4,215✔
1229
                        continue
×
1230
                }
1231
                diff := startPosition.Between(currentPosition)
4,215✔
1232
                var text *ast.Text
4,215✔
1233
                if lineBreakFlags&(lineBreakHard|lineBreakVisible) == lineBreakHard|lineBreakVisible {
4,254✔
1234
                        text = ast.NewTextSegment(diff)
39✔
1235
                } else {
4,215✔
1236
                        text = ast.NewTextSegment(diff.TrimRightSpace(source))
4,176✔
1237
                }
4,176✔
1238
                text.SetSoftLineBreak(lineBreakFlags&lineBreakSoft != 0)
4,215✔
1239
                text.SetHardLineBreak(lineBreakFlags&lineBreakHard != 0)
4,215✔
1240
                parent.AppendChild(parent, text)
4,215✔
1241
                block.AdvanceLine()
4,215✔
1242
        }
1243

1244
        ProcessDelimiters(nil, pc)
13,140✔
1245
        for _, ip := range p.closeBlockers {
26,280✔
1246
                ip.CloseBlock(parent, block, pc)
13,140✔
1247
        }
13,140✔
1248

1249
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc