org-lint
.org/.mdファイルの散文書式ルール(~/.claude/rules/org-babel.mdに文書化)のためのリンターです。目的は、毎回手動で個々のルールを記憶(そして、ついさっき直したルールだけを修正して残りを忘れるリスク)に頼るのではなく、明示的なルールリストに対してテキストをチェックすることです。
使用タイミング
.org/.mdファイル内の散文段落を編集または新規作成した直後(結果をユーザーに表示する前に、各ルールを手動で再確認する代わりに)。- ユーザーから明示的に要求があった場合(「orgルールをチェックして」「リントを実行して」、または
clippyとの比較など)。 #+begin_src/#+begin_exampleブロック(コード)内の内容には適用しません。散文段落と見出しのみが対象です。
ステップ1 — 機械的チェック
編集したファイルに対してスクリプトを実行します。
python3 ~/.claude/skills/org-lint/scripts/lint.py ファイルパス.org
スクリプトは正規表現で以下を検出します。
~...~/=...=の周りに空白がない/(例:~a~/~b~、正しくは~a~ / ~b~) — 確実な違反。[[file:../— 現在のディレクトリからの相対リンク。root:省略形を使用すべき — 確実な違反。- ファイル末尾の改行なし — 確実な違反。
=...=内の大文字の頭字語(例:=CSS=) — 要確認(稀な例外は許可)。- イタリック
/単語/— 要確認:これが本当にプロジェクト用語集の用語(spcfctn/glossary.orgに対応するセクションがあるか?このファイル内の初出で用語集へのリンクがあるか?)なのか、それとも誤って変換されなかったセクション/ファイルへのリンク([[*セクション名][...]]であるべき)なのか。 =パス/ファイル.org=としてのファイル参照 — 要確認:実際の[[file:...]]リンクであるべきではないか。- 「パラメータ」(parameter)という単語 — 要確認:Rust/Yewコンポーネントのプロパティ(
struct ...Props)について言及している場合、正しい訳語は「プロパティ」(properties)であり、「パラメータ」(parameters)は古い用語。Yewコンポーネント以外(関数パラメータ、CLI、REST手続きなど)では「パラメータ」でOK、変更しない。 :noweb-ref 定義が:noweb-ref 宣言よりも前のセクションにある — 要確認:ファイルを上から読んだ順で、宣言が最初に来るべき(org-babel.md参照)。- 同一の
:tangle パスを持つ#+begin_srcブロックが複数あり、ファイル内に:noweb yes/<<...>>が全くない — 確実な違反(古い結合方法。古いgitignore.orgのように。org-babel.md参照)。:tango+:noweb yesを持つ単一のコンテナブロックに<<名前>>プレースホルダと:noweb-ref 名前セクションを使って書き直すこと。 - ファイルが複数の異なるターゲットファイルにタングルされ、そのうち少なくとも2つが noweb を消費する(例:
config.org→.env.example+parameters.php、スケルトン#+begin_srcに:noweb yesを持つ)にもかかわらず、少なくとも1つの:noweb-ref/<<...>>ブロックがターゲットプレフィックスなしで命名されている(env:名前ではなく名前) — 確実な違反(org-babel.md、マルチターゲットorgファイルのプレフィックスルール参照)。単一ターゲットファイルの場合、および noweb を消費するターゲットが1つだけで、残りが自己完結型の単一ブロック:tangleターゲット(<<...>>なし)であるファイル(例:gitignore.org:5つの:tangleターゲットがあるが、ルートの.gitignoreだけが noweb スケルトン経由) — プレフィックスは不要。実際のあいまいさがないため。 #+begin_src行のパラメータの順序が間違っている — 確実な違反。正しい順序:ブロックタイプ →:noweb yes→:noweb-sep "..."→ 残りのパラメータ →:tangle パスまたは:noweb-ref 名前(最後、org-babel.md参照)。- ファイル名が
cargo.org— 確実な違反(Rustプロジェクトの古い名前。config.orgにリネーム。org-babel.md参照)。 :tangleが絶対パス(/...、~/...)または上位ディレクトリ..を使用している — 確実な違反(org-babel.md参照)。ターゲットはorgファイルからの純粋な下向き相対パスである必要がある。ターゲットが本当にソースツリーの外にある場合、../絶対パスではなく、symlink または別のディレクトリ構造が必要(事例:farba-override→farba-themeを../farba-theme/...経由、チケットFS-1085)。=...=の外での大文字使用(例:NOT、ONLY) — 要確認:単語の強調であり、頭字語/略語でない場合、大文字ではなく org bold(*not*)に置き換える。- 「フック」(hook、React/Yewの訳語) — 古い用語。正しい訳語は「フック」(hook)のままとする(ただし、コード識別子
use_effect_withなどは対象外。散文のみ)。 - 105文字より長い散文行 —
emacsclient経由でfill-paragraphを実行する必要あり(下記参照)。
「確実な」検出結果は常に修正します。「手動確認が必要」なものは、それぞれ確認しますが、すべてがエラーとは限りません(例:=rest= / =forge-yew= は正当な略語/クレート名であり、違反ではありません)。
ステップ2 — 手動チェックリスト(スクリプトでは検出できないもの)
- コード識別子/型/関数/変数/モジュール — および任意のコード断片や式(例:
id=1、value == 0、items()) — チルダ~...~。略語/頭字語およびクレート/ライブラリ名の「パブリック」形式(ハイフンを含む) — 等号=...=。修飾パスクレート::型はコードからの型名であるため、クレートプレフィックスが含まれていても全体をチルダで囲む。 - 略語/頭字語 — 文の中では小文字、文頭のみ大文字(すべて大文字にはしない)。稀な確立された例外を除く。
- 同一ファイル内のセクションへのリンク —
[[*見出し名][名前]]。イタリックや単なるテキストではない。同一ディレクトリ内のファイルへのリンク — 単に[[file:隣のファイル.org][説明]]。別のディレクトリ内のファイル(特に共通のワークディレクトリ内の別のgitリポジトリ)へのリンク —root:省略形を使用([[root:code/ads-back/ファイル.org::N][説明]])、../../../のような現在のディレクトリからの相対パスは使用しない。省略形はワークディレクトリのルートにある.dir-locals.elで宣言される(evalフォーム、locate-dominating-fileを使用)。プロジェクトにまだ存在しない場合は作成する。 - イタリック
/.../— プロジェクト用語集の用語のみに使用(直後に(/translation/)を伴う)。各ファイル内で用語が最初に登場する箇所では、定義を繰り返すのではなく、[[file:.../spcfctn/glossary.org::*用語][...]]で該当セクションにリンクする。
ステップ3 — 桁揃え
編集した各段落に対して、手動で桁揃えをするのではなく、emacsclient --eval 経由で実際の fill-paragraph を呼び出します(org-babel.mdの該当ルール参照)。段落のテキストは new_string に、またはファイル作成時は1行で書き、自分で改行を推測しようとしないでください。fill-paragraph が改行を完全に決定します。
重要: fill-paragraph(または org-fill-paragraph)を呼び出す前に、必ず (font-lock-ensure) を実行してください。find-file-noselect でヘッドレスで開かれたバッファはフォント化が行われず、Orgリンク [[パス][説明]] が短い表示テキスト 説明 にまだ「折りたたまれていません」 — font-lock-ensure なしでは、fill-paragraph はリンクの表示幅ではなく、長い生テキストに基づいて改行を計算するため、結果がライブのインタラクティブEmacsで M-q を実行した場合と異なります。
例:
emacsclient --eval '
(let ((buf (find-file-noselect "ファイルパス.org")))
(with-current-buffer buf
(org-mode)
(font-lock-ensure)
(goto-char (point-min))
(search-forward "段落内のユニークな文字列")
(org-fill-paragraph)
(save-buffer)))
'