markitzero.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18import (
19 "fmt"
20 "regexp"
21 "strings"
22
23 "golang.org/x/net/html"
24)
25
26var re_bolder = regexp.MustCompile(`(^|\W)\*\*([\w\s,.!?':_-]+)\*\*($|\W)`)
27var re_italicer = regexp.MustCompile(`(^|\W)\*([\w\s,.!?':_-]+)\*($|\W)`)
28var re_bigcoder = regexp.MustCompile("```\n?((?s:.*?))\n?```\n?")
29var re_coder = regexp.MustCompile("`([^`]*)`")
30var re_quoter = regexp.MustCompile(`(?m:^> (.*)\n?)`)
31var re_link = regexp.MustCompile(`.?.?https?://[^\s"]+[\w/)]`)
32var re_zerolink = regexp.MustCompile(`\[([^]]*)\]\(([^)]*\)?)\)`)
33
34func markitzero(s string) string {
35 // prepare the string
36 s = strings.TrimSpace(s)
37 s = strings.Replace(s, "\r", "", -1)
38 s = html.EscapeString(s)
39 s = strings.Replace(s, "'", "'", -1) // dammit go
40
41 // save away the code blocks so we don't mess them up further
42 var bigcodes, lilcodes []string
43 s = re_bigcoder.ReplaceAllStringFunc(s, func(code string) string {
44 bigcodes = append(bigcodes, code)
45 return "``````"
46 })
47 s = re_coder.ReplaceAllStringFunc(s, func(code string) string {
48 lilcodes = append(lilcodes, code)
49 return "`x`"
50 })
51
52 // mark it zero
53 s = re_bolder.ReplaceAllString(s, "$1<b>$2</b>$3")
54 s = re_italicer.ReplaceAllString(s, "$1<i>$2</i>$3")
55 s = re_quoter.ReplaceAllString(s, "<blockquote>$1</blockquote><p>")
56 s = re_link.ReplaceAllStringFunc(s, linkreplacer)
57 s = re_zerolink.ReplaceAllString(s, `<a class="mention u-url" href="$2">$1</a>`)
58
59 // now restore the code blocks
60 s = re_coder.ReplaceAllStringFunc(s, func(s string) string {
61 code := lilcodes[0]
62 lilcodes = lilcodes[1:]
63 return code
64 })
65 s = re_bigcoder.ReplaceAllStringFunc(s, func(s string) string {
66 code := bigcodes[0]
67 bigcodes = bigcodes[1:]
68 return code
69 })
70 s = re_bigcoder.ReplaceAllString(s, "<pre><code>$1</code></pre><p>")
71 s = re_coder.ReplaceAllString(s, "<code>$1</code>")
72
73 // some final fixups
74 s = strings.Replace(s, "\n", "<br>", -1)
75 s = strings.Replace(s, "<br><blockquote>", "<blockquote>", -1)
76 s = strings.Replace(s, "<br><pre>", "<pre>", -1)
77 s = strings.Replace(s, "<p><br>", "<p>", -1)
78 return s
79}
80
81func linkreplacer(url string) string {
82 if url[0:2] == "](" {
83 return url
84 }
85 prefix := ""
86 for !strings.HasPrefix(url, "http") {
87 prefix += url[0:1]
88 url = url[1:]
89 }
90 addparen := false
91 adddot := false
92 if strings.HasSuffix(url, ")") && strings.IndexByte(url, '(') == -1 {
93 url = url[:len(url)-1]
94 addparen = true
95 }
96 if strings.HasSuffix(url, ".") {
97 url = url[:len(url)-1]
98 adddot = true
99 }
100 url = fmt.Sprintf(`<a class="mention u-url" href="%s">%s</a>`, url, url)
101 if adddot {
102 url += "."
103 }
104 if addparen {
105 url += ")"
106 }
107 return prefix + url
108}