Hugo: Injecting an external file into a page with syntax highlighting

2021-06-28

When I want to include code longer than a few lines in a document published with Hugo, it is sometimes convenient to keep that code in a separate file rather than to inline it with Markdown indentation or code fences.

I wrote a shortcode to read in these separate files and syntax highlight them. Drop this in layouts/shortcodes for your site or theme. See the documentation in the comment block for how to use it.

It expects that the source code file is in the same directory as the file referencing it (or in a subdirectory thereof).

{{/* "Import" source code directly into the output HTML file, with syntax highlighting
   *
   * This shortcode expects arguments:
   *     filename:   A path _relative to the calling page_
   *                 That is, in a file like content/posts/x/index.md,
   *                 referencing this shortcode with {{< import "x.py" "python3" >}}
   *                 will insert the file at content/posts/x/x.py.
   *     language:   A language supported by the Hugo syntax highlighter
   *     options:    (Optional) A list of options to pass to the Hugo syntax highlighter
   *
   * Note that you should call this shortcode with angle brackets, not %.
   * e.g., put something like this in your content:
   *     {{< importcode "filename.py" "python3" >}}
   * If you use the {{% import ... %}} style syntax,
   * it assumes the output of this shortcode is _markdown_,
   * which can contain HTML,
   * but will break for nontrivial code blocks
   * e.g. comments prefixed with '#' will be interpreted as Markdown titles.
   * See also <https://gohugo.io/content-management/shortcodes/#shortcodes-with-markdown>
   */}}
{{- $filename := (path.Join (path.Dir .Page.File.Path) (.Get 0)) -}}
{{- $language := .Get 1 -}}
{{- $options := .Get 2 -}}
{{- $fileContents := readFile $filename | safeHTML -}}
{{- highlight $fileContents $language $options -}}

It is used in this very post to show a copy of itself. I saved a copy of it as layouts/shortcodes/importcode.html in my theme, and then saved another copy of it as importcode.html.txt in the directory for this page. (Saving it without the .html extension was required, or else Hugo would interpret it as content to be processed.)

Then I just do:

{{< importcode "importcode.html.txt" "go-html-template" >}}