diff options
Diffstat (limited to 'markup/goldmark/links')
-rw-r--r-- | markup/goldmark/links/integration_test.go | 113 | ||||
-rw-r--r-- | markup/goldmark/links/transform.go | 73 |
2 files changed, 186 insertions, 0 deletions
diff --git a/markup/goldmark/links/integration_test.go b/markup/goldmark/links/integration_test.go new file mode 100644 index 000000000..20d4d74b1 --- /dev/null +++ b/markup/goldmark/links/integration_test.go @@ -0,0 +1,113 @@ +package images_test + +import ( + "strings" + "testing" + + "github.com/gohugoio/hugo/hugolib" +) + +func TestDisableWrapStandAloneImageWithinParagraph(t *testing.T) { + t.Parallel() + + filesTemplate := ` +-- config.toml -- +[markup.goldmark.renderer] + unsafe = false +[markup.goldmark.parser] +wrapStandAloneImageWithinParagraph = CONFIG_VALUE +[markup.goldmark.parser.attribute] + block = true + title = true +-- content/p1.md -- +--- +title: "p1" +--- + +This is an inline image: ![Inline Image](/inline.jpg). Some more text. + +![Block Image](/block.jpg) +{.b} + + +-- layouts/_default/single.html -- +{{ .Content }} +` + + t.Run("With Hook, no wrap", func(t *testing.T) { + files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "false") + files = files + `-- layouts/_default/_markup/render-image.html -- +{{ if .IsBlock }} +<figure class="{{ .Attributes.class }}"> + <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}|{{ .Ordinal }}" /> +</figure> +{{ else }} + <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}|{{ .Ordinal }}" /> +{{ end }} +` + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", + "This is an inline image: \n\t<img src=\"/inline.jpg\" alt=\"Inline Image|0\" />\n. Some more text.</p>", + "<figure class=\"b\">\n\t<img src=\"/block.jpg\" alt=\"Block Image|1\" />", + ) + }) + + t.Run("With Hook, wrap", func(t *testing.T) { + files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "true") + files = files + `-- layouts/_default/_markup/render-image.html -- +{{ if .IsBlock }} +<figure class="{{ .Attributes.class }}"> + <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" /> +</figure> +{{ else }} + <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" /> +{{ end }} +` + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", + "This is an inline image: \n\t<img src=\"/inline.jpg\" alt=\"Inline Image\" />\n. Some more text.</p>", + "<p class=\"b\">\n\t<img src=\"/block.jpg\" alt=\"Block Image\" />\n</p>", + ) + }) + + t.Run("No Hook, no wrap", func(t *testing.T) { + files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "false") + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "<p>This is an inline image: <img src=\"/inline.jpg\" alt=\"Inline Image\">. Some more text.</p>\n<img src=\"/block.jpg\" alt=\"Block Image\" class=\"b\">") + }) + + t.Run("No Hook, wrap", func(t *testing.T) { + files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "true") + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "<p class=\"b\"><img src=\"/block.jpg\" alt=\"Block Image\"></p>") + }) + +} diff --git a/markup/goldmark/links/transform.go b/markup/goldmark/links/transform.go new file mode 100644 index 000000000..b4f6b6dc5 --- /dev/null +++ b/markup/goldmark/links/transform.go @@ -0,0 +1,73 @@ +package images + +import ( + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" +) + +type ( + linksExtension struct { + wrapStandAloneImageWithinParagraph bool + } +) + +const ( + // Used to signal to the rendering step that an image is used in a block context. + // Dont's change this; the prefix must match the internalAttrPrefix in the root goldmark package. + AttrIsBlock = "_h__isBlock" +) + +func New(wrapStandAloneImageWithinParagraph bool) goldmark.Extender { + return &linksExtension{wrapStandAloneImageWithinParagraph: wrapStandAloneImageWithinParagraph} +} + +func (e *linksExtension) Extend(m goldmark.Markdown) { + m.Parser().AddOptions( + parser.WithASTTransformers( + util.Prioritized(&Transformer{wrapStandAloneImageWithinParagraph: e.wrapStandAloneImageWithinParagraph}, 300), + ), + ) +} + +type Transformer struct { + wrapStandAloneImageWithinParagraph bool +} + +// Transform transforms the provided Markdown AST. +func (t *Transformer) Transform(doc *ast.Document, reader text.Reader, pctx parser.Context) { + ast.Walk(doc, func(node ast.Node, enter bool) (ast.WalkStatus, error) { + if !enter { + return ast.WalkContinue, nil + } + + if n, ok := node.(*ast.Image); ok { + parent := n.Parent() + + if !t.wrapStandAloneImageWithinParagraph { + isBlock := parent.ChildCount() == 1 + if isBlock { + n.SetAttributeString(AttrIsBlock, true) + } + + if isBlock && parent.Kind() == ast.KindParagraph { + for _, attr := range parent.Attributes() { + // Transfer any attribute set down to the image. + // Image elements does not support attributes on its own, + // so it's safe to just set without checking first. + n.SetAttribute(attr.Name, attr.Value) + } + grandParent := parent.Parent() + grandParent.ReplaceChild(grandParent, parent, n) + } + } + + } + + return ast.WalkContinue, nil + + }) + +} |