hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

convert.go (5783B)

    1 // Copyright 2019 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 // Package goldmark converts Markdown to HTML using Goldmark.
   15 package goldmark
   16 
   17 import (
   18 	"bytes"
   19 
   20 	"github.com/gohugoio/hugo/markup/goldmark/codeblocks"
   21 	"github.com/gohugoio/hugo/markup/goldmark/internal/extensions/attributes"
   22 	"github.com/gohugoio/hugo/markup/goldmark/internal/render"
   23 
   24 	"github.com/gohugoio/hugo/identity"
   25 
   26 	"github.com/gohugoio/hugo/markup/converter"
   27 	"github.com/gohugoio/hugo/markup/tableofcontents"
   28 	"github.com/yuin/goldmark"
   29 	"github.com/yuin/goldmark/extension"
   30 	"github.com/yuin/goldmark/parser"
   31 	"github.com/yuin/goldmark/renderer"
   32 	"github.com/yuin/goldmark/renderer/html"
   33 	"github.com/yuin/goldmark/text"
   34 )
   35 
   36 // Provider is the package entry point.
   37 var Provider converter.ProviderProvider = provide{}
   38 
   39 type provide struct{}
   40 
   41 func (p provide) New(cfg converter.ProviderConfig) (converter.Provider, error) {
   42 	md := newMarkdown(cfg)
   43 
   44 	return converter.NewProvider("goldmark", func(ctx converter.DocumentContext) (converter.Converter, error) {
   45 		return &goldmarkConverter{
   46 			ctx: ctx,
   47 			cfg: cfg,
   48 			md:  md,
   49 			sanitizeAnchorName: func(s string) string {
   50 				return sanitizeAnchorNameString(s, cfg.MarkupConfig.Goldmark.Parser.AutoHeadingIDType)
   51 			},
   52 		}, nil
   53 	}), nil
   54 }
   55 
   56 var _ converter.AnchorNameSanitizer = (*goldmarkConverter)(nil)
   57 
   58 type goldmarkConverter struct {
   59 	md  goldmark.Markdown
   60 	ctx converter.DocumentContext
   61 	cfg converter.ProviderConfig
   62 
   63 	sanitizeAnchorName func(s string) string
   64 }
   65 
   66 func (c *goldmarkConverter) SanitizeAnchorName(s string) string {
   67 	return c.sanitizeAnchorName(s)
   68 }
   69 
   70 func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
   71 	mcfg := pcfg.MarkupConfig
   72 	cfg := pcfg.MarkupConfig.Goldmark
   73 	var rendererOptions []renderer.Option
   74 
   75 	if cfg.Renderer.HardWraps {
   76 		rendererOptions = append(rendererOptions, html.WithHardWraps())
   77 	}
   78 
   79 	if cfg.Renderer.XHTML {
   80 		rendererOptions = append(rendererOptions, html.WithXHTML())
   81 	}
   82 
   83 	if cfg.Renderer.Unsafe {
   84 		rendererOptions = append(rendererOptions, html.WithUnsafe())
   85 	}
   86 
   87 	var (
   88 		extensions = []goldmark.Extender{
   89 			newLinks(cfg),
   90 			newTocExtension(rendererOptions),
   91 		}
   92 		parserOptions []parser.Option
   93 	)
   94 
   95 	if mcfg.Highlight.CodeFences {
   96 		extensions = append(extensions, codeblocks.New())
   97 	}
   98 
   99 	if cfg.Extensions.Table {
  100 		extensions = append(extensions, extension.Table)
  101 	}
  102 
  103 	if cfg.Extensions.Strikethrough {
  104 		extensions = append(extensions, extension.Strikethrough)
  105 	}
  106 
  107 	if cfg.Extensions.Linkify {
  108 		extensions = append(extensions, extension.Linkify)
  109 	}
  110 
  111 	if cfg.Extensions.TaskList {
  112 		extensions = append(extensions, extension.TaskList)
  113 	}
  114 
  115 	if cfg.Extensions.Typographer {
  116 		extensions = append(extensions, extension.Typographer)
  117 	}
  118 
  119 	if cfg.Extensions.DefinitionList {
  120 		extensions = append(extensions, extension.DefinitionList)
  121 	}
  122 
  123 	if cfg.Extensions.Footnote {
  124 		extensions = append(extensions, extension.Footnote)
  125 	}
  126 
  127 	if cfg.Parser.AutoHeadingID {
  128 		parserOptions = append(parserOptions, parser.WithAutoHeadingID())
  129 	}
  130 
  131 	if cfg.Parser.Attribute.Title {
  132 		parserOptions = append(parserOptions, parser.WithAttribute())
  133 	}
  134 
  135 	if cfg.Parser.Attribute.Block {
  136 		extensions = append(extensions, attributes.New())
  137 	}
  138 
  139 	md := goldmark.New(
  140 		goldmark.WithExtensions(
  141 			extensions...,
  142 		),
  143 		goldmark.WithParserOptions(
  144 			parserOptions...,
  145 		),
  146 		goldmark.WithRendererOptions(
  147 			rendererOptions...,
  148 		),
  149 	)
  150 
  151 	return md
  152 }
  153 
  154 var _ identity.IdentitiesProvider = (*converterResult)(nil)
  155 
  156 type converterResult struct {
  157 	converter.Result
  158 	toc tableofcontents.Root
  159 	ids identity.Identities
  160 }
  161 
  162 func (c converterResult) TableOfContents() tableofcontents.Root {
  163 	return c.toc
  164 }
  165 
  166 func (c converterResult) GetIdentities() identity.Identities {
  167 	return c.ids
  168 }
  169 
  170 var converterIdentity = identity.KeyValueIdentity{Key: "goldmark", Value: "converter"}
  171 
  172 func (c *goldmarkConverter) Convert(ctx converter.RenderContext) (result converter.Result, err error) {
  173 
  174 	buf := &render.BufWriter{Buffer: &bytes.Buffer{}}
  175 	result = buf
  176 	pctx := c.newParserContext(ctx)
  177 	reader := text.NewReader(ctx.Src)
  178 
  179 	doc := c.md.Parser().Parse(
  180 		reader,
  181 		parser.WithContext(pctx),
  182 	)
  183 
  184 	rcx := &render.RenderContextDataHolder{
  185 		Rctx: ctx,
  186 		Dctx: c.ctx,
  187 		IDs:  identity.NewManager(converterIdentity),
  188 	}
  189 
  190 	w := &render.Context{
  191 		BufWriter:   buf,
  192 		ContextData: rcx,
  193 	}
  194 
  195 	if err := c.md.Renderer().Render(w, ctx.Src, doc); err != nil {
  196 		return nil, err
  197 	}
  198 
  199 	return converterResult{
  200 		Result: buf,
  201 		ids:    rcx.IDs.GetIdentities(),
  202 		toc:    pctx.TableOfContents(),
  203 	}, nil
  204 }
  205 
  206 var featureSet = map[identity.Identity]bool{
  207 	converter.FeatureRenderHooks: true,
  208 }
  209 
  210 func (c *goldmarkConverter) Supports(feature identity.Identity) bool {
  211 	return featureSet[feature.GetIdentity()]
  212 }
  213 
  214 func (c *goldmarkConverter) newParserContext(rctx converter.RenderContext) *parserContext {
  215 	ctx := parser.NewContext(parser.WithIDs(newIDFactory(c.cfg.MarkupConfig.Goldmark.Parser.AutoHeadingIDType)))
  216 	ctx.Set(tocEnableKey, rctx.RenderTOC)
  217 	return &parserContext{
  218 		Context: ctx,
  219 	}
  220 }
  221 
  222 type parserContext struct {
  223 	parser.Context
  224 }
  225 
  226 func (p *parserContext) TableOfContents() tableofcontents.Root {
  227 	if v := p.Get(tocResultKey); v != nil {
  228 		return v.(tableofcontents.Root)
  229 	}
  230 	return tableofcontents.Root{}
  231 }