org-lint 是做什麼的?
檢查 org/md 檔案中散文格式規則的 lint 工具,這些規則記錄在
~/.claude/rules/org-babel.md 中。目標是
不再每次依賴手動記憶每一條規則(因而容易出現:修正了剛提到的規則
卻漏掉其他規則),而是讓文字經過明確的清單檢查。
何時使用
- 剛編輯或撰寫完
.org/.md檔案中的一段新散文段落之後(不必再手動 逐一檢查每條規則)— 在將結果呈現給使用者之前。 - 使用者明確要求時(「檢查 org 規則」、「執行 lint」,或提到
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:...]]連結。 - 出現「參數」一詞 — 需要檢查:如果指的是 Rust/Yew 元件的屬性 (
struct ...Props),正確翻譯應為「屬性」而非「參數」(舊的術語)。若不在 Yew 元件範圍內(例如函式參數、CLI 參數、REST 程序參數等),則「參數」是正確的,無須修改。 :noweb-ref 定義區塊出現在:noweb-ref 宣告區塊之前 — 需要檢查:宣告應放在前面,按檔案從上到下的閱讀順序(參見 org-babel.md)。- 多個
#+begin_src區塊有相同的:tangle 路徑,但檔案中沒有任何:noweb yes或<<...>>— 明確違規(過時的串接方式,如舊的gitignore.org— 參見 org-babel.md)。應改為一個容器區塊使用:tangle+:noweb yes,搭配<<名稱>>佔位符,以及使用:noweb-ref 名稱的區段。 - 檔案會 tangled 到多個不同的目標檔案,其中至少兩個目標本身會使用 noweb(在骨架
#+begin_src上有:noweb yes,例如config.org→.env.example+parameters.php),但至少有某個:noweb-ref/<<...>>區塊沒有加上目標前綴(只用名稱而不用env:名稱)— 明確違規(參見 org-babel.md 中關於多目標 org 檔案的前綴規則)。對於只有單一目標檔案的 org 檔案,以及那些只有一個目標使用 noweb、而其餘目標是自包含單區塊: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 檔案向下的相對路徑。若目標確實位於原始碼樹之外,應使用符號連結或重新安排目錄結構,而非../絕對路徑(案例:farba-override→farba-theme透過../farba-theme/...,任務FS-1085)。=...=外部出現全大寫(例如不、只)— 需要檢查:如果是為了強調某個詞,而非縮寫/簡稱,應改用 org 粗體 (*不*),而非全大寫。- 出現「鉤子」(hook 的翻譯,React/Yew) — 過時翻譯;正確譯法應為「掛鉤」(僅適用於散文,不影響程式碼識別符如
use_effect_with)。 - 散文行超過 105 個字元 — 需透過
emacsclient呼叫fill-paragraph(見下方)。
「明確違規」的發現必須一律修正。「需要人工確認」的項目請逐一檢查,但並非所有都是錯誤(例如 =rest=/=forge-yew= — 是合法的縮寫/crate 名稱,不違規)。
步驟 2 — 人工檢查清單(指令稿無法捕捉的部分)
- 程式碼識別符/型別/函式/變數/模組 — 以及任何程式碼片段或表達式(例如
id=1、value == 0、items())— 使用波浪號~...~。縮寫/首字母縮略詞及 crate/函式庫的「公開」名稱(含有連字號)— 使用等號=...=。限定路徑crate::Type是程式碼中的型別名稱,因此整個應放在波浪號內,即使帶有 crate 前綴。 - 縮寫/首字母縮略詞 — 在句子內部應使用小寫,僅在句首時首字母大寫(不使用全大寫),罕見的固定例外除外。
- 指向同一檔案內章節的連結 — 使用
[[*標題名稱][名稱]],不要使用斜體或純文字。指向同一目錄下檔案的連結 — 使用[[file:鄰居.org][描述]];指向其他目錄(尤其是在共同工作目錄內的其他 git 倉庫)的檔案 — 使用root:縮寫(例如[[root:code/ads-back/檔案.org::N][描述]]),而不是使用相對於當前目錄的../../../。該縮寫需在.dir-locals.el檔案中於工作目錄根目錄宣告(使用locate-dominating-file的eval形式)— 如果專案中尚不存在,請建立。 - 斜體
/.../— 僅用於專案詞彙表中的術語(後面緊跟著翻譯(/translation/));在每個檔案中首次提及該術語時,應使用[[file:.../spcfctn/glossary.org::*術語][...]]連結到對應的章節,而不是重複定義。
步驟 3 — 對齊行寬
對於每個被編輯的段落,透過 emacsclient --eval 呼叫真正的 fill-paragraph,不要手動對齊(參見 org-babel.md 中的對應規則)。撰寫段落文字時,在 new_string 中/或建立檔案時,將整個段落寫成一行,不要自行猜測斷行位置 — 因為這完全由 fill-paragraph 決定。
務必在呼叫 org-fill-paragraph 之前先執行 (font-lock-ensure):透過 find-file-noselect 以 headless 模式開啟的緩衝區不會進行字型鎖定,因此 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)))
'