Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / present / style.go
1 // Copyright 2012 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 present
6
7 import (
8         "bytes"
9         "html"
10         "html/template"
11         "strings"
12         "unicode"
13         "unicode/utf8"
14 )
15
16 /*
17         Fonts are demarcated by an initial and final char bracketing a
18         space-delimited word, plus possibly some terminal punctuation.
19         The chars are
20                 _ for italic
21                 * for bold
22                 ` (back quote) for fixed width.
23         Inner appearances of the char become spaces. For instance,
24                 _this_is_italic_!
25         becomes
26                 <i>this is italic</i>!
27 */
28
29 func init() {
30         funcs["style"] = Style
31 }
32
33 // Style returns s with HTML entities escaped and font indicators turned into
34 // HTML font tags.
35 func Style(s string) template.HTML {
36         return template.HTML(font(html.EscapeString(s)))
37 }
38
39 // font returns s with font indicators turned into HTML font tags.
40 func font(s string) string {
41         if !strings.ContainsAny(s, "[`_*") {
42                 return s
43         }
44         words := split(s)
45         var b bytes.Buffer
46 Word:
47         for w, word := range words {
48                 if len(word) < 2 {
49                         continue Word
50                 }
51                 if link, _ := parseInlineLink(word); link != "" {
52                         words[w] = link
53                         continue Word
54                 }
55                 const marker = "_*`"
56                 // Initial punctuation is OK but must be peeled off.
57                 first := strings.IndexAny(word, marker)
58                 if first == -1 {
59                         continue Word
60                 }
61                 // Opening marker must be at the beginning of the token or else preceded by punctuation.
62                 if first != 0 {
63                         r, _ := utf8.DecodeLastRuneInString(word[:first])
64                         if !unicode.IsPunct(r) {
65                                 continue Word
66                         }
67                 }
68                 open, word := word[:first], word[first:]
69                 char := word[0] // ASCII is OK.
70                 close := ""
71                 switch char {
72                 default:
73                         continue Word
74                 case '_':
75                         open += "<i>"
76                         close = "</i>"
77                 case '*':
78                         open += "<b>"
79                         close = "</b>"
80                 case '`':
81                         open += "<code>"
82                         close = "</code>"
83                 }
84                 // Closing marker must be at the end of the token or else followed by punctuation.
85                 last := strings.LastIndex(word, word[:1])
86                 if last == 0 {
87                         continue Word
88                 }
89                 if last+1 != len(word) {
90                         r, _ := utf8.DecodeRuneInString(word[last+1:])
91                         if !unicode.IsPunct(r) {
92                                 continue Word
93                         }
94                 }
95                 head, tail := word[:last+1], word[last+1:]
96                 b.Reset()
97                 b.WriteString(open)
98                 var wid int
99                 for i := 1; i < len(head)-1; i += wid {
100                         var r rune
101                         r, wid = utf8.DecodeRuneInString(head[i:])
102                         if r != rune(char) {
103                                 // Ordinary character.
104                                 b.WriteRune(r)
105                                 continue
106                         }
107                         if head[i+1] != char {
108                                 // Inner char becomes space.
109                                 b.WriteRune(' ')
110                                 continue
111                         }
112                         // Doubled char becomes real char.
113                         // Not worth worrying about "_x__".
114                         b.WriteByte(char)
115                         wid++ // Consumed two chars, both ASCII.
116                 }
117                 b.WriteString(close) // Write closing tag.
118                 b.WriteString(tail)  // Restore trailing punctuation.
119                 words[w] = b.String()
120         }
121         return strings.Join(words, "")
122 }
123
124 // split is like strings.Fields but also returns the runs of spaces
125 // and treats inline links as distinct words.
126 func split(s string) []string {
127         var (
128                 words = make([]string, 0, 10)
129                 start = 0
130         )
131
132         // appendWord appends the string s[start:end] to the words slice.
133         // If the word contains the beginning of a link, the non-link portion
134         // of the word and the entire link are appended as separate words,
135         // and the start index is advanced to the end of the link.
136         appendWord := func(end int) {
137                 if j := strings.Index(s[start:end], "[["); j > -1 {
138                         if _, l := parseInlineLink(s[start+j:]); l > 0 {
139                                 // Append portion before link, if any.
140                                 if j > 0 {
141                                         words = append(words, s[start:start+j])
142                                 }
143                                 // Append link itself.
144                                 words = append(words, s[start+j:start+j+l])
145                                 // Advance start index to end of link.
146                                 start = start + j + l
147                                 return
148                         }
149                 }
150                 // No link; just add the word.
151                 words = append(words, s[start:end])
152                 start = end
153         }
154
155         wasSpace := false
156         for i, r := range s {
157                 isSpace := unicode.IsSpace(r)
158                 if i > start && isSpace != wasSpace {
159                         appendWord(i)
160                 }
161                 wasSpace = isSpace
162         }
163         for start < len(s) {
164                 appendWord(len(s))
165         }
166         return words
167 }