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

yuin / goldmark / 23470262150

24 Mar 2026 02:27AM UTC coverage: 82.558% (-0.7%) from 83.229%
23470262150

push

github

yuin
feat: add position information to all nodes

- add position information to all nodes, including inline nodes and link
  reference definition nodes.
- Now link reference definition nodes are represented as a new node
  type.
- Link and image nodes have a new field Reference which is a pointer to the reference
  link if this link is a reference link. This field is nil for non-reference
  links.

182 of 283 new or added lines in 21 files covered. (64.31%)

12 existing lines in 1 file now uncovered.

6286 of 7614 relevant lines covered (82.56%)

470127.28 hits per line

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

91.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.
UNCOV
36
func NewReference(label, destination, title []byte) Reference {
×
UNCOV
37
        return &reference{label, destination, title}
×
UNCOV
38
}
×
39

40
func newASTReference(v *ast.LinkReferenceDefinition) Reference {
528✔
41
        return &astReference{v}
528✔
42
}
528✔
43

UNCOV
44
func (r *reference) Label() []byte {
×
UNCOV
45
        return r.label
×
UNCOV
46
}
×
47

UNCOV
48
func (r *reference) Destination() []byte {
×
UNCOV
49
        return r.destination
×
UNCOV
50
}
×
51

UNCOV
52
func (r *reference) Title() []byte {
×
UNCOV
53
        return r.title
×
UNCOV
54
}
×
55

56
func (r *reference) String() string {
×
57
        return fmt.Sprintf("Reference{Label:%s, Destination:%s, Title:%s}", r.label, r.destination, r.title)
×
58
}
×
59

60
type astReference struct {
61
        v *ast.LinkReferenceDefinition
62
}
63

64
func (r *astReference) Label() []byte {
528✔
65
        return r.v.Label
528✔
66
}
528✔
67

68
func (r *astReference) Destination() []byte {
447✔
69
        return r.v.Destination
447✔
70
}
447✔
71

72
func (r *astReference) Title() []byte {
447✔
73
        return r.v.Title
447✔
74
}
447✔
75

NEW
76
func (r *astReference) String() string {
×
NEW
77
        return fmt.Sprintf("Reference{Label:%s, Destination:%s, Title:%s}", r.Label(), r.Destination(), r.Title())
×
NEW
78
}
×
79

80
// An IDs interface is a collection of the element ids.
81
type IDs interface {
82
        // Generate generates a new element id.
83
        Generate(value []byte, kind ast.NodeKind) []byte
84

85
        // Put puts a given element id to the used ids table.
86
        Put(value []byte)
87
}
88

89
type ids struct {
90
        values map[string]bool
91
}
92

93
func newIDs() IDs {
4,557✔
94
        return &ids{
4,557✔
95
                values: map[string]bool{},
4,557✔
96
        }
4,557✔
97
}
4,557✔
98

99
func (s *ids) Generate(value []byte, kind ast.NodeKind) []byte {
213✔
100
        value = util.TrimLeftSpace(value)
213✔
101
        value = util.TrimRightSpace(value)
213✔
102
        result := []byte{}
213✔
103
        for i := 0; i < len(value); {
1,533✔
104
                v := value[i]
1,320✔
105
                l := util.UTF8Len(v)
1,320✔
106
                i += int(l)
1,320✔
107
                if l != 1 {
1,320✔
108
                        continue
×
109
                }
110
                if util.IsAlphaNumeric(v) {
2,346✔
111
                        if 'A' <= v && v <= 'Z' {
1,134✔
112
                                v += 'a' - 'A'
108✔
113
                        }
108✔
114
                        result = append(result, v)
1,026✔
115
                } else if util.IsSpace(v) || v == '-' || v == '_' {
372✔
116
                        result = append(result, '-')
78✔
117
                }
78✔
118
        }
119
        if len(result) == 0 {
225✔
120
                if kind == ast.KindHeading {
24✔
121
                        result = []byte("heading")
12✔
122
                } else {
12✔
123
                        result = []byte("id")
×
124
                }
×
125
        }
126
        if _, ok := s.values[util.BytesToReadOnlyString(result)]; !ok {
375✔
127
                s.values[util.BytesToReadOnlyString(result)] = true
162✔
128
                return result
162✔
129
        }
162✔
130
        for i := 1; ; i++ {
144✔
131
                newResult := fmt.Sprintf("%s-%d", result, i)
93✔
132
                if _, ok := s.values[newResult]; !ok {
144✔
133
                        s.values[newResult] = true
51✔
134
                        return []byte(newResult)
51✔
135
                }
51✔
136

137
        }
138
}
139

140
func (s *ids) Put(value []byte) {
24✔
141
        s.values[util.BytesToReadOnlyString(value)] = true
24✔
142
}
24✔
143

144
// ContextKey is a key that is used to set arbitrary values to the context.
145
type ContextKey int
146

147
// ContextKeyMax is a maximum value of the ContextKey.
148
var ContextKeyMax ContextKey
149

150
// NewContextKey return a new ContextKey value.
151
func NewContextKey() ContextKey {
96✔
152
        ContextKeyMax++
96✔
153
        return ContextKeyMax
96✔
154
}
96✔
155

156
// A Context interface holds a information that are necessary to parse
157
// Markdown text.
158
type Context interface {
159
        // String implements Stringer.
160
        String() string
161

162
        // Get returns a value associated with the given key.
163
        Get(ContextKey) any
164

165
        // ComputeIfAbsent computes a value if a value associated with the given key is absent and returns the value.
166
        ComputeIfAbsent(ContextKey, func() any) any
167

168
        // Set sets the given value to the context.
169
        Set(ContextKey, any)
170

171
        // AddReference adds the given reference to this context.
172
        AddReference(Reference)
173

174
        // Reference returns (a reference, true) if a reference associated with
175
        // the given label exists, otherwise (nil, false).
176
        Reference(label string) (Reference, bool)
177

178
        // References returns a list of references.
179
        References() []Reference
180

181
        // IDs returns a collection of the element ids.
182
        IDs() IDs
183

184
        // BlockOffset returns a first non-space character position on current line.
185
        // This value is valid only for BlockParser.Open.
186
        // BlockOffset returns -1 if current line is blank.
187
        BlockOffset() int
188

189
        // BlockOffset sets a first non-space character position on current line.
190
        // This value is valid only for BlockParser.Open.
191
        SetBlockOffset(int)
192

193
        // BlockIndent returns an indent width on current line.
194
        // This value is valid only for BlockParser.Open.
195
        // BlockIndent returns -1 if current line is blank.
196
        BlockIndent() int
197

198
        // BlockIndent sets an indent width on current line.
199
        // This value is valid only for BlockParser.Open.
200
        SetBlockIndent(int)
201

202
        // FirstDelimiter returns a first delimiter of the current delimiter list.
203
        FirstDelimiter() *Delimiter
204

205
        // LastDelimiter returns a last delimiter of the current delimiter list.
206
        LastDelimiter() *Delimiter
207

208
        // PushDelimiter appends the given delimiter to the tail of the current
209
        // delimiter list.
210
        PushDelimiter(delimiter *Delimiter)
211

212
        // RemoveDelimiter removes the given delimiter from the current delimiter list.
213
        RemoveDelimiter(d *Delimiter)
214

215
        // ClearDelimiters clears the current delimiter list.
216
        ClearDelimiters(bottom ast.Node)
217

218
        // OpenedBlocks returns a list of nodes that are currently in parsing.
219
        OpenedBlocks() []Block
220

221
        // SetOpenedBlocks sets a list of nodes that are currently in parsing.
222
        SetOpenedBlocks([]Block)
223

224
        // LastOpenedBlock returns a last node that is currently in parsing.
225
        LastOpenedBlock() Block
226

227
        // IsInLinkLabel returns true if current position seems to be in link label.
228
        IsInLinkLabel() bool
229
}
230

