Communityコーディング&開発github.com

pwquint24/org-babel-literate-tangle

Org Babelによるリテレートプログラミング、コード抽出(タングリング)、Nowebマクロ展開、バッチコンパイル、設定管理のガイド。

org-babel-literate-tangle とは?

このスキルは、Emacs Org Babelを用いたリテレートプログラミング(文芸的プログラミング)の実践ガイドです。Donald Knuthが提唱したパラダイムに基づき、単一のOrgファイル内で人間向けのドキュメント(散文、設計意図)と機械実行可能なコードブロックを統合します。主な機能として、コードブロックを指定したファイルに抽出する「タングリング」、Noweb記法によるモジュール式のコード組み立て、ヘッドレスでのバッチ処理(CI/CDやMakefileとの統合)、そして設定管理(init.orgパターン)があります。このスキルはEmacs上で動作し、ターミナルからのバッチ実行もサポートしています。特にPythonなどのホワイトスペース依存言語では、インデント保持設定(`org-src-preserve-indentation`)が重要で、これにより生成コードの構文が保証されます。また、生成されたファイルから元のOrgドキュメントの該当箇所にジャンプする双方向ナビゲーション機能も提供します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/pwquint24/org-babel-agent-skills/tree/HEAD/skills/org-babel-literate-tangle

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Org Babelを用いたリテレートプログラミングとタングリング

Org BabelはDonald Knuthのリテレートプログラミング(文芸的プログラミング)パラダイムの実装です。コードにコメントを散りばめる代わりに、人間向けの散文、アーキテクチャノート、設計根拠を記述し、その中に関数コードブロックを埋め込みます。

単一のOrgファイルが、以下の2つの異なる出力の信頼できる唯一の情報源(Single Source of Truth)として機能します。

  1. ウィービング(人間向けドキュメント): ノートブックを整形されたHTML、LaTeX/PDF、またはMarkdownにエクスポートして読者に提供します。
  2. タングリング(機械実行): コンパイラ、インタプリタ、デプロイシステム向けに、純粋で実行可能なソースコードファイル(.py、.sh、.c、.conf、.el)を抽出します。

1. タングリングの基本: :tangle ヘッダー引数

デフォルトでは、コードブロックはタングルされません。:tangle 系のヘッダー引数で、ブロックをどのようにどこにディスクへ抽出するかを指定します。

1.1 :tangle 引数の値

値動作
:tangle no (デフォルト)ブロックはいかなるファイルにも書き出されません。(<<noweb>>を使って他のブロックに含めることは可能です)
:tangle "path/to/file.py"ブロック本体を指定されたファイルパスに直接書き出します(バッファディレクトリからの相対パス、または絶対パス)。
:tangle yesOrgファイルのベース名と言語の標準ファイル拡張子でファイルにタングルします(例: model.org -> model.py)。

