2024-04-01 12:43:36 +02:00

140 lines
2.3 KiB
Go

package markdown
import (
"html"
"strings"
)
var (
headerStart = []string{"<h1>", "<h2>", "<h3>", "<h4>", "<h5>", "<h6>"}
headerEnd = []string{"</h1>", "</h2>", "</h3>", "</h4>", "</h5>", "</h6>"}
)
// Render creates HTML from the supplied markdown text.
func Render(markdown string) string {
var (
out = strings.Builder{}
paragraph = strings.Builder{}
i = 0
lineStart = 0
)
flush := func() {
if paragraph.Len() == 0 {
return
}
out.WriteString("<p>")
writeText(&out, paragraph.String())
out.WriteString("</p>")
paragraph.Reset()
}
for {
if i > len(markdown) {
flush()
return out.String()
}
if i != len(markdown) && markdown[i] != '\n' {
i++
continue
}
line := markdown[lineStart:i]
lineStart = i + 1
i++
switch {
case strings.HasPrefix(line, "#"):
flush()
space := strings.IndexByte(line, ' ')
if space > 0 && space <= 6 {
out.WriteString(headerStart[space-1])
writeText(&out, line[space+1:])
out.WriteString(headerEnd[space-1])
}
default:
if len(line) == 0 {
flush()
continue
}
if paragraph.Len() > 0 {
paragraph.WriteByte(' ')
}
paragraph.WriteString(line)
}
}
}
func writeText(out *strings.Builder, text string) {
var (
i = 0
tokenStart = 0
)
var (
textStart = -1
textEnd = -1
urlStart = -1
parentheses = 0
)
for {
if i == len(text) {
out.WriteString(html.EscapeString(text[tokenStart:]))
return
}
c := text[i]
switch c {
case '[':
out.WriteString(html.EscapeString(text[tokenStart:i]))
tokenStart = i
textStart = i
case ']':
textEnd = i
case '(':
if parentheses == 0 {
urlStart = i
}
parentheses++
case ')':
parentheses--
if parentheses == 0 && textStart >= 0 && textEnd >= 0 && urlStart >= 0 {
linkText := text[textStart+1 : textEnd]
linkURL := text[urlStart+1 : i]
out.WriteString("<a href=\"")
out.WriteString(formatURL(linkURL))
out.WriteString("\">")
out.WriteString(html.EscapeString(linkText))
out.WriteString("</a>")
textStart = -1
textEnd = -1
urlStart = -1
tokenStart = i + 1
}
}
i++
}
}
func formatURL(linkURL string) string {
if strings.HasPrefix(strings.ToLower(linkURL), "javascript:") {
return ""
}
return html.EscapeString(linkURL)
}