1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-12-12 14:57:21 +01:00
gojekyll/pipelines/markdown.go
2017-08-11 17:22:11 -04:00

117 lines
2.5 KiB
Go

package pipelines
import (
"bytes"
"io"
"regexp"
"github.com/russross/blackfriday"
"golang.org/x/net/html"
)
const blackfridayFlags = 0 |
blackfriday.HTML_USE_XHTML |
blackfriday.HTML_USE_SMARTYPANTS |
blackfriday.HTML_SMARTYPANTS_FRACTIONS |
blackfriday.HTML_SMARTYPANTS_DASHES |
blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
const blackfridayExtensions = 0 |
blackfriday.EXTENSION_NO_INTRA_EMPHASIS |
blackfriday.EXTENSION_TABLES |
blackfriday.EXTENSION_FENCED_CODE |
blackfriday.EXTENSION_AUTOLINK |
blackfriday.EXTENSION_STRIKETHROUGH |
blackfriday.EXTENSION_SPACE_HEADERS |
blackfriday.EXTENSION_HEADER_IDS |
blackfriday.EXTENSION_BACKSLASH_LINE_BREAK |
blackfriday.EXTENSION_DEFINITION_LISTS |
// added relative to commonExtensions
blackfriday.EXTENSION_AUTO_HEADER_IDS
func renderMarkdown(md []byte) []byte {
renderer := blackfriday.HtmlRenderer(blackfridayFlags, "", "")
html := blackfriday.MarkdownOptions(md, renderer, blackfriday.Options{
Extensions: blackfridayExtensions})
html, err := renderInnerMarkdown(html)
if err != nil {
panic(err)
}
return html
}
var markdownAttrRE = regexp.MustCompile(`\s*markdown\s*=\s*("1"|'1'|1)\s*`)
func renderInnerMarkdown(b []byte) ([]byte, error) {
z := html.NewTokenizer(bytes.NewReader(b))
buf := new(bytes.Buffer)
outer:
for {
tt := z.Next()
switch tt {
case html.ErrorToken:
if z.Err() == io.EOF {
break outer
}
return nil, z.Err()
case html.StartTagToken:
if hasAttr(z) {
tag := markdownAttrRE.ReplaceAll(z.Raw(), []byte(" "))
tag = bytes.Replace(tag, []byte(" >"), []byte(">"), 1)
_, err := buf.Write(tag)
if err != nil {
return nil, err
}
if err := processInnerMarkdown(buf, z); err != nil {
return nil, err
}
// the above leaves z set to the end token
}
}
_, err := buf.Write(z.Raw())
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
func hasAttr(z *html.Tokenizer) bool {
for {
k, v, more := z.TagAttr()
switch {
case string(k) == "markdown" && string(v) == "1":
return true
case !more:
return false
}
}
}
func processInnerMarkdown(w io.Writer, z *html.Tokenizer) error {
buf := new(bytes.Buffer)
depth := 1
loop:
for {
tt := z.Next()
switch tt {
case html.ErrorToken:
return z.Err()
case html.StartTagToken:
depth++
case html.EndTagToken:
depth--
if depth == 0 {
break loop
}
}
_, err := buf.Write(z.Raw())
if err != nil {
panic(err)
}
}
html := renderMarkdown(buf.Bytes())
_, err := w.Write(html)
return err
}