shithub: hugo

Download patch

ref: 8717a60cc030f4310c1779c0cdd51db37ad636cd
parent: 2818878994e906c292cbe00cb2a83f1531a21f32
author: Brendan Roy <[email protected]>
date: Fri Sep 29 13:04:55 EDT 2017

Change SummaryLength to be configurable (#3924)

Move SummaryLength into the ContentSpec struct and refactor the
relevant summary functions to be methods of ContentSpec. The new
summaryLength struct member is configurable by the summaryLength config
value, and the default remains 70. Also updates hugolib/page to use the
refactored methods.

Resolves #3734

--- a/docs/content/getting-started/configuration.md
+++ b/docs/content/getting-started/configuration.md
@@ -111,6 +111,8 @@
 # if true, auto-detect Chinese/Japanese/Korean Languages in the content. (.Summary and .WordCount can work properly in CJKLanguage)
 hasCJKLanguage:             false
 languageCode:               ""
+# the length of text to show in a .Summary
+summaryLength:              70
 layoutDir:                  "layouts"
 # Enable Logging
 log:                        false
@@ -252,6 +254,8 @@
 # if true, auto-detect Chinese/Japanese/Korean Languages in the content. (.Summary and .WordCount can work properly in CJKLanguage)
 hasCJKLanguage =              false
 languageCode =                ""
+# the length of text to show in a .Summary
+summaryLength:              70
 layoutDir =                   "layouts"
 # Enable Logging
 log =                         false
--- a/helpers/content.go
+++ b/helpers/content.go
@@ -36,9 +36,6 @@
 	"strings"
 )
 
-// SummaryLength is the length of the summary that Hugo extracts from a content.
-var SummaryLength = 70
-
 // SummaryDivider denotes where content summarization should end. The default is "<!--more-->".
 var SummaryDivider = []byte("<!--more-->")
 
@@ -47,6 +44,8 @@
 	blackfriday                map[string]interface{}
 	footnoteAnchorPrefix       string
 	footnoteReturnLinkContents string
+	// SummaryLength is the length of the summary that Hugo extracts from a content.
+	summaryLength int
 
 	Highlight            func(code, lang, optsStr string) (string, error)
 	defatultPygmentsOpts map[string]string
@@ -61,6 +60,7 @@
 		blackfriday:                cfg.GetStringMap("blackfriday"),
 		footnoteAnchorPrefix:       cfg.GetString("footnoteAnchorPrefix"),
 		footnoteReturnLinkContents: cfg.GetString("footnoteReturnLinkContents"),
+		summaryLength:              cfg.GetInt("summaryLength"),
 
 		cfg: cfg,
 	}
@@ -480,20 +480,20 @@
 }
 
 // TruncateWordsByRune truncates words by runes.
