Very experimental support for mmark
authorAnthony Fok <foka@debian.org>
Fri, 30 Jan 2015 14:17:50 +0000 (07:17 -0700)
committerspf13 <steve.francia@gmail.com>
Sat, 9 May 2015 02:05:41 +0000 (22:05 -0400)
Either name the content files as `*.mmark`,
or add `markup = "mmark"` in the front matter
of your `*.md` content files.

helpers/content.go
helpers/general.go
helpers/general_test.go
hugolib/handler_page.go

index a5f2a5608c0e53548af732781fb13a0776681136..0bb50f440d56d93b054df21b0851e4507b509baf 100644 (file)
@@ -22,6 +22,7 @@ import (
        "html/template"
        "os/exec"
 
+       "github.com/miekg/mmark"
        "github.com/russross/blackfriday"
        bp "github.com/spf13/hugo/bufferpool"
        jww "github.com/spf13/jwalterweatherman"
@@ -74,6 +75,19 @@ var blackfridayExtensionMap = map[string]int{
 
 var stripHTMLReplacer = strings.NewReplacer("\n", " ", "</p>", "\n", "<br>", "\n", "<br />", "\n")
 
+var mmarkExtensionMap = map[string]int{
+       "tables":                 mmark.EXTENSION_TABLES,
+       "fencedCode":             mmark.EXTENSION_FENCED_CODE,
+       "autolink":               mmark.EXTENSION_AUTOLINK,
+       "laxHtmlBlocks":          mmark.EXTENSION_LAX_HTML_BLOCKS,
+       "spaceHeaders":           mmark.EXTENSION_SPACE_HEADERS,
+       "hardLineBreak":          mmark.EXTENSION_HARD_LINE_BREAK,
+       "footnotes":              mmark.EXTENSION_FOOTNOTES,
+       "noEmptyLineBeforeBlock": mmark.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK,
+       "headerIds":              mmark.EXTENSION_HEADER_IDS,
+       "autoHeaderIds":          mmark.EXTENSION_AUTO_HEADER_IDS,
+}
+
 // StripHTML accepts a string, strips out all HTML tags and returns it.
 func StripHTML(s string) string {
 
@@ -174,6 +188,61 @@ func markdownRenderWithTOC(ctx *RenderingContext) []byte {
                getMarkdownExtensions(ctx))
 }
 
+// mmark
+func GetMmarkHtmlRenderer(defaultFlags int, ctx *RenderingContext) mmark.Renderer {
+       renderParameters := mmark.HtmlRendererParameters{
+               FootnoteAnchorPrefix:       viper.GetString("FootnoteAnchorPrefix"),
+               FootnoteReturnLinkContents: viper.GetString("FootnoteReturnLinkContents"),
+       }
+
+       b := len(ctx.DocumentID) != 0
+
+       if b && !ctx.getConfig().PlainIDAnchors {
+               renderParameters.FootnoteAnchorPrefix = ctx.DocumentID + ":" + renderParameters.FootnoteAnchorPrefix
+               // renderParameters.HeaderIDSuffix = ":" + ctx.DocumentId
+       }
+
+       htmlFlags := defaultFlags
+       htmlFlags |= mmark.HTML_FOOTNOTE_RETURN_LINKS
+
+       return mmark.HtmlRendererWithParameters(htmlFlags, "", "", renderParameters)
+}
+
+func GetMmarkExtensions(ctx RenderingContext) int {
+       flags := 0
+       flags |= mmark.EXTENSION_TABLES
+       flags |= mmark.EXTENSION_FENCED_CODE
+       flags |= mmark.EXTENSION_AUTOLINK
+       flags |= mmark.EXTENSION_SPACE_HEADERS
+       flags |= mmark.EXTENSION_CITATION
+       flags |= mmark.EXTENSION_TITLEBLOCK_TOML
+       flags |= mmark.EXTENSION_HEADER_IDS
+       flags |= mmark.EXTENSION_AUTO_HEADER_IDS
+       flags |= mmark.EXTENSION_UNIQUE_HEADER_IDS
+       flags |= mmark.EXTENSION_FOOTNOTES
+       flags |= mmark.EXTENSION_SHORT_REF
+       flags |= mmark.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
+       flags |= mmark.EXTENSION_INCLUDE
+
+       for _, extension := range ctx.getConfig().Extensions {
+               if flag, ok := mmarkExtensionMap[extension]; ok {
+                       flags |= flag
+               }
+       }
+       return flags
+}
+
+func MmarkRender(ctx *RenderingContext) []byte {
+       return mmark.Parse(ctx.Content, GetMmarkHtmlRenderer(0, ctx),
+               GetMmarkExtensions(*ctx)).Bytes()
+}
+
+func MmarkRenderWithTOC(ctx *RenderingContext) []byte {
+       return mmark.Parse(ctx.Content,
+               GetMmarkHtmlRenderer(0, ctx),
+               GetMmarkExtensions(*ctx)).Bytes()
+}
+
 // ExtractTOC extracts Table of Contents from content.
 func ExtractTOC(content []byte) (newcontent []byte, toc []byte) {
        origContent := make([]byte, len(content))
@@ -238,6 +307,8 @@ func RenderBytesWithTOC(ctx *RenderingContext) []byte {
                return markdownRenderWithTOC(ctx)
        case "asciidoc":
                return []byte(GetAsciidocContent(ctx.Content))
+       case "mmark":
+               return MmarkRenderWithTOC(ctx)
        case "rst":
                return []byte(GetRstContent(ctx.Content))
        }
@@ -252,6 +323,8 @@ func RenderBytes(ctx *RenderingContext) []byte {
                return markdownRender(ctx)
        case "asciidoc":
                return []byte(GetAsciidocContent(ctx.Content))
+       case "mmark":
+               return MmarkRender(ctx)
        case "rst":
                return []byte(GetRstContent(ctx.Content))
        }
index dc9029b0635fa0e3df05e7fbb84cf65482c25a2d..97b1e7e83eb066d885c371b2a7954099cbb43665 100644 (file)
@@ -19,16 +19,17 @@ import (
        "encoding/hex"
        "errors"
        "fmt"
-       "github.com/spf13/cast"
-       bp "github.com/spf13/hugo/bufferpool"
-       jww "github.com/spf13/jwalterweatherman"
-       "github.com/spf13/viper"
        "io"
        "net"
        "path/filepath"
        "reflect"
        "strings"
        "sync"
+
+       "github.com/spf13/cast"
+       bp "github.com/spf13/hugo/bufferpool"
+       jww "github.com/spf13/jwalterweatherman"
+       "github.com/spf13/viper"
 )
 
 // Filepath separator defined by os.Separator.
@@ -66,6 +67,8 @@ func GuessType(in string) string {
                return "markdown"
        case "asciidoc", "adoc", "ad":
                return "asciidoc"
+       case "mmark":
+               return "mmark"
        case "rst":
                return "rst"
        case "html", "htm":
index 144f7a312f1fc2cb2235f16ce8b3f8f27272b354..0147822c77cc0879d3b01ec80afc809176ed59d0 100644 (file)
@@ -21,6 +21,7 @@ func TestGuessType(t *testing.T) {
                {"adoc", "asciidoc"},
                {"ad", "asciidoc"},
                {"rst", "rst"},
+               {"mmark", "mmark"},
                {"html", "html"},
                {"htm", "html"},
                {"excel", "unknown"},
index a29b664e94b4e391c461870a02d8835a4c170e07..24c81021fe77bb9d2b58a1a9e95f718185539b67 100644 (file)
@@ -25,6 +25,7 @@ func init() {
        RegisterHandler(new(htmlHandler))
        RegisterHandler(new(asciidocHandler))
        RegisterHandler(new(rstHandler))
+       RegisterHandler(new(mmarkHandler))
 }
 
 type basicPageHandler Handle
@@ -156,3 +157,30 @@ func (h rstHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
 
        return HandledResult{err: nil}
 }
+
+type mmarkHandler struct {
+       basicPageHandler
+}
+
+func (h mmarkHandler) Extensions() []string { return []string{"mmark"} }
+func (h mmarkHandler) PageConvert(p *Page, t tpl.Template) HandledResult {
+       p.ProcessShortcodes(t)
+
+       tmpContent, tmpTableOfContents := helpers.ExtractTOC(p.renderContent(helpers.RemoveSummaryDivider(p.rawContent)))
+
+       if len(p.contentShortCodes) > 0 {
+               tmpContentWithTokensReplaced, err := replaceShortcodeTokens(tmpContent, shortcodePlaceholderPrefix, true, p.contentShortCodes)
+
+               if err != nil {
+                       jww.FATAL.Printf("Fail to replace short code tokens in %s:\n%s", p.BaseFileName(), err.Error())
+                       return HandledResult{err: err}
+               } else {
+                       tmpContent = tmpContentWithTokensReplaced
+               }
+       }
+
+       p.Content = helpers.BytesToHTML(tmpContent)
+       p.TableOfContents = helpers.BytesToHTML(tmpTableOfContents)
+
+       return HandledResult{err: nil}
+}