231
// A ContextConfig struct is a data structure that holds configuration of the Context.
232
type ContextConfig struct {
233
        IDs IDs
234
}
235

236
// An ContextOption is a functional option type for the Context.
237
type ContextOption func(*ContextConfig)
238

239
// WithIDs is a functional option for the Context.
240
func WithIDs(ids IDs) ContextOption {
3✔
241
        return func(c *ContextConfig) {
6✔
242
                c.IDs = ids
3✔
243
        }
3✔
244
}
245

246
type parseContext struct {
247
        store         []any
248
        ids           IDs
249
        refs          map[string]Reference
250
        blockOffset   int
251
        blockIndent   int
252
        delimiters    *Delimiter
253
        lastDelimiter *Delimiter
254
        openedBlocks  []Block
255
}
256

257
// NewContext returns a new Context.
258
func NewContext(options ...ContextOption) Context {
4,557✔
259
        cfg := &ContextConfig{
4,557✔
260
                IDs: newIDs(),
4,557✔
261
        }
4,557✔
262
        for _, option := range options {
4,560✔
263
                option(cfg)
3✔
264
        }
3✔
265

266
        return &parseContext{
4,557✔
267
                store:         make([]any, ContextKeyMax+1),
4,557✔
268
                refs:          map[string]Reference{},
4,557✔
269
                ids:           cfg.IDs,
4,557✔
270
                blockOffset:   -1,
4,557✔
271
                blockIndent:   -1,
4,557✔
272
                delimiters:    nil,
4,557✔
273
                lastDelimiter: nil,
4,557✔
274
                openedBlocks:  []Block{},
4,557✔
275
        }
4,557✔
276
}
277

278
func (p *parseContext) Get(key ContextKey) any {
1,688,937✔
279
        return p.store[key]
1,688,937✔
280
}
1,688,937✔
281

282
func (p *parseContext) ComputeIfAbsent(key ContextKey, f func() any) any {
3✔
283
        v := p.store[key]
3✔
284
        if v == nil {
6✔
285
                v = f()
3✔
286
                p.store[key] = v
3✔
287
        }
3✔
288
        return v
3✔
289
}
290

291
func (p *parseContext) Set(key ContextKey, value any) {
926,100✔
292
        p.store[key] = value
926,100✔
293
}
926,100✔
294

295
func (p *parseContext) IDs() IDs {
243✔
296
        return p.ids
243✔
297
}
243✔
298

299
func (p *parseContext) BlockOffset() int {
1,092✔
300
        return p.blockOffset
1,092✔
301
}
1,092✔
302

303
func (p *parseContext) SetBlockOffset(v int) {
12,411✔
304
        p.blockOffset = v
12,411✔
305
}
12,411✔
306

307
func (p *parseContext) BlockIndent() int {
603✔
308
        return p.blockIndent
603✔
309
}
603✔
310

311
func (p *parseContext) SetBlockIndent(v int) {
12,411✔
312
        p.blockIndent = v
12,411✔
313
}
12,411✔
314

315
func (p *parseContext) LastDelimiter() *Delimiter {
465,015✔
316
        return p.lastDelimiter
465,015✔
317
}
465,015✔
318

319
func (p *parseContext) FirstDelimiter() *Delimiter {
1,116✔
320
        return p.delimiters
1,116✔
321
}
1,116✔
322

323
func (p *parseContext) PushDelimiter(d *Delimiter) {
2,784✔
324
        if p.delimiters == nil {
3,984✔
325
                p.delimiters = d
1,200✔
326
                p.lastDelimiter = d
1,200✔
327
        } else {
2,784✔
328
                l := p.lastDelimiter
1,584✔
329
                p.lastDelimiter = d
1,584✔
330
                l.NextDelimiter = d
1,584✔
331
                d.PreviousDelimiter = l
1,584✔
332
        }
1,584✔
333
}
334

335
func (p *parseContext) RemoveDelimiter(d *Delimiter) {
2,784✔
336
        if d.PreviousDelimiter == nil {
4,812✔
337
                p.delimiters = d.NextDelimiter
2,028✔
338
        } else {
2,784✔
339
                d.PreviousDelimiter.NextDelimiter = d.NextDelimiter
756✔
340
                if d.NextDelimiter != nil {
1,215✔
341
                        d.NextDelimiter.PreviousDelimiter = d.PreviousDelimiter
459✔
342
                }
459✔
343
        }
344
        if d.NextDelimiter == nil {
4,281✔
345
                p.lastDelimiter = d.PreviousDelimiter
1,497✔
346
        }
1,497✔
347
        if p.delimiters != nil {
4,368✔
348
                p.delimiters.PreviousDelimiter = nil
1,584✔
349
        }
1,584✔
350
        if p.lastDelimiter != nil {
4,368✔
351
                p.lastDelimiter.NextDelimiter = nil
1,584✔
352
        }
1,584✔
353
        d.NextDelimiter = nil
2,784✔
354
        d.PreviousDelimiter = nil
2,784✔
355
        if d.Length != 0 {
3,627✔
356
                ast.MergeOrReplaceTextSegment(d.Parent(), d, d.Segment)
843✔
357
        } else {
2,784✔
358
                d.Parent().RemoveChild(d.Parent(), d)
1,941✔
359
        }
1,941✔
360
}
361

362
func (p *parseContext) ClearDelimiters(bottom ast.Node) {
1,278✔
363
        if p.lastDelimiter == nil {
2,061✔
364
                return
783✔
365
        }
783✔
366
        var c ast.Node
495✔
367
        for c = p.lastDelimiter; c != nil && c != bottom; {
1,479✔
368
                prev := c.PreviousSibling()
984✔
369
                if d, ok := c.(*Delimiter); ok {
1,572✔
370
                        p.RemoveDelimiter(d)
588✔
371
                }
588✔
372
                c = prev
984✔
373
        }
374
}
375

