github.com/bir3/gocompiler@v0.9.2202/src/go/doc/comment/html.go (about) 1 // Copyright 2022 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 5 package comment 6 7 import ( 8 "bytes" 9 "fmt" 10 "strconv" 11 ) 12 13 // An htmlPrinter holds the state needed for printing a [Doc] as HTML. 14 type htmlPrinter struct { 15 *Printer 16 tight bool 17 } 18 19 // HTML returns an HTML formatting of the [Doc]. 20 // See the [Printer] documentation for ways to customize the HTML output. 21 func (p *Printer) HTML(d *Doc) []byte { 22 hp := &htmlPrinter{Printer: p} 23 var out bytes.Buffer 24 for _, x := range d.Content { 25 hp.block(&out, x) 26 } 27 return out.Bytes() 28 } 29 30 // block prints the block x to out. 31 func (p *htmlPrinter) block(out *bytes.Buffer, x Block) { 32 switch x := x.(type) { 33 default: 34 fmt.Fprintf(out, "?%T", x) 35 36 case *Paragraph: 37 if !p.tight { 38 out.WriteString("<p>") 39 } 40 p.text(out, x.Text) 41 out.WriteString("\n") 42 43 case *Heading: 44 out.WriteString("<h") 45 h := strconv.Itoa(p.headingLevel()) 46 out.WriteString(h) 47 if id := p.headingID(x); id != "" { 48 out.WriteString(` id="`) 49 p.escape(out, id) 50 out.WriteString(`"`) 51 } 52 out.WriteString(">") 53 p.text(out, x.Text) 54 out.WriteString("</h") 55 out.WriteString(h) 56 out.WriteString(">\n") 57 58 case *Code: 59 out.WriteString("<pre>") 60 p.escape(out, x.Text) 61 out.WriteString("</pre>\n") 62 63 case *List: 64 kind := "ol>\n" 65 if x.Items[0].Number == "" { 66 kind = "ul>\n" 67 } 68 out.WriteString("<") 69 out.WriteString(kind) 70 next := "1" 71 for _, item := range x.Items { 72 out.WriteString("<li") 73 if n := item.Number; n != "" { 74 if n != next { 75 out.WriteString(` value="`) 76 out.WriteString(n) 77 out.WriteString(`"`) 78 next = n 79 } 80 next = inc(next) 81 } 82 out.WriteString(">") 83 p.tight = !x.BlankBetween() 84 for _, blk := range item.Content { 85 p.block(out, blk) 86 } 87 p.tight = false 88 } 89 out.WriteString("</") 90 out.WriteString(kind) 91 } 92 } 93 94 // inc increments the decimal string s. 95 // For example, inc("1199") == "1200". 96 func inc(s string) string { 97 b := []byte(s) 98 for i := len(b) - 1; i >= 0; i-- { 99 if b[i] < '9' { 100 b[i]++ 101 return string(b) 102 } 103 b[i] = '0' 104 } 105 return "1" + string(b) 106 } 107 108 // text prints the text sequence x to out. 109 func (p *htmlPrinter) text(out *bytes.Buffer, x []Text) { 110 for _, t := range x { 111 switch t := t.(type) { 112 case Plain: 113 p.escape(out, string(t)) 114 case Italic: 115 out.WriteString("<i>") 116 p.escape(out, string(t)) 117 out.WriteString("</i>") 118 case *Link: 119 out.WriteString(`<a href="`) 120 p.escape(out, t.URL) 121 out.WriteString(`">`) 122 p.text(out, t.Text) 123 out.WriteString("</a>") 124 case *DocLink: 125 url := p.docLinkURL(t) 126 if url != "" { 127 out.WriteString(`<a href="`) 128 p.escape(out, url) 129 out.WriteString(`">`) 130 } 131 p.text(out, t.Text) 132 if url != "" { 133 out.WriteString("</a>") 134 } 135 } 136 } 137 } 138 139 // escape prints s to out as plain text, 140 // escaping < & " ' and > to avoid being misinterpreted 141 // in larger HTML constructs. 142 func (p *htmlPrinter) escape(out *bytes.Buffer, s string) { 143 start := 0 144 for i := 0; i < len(s); i++ { 145 switch s[i] { 146 case '<': 147 out.WriteString(s[start:i]) 148 out.WriteString("<") 149 start = i + 1 150 case '&': 151 out.WriteString(s[start:i]) 152 out.WriteString("&") 153 start = i + 1 154 case '"': 155 out.WriteString(s[start:i]) 156 out.WriteString(""") 157 start = i + 1 158 case '\'': 159 out.WriteString(s[start:i]) 160 out.WriteString("'") 161 start = i + 1 162 case '>': 163 out.WriteString(s[start:i]) 164 out.WriteString(">") 165 start = i + 1 166 } 167 } 168 out.WriteString(s[start:]) 169 }