latexdiff-vcとGitとVSCodeを組み合わせて毎回差分を出力するLaTeX Workshopの設定

LaTeXなんぞ書きとうなかった。差分出力の為だけにGitを使い前回のコミットとの差分を取るので普通の細やかな文書管理は死ぬ。比較的速い(セキュリティが薄いため)WSL2を使っていて、コマンドもUbuntu仕様。注意

大量に書きながら他人に添削してもらおうという時、差分を送れば相手がそこだけ見られるので楽。latexdiff-vcというのが知られていて、Gitのrevisionを指定するといい感じにできるらしい:

latexdiff-vc -e utf8 --git -r HEAD -d diff --force main.tex

対訳は「エンコード(-e)はUTF-8。GitのHEAD(1つ前) revision(-r)を参照して、./diffディレクトリ(-d)下に./main.texの差分を(同じ名前で)出力し、(./diff/main.texを)強制的(--force)に上書きする」である。相対パスのみ対応してるみたいなので注意。

LaTeX Workshopの設定は忌避されがちだが、設定にターミナルのコマンドを書いて自動で叩いてるだけなので実は何でもない。"latex-workshop.latex.tools"にコマンドを1つずつ関数みたいに登録して、それを複数並べたものをまた関数みたいに"latex-workshop.latex.recipes"に登録する。それだけ。

まず上のlatexdiff-vc"latex-workshop.latex.tools"に登録するところから始めよう。VSCodeの設定から「設定(JSONを開く)」とかいう紙+曲がり矢印のアイコンをクリックするとJSONファイル開かれるので"latex-workshop.latex.tools"キーの配列に次のやつを追加。名前はtake-diffとかでいいか:

{
    "name": "take-diff",
    "command": "latexdiff-vc",
    "args": [
        "-e", "utf8",
        "--git", "-r", "HEAD",
        "-d", "diff",
        "--force",
        "%DOCFILE%.tex"
    ],
},

コマンド実行時のカレントディレクトリはTeXファイルのあるところで、%DOCFILE%というのが拡張子を除いたファイル名(拡張子を除いたパスが欲しいなら%DOC%だけどここでは使わない)。

タイプセット

これでビルドしたいTeXファイルと同じ名前のTeXファイルが./diffディレクトリ下に作成された。これもタイプセット(俗にコンパイルと呼ばれる操作)しよう。latexmkを使えば楽:

latexmk -outdir=%OUTDIR%/diff %OUTDIR%/diff/%DOCFILE%

ここで%OUTDIR%はTeXファイルのあるディレクトリのこと。以上のコマンドを打ちたいので、次。名前はlatexmk-diffとでもしよう:

{
    "name": "latexmk-diff",
    "command": "latexmk",
    "args": [
        "-outdir=%OUTDIR%/diff",
        "%OUTDIR%/diff/%DOCFILE%"
    ],
},

ゴミファイル(失礼)の削除

LaTeXのタイプセット時には(本来必要だった)雑多ファイルがガンガン出力される上に、.texファイルと同じ場所に居座るので非常にうざったい。TeXとPDFだけ見返させてくれ。お願いだから。

このお願いを本来LaTeX Workshopは叶えてくれるんだけど、最後に行ったタイプセットしか掃除してくれないみたいなので、元のTeXファイルのゴミもdiffファイルのゴミも手動で掃除しよう。まずは元のTeXファイルをタイプセットして掃除までを見ていく:

latexmk -synctex=1 -interaction=nonstopmode -file-line-error -halt-on-error -outdir=%OUTDIR% %DOC%
rm -f %DOCFILE%.bbl %DOCFILE%.blg %DOCFILE%.dvi %DOCFILE%.fdb_latexmk %DOCFILE%.fls %DOCFILE%.log

latexmkでタイプセットした後、直接bashのrmコマンドを-fで(確認なしに)叩いてるだけ。おまじない引数については語り尽くされてるので説明しない(synctexは便利とだけ)。こうすると%DOCFILE%.pdf%DOCFILE%.aux%DOCFILE%.synctex.gz%DOCFILE%.tex(元のファイル)だけ残る。%DOCFILE%.auxは命題番号とかが記録されてて、個人的にrx(別ファイルの番号を引用するやつ)に使う。あと%DOCFILE%.synctex.gzもsynctexのために残してある(消すとエディタからLaTeXに飛べなくなる)。

(実はlatexmkをしたあとlatexmk -c -outdir=%OUTDIR% %DOC%を叩くとあらかた消えるらしいんだけど、何を削除するかが選べないので泣く泣く手動でやってる)

とりあえず登録しよう。名前はlatexmkcleanでいいや:

{
    "name": "latexmk",
    "command": "latexmk",
    "args": [
        "-synctex=1",
        "-interaction=nonstopmode",
        "-file-line-error",
        "-halt-on-error",
        "-outdir=%OUTDIR%",
        "%DOC%"
    ],
},
{
    "name": "clean",
    "command": "rm",
    "args": [
        "-f",
        "%DOCFILE%.bbl",
        "%DOCFILE%.blg",
        "%DOCFILE%.dvi",
        "%DOCFILE%.fdb_latexmk",
        "%DOCFILE%.fls",
        "%DOCFILE%.log",
    ],
},

