Community程式設計與開發github.com

pwquint24/org-babel-literate-tangle

Org Babel 文藝程式設計與程式碼萃取(tangling)指南,涵蓋 noweb 巨集展開、批次編譯與設定管理。

org-babel-literate-tangle 是什麼?

此技能(SKILL)是一份完整的指南,專注於利用 Emacs 的 Org Babel 實作 Donald Knuth 提出的文藝程式設計(Literate Programming)方法論。其核心概念是將人類可讀的散文、架構筆記與設計原理撰寫在一個 Org 檔案中,並將可執行的程式碼區塊嵌入其中。透過此技能,使用者可以從同一個 Org 檔案「織出」(Weave)給人類閱讀的文件(如 HTML、LaTeX、PDF),以及「纏出」(Tangle)給編譯器、直譯器或部署系統使用的純淨可執行程式碼(如 .py、.sh、.c)。 此技能詳細說明了如何透過 `:tangle` 頭引數控制程式碼的萃取路徑與行為,包括設定 shebang、檔案權限、自動建立目錄等。它還介紹了 noweb 語法,允許使用者將大型原始碼檔案分解成較小、易於解釋的模組化片段,並透過 `<<block_name>>` 參考組合起來,同時保持正確的縮排(這對 Python 等對空白敏感的語言至關重要)。此外,它還涵蓋了如何從 Org 表格或標題屬性將設定值內嵌至纏出的程式碼中。 此技能支援所有 Org Babel 可執行的平台(即任何能執行 Emacs 的作業系統)。使用方法包括在 Emacs 內互動式執行(如按鍵綁定 `C-c C-v t` 纏出所有區塊,或 `C-c C-v f` 纏出指定檔案),以及透過無頭模式(`emacs --batch`)整合到 CI/CD 流程或 Makefile 中進行批次處理。它還說明了 `org-babel-tangle-jump-to-org` 功能,可從產生的原始碼檔案反向導航回 Org 來源文件。 最後,技能示範了將 Org Babel 用於管理 Emacs 設定(`init.org` 模式)或系統 dotfiles 的常見實務,使設定本身也成為可執行的文件。這是一份讓開發者將文件與程式碼同步,徹底實現「程式碼即文件」理念的進階工具指南。

相容平台~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 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

使用 Org Babel 進行文藝程式設計與程式碼纏出 (Tangling)

Org Babel 是 Donald Knuth 文藝程式設計 (Literate Programming) 概念的實作。與其撰寫帶有散落註解的程式碼,您應該撰寫以人為導向的散文、架構筆記和設計原理,並將功能性的程式碼區塊嵌入其中。

一個單一的 Org 檔案作為兩個不同產出的最終真實來源:

  1. 織出 (Weaving/人類文件): 將筆記本匯出為格式化的 HTML、LaTeX/PDF 或 Markdown 供讀者閱讀。
  2. 纏出 (Tangling/機器執行): 萃取出純淨、可執行的原始碼檔案 (.py、.sh、.c、.conf、.el),供編譯器、直譯器和部署系統使用。

1. 纏出基礎::tangle 頭引數

預設情況下,程式碼區塊不會被纏出。:tangle 系列的頭引數指定了區塊在磁碟上的萃取位置和方式。

1.1 :tangle 引數的值

值行為
:tangle no (預設)區塊不會被寫入任何檔案。(但仍可透過 <<noweb>> 包含在其他區塊中)
:tangle "path/to/file.py"將區塊內容直接寫入指定的檔案路徑 (相對於緩衝區目錄或絕對路徑)。
:tangle yes纏出到一個檔案,其名稱與 Org 檔案的基底名稱相同,並使用該語言的標準副檔名 (例如 model.org -> model.py)。

1.2 額外的纏出頭引數

  • :shebang "#!/usr/bin/env python3": 在纏出檔案的第一行,插入指定的 shebang 字串。
  • :tangle-mode (identity #o755): 設定產生檔案的檔案系統權限 (例如 #o755 讓腳本變成可執行)。
  • :no-expand: 防止變數賦值 (:var) 被內聯到纏出的程式碼中;僅寫入原始的區塊內容。
  • :mkdirp yes: 在纏出路徑中,自動建立任何不存在的父目錄。

2. 纏出範圍:區塊、子樹與緩衝區層級

纏出引數可以清晰地設定在整個專案的不同範圍:

2.1 子樹層級纏出 (推薦模式)

不同的標題可以纏出到不同的檔案或模組:

* 核心工具
: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

* 資料處理管線
: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="處理資料集。")
  parser.add_argument("--input", required=True, type=Path, help="輸入 CSV 路徑")
  parser.add_argument("--threshold", default=0.5, type=float, help="分數閾值")
  args = parser.parse_args()
#+END_SRC

#+NAME: process-data
#+BEGIN_SRC python :exports code
  print(f"從 {args.input} 載入資料...")
  # 此處為處理邏輯
#+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 遵循縮排規則:如果 <<argument-parser>> 在 def main(): 內部被縮排了 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")'

# 從 shell 迴圈或 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 設定或系統 dotfiles。

在 ~/.emacs.d/init.el 中:

;; 引導 Org Babel 文藝設定
(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.

相關技能