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

yuin / goldmark / 11306130116

12 Oct 2024 01:42PM UTC coverage: 86.735% (+3.8%) from 82.901%
11306130116

push

github

yuin
Fix lint errors

8278 of 9544 relevant lines covered (86.74%)

249421.64 hits per line

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

93.62
/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 {
350✔
37
        return &reference{label, destination, title}
350✔
38
}
350✔
39

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

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

48
func (r *reference) Title() []byte {
292✔
49
        return r.title
292✔
50
}
292✔
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 {
2,974✔
70
        return &ids{
2,974✔
71
                values: map[string]bool{},
2,974✔
72
        }
2,974✔
73
}
2,974✔
74

75
func (s *ids) Generate(value []byte, kind ast.NodeKind) []byte {
140✔
76
        value = util.TrimLeftSpace(value)
140✔
77
        value = util.TrimRightSpace(value)
140✔
78
        result := []byte{}
140✔
79
        for i := 0; i < len(value); {
1,006✔
80
                v := value[i]
866✔
81
                l := util.UTF8Len(v)
866✔
82
                i += int(l)
866✔
83
                if l != 1 {
866✔
84
                        continue
×
85
                }
86
                if util.IsAlphaNumeric(v) {
1,536✔
87
                        if 'A' <= v && v <= 'Z' {
740✔
88
                                v += 'a' - 'A'
70✔
89
                        }
70✔
90
                        result = append(result, v)
670✔
91
                } else if util.IsSpace(v) || v == '-' || v == '_' {
248✔
92
                        result = append(result, '-')
52✔
93
                }
52✔
94
        }
95
        if len(result) == 0 {
148✔
96
                if kind == ast.KindHeading {
16✔
97
                        result = []byte("heading")
8✔
98
                } else {
8✔
99
                        result = []byte("id")
×
100
                }
×
101
        }
102
        if _, ok := s.values[util.BytesToReadOnlyString(result)]; !ok {
246✔
103
                s.values[util.BytesToReadOnlyString(result)] = true
106✔
104
                return result
106✔
105
        }
106✔
106
        for i := 1; ; i++ {
96✔
107
                newResult := fmt.Sprintf("%s-%d", result, i)
62✔
108
                if _, ok := s.values[newResult]; !ok {
96✔
109
                        s.values[newResult] = true
34✔
110
                        return []byte(newResult)
34✔
111
                }
34✔
112

113
        }
114
}
115