376
func (p *parseContext) AddReference(ref Reference) {
528✔
377
        key := util.ToLinkReference(ref.Label())
528✔
378
        if _, ok := p.refs[key]; !ok {
1,044✔
379
                p.refs[key] = ref
516✔
380
        }
516✔
381
}
382

383
func (p *parseContext) Reference(label string) (Reference, bool) {
816✔
384
        v, ok := p.refs[label]
816✔
385
        return v, ok
816✔
386
}
816✔
387

388
func (p *parseContext) References() []Reference {
×
389
        ret := make([]Reference, 0, len(p.refs))
×
390
        for _, v := range p.refs {
×
391
                ret = append(ret, v)
×
392
        }
×
393
        return ret
×
394
}
395

396
func (p *parseContext) String() string {
×
397
        refs := []string{}
×
398
        for _, r := range p.refs {
×
399
                refs = append(refs, r.String())
×
400
        }
×
401

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

405
func (p *parseContext) OpenedBlocks() []Block {
28,356✔
406
        return p.openedBlocks
28,356✔
407
}
28,356✔
408

409
func (p *parseContext) SetOpenedBlocks(v []Block) {
21,543✔
410
        p.openedBlocks = v
21,543✔
411
}
21,543✔
412

413
func (p *parseContext) LastOpenedBlock() Block {
36,882✔
414
        if l := len(p.openedBlocks); l != 0 {
52,932✔
415
                return p.openedBlocks[l-1]
16,050✔
416
        }
16,050✔
417
        return Block{}
20,832✔
418
}
419

420
func (p *parseContext) IsInLinkLabel() bool {
7,482✔
421
        tlist := p.Get(linkLabelStateKey)
7,482✔
422
        return tlist != nil
7,482✔
423
}
7,482✔
424

425
// State represents parser's state.
426
// State is designed to use as a bit flag.
427
type State int
428

429
const (
430
        // None is a default value of the [State].
431
        None State = 1 << iota
432

433
        // Continue indicates parser can continue parsing.
434
        Continue
435

436
        // Close indicates parser cannot parse anymore.
437
        Close
438

439
        // HasChildren indicates parser may have child blocks.
440
        HasChildren
441

442
        // NoChildren indicates parser does not have child blocks.
443
        NoChildren
444

445
        // RequireParagraph indicates parser requires that the last node
446
        // must be a paragraph and is not converted to other nodes by
447
        // ParagraphTransformers.
448
        RequireParagraph
449
)
450

451
// A Config struct is a data structure that holds configuration of the Parser.
452
type Config struct {
453
        Options               map[OptionName]any
454
        BlockParsers          util.PrioritizedSlice /*<BlockParser>*/
455
        InlineParsers         util.PrioritizedSlice /*<InlineParser>*/
456
        ParagraphTransformers util.PrioritizedSlice /*<ParagraphTransformer>*/
457
        ASTTransformers       util.PrioritizedSlice /*<ASTTransformer>*/
458
        EscapedSpace          bool
459
}
460

461
// NewConfig returns a new Config.
462
func NewConfig() *Config {
2,148✔
463
        return &Config{
2,148✔
464
                Options:               map[OptionName]any{},
2,148✔
465
                BlockParsers:          util.PrioritizedSlice{},
2,148✔
466
                InlineParsers:         util.PrioritizedSlice{},
2,148✔
467
                ParagraphTransformers: util.PrioritizedSlice{},
2,148✔
468
                ASTTransformers:       util.PrioritizedSlice{},
2,148✔
469
        }
2,148✔
470
}
2,148✔
471

472
// An Option interface is a functional option type for the Parser.
473
type Option interface {
474
        SetParserOption(*Config)
475
}
476

477
// OptionName is a name of parser options.
478
type OptionName string
479

480
// Attribute is an option name that spacify attributes of elements.
481
const optAttribute OptionName = "Attribute"
482

483
type withAttribute struct {
484
}
485

486
func (o *withAttribute) SetParserOption(c *Config) {
1,965✔
487
        c.Options[optAttribute] = true
1,965✔
488
}
1,965✔
489

490
// WithAttribute is a functional option that enables custom attributes.
491
func WithAttribute() Option {
1,965✔
492
        return &withAttribute{}
1,965✔
493
}
1,965✔
494

495
// A Parser interface parses Markdown text into AST nodes.
496
type Parser interface {
497
        // Parse parses the given Markdown text into AST nodes.
498
        Parse(reader text.Reader, opts ...ParseOption) ast.Node
499

500
        // AddOption adds the given option to this parser.
501
        AddOptions(...Option)
502
}
503

504
// A SetOptioner interface sets the given option to the object.
505
type SetOptioner interface {
506
        // SetOption sets the given option to the object.
507
        // Unacceptable options may be passed.
508
        // Thus implementations must ignore unacceptable options.
509
        SetOption(name OptionName, value any)
510
}
511

512
// A BlockParser interface parses a block level element like Paragraph, List,
513
// Blockquote etc.
514
type BlockParser interface {
515
        // Trigger returns a list of characters that triggers Parse method of
516
        // this parser.
517
        // If Trigger returns a nil, Open will be called with any lines.
518
        Trigger() []byte
519

520
        // Open parses the current line and returns a result of parsing.
521
        //
522
        // Open must not parse beyond the current line.
523
        // If Open has been able to parse the current line, Open must advance a reader
524
        // position by consumed byte length.
525
        //
526
        // If Open has not been able to parse the current line, Open should returns
527
        // (nil, NoChildren). If Open has been able to parse the current line, Open
528
        // should returns a new Block node and returns HasChildren or NoChildren.
529
        Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State)
530

531
        // Continue parses the current line and returns a result of parsing.
532
        //
533
        // Continue must not parse beyond the current line.
534
        // If Continue has been able to parse the current line, Continue must advance
535
        // a reader position by consumed byte length.
536
        //
537
        // If Continue has not been able to parse the current line, Continue should
538
        // returns Close. If Continue has been able to parse the current line,
539
        // Continue should returns (Continue | NoChildren) or
540
        // (Continue | HasChildren)
541
        Continue(node ast.Node, reader text.Reader, pc Context) State
542

543
        // Close will be called when the parser returns Close.
544
        Close(node ast.Node, reader text.Reader, pc Context)
545

546
        // CanInterruptParagraph returns true if the parser can interrupt paragraphs,
547
        // otherwise false.
548
        CanInterruptParagraph() bool
549

550
        // CanAcceptIndentedLine returns true if the parser can open new node when
551
        // the given line is being indented more than 3 spaces.
552
        CanAcceptIndentedLine() bool
553
}
554

