80 lines
3.9 KiB
Markdown
80 lines
3.9 KiB
Markdown
|
---
|
||
|
title: "Approximating Custom Functions in Hugo"
|
||
|
date: 2021-01-17T18:44:53-08:00
|
||
|
tags: ["Hugo"]
|
||
|
---
|
||
|
|
||
|
This will be an uncharacteristically short post. Recently,
|
||
|
I wrote about my experience with [including code from local files]({{< relref "codelines" >}}).
|
||
|
After I wrote that post, I decided to expand upon my setup. In particular,
|
||
|
I wanted to display links to the files I'm referring to, in three
|
||
|
different cases: when I'm referring to an entire code file, to an entire raw (non-highlighted)
|
||
|
file, or to a portion of a code file.
|
||
|
|
||
|
The problem was that in all three cases, I needed to determine the
|
||
|
correct file URL to link to. The process for doing so is identical: it
|
||
|
really only depends on the path to the file I'm including. However,
|
||
|
many other aspects of each case are different. In the "entire code file"
|
||
|
case, I simply need to read in a file. In the "portion of a code file"
|
||
|
case, I have to perform some processing to extract the specific lines I want to include.
|
||
|
Whenever I include a code file -- entirely or partially -- I need to invoke the `highlight`
|
||
|
function to perform syntax highlighting; however, I don't want to do that when including a raw file.
|
||
|
It would be difficult to write a single shortcode or partial to handle all of these different cases.
|
||
|
|
||
|
However, computing the target URL is a simple transformation
|
||
|
of a path and a list of submodules into a link. More plainly,
|
||
|
it is a function. Hugo doesn't really have support for
|
||
|
custom functions, at least according to this [Discourse post](https://discourse.gohugo.io/t/adding-custom-functions/14164). The only approach to add a _real_ function to Hugo is to edit the Go-based
|
||
|
source code, and recompile the thing. However, your own custom functions
|
||
|
would then not be included in normal Hugo distributions, and any websites
|
||
|
using these functions would not be portable. _Really_ adding your own functions
|
||
|
is not viable.
|
||
|
|
||
|
However, we can approximate functions using Hugo's
|
||
|
[scratchpad feature](https://gohugo.io/functions/scratch/)
|
||
|
By feeding a
|
||
|
{{< sidenote "right" "mutable-note" "scratchpad" >}}
|
||
|
In reality, any mutable container will work. The scratchpad
|
||
|
just seems like the perfect tool for this purpose.
|
||
|
{{< /sidenote >}}
|
||
|
to a partial, and expecting the partial to modify the
|
||
|
scratchpad in some way, we can effectively recover data.
|
||
|
For instance, in my `geturl` partial, I have something like
|
||
|
the following:
|
||
|
|
||
|
```
|
||
|
{{ .scratch.Set "bestUrl" theUrl }}
|
||
|
```
|
||
|
|
||
|
Once this partial executes, and the rendering engine is back to its call site,
|
||
|
the scratchpad will contain `bestUrl`. To call this partial while providing inputs
|
||
|
(like the file path, for example), we can use Hugo's `dict` function. An (abridged)
|
||
|
example:
|
||
|
|
||
|
```
|
||
|
{{ partial "geturl.html" (dict "scratch" .Scratch "path" filePath) }}
|
||
|
```
|
||
|
|
||
|
Now, from inside the partial, we'll be able to access the two variable using `.scratch` and `.path`.
|
||
|
Once we've called our partial, we simply extract the returned data from the scratchpad and use it:
|
||
|
|
||
|
```
|
||
|
{{ partial "geturl.html" (dict "scratch" .Scratch "path" filePath) }}
|
||
|
{{ $bestUrl := .Scratch.Get "bestUrl" }}
|
||
|
{{ ... do stuff with $bestUrl ... }}
|
||
|
```
|
||
|
|
||
|
Thus, although it's a little bit tedious, we're able to use `geturl` like a function,
|
||
|
thereby refraining from duplicating its definition everywhere the same logic is needed. A few
|
||
|
closing thoughts:
|
||
|
|
||
|
* __Why not just use a real language?__ It's true that I wrote a Ruby script to
|
||
|
do some of the work involved with linking submodules. However, generating the same
|
||
|
information for all calls to `codelines` would complicate the process of rendering
|
||
|
the blog, and make live preview impossible. In general, by limiting the use of external
|
||
|
scripts, it's easier to make `hugo` the only "build tool" for the site.
|
||
|
* __Is there an easier way?__ I _think_ that calls to `partial` return a string. If you
|
||
|
simply wanted to return a string, you could probably do without using a scratchpad.
|
||
|
However, if you wanted to do something more complicated (say, return a map or list),
|
||
|
you'd probably want the scratchpad method after all.
|