GoPLS Viewer

Home|gopls/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
5package present
6
7import (
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
29func init() {
30    funcs["style"] = Style
31}
32
33// Style returns s with HTML entities escaped and font indicators turned into
34// HTML font tags.
35func Style(s stringtemplate.HTML {
36    return template.HTML(font(html.EscapeString(s)))
37}
38
39// font returns s with font indicators turned into HTML font tags.
40func font(s stringstring {
41    if !strings.ContainsAny(s"[`_*") {
42        return s
43    }
44    words := split(s)
45    var b bytes.Buffer
46Word:
47    for wword := 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(wordmarker)
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        openword := 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(wordword[: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        headtail := word[:last+1], word[last+1:]
96        b.Reset()
97        b.WriteString(open)
98        var wid int
99        for i := 1i < len(head)-1i += wid {
100            var r rune
101            rwid = 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.
126func split(s string) []string {
127    var (
128        words = make([]string010)
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(wordss[start:start+j])
142                }
143                // Append link itself.
144                words = append(wordss[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(wordss[start:end])
152        start = end
153    }
154
155    wasSpace := false
156    for ir := 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
MembersX
font.s
font.RangeStmt_1010.BlockStmt.link
split.RangeStmt_3811.BlockStmt.isSpace
font.RangeStmt_1010.BlockStmt.first
split.start
split.BlockStmt.BlockStmt._
Style.s
font
font.RangeStmt_1010.BlockStmt.close
split.BlockStmt.j
font.RangeStmt_1010.BlockStmt.BlockStmt._
font.RangeStmt_1010.BlockStmt.last
split.RangeStmt_3811.r
split.RangeStmt_3811.i
font.RangeStmt_1010.BlockStmt.i
split.s
split.BlockStmt.BlockStmt.l
font.words
font.RangeStmt_1010.w
font.RangeStmt_1010.BlockStmt.BlockStmt.r
font.RangeStmt_1010.BlockStmt.wid
Style
font.b
font.RangeStmt_1010.word
font.RangeStmt_1010.BlockStmt.marker
font.RangeStmt_1010.BlockStmt._
split
split.wasSpace
Members
X