Last active
February 27, 2023 04:21
-
-
Save skyzyx/a796d66f6a124f057f3374eff0b3f99a to your computer and use it in GitHub Desktop.
Hugo Partial for Generating the Table of Contents
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
{{- partial "toc.html" . -}} | |
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{{/* https://github.com/gohugoio/hugo/issues/1778 */}} | |
{{/* ignore empty links with + */}} | |
{{- $headers := findRE "<h[2-4].*?>(.|\n])+?</h[2-4]>" .Content -}} | |
{{ .Scratch.Set "last_level" 1 }} | |
{{/* at least one header to link to */}} | |
{{- $has_headers := ge (len $headers) 1 -}} | |
{{- if $has_headers -}} | |
<aside class="table-of-contents"> | |
<details> | |
<summary> | |
<b>Table of Contents</b> | |
</summary> | |
{{- range $headers -}} | |
{{- $header := . -}} | |
{{- $base := ($.Page.File.LogicalName) -}} | |
{{- $anchorId := ($header | plainify | htmlUnescape | anchorize) -}} | |
{{- $href := delimit (slice $base $anchorId) "#" | string -}} | |
{{- range findRE "[2-4]" . 1 -}} | |
{{- $next_heading := (int .) -}} | |
{{- if gt $next_heading ($.Scratch.Get "last_level") -}} | |
<ul class="toc-h{{ . }}"> | |
{{- else if lt $next_heading ($.Scratch.Get "last_level") -}} | |
</ul> | |
{{- end -}} | |
<li><a href="{{ relref $.Page $href }}">{{- $header | plainify | htmlUnescape -}}</a></li> | |
{{ $.Scratch.Set "last_level" $next_heading }} | |
{{- end -}} | |
{{- end -}} | |
</details> | |
</aside> | |
{{- end -}} |
But @Iooeee 's code rendered too many end tags 😂
I have investigated into it and wrote a blog post
If the blog is too abstract, here is the code
{{- $headers := findRE "<h[1-4].*?>(.|\n])+?</h[1-4]>" .Content -}}
{{- $has_headers := ge (len $headers) 1 -}}
{{- if $has_headers -}}
{{- $largest := 6 -}}
{{- range $headers -}}
{{- $headerLevel := index (findRE "[1-4]" . 1) 0 -}}
{{- $headerLevel := len (seq $headerLevel) -}}
{{- if lt $headerLevel $largest -}}
{{- $largest = $headerLevel -}}
{{- end -}}
{{- end -}}
{{- $firstHeaderLevel := len (seq (index (findRE "[1-4]" (index $headers 0) 1) 0)) -}}
{{- $.Scratch.Set "bareul" slice -}}
<div id="TableOfContents">
<ul>
{{- range seq (sub $firstHeaderLevel $largest) -}}
<ul>
{{- $.Scratch.Add "bareul" (sub (add $largest .) 1) -}}
{{- end -}}
{{- range $i, $header := $headers -}}
{{- $headerLevel := index (findRE "[1-4]" . 1) 0 -}}
{{- $headerLevel := len (seq $headerLevel) -}}
{{/* get id="xyz" */}}
{{ $id := index (findRE "(id=\"(.*?)\")" $header 9) 0 }}
{{/* strip id="" to leave xyz (no way to get regex capturing groups in hugo :( */}}
{{ $cleanedID := replace (replace $id "id=\"" "") "\"" "" }}
{{- $header := replaceRE "<h[1-4].*?>((.|\n])+?)</h[1-4]>" "$1" $header -}}
{{- if ne $i 0 -}}
{{- $prevHeaderLevel := index (findRE "[1-4]" (index $headers (sub $i 1)) 1) 0 -}}
{{- $prevHeaderLevel := len (seq $prevHeaderLevel) -}}
{{- if gt $headerLevel $prevHeaderLevel -}}
{{- range seq $prevHeaderLevel (sub $headerLevel 1) -}}
<ul>
{{/* the first should not be recorded */}}
{{- if ne $prevHeaderLevel . -}}
{{- $.Scratch.Add "bareul" . -}}
{{- end -}}
{{- end -}}
{{- else -}}
</li>
{{- if lt $headerLevel $prevHeaderLevel -}}
{{- range seq (sub $prevHeaderLevel 1) -1 $headerLevel -}}
{{- if in ($.Scratch.Get "bareul") . -}}
</ul>
{{/* manually do pop item */}}
{{- $tmp := $.Scratch.Get "bareul" -}}
{{- $.Scratch.Delete "bareul" -}}
{{- $.Scratch.Set "bareul" slice}}
{{- range seq (sub (len $tmp) 1) -}}
{{- $.Scratch.Add "bareul" (index $tmp (sub . 1)) -}}
{{- end -}}
{{- else -}}
</ul></li>
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
<li>
<a href="#{{- $cleanedID -}}">{{- $header | safeHTML -}}</a>
{{- else -}}
<li>
<a href="#{{- $cleanedID -}}">{{- $header | safeHTML -}}</a>
{{- end -}}
{{- end -}}
<!-- {{- $firstHeaderLevel := len (seq (index (findRE "[1-4]" (index $headers 0) 1) 0)) -}} -->
{{ $firstHeaderLevel := $largest }}
{{- $lastHeaderLevel := len (seq (index (findRE "[1-4]" (index $headers (sub (len $headers) 1)) 1) 0)) -}}
</li>
{{- range seq (sub $lastHeaderLevel $firstHeaderLevel) -}}
{{- if in ($.Scratch.Get "bareul") (add . $firstHeaderLevel) -}}
</ul>
{{- else -}}
</ul></li>
{{- end -}}
{{- end -}}
</ul>
</div>
{{- end -}}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Neither of these work correctly for me - they both leave
<ul>
tags unclosed. However I found one one disaev.me that works.I've edited it so that it works with custom anchors which none of the others do, and removed all extra HTML so it just returns nested
<ul>