1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-11-27 01:34:41 +01:00
gojekyll/renderers/markdown.go

159 lines
3.6 KiB
Go
Raw Permalink Normal View History

2017-08-18 17:07:01 +02:00
package renderers
2017-07-14 18:12:25 +02:00
2017-08-11 23:16:49 +02:00
import (
"bytes"
"io"
"regexp"
2022-01-29 19:43:58 +01:00
"github.com/danog/blackfriday/v2"
2017-08-22 17:00:46 +02:00
"github.com/osteele/gojekyll/utils"
2017-08-11 23:16:49 +02:00
"golang.org/x/net/html"
)
2017-07-14 18:12:25 +02:00
const blackfridayFlags = 0 |
2021-06-04 12:13:49 +02:00
blackfriday.UseXHTML |
blackfriday.Smartypants |
blackfriday.SmartypantsFractions |
blackfriday.SmartypantsDashes |
blackfriday.SmartypantsLatexDashes
2017-07-14 18:12:25 +02:00
const blackfridayExtensions = 0 |
2021-06-04 12:13:49 +02:00
blackfriday.NoIntraEmphasis |
blackfriday.Tables |
blackfriday.FencedCode |
blackfriday.Autolink |
blackfriday.Strikethrough |
blackfriday.SpaceHeadings |
blackfriday.HeadingIDs |
blackfriday.BackslashLineBreak |
blackfriday.DefinitionLists |
2022-01-29 19:43:58 +01:00
blackfriday.NoEmptyLineBeforeBlock |
2017-07-14 18:12:25 +02:00
// added relative to commonExtensions
2021-06-04 12:13:49 +02:00
blackfriday.AutoHeadingIDs
2017-07-14 18:12:25 +02:00
2017-08-22 16:47:34 +02:00
func renderMarkdown(md []byte) ([]byte, error) {
2021-06-04 12:13:49 +02:00
params := blackfriday.HTMLRendererParameters{
Flags: blackfridayFlags,
}
renderer := blackfriday.NewHTMLRenderer(params)
html := blackfriday.Run(
2017-08-22 16:47:34 +02:00
md,
2021-06-04 12:13:49 +02:00
blackfriday.WithRenderer(renderer),
blackfriday.WithExtensions(blackfridayExtensions),
2017-08-22 16:47:34 +02:00
)
2017-08-11 23:16:49 +02:00
html, err := renderInnerMarkdown(html)
if err != nil {
2017-08-22 17:00:46 +02:00
return nil, utils.WrapError(err, "markdown")
2017-08-11 23:16:49 +02:00
}
2017-08-22 16:47:34 +02:00
return html, nil
2017-08-11 23:16:49 +02:00
}
2017-08-22 16:47:34 +02:00
func _renderMarkdown(md []byte) ([]byte, error) {
2021-06-04 12:13:49 +02:00
params := blackfriday.HTMLRendererParameters{
Flags: blackfridayFlags,
}
renderer := blackfriday.NewHTMLRenderer(params)
html := blackfriday.Run(
2017-08-22 16:47:34 +02:00
md,
2021-06-04 12:13:49 +02:00
blackfriday.WithRenderer(renderer),
blackfriday.WithExtensions(blackfridayExtensions),
2017-08-22 16:47:34 +02:00
)
return html, nil
}
// search HTML for markdown=1, and process if found
2017-08-11 23:16:49 +02:00
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:
2017-08-14 21:23:00 +02:00
if hasMarkdownAttr(z) {
2017-08-22 16:47:34 +02:00
_, err := buf.Write(stripMarkdownAttr(z.Raw()))
2017-08-11 23:16:49 +02:00
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
2017-08-22 16:47:34 +02:00
// fall through to render it
2017-08-11 23:16:49 +02:00
}
}
_, err := buf.Write(z.Raw())
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
2017-08-14 21:23:00 +02:00
func hasMarkdownAttr(z *html.Tokenizer) bool {
2017-08-11 23:16:49 +02:00
for {
k, v, more := z.TagAttr()
switch {
case string(k) == "markdown" && string(v) == "1":
return true
case !more:
return false
}
}
}
2017-08-22 16:47:34 +02:00
var markdownAttrRE = regexp.MustCompile(`\s*markdown\s*=[^\s>]*\s*`)
// return the text of a start tag, w/out the markdown attribute
func stripMarkdownAttr(tag []byte) []byte {
2017-08-14 21:23:00 +02:00
tag = markdownAttrRE.ReplaceAll(tag, []byte(" "))
tag = bytes.Replace(tag, []byte(" >"), []byte(">"), 1)
return tag
}
// Used inside markdown=1.
// TODO Instead of this approach, only count tags that match the start
// tag. For example, if <div markdown="1"> kicked off the inner markdown,
// count the div depth.
var notATagRE = regexp.MustCompile(`@|(https?|ftp):`)
2017-08-22 16:47:34 +02:00
// called once markdown="1" attribute is detected.
// Collects the HTML tokens into a string, applies markdown to them,
// and writes the result
2017-08-11 23:16:49 +02:00
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:
if !notATagRE.Match(z.Raw()) {
depth++
}
2017-08-11 23:16:49 +02:00
case html.EndTagToken:
depth--
if depth == 0 {
break loop
}
}
_, err := buf.Write(z.Raw())
if err != nil {
2017-08-22 16:47:34 +02:00
return err
2017-08-11 23:16:49 +02:00
}
}
2017-08-22 16:47:34 +02:00
html, err := _renderMarkdown(buf.Bytes())
if err != nil {
return err
}
_, err = w.Write(html)
2017-08-11 23:16:49 +02:00
return err
2017-07-14 18:12:25 +02:00
}