555
// An InlineParser interface parses an inline level element like CodeSpan, Link etc.
556
type InlineParser interface {
557
        // Trigger returns a list of characters that triggers Parse method of
558
        // this parser.
559
        // Trigger characters must be a punctuation or a halfspace.
560
        // Halfspaces triggers this parser when character is any spaces characters or
561
        // a head of line
562
        Trigger() []byte
563

564
        // Parse parse the given block into an inline node.
565
        //
566
        // Parse can parse beyond the current line.
567
        // If Parse has been able to parse the current line, it must advance a reader
568
        // position by consumed byte length.
569
        Parse(parent ast.Node, block text.Reader, pc Context) ast.Node
570
}
571

572
// A CloseBlocker interface is a callback function that will be
573
// called when block is closed in the inline parsing.
574
type CloseBlocker interface {
575
        // CloseBlock will be called when a block is closed.
576
        CloseBlock(parent ast.Node, block text.Reader, pc Context)
577
}
578

579
// A ParagraphTransformer transforms parsed Paragraph nodes.
580
// For example, link references are searched in parsed Paragraphs.
581
type ParagraphTransformer interface {
582
        // Transform transforms the given paragraph.
583
        Transform(node *ast.Paragraph, reader text.Reader, pc Context)
584
}
585

586
// ASTTransformer transforms entire Markdown document AST tree.
587
type ASTTransformer interface {
588
        // Transform transforms the given AST tree.
589
        Transform(node *ast.Document, reader text.Reader, pc Context)
590
}
591

592
// DefaultBlockParsers returns a new list of default BlockParsers.
593
// Priorities of default BlockParsers are:
594
//
595
//        SetextHeadingParser, 100
596
//        ThematicBreakParser, 200
597
//        ListParser, 300
598
//        ListItemParser, 400
599
//        CodeBlockParser, 500
600
//        ATXHeadingParser, 600
601
//        FencedCodeBlockParser, 700
602
//        BlockquoteParser, 800
603
//        HTMLBlockParser, 900
604
//        ParagraphParser, 1000
605
func DefaultBlockParsers() []util.PrioritizedValue {
2,148✔
606
        return []util.PrioritizedValue{
2,148✔
607
                util.Prioritized(NewSetextHeadingParser(), 100),
2,148✔
608
                util.Prioritized(NewThematicBreakParser(), 200),
2,148✔
609
                util.Prioritized(NewListParser(), 300),
2,148✔
610
                util.Prioritized(NewListItemParser(), 400),
2,148✔
611
                util.Prioritized(NewCodeBlockParser(), 500),
2,148✔
612
                util.Prioritized(NewATXHeadingParser(), 600),
2,148✔
613
                util.Prioritized(NewFencedCodeBlockParser(), 700),
2,148✔
614
                util.Prioritized(NewBlockquoteParser(), 800),
2,148✔
615
                util.Prioritized(NewHTMLBlockParser(), 900),
2,148✔
616
                util.Prioritized(NewParagraphParser(), 1000),
2,148✔
617
        }
2,148✔
618
}
2,148✔
619

620
// DefaultInlineParsers returns a new list of default InlineParsers.
621
// Priorities of default InlineParsers are:
622
//
623
//        CodeSpanParser, 100
624
//        LinkParser, 200
625
//        AutoLinkParser, 300
626
//        RawHTMLParser, 400
627
//        EmphasisParser, 500
628
func DefaultInlineParsers() []util.PrioritizedValue {
2,148✔
629
        return []util.PrioritizedValue{
2,148✔
630
                util.Prioritized(NewCodeSpanParser(), 100),
2,148✔
631
                util.Prioritized(NewLinkParser(), 200),
2,148✔
632
                util.Prioritized(NewAutoLinkParser(), 300),
2,148✔
633
                util.Prioritized(NewRawHTMLParser(), 400),
2,148✔
634
                util.Prioritized(NewEmphasisParser(), 500),
2,148✔
635
        }
2,148✔
636
}
2,148✔
637

638
// DefaultParagraphTransformers returns a new list of default ParagraphTransformers.
639
// Priorities of default ParagraphTransformers are:
640
//
641
//        LinkReferenceParagraphTransformer, 100
642
func DefaultParagraphTransformers() []util.PrioritizedValue {
2,148✔
643
        return []util.PrioritizedValue{
2,148✔
644
                util.Prioritized(LinkReferenceParagraphTransformer, 100),
2,148✔
645
        }
2,148✔
646
}
2,148✔
647

648
// A Block struct holds a node and correspond parser pair.
649
type Block struct {
650
        // Node is a BlockNode.
651
        Node ast.Node
652
        // Parser is a BlockParser.
653
        Parser BlockParser
654
}
655

656
type parser struct {
657
        options               map[OptionName]any
658
        blockParsers          [256][]BlockParser
659
        freeBlockParsers      []BlockParser
660
        inlineParsers         [256][]InlineParser
661
        closeBlockers         []CloseBlocker
662
        paragraphTransformers []ParagraphTransformer
663
        astTransformers       []ASTTransformer
664
        escapedSpace          bool
665
        config                *Config
666
        initSync              sync.Once
667
}
668

669
type withBlockParsers struct {
670
        value []util.PrioritizedValue
671
}
672

673
func (o *withBlockParsers) SetParserOption(c *Config) {
6,078✔
674
        c.BlockParsers = append(c.BlockParsers, o.value...)
6,078✔
675
}
6,078✔
676

677
// WithBlockParsers is a functional option that allow you to add
678
// BlockParsers to the parser.
679
func WithBlockParsers(bs ...util.PrioritizedValue) Option {
6,078✔
680
        return &withBlockParsers{bs}
6,078✔
681
}
6,078✔
682

683
type withInlineParsers struct {
684
        value []util.PrioritizedValue
685
}
686

687
func (o *withInlineParsers) SetParserOption(c *Config) {
15,879✔
688
        c.InlineParsers = append(c.InlineParsers, o.value...)
15,879✔
689
}
15,879✔
690

691
// WithInlineParsers is a functional option that allow you to add
692
// InlineParsers to the parser.
693
func WithInlineParsers(bs ...util.PrioritizedValue) Option {
15,879✔
694
        return &withInlineParsers{bs}
15,879✔
695
}
15,879✔
696

697
type withParagraphTransformers struct {
698
        value []util.PrioritizedValue
699
}
700

701
func (o *withParagraphTransformers) SetParserOption(c *Config) {
6,096✔
702
        c.ParagraphTransformers = append(c.ParagraphTransformers, o.value...)
6,096✔
703
}
6,096✔
704

705
// WithParagraphTransformers is a functional option that allow you to add
706
// ParagraphTransformers to the parser.
707
func WithParagraphTransformers(ps ...util.PrioritizedValue) Option {
6,096✔
708
        return &withParagraphTransformers{ps}
6,096✔
709
}
6,096✔
710

711
type withASTTransformers struct {
712
        value []util.PrioritizedValue
713
}
714

