diff options
Diffstat (limited to 'markup/mmark')
-rw-r--r-- | markup/mmark/convert.go | 143 | ||||
-rw-r--r-- | markup/mmark/convert_test.go | 77 | ||||
-rw-r--r-- | markup/mmark/renderer.go | 44 |
3 files changed, 264 insertions, 0 deletions
diff --git a/markup/mmark/convert.go b/markup/mmark/convert.go new file mode 100644 index 000000000..a0da346c1 --- /dev/null +++ b/markup/mmark/convert.go @@ -0,0 +1,143 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package mmark converts Markdown to HTML using MMark v1. +package mmark + +import ( + "github.com/gohugoio/hugo/markup/internal" + + "github.com/gohugoio/hugo/markup/converter" + "github.com/miekg/mmark" +) + +// Provider is the package entry point. +var Provider converter.NewProvider = provider{} + +type provider struct { +} + +func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error) { + defaultBlackFriday, err := internal.NewBlackfriday(cfg) + if err != nil { + return nil, err + } + + defaultExtensions := getMmarkExtensions(defaultBlackFriday) + + var n converter.NewConverter = func(ctx converter.DocumentContext) (converter.Converter, error) { + b := defaultBlackFriday + extensions := defaultExtensions + + if ctx.ConfigOverrides != nil { + var err error + b, err = internal.UpdateBlackFriday(b, ctx.ConfigOverrides) + if err != nil { + return nil, err + } + extensions = getMmarkExtensions(b) + } + + return &mmarkConverter{ + ctx: ctx, + b: b, + extensions: extensions, + cfg: cfg, + }, nil + } + + return n, nil + +} + +type mmarkConverter struct { + ctx converter.DocumentContext + extensions int + b *internal.BlackFriday + cfg converter.ProviderConfig +} + +func (c *mmarkConverter) Convert(ctx converter.RenderContext) (converter.Result, error) { + r := getHTMLRenderer(c.ctx, c.b, c.cfg) + return mmark.Parse(ctx.Src, r, c.extensions), nil +} + +func getHTMLRenderer( + ctx converter.DocumentContext, + cfg *internal.BlackFriday, + pcfg converter.ProviderConfig) mmark.Renderer { + + var ( + flags int + documentID string + ) + + documentID = ctx.DocumentID + + renderParameters := mmark.HtmlRendererParameters{ + FootnoteAnchorPrefix: cfg.FootnoteAnchorPrefix, + FootnoteReturnLinkContents: cfg.FootnoteReturnLinkContents, + } + + if documentID != "" && !cfg.PlainIDAnchors { + renderParameters.FootnoteAnchorPrefix = documentID + ":" + renderParameters.FootnoteAnchorPrefix + } + + htmlFlags := flags + htmlFlags |= mmark.HTML_FOOTNOTE_RETURN_LINKS + + return &mmarkRenderer{ + Config: cfg, + Cfg: pcfg.Cfg, + highlight: pcfg.Highlight, + Renderer: mmark.HtmlRendererWithParameters(htmlFlags, "", "", renderParameters), + } + +} + +func getMmarkExtensions(cfg *internal.BlackFriday) 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 cfg.Extensions { + if flag, ok := mmarkExtensionMap[extension]; ok { + flags |= flag + } + } + return flags +} + +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, +} diff --git a/markup/mmark/convert_test.go b/markup/mmark/convert_test.go new file mode 100644 index 000000000..d015ee94c --- /dev/null +++ b/markup/mmark/convert_test.go @@ -0,0 +1,77 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mmark + +import ( + "testing" + + "github.com/spf13/viper" + + "github.com/gohugoio/hugo/common/loggers" + + "github.com/miekg/mmark" + + "github.com/gohugoio/hugo/markup/internal" + + "github.com/gohugoio/hugo/markup/converter" + + qt "github.com/frankban/quicktest" +) + +func TestGetMmarkExtensions(t *testing.T) { + c := qt.New(t) + b, err := internal.NewBlackfriday(converter.ProviderConfig{Cfg: viper.New()}) + c.Assert(err, qt.IsNil) + + //TODO: This is doing the same just with different marks... + type data struct { + testFlag int + } + + b.Extensions = []string{"tables"} + b.ExtensionsMask = []string{""} + allExtensions := []data{ + {mmark.EXTENSION_TABLES}, + {mmark.EXTENSION_FENCED_CODE}, + {mmark.EXTENSION_AUTOLINK}, + {mmark.EXTENSION_SPACE_HEADERS}, + {mmark.EXTENSION_CITATION}, + {mmark.EXTENSION_TITLEBLOCK_TOML}, + {mmark.EXTENSION_HEADER_IDS}, + {mmark.EXTENSION_AUTO_HEADER_IDS}, + {mmark.EXTENSION_UNIQUE_HEADER_IDS}, + {mmark.EXTENSION_FOOTNOTES}, + {mmark.EXTENSION_SHORT_REF}, + {mmark.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK}, + {mmark.EXTENSION_INCLUDE}, + } + + actualFlags := getMmarkExtensions(b) + for _, e := range allExtensions { + if actualFlags&e.testFlag != e.testFlag { + t.Errorf("Flag %v was not found in the list of extensions.", e) + } + } +} + +func TestConvert(t *testing.T) { + c := qt.New(t) + p, err := Provider.New(converter.ProviderConfig{Cfg: viper.New(), Logger: loggers.NewErrorLogger()}) + c.Assert(err, qt.IsNil) + conv, err := p.New(converter.DocumentContext{}) + c.Assert(err, qt.IsNil) + b, err := conv.Convert(converter.RenderContext{Src: []byte("testContent")}) + c.Assert(err, qt.IsNil) + c.Assert(string(b.Bytes()), qt.Equals, "<p>testContent</p>\n") +} diff --git a/markup/mmark/renderer.go b/markup/mmark/renderer.go new file mode 100644 index 000000000..07fe71c95 --- /dev/null +++ b/markup/mmark/renderer.go @@ -0,0 +1,44 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mmark + +import ( + "bytes" + "strings" + + "github.com/miekg/mmark" + + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/markup/internal" +) + +// hugoHTMLRenderer wraps a blackfriday.Renderer, typically a blackfriday.Html +// adding some custom behaviour. +type mmarkRenderer struct { + Cfg config.Provider + Config *internal.BlackFriday + highlight func(code, lang, optsStr string) (string, error) + mmark.Renderer +} + +// BlockCode renders a given text as a block of code. +func (r *mmarkRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string, caption []byte, subfigure bool, callouts bool) { + if r.Cfg.GetBool("pygmentsCodeFences") && (lang != "" || r.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")) { + str := strings.Trim(string(text), "\n\r") + highlighted, _ := r.highlight(str, lang, "") + out.WriteString(highlighted) + } else { + r.Renderer.BlockCode(out, text, lang, caption, subfigure, callouts) + } +} |