goldberry/markdown.go

100 lines
2.9 KiB
Go

package main
import (
"bufio"
"bytes"
"io"
"regexp"
"strings"
)
var (
boldItalicReg = regexp.MustCompile(`\*\*\*(.*?)\*\*\*`)
boldReg = regexp.MustCompile(`\*\*(.*?)\*\*`)
italicReg = regexp.MustCompile(`\*(.*?)\*`)
strikeReg = regexp.MustCompile(`\~\~(.*?)\~\~`)
underscoreReg = regexp.MustCompile(`__(.*?)__`)
anchorReg = regexp.MustCompile(`\[(.*?)\]\((.*?)\)[^\)]`)
imgReg = regexp.MustCompile(`\!\[(.*?)\]\((.*?)\)[^\)]`)
escapeReg = regexp.MustCompile(`^\>(\s|)`)
blockquoteReg = regexp.MustCompile(`\&gt\;(.*?)$`)
backtipReg = regexp.MustCompile("`(.*?)`")
h1Reg = regexp.MustCompile(`^#(\s|)(.*?)$`)
h2Reg = regexp.MustCompile(`^##(\s|)(.*?)$`)
h3Reg = regexp.MustCompile(`^###(\s|)(.*?)$`)
h4Reg = regexp.MustCompile(`^####(\s|)(.*?)$`)
h5Reg = regexp.MustCompile(`^#####(\s|)(.*?)$`)
h6Reg = regexp.MustCompile(`^######(\s|)(.*?)$`)
)
func NewMarkdown(input io.Reader) string {
buf := bytes.NewBuffer(nil)
inParagraph := false
scanner := bufio.NewScanner(input)
for scanner.Scan() {
line := bytes.TrimSpace(scanner.Bytes())
if len(line) == 0 {
buf.WriteByte('\n')
if inParagraph {
buf.WriteString("</p>")
}
continue
}
// wrap bold and italic text in "<b>" and "<i>" elements
line = boldItalicReg.ReplaceAll(line, []byte(`<b><i>$1</i></b>`))
line = boldReg.ReplaceAll(line, []byte(`<b>$1</b>`))
line = italicReg.ReplaceAll(line, []byte(`<i>$1</i>`))
// wrap strikethrough text in "<s>" tags
line = strikeReg.ReplaceAll(line, []byte(`<s>$1</s>`))
// wrap underscored text in "<u>" tags
line = underscoreReg.ReplaceAll(line, []byte(`<u>$1</u>`))
// convert images to image tags
line = imgReg.ReplaceAll(line, []byte(`<img src="$2" alt="$1">`))
// convert links to anchor tags
line = anchorReg.ReplaceAll(line, []byte(`<a href="$2" class="inline-link">$1</a> `))
// escape and wrap blockquotes in "<blockquote>" tags
line = escapeReg.ReplaceAll(line, []byte(`&gt;`))
line = blockquoteReg.ReplaceAll(line, []byte(`<blockquote>$1</blockquote>`))
// wrap the content of backticks inside of "<code>" tags
line = backtipReg.ReplaceAll(line, []byte(`<code>$1</code>`))
// convert headings
if line[0] == '#' {
count := bytes.Count(line, []byte(`#`))
switch count {
case 1:
line = h1Reg.ReplaceAll(line, []byte(`<h1>$2</h1>`))
case 2:
line = h2Reg.ReplaceAll(line, []byte(`<h2>$2</h2>`))
case 3:
line = h3Reg.ReplaceAll(line, []byte(`<h3>$2</h3>`))
case 4:
line = h4Reg.ReplaceAll(line, []byte(`<h4>$2</h4>`))
case 5:
line = h5Reg.ReplaceAll(line, []byte(`<h5>$2</h5>`))
case 6:
line = h6Reg.ReplaceAll(line, []byte(`<h6>$2</h6>`))
}
} else {
if !inParagraph {
buf.WriteString("<p>")
}
buf.WriteByte(' ')
inParagraph = true
}
buf.Write(line)
buf.WriteByte('\n')
}
return buf.String()
}
func ParseMarkdown(md string) string {
return NewMarkdown(strings.NewReader(md))
}