Skip to content
Go back

Hugoでcitationを扱うには?

  • 先日、Obsidianでcitationを扱う方法を考えたので、Hugoでもどこまでできそうか考えたい
  • Hugoはスクリプトである程度自由にできてしまうので、問題なくできそうではある
    • ページ内の[[@citekey]]を集めてfootnote1のように最下部に一覧表示するとか、[@citekey]を指定のスタイルで置き換えるとか
    • @citekeyページを作っておけば、そのfrontmatterからメタ情報を取得できるはず?
  • Obsidianとの相互運用性を考慮して、@citekeyで文献ノートを作り、wikilink形式でリンクを張るとする
  • とりあえず、前回作ったpost_content.htmlを以下のように、処理中に情報をstoreに集められるように修正した
{{- /* コードブロックの外だけで置換処理を適用するpartial */ -}}
{{- $store := newScratch -}}
{{- $result := "" -}}

{{- /* codeタグで分割する */ -}}
{{- $s1 := split .Content "<code" -}}

{{- /* 先頭は必ずコード外として処理する */ -}}
{{- $content := index $s1 0 -}}
{{- $content = partial "replace-japanese-novel-ruby.html" (dict "Content" $content "Store" $store) -}}
{{- $content = partial "replace-wikilinks.html" (dict "Content" $content "Store" $store) -}}
{{- $result = print $result $content -}}

{{- /* 残りはコード内と外のペアとして処理する */ -}}
{{- range after 1 $s1 -}}
    {{- /* codeタグで分割する */ -}}
    {{- $s2 := split . "</code>" -}}

    {{- /* 先方はコード内 */ -}}
    {{- $result = print $result "<code" (index $s2 0) "</code>" -}}

    {{- /* 後方はコード外 */ -}}
    {{- $content := (index $s2 1) -}}
    {{- $content = partial "replace-japanese-novel-ruby.html" (dict "Content" $content "Store" $store) -}}
    {{- $content = partial "replace-wikilinks.html" (dict "Content" $content "Store" $store) -}}
    {{- $result = print $result $content -}}
{{- end -}}

{{- $result | safeHTML -}}

{{- partial "references.html" $store -}}
  • replace-wikilinks.htmlを以下のように修正する
    • 頭に@を持つリンクは、そのaタグにid属性を個別に付ける
    • どの@citekeyがどれだけあるかを.Storeに記録する
    • 一応、@citekeyに関係なく連続するインデックスでもIDを割り当てている
{{- $content := .Content -}}
{{- range (findRESubmatch `\[\[(((@?)[^#\|\]\n]*)(?:#([^\|\]\n]*))?)(?:\|([^\]\n]*))?\]\]` $content) -}}
    {{- $matched := index . 0 -}}
    {{- $full_name := index . 1 -}}
    {{- $file_name := index . 2 -}}
    {{- $at := index . 3 -}}
    {{- $heading_name := index . 4 -}}
    {{- $title := index . 5 -}}

    {{- /* リンク先をURLに変換する */ -}}
    {{- $url := "" -}}
    {{- if $file_name -}}
        {{- /* エラー隠蔽のためにtryを使いたかったが、上手くいかなかった(v0.148.1) */ -}}
        {{- $path := relref page $file_name -}}
        {{- if eq $path "" -}}
            {{- /* リンク先が存在しなければ、リンクを置き換えない */ -}}
            {{- continue -}}
        {{- end -}}
        {{- $url = print $path  -}}
    {{- end -}}
    {{- if $heading_name -}}
        {{- $url = print $url "#" (anchorize $heading_name) -}}
    {{- end -}}

    {{- /* @citekey形式はaタグにidを付ける */ -}}
    {{- $global_id := "" -}}
    {{- $local_id := "" -}}
    {{- if $at -}}
        {{- /* @citekeyの出現順を含めたidを作る */ -}}
        {{- $global_count_k := "references::global_count" -}}
        {{- $global_count_v := add (or ($.Store.Get $global_count_k) 0) 1 -}}
        {{- $global_id = print "citation-" $global_count_v -}}
        {{- $local_count_k := print "references::" $file_name ".count" -}}
        {{- $local_count_v := add (or ($.Store.Get $local_count_k) 0) 1 -}}
        {{- $local_id = print "citation-" $file_name "-" $local_count_v -}}

        {{- /* 同名の@citekeyで情報を共有するために保存しておく */ -}}
        {{- $.Store.Set $global_count_k $global_count_v -}}
        {{- $.Store.Set $local_count_k $local_count_v -}}
        {{- $.Store.Add "references::citekeys" (slice $file_name) -}}
    {{- end -}}

    {{- /* wikilink形式をHTMLに置き換える */ -}}
    {{- $c_attr := "" -}}
    {{- with $global_id -}}
        {{- $c_attr = print $c_attr " id=\"" . "\"" -}}
    {{- end -}}
    {{- $a_attr := "" -}}
    {{- with $local_id -}}
        {{- $a_attr = print $a_attr " id=\"" . "\"" -}}
    {{- end -}}
    {{- with $url -}}
        {{- $a_attr = print $a_attr " href=\"" . "\"" -}}
    {{- end -}}
    {{- $content = replace $content $matched (print "<span id=\"" $c_attr "\"><a" $a_attr ">" (or $title $full_name) "</a></span>") 1 -}}
{{- end -}}
{{- $content | safeHTML -}}
  • references.htmlを以下の内容で作る
    • .Storeに格納された@citekeyリストを使って一覧を表示する
    • とりあえず、footnoteの書き方をそのまま利用している
    • その文献の情報はその文献ノートのfrontmatterから取得する
{{- with .Get "references::citekeys" | uniq | sort -}}
    <div class="footnotes" role="doc-endnotes">
        <hr>
        <h6>References</h6>
        <ul>
            {{- range . -}}
                {{- $citekey := . -}}
                {{- $url := relref page $citekey -}}
                {{- $page := site.GetPage $url -}}
                <li>
                    <p><a href="{{$url}}">[{{substr $citekey 1}}] {{print (or $page.Params.authors "[AUTHORS]") ", \"" (or $page.Title "[TITLE]") ",\" " (or $page.Params.year "[YEAR]") "."}}</a>&#160;
                        {{- range $.Get (print "references::" $citekey ".count") -}}
                        {{- /* whitespace */}} <a href="#citation-{{$citekey}}-{{add . 1}}" class="footnote-backref"
                            role="doc-backlink">&#x21a9;&#xfe0e;</a>
                        {{- end -}}
                    </p>
                </li>
            {{- end -}}
        </ul>
    </div>
{{- end -}}
  • これで、頭に@のついた生きたリンクがあれば、footnoteの下に参考文献として表示されるようになったはず
    • [[ Last 2025Last, F. 2025. Example.]]
  • ここまでやってみて、ビルドがどれだけ重くなったかは検証していない

おまけ#

  • .Page.Siteに関連した.Storeもあるのでグローバル変数のような感覚で使える
    • ただし、ライブリロードではリセットされないので、ちょっと使いづらい
  • frontmatterにあるユーザー定義の値は.Page.Paramsから取得できるらしい
    • 詳しくは不明だが、実際そのように見える

参考文献#

Footnotes#

  1. こういうやつ