hugolib: Fix regression of .Truncated evaluation in manual summaries

This fixes the behavior of .Truncated that was introduced with commit
bef496b97e which was later broken.  The
desired behavior is that .Truncated would evaluate to false when there
was nothing after the user defined summary marker.

This also adds a simple unit test to ensure that this feature isn't
broken again.  The check for content after the user defined summary
marker is done on the raw content instead of the working copy because
some of the markup renderers add elements after the marker, making it
difficult to determine if there is actually any content.

The behavior (evaluating to false when there is no content, just
summary) is also now documented.
This commit is contained in:
Anton Staaf 2017-01-26 11:58:25 -08:00 committed by Bjørn Erik Pedersen
parent 9416fdd334
commit 99fbc75e7a
4 changed files with 59 additions and 45 deletions

View file

@ -33,6 +33,7 @@ Alternatively, you may add the <code>&#60;&#33;&#45;&#45;more&#45;&#45;&#62;</co
Be careful to enter <code>&#60;&#33;&#45;&#45;more&#45;&#45;&#62;</code> exactly, i.e. all lowercase with no whitespace, otherwise it would be treated as regular comment and ignored. Be careful to enter <code>&#60;&#33;&#45;&#45;more&#45;&#45;&#62;</code> exactly, i.e. all lowercase with no whitespace, otherwise it would be treated as regular comment and ignored.
If there is nothing but spaces and newlines after the summary divider then `.Truncated` will be false.
## Showing Summaries ## Showing Summaries

View file