116
func (s *ids) Put(value []byte) {
16✔
117
        s.values[util.BytesToReadOnlyString(value)] = true
16✔
118
}
16✔
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 {
64✔
128
        ContextKeyMax++
64✔
129
        return ContextKeyMax
64✔
130
}
64✔
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 {
2✔
217
        return func(c *ContextConfig) {
4✔
218
                c.IDs = ids
2✔
219
        }
2✔
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 {
2,974✔
235
        cfg := &ContextConfig{
2,974✔
236
                IDs: newIDs(),
2,974✔
237
        }
2,974✔
238
        for _, option := range options {
2,976✔
239
                option(cfg)
2✔
240
        }
2✔
241

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

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

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

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

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

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

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

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

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

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

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

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

311
func (p *parseContext) RemoveDelimiter(d *Delimiter) {
1,838✔
312
        if d.PreviousDelimiter == nil {
3,178✔
313
                p.delimiters = d.NextDelimiter
1,340✔
314
        } else {
1,838✔
315
                d.PreviousDelimiter.NextDelimiter = d.NextDelimiter
498✔
316
                if d.NextDelimiter != nil {
798✔
317
                        d.NextDelimiter.PreviousDelimiter = d.PreviousDelimiter
300✔
318
                }
300✔
319
        }
320
        if d.NextDelimiter == nil {
2,830✔
321
                p.lastDelimiter = d.PreviousDelimiter
992✔
322
        }
992✔
323
        if p.delimiters != nil {
2,882✔
324
                p.delimiters.PreviousDelimiter = nil
1,044✔
325
        }
1,044✔
326
        if p.lastDelimiter != nil {
2,882✔
327
                p.lastDelimiter.NextDelimiter = nil
1,044✔
328
        }
1,044✔
329
        d.NextDelimiter = nil
1,838✔
330
        d.PreviousDelimiter = nil
1,838✔
331
        if d.Length != 0 {
2,400✔
332
                ast.MergeOrReplaceTextSegment(d.Parent(), d, d.Segment)
562✔
333
        } else {
1,838✔
334
                d.Parent().RemoveChild(d.Parent(), d)
1,276✔
335
        }
1,276✔
336
}
337

338
func (p *parseContext) ClearDelimiters(bottom ast.Node) {
846✔
339
        if p.lastDelimiter == nil {
1,362✔
340
                return
516✔
341
        }
516✔
342
        var c ast.Node
330✔
343
        for c = p.lastDelimiter; c != nil && c != bottom; {
986✔
344
                prev := c.PreviousSibling()
656✔
345
                if d, ok := c.(*Delimiter); ok {
1,048✔
346
                        p.RemoveDelimiter(d)
392✔
347
                }
392✔
348
                c = prev
656✔
349
        }
350
}
351

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

359
func (p *parseContext) Reference(label string) (Reference, bool) {
538✔
360
        v, ok := p.refs[label]
538✔
361
        return v, ok
538✔
362
}
538✔
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 {
18,320✔
382
        return p.openedBlocks
18,320✔
383
}
18,320✔
384

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

389
func (p *parseContext) LastOpenedBlock() Block {
23,790✔
390
        if l := len(p.openedBlocks); l != 0 {
34,072✔
391
                return p.openedBlocks[l-1]
10,282✔
392
        }
10,282✔
393
        return Block{}
13,508✔
394
}
395

396
func (p *parseContext) IsInLinkLabel() bool {
5,020✔
397
        tlist := p.Get(linkLabelStateKey)
5,020✔
398
        return tlist != nil
5,020✔
399
}
5,020✔
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 {
1,392✔
439
        return &Config{
1,392✔
440
                Options:               map[OptionName]interface{}{},
1,392✔
441
                BlockParsers:          util.PrioritizedSlice{},
1,392✔
442
                InlineParsers:         util.PrioritizedSlice{},
1,392✔
443
                ParagraphTransformers: util.PrioritizedSlice{},
1,392✔
444
                ASTTransformers:       util.PrioritizedSlice{},
1,392✔
445
        }
1,392✔
446
}
1,392✔
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,306✔
463
        c.Options[optAttribute] = true
1,306✔
464
}
1,306✔
465

466
// WithAttribute is a functional option that enables custom attributes.
467
func WithAttribute() Option {
1,306✔
468
        return &withAttribute{}
1,306✔
469
}
1,306✔
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 {
1,392✔
582
        return []util.PrioritizedValue{
1,392✔
583
                util.Prioritized(NewSetextHeadingParser(), 100),
1,392✔
584
                util.Prioritized(NewThematicBreakParser(), 200),
1,392✔
585
                util.Prioritized(NewListParser(), 300),
1,392✔
586
                util.Prioritized(NewListItemParser(), 400),
1,392✔
587
                util.Prioritized(NewCodeBlockParser(), 500),
1,392✔
588
                util.Prioritized(NewATXHeadingParser(), 600),
1,392✔
589
                util.Prioritized(NewFencedCodeBlockParser(), 700),
1,392✔
590
                util.Prioritized(NewBlockquoteParser(), 800),
1,392✔
591
                util.Prioritized(NewHTMLBlockParser(), 900),
1,392✔
592
                util.Prioritized(NewParagraphParser(), 1000),
1,392✔
593
        }
1,392✔
594
}
1,392✔
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 {
1,392✔
605
        return []util.PrioritizedValue{
1,392✔
606
                util.Prioritized(NewCodeSpanParser(), 100),
1,392✔
607
                util.Prioritized(NewLinkParser(), 200),
1,392✔
608
                util.Prioritized(NewAutoLinkParser(), 300),
1,392✔
609
                util.Prioritized(NewRawHTMLParser(), 400),
1,392✔
610
                util.Prioritized(NewEmphasisParser(), 500),
1,392✔
611
        }
1,392✔
612
}
1,392✔
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 {
1,392✔
619
        return []util.PrioritizedValue{
1,392✔
620
                util.Prioritized(LinkReferenceParagraphTransformer, 100),
1,392✔
621
        }
1,392✔
622
}
1,392✔
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) {
4,008✔
650
        c.BlockParsers = append(c.BlockParsers, o.value...)
4,008✔
651
}
4,008✔
652

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

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

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

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

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

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

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

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

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

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

