...
1
2
3
4
5 package present
6
7 import (
8 "bytes"
9 "html"
10 "html/template"
11 "strings"
12 "unicode"
13 "unicode/utf8"
14 )
15
16
28
29 func init() {
30 funcs["style"] = Style
31 }
32
33
34
35 func Style(s string) template.HTML {
36 return template.HTML(font(html.EscapeString(s)))
37 }
38
39
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
57 first := strings.IndexAny(word, marker)
58 if first == -1 {
59 continue Word
60 }
61
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]
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
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
104 b.WriteRune(r)
105 continue
106 }
107 if head[i+1] != char {
108
109 b.WriteRune(' ')
110 continue
111 }
112
113
114 b.WriteByte(char)
115 wid++
116 }
117 b.WriteString(close)
118 b.WriteString(tail)
119 words[w] = b.String()
120 }
121 return strings.Join(words, "")
122 }
123
124
125
126 func split(s string) []string {
127 var (
128 words = make([]string, 0, 10)
129 start = 0
130 )
131
132
133
134
135
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
140 if j > 0 {
141 words = append(words, s[start:start+j])
142 }
143
144 words = append(words, s[start+j:start+j+l])
145
146 start = start + j + l
147 return
148 }
149 }
150
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 }
168
View as plain text