-func TruncateWordsByRune(words []string, max int) (string, bool) {
+func (c *ContentSpec) TruncateWordsByRune(words []string) (string, bool) {
 	count := 0
 	for index, word := range words {
-		if count >= max {
+		if count >= c.summaryLength {
 			return strings.Join(words[:index], " "), true
 		}
 		runeCount := utf8.RuneCountInString(word)
 		if len(word) == runeCount {
 			count++
-		} else if count+runeCount < max {
+		} else if count+runeCount < c.summaryLength {
 			count += runeCount
 		} else {
 			for ri := range word {
-				if count >= max {
+				if count >= c.summaryLength {
 					truncatedWords := append(words[:index], word[:ri])
 					return strings.Join(truncatedWords, " "), true
 				}
@@ -507,8 +507,7 @@
 
 // TruncateWordsToWholeSentence takes content and truncates to whole sentence
 // limited by max number of words. It also returns whether it is truncated.
-func TruncateWordsToWholeSentence(s string, max int) (string, bool) {
-
+func (c *ContentSpec) TruncateWordsToWholeSentence(s string) (string, bool) {
 	var (
 		wordCount     = 0
 		lastWordIndex = -1
@@ -519,7 +518,7 @@
 			wordCount++
 			lastWordIndex = i
 
-			if wordCount >= max {
+			if wordCount >= c.summaryLength {
 				break
 			}
 
@@ -551,24 +550,24 @@
 }
 
 // Kept only for benchmark.
-func truncateWordsToWholeSentenceOld(content string, max int) (string, bool) {
+func (c *ContentSpec) truncateWordsToWholeSentenceOld(content string) (string, bool) {
 	words := strings.Fields(content)
 
-	if max >= len(words) {
+	if c.summaryLength >= len(words) {
 		return strings.Join(words, " "), false
 	}
 
-	for counter, word := range words[max:] {
+	for counter, word := range words[c.summaryLength:] {
 		if strings.HasSuffix(word, ".") ||
 			strings.HasSuffix(word, "?") ||
 			strings.HasSuffix(word, ".\"") ||
 			strings.HasSuffix(word, "!") {
-			upper := max + counter + 1
+			upper := c.summaryLength + counter + 1
 			return strings.Join(words[:upper], " "), (upper < len(words))
 		}
 	}
 
-	return strings.Join(words[:max], " "), true
+	return strings.Join(words[:c.summaryLength], " "), true
 }
 
 func getAsciidocExecPath() string {
--- a/helpers/content_test.go
+++ b/helpers/content_test.go
@@ -76,20 +76,23 @@
 var benchmarkTruncateString = strings.Repeat("This is a sentence about nothing.", 20)
 
 func BenchmarkTestTruncateWordsToWholeSentence(b *testing.B) {
+	c := newTestContentSpec()
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
-		TruncateWordsToWholeSentence(benchmarkTruncateString, SummaryLength)
+		c.TruncateWordsToWholeSentence(benchmarkTruncateString)
 	}
 }
 
 func BenchmarkTestTruncateWordsToWholeSentenceOld(b *testing.B) {
+	c := newTestContentSpec()
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
-		truncateWordsToWholeSentenceOld(benchmarkTruncateString, SummaryLength)
+		c.truncateWordsToWholeSentenceOld(benchmarkTruncateString)
 	}
 }
 
 func TestTruncateWordsToWholeSentence(t *testing.T) {
+	c := newTestContentSpec()
 	type test struct {
 		input, expected string
 		max             int
@@ -104,9 +107,11 @@
 		{"To be. Or not to be. That's the question.", "To be.", 1, true},
 		{" \nThis is not a sentence\nAnd this is another", "This is not a sentence", 4, true},
 		{"", "", 10, false},
+		{"This... is a more difficult test?", "This... is a more difficult test?", 1, false},
 	}
 	for i, d := range data {
-		output, truncated := TruncateWordsToWholeSentence(d.input, d.max)
+		c.summaryLength = d.max
+		output, truncated := c.TruncateWordsToWholeSentence(d.input)
 		if d.expected != output {
 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
 		}
@@ -118,6 +123,7 @@
 }
 
 func TestTruncateWordsByRune(t *testing.T) {
+	c := newTestContentSpec()
 	type test struct {
 		input, expected string
 		max             int
@@ -139,7 +145,8 @@
 		{" \nThis is    not a sentence\n ", "This is not", 3, true},
 	}
 	for i, d := range data {
-		output, truncated := TruncateWordsByRune(strings.Fields(d.input), d.max)
+		c.summaryLength = d.max
+		output, truncated := c.TruncateWordsByRune(strings.Fields(d.input))
 		if d.expected != output {
 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
 		}
--- a/hugolib/config.go
+++ b/hugolib/config.go
@@ -135,6 +135,7 @@
 	v.SetDefault("newContentEditor", "")
 	v.SetDefault("paginate", 10)
 	v.SetDefault("paginatePath", "page")
+	v.SetDefault("summaryLength", 70)
 	v.SetDefault("blackfriday", c.NewBlackfriday())
 	v.SetDefault("rSSUri", "index.xml")
 	v.SetDefault("rssLimit", -1)
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -677,9 +677,9 @@
 	var summary string
 	var truncated bool
 	if p.isCJKLanguage {
-		summary, truncated = helpers.TruncateWordsByRune(p.PlainWords(), helpers.SummaryLength)
+		summary, truncated = p.s.ContentSpec.TruncateWordsByRune(p.PlainWords())
 	} else {
-		summary, truncated = helpers.TruncateWordsToWholeSentence(p.Plain(), helpers.SummaryLength)
+		summary, truncated = p.s.ContentSpec.TruncateWordsToWholeSentence(p.Plain())
 	}
 	p.Summary = template.HTML(summary)
 	p.Truncated = truncated