701
type withEscapedSpace struct {
702
}
703

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

708
// WithEscapedSpace is a functional option indicates that a '\' escaped half-space(0x20) should not trigger parsers.
709
func WithEscapedSpace() Option {
4✔
710
        return &withEscapedSpace{}
4✔
711
}
4✔
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 {
1,392✔
730
        config := NewConfig()
1,392✔
731
        for _, opt := range options {
5,568✔
732
                opt.SetParserOption(config)
4,176✔
733
        }
4,176✔
734

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

1,392✔
740
        return p
1,392✔
741
}
742

743
func (p *parser) AddOptions(opts ...Option) {
14,400✔
744
        for _, opt := range opts {
35,352✔
745
                opt.SetParserOption(p.config)
20,952✔
746
        }
20,952✔
747
}
748

749
func (p *parser) addBlockParser(v util.PrioritizedValue, options map[OptionName]interface{}) {
17,762✔
750
        bp, ok := v.Value.(BlockParser)
17,762✔
751
        if !ok {
17,762✔
752
                panic(fmt.Sprintf("%v is not a BlockParser", v.Value))
×
753
        }
754
        tcs := bp.Trigger()
17,762✔
755
        so, ok := v.Value.(SetOptioner)
17,762✔
756
        if ok {
20,530✔
757
                for oname, ovalue := range options {
7,996✔
758
                        so.SetOption(oname, ovalue)
5,228✔
759
                }
5,228✔
760
        }
761
        if tcs == nil {
20,530✔
762
                p.freeBlockParsers = append(p.freeBlockParsers, bp)
2,768✔
763
        } else {
17,762✔
764
                for _, tc := range tcs {
68,740✔
765
                        if p.blockParsers[tc] == nil {
84,042✔
766
                                p.blockParsers[tc] = []BlockParser{}
30,296✔
767
                        }
30,296✔
768
                        p.blockParsers[tc] = append(p.blockParsers[tc], bp)
53,746✔
769
                }
770
        }
771
}
772

773
func (p *parser) addInlineParser(v util.PrioritizedValue, options map[OptionName]interface{}) {
16,072✔
774
        ip, ok := v.Value.(InlineParser)
16,072✔
775
        if !ok {
16,072✔
776
                panic(fmt.Sprintf("%v is not a InlineParser", v.Value))
×
777
        }
778
        tcs := ip.Trigger()
16,072✔
779
        so, ok := v.Value.(SetOptioner)
16,072✔
780
        if ok {
19,998✔
781
                for oname, ovalue := range options {
11,750✔
782
                        so.SetOption(oname, ovalue)
7,824✔
783
                }
7,824✔
784
        }
785
        if cb, ok := ip.(CloseBlocker); ok {
17,456✔
786
                p.closeBlockers = append(p.closeBlockers, cb)
1,384✔
787
        }
1,384✔
788
        for _, tc := range tcs {
58,534✔
789
                if p.inlineParsers[tc] == nil {
63,936✔
790
                        p.inlineParsers[tc] = []InlineParser{}
21,474✔
791
                }
21,474✔
792
                p.inlineParsers[tc] = append(p.inlineParsers[tc], ip)
42,462✔
793
        }
794
}
795

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

810
func (p *parser) addASTTransformer(v util.PrioritizedValue, options map[OptionName]interface{}) {
3,940✔
811
        at, ok := v.Value.(ASTTransformer)
3,940✔
812
        if !ok {
3,940✔
813
                panic(fmt.Sprintf("%v is not a ASTTransformer", v.Value))
×
814
        }
815
        so, ok := v.Value.(SetOptioner)
3,940✔
816
        if ok {
3,940✔
817
                for oname, ovalue := range options {
×
818
                        so.SetOption(oname, ovalue)
×
819
                }
×
820
        }
821
        p.astTransformers = append(p.astTransformers, at)
3,940✔
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 {
2✔
835
        return func(c *ParseConfig) {
4✔
836
                c.Context = context
2✔
837
        }
2✔
838
}
839

840
func (p *parser) Parse(reader text.Reader, opts ...ParseOption) ast.Node {
2,974✔
841
        p.initSync.Do(func() {
4,358✔
842
                p.config.BlockParsers.Sort()
1,384✔
843
                for _, v := range p.config.BlockParsers {
19,146✔
844
                        p.addBlockParser(v, p.config.Options)
17,762✔
845
                }
17,762✔
846
                for i := range p.blockParsers {
355,688✔
847
                        if p.blockParsers[i] != nil {
384,600✔
848
                                p.blockParsers[i] = append(p.blockParsers[i], p.freeBlockParsers...)
30,296✔
849
                        }
30,296✔
850
                }
851

852
                p.config.InlineParsers.Sort()
1,384✔
853
                for _, v := range p.config.InlineParsers {
17,456✔
854
                        p.addInlineParser(v, p.config.Options)
16,072✔
855
                }
16,072✔
856
                p.config.ParagraphTransformers.Sort()
1,384✔
857
                for _, v := range p.config.ParagraphTransformers {
5,394✔
858
                        p.addParagraphTransformer(v, p.config.Options)
4,010✔
859
                }
4,010✔
860
                p.config.ASTTransformers.Sort()
1,384✔
861
                for _, v := range p.config.ASTTransformers {
5,324✔
862
                        p.addASTTransformer(v, p.config.Options)
3,940✔
863
                }
3,940✔
864
                p.escapedSpace = p.config.EscapedSpace
1,384✔
865
                p.config = nil
1,384✔
866
        })
867
        c := &ParseConfig{}
2,974✔
868
        for _, opt := range opts {
2,976✔
869
                opt(c)
2✔
870
        }
2✔
871
        if c.Context == nil {
5,946✔
872
                c.Context = NewContext()
2,972✔
873
        }
2,972✔
874
        pc := c.Context
2,974✔
875
        root := ast.NewDocument()
2,974✔
876
        p.parseBlocks(root, reader, pc)
2,974✔
877

2,974✔
878
        blockReader := text.NewBlockReader(reader.Source(), nil)
2,974✔
879
        p.walkBlock(root, func(node ast.Node) {
12,164✔
880
                p.parseBlock(blockReader, node, pc)
9,190✔
881
                lines := node.Lines()
9,190✔
882
                if lines != nil && lines.Len() != 0 {
13,320✔
883
                        s := lines.At(lines.Len() - 1)
4,130✔
884
                        s.EOB = true
4,130✔
885
                        lines.Set(lines.Len()-1, s)
4,130✔
886
                }
4,130✔
887
        })
888
        for _, at := range p.astTransformers {
6,946✔
889
                at.Transform(root, reader, pc)
3,972✔
890
        }
3,972✔
891

892
        // root.Dump(reader.Source(), 0)
893
        return root
2,974✔
894
}
895

896
func (p *parser) transformParagraph(node *ast.Paragraph, reader text.Reader, pc Context) bool {
3,538✔
897
        for _, pt := range p.paragraphTransformers {
9,884✔
898
                pt.Transform(node, reader, pc)
6,346✔
899
                if node.Parent() == nil {
6,686✔
900
                        return true
340✔
901
                }
340✔
902
        }
903
        return false
3,198✔
904
}
905

906
func (p *parser) closeBlocks(from, to int, reader text.Reader, pc Context) {
4,770✔
907
        blocks := pc.OpenedBlocks()
4,770✔
908
        for i := from; i >= to; i-- {
10,754✔
909
                node := blocks[i].Node
5,984✔
910
                paragraph, ok := node.(*ast.Paragraph)
5,984✔
911
                if ok && node.Parent() != nil {
9,414✔
912
                        p.transformParagraph(paragraph, reader, pc)
3,430✔
913
                }
3,430✔
914
                if node.Parent() != nil { // closes only if node has not been transformed
11,626✔
915
                        blocks[i].Parser.Close(blocks[i].Node, reader, pc)
5,642✔
916
                }
5,642✔
917
        }
918
        if from == len(blocks)-1 {
8,672✔
919
                blocks = blocks[0:to]
3,902✔
920
        } else {
4,770✔
921
                blocks = append(blocks[0:to], blocks[from+1:]...)
868✔
922
        }
868✔
923
        pc.SetOpenedBlocks(blocks)
4,770✔
924
}
925

926
type blockOpenResult int
927

928
const (
929
        paragraphContinuation blockOpenResult = iota + 1
930
        newBlocksOpened
931
        noBlocksOpened
932
)
933

934
func (p *parser) openBlocks(parent ast.Node, blankLine bool, reader text.Reader, pc Context) blockOpenResult {
6,526✔
935
        result := blockOpenResult(noBlocksOpened)
6,526✔
936
        continuable := false
6,526✔
937
        lastBlock := pc.LastOpenedBlock()
6,526✔
938
        if lastBlock.Node != nil {
9,410✔
939
                continuable = ast.IsParagraph(lastBlock.Node)
2,884✔
940
        }
2,884✔
941
retry:
942
        var bps []BlockParser
7,970✔
943
        line, _ := reader.PeekLine()
7,970✔
944
        w, pos := util.IndentWidth(line, reader.LineOffset())
7,970✔
945
        if w >= len(line) {
8,006✔
946
                pc.SetBlockOffset(-1)
36✔
947
                pc.SetBlockIndent(-1)
36✔
948
        } else {
7,970✔
949
                pc.SetBlockOffset(pos)
7,934✔
950
                pc.SetBlockIndent(w)
7,934✔
951
        }
7,934✔
952
        if line == nil || line[0] == '\n' {
9,052✔
953
                goto continuable
1,082✔
954
        }
955
        bps = p.freeBlockParsers
6,888✔
956
        if pos < len(line) {
13,776✔
957
                bps = p.blockParsers[line[pos]]
6,888✔
958
                if bps == nil {
9,980✔
959
                        bps = p.freeBlockParsers
3,092✔
960
                }
3,092✔
961
        }
962
        if bps == nil {
6,888✔
963
                goto continuable
×
964
        }
965

966
        for _, bp := range bps {
22,890✔
967
                if continuable && result == noBlocksOpened && !bp.CanInterruptParagraph() {
17,578✔
968
                        continue
1,576✔
969
                }
970
                if w > 3 && !bp.CanAcceptIndentedLine() {
14,568✔
971
                        continue
142✔
972
                }
973
                lastBlock = pc.LastOpenedBlock()
14,284✔
974
                last := lastBlock.Node
14,284✔
975
                node, state := bp.Open(parent, reader, pc)
14,284✔
976
                if node != nil {
20,380✔
977
                        // Parser requires last node to be a paragraph.
6,096✔
978
                        // With table extension:
6,096✔
979
                        //
6,096✔
980
                        //     0
6,096✔
981
                        //     -:
6,096✔
982
                        //     -
6,096✔
983
                        //
6,096✔
984
                        // '-' on 3rd line seems a Setext heading because 1st and 2nd lines
6,096✔
985
                        // are being paragraph when the Settext heading parser tries to parse the 3rd
6,096✔
986
                        // line.
6,096✔
987
                        // But 1st line and 2nd line are a table. Thus this paragraph will be transformed
6,096✔
988
                        // by a paragraph transformer. So this text should be converted to a table and
6,096✔
989
                        // an empty list.
6,096✔
990
                        if state&RequireParagraph != 0 {
6,208✔
991
                                if last == parent.LastChild() {
220✔
992
                                        // Opened paragraph may be transformed by ParagraphTransformers in
108✔
993
                                        // closeBlocks().
108✔
994
                                        lastBlock.Parser.Close(last, reader, pc)
108✔
995
                                        blocks := pc.OpenedBlocks()
108✔
996
                                        pc.SetOpenedBlocks(blocks[0 : len(blocks)-1])
108✔
997
                                        if p.transformParagraph(last.(*ast.Paragraph), reader, pc) {
112✔
998
                                                // Paragraph has been transformed.
4✔
999
                                                // So this parser is considered as failing.
4✔
1000
                                                continuable = false
4✔
1001
                                                goto retry
4✔
1002
                                        }
1003
                                }
1004
                        }
1005
                        node.SetBlankPreviousLines(blankLine)
6,092✔
1006
                        if last != nil && last.Parent() == nil {
6,092✔
1007
                                lastPos := len(pc.OpenedBlocks()) - 1
×
1008
                                p.closeBlocks(lastPos, lastPos, reader, pc)
×
1009
                        }
×
1010
                        parent.AppendChild(parent, node)
6,092✔
1011
                        result = newBlocksOpened
6,092✔
1012
                        be := Block{node, bp}
6,092✔
1013
                        pc.SetOpenedBlocks(append(pc.OpenedBlocks(), be))
6,092✔
1014
                        if state&HasChildren != 0 {
7,532✔
1015
                                parent = node
1,440✔
1016
                                goto retry // try child block
1,440✔
1017
                        }
1018
                        break // no children, can not open more blocks on this line
4,652✔
1019
                }
1020
        }
1021

1022
continuable:
1023
        if result == noBlocksOpened && continuable {
8,054✔
1024
                state := lastBlock.Parser.Continue(lastBlock.Node, reader, pc)
1,528✔
1025
                if state&Continue != 0 {
2,308✔
1026
                        result = paragraphContinuation
780✔
1027
                }
780✔
1028
        }
1029
        return result
6,526✔
1030
}
1031

1032
type lineStat struct {
1033
        lineNum int
1034
        level   int
1035
        isBlank bool
1036
}
1037

1038
func isBlankLine(lineNum, level int, stats []lineStat) bool {
6,526✔
1039
        ret := true
6,526✔
1040
        for i := len(stats) - 1 - level; i >= 0; i-- {
12,164✔
1041
                ret = false
5,638✔
1042
                s := stats[i]
5,638✔
1043
                if s.lineNum == lineNum {
8,142✔
1044
                        if s.level < level && s.isBlank {
2,504✔
1045
                                return true
×
1046
                        } else if s.level == level {
4,332✔
1047
                                return s.isBlank
1,828✔
1048
                        }
1,828✔
1049
                }
1050
                if s.lineNum < lineNum {
4,060✔
1051
                        return ret
250✔
1052
                }
250✔
1053
        }
1054
        return ret
4,448✔
1055
}
1056

1057
func (p *parser) parseBlocks(parent ast.Node, reader text.Reader, pc Context) {
2,974✔
1058
        pc.SetOpenedBlocks([]Block{})
2,974✔
1059
        blankLines := make([]lineStat, 0, 128)
2,974✔
1060
        var isBlank bool
2,974✔
1061
        for { // process blocks separated by blank lines
6,722✔
1062
                _, lines, ok := reader.SkipBlankLines()
3,748✔
1063
                if !ok {
3,854✔
1064
                        return
106✔
1065
                }
106✔
1066
                lineNum, _ := reader.Position()
3,642✔
1067
                if lines != 0 {
3,682✔
1068
                        blankLines = blankLines[0:0]
40✔
1069
                        l := len(pc.OpenedBlocks())
40✔
1070
                        for i := 0; i < l; i++ {
40✔
1071
                                blankLines = append(blankLines, lineStat{lineNum - 1, i, lines != 0})
×
1072
                        }
×
1073
                }
1074
                isBlank = isBlankLine(lineNum-1, 0, blankLines)
3,642✔
1075
                // first, we try to open blocks
3,642✔
1076
                if p.openBlocks(parent, isBlank, reader, pc) != newBlocksOpened {
3,642✔
1077
                        return
×
1078
                }
×
1079
                reader.AdvanceLine()
3,642✔
1080
                for { // process opened blocks line by line
10,952✔
1081
                        openedBlocks := pc.OpenedBlocks()
7,310✔
1082
                        l := len(openedBlocks)
7,310✔
1083
                        if l == 0 {
8,084✔
1084
                                break
774✔
1085
                        }
1086
                        lastIndex := l - 1
6,536✔
1087
                        for i := 0; i < l; i++ {
14,896✔
1088
                                be := openedBlocks[i]
8,360✔
1089
                                line, _ := reader.PeekLine()
8,360✔
1090
                                if line == nil {
11,228✔
1091
                                        p.closeBlocks(lastIndex, 0, reader, pc)
2,868✔
1092
                                        reader.AdvanceLine()
2,868✔
1093
                                        return
2,868✔
1094
                                }
2,868✔
1095
                                lineNum, _ := reader.Position()
5,492✔
1096
                                blankLines = append(blankLines, lineStat{lineNum, i, util.IsBlank(line)})
5,492✔
1097
                                // If node is a paragraph, p.openBlocks determines whether it is continuable.
5,492✔
1098
                                // So we do not process paragraphs here.
5,492✔
1099
                                if !ast.IsParagraph(be.Node) {
9,274✔
1100
                                        state := be.Parser.Continue(be.Node, reader, pc)
3,782✔
1101
                                        if state&Continue != 0 {
6,592✔
1102
                                                // When current node is a container block and has no children,
2,810✔
1103
                                                // we try to open new child nodes
2,810✔
1104
                                                if state&HasChildren != 0 && i == lastIndex {
3,012✔
1105
                                                        isBlank = isBlankLine(lineNum-1, i, blankLines)
202✔
1106
                                                        p.openBlocks(be.Node, isBlank, reader, pc)
202✔
1107
                                                        break
202✔
1108
                                                }
1109
                                                continue
2,608✔
1110
                                        }
1111
                                }
1112
                                // current node may be closed or lazy continuation
1113
                                isBlank = isBlankLine(lineNum-1, i, blankLines)
2,682✔
1114
                                thisParent := parent
2,682✔
1115
                                if i != 0 {
3,388✔
1116
                                        thisParent = openedBlocks[i-1].Node
706✔
1117
                                }
706✔
1118
                                lastNode := openedBlocks[lastIndex].Node
2,682✔
1119
                                result := p.openBlocks(thisParent, isBlank, reader, pc)
2,682✔
1120
                                if result != paragraphContinuation {
4,584✔
1121
                                        // lastNode is a paragraph and was transformed by the paragraph
1,902✔
1122
                                        // transformers.
1,902✔
1123
                                        if openedBlocks[lastIndex].Node != lastNode {
2,010✔
1124
                                                lastIndex--
108✔
1125
                                        }
108✔
1126
                                        p.closeBlocks(lastIndex, i, reader, pc)
1,902✔
1127
                                }
1128
                                break
2,682✔
1129
                        }
1130

1131
                        reader.AdvanceLine()
3,668✔
1132
                }
1133
        }
1134
}
1135

1136
func (p *parser) walkBlock(block ast.Node, cb func(node ast.Node)) {
9,190✔
1137
        for c := block.FirstChild(); c != nil; c = c.NextSibling() {
15,406✔
1138
                p.walkBlock(c, cb)
6,216✔
1139
        }
6,216✔
1140
        cb(block)
9,190✔
1141
}
1142

1143
const (
1144
        lineBreakHard uint8 = 1 << iota
1145
        lineBreakSoft
1146
        lineBreakVisible
1147
)
1148

1149
func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context) {
9,190✔
1150
        if parent.IsRaw() {
9,826✔
1151
                return
636✔
1152
        }
636✔
1153
        escaped := false
8,554✔
1154
        source := block.Source()
8,554✔
1155
        block.Reset(parent.Lines())
8,554✔
1156
        for {
19,824✔
1157
        retry:
11,270✔
1158
                line, _ := block.PeekLine()
315,328✔
1159
                if line == nil {
323,882✔
1160
                        break
8,554✔
1161
                }
1162
                lineLength := len(line)
306,774✔
1163
                var lineBreakFlags uint8
306,774✔
1164
                hasNewLine := line[lineLength-1] == '\n'
306,774✔
1165
                if ((lineLength >= 3 && line[lineLength-2] == '\\' &&
306,774✔
1166
                        line[lineLength-3] != '\\') || (lineLength == 2 && line[lineLength-2] == '\\')) && hasNewLine { // ends with \\n
306,822✔
1167
                        lineLength -= 2
48✔
1168
                        lineBreakFlags |= lineBreakHard | lineBreakVisible
48✔
1169
                } else if ((lineLength >= 4 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r' &&
306,774✔
1170
                        line[lineLength-4] != '\\') || (lineLength == 3 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r')) &&
306,726✔
1171
                        hasNewLine { // ends with \\r\n
306,728✔
1172
                        lineLength -= 3
2✔
1173
                        lineBreakFlags |= lineBreakHard | lineBreakVisible
2✔
1174
                } else if lineLength >= 3 && line[lineLength-3] == ' ' && line[lineLength-2] == ' ' &&
306,726✔
1175
                        hasNewLine { // ends with [space][space]\n
306,756✔
1176
                        lineLength -= 3
32✔
1177
                        lineBreakFlags |= lineBreakHard
32✔
1178
                } else if lineLength >= 4 && line[lineLength-4] == ' ' && line[lineLength-3] == ' ' &&
306,724✔
1179
                        line[lineLength-2] == '\r' && hasNewLine { // ends with [space][space]\r\n
306,694✔
1180
                        lineLength -= 4
2✔
1181
                        lineBreakFlags |= lineBreakHard
2✔
1182
                } else if hasNewLine {
307,492✔
1183
                        // If the line ends with a newline character, but it is not a hardlineBreak, then it is a softLinebreak
800✔
1184
                        // If the line ends with a hardlineBreak, then it cannot end with a softLinebreak
800✔
1185
                        // See https://spec.commonmark.org/0.30/#soft-line-breaks
800✔
1186
                        lineBreakFlags |= lineBreakSoft
800✔
1187
                }
800✔
1188

1189
                l, startPosition := block.Position()
306,774✔
1190
                n := 0
306,774✔
1191
                for i := 0; i < lineLength; i++ {
3,038,954✔
1192
                        c := line[i]
2,732,180✔
1193
                        if c == '\n' {
2,732,648✔
1194
                                break
468✔
1195
                        }
1196
                        isSpace := util.IsSpace(c) && c != '\r' && c != '\n'
2,731,712✔
1197
                        isPunct := util.IsPunct(c)
2,731,712✔
1198
                        if (isPunct && !escaped) || isSpace && !(escaped && p.escapedSpace) || i == 0 {
4,744,700✔
1199
                                parserChar := c
2,012,988✔
1200
                                if isSpace || (i == 0 && !isPunct) {
2,719,296✔
1201
                                        parserChar = ' '
706,308✔
1202
                                }
706,308✔
1203
                                ips := p.inlineParsers[parserChar]
2,012,988✔
1204
                                if ips != nil {
3,020,230✔
1205
                                        block.Advance(n)
1,007,242✔
1206
                                        n = 0
1,007,242✔
1207
                                        savedLine, savedPosition := block.Position()
1,007,242✔
1208
                                        if i != 0 {
1,910,572✔
1209
                                                _, currentPosition := block.Position()
903,330✔
1210
                                                ast.MergeOrAppendTextSegment(parent, startPosition.Between(currentPosition))
903,330✔
1211
                                                _, startPosition = block.Position()
903,330✔
1212
                                        }
903,330✔
1213
                                        var inlineNode ast.Node
1,007,242✔
1214
                                        for _, ip := range ips {
2,418,356✔
1215
                                                inlineNode = ip.Parse(parent, block, pc)
1,411,114✔
1216
                                                if inlineNode != nil {
1,715,172✔
1217
                                                        break
304,058✔
1218
                                                }
1219
                                                block.SetPosition(savedLine, savedPosition)
1,107,056✔
1220
                                        }
1221
                                        if inlineNode != nil {
1,311,300✔
1222
                                                parent.AppendChild(parent, inlineNode)
304,058✔
1223
                                                goto retry
304,058✔
1224
                                        }
1225
                                }
1226
                        }
1227
                        if escaped {
2,428,002✔
1228
                                escaped = false
348✔
1229
                                n++
348✔
1230
                                continue
348✔
1231
                        }
1232

1233
                        if c == '\\' {
2,427,666✔
1234
                                escaped = true
360✔
1235
                                n++
360✔
1236
                                continue
360✔
1237
                        }
1238

1239
                        escaped = false
2,426,946✔
1240
                        n++
2,426,946✔
1241
                }
1242
                if n != 0 {
5,354✔
1243
                        block.Advance(n)
2,638✔
1244
                }
2,638✔
1245
                currentL, currentPosition := block.Position()
2,716✔
1246
                if l != currentL {
2,716✔
1247
                        continue
×
1248
                }
1249
                diff := startPosition.Between(currentPosition)
2,716✔
1250
                var text *ast.Text
2,716✔
1251
                if lineBreakFlags&(lineBreakHard|lineBreakVisible) == lineBreakHard|lineBreakVisible {
2,742✔
1252
                        text = ast.NewTextSegment(diff)
26✔
1253
                } else {
2,716✔
1254
                        text = ast.NewTextSegment(diff.TrimRightSpace(source))
2,690✔
1255
                }
2,690✔
1256
                text.SetSoftLineBreak(lineBreakFlags&lineBreakSoft != 0)
2,716✔
1257
                text.SetHardLineBreak(lineBreakFlags&lineBreakHard != 0)
2,716✔
1258
                parent.AppendChild(parent, text)
2,716✔
1259
                block.AdvanceLine()
2,716✔
1260
        }
1261

1262
        ProcessDelimiters(nil, pc)
8,554✔
1263
        for _, ip := range p.closeBlockers {
17,108✔
1264
                ip.CloseBlock(parent, block, pc)
8,554✔
1265
        }
8,554✔
1266

1267
}
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