Handbooks
Справочник

Поддержка языков

18 языков в двух уровнях анализа — какие расширения забирает каждый, чем жертвует обобщённый уровень и две оговорки, о которых стоит знать заранее.

--lang auto (значение по умолчанию) определяет и объединяет все языки за один проход. Называть конкретный язык почти никогда не нужно.

handbook analyze --source ~/code/polyglot-repo --work work/poly   # one pass, all languages
handbook analyze --source ~/code/repo --work work/x --lang python # or pin one

Полный уровень

Написанные вручную адаптеры: разрешение вызовов по типам, унаследованные члены, отслеживание состояния по каждому атрибуту, границы операторов.

ЯзыкРасширенияКлюч в реестреИндексируемые типы
Python.pypythonclass
TypeScript (и JavaScript).ts .tsx .js .jsx .mjs .cjstypescriptclass interface enum alias
Go.gogostruct interface alias other
Rust.rsruststruct enum trait alias other
Java.javajavaclass interface enum record other
C#.cscsharpclass interface struct record enum other
C/C++.c .h .cpp .cc .cxx .c++ .hpp .hh .hxxcppclass struct enum alias other
Ruby.rb .rake .gemspecrubyclass other
PHP.php .phtmlphpclass interface enum trait
Swift.swiftswiftclass struct enum interface alias
Dart.dartdartclass enum trait alias other
Solidity.solsolidityclass interface struct enum other
Shell.sh .bashshell

Последний столбец — это AdapterCapabilities.typeKinds: то, что адаптер действительно находит, а не то, к чему он стремится. В Shell нет объявлений типов, поэтому в той строке — положительное утверждение, а не пробел. Константы, переменные и макросы не индексируются ни в одном языке.

У JavaScript нет отдельного адаптера

Его покрывает адаптер TypeScript. Выбирать нечего — .js, .jsx, .mjs и .cjs забираются автоматически.

Один адаптер для семейства C, а не два

Грамматика cpp справляется и с C; грамматика c не справляется с C++ вовсе. Поэтому один адаптер забирает оба языка, и для чисто сишного проекта правильно указывать --lang cpp.

Обобщённый уровень

Один движок, управляемый конфигурацией, и одна декларативная спецификация на язык. Инвентаризация файлов и функций точна; отношения вызовов — по мере возможности.

ЯзыкРасширенияКлюч в реестре
Kotlin.kt .ktskotlin
Scala.scala .scscala
Zig.zigzig
Objective-C.mobjc
OCaml.mlocaml

Руководство, чей анализ смешивает уровни, говорит об этом в своём обзоре. О том, что именно деградирует, см. Достоверность анализа.

Две оговорки, названные до того, как вы на них наткнётесь

Swift обрушивает процесс на V8 ≥ 13

Поставляемая грамматика Swift вызывает фатальный обвал по нехватке памяти, как только V8 поднимает WASM-модуль на следующий уровень компиляции — замерено фатально 5 раз из 5 на Node 24, нормально на Node 21, и это единственная такая грамматика из девятнадцати.

На таком рантайме адаптер отказывается ещё на этапе обнаружения и называет средство исправления, вместо того чтобы уронить весь ваш запуск:

node --liftoff-only $(which handbook) analyze --source ~/code/ios-app --work work/ios

--liftoff-only пропускает ту самую компиляцию с повышением уровня, которая это запускает. Собственный набор тестов репозитория передаёт этот же флаг ровно по этой причине.

Shell-скрипты, содержащие case, пропускаются

Внешний сканер зафиксированной грамматики bash импортирует env.isalpha, которого динамический компоновщик web-tree-sitter@0.25.10 не предоставляет, поэтому parse() бросает исключение на любом операторе case — и оставляет парсер отравленным, так что адаптер его отбрасывает и идёт дальше.

Это серьёзнее, чем звучит

case вездесущ в shell. Замерено на nvm: пропущены все 6 файлов и все 122 функции. Shell числится в полном уровне, потому что адаптер полного уровня, но считайте покрытие shell частичным, пока эта грамматика не будет исправлена в апстриме.

Лог сканирования говорит об этом прямо и называет причину:

[scan] shell: 5 file(s) the grammar could not parse — the pinned bash grammar throws on
`case`, which most real scripts use; their functions are absent from this graph, not
merely unresolved — install.sh, nvm.sh, …

Каждый такой файл вдобавок назван поимённо в phase1/scan-coverage.json с reason: "unparsable" и не попадает в scannedFiles графа — так что это видимый пробел, а не молчаливый.

Что пропускается везде

Каждый адаптер соблюдает один общий список пропуска:

.git  .hg  .svn  node_modules  vendor  target  build  dist  out
__pycache__  .mypy_cache  .pytest_cache  .ruff_cache  .tox
venv  .venv  env  .env  site-packages  .idea  .vscode  .handbook-patches

Если результат вашей сборки лежит где-то ещё, направьте --source на настоящий корень исходников, а не на корень репозитория.

Когда один файл мог бы принадлежать двум языкам

Обнаружение запускает адаптеры в фиксированном порядке, и файл остаётся за тем, кто забрал его первым; при сопоставлении расширений предпочтение отдаётся самому длинному совпадению. На практике это имеет значение только для .h (забирает адаптер C/C++) и .m (Objective-C).

Как прочитать, что на самом деле было просканировано

handbook analyze --source ~/code/repo --work work/x -v
# [scan] auto root=/Users/me/code/repo
# [scan] typescript: 284 files
# [scan] python: 96 files
# [scan] shell: 12 files
# [build] functions=3187 kept=9042 dropped=611
jq '.metadata.languages | keys' work/x/phase1/graph.json      # which tiers are in play
jq '.metadata.byCategory' work/x/phase1/dropped-calls.json    # what could not be resolved
jq '.metadata.byReason, .files' work/x/phase1/scan-coverage.json  # what could not be parsed at all

Добавление языка

Добавление языка обобщённого уровня — это декларативная спецификация, а не парсер, и она не требует новой зависимости, потому что грамматики уже поставляются. См. Добавление языка.

На этой странице