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:— ПЕВНЕ порушення.- відсутність кінцевого newline у файлі — ПЕВНЕ порушення.
- акронім капсом усередині
=...=(напр.=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 Ім'я. - файл тангліться у КІЛЬКА різних цільових файлів, серед яких ЩОНАЙМЕНШЕ ДВА самі споживають noweb
(мають
:noweb yesна скелетному#+begin_src, напр.config.org→.env.example+parameters.php), але хоч один:noweb-ref/<<...>>-блок названо без префікса цілі (Ім'язамістьenv:Ім'я) — ПЕВНЕ порушення (див. org-babel.md, правило про префікси в multi-target 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-файлу. Якщо ціль дійсно поза деревом джерела — потрібен symlink чи інша структура каталогів, а не../абсолютний шлях (епізод:farba-override→farba-themeчерез../farba-theme/..., задачаFS-1085).- капс поза
=...=(напр.НЕ,ЛИШЕ) — потребує перевірки: якщо це виділення слова, а не акронім/скорочення, заміни на org bold (*не*), а не капс. - слово "хук"/"гак" (переклад "hook", React/Yew) — застаріле; правильний переклад — "зачіпка"
(стосується лише прози, не ідентифікаторів коду типу
use_effect_with). - рядки прози, довші за 105 символів — потребує
fill-paragraphчерезemacsclient(див. нижче).
"ПЕВНІ" знахідки завжди виправляй. "Потребує ручної перевірки" — переглянь кожну, але не всі є
помилками (напр. =rest=/=forge-yew= — леgітимні абревіатура/назва крейта, не порушення).
Крок 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 — вирівнювання
Для кожного правленого абзацу виклич реальний fill-paragraph через emacsclient --eval, а не
вирівнюй вручну (див. відповідне правило в org-babel.md). Пиши текст абзацу в new_string/при створенні
файлу одним рядком, без спроби самому вгадати переноси, — це однаково цілком визначає fill-paragraph.
Обов'язково виклич (font-lock-ensure) ПЕРЕД org-fill-paragraph: буфер, відкритий headless через
find-file-noselect, не проходить фонтифікацію, тому org-посилання [[шлях][опис]] ще не "згорнуті" до
короткого видимого тексту опис — без font-lock-ensure fill-paragraph порахує перенос по довгому
сирому тексту посилання, а не по його видимій ширині, і результат розійдеться з тим, що дає M-q у
живому інтерактивному Emacs. Приклад:
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)))
'