• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package markdown
6
7import (
8	"bytes"
9	"strings"
10)
11
12type Empty struct {
13	Position
14}
15
16func (b *Empty) PrintHTML(buf *bytes.Buffer) {}
17
18func (b *Empty) printMarkdown(*bytes.Buffer, mdState) {}
19
20type Paragraph struct {
21	Position
22	Text *Text
23}
24
25func (b *Paragraph) PrintHTML(buf *bytes.Buffer) {
26	buf.WriteString("<p>")
27	b.Text.PrintHTML(buf)
28	buf.WriteString("</p>\n")
29}
30
31func (b *Paragraph) printMarkdown(buf *bytes.Buffer, s mdState) {
32	// // Ignore prefix when in a list.
33	// if s.bullet == 0 {
34	// 	buf.WriteString(s.prefix)
35	// }
36	b.Text.printMarkdown(buf, s)
37}
38
39type paraBuilder struct {
40	text  []string
41	table *tableBuilder
42}
43
44func (b *paraBuilder) extend(p *parseState, s line) (line, bool) {
45	return s, false
46}
47
48func (b *paraBuilder) build(p buildState) Block {
49	if b.table != nil {
50		return b.table.build(p)
51	}
52
53	s := strings.Join(b.text, "\n")
54	for s != "" {
55		end, ok := parseLinkRefDef(p, s)
56		if !ok {
57			break
58		}
59		s = s[skipSpace(s, end):]
60	}
61
62	if s == "" {
63		return &Empty{p.pos()}
64	}
65
66	// Recompute EndLine because a line of b.text
67	// might have been taken away to start a table.
68	pos := p.pos()
69	pos.EndLine = pos.StartLine + len(b.text) - 1
70	return &Paragraph{
71		pos,
72		p.newText(pos, s),
73	}
74}
75
76func newPara(p *parseState, s line) (line, bool) {
77	// Process paragraph continuation text or start new paragraph.
78	b := p.para()
79	indented := p.lineDepth == len(p.stack)-2 // fully indented, not playing "pargraph continuation text" games
80	text := s.trimSpaceString()
81
82	if b != nil && b.table != nil {
83		if indented && text != "" && text != "|" {
84			// Continue table.
85			b.table.addRow(text)
86			return line{}, true
87		}
88		// Blank or unindented line ends table.
89		// (So does a new block structure, but the caller has checked that already.)
90		// So does a line with just a pipe:
91		// https://github.com/github/cmark-gfm/pull/127 and
92		// https://github.com/github/cmark-gfm/pull/128
93		// fixed a buffer overread by rejecting | by itself as a table line.
94		// That seems to violate the spec, but we will play along.
95		b = nil
96	}
97
98	// If we are looking for tables and this is a table start, start a table.
99	if p.Table && b != nil && indented && len(b.text) > 0 && isTableStart(b.text[len(b.text)-1], text) {
100		hdr := b.text[len(b.text)-1]
101		b.text = b.text[:len(b.text)-1]
102		tb := new(paraBuilder)
103		p.addBlock(tb)
104		tb.table = new(tableBuilder)
105		tb.table.start(hdr, text)
106		return line{}, true
107	}
108
109	if b != nil {
110		for i := p.lineDepth; i < len(p.stack); i++ {
111			p.stack[i].pos.EndLine = p.lineno
112		}
113	} else {
114		// Note: Ends anything without a matching prefix.
115		b = new(paraBuilder)
116		p.addBlock(b)
117	}
118	b.text = append(b.text, text)
119	return line{}, true
120}
121