差分確認用PDFの改名

そもそも差分確認用のTeXファイルは元TeXファイルの名前と一緒で、そこから生成されるPDFもその名前と一緒。つまり、差分確認用のPDFの名前は元のPDFと同名になる。これをメールで一緒に送ったら訳わかんなくなるので、名前を変更しておこう。PDFの新しい名前は%DOCFILE%-diff.pdfとして、コマンドの名前はrename-diffで:

mv %OUTDIR%/diff/%DOCFILE%.pdf %OUTDIR%/diff/%DOCFILE%-diff.pdf
{
    "name": "rename-diff",
    "command": "mv",
    "args": [
        "%OUTDIR%/diff/%DOCFILE%.pdf",
        "%OUTDIR%/diff/%DOCFILE%-diff.pdf"
    ],
},

差分確認用PDFのゴミファイル削除

差分LaTeXファイルも要らないので削除してしまおう。残るのはPDFと.auxのみ:

{
    "name": "clean-diff",
    "command": "rm",
    "args": [
        "-f",
        "%OUTDIR%/diff/%DOCFILE%.bbl",
        "%OUTDIR%/diff/%DOCFILE%.blg",
        "%OUTDIR%/diff/%DOCFILE%.dvi",
        "%OUTDIR%/diff/%DOCFILE%.fdb_latexmk",
        "%OUTDIR%/diff/%DOCFILE%.fls",
        "%OUTDIR%/diff/%DOCFILE%.log",
        "%OUTDIR%/diff/%DOCFILE%.tex",
    ],
},

まとめる

"latex-workshop.latex.recipes"キーの配列に以下を追加。さっき書いたコマンドに付けた名前をそのまま並べただけ:

{
    "name": "latexmk-and-diff",
    "tools": [
        "latexmk",
        "clean",
        "take-diff",
        "latexmk-diff",
        "rename-diff",
        "clean-diff",
    ]
},

全部合わせると以下。長いね:

"latex-workshop.latex.recipes": [
    {
        "name": "latexmk-and-diff",
        "tools": [
            "latexmk",
            "clean",
            "take-diff",
            "latexmk-diff",
            "rename-diff",
            "clean-diff",
        ]
    },
],
"latex-workshop.latex.tools": [
    {
        "name": "latexmk",
        "command": "latexmk",
        "args": [
            "-synctex=1",
            "-interaction=nonstopmode",
            "-file-line-error",
            "-halt-on-error",
            "-outdir=%OUTDIR%",
            "%DOC%"
        ],
    },
    {
        "name": "clean",
        "command": "rm",
        "args": [
            "-f",
            "%DOCFILE%.bbl",
            "%DOCFILE%.blg",
            "%DOCFILE%.dvi",
            "%DOCFILE%.fdb_latexmk",
            "%DOCFILE%.fls",
            "%DOCFILE%.log",
        ],
    },
    {
        "name": "take-diff",
        "command": "latexdiff-vc",
        "args": [
            "-e", "utf8",
            "--git", "-r", "HEAD",
            "-d", "diff",
            "--force",
            "%DOCFILE%.tex"
        ],
    },
    {
        "name": "latexmk-diff",
        "command": "latexmk",
        "args": [
            "-synctex=1",
            "-interaction=nonstopmode",
            "-file-line-error",
            "-halt-on-error",
            "-outdir=%OUTDIR%/diff",
            "%OUTDIR%/diff/%DOCFILE%"
        ],
    },
    {
        "name": "rename-diff",
        "command": "mv",
        "args": [
            "%OUTDIR%/diff/%DOCFILE%.pdf",
            "%OUTDIR%/diff/%DOCFILE%-diff.pdf"
        ],
    },
    {
        "name": "clean-diff",
        "command": "rm",
        "args": [
            "-f",
            "%OUTDIR%/diff/%DOCFILE%.bbl",
            "%OUTDIR%/diff/%DOCFILE%.blg",
            "%OUTDIR%/diff/%DOCFILE%.dvi",
            "%OUTDIR%/diff/%DOCFILE%.fdb_latexmk",
            "%OUTDIR%/diff/%DOCFILE%.fls",
            "%OUTDIR%/diff/%DOCFILE%.log",
            "%OUTDIR%/diff/%DOCFILE%.tex",
        ],
    },
]

おまけ

%DOCFILE%.aux%DOCFILE%.synctex.gzは使うんだけど別に読むわけじゃないので表示しなくていい。VSCodeの設定で非表示にする(見えなくなるのでなくても気づかない。注意):

"files.exclude": {
    "**/*.aux": true,
    "**/*.synctex.gz": true
},

追記

生成がワンテンポ送れるからどうしたのかなと思ってたが、.bblファイルとかが消えて再生成が掛かってたらしいので、だいたい同じことをするパッチファイルをつくり、差分生成を非同期で行うことにした。もう面倒だしLaTeX-Workshopから外れるので書かない。メンタルヤバ目なので寝ます。