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