diff --git a/markup/goldmark/convert.go b/markup/goldmark/convert.go index 4c1641a0b..9442ee9e7 100644 --- a/markup/goldmark/convert.go +++ b/markup/goldmark/convert.go @@ -94,7 +94,7 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown { var ( extensions = []goldmark.Extender{ - newLinks(), + newLinks(cfg), newTocExtension(rendererOptions), } parserOptions []parser.Option diff --git a/markup/goldmark/goldmark_config/config.go b/markup/goldmark/goldmark_config/config.go index 82b8d9630..a3238091b 100644 --- a/markup/goldmark/goldmark_config/config.go +++ b/markup/goldmark/goldmark_config/config.go @@ -23,13 +23,14 @@ const ( // DefaultConfig holds the default Goldmark configuration. var Default = Config{ Extensions: Extensions{ - Typographer: true, - Footnote: true, - DefinitionList: true, - Table: true, - Strikethrough: true, - Linkify: true, - TaskList: true, + Typographer: true, + Footnote: true, + DefinitionList: true, + Table: true, + Strikethrough: true, + Linkify: true, + LinkifyProtocol: "https", + TaskList: true, }, Renderer: Renderer{ Unsafe: false, @@ -57,10 +58,11 @@ type Extensions struct { DefinitionList bool // GitHub flavored markdown - Table bool - Strikethrough bool - Linkify bool - TaskList bool + Table bool + Strikethrough bool + Linkify bool + LinkifyProtocol string + TaskList bool } type Renderer struct { diff --git a/markup/goldmark/integration_test.go b/markup/goldmark/integration_test.go index 89cd5bbb6..d8f218b31 100644 --- a/markup/goldmark/integration_test.go +++ b/markup/goldmark/integration_test.go @@ -423,3 +423,70 @@ title: "p1" "a" `) } + +func TestLinkifyProtocol(t *testing.T) { + t.Parallel() + + runTest := func(protocol string, withHook bool) *hugolib.IntegrationTestBuilder { + + files := ` +-- config.toml -- +[markup.goldmark] +[markup.goldmark.extensions] +linkify = true +linkifyProtocol = "PROTOCOL" +-- content/p1.md -- +--- +title: "p1" +--- +Link no procol: www.example.org +Link http procol: http://www.example.org +Link https procol: https://www.example.org + +-- layouts/_default/single.html -- +{{ .Content }} +` + files = strings.ReplaceAll(files, "PROTOCOL", protocol) + + if withHook { + files += `-- layouts/_default/_markup/render-link.html -- +{{ .Text | safeHTML }}` + } + + return hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + } + + for _, withHook := range []bool{false, true} { + + b := runTest("https", withHook) + + b.AssertFileContent("public/p1/index.html", + "Link no procol: www.example.org", + "Link http procol: http://www.example.org", + "Link https procol: https://www.example.org

", + ) + + b = runTest("http", withHook) + + b.AssertFileContent("public/p1/index.html", + "Link no procol: www.example.org", + "Link http procol: http://www.example.org", + "Link https procol: https://www.example.org

", + ) + + b = runTest("gopher", withHook) + + b.AssertFileContent("public/p1/index.html", + "Link no procol: www.example.org", + "Link http procol: http://www.example.org", + "Link https procol: https://www.example.org

", + ) + + } +} diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go index 138a60d26..a22030f54 100644 --- a/markup/goldmark/render_hooks.go +++ b/markup/goldmark/render_hooks.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/gohugoio/hugo/markup/converter/hooks" + "github.com/gohugoio/hugo/markup/goldmark/goldmark_config" "github.com/gohugoio/hugo/markup/goldmark/internal/render" "github.com/gohugoio/hugo/markup/internal/attributes" @@ -30,8 +31,9 @@ import ( var _ renderer.SetOptioner = (*hookedRenderer)(nil) -func newLinkRenderer() renderer.NodeRenderer { +func newLinkRenderer(cfg goldmark_config.Config) renderer.NodeRenderer { r := &hookedRenderer{ + linkifyProtocol: []byte(cfg.Extensions.LinkifyProtocol), Config: html.Config{ Writer: html.DefaultWriter, }, @@ -39,8 +41,8 @@ func newLinkRenderer() renderer.NodeRenderer { return r } -func newLinks() goldmark.Extender { - return &links{} +func newLinks(cfg goldmark_config.Config) goldmark.Extender { + return &links{cfg: cfg} } type linkContext struct { @@ -105,6 +107,7 @@ func (ctx headingContext) PlainText() string { } type hookedRenderer struct { + linkifyProtocol []byte html.Config } @@ -279,7 +282,7 @@ func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node as return r.renderAutoLinkDefault(w, source, node, entering) } - url := string(n.URL(source)) + url := string(r.autoLinkURL(n, source)) label := string(n.Label(source)) if n.AutoLinkType == ast.AutoLinkEmail && !strings.HasPrefix(strings.ToLower(url), "mailto:") { url = "mailto:" + url @@ -310,8 +313,9 @@ func (r *hookedRenderer) renderAutoLinkDefault(w util.BufWriter, source []byte, if !entering { return ast.WalkContinue, nil } + _, _ = w.WriteString(` 0 && !bytes.Equal(n.Protocol, r.linkifyProtocol) { + // The CommonMark spec says "http" is the correct protocol for links, + // but this doesn't make much sense (the fact that they should care about the rendered output). + // Note that n.Protocol is not set if protocol is provided by user. + url = append(r.linkifyProtocol, url[len(n.Protocol):]...) + } + return url +} + func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { n := node.(*ast.Heading) var hr hooks.HeadingRenderer @@ -394,11 +409,13 @@ func (r *hookedRenderer) renderHeadingDefault(w util.BufWriter, source []byte, n return ast.WalkContinue, nil } -type links struct{} +type links struct { + cfg goldmark_config.Config +} // Extend implements goldmark.Extender. func (e *links) Extend(m goldmark.Markdown) { m.Renderer().AddOptions(renderer.WithNodeRenderers( - util.Prioritized(newLinkRenderer(), 100), + util.Prioritized(newLinkRenderer(e.cfg), 100), )) }