Include code from a file with Hugo

Posted by Marcus Folkesson on Thursday, October 3, 2024

Include code from a file with Hugo

First of all, all credit goes to Marcus Olsson [1] who pretty much wrote this Hugo Shortcode [2] which I only have made some small changes to.

I also refer to his post [3] for a good explaination on how it works.

Background

I prepare for some bigger posts with a lot of code examples where I only want to show fragments of a whole file. I found this shortcut and it fits my needs perfect.

Usage

My file, examples/main.c, has the following content:

 1#include <stdio.h>
 2
 3//BEGIN foo
 4void foo()
 5{
 6	printf("foo\n");
 7}
 8//END foo
 9
10//BEGIN bar
11int bar()
12{
13	printf("foobar\n");
14	return 0;
15}
16//END bar
17
18
19// BEGIN main
20int main()
21{
22	printf("Hello world\n");
23	return 0;
24}
25// END main

It wraps each function in a BEGIN and END tag. Then

{{ < code language="C" source="examples/main.c" >}}

prints the whole file without the BEGIN & END tags:

 1#include <stdio.h>
 2
 3void foo()
 4{
 5	printf("foo\n");
 6}
 7
 8int bar()
 9{
10	printf("foobar\n");
11	return 0;
12}
13
14
15int main()
16{
17	printf("Hello world\n");
18	return 0;
19}

Or I can use the id parameter to specify which snippet to display:

{{ < code language="C" source="examples/main.c" id="foo">}}

Which gives us:

1void foo()
2{
3	printf("foo\n");
4}

The shortcut

My changes

I wanted to be able to print the whole file without the BEGIN and END tags, so I simply remove them from then output snippet. That is pretty much it.

Shortcut

To use this shortut, create a new file at layout/shortcodes/code.html with the following content:

 1{{ $language := .Get "language" }}
 2{{ $source := .Get "source" }}
 3{{ $options := .Get "options" }}
 4{{ $id := .Get "id" }}
 5
 6{{ with $source | readFile }}
 7  {{ $snippet := . }}
 8  {{ $lines := split $snippet "\n" }}
 9  {{ $startTag := printf "BEGIN %s" $id }}
10  {{ $endTag := printf "END %s" $id }}
11
12  {{ if $id }}
13    {{ $startl := -1 }}
14    {{ $endl := -1 }}
15
16    {{/* Find the lines that ends with the start and end tags. */}}
17    {{ range $index, $line := $lines }}
18      {{ if hasSuffix $line $startTag }}
19        {{ $startl = $index }}
20      {{ else if hasSuffix $line $endTag }}
21        {{ $endl = $index }}
22      {{ end }}
23    {{ end }}
24
25    {{/* Let's add some basic assertions. */}}
26    {{ if lt $startl 0 }}
27      {{ errorf "Named snippet is missing BEGIN tag" }}
28    {{ end }}
29
30    {{ if lt $endl 0 }}
31      {{ errorf "Named snippet is missing END tag" }}
32    {{ end }}
33
34    {{/* Size of the snippet in number of lines. */}}
35    {{ $snippetLen := sub (sub $endl $startl) 1 }}
36
37    {{/* Create slice with only the lines between the tags. */}}
38    {{ $includedLines := first $snippetLen (after (add $startl 1) $lines) }}
39
40    {{/* Join the lines into the final snippet. */}}
41    {{ $snippet = delimit $includedLines "\n" }}
42  {{else}}
43
44    {{ $snippet = "" }}
45    {{ range $index, $line := $lines }}
46      {{ if strings.Contains $line $startTag }}
47      {{ else if strings.Contains $line $endTag }}
48      {{ else }}
49        {{ $snippet = (print $snippet "\n"  $line) }}
50      {{ end }}
51    {{ end }}
52
53  {{end}}
54    {{ highlight (trim $snippet "\n\r") $language $options }}
55{{ end }}

And you are ready to go.