Repository: gohugoio/hugo (language: go)

Issue:
`inline` option of RenderString does not work for AsciiDoc
<!--
Please do not use the issue queue for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the forum:

https://discourse.gohugo.io
-->

<!-- Please answer these questions before submitting your issue. Thanks! -->

### What version of Hugo are you using (`hugo version`)?

<pre>
$ hugo version
hugo v0.124.1-db083b05f16c945fec04f745f0ca8640560cf1ec windows/amd64 BuildDate=2024-03-20T11:40:10Z VendorInfo=gohugoio
</pre>

### Does this issue reproduce with the latest release?

Yes.

### Issue

`RenderString` with `display = 'inline'` option does not work fine for AsciiDoc. 

This may because Asciidoctor generates text surrounded by `<div class="paragraph"><p> ... </p></div>`, and `display = 'inline'` removes only `<p>` tags.

See the releating issue I encountered: https://github.com/McShelby/hugo-theme-relearn/issues/829


Discussion / hints:

Fix patch:
diff --git a/helpers/content.go b/helpers/content.go
index 889294382ce..be79ad54083 100644
--- a/helpers/content.go
+++ b/helpers/content.go
@@ -36,11 +36,6 @@ import (
 	"github.com/gohugoio/hugo/config"
 )
 
-var (
-	openingPTag = []byte("<p>")
-	closingPTag = []byte("</p>")
-)
-
 // ContentSpec provides functionality to render markdown content.
 type ContentSpec struct {
 	Converters          markup.ConverterProvider
@@ -242,19 +237,26 @@ func (c *ContentSpec) TruncateWordsToWholeSentence(s string) (string, bool) {
 	return strings.TrimSpace(s[:endIndex]), endIndex < len(s)
 }
 
-// TrimShortHTML removes the <p>/</p> tags from HTML input in the situation
-// where said tags are the only <p> tags in the input and enclose the content
-// of the input (whitespace excluded).
-func (c *ContentSpec) TrimShortHTML(input []byte) []byte {
-	if bytes.Count(input, openingPTag) == 1 {
+// TrimShortHTML removes the outer tags from HTML input where (a) the opening
+// tag is present only once with the input, and (b) the opening and closing
+// tags wrap the input after white space removal.
+func (c *ContentSpec) TrimShortHTML(input []byte, markup string) []byte {
+	openingTag := []byte("<p>")
+	closingTag := []byte("</p>")
+
+	if markup == "asciidocext" {
+		openingTag = []byte("<div class=\"paragraph\">\n<p>")
+		closingTag = []byte("</p>\n</div>")
+	}
+
+	if bytes.Count(input, openingTag) == 1 {
 		input = bytes.TrimSpace(input)
-		if bytes.HasPrefix(input, openingPTag) && bytes.HasSuffix(input, closingPTag) {
-			input = bytes.TrimPrefix(input, openingPTag)
-			input = bytes.TrimSuffix(input, closingPTag)
+		if bytes.HasPrefix(input, openingTag) && bytes.HasSuffix(input, closingTag) {
+			input = bytes.TrimPrefix(input, openingTag)
+			input = bytes.TrimSuffix(input, closingTag)
 			input = bytes.TrimSpace(input)
 		}
 	}
-
 	return input
 }
 
diff --git a/hugolib/page__content.go b/hugolib/page__content.go
index 54f7be9618a..799fc89b675 100644
--- a/hugolib/page__content.go
+++ b/hugolib/page__content.go
@@ -778,7 +778,7 @@ func (c *cachedContent) contentPlain(ctx context.Context, cp *pageContentOutput)
 			if err != nil {
 				return nil, err
 			}
-			html := cp.po.p.s.ContentSpec.TrimShortHTML(b.Bytes())
+			html := cp.po.p.s.ContentSpec.TrimShortHTML(b.Bytes(), cp.po.p.m.pageConfig.Markup)
 			result.summary = helpers.BytesToHTML(html)
 		} else {
 			var summary string
diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go
index 7220c86fe43..fac719ea959 100644
--- a/hugolib/page__per_output.go
+++ b/hugolib/page__per_output.go
@@ -363,9 +363,11 @@ func (pco *pageContentOutput) RenderString(ctx context.Context, args ...any) (te
 	}
 
 	if opts.Display == "inline" {
-		// We may have to rethink this in the future when we get other
-		// renderers.
-		rendered = pco.po.p.s.ContentSpec.TrimShortHTML(rendered)
+		markup := pco.po.p.m.pageConfig.Markup
+		if opts.Markup != "" {
+			markup = pco.po.p.s.ContentSpec.ResolveMarkup(opts.Markup)
+		}
+		rendered = pco.po.p.s.ContentSpec.TrimShortHTML(rendered, markup)
 	}
 
 	return template.HTML(string(rendered)), nil
diff --git a/tpl/transform/transform.go b/tpl/transform/transform.go
index 7054c6988f2..5ef9bff218d 100644
--- a/tpl/transform/transform.go
+++ b/tpl/transform/transform.go
@@ -167,7 +167,7 @@ func (ns *Namespace) Markdownify(ctx context.Context, s any) (template.HTML, err
 	}
 
 	// Strip if this is a short inline type of text.
-	bb := ns.deps.ContentSpec.TrimShortHTML([]byte(ss))
+	bb := ns.deps.ContentSpec.TrimShortHTML([]byte(ss), "markdown")
 
 	return helpers.BytesToHTML(bb), nil
 }


Test patch:
diff --git a/helpers/content_test.go b/helpers/content_test.go
index 53e18e727e9..f1cbfad04c7 100644
--- a/helpers/content_test.go
+++ b/helpers/content_test.go
@@ -26,24 +26,27 @@ import (
 
 func TestTrimShortHTML(t *testing.T) {
 	tests := []struct {
-		input, output []byte
+		markup string
+		input  []byte
+		output []byte
 	}{
-		{[]byte(""), []byte("")},
-		{[]byte("Plain text"), []byte("Plain text")},
-		// This seems wrong. Why touch it if it doesn't have p tag?
-		// {[]byte("  \t\n Whitespace text\n\n"), []byte("Whitespace text")},
-		{[]byte("<p>Simple paragraph</p>"), []byte("Simple paragraph")},
-		{[]byte("\n  \n \t  <p> \t Whitespace\nHTML  \n\t </p>\n\t"), []byte("Whitespace\nHTML")},
-		{[]byte("<p>Multiple</p><p>paragraphs</p>"), []byte("<p>Multiple</p><p>paragraphs</p>")},
-		{[]byte("<p>Nested<p>paragraphs</p></p>"), []byte("<p>Nested<p>paragraphs</p></p>")},
-		{[]byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>"), []byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>")},
-		// Issue #11698
-		{[]byte("<h2 id=`a`>b</h2>\n\n<p>c</p>"), []byte("<h2 id=`a`>b</h2>\n\n<p>c</p>")},
+		{"markdown", []byte(""), []byte("")},
+		{"markdown", []byte("Plain text"), []byte("Plain text")},
+		{"markdown", []byte("<p>Simple paragraph</p>"), []byte("Simple paragraph")},
+		{"markdown", []byte("\n  \n \t  <p> \t Whitespace\nHTML  \n\t </p>\n\t"), []byte("Whitespace\nHTML")},
+		{"markdown", []byte("<p>Multiple</p><p>paragraphs</p>"), []byte("<p>Multiple</p><p>paragraphs</p>")},
+		{"markdown", []byte("<p>Nested<p>paragraphs</p></p>"), []byte("<p>Nested<p>paragraphs</p></p>")},
+		{"markdown", []byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>"), []byte("<p>Hello</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n</ul>")},
+		// Issue 11698
+		{"markdown", []byte("<h2 id=`a`>b</h2>\n\n<p>c</p>"), []byte("<h2 id=`a`>b</h2>\n\n<p>c</p>")},
+		// Issue 12369
+		{"markdown", []byte("<div class=\"paragraph\">\n<p>foo</p>\n</div>"), []byte("<div class=\"paragraph\">\n<p>foo</p>\n</div>")},
+		{"asciidocext", []byte("<div class=\"paragraph\">\n<p>foo</p>\n</div>"), []byte("foo")},
 	}
 
 	c := newTestContentSpec(nil)
 	for i, test := range tests {
-		output := c.TrimShortHTML(test.input)
+		output := c.TrimShortHTML(test.input, test.markup)
 		if !bytes.Equal(test.output, output) {
 			t.Errorf("Test %d failed. Expected %q got %q", i, test.output, output)
 		}
@@ -54,7 +57,7 @@ func BenchmarkTrimShortHTML(b *testing.B) {
 	c := newTestContentSpec(nil)
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
-		c.TrimShortHTML([]byte("<p>Simple paragraph</p>"))
+		c.TrimShortHTML([]byte("<p>Simple paragraph</p>"), "markdown")
 	}
 }