715
func (o *withASTTransformers) SetParserOption(c *Config) {
5,919✔
716
        c.ASTTransformers = append(c.ASTTransformers, o.value...)
5,919✔
717
}
5,919✔
718

719
// WithASTTransformers is a functional option that allow you to add
720
// ASTTransformers to the parser.
721
func WithASTTransformers(ps ...util.PrioritizedValue) Option {
5,919✔
722
        return &withASTTransformers{ps}
5,919✔
723
}
5,919✔
724

725
type withEscapedSpace struct {
726
}
727

728
func (o *withEscapedSpace) SetParserOption(c *Config) {
6✔
729
        c.EscapedSpace = true
6✔
730
}
6✔
731

732
// WithEscapedSpace is a functional option indicates that a '\' escaped half-space(0x20) should not trigger parsers.
733
func WithEscapedSpace() Option {
6✔
734
        return &withEscapedSpace{}
6✔
735
}
6✔
736

737
type withOption struct {
738
        name  OptionName
739
        value any
740
}
741

742
func (o *withOption) SetParserOption(c *Config) {
×
743
        c.Options[o.name] = o.value
×
744
}
×
745

746
// WithOption is a functional option that allow you to set
747
// an arbitrary option to the parser.
NEW
748
func WithOption(name OptionName, value any) Option {
×
749
        return &withOption{name, value}
×
750
}
×
751

752
// NewParser returns a new Parser with given options.
753
func NewParser(options ...Option) Parser {
2,148✔
754
        config := NewConfig()
2,148✔
755
        for _, opt := range options {
8,592✔
756
                opt.SetParserOption(config)
6,444✔
757
        }
6,444✔
758

759
        p := &parser{
2,148✔
760
                options: map[OptionName]any{},
2,148✔
761
                config:  config,
2,148✔
762
        }
2,148✔
763

2,148✔
764
        return p
2,148✔
765
}
766

767
func (p *parser) AddOptions(opts ...Option) {
21,624✔
768
        for _, opt := range opts {
53,091✔
769
                opt.SetParserOption(p.config)
31,467✔
770
        }
31,467✔
771
}
772

773
func (p *parser) addBlockParser(v util.PrioritizedValue, options map[OptionName]any) {
27,255✔
774
        bp, ok := v.Value.(BlockParser)
27,255✔
775
        if !ok {
27,255✔
776
                panic(fmt.Sprintf("%v is not a BlockParser", v.Value))
×
777
        }
778
        tcs := bp.Trigger()
27,255✔
779
        so, ok := v.Value.(SetOptioner)
27,255✔
780
        if ok {
31,527✔
781
                for oname, ovalue := range options {
12,138✔
782
                        so.SetOption(oname, ovalue)
7,866✔
783
                }
7,866✔
784
        }
785
        if tcs == nil {
31,527✔
786
                p.freeBlockParsers = append(p.freeBlockParsers, bp)
4,272✔
787
        } else {
27,255✔
788
                for _, tc := range tcs {
105,774✔
789
                        if p.blockParsers[tc] == nil {
129,441✔
790
                                p.blockParsers[tc] = []BlockParser{}
46,650✔
791
                        }
46,650✔
792
                        p.blockParsers[tc] = append(p.blockParsers[tc], bp)
82,791✔
793
                }
794
        }
795
}
796

797
func (p *parser) addInlineParser(v util.PrioritizedValue, options map[OptionName]any) {
24,411✔
798
        ip, ok := v.Value.(InlineParser)
24,411✔
799
        if !ok {
24,411✔
800
                panic(fmt.Sprintf("%v is not a InlineParser", v.Value))
×
801
        }
802
        tcs := ip.Trigger()
24,411✔
803
        so, ok := v.Value.(SetOptioner)
24,411✔
804
        if ok {
30,300✔
805
                for oname, ovalue := range options {
17,625✔
806
                        so.SetOption(oname, ovalue)
11,736✔
807
                }
11,736✔
808
        }
809
        if cb, ok := ip.(CloseBlocker); ok {
26,547✔
810
                p.closeBlockers = append(p.closeBlockers, cb)
2,136✔
811
        }
2,136✔
812
        for _, tc := range tcs {
88,587✔
813
                if p.inlineParsers[tc] == nil {
96,810✔
814
                        p.inlineParsers[tc] = []InlineParser{}
32,634✔
815
                }
32,634✔
816
                p.inlineParsers[tc] = append(p.inlineParsers[tc], ip)
64,176✔
817
        }
818
}
819

820
func (p *parser) addParagraphTransformer(v util.PrioritizedValue, options map[OptionName]any) {
6,084✔
821
        pt, ok := v.Value.(ParagraphTransformer)
6,084✔
822
        if !ok {
6,084✔
823
                panic(fmt.Sprintf("%v is not a ParagraphTransformer", v.Value))
×
824
        }
825
        so, ok := v.Value.(SetOptioner)
6,084✔
826
        if ok {
6,084✔
827
                for oname, ovalue := range options {
×
828
                        so.SetOption(oname, ovalue)
×
829
                }
×
830
        }
831
        p.paragraphTransformers = append(p.paragraphTransformers, pt)
6,084✔
832
}
833

834
func (p *parser) addASTTransformer(v util.PrioritizedValue, options map[OptionName]any) {
5,919✔
835
        at, ok := v.Value.(ASTTransformer)
5,919✔
836
        if !ok {
5,919✔
837
                panic(fmt.Sprintf("%v is not a ASTTransformer", v.Value))
×
838
        }
839
        so, ok := v.Value.(SetOptioner)
5,919✔
840
        if ok {
5,919✔
841
                for oname, ovalue := range options {
×
842
                        so.SetOption(oname, ovalue)
×
843
                }
×
844
        }
845
        p.astTransformers = append(p.astTransformers, at)
5,919✔
846
}
847

848
// A ParseConfig struct is a data structure that holds configuration of the Parser.Parse.
849
type ParseConfig struct {
850
        Context Context
851
}
852

853
// A ParseOption is a functional option type for the Parser.Parse.
854
type ParseOption func(c *ParseConfig)
855

856
// WithContext is a functional option that allow you to override
857
// a default context.
858
func WithContext(context Context) ParseOption {
9✔
859
        return func(c *ParseConfig) {
18✔
860
                c.Context = context
9✔
861
        }
9✔
862
}
863

