Skip to content

Commit

Permalink
Change SummaryLength to be configurable (#3924)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bmon authored and bep committed Sep 29, 2017
1 parent 2818878 commit 8717a60
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 21 deletions.
4 changes: 4 additions & 0 deletions docs/content/getting-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ googleAnalytics: ""
# 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
Expand Down Expand Up @@ -252,6 +254,8 @@ googleAnalytics = ""
# 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

This comment has been minimized.

Copy link
@matrixik

matrixik Oct 17, 2017

should be summaryLength = 70

This comment has been minimized.

Copy link
@bep

bep Oct 17, 2017

Member

Yes, Could you create a PR here: https://github.com/gohugoio/hugoDocs

This comment has been minimized.

Copy link
@digitalcraftsman
layoutDir = "layouts"
# Enable Logging
log = false
Expand Down
29 changes: 14 additions & 15 deletions helpers/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ import (
"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-->")

Expand All @@ -47,6 +44,8 @@ type ContentSpec struct {
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
Expand All @@ -61,6 +60,7 @@ func NewContentSpec(cfg config.Provider) (*ContentSpec, error) {
blackfriday: cfg.GetStringMap("blackfriday"),
footnoteAnchorPrefix: cfg.GetString("footnoteAnchorPrefix"),
footnoteReturnLinkContents: cfg.GetString("footnoteReturnLinkContents"),
summaryLength: cfg.GetInt("summaryLength"),

cfg: cfg,
}
Expand Down Expand Up @@ -480,20 +480,20 @@ func totalWordsOld(s string) int {
}

// 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
}
Expand All @@ -507,8 +507,7 @@ func TruncateWordsByRune(words []string, max int) (string, bool) {

// 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
Expand All @@ -519,7 +518,7 @@ func TruncateWordsToWholeSentence(s string, max int) (string, bool) {
wordCount++
lastWordIndex = i

if wordCount >= max {
if wordCount >= c.summaryLength {
break
}

Expand Down Expand Up @@ -551,24 +550,24 @@ func isEndOfSentence(r rune) bool {
}

// 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 {
Expand Down
15 changes: 11 additions & 4 deletions helpers/content_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,23 @@ func TestBytesToHTML(t *testing.T) {
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
Expand All @@ -104,9 +107,11 @@ func TestTruncateWordsToWholeSentence(t *testing.T) {
{"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)
}
Expand All @@ -118,6 +123,7 @@ func TestTruncateWordsToWholeSentence(t *testing.T) {
}

func TestTruncateWordsByRune(t *testing.T) {
c := newTestContentSpec()
type test struct {
input, expected string
max int
Expand All @@ -139,7 +145,8 @@ func TestTruncateWordsByRune(t *testing.T) {
{" \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)
}
Expand Down
1 change: 1 addition & 0 deletions hugolib/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ func loadDefaultSettingsFor(v *viper.Viper) error {
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)
Expand Down
4 changes: 2 additions & 2 deletions hugolib/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,9 +677,9 @@ func (p *Page) setAutoSummary() error {
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
Expand Down

0 comments on commit 8717a60

Please sign in to comment.