@ -14,7 +14,6 @@
package hugolib package hugolib
import ( import (
"bytes"
"fmt" "fmt"
"github.com/spf13/hugo/helpers" "github.com/spf13/hugo/helpers"
@ -127,10 +126,7 @@ func commonConvert(p *Page) HandledResult {
p.workContent = helpers.Emojify(p.workContent) p.workContent = helpers.Emojify(p.workContent)
} }
// We have to replace the <!--more--> with something that survives all the p.workContent = p.replaceDivider(p.workContent)
// rendering engines.
// TODO(bep) inline replace
p.workContent = bytes.Replace(p.workContent, []byte(helpers.SummaryDivider), internalSummaryDivider, 1)
p.workContent = p.renderContent(p.workContent) p.workContent = p.renderContent(p.workContent)
return HandledResult{err: nil} return HandledResult{err: nil}

View file

@ -414,6 +414,23 @@ var (
internalSummaryDivider = []byte("HUGOMORE42") internalSummaryDivider = []byte("HUGOMORE42")
) )
// We have to replace the <!--more--> with something that survives all the
// rendering engines.
// TODO(bep) inline replace
func (p *Page) replaceDivider(content []byte) []byte {
sections := bytes.Split(content, helpers.SummaryDivider)
// If the raw content has nothing but whitespace after the summary
// marker then the page shouldn't be marked as truncated. This check
// is simplest against the raw content because different markup engines
// (rst and asciidoc in particular) add div and p elements after the
// summary marker.
p.Truncated = (len(sections) == 2 &&
len(bytes.Trim(sections[1], " \n\r")) > 0)
return bytes.Join(sections, internalSummaryDivider)
}
// Returns the page as summary and main if a user defined split is provided. // Returns the page as summary and main if a user defined split is provided.
func (p *Page) setUserDefinedSummaryIfProvided(rawContentCopy []byte) (*summaryContent, error) { func (p *Page) setUserDefinedSummaryIfProvided(rawContentCopy []byte) (*summaryContent, error) {
@ -428,12 +445,6 @@ func (p *Page) setUserDefinedSummaryIfProvided(rawContentCopy []byte) (*summaryC
return nil, nil return nil, nil
} }
p.Truncated = true
if len(sc.content) < 20 {
// only whitespace?
p.Truncated = len(bytes.Trim(sc.content, " \n\r")) > 0
}
p.Summary = helpers.BytesToHTML(sc.summary) p.Summary = helpers.BytesToHTML(sc.summary)
return sc, nil return sc, nil
@ -441,9 +452,8 @@ func (p *Page) setUserDefinedSummaryIfProvided(rawContentCopy []byte) (*summaryC
// Make this explicit so there is no doubt about what is what. // Make this explicit so there is no doubt about what is what.
type summaryContent struct { type summaryContent struct {
summary []byte summary []byte
content []byte content []byte
contentWithoutSummary []byte
} }
func splitUserDefinedSummaryAndContent(markup string, c []byte) (sc *summaryContent, err error) { func splitUserDefinedSummaryAndContent(markup string, c []byte) (sc *summaryContent, err error) {
@ -467,7 +477,6 @@ func splitUserDefinedSummaryAndContent(markup string, c []byte) (sc *summaryCont
startMarkup []byte startMarkup []byte
endMarkup []byte endMarkup []byte
addDiv bool addDiv bool
divStart = []byte("<div class=\"document\">")
) )
switch markup { switch markup {
@ -499,20 +508,16 @@ func splitUserDefinedSummaryAndContent(markup string, c []byte) (sc *summaryCont
withoutDivider := bytes.TrimSpace(append(c[:startDivider], c[endDivider:]...)) withoutDivider := bytes.TrimSpace(append(c[:startDivider], c[endDivider:]...))
var ( var (
contentWithoutSummary []byte summary []byte
summary []byte
) )
if len(withoutDivider) > 0 { if len(withoutDivider) > 0 {
contentWithoutSummary = bytes.TrimSpace(withoutDivider[endSummary:])
summary = bytes.TrimSpace(withoutDivider[:endSummary]) summary = bytes.TrimSpace(withoutDivider[:endSummary])
} }
if addDiv { if addDiv {
// For the rst // For the rst
summary = append(append([]byte(nil), summary...), []byte("</div>")...) summary = append(append([]byte(nil), summary...), []byte("</div>")...)
// TODO(bep) include the document class, maybe
contentWithoutSummary = append(divStart, contentWithoutSummary...)
} }
if err != nil { if err != nil {
@ -520,9 +525,8 @@ func splitUserDefinedSummaryAndContent(markup string, c []byte) (sc *summaryCont
} }
sc = &summaryContent{ sc = &summaryContent{
summary: summary, summary: summary,
content: withoutDivider, content: withoutDivider,
contentWithoutSummary: contentWithoutSummary,
} }
return return

View file

@ -164,6 +164,14 @@ title: Simple
Summary Same Line<!--more--> Summary Same Line<!--more-->
Some more text Some more text
`
simplePageWithSummaryDelimiterOnlySummary = `---
title: Simple
---
Summary text
<!--more-->
` `
simplePageWithAllCJKRunes = `--- simplePageWithAllCJKRunes = `---
@ -665,46 +673,42 @@ func TestCreateNewPage(t *testing.T) {
func TestSplitSummaryAndContent(t *testing.T) { func TestSplitSummaryAndContent(t *testing.T) {
t.Parallel() t.Parallel()
for i, this := range []struct { for i, this := range []struct {
markup string markup string
content string content string
expectedSummary string expectedSummary string
expectedContent string expectedContent string
expectedContentWithoutSummary string
}{ }{
{"markdown", `<p>Summary Same LineHUGOMORE42</p> {"markdown", `<p>Summary Same LineHUGOMORE42</p>
<p>Some more text</p>`, "<p>Summary Same Line</p>", "<p>Summary Same Line</p>\n\n<p>Some more text</p>", "<p>Some more text</p>"}, <p>Some more text</p>`, "<p>Summary Same Line</p>", "<p>Summary Same Line</p>\n\n<p>Some more text</p>"},
{"asciidoc", `<div class="paragraph"><p>sn</p></div><div class="paragraph"><p>HUGOMORE42Some more text</p></div>`, {"asciidoc", `<div class="paragraph"><p>sn</p></div><div class="paragraph"><p>HUGOMORE42Some more text</p></div>`,
"<div class=\"paragraph\"><p>sn</p></div>", "<div class=\"paragraph\"><p>sn</p></div>",
"<div class=\"paragraph\"><p>sn</p></div><div class=\"paragraph\"><p>Some more text</p></div>", "<div class=\"paragraph\"><p>sn</p></div><div class=\"paragraph\"><p>Some more text</p></div>"},
"<div class=\"paragraph\"><p>Some more text</p></div>"},
{"rst", {"rst",
"<div class=\"document\"><p>Summary Next Line</p><p>HUGOMORE42Some more text</p></div>", "<div class=\"document\"><p>Summary Next Line</p><p>HUGOMORE42Some more text</p></div>",
"<div class=\"document\"><p>Summary Next Line</p></div>", "<div class=\"document\"><p>Summary Next Line</p></div>",
"<div class=\"document\"><p>Summary Next Line</p><p>Some more text</p></div>", "<div class=\"document\"><p>Summary Next Line</p><p>Some more text</p></div>"},
"<div class=\"document\"><p>Some more text</p></div>"}, {"markdown", "<p>a</p><p>b</p><p>HUGOMORE42c</p>", "<p>a</p><p>b</p>", "<p>a</p><p>b</p><p>c</p>"},
{"markdown", "<p>a</p><p>b</p><p>HUGOMORE42c</p>", "<p>a</p><p>b</p>", "<p>a</p><p>b</p><p>c</p>", "<p>c</p>"}, {"markdown", "<p>a</p><p>b</p><p>cHUGOMORE42</p>", "<p>a</p><p>b</p><p>c</p>", "<p>a</p><p>b</p><p>c</p>"},
{"markdown", "<p>a</p><p>b</p><p>cHUGOMORE42</p>", "<p>a</p><p>b</p><p>c</p>", "<p>a</p><p>b</p><p>c</p>", ""}, {"markdown", "<p>a</p><p>bHUGOMORE42</p><p>c</p>", "<p>a</p><p>b</p>", "<p>a</p><p>b</p><p>c</p>"},
{"markdown", "<p>a</p><p>bHUGOMORE42</p><p>c</p>", "<p>a</p><p>b</p>", "<p>a</p><p>b</p><p>c</p>", "<p>c</p>"}, {"markdown", "<p>aHUGOMORE42</p><p>b</p><p>c</p>", "<p>a</p>", "<p>a</p><p>b</p><p>c</p>"},
{"markdown", "<p>aHUGOMORE42</p><p>b</p><p>c</p>", "<p>a</p>", "<p>a</p><p>b</p><p>c</p>", "<p>b</p><p>c</p>"}, {"markdown", " HUGOMORE42 ", "", ""},
{"markdown", " HUGOMORE42 ", "", "", ""}, {"markdown", "HUGOMORE42", "", ""},
{"markdown", "HUGOMORE42", "", "", ""}, {"markdown", "<p>HUGOMORE42", "<p>", "<p>"},
{"markdown", "<p>HUGOMORE42", "<p>", "<p>", ""}, {"markdown", "HUGOMORE42<p>", "", "<p>"},
{"markdown", "HUGOMORE42<p>", "", "<p>", "<p>"}, {"markdown", "\n\n<p>HUGOMORE42</p>\n", "<p></p>", "<p></p>"},
{"markdown", "\n\n<p>HUGOMORE42</p>\n", "<p></p>", "<p></p>", ""},
// Issue #2586 // Issue #2586
// Note: Hugo will not split mid-sentence but will look for the closest // Note: Hugo will not split mid-sentence but will look for the closest
// paragraph end marker. This may be a change from Hugo 0.16, but it makes sense. // paragraph end marker. This may be a change from Hugo 0.16, but it makes sense.
{"markdown", `<p>this is an example HUGOMORE42of the issue.</p>`, {"markdown", `<p>this is an example HUGOMORE42of the issue.</p>`,
"<p>this is an example of the issue.</p>", "<p>this is an example of the issue.</p>",
"<p>this is an example of the issue.</p>", ""}, "<p>this is an example of the issue.</p>"},
// Issue: #2538 // Issue: #2538
{"markdown", fmt.Sprintf(` <p class="lead">%s</p>HUGOMORE42<p>%s</p> {"markdown", fmt.Sprintf(` <p class="lead">%s</p>HUGOMORE42<p>%s</p>
`, `,
strings.Repeat("A", 10), strings.Repeat("B", 31)), strings.Repeat("A", 10), strings.Repeat("B", 31)),
fmt.Sprintf(`<p class="lead">%s</p>`, strings.Repeat("A", 10)), fmt.Sprintf(`<p class="lead">%s</p>`, strings.Repeat("A", 10)),
fmt.Sprintf(`<p class="lead">%s</p><p>%s</p>`, strings.Repeat("A", 10), strings.Repeat("B", 31)), fmt.Sprintf(`<p class="lead">%s</p><p>%s</p>`, strings.Repeat("A", 10), strings.Repeat("B", 31)),
fmt.Sprintf(`<p>%s</p>`, strings.Repeat("B", 31)),
}, },
} { } {
@ -714,7 +718,6 @@ func TestSplitSummaryAndContent(t *testing.T) {
require.NotNil(t, sc, fmt.Sprintf("[%d] Nil %s", i, this.markup)) require.NotNil(t, sc, fmt.Sprintf("[%d] Nil %s", i, this.markup))
require.Equal(t, this.expectedSummary, string(sc.summary), fmt.Sprintf("[%d] Summary markup %s", i, this.markup)) require.Equal(t, this.expectedSummary, string(sc.summary), fmt.Sprintf("[%d] Summary markup %s", i, this.markup))
require.Equal(t, this.expectedContent, string(sc.content), fmt.Sprintf("[%d] Content markup %s", i, this.markup)) require.Equal(t, this.expectedContent, string(sc.content), fmt.Sprintf("[%d] Content markup %s", i, this.markup))
require.Equal(t, this.expectedContentWithoutSummary, string(sc.contentWithoutSummary), fmt.Sprintf("[%d] Content without summary, markup %s", i, this.markup))
} }
} }
@ -850,6 +853,16 @@ func TestPageWithMoreTag(t *testing.T) {
testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiterSameLine) testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiterSameLine)
} }
func TestPageWithMoreTagOnlySummary(t *testing.T) {
assertFunc := func(t *testing.T, ext string, pages Pages) {
p := pages[0]
checkTruncation(t, p, false, "page with summary delimiter at end")
}
testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiterOnlySummary)
}
func TestPageWithDate(t *testing.T) { func TestPageWithDate(t *testing.T) {
t.Parallel() t.Parallel()
cfg, fs := newTestCfg() cfg, fs := newTestCfg()