[Solved] Including source files from a repo in HUGO posts

I’m setting up a blog as a commented code repo, and I’m including source files right from the repos on the posts.

I’ve been able to get it to a working condition and want to improve it but I’m stuck.

What I’ve done

There’s a .gitignore’d repos dir inside the HUGO site root, that holds the source code repos.

There’s a getSourceFile.html shortcode:

{{ with .Get 0 }}
<pre><code>{{ readFile . }}</code>
<span class="source-footer">{{.}}</span>
{{ end }}

Then, in the post I can use the shortcode like so:

#### Base/EntityTypeConfiguration.cs

Estas clases permiten manejar una clase de configuración por cada clase del modelo...

{{< getSourceFile "repos/EFCoreApp/src/EFCore.App/Base/EntityTypeConfiguration.cs" >}}

and I get this:

Which is pretty nice, since I don’t have to copy and paste code, it’s 100% up to date and I am sure it compiles.

But this is where I’m stuck!

What I would like to do

  1. Setting up the repo root in the front matter so the shortcode is simpler to use, like so:

    {{< getSourceFile “src/EFCore.App/Base/EntityTypeConfiguration.cs” >}}

  2. Being able to pass the language as a parameter to the shortcode to use it in the highlighting feature, something like so (this does not work):

    {{< getSourceFile “src/EFCore.App/Base/EntityTypeConfiguration.cs” “csharp” >}}


{{ with .Get 0 }}
```{{.Get 1}}
<pre><code>{{ readFile . }}</code>
<span class="source-footer">{{.}}</span>
{{ end }}

Or better yet, infer it from the file extension! :wink:

I think it shouldn’t be too difficult but this is my first experience with Hugo, Go and the Templates, so, Could someone help me with this, please?

Thanks in advance.

As to 1), set it in site config (i.e. config.toml or similar):

repoRoot = "src/something"

Then pick up that value from the shortcode:

$root := .Site.Param "repoRoot"

Since bep already explained the first part, I try to give you the idea why your approach for point 2 did not work.

You have used with to check if the .Get 0 parameter exists. Within this block, the context (the dot - .) has just the filestring. So you try to access .Get of the string you passed in.

To do so you can either use $.Get 1 ($ goes to the global context - as far as I understood this) or prepare the variables before like:

$language := .Get 1;
{{ with .Get 0 }}
```{{- $language }}
<pre><code>{{ readFile . }}</code>
<span class="source-footer">{{.}}</span>

{{ end }}

Hmmm, I’ve been tweaking my own code block shortcode a lot lately, so these are some ideas:

First, you can set up a really flexible shortcode with .IsNamedParams to allow for human error…or at least the combination of named and positional parameters. Once you start passing in more than 1 or 2 positional parameters, I find it’s easier to just called out the named parameter instead. But with the above link to flexible shortcodes, you’ll see you can do both.

I’m doing something similar to this, so the shortcode is called as follows:

{{% code file="/here/is/a/path/to/index.html" %}}
All my fancy code stuff"
{{% /code % }}

@bep’s suggestion of setting repoRoot in the front matter makes way more sense for your use case, but I’m using the full path to the file and writing it to the page for tutorial/instructive purposes, so it changes with each code block (again, not your use case).

Then in addition to your current shortcode templating, you can do this to grab the extension:

{{$ext := index (split (.Get "file") ".") 1 }}
=> ".html" in above example

Of course, this assumes you will only have a single dot . in the named parameter. If you want more complexity, look into the replaceRE function.

From $ext you can do the following in your shortcode template; e.g., layouts/shortcodes/code.html.

Keep in mind I use client-side highlighting because I think it’s faster and more flexible:

{{$ext := index (split (.Get "file") ".") 1 }}
<pre><code class="language-{{$ext}}">Whatever you want inside of this code block...</code></pre>

Then you can eliminate the need for…


Thank you guys, merging all your suggestions I could finally get just what I was expecting, you all rock!

Reading @bep solution I realized the repo root has to be in the front matter for each post, since each post should be scoped to one repo.

@rwi answer helped me understand a little more about the {{}}.

I never got to understand why the three backticks don’t work in the shortcode, even using {{% %}} from the post, but that was addressed in @rdwatters’s

And @rdwatters’s got me on the final result, including topping and everything else. I had to use findRE because I use “dots” a lot in the folder names.

So, with a bit of further reading, this is the final shortcode:

{{ $file := .Get 0 }}
{{ $repoFilePath := printf "%s/%s" $.Page.Params.repoName $file }}
{{ $repoFile := printf "repos/%s" $repoFilePath }}
{{ $fileExt := replace (index (findRE "(\\.)\\w+$" $file) 0) "." "" }}
<pre><code class="language-{{ $fileExt }}">{{ readFile $repoFile }}</code>
<span class="source-footer">{{ $repoFilePath }}</span>

Thanks again.

1 Like

Glad it worked out. One thing I’m surprised by though is that `.Params.repoName’ is working for you without throwing an error. I believe you still need to call page params in all lowercase. Just throwing that out there in hopes it saves you from some head-scratch debugging next time :smile: Cheers.

Yeah, I was surprised too, because I understood so in the docs, so I tried all combinations and it’s working as if it where case insensitive, at least as of 0.19.


1 Like

The three backticks don’t work in the shortcode because the backticks are markdown and the shortcode is HTML.

Thanks @gaetawoo!