remark-theorem
v1.0.0
Published
Custom LaTeX theorem/proof/lemma/... environments reimplemented as remark container-directive plugins for Astro, using a two-pass build for cross-file numbering and cross-references.
Readme
remark-theorem
自作のLaTeX定理環境(theorem / lemma / proof / axm / defi / exam / prop / rema / corr)を
Astro (remark container-directive) 向けに再実装したプラグインです。
acrotermと同じく、LaTeXコードの移植ではなく挙動の再設計・再実装です。
LaTeXコマンドとの対応
| LaTeX | このプラグイン |
|---|---|
| \begin{theorem}[名前](label)本文\end{theorem} | :::theorem{label=... name="名前"}本文::: |
| \begin{theorem}本文\end{theorem}(番号なし) | :::theorem本文:::(label属性を省略) |
| \begin{proof}(label)本文\end{proof} | :::proof{ref=label}本文::: |
| \thoremref{label} \lemmref{label} 等9種 | :ref[label](種別は自動判定) |
| \proofref{label} | :proofref[label] |
lemma axm(公理)defi(定義)exam(例)prop(命題)rema(注)corr(系)もすべて
theoremと同じ構文で使えます(THEOREM_TYPESテーブルで日本語見出しに対応)。
設計上の要点
- 番号付け:元のLaTeXコードは種別ごとに独立したカウンタ(
\newtheoremを共有オプションなしで個別に作成) なので、定理1, 定理2...補題1, 補題2...のように種別ごとに独立採番します。 labelを省略した場合:LaTeXの_theorem*(星付き環境)相当として無番号で出力し、カウンタも消費しません。 ただし相互参照もできなくなります(LaTeX側も同様の制約です)。proofの番号:元コードは\setcounter{_proof}{\getrefnumber{theorem:#2}-1}という手の込んだ方法で 「証明の番号を参照先の定理番号に一致させる」ことをしていますが、これは要するに 「証明は自分の番号を持たず、ref先の番号をそのまま使う」という意味なので、素直にそう実装しています。- 2段階ビルド:番号付けも相互参照も「まだレンダリングしていない他ページの情報」を要求するため、
acrotermと同じ Pass 1 (
scan.mjsで全文書を事前スキャンし通し番号とラベル辞書を確定) → Pass 2 (plugin.mjsで実際に変換) という構成です。LaTeXが\label/\ref解決のために 2回コンパイルするのと同じ理由です。 - acrotermとの違いとして、こちらはラベルが本文中に明示されるため(
label=pythagoras)、 Pass 2側は出現順カーソルを追跡する必要がなく、registry.labels[label]を直接引くだけで済みます。
セットアップ
yarn add remark-theorem remark-directive unified unist-util-visitastro.config.mjs(example/astro.config.mjs参照):
import theoremIntegration from 'remark-theorem/astro-integration.mjs';
import theoremPlugin from 'remark-theorem/src/plugin.mjs';
const theoremOptions = {
contentDir: './src/content/docs',
registryPath: './.theorem/registry.json',
};
export default defineConfig({
integrations: [theoremIntegration(theoremOptions)],
markdown: { remarkPlugins: [[theoremPlugin, theoremOptions]] },
});見出しのスタイル(太字+下線)は example/theorem.css を参照してください。
書き方の例
:::theorem{label=pythagoras name="ピタゴラスの定理"}
直角三角形において $a^2+b^2=c^2$ が成り立つ。
:::
:::proof{ref=pythagoras}
正方形の面積を2通りに数えることで証明できる。
:::
前章の :ref[pythagoras] は :proofref[pythagoras] で示した通りである。出力:
<div class="thm-block thm-theorem" id="label-pythagoras">
<p class="thm-head thm-head-theorem">定理1 ピタゴラスの定理:</p>
<p>直角三角形において $a^2+b^2=c^2$ が成り立つ。</p>
</div>
<div class="thm-block thm-proof" id="label-proof-pythagoras">
<p class="thm-head thm-head-proof">定理1の証明:</p>
<p>正方形の面積を2通りに数えることで証明できる。</p>
</div>
<p>前章の <a href="#label-pythagoras" class="thm-ref">定理1</a> は
<a href="#label-proof-pythagoras" class="thm-ref">定理1の証明</a> で示した通りである。</p>既知の制約
labelはサイト全体で一意である必要があります(重複するとPass 1でエラーになります)。- 番号順は「コンテンツディレクトリ内のファイルパス昇順」に依存します(acrotermと同じ制約)。
- 1つの
labelに対してproofブロックは1つまでを想定しています(同じ定理に複数の証明を つけたい場合はアンカーが衝突するため、refとは別にproof自身にもlabel属性を持たせる 拡張が必要です)。 - コンテナディレクティブは
:::(コロン3つ)が必須です。::(2つ)は1行で完結する leafDirective として解釈されるため、複数段落にまたがる本文を書くと壊れます。
テスト
node src/scan.mjs example/content .theorem/registry.json
node test-run.mjs