864
func (p *parser) Parse(reader text.Reader, opts ...ParseOption) ast.Node {
4,557✔
865
        p.initSync.Do(func() {
6,693✔
866
                p.config.BlockParsers.Sort()
2,136✔
867
                for _, v := range p.config.BlockParsers {
29,391✔
868
                        p.addBlockParser(v, p.config.Options)
27,255✔
869
                }
27,255✔
870
                for i := range p.blockParsers {
548,952✔
871
                        if p.blockParsers[i] != nil {
593,466✔
872
                                p.blockParsers[i] = append(p.blockParsers[i], p.freeBlockParsers...)
46,650✔
873
                        }
46,650✔
874
                }
875

876
                p.config.InlineParsers.Sort()
2,136✔
877
                for _, v := range p.config.InlineParsers {
26,547✔
878
                        p.addInlineParser(v, p.config.Options)
24,411✔
879
                }
24,411✔
880
                p.config.ParagraphTransformers.Sort()
2,136✔
881
                for _, v := range p.config.ParagraphTransformers {
8,220✔
882
                        p.addParagraphTransformer(v, p.config.Options)
6,084✔
883
                }
6,084✔
884
                p.config.ASTTransformers.Sort()
2,136✔
885
                for _, v := range p.config.ASTTransformers {
8,055✔
886
                        p.addASTTransformer(v, p.config.Options)
5,919✔
887
                }
5,919✔
888
                p.escapedSpace = p.config.EscapedSpace
2,136✔
889
                p.config = nil
2,136✔
890
        })
891
        c := &ParseConfig{}
4,557✔
892
        for _, opt := range opts {
4,566✔
893
                opt(c)
9✔
894
        }
9✔
895
        if c.Context == nil {
9,105✔
896
                c.Context = NewContext()
4,548✔
897
        }
4,548✔
898
        pc := c.Context
4,557✔
899
        root := ast.NewDocument()
4,557✔
900
        p.parseBlocks(root, reader, pc)
4,557✔
901

4,557✔
902
        blockReader := text.NewBlockReader(reader.Source(), nil)
4,557✔
903
        p.walkBlock(root, func(node ast.Node) {
18,849✔
904
                p.parseBlock(blockReader, node, pc)
14,292✔
905
        })
14,292✔
906
        for _, at := range p.astTransformers {
10,527✔
907
                at.Transform(root, reader, pc)
5,970✔
908
        }
5,970✔
909

910
        // root.Dump(reader.Source(), 0)
911
        return root
4,557✔
912
}
913

914
func (p *parser) transformParagraph(node *ast.Paragraph, reader text.Reader, pc Context) bool {
5,454✔
915
        for _, pt := range p.paragraphTransformers {
15,150✔
916
                pt.Transform(node, reader, pc)
9,696✔
917
                if node.Parent() == nil {
10,218✔
918
                        return true
522✔
919
                }
522✔
920
        }
921
        return false
4,932✔
922
}
923

924
func (p *parser) closeBlocks(from, to int, reader text.Reader, pc Context) {
7,371✔
925
        blocks := pc.OpenedBlocks()
7,371✔
926
        for i := from; i >= to; i-- {
16,638✔
927
                node := blocks[i].Node
9,267✔
928
                paragraph, ok := node.(*ast.Paragraph)
9,267✔
929
                if ok && node.Parent() != nil {
14,547✔
930
                        p.transformParagraph(paragraph, reader, pc)
5,280✔
931
                }
5,280✔
932
                if node.Parent() != nil { // closes only if node has not been transformed
18,009✔
933
                        blocks[i].Parser.Close(blocks[i].Node, reader, pc)
8,742✔
934
                }
8,742✔
935
        }
936
        if from == len(blocks)-1 {
13,422✔
937
                blocks = blocks[0:to]
6,051✔
938
        } else {
7,371✔
939
                blocks = append(blocks[0:to], blocks[from+1:]...)
1,320✔
940
        }
1,320✔
941
        pc.SetOpenedBlocks(blocks)
7,371✔
942
}
943

944
type blockOpenResult int
945

946
const (
947
        paragraphContinuation blockOpenResult = iota + 1
948
        newBlocksOpened
949
        noBlocksOpened
950
)
951

952
func (p *parser) openBlocks(parent ast.Node, blankLine bool, reader text.Reader, pc Context) blockOpenResult {
10,149✔
953
        result := blockOpenResult(noBlocksOpened)
10,149✔
954
        continuable := false
10,149✔
955
        lastBlock := pc.LastOpenedBlock()
10,149✔
956
        if lastBlock.Node != nil {
14,658✔
957
                continuable = ast.IsParagraph(lastBlock.Node)
4,509✔
958
        }
4,509✔
959
retry:
960
        var bps []BlockParser
12,411✔
961
        line, _ := reader.PeekLine()
12,411✔
962
        w, pos := util.IndentWidth(line, reader.LineOffset())
12,411✔
963
        if w >= len(line) {
12,465✔
964
                pc.SetBlockOffset(-1)
54✔
965
                pc.SetBlockIndent(-1)
54✔
966
        } else {
12,411✔
967
                pc.SetBlockOffset(pos)
12,357✔
968
                pc.SetBlockIndent(w)
12,357✔
969
        }
12,357✔
970
        if line == nil || line[0] == '\n' {
14,136✔
971
                goto continuable
1,725✔
972
        }
973
        bps = p.freeBlockParsers
10,686✔
974
        if pos < len(line) {
21,372✔
975
                bps = p.blockParsers[line[pos]]
10,686✔
976
                if bps == nil {
15,498✔
977
                        bps = p.freeBlockParsers
4,812✔
978
                }
4,812✔
979
        }
980
        if bps == nil {
10,686✔
981
                goto continuable
×
982
        }
983

984
        for _, bp := range bps {
35,457✔
985
                if continuable && result == noBlocksOpened && !bp.CanInterruptParagraph() {
27,237✔
986
                        continue
2,466✔
987
                }
988
                if w > 3 && !bp.CanAcceptIndentedLine() {
22,518✔
989
                        continue
213✔
990
                }
991
                lastBlock = pc.LastOpenedBlock()
22,092✔
992
                last := lastBlock.Node
22,092✔
993
                node, state := bp.Open(parent, reader, pc)
22,092✔
994
                if node != nil {
31,539✔
995
                        // Parser requires last node to be a paragraph.
9,447✔
996
                        // With table extension:
9,447✔
997
                        //
9,447✔
998
                        //     0
9,447✔
999
                        //     -:
9,447✔
1000
                        //     -
9,447✔
1001
                        //
9,447✔
1002
                        // '-' on 3rd line seems a Setext heading because 1st and 2nd lines
9,447✔
1003
                        // are being paragraph when the Settext heading parser tries to parse the 3rd
9,447✔
1004
                        // line.
9,447✔
1005
                        // But 1st line and 2nd line are a table. Thus this paragraph will be transformed
9,447✔
1006
                        // by a paragraph transformer. So this text should be converted to a table and
9,447✔
1007
                        // an empty list.
9,447✔
1008
                        if state&RequireParagraph != 0 {
9,627✔
1009
                                if last == parent.LastChild() {
354✔
1010
                                        // Opened paragraph may be transformed by ParagraphTransformers in
174✔
1011
                                        // closeBlocks().
174✔
1012
                                        lastBlock.Parser.Close(last, reader, pc)
174✔
1013
                                        blocks := pc.OpenedBlocks()
174✔
1014
                                        pc.SetOpenedBlocks(blocks[0 : len(blocks)-1])
174✔
1015
                                        if p.transformParagraph(last.(*ast.Paragraph), reader, pc) {
180✔
1016
                                                // Paragraph has been transformed.
6✔
1017
                                                // So this parser is considered as failing.
6✔
1018
                                                continuable = false
6✔
1019
                                                goto retry
6✔
1020
                                        }
1021
                                }
1022
                        }
1023
                        node.SetBlankPreviousLines(blankLine)
9,441✔
1024
                        if last != nil && last.Parent() == nil {
9,441✔
1025
                                lastPos := len(pc.OpenedBlocks()) - 1
×
1026
                                p.closeBlocks(lastPos, lastPos, reader, pc)
×
1027
                        }
×
1028
                        parent.AppendChild(parent, node)
9,441✔
1029
                        result = newBlocksOpened
9,441✔
1030
                        be := Block{node, bp}
9,441✔
1031
                        pc.SetOpenedBlocks(append(pc.OpenedBlocks(), be))
9,441✔
1032
                        if state&HasChildren != 0 {
11,697✔
1033
                                parent = node
2,256✔
1034
                                goto retry // try child block
2,256✔
1035
                        }
1036
                        break // no children, can not open more blocks on this line
7,185✔
1037
                }
1038
        }
1039

1040
continuable:
1041
        if result == noBlocksOpened && continuable {
12,558✔
1042
                state := lastBlock.Parser.Continue(lastBlock.Node, reader, pc)
2,409✔
1043
                if state&Continue != 0 {
3,630✔
1044
                        result = paragraphContinuation
1,221✔
1045
                }
1,221✔
1046
        }
1047
        return result
10,149✔
1048
}
1049

