Поддержка языков
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 | .py | python | class |
| TypeScript (и JavaScript) | .ts .tsx .js .jsx .mjs .cjs | typescript | class interface enum alias |
| Go | .go | go | struct interface alias other |
| Rust | .rs | rust | struct enum trait alias other |
| Java | .java | java | class interface enum record other |
| C# | .cs | csharp | class interface struct record enum other |
| C/C++ | .c .h .cpp .cc .cxx .c++ .hpp .hh .hxx | cpp | class struct enum alias other |
| Ruby | .rb .rake .gemspec | ruby | class other |
| PHP | .php .phtml | php | class interface enum trait |
| Swift | .swift | swift | class struct enum interface alias |
| Dart | .dart | dart | class enum trait alias other |
| Solidity | .sol | solidity | class interface struct enum other |
| Shell | .sh .bash | shell | — |
Последний столбец — это AdapterCapabilities.typeKinds: то, что адаптер действительно
находит, а не то, к чему он стремится. В Shell нет объявлений типов, поэтому — в той строке —
положительное утверждение, а не пробел. Константы, переменные и макросы не индексируются ни в
одном языке.
У JavaScript нет отдельного адаптера
Его покрывает адаптер TypeScript. Выбирать нечего — .js, .jsx, .mjs и .cjs забираются автоматически.
Один адаптер для семейства C, а не два
Грамматика cpp справляется и с C; грамматика c не справляется с C++ вовсе. Поэтому один адаптер забирает
оба языка, и для чисто сишного проекта правильно указывать --lang cpp.
Обобщённый уровень
Один движок, управляемый конфигурацией, и одна декларативная спецификация на язык. Инвентаризация файлов и функций точна; отношения вызовов — по мере возможности.
| Язык | Расширения | Ключ в реестре |
|---|---|---|
| Kotlin | .kt .kts | kotlin |
| Scala | .scala .sc | scala |
| Zig | .zig | zig |
| Objective-C | .m | objc |
| OCaml | .ml | ocaml |
Руководство, чей анализ смешивает уровни, говорит об этом в своём обзоре. О том, что именно деградирует, см. Достоверность анализа.
Две оговорки, названные до того, как вы на них наткнётесь
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=611jq '.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Добавление языка
Добавление языка обобщённого уровня — это декларативная спецификация, а не парсер, и она не требует новой зависимости, потому что грамматики уже поставляются. См. Добавление языка.