Posted on under Hugo

Collected under Programmings

While I was first designing this website I knew I wanted to have the ability to display excerpts from various posts/writings. There isn’t any built-in functionality for doing this, but Hugo does contain all of the necessary tools to build this feature yourself.

An Example

...and sanguine familiarity. Did you see? All my life's a fair, My confusion the crowd's bustle, My anxiety the blinking lights, and you? You're the candy, the music, the thrills, the playful laughter, the song and dance, the view on high a stopped Ferris Wheel like time frozen in a bead of amber and adulation. And you, if... And You

This excerpt is being generated with a simple shortcode:

{{< excerpt Section writings 50 >}}

The contents of the shortcode template can be seen below:

{{ $seed := now.Unix }}
{{ $post := index (shuffle (where .Site.RegularPages (.Get 0) (.Get 1) )) 0 }}
{{ $words := split ($post.RawContent) " " }}
{{ $start := mod (add (mul 13 $seed) 97) (len $words) }}
<blockquote>
    ...{{ delimit (first (.Get 2) ( after $start ($words))) " " }}...
    <cite><a href="{{ $post.RelPermalink }}">{{ $post.Title }}</a></cite>
</blockquote>

Usage

The first two parameters (“Section” and “writings”) tell the shortcode what content to look for and are passed to Hugo’s where function using (.get 0) and (.get 1):

{{ $post := index (shuffle (where .Site.RegularPages (.Get 0) (.Get 1) )) 0 }}

The rest of this line is shuffling the results of the query and then pulls out the very first result. In the above example this means that we’re pulling a random post from anything in the writings section, but we can also grab a specific post by being more specific:

{{< excerpt Title "Why I Don't Eat Raisins" 25 >}}
...a young child. I was probably 10 or 12 or something, but it’s really not all that important. I had always liked raisins up... Why I Don't Eat Raisins

The very last parameter, (.get 2), is the number of words to display. I decided to go with words instead of characters to promote better readability of the output.

How the Excerpt is Generated

The important part is figuring out how to only display a discreet portion of the post. Hugo has a shuffle function for randomizing an array but this isn’t quite what we need. What we really need is to pick a random spot in the text and then pull out the required number of words after.

First, we’ll split the post’s content into words. This is done by delimiting by an empty space " ":

{{ $words := split ($post.RawContent) " " }}

Technically, Hugo doesn’t include any functions for random number generation. But it does have competent math functions which we can use to generate random numbers.

Pseudorandom number generation typically uses a timestamp as a seed:

{{ $seed := now.Unix }}

Then we need to pick a random spot to start our excerpt:

{{ $start := mod (add (mul 13 $seed) 97) (len $words) }}

Don’t worry too much about the specifics of the math going on here, this is a pretty basic pseudorandom number generator. The important part is (len $words). This is the total number of words found in the post’s content. We’re using this to define the upper bounds of our random number as we don’t want to accidentally go past the end of the post.

Finally, we extract the words we want and rebuild our text:

{{ delimit (first (.Get 2) ( after $start ($words))) " " }}

This might look a little confusing as there’s some nesting going on performing multiple actions. We’re extracting our excerpt with first (.Get 2) ( after $start ($words)). Remember that (.get 2) refers to our word count specified in the calling shortcode. So if we specified 25, this line is saying “get the first 25 words after $start”, where $start is the random number generated previously. Hugo doesn’t mind if our length happens to exceed the number of remaining words.

Because this returns an array of words we need to put them all back together into a text string. We can do that with delimit which performs the exact opposite operation of our earlier usage of split, once again using " " as a delimiter.

Some Caveats

You may have noticed that all of the examples I posted here are from pieces of prose. The reason is that there’s nothing in the shortcode that guards the output of the text. So, if we pull an excerpt from a post that contains other shortcodes, we might accidentally render an additional shortcode inside our excerpt. Additionally, pulling a random excerpt from a piece filled with programming code will look quite ugly as none of the code formatting will be displayed in the excerpt.

Latest commit: 2022926 Fix typo in article description

comments powered by Disqus