Change language in HighlightCodeBlock

I’ve created a Hugo theme and have written a Markdown render hook for codeblocks. I’d like to have line highlighting (e.g. {hl_lines="3 6"}) even for languages that are not supported by Chroma.

In my render hook I’m using transform.HighlightCodeBlock like so:

{{- $lang := .Type -}}
{{- if transform.CanHighlight $lang -}}
  {{- $opts := dict "noClasses" false -}}
  {{- $highlightingResult := transform.HighlightCodeBlock . $opts -}}
  <pre><code class="chroma" data-lang="{{ $lang }}">{{ $highlightingResult.Inner }}</code></pre>
{{- else -}}
  <pre><code class="bg" data-lang="{{ $lang }}">{{ .Inner }}</code></pre>
{{ end }}

So, I’d figure to use transform.HighlightCodeBlock even in the else branch but would need to overwrite .Type with the value text.

What I’ve already tried:

  • Set .Type: not possible(?)
  • Create a custom context as dict to page to HighlightCodeBlock: wrong type
  • Remove the if and the else block: doesn’t help

I’m guessing there’s no way to do this but I’d figured I ask anyways - just to be sure.

You’re right, there’s currently no way to do this, but I agree that it would be useful.

The simplest way would probably to allow overriding the type in the opts passed in. If you could create a GitHub issue for it, I’m sure someone will pick it up.

What you could try is to set the option guessSyntax which will give you highlighting even for non-supported types; the current “guessing” is pretty basic (I think you get a text lexer for all), but you should at least get line highlighting.

Then you could just drop the

{{- if transform.CanHighlight $lang -}}

Conditional.

The .Options map passed into the code block render hook is empty when transform.CanHighlight is false. However, in this case, the highlight options are available in the .Attributes map.

So we can cheat.

If you’re OK using the default wrapper, set the default language to “text” (or whatever), and use the transform.Highlight function.

{{- $lang := "text" }}
{{- $options := .Options }}
{{- if transform.CanHighlight .Type }}
  {{- $lang = .Type }}
{{- else }}
  {{- $options = .Attributes }}
{{- end }}
{{- $options = merge (dict "noClasses" false) $options }}

{{- transform.Highlight .Inner $lang $options }}

With the above, all of these work:

```python {hl_lines="3 6"}

```invalid {hl_lines="3 6"}

``` {hl_lines="3 6"}

The default wrapper generated by transform.Highlight (as shown above) is a simpler approach if you plan to mix line number options (none, inline, or table) throughout the site.

1 Like

These are all great suggestions. Thank you both.

Done: Allow to specify language for "transform.HighlightCodeBlock" · Issue #11872 · gohugoio/hugo · GitHub

In the end I ended up using transform.Highlight again but I may be switching back to transform.HighlightCodeBlock once this feature is implemented.

For reference, this is what I ended up with: devlog-theme/layouts/_default/_markup/render-codeblock.html at 511059b99d2ce115472a6cb1dab6924e55a202be · skrysmanski/devlog-theme · GitHub

This is exactly what I’ve done. One thing I noticed though was that the casing in .Options and .Attributes differ: especially for the lineNos parameter - which is lineNos in .Options (upper-case N) but linenos in .Attributes (lower-case N).

To check for this parameter I ended up with this:

index $originalOptions "lineNos" | default (index $originalOptions "linenos")

Unsure whether this should be considered a bug but it makes things harder than the should be (IMHO).