Add render template hooks for headings
authorEli W. Hunter <elihunter173@gmail.com>
Sat, 14 Mar 2020 14:43:10 +0000 (10:43 -0400)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 15 May 2020 19:12:43 +0000 (21:12 +0200)
This commit also

* Renames previous types to be non-specific. (e.g. hookedRenderer rather
  than linkRenderer)

Resolves #6713

hugolib/page.go
hugolib/page__per_output.go
hugolib/site.go
markup/converter/converter.go
markup/converter/hooks/hooks.go
markup/goldmark/render_hooks.go [new file with mode: 0644]
markup/goldmark/render_link.go [deleted file]

index fddc25fa0583f345e851ea0e83c87fd43c6e0b88..bd518c1e16daecc2adac36566e4b45f4d54a2d67 100644 (file)
@@ -375,48 +375,54 @@ func (ps *pageState) initCommonProviders(pp pagePaths) error {
        return nil
 }
 
-func (p *pageState) createRenderHooks(f output.Format) (*hooks.Render, error) {
-
+func (p *pageState) createRenderHooks(f output.Format) (*hooks.Renderers, error) {
        layoutDescriptor := p.getLayoutDescriptor()
        layoutDescriptor.RenderingHook = true
        layoutDescriptor.LayoutOverride = false
        layoutDescriptor.Layout = ""
 
+       var renderers hooks.Renderers
+
        layoutDescriptor.Kind = "render-link"
-       linkTempl, linkTemplFound, err := p.s.Tmpl().LookupLayout(layoutDescriptor, f)
+       templ, templFound, err := p.s.Tmpl().LookupLayout(layoutDescriptor, f)
        if err != nil {
                return nil, err
        }
+       if templFound {
+               renderers.LinkRenderer = hookRenderer{
+                       templateHandler: p.s.Tmpl(),
+                       Provider:        templ.(tpl.Info),
+                       templ:           templ,
+               }
+       }
 
        layoutDescriptor.Kind = "render-image"
-       imgTempl, imgTemplFound, err := p.s.Tmpl().LookupLayout(layoutDescriptor, f)
+       templ, templFound, err = p.s.Tmpl().LookupLayout(layoutDescriptor, f)
        if err != nil {
                return nil, err
        }
-
-       var linkRenderer hooks.LinkRenderer
-       var imageRenderer hooks.LinkRenderer
-
-       if linkTemplFound {
-               linkRenderer = contentLinkRenderer{
+       if templFound {
+               renderers.ImageRenderer = hookRenderer{
                        templateHandler: p.s.Tmpl(),
-                       Provider:        linkTempl.(tpl.Info),
-                       templ:           linkTempl,
+                       Provider:        templ.(tpl.Info),
+                       templ:           templ,
                }
        }
 
-       if imgTemplFound {
-               imageRenderer = contentLinkRenderer{
+       layoutDescriptor.Kind = "render-heading"
+       templ, templFound, err = p.s.Tmpl().LookupLayout(layoutDescriptor, f)
+       if err != nil {
+               return nil, err
+       }
+       if templFound {
+               renderers.HeadingRenderer = hookRenderer{
                        templateHandler: p.s.Tmpl(),
-                       Provider:        imgTempl.(tpl.Info),
-                       templ:           imgTempl,
+                       Provider:        templ.(tpl.Info),
+                       templ:           templ,
                }
        }
 
-       return &hooks.Render{
-               LinkRenderer:  linkRenderer,
-               ImageRenderer: imageRenderer,
-       }, nil
+       return &renderers, nil
 }
 
 func (p *pageState) getLayoutDescriptor() output.LayoutDescriptor {
index d7841f1788e092abfc1e4ab5ded26b09cf8eabff..77a01801d667333f891729c84b5f334ff26deffb 100644 (file)
@@ -245,7 +245,7 @@ type pageContentOutput struct {
        placeholdersEnabledInit sync.Once
 
        // May be nil.
-       renderHooks *hooks.Render
+       renderHooks *hooks.Renderers
        // Set if there are more than one output format variant
        renderHooksHaveVariants bool // TODO(bep) reimplement this in another way, consolidate with shortcodes
 
index 5688b5fac2e5b6c9164a9f3e1f7f4a2bfa440059..34671443e5172065c8681b7c7e59afe2b5559bf3 100644 (file)
@@ -1650,14 +1650,20 @@ var infoOnMissingLayout = map[string]bool{
        "404": true,
 }
 
-type contentLinkRenderer struct {
+// hookRenderer is the canonical implementation of all hooks.ITEMRenderer,
+// where ITEM is the thing being hooked.
+type hookRenderer struct {
        templateHandler tpl.TemplateHandler
        identity.Provider
        templ tpl.Template
 }
 
-func (r contentLinkRenderer) Render(w io.Writer, ctx hooks.LinkContext) error {
-       return r.templateHandler.Execute(r.templ, w, ctx)
+func (hr hookRenderer) RenderLink(w io.Writer, ctx hooks.LinkContext) error {
+       return hr.templateHandler.Execute(hr.templ, w, ctx)
+}
+
+func (hr hookRenderer) RenderHeading(w io.Writer, ctx hooks.HeadingContext) error {
+       return hr.templateHandler.Execute(hr.templ, w, ctx)
 }
 
 func (s *Site) renderForTemplate(name, outputFormat string, d interface{}, w io.Writer, templ tpl.Template) (err error) {
index 3537758269f7dfcffb4dba9d5cbb8e004784370a..df4bad95ca07a4293734d735a423437db9f31db0 100644 (file)
@@ -126,7 +126,7 @@ type DocumentContext struct {
 type RenderContext struct {
        Src         []byte
        RenderTOC   bool
-       RenderHooks *hooks.Render
+       RenderHooks *hooks.Renderers
 }
 
 var (
index 5dfb09e2d40cdd99d6334b07b7ecb7602503ad95..ab26a6f1ab14260b96e056ea23cbfd0bc39cdafb 100644 (file)
@@ -27,13 +27,41 @@ type LinkContext interface {
        PlainText() string
 }
 
-type Render struct {
-       LinkRenderer  LinkRenderer
-       ImageRenderer LinkRenderer
+type LinkRenderer interface {
+       RenderLink(w io.Writer, ctx LinkContext) error
+       identity.Provider
+}
+
+// HeadingContext contains accessors to all attributes that a HeadingRenderer
+// can use to render a heading.
+type HeadingContext interface {
+       // Page is the page containing the heading.
+       Page() interface{}
+       // Level is the level of the header (i.e. 1 for top-level, 2 for sub-level, etc.).
+       Level() int
+       // Anchor is the HTML id assigned to the heading.
+       Anchor() string
+       // Text is the rendered (HTML) heading text, excluding the heading marker.
+       Text() string
+       // PlainText is the unrendered version of Text.
+       PlainText() string
+}
+
+// HeadingRenderer describes a uniquely identifiable rendering hook.
+type HeadingRenderer interface {
+       // Render writes the renderered content to w using the data in w.
+       RenderHeading(w io.Writer, ctx HeadingContext) error
+       identity.Provider
 }
 
-func (r *Render) Eq(other interface{}) bool {
-       ro, ok := other.(*Render)
+type Renderers struct {
+       LinkRenderer    LinkRenderer
+       ImageRenderer   LinkRenderer
+       HeadingRenderer HeadingRenderer
+}
+
+func (r *Renderers) Eq(other interface{}) bool {
+       ro, ok := other.(*Renderers)
        if !ok {
                return false
        }
@@ -49,10 +77,9 @@ func (r *Render) Eq(other interface{}) bool {
                return false
        }
 
-       return true
-}
+       if r.HeadingRenderer.GetIdentity() != ro.HeadingRenderer.GetIdentity() {
+               return false
+       }
 
-type LinkRenderer interface {
-       Render(w io.Writer, ctx LinkContext) error
-       identity.Provider
+       return true
 }
diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go
new file mode 100644 (file)
index 0000000..aaae68e
--- /dev/null
@@ -0,0 +1,324 @@
+// 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 goldmark
+
+import (
+       "github.com/gohugoio/hugo/markup/converter/hooks"
+
+       "github.com/yuin/goldmark"
+       "github.com/yuin/goldmark/ast"
+       "github.com/yuin/goldmark/renderer"
+       "github.com/yuin/goldmark/renderer/html"
+       "github.com/yuin/goldmark/util"
+)
+
+var _ renderer.SetOptioner = (*hookedRenderer)(nil)
+
+func newLinkRenderer() renderer.NodeRenderer {
+       r := &hookedRenderer{
+               Config: html.Config{
+                       Writer: html.DefaultWriter,
+               },
+       }
+       return r
+}
+
+func newLinks() goldmark.Extender {
+       return &links{}
+}
+
+type linkContext struct {
+       page        interface{}
+       destination string
+       title       string
+       text        string
+       plainText   string
+}
+
+func (ctx linkContext) Destination() string {
+       return ctx.destination
+}
+
+func (ctx linkContext) Resolved() bool {
+       return false
+}
+
+func (ctx linkContext) Page() interface{} {
+       return ctx.page
+}
+
+func (ctx linkContext) Text() string {
+       return ctx.text
+}
+
+func (ctx linkContext) PlainText() string {
+       return ctx.plainText
+}
+
+func (ctx linkContext) Title() string {
+       return ctx.title
+}
+
+type headingContext struct {
+       page      interface{}
+       level     int
+       anchor    string
+       text      string
+       plainText string
+}
+
+func (ctx headingContext) Page() interface{} {
+       return ctx.page
+}
+
+func (ctx headingContext) Level() int {
+       return ctx.level
+}
+
+func (ctx headingContext) Anchor() string {
+       return ctx.anchor
+}
+
+func (ctx headingContext) Text() string {
+       return ctx.text
+}
+
+func (ctx headingContext) PlainText() string {
+       return ctx.plainText
+}
+
+type hookedRenderer struct {
+       html.Config
+}
+
+func (r *hookedRenderer) SetOption(name renderer.OptionName, value interface{}) {
+       r.Config.SetOption(name, value)
+}
+
+// RegisterFuncs implements NodeRenderer.RegisterFuncs.
+func (r *hookedRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
+       reg.Register(ast.KindLink, r.renderLink)
+       reg.Register(ast.KindImage, r.renderImage)
+       reg.Register(ast.KindHeading, r.renderHeading)
+}
+
+// https://github.com/yuin/goldmark/blob/b611cd333a492416b56aa8d94b04a67bf0096ab2/renderer/html/html.go#L404
+func (r *hookedRenderer) RenderAttributes(w util.BufWriter, node ast.Node) {
+
+       for _, attr := range node.Attributes() {
+               _, _ = w.WriteString(" ")
+               _, _ = w.Write(attr.Name)
+               _, _ = w.WriteString(`="`)
+               _, _ = w.Write(util.EscapeHTML(attr.Value.([]byte)))
+               _ = w.WriteByte('"')
+       }
+}
+
+// Fall back to the default Goldmark render funcs. Method below borrowed from:
+// https://github.com/yuin/goldmark/blob/b611cd333a492416b56aa8d94b04a67bf0096ab2/renderer/html/html.go#L404
+func (r *hookedRenderer) renderDefaultImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+       if !entering {
+               return ast.WalkContinue, nil
+       }
+       n := node.(*ast.Image)
+       _, _ = w.WriteString("<img src=\"")
+       if r.Unsafe || !html.IsDangerousURL(n.Destination) {
+               _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
+       }
+       _, _ = w.WriteString(`" alt="`)
+       _, _ = w.Write(n.Text(source))
+       _ = w.WriteByte('"')
+       if n.Title != nil {
+               _, _ = w.WriteString(` title="`)
+               r.Writer.Write(w, n.Title)
+               _ = w.WriteByte('"')
+       }
+       if r.XHTML {
+               _, _ = w.WriteString(" />")
+       } else {
+               _, _ = w.WriteString(">")
+       }
+       return ast.WalkSkipChildren, nil
+}
+
+func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+       n := node.(*ast.Image)
+       var h *hooks.Renderers
+
+       ctx, ok := w.(*renderContext)
+       if ok {
+               h = ctx.RenderContext().RenderHooks
+               ok = h != nil && h.ImageRenderer != nil
+       }
+
+       if !ok {
+               return r.renderDefaultImage(w, source, node, entering)
+       }
+
+       if entering {
+               // Store the current pos so we can capture the rendered text.
+               ctx.pos = ctx.Buffer.Len()
+               return ast.WalkContinue, nil
+       }
+
+       text := ctx.Buffer.Bytes()[ctx.pos:]
+       ctx.Buffer.Truncate(ctx.pos)
+
+       err := h.ImageRenderer.RenderLink(
+               w,
+               linkContext{
+                       page:        ctx.DocumentContext().Document,
+                       destination: string(n.Destination),
+                       title:       string(n.Title),
+                       text:        string(text),
+                       plainText:   string(n.Text(source)),
+               },
+       )
+
+       ctx.AddIdentity(h.ImageRenderer.GetIdentity())
+
+       return ast.WalkContinue, err
+
+}
+
+// Fall back to the default Goldmark render funcs. Method below borrowed from:
+// https://github.com/yuin/goldmark/blob/b611cd333a492416b56aa8d94b04a67bf0096ab2/renderer/html/html.go#L404
+func (r *hookedRenderer) renderDefaultLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+       n := node.(*ast.Link)
+       if entering {
+               _, _ = w.WriteString("<a href=\"")
+               if r.Unsafe || !html.IsDangerousURL(n.Destination) {
+                       _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
+               }
+               _ = w.WriteByte('"')
+               if n.Title != nil {
+                       _, _ = w.WriteString(` title="`)
+                       r.Writer.Write(w, n.Title)
+                       _ = w.WriteByte('"')
+               }
+               _ = w.WriteByte('>')
+       } else {
+               _, _ = w.WriteString("</a>")
+       }
+       return ast.WalkContinue, nil
+}
+
+func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+       n := node.(*ast.Link)
+       var h *hooks.Renderers
+
+       ctx, ok := w.(*renderContext)
+       if ok {
+               h = ctx.RenderContext().RenderHooks
+               ok = h != nil && h.LinkRenderer != nil
+       }
+
+       if !ok {
+               return r.renderDefaultLink(w, source, node, entering)
+       }
+
+       if entering {
+               // Store the current pos so we can capture the rendered text.
+               ctx.pos = ctx.Buffer.Len()
+               return ast.WalkContinue, nil
+       }
+
+       text := ctx.Buffer.Bytes()[ctx.pos:]
+       ctx.Buffer.Truncate(ctx.pos)
+
+       err := h.LinkRenderer.RenderLink(
+               w,
+               linkContext{
+                       page:        ctx.DocumentContext().Document,
+                       destination: string(n.Destination),
+                       title:       string(n.Title),
+                       text:        string(text),
+                       plainText:   string(n.Text(source)),
+               },
+       )
+
+       ctx.AddIdentity(h.LinkRenderer.GetIdentity())
+
+       return ast.WalkContinue, err
+}
+
+func (r *hookedRenderer) renderDefaultHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+       n := node.(*ast.Heading)
+       if entering {
+               _, _ = w.WriteString("<h")
+               _ = w.WriteByte("0123456"[n.Level])
+               if n.Attributes() != nil {
+                       r.RenderAttributes(w, node)
+               }
+               _ = w.WriteByte('>')
+       } else {
+               _, _ = w.WriteString("</h")
+               _ = w.WriteByte("0123456"[n.Level])
+               _, _ = w.WriteString(">\n")
+       }
+       return ast.WalkContinue, nil
+}
+
+func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+       n := node.(*ast.Heading)
+       var h *hooks.Renderers
+
+       ctx, ok := w.(*renderContext)
+       if ok {
+               h = ctx.RenderContext().RenderHooks
+               ok = h != nil && h.HeadingRenderer != nil
+       }
+
+       if !ok {
+               return r.renderDefaultHeading(w, source, node, entering)
+       }
+
+       if entering {
+               // Store the current pos so we can capture the rendered text.
+               ctx.pos = ctx.Buffer.Len()
+               return ast.WalkContinue, nil
+       }
+
+       text := ctx.Buffer.Bytes()[ctx.pos:]
+       ctx.Buffer.Truncate(ctx.pos)
+       // All ast.Heading nodes are guaranteed to have an attribute called "id"
+       // that is an array of bytes that encode a valid string.
+       anchori, _ := n.AttributeString("id")
+       anchor := anchori.([]byte)
+
+       err := h.HeadingRenderer.RenderHeading(
+               w,
+               headingContext{
+                       page:      ctx.DocumentContext().Document,
+                       level:     n.Level,
+                       anchor:    string(anchor),
+                       text:      string(text),
+                       plainText: string(n.Text(source)),
+               },
+       )
+
+       ctx.AddIdentity(h.HeadingRenderer.GetIdentity())
+
+       return ast.WalkContinue, err
+}
+
+type links struct {
+}
+
+// Extend implements goldmark.Extender.
+func (e *links) Extend(m goldmark.Markdown) {
+       m.Renderer().AddOptions(renderer.WithNodeRenderers(
+               util.Prioritized(newLinkRenderer(), 100),
+       ))
+}
diff --git a/markup/goldmark/render_link.go b/markup/goldmark/render_link.go
deleted file mode 100644 (file)
index c0269be..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-// 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 goldmark
-
-import (
-       "github.com/gohugoio/hugo/markup/converter/hooks"
-
-       "github.com/yuin/goldmark"
-       "github.com/yuin/goldmark/ast"
-       "github.com/yuin/goldmark/renderer"
-       "github.com/yuin/goldmark/renderer/html"
-       "github.com/yuin/goldmark/util"
-)
-
-var _ renderer.SetOptioner = (*linkRenderer)(nil)
-
-func newLinkRenderer() renderer.NodeRenderer {
-       r := &linkRenderer{
-               Config: html.Config{
-                       Writer: html.DefaultWriter,
-               },
-       }
-       return r
-}
-
-func newLinks() goldmark.Extender {
-       return &links{}
-}
-
-type linkContext struct {
-       page        interface{}
-       destination string
-       title       string
-       text        string
-       plainText   string
-}
-
-func (ctx linkContext) Destination() string {
-       return ctx.destination
-}
-
-func (ctx linkContext) Resolved() bool {
-       return false
-}
-
-func (ctx linkContext) Page() interface{} {
-       return ctx.page
-}
-
-func (ctx linkContext) Text() string {
-       return ctx.text
-}
-
-func (ctx linkContext) PlainText() string {
-       return ctx.plainText
-}
-
-func (ctx linkContext) Title() string {
-       return ctx.title
-}
-
-type linkRenderer struct {
-       html.Config
-}
-
-func (r *linkRenderer) SetOption(name renderer.OptionName, value interface{}) {
-       r.Config.SetOption(name, value)
-}
-
-// RegisterFuncs implements NodeRenderer.RegisterFuncs.
-func (r *linkRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
-       reg.Register(ast.KindLink, r.renderLink)
-       reg.Register(ast.KindImage, r.renderImage)
-}
-
-// Fall back to the default Goldmark render funcs. Method below borrowed from:
-// https://github.com/yuin/goldmark/blob/b611cd333a492416b56aa8d94b04a67bf0096ab2/renderer/html/html.go#L404
-func (r *linkRenderer) renderDefaultImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
-       if !entering {
-               return ast.WalkContinue, nil
-       }
-       n := node.(*ast.Image)
-       _, _ = w.WriteString("<img src=\"")
-       if r.Unsafe || !html.IsDangerousURL(n.Destination) {
-               _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
-       }
-       _, _ = w.WriteString(`" alt="`)
-       _, _ = w.Write(n.Text(source))
-       _ = w.WriteByte('"')
-       if n.Title != nil {
-               _, _ = w.WriteString(` title="`)
-               r.Writer.Write(w, n.Title)
-               _ = w.WriteByte('"')
-       }
-       if r.XHTML {
-               _, _ = w.WriteString(" />")
-       } else {
-               _, _ = w.WriteString(">")
-       }
-       return ast.WalkSkipChildren, nil
-}
-
-// Fall back to the default Goldmark render funcs. Method below borrowed from:
-// https://github.com/yuin/goldmark/blob/b611cd333a492416b56aa8d94b04a67bf0096ab2/renderer/html/html.go#L404
-func (r *linkRenderer) renderDefaultLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
-       n := node.(*ast.Link)
-       if entering {
-               _, _ = w.WriteString("<a href=\"")
-               if r.Unsafe || !html.IsDangerousURL(n.Destination) {
-                       _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
-               }
-               _ = w.WriteByte('"')
-               if n.Title != nil {
-                       _, _ = w.WriteString(` title="`)
-                       r.Writer.Write(w, n.Title)
-                       _ = w.WriteByte('"')
-               }
-               _ = w.WriteByte('>')
-       } else {
-               _, _ = w.WriteString("</a>")
-       }
-       return ast.WalkContinue, nil
-}
-
-func (r *linkRenderer) renderImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
-       n := node.(*ast.Image)
-       var h *hooks.Render
-
-       ctx, ok := w.(*renderContext)
-       if ok {
-               h = ctx.RenderContext().RenderHooks
-               ok = h != nil && h.ImageRenderer != nil
-       }
-
-       if !ok {
-               return r.renderDefaultImage(w, source, node, entering)
-       }
-
-       if entering {
-               // Store the current pos so we can capture the rendered text.
-               ctx.pos = ctx.Buffer.Len()
-               return ast.WalkContinue, nil
-       }
-
-       text := ctx.Buffer.Bytes()[ctx.pos:]
-       ctx.Buffer.Truncate(ctx.pos)
-
-       err := h.ImageRenderer.Render(
-               w,
-               linkContext{
-                       page:        ctx.DocumentContext().Document,
-                       destination: string(n.Destination),
-                       title:       string(n.Title),
-                       text:        string(text),
-                       plainText:   string(n.Text(source)),
-               },
-       )
-
-       ctx.AddIdentity(h.ImageRenderer.GetIdentity())
-
-       return ast.WalkContinue, err
-
-}
-
-func (r *linkRenderer) renderLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
-       n := node.(*ast.Link)
-       var h *hooks.Render
-
-       ctx, ok := w.(*renderContext)
-       if ok {
-               h = ctx.RenderContext().RenderHooks
-               ok = h != nil && h.LinkRenderer != nil
-       }
-
-       if !ok {
-               return r.renderDefaultLink(w, source, node, entering)
-       }
-
-       if entering {
-               // Store the current pos so we can capture the rendered text.
-               ctx.pos = ctx.Buffer.Len()
-               return ast.WalkContinue, nil
-       }
-
-       text := ctx.Buffer.Bytes()[ctx.pos:]
-       ctx.Buffer.Truncate(ctx.pos)
-
-       err := h.LinkRenderer.Render(
-               w,
-               linkContext{
-                       page:        ctx.DocumentContext().Document,
-                       destination: string(n.Destination),
-                       title:       string(n.Title),
-                       text:        string(text),
-                       plainText:   string(n.Text(source)),
-               },
-       )
-
-       ctx.AddIdentity(h.LinkRenderer.GetIdentity())
-
-       return ast.WalkContinue, err
-
-}
-
-type links struct {
-}
-
-// Extend implements goldmark.Extender.
-func (e *links) Extend(m goldmark.Markdown) {
-       m.Renderer().AddOptions(renderer.WithNodeRenderers(
-               util.Prioritized(newLinkRenderer(), 100),
-       ))
-}