diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2021-07-15 08:46:54 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2021-07-15 10:14:52 +0200 |
commit | ee3d2bb1d3974584f47cde7c973fbd1ae1f512b6 (patch) | |
tree | 6ce5324b0e5f935cd1834c514495c3eb7cc5dc60 | |
parent | eb2a500367780b07d67c301ce7c866e6b67aa687 (diff) |
markup/goldmark: Support auto links in render hook
Fixes #8755
-rw-r--r-- | hugolib/content_render_hooks_test.go | 11 | ||||
-rw-r--r-- | markup/goldmark/convert_test.go | 13 | ||||
-rw-r--r-- | markup/goldmark/render_hooks.go | 70 |
3 files changed, 91 insertions, 3 deletions
diff --git a/hugolib/content_render_hooks_test.go b/hugolib/content_render_hooks_test.go index 1d7a4f8e3..f1c27d511 100644 --- a/hugolib/content_render_hooks_test.go +++ b/hugolib/content_render_hooks_test.go @@ -56,7 +56,6 @@ title: P1 b.AssertFileContent("public/p1/index.html", `Link First Link|PARTIAL1_EDITED PARTIAL2_EDITEDEND`) } - func TestRenderHooks(t *testing.T) { config := ` baseURL="https://example.org" @@ -110,6 +109,10 @@ title: Cool Page --- [First Link](https://www.google.com "Google's Homepage") +<https://foo.bar/> +https://bar.baz/ +<fake@example.com> +<mailto:fake2@example.com> {{< myshortcode3 >}} @@ -209,7 +212,11 @@ title: No Template b.Assert(int(counters.contentRenderCounter), qt.Equals, 45) b.AssertFileContent("public/blog/p1/index.html", ` -<p>Cool Page|https://www.google.com|Title: Google's Homepage|Text: First Link|END</p> +Cool Page|https://www.google.com|Title: Google's Homepage|Text: First Link|END +Cool Page|https://foo.bar/|Title: |Text: https://foo.bar/|END +Cool Page|https://bar.baz/|Title: |Text: https://bar.baz/|END +Cool Page|mailto:fake@example.com|Title: |Text: fake@example.com|END +Cool Page|mailto:fake2@example.com|Title: |Text: mailto:fake2@example.com|END Text: Second SHORT3| <p>IMAGE: Cool Page||/images/Dragster.jpg|Title: image title|Text: Drag Racing|END</p> diff --git a/markup/goldmark/convert_test.go b/markup/goldmark/convert_test.go index 2b66a1910..6e6b0009f 100644 --- a/markup/goldmark/convert_test.go +++ b/markup/goldmark/convert_test.go @@ -14,6 +14,7 @@ package goldmark import ( + "fmt" "strings" "testing" @@ -59,6 +60,10 @@ https://github.com/gohugoio/hugo/issues/6528 [Live Demo here!](https://docuapi.netlify.com/) [I'm an inline-style link with title](https://www.google.com "Google's Homepage") +<https://foo.bar/> +https://bar.baz/ +<fake@example.com> +<mailto:fake2@example.com> ## Code Fences @@ -132,8 +137,14 @@ description b := convert(c, mconf, content) got := string(b.Bytes()) + fmt.Println(got) + // Links - // c.Assert(got, qt.Contains, `<a href="https://docuapi.netlify.com/">Live Demo here!</a>`) + c.Assert(got, qt.Contains, `<a href="https://docuapi.netlify.com/">Live Demo here!</a>`) + c.Assert(got, qt.Contains, `<a href="https://foo.bar/">https://foo.bar/</a>`) + c.Assert(got, qt.Contains, `<a href="https://bar.baz/">https://bar.baz/</a>`) + c.Assert(got, qt.Contains, `<a href="mailto:fake@example.com">fake@example.com</a>`) + c.Assert(got, qt.Contains, `<a href="mailto:fake2@example.com">mailto:fake2@example.com</a></p>`) // Header IDs c.Assert(got, qt.Contains, `<h2 id="custom">Custom ID</h2>`, qt.Commentf(got)) diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go index 5e0865a70..9c159f9cf 100644 --- a/markup/goldmark/render_hooks.go +++ b/markup/goldmark/render_hooks.go @@ -15,6 +15,7 @@ package goldmark import ( "bytes" + "strings" "sync" "github.com/spf13/cast" @@ -134,6 +135,7 @@ func (r *hookedRenderer) SetOption(name renderer.OptionName, value interface{}) // RegisterFuncs implements NodeRenderer.RegisterFuncs. func (r *hookedRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { reg.Register(ast.KindLink, r.renderLink) + reg.Register(ast.KindAutoLink, r.renderAutoLink) reg.Register(ast.KindImage, r.renderImage) reg.Register(ast.KindHeading, r.renderHeading) } @@ -307,6 +309,74 @@ func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.No return ast.WalkContinue, err } +func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { + if !entering { + return ast.WalkContinue, nil + } + + n := node.(*ast.AutoLink) + var h hooks.Renderers + + ctx, ok := w.(*renderContext) + if ok { + h = ctx.RenderContext().RenderHooks + ok = h.LinkRenderer != nil + } + + if !ok { + return r.renderDefaultAutoLink(w, source, node, entering) + } + + url := string(n.URL(source)) + label := string(n.Label(source)) + if n.AutoLinkType == ast.AutoLinkEmail && !strings.HasPrefix(strings.ToLower(url), "mailto:") { + url = "mailto:" + url + } + + err := h.LinkRenderer.RenderLink( + w, + linkContext{ + page: ctx.DocumentContext().Document, + destination: url, + text: label, + plainText: label, + }, + ) + + // TODO(bep) I have a working branch that fixes these rather confusing identity types, + // but for now it's important that it's not .GetIdentity() that's added here, + // to make sure we search the entire chain on changes. + ctx.AddIdentity(h.LinkRenderer) + + return ast.WalkContinue, err +} + +// Fall back to the default Goldmark render funcs. Method below borrowed from: +// https://github.com/yuin/goldmark/blob/5588d92a56fe1642791cf4aa8e9eae8227cfeecd/renderer/html/html.go#L439 +func (r *hookedRenderer) renderDefaultAutoLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { + n := node.(*ast.AutoLink) + if !entering { + return ast.WalkContinue, nil + } + _, _ = w.WriteString(`<a href="`) + url := n.URL(source) + label := n.Label(source) + if n.AutoLinkType == ast.AutoLinkEmail && !bytes.HasPrefix(bytes.ToLower(url), []byte("mailto:")) { + _, _ = w.WriteString("mailto:") + } + _, _ = w.Write(util.EscapeHTML(util.URLEscape(url, false))) + if n.Attributes() != nil { + _ = w.WriteByte('"') + html.RenderAttributes(w, n, html.LinkAttributeFilter) + _ = w.WriteByte('>') + } else { + _, _ = w.WriteString(`">`) + } + _, _ = w.Write(util.EscapeHTML(label)) + _, _ = w.WriteString(`</a>`) + return ast.WalkContinue, nil +} + func (r *hookedRenderer) renderDefaultHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { n := node.(*ast.Heading) if entering { |