1.2 その他のタングリングヘッダー引数

  • :shebang "#!/usr/bin/env python3": タングルされたファイルの先頭行に指定されたシバン文字列を追加します。
  • :tangle-mode (identity #o755): 生成されたファイルのファイルシステムパーミッションを設定します(例: #o755 でスクリプトを実行可能にします)。
  • :no-expand: 変数代入(:var)がタングルコードにインライン展開されるのを防ぎ、生のブロック本体のみを書き出します。
  • :mkdirp yes: タングルパスに存在しない親ディレクトリを自動的に作成します。

2. タングリングのスコープ: ブロック、サブツリー、バッファレベル

タングリング引数はプロジェクト全体にわたって整理してスコープ指定できます。

2.1 サブツリーレベルのタングリング(推奨パターン)

異なる見出しで、異なるファイルやモジュールにタングルできます。

* Core Utilities
:PROPERTIES:
:header-args:python: :tangle src/utils.py :mkdirp yes
:END:

#+BEGIN_SRC python
  def slugify(text: str) -> str:
      return "-".join(text.lower().split())
#+END_SRC

* Data Processing Pipeline
:PROPERTIES:
:header-args:python: :tangle src/pipeline.py :mkdirp yes
:END:

#+BEGIN_SRC python
  from utils import slugify

  def process_record(rec: dict) -> dict:
      rec["slug"] = slugify(rec["name"])
      return rec
#+END_SRC

2.2 バッファ全体のタングリングデフォルト

ファイルの先頭で設定します:

#+PROPERTY: header-args:python :tangle src/main.py

3. Noweb構文: モジュール式コードの組み立て

Noweb を使用すると、より大きなソースファイルを、小さくモジュール化された、簡単に説明できる断片から組み立てられます。<<block_name>> を使用して、あるコードブロックを別のコードブロック内で参照します。

3.1 Noweb展開の有効化

Nowebはデフォルトで無効です。:noweb ヘッダー引数で制御します:

:noweb の値Emacsで評価されるか?ディスクにタングルされるか?ドキュメントにエクスポートされるか?
no (デフォルト)いいえいいえいいえ
yesはいはいはい
tangleいいえはいいいえ
strip-tangleはいいいえ (参照は削除されます)はい
no-exportはいはいいいえ
evalはいいいえいいえ

3.2 Nowebの例: リテレートスクリプトの構築

#+NAME: imports
#+BEGIN_SRC python :exports none
  import sys
  import argparse
  from pathlib import Path
#+END_SRC

#+NAME: argument-parser
#+BEGIN_SRC python :exports code
  parser = argparse.ArgumentParser(description="Process dataset.")
  parser.add_argument("--input", required=True, type=Path, help="Input CSV path")
  parser.add_argument("--threshold", default=0.5, type=float, help="Score cutoff")
  args = parser.parse_args()
#+END_SRC

#+NAME: process-data
#+BEGIN_SRC python :exports code
  print(f"Loading data from {args.input}...")
  # Processing logic here
#+END_SRC

# 最終的な実行可能スクリプトの組み立て:
#+NAME: main-cli
#+BEGIN_SRC python :tangle bin/run_pipeline.py :shebang "#!/usr/bin/env python3" :tangle-mode (identity #o755) :noweb yes :exports none
  <<imports>>

  def main():
      <<argument-parser>>
      <<process-data>>

  if __name__ == "__main__":
      main()
#+END_SRC

タングルされると、bin/run_pipeline.py は、すべての断片が適切に配置されインデントされた、クリーンで実行可能なスクリプトとして書き出されます!

3.3 Nowebのプレフィックスとインデント

  • Nowebはインデントを尊重します。def main(): 内で <<argument-parser>> が4スペースインデントされている場合、展開されたブロックのすべての行が4スペースでインデントされます。
  • プレフィックス動作を無効にするには: :noweb-prefix no
  • ソースコードの代わりにブロックの実行結果を挿入するには、括弧を追加します: <<compute_constant()>>

3.4 展開のデバッグ

タングリングや実行の前に、noweb ブロックがどのように展開されるかを確認するには:

  • ブロック内にポイントを置き、C-c C-v v (org-babel-expand-src-block) を押します。
  • Org は、すべての noweb および :var 置換後の正確なコードを示す一時バッファを開きます。

4. 設定とデータのタングルファイルへのインライン化

Babel は、Org テーブルや見出しプロパティの変数をタングルコードに直接インライン化できます:

# Org テーブルに格納された設定:
#+NAME: server_config
| Key     | Value            |
|---------+------------------|
| host    | 10.0.1.25        |
| port    | 8080             |
| timeout | 30               |

# タングルされる設定モジュール:
#+BEGIN_SRC python :tangle config.py :var cfg=server_config :colnames yes
  HOST = cfg[0][1]
  PORT = int(cfg[1][1])
  TIMEOUT = int(cfg[2][1])
#+END_SRC

Babel がタングルコード内に変数ラッパーを生成するのを防ぐには、:no-expand を渡します。


5. インデント処理: ホワイトスペース依存言語

Python や YAML のようなホワイトスペースに依存する言語では、Org の自動再インデントが構文を壊す可能性があります。

Python タングリングにおける重要なルール:

org-src-preserve-indentation を t に設定します。これが t の場合、Org はエクスポートおよびタングリング時にインデントを追加したり削除したりしません:

;; Emacs 設定ファイル内:
(setq org-src-preserve-indentation t)

または、ブロックごとにスイッチ -i を使用:

#+BEGIN_SRC python -i :tangle script.py

6. タングリングの実行方法

6.1 Emacs 内での対話的操作

キーバインドコマンド説明
C-c C-v t / C-c C-v C-torg-babel-tangle現在のバッファ内のすべてのコードブロックをタングルします。
C-c C-v f / C-c C-v C-forg-babel-tangle-fileディスク上の Org ファイルを選択してタングルします。

6.2 タングルファイルから Org へのナビゲーション

タングルされると、Babel は生成ファイルにバックリンクコメントを挿入します:

# [[file:~/project/notebook.org::*Core Utilities][core-slugify]]
def slugify(text: str) -> str:
...
# core-slugify ends here

Emacs で、生成されたファイルを開き、M-x org-babel-tangle-jump-to-org を実行すると、ソースの .org ドキュメント内の正確な見出しとブロックに瞬時にジャンプします!

6.3 ヘッドレスコマンドライン / CI / Make バッチタングリング

Emacs GUI ウィンドウを開かずにファイルをタングルします(ビルドスクリプト、Makefile、継続的インテグレーションに最適):

# 単一ファイルのタングル:
emacs -Q --batch \
  --eval "(require 'org)" \
  --eval "(require 'ob-tangle)" \
  --eval '(org-babel-tangle-file "analysis.org")'

# シェルループまたは Makefile から複数ファイルのタングル:
emacs -Q --batch --eval "
  (progn
    (require 'org)
    (require 'ob-tangle)
    (dolist (file command-line-args-left)
      (with-current-buffer (find-file-noselect file)
        (org-babel-tangle))))" *.org

7. リテレート設定管理: init.org パターン

Org Babel リテレートプログラミングの人気のあるユースケースは、Emacs 設定やシステムドットファイルの管理です。

~/.emacs.d/init.el 内:

;; Bootstrap Org Babel literate configuration
(require 'org)
(require 'ob-tangle)
(org-babel-load-file (expand-file-name "config.org" user-emacs-directory))

org-babel-load-file は自動的に、config.org が最後に config.el が生成されてから変更されたかどうかを確認し、必要に応じて新しいバージョンをタングルし、結果の .el ファイルをロードします。


8. タングリングプロジェクトのためのチェックリストまとめ

  1. 明示的なタングリングパス: 明示的なファイル名を使用します(例: :tangle "src/core.py")。
  2. 可読性のために Noweb を使用: 複雑なアルゴリズムを論理的なステップ(<<init-weights>>、<<forward-pass>>、<<backprop>>)に分割して、読者のために説明が自然に流れるようにします。
  3. インデントを保持: Python コードの場合、常に org-src-preserve-indentation が t であることを確認します。
  4. Make/CI で自動化: ビルドシステムにヘッドレス emacs --batch タングリングステップを含め、生成されたソースコードを Org ドキュメントと同期させます。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

関連スキル