1050
type lineStat struct {
1051
        lineNum int
1052
        level   int
1053
        isBlank bool
1054
}
1055

1056
func isBlankLine(lineNum, level int, stats []lineStat) bool {
4,509✔
1057
        l := len(stats)
4,509✔
1058
        if l == 0 {
4,509✔
1059
                return true
×
1060
        }
×
1061
        for i := l - 1 - level; i >= 0; i-- {
11,082✔
1062
                s := stats[i]
6,573✔
1063
                if s.lineNum == lineNum && s.level <= level {
8,628✔
1064
                        return s.isBlank
2,055✔
1065
                } else if s.lineNum < lineNum {
6,573✔
1066
                        break
×
1067
                }
1068
        }
1069
        return false
2,454✔
1070
}
1071

1072
func (p *parser) parseBlocks(parent ast.Node, reader text.Reader, pc Context) {
4,557✔
1073
        pc.SetOpenedBlocks(nil)
4,557✔
1074
        blankLines := make([]lineStat, 0, 128)
4,557✔
1075
        for { // process blocks separated by blank lines
10,356✔
1076
                _, _, ok := reader.SkipBlankLines()
5,799✔
1077
                if !ok {
5,958✔
1078
                        return
159✔
1079
                }
159✔
1080
                // first, we try to open blocks
1081
                if p.openBlocks(parent, true, reader, pc) != newBlocksOpened {
5,640✔
1082
                        return
×
1083
                }
×
1084
                reader.AdvanceLine()
5,640✔
1085
                blankLines = blankLines[0:0]
5,640✔
1086
                for { // process opened blocks line by line
17,010✔
1087
                        openedBlocks := pc.OpenedBlocks()
11,370✔
1088
                        l := len(openedBlocks)
11,370✔
1089
                        if l == 0 {
12,612✔
1090
                                break
1,242✔
1091
                        }
1092
                        lastIndex := l - 1
10,128✔
1093
                        for i := range l {
23,112✔
1094
                                be := openedBlocks[i]
12,984✔
1095
                                line, _ := reader.PeekLine()
12,984✔
1096
                                if line == nil {
17,382✔
1097
                                        p.closeBlocks(lastIndex, 0, reader, pc)
4,398✔
1098
                                        reader.AdvanceLine()
4,398✔
1099
                                        return
4,398✔
1100
                                }
4,398✔
1101
                                lineNum, _ := reader.Position()
8,586✔
1102
                                blankLines = append(blankLines, lineStat{lineNum, i, util.IsBlank(line)})
8,586✔
1103
                                // If node is a paragraph, p.openBlocks determines whether it is continuable.
8,586✔
1104
                                // So we do not process paragraphs here.
8,586✔
1105
                                if !ast.IsParagraph(be.Node) {
14,484✔
1106
                                        state := be.Parser.Continue(be.Node, reader, pc)
5,898✔
1107
                                        if state&Continue != 0 {
10,290✔
1108
                                                // When current node is a container block and has no children,
4,392✔
1109
                                                // we try to open new child nodes
4,392✔
1110
                                                if state&HasChildren != 0 && i == lastIndex {
4,707✔
1111
                                                        isBlank := isBlankLine(lineNum-1, i+1, blankLines)
315✔
1112
                                                        p.openBlocks(be.Node, isBlank, reader, pc)
315✔
1113
                                                        break
315✔
1114
                                                }
1115
                                                continue
4,077✔
1116
                                        }
1117
                                }
1118
                                // current node may be closed or lazy continuation
1119
                                isBlank := isBlankLine(lineNum-1, i, blankLines)
4,194✔
1120
                                thisParent := parent
4,194✔
1121
                                if i != 0 {
5,310✔
1122
                                        thisParent = openedBlocks[i-1].Node
1,116✔
1123
                                }
1,116✔
1124
                                lastNode := openedBlocks[lastIndex].Node
4,194✔
1125
                                result := p.openBlocks(thisParent, isBlank, reader, pc)
4,194✔
1126
                                if result != paragraphContinuation {
7,167✔
1127
                                        // lastNode is a paragraph and was transformed by the paragraph
2,973✔
1128
                                        // transformers.
2,973✔
1129
                                        if openedBlocks[lastIndex].Node != lastNode {
3,147✔
1130
                                                lastIndex--
174✔
1131
                                        }
174✔
1132
                                        p.closeBlocks(lastIndex, i, reader, pc)
2,973✔
1133
                                }
1134
                                break
4,194✔
1135
                        }
1136

1137
                        reader.AdvanceLine()
5,730✔
1138
                }
1139
        }
1140
}
1141

1142
func (p *parser) walkBlock(block ast.Node, cb func(node ast.Node)) {
14,292✔
1143
        for c := block.FirstChild(); c != nil; c = c.NextSibling() {
24,027✔
1144
                p.walkBlock(c, cb)
9,735✔
1145
        }
9,735✔
1146
        cb(block)
14,292✔
1147
}
1148

1149
const (
1150
        lineBreakHard uint8 = 1 << iota
1151
        lineBreakSoft
1152
        lineBreakVisible
1153
)
1154

1155
func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context) {
14,292✔
1156
        if parent.IsRaw() {
15,813✔
1157
                return
1,521✔
1158
        }
1,521✔
1159
        escaped := false
12,771✔
1160
        source := block.Source()
12,771✔
1161
        block.Reset(parent.Lines())
12,771✔
1162
        for {
29,775✔
1163
        retry:
17,004✔
1164
                line, _ := block.PeekLine()
473,187✔
1165
                if line == nil {
485,958✔
1166
                        break
12,771✔
1167
                }
1168
                lineLength := len(line)
460,416✔
1169
                var lineBreakFlags uint8
460,416✔
1170
                hasNewLine := line[lineLength-1] == '\n'
460,416✔
1171
                if ((lineLength >= 3 && line[lineLength-2] == '\\' &&
460,416✔
1172
                        line[lineLength-3] != '\\') || (lineLength == 2 && line[lineLength-2] == '\\')) && hasNewLine { // ends with \\n
460,488✔
1173
                        lineLength -= 2
72✔
1174
                        lineBreakFlags |= lineBreakHard | lineBreakVisible
72✔
1175
                } else if ((lineLength >= 4 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r' &&
460,416✔
1176
                        line[lineLength-4] != '\\') || (lineLength == 3 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r')) &&
460,344✔
1177
                        hasNewLine { // ends with \\r\n
460,347✔
1178
                        lineLength -= 3
3✔
1179
                        lineBreakFlags |= lineBreakHard | lineBreakVisible
3✔
1180
                } else if lineLength >= 3 && line[lineLength-3] == ' ' && line[lineLength-2] == ' ' &&
460,344✔
1181
                        hasNewLine { // ends with [space][space]\n
460,389✔
1182
                        lineLength -= 3
48✔
1183
                        lineBreakFlags |= lineBreakHard
48✔
1184
                } else if lineLength >= 4 && line[lineLength-4] == ' ' && line[lineLength-3] == ' ' &&
460,341✔
1185
                        line[lineLength-2] == '\r' && hasNewLine { // ends with [space][space]\r\n
460,296✔
1186
                        lineLength -= 4
3✔
1187
                        lineBreakFlags |= lineBreakHard
3✔
1188
                } else if hasNewLine {
461,526✔
1189
                        // If the line ends with a newline character, but it is not a hardlineBreak, then it is a softLinebreak
1,233✔
1190
                        // If the line ends with a hardlineBreak, then it cannot end with a softLinebreak
1,233✔
1191
                        // See https://spec.commonmark.org/0.30/#soft-line-breaks
1,233✔
1192
                        lineBreakFlags |= lineBreakSoft
1,233✔
1193
                }
1,233✔
1194

1195
                l, startPosition := block.Position()
460,416✔
1196
                n := 0
460,416✔
1197
                for i := range lineLength {
4,559,181✔
1198
                        c := line[i]
4,098,765✔
1199
                        if c == '\n' {
4,099,494✔
1200
                                break
729✔
1201
                        }
1202
                        isSpace := util.IsSpace(c) && c != '\r' && c != '\n'
4,098,036✔
1203
                        isPunct := util.IsPunct(c)
4,098,036✔
1204
                        if (isPunct && !escaped) || isSpace && !(escaped && p.escapedSpace) || i == 0 {
7,117,755✔
1205
                                parserChar := c
3,019,719✔
1206
                                if isSpace || (i == 0 && !isPunct) {
4,079,316✔
1207
                                        parserChar = ' '
1,059,597✔
1208
                                }
1,059,597✔
1209
                                ips := p.inlineParsers[parserChar]
3,019,719✔
1210
                                if ips != nil {
4,530,654✔
1211
                                        block.Advance(n)
1,510,935✔
1212
                                        n = 0
1,510,935✔
1213
                                        savedLine, savedPosition := block.Position()
1,510,935✔
1214
                                        if i != 0 {
2,865,954✔
1215
                                                _, currentPosition := block.Position()
1,355,019✔
1216
                                                ast.MergeOrAppendTextSegment(parent, startPosition.Between(currentPosition))
1,355,019✔
1217
                                                _, startPosition = block.Position()
1,355,019✔
1218
                                        }
1,355,019✔
1219
                                        var inlineNode ast.Node
1,510,935✔
1220
                                        for _, ip := range ips {
3,627,660✔
1221
                                                inlineNode = ip.Parse(parent, block, pc)
2,116,725✔
1222
                                                if inlineNode != nil {
2,572,908✔
1223
                                                        if inlineNode.Pos() < 0 {
911,397✔
1224
                                                                inlineNode.(interface{ SetPos(int) }).SetPos(startPosition.Start)
455,214✔
1225
                                                        }
455,214✔
1226
                                                        break
456,183✔
1227
                                                }
1228
                                                block.SetPosition(savedLine, savedPosition)
1,660,542✔
1229
                                        }
1230
                                        if inlineNode != nil {
1,967,118✔
1231
                                                parent.AppendChild(parent, inlineNode)
456,183✔
1232
                                                goto retry
456,183✔
1233
                                        }
1234
                                }
1235
                        }
1236
                        if escaped {
3,642,375✔
1237
                                escaped = false
522✔
1238
                                n++
522✔
1239
                                continue
522✔
1240
                        }
1241

1242
                        if c == '\\' {
3,641,871✔
1243
                                escaped = true
540✔
1244
                                n++
540✔
1245
                                continue
540✔
1246
                        }
1247

1248
                        escaped = false
3,640,791✔
1249
                        n++
3,640,791✔
1250
                }
1251
                if n != 0 {
8,346✔
1252
                        block.Advance(n)
4,113✔
1253
                }
4,113✔
1254
                currentL, currentPosition := block.Position()
4,233✔
1255
                if l != currentL {
4,233✔
1256
                        continue
×
1257
                }
1258
                diff := startPosition.Between(currentPosition)
4,233✔
1259
                var text *ast.Text
4,233✔
1260
                if lineBreakFlags&(lineBreakHard|lineBreakVisible) == lineBreakHard|lineBreakVisible {
4,272✔
1261
                        text = ast.NewTextSegment(diff)
39✔
1262
                } else {
4,233✔
1263
                        text = ast.NewTextSegment(diff.TrimRightSpace(source))
4,194✔
1264
                }
4,194✔
1265
                text.SetSoftLineBreak(lineBreakFlags&lineBreakSoft != 0)
4,233✔
1266
                text.SetHardLineBreak(lineBreakFlags&lineBreakHard != 0)
4,233✔
1267
                parent.AppendChild(parent, text)
4,233✔
1268
                block.AdvanceLine()
4,233✔
1269
        }
1270

1271
        ProcessDelimiters(nil, pc)
12,771✔
1272
        for _, ip := range p.closeBlockers {
25,542✔
1273
                ip.CloseBlock(parent, block, pc)
12,771✔
1274
        }
12,771✔
1275

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