Compare commits
2 Commits
d1ea7b5364
...
ca939da28e
Author | SHA1 | Date | |
---|---|---|---|
ca939da28e | |||
5d0920cb6d |
79
content/blog/hugo_functions.md
Normal file
79
content/blog/hugo_functions.md
Normal file
@ -0,0 +1,79 @@
|
||||
---
|
||||
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.
|
@ -15,6 +15,10 @@ $code-color-comment: grey;
|
||||
}
|
||||
}
|
||||
|
||||
.highlight-group pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: $font-code;
|
||||
background-color: $code-color;
|
||||
|
12
themes/vanilla/layouts/partials/geturl.html
Normal file
12
themes/vanilla/layouts/partials/geturl.html
Normal file
@ -0,0 +1,12 @@
|
||||
{{ $scratch := .scratch }}
|
||||
{{ $scratch.Set "bestLength" -1 }}
|
||||
{{ $scratch.Set "bestUrl" (printf "https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/%s" .path) }}
|
||||
{{ $filePath := .path }}
|
||||
{{ range $module, $props := .submoduleLinks }}
|
||||
{{ $path := index $props "path" }}
|
||||
{{ $bestLength := $scratch.Get "bestLength" }}
|
||||
{{ if and (le $bestLength (len $path)) (hasPrefix $filePath $path) }}
|
||||
{{ $scratch.Set "bestLength" (len $path) }}
|
||||
{{ $scratch.Set "bestUrl" (printf "%s%s" (index $props "url") (strings.TrimPrefix $path $filePath)) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
3
themes/vanilla/layouts/partials/group.html
Normal file
3
themes/vanilla/layouts/partials/group.html
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="highlight-group">
|
||||
<div class="highlight-label">From <a href="{{ .url }}">{{ path.Base .path }}</a>{{ .comment }}</div>{{ .content }}
|
||||
</div>
|
1
themes/vanilla/layouts/partials/highlightgroup.html
Normal file
1
themes/vanilla/layouts/partials/highlightgroup.html
Normal file
@ -0,0 +1 @@
|
||||
{{ partial "group.html" (dict "url" .url "path" .path "comment" .comment "content" (highlight .code .language .opts)) }}
|
@ -1 +1,2 @@
|
||||
{{ highlight (readFile (printf "code/%s" (.Get 1))) (.Get 0) "" }}
|
||||
{{ partial "geturl.html" (dict "scratch" .Scratch "path" (.Get 1) "submoduleLinks" .Site.Params.submoduleLinks) }}
|
||||
{{ partial "highlightgroup.html" (dict "url" (.Scratch.Get "bestUrl") "path" (.Get 1) "comment" ", entire file" "code" (readFile (printf "code/%s" (.Get 1))) "language" (.Get 0) "opts" "linenos=table") }}
|
||||
|
@ -13,20 +13,11 @@
|
||||
{{ .Scratch.Set "opts" "" }}
|
||||
{{ end }}
|
||||
|
||||
{{ .Scratch.Set "bestLength" -1 }}
|
||||
{{ .Scratch.Set "bestUrl" (printf "https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/%s" (.Get 1)) }}
|
||||
{{ $filePath := (.Get 1) }}
|
||||
{{ $scratch := .Scratch }}
|
||||
{{ range $module, $props := .Site.Params.submoduleLinks }}
|
||||
{{ $path := index $props "path" }}
|
||||
{{ $bestLength := $scratch.Get "bestLength" }}
|
||||
{{ if and (le $bestLength (len $path)) (hasPrefix $filePath $path) }}
|
||||
{{ $scratch.Set "bestLength" (len $path) }}
|
||||
{{ $scratch.Set "bestUrl" (printf "%s%s" (index $props "url") (strings.TrimPrefix $path $filePath)) }}
|
||||
{{ partial "geturl.html" (dict "scratch" .Scratch "path" (.Get 1) "submoduleLinks" .Site.Params.submoduleLinks) }}
|
||||
|
||||
{{ if eq (.Get 2) (.Get 3) }}
|
||||
{{ .Scratch.Set "comment" (printf ", line %d" (.Get 2)) }}
|
||||
{{ else }}
|
||||
{{ .Scratch.Set "comment" (printf ", lines %d through %d" (.Get 2) (.Get 3)) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<div class="highlight-group">
|
||||
<div class="highlight-label">From <a href="{{ .Scratch.Get "bestUrl" }}">{{ path.Base (.Get 1) }}</a>,
|
||||
{{ if eq (.Get 2) (.Get 3) }}line {{ .Get 2 }}{{ else }} lines {{ .Get 2 }} through {{ .Get 3 }}{{ end }}</div>
|
||||
{{ highlight (delimit $lines "\n") (.Get 0) (printf "linenos=table,linenostart=%d%s" (.Get 2) (.Scratch.Get "opts")) }}
|
||||
</div>
|
||||
{{ partial "highlightgroup.html" (dict "url" (.Scratch.Get "bestUrl") "path" (.Get 1) "comment" (.Scratch.Get "comment") "code" (delimit $lines "\n") "language" (.Get 0) "opts" (printf "linenos=table,linenostart=%d%s" (.Get 2) (.Scratch.Get "opts"))) }}
|
||||
|
@ -1 +1,2 @@
|
||||
<pre><code>{{ readFile (printf "code/%s" (.Get 0)) }}</code></pre>
|
||||
{{ partial "geturl.html" (dict "scratch" .Scratch "path" (.Get 0) "submoduleLinks" .Site.Params.submoduleLinks) }}
|
||||
{{ partial "group.html" (dict "url" (.Scratch.Get "bestUrl") "path" (.Get 0) "comment" ", entire file" "content" (safeHTML (printf "<pre><code>%s</code></pre>" (readFile (printf "code/%s" (.Get 0)))))) }}
|
||||
|
Loading…
Reference in New Issue
Block a user