2025, Oct 22 14:34

Python в Neovim: почему свёртка Treesitter не применяется и как исправить

В Neovim свёртка по выражению Treesitter для Python не применяется при открытии файла. Решение: запускать zx через BufReadPost и vim.schedule. Автоприменение.

Файлы Python открываются, но свёртки не применяются, пока вы вручную не нажмёте zx. При этом с теми же настройками всё работает для свёртки по отступам или в других типах файлов, например Lua. Если вы используете основанную на Treesitter свёртку выражениями в Neovim и сталкиваетесь с проблемой только в Python, вот почему так происходит и как это исправить.

Минимальная конфигурация, на которой проявляется проблема

Конфигурация ниже включает свёртку на основе выражения через Treesitter. С ней файлы Python загружаются полностью развёрнутыми, пока вы не запустите сворачивание вручную.

local o = vim.opt
o.foldcolumn = "1"
o.foldlevel = 1
o.foldtext = ""
o.foldmethod = "expr"
o.foldexpr = "nvim_treesitter#foldexpr()"

Что происходит

Выражение свёртки nvim_treesitter#foldexpr() зависит от синтаксического дерева Treesitter. При первом открытии буфера Python парсер и его дерево могут быть ещё не готовы. Поскольку выражение выполняется до появления дерева, сворачивать нечего, и буфер выглядит развёрнутым. Позже нажатие zx срабатывает, потому что к тому моменту дерево уже создано.

Такое поведение связано именно с моментом инициализации Treesitter. Парсер установлен и работает исправно, нет конфликтующих автокоманд, специфичных для Python, и нет дополнительных плагинов для Python. Разница, заметная между Python и, например, Lua или свёрткой по отступам, объясняется тем, когда становится доступно синтаксическое дерево.

Решение: применять свёртки после инициализации Treesitter

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

-- группа гарантирует единый набор правил, который можно заменить
local grp = vim.api.nvim_create_augroup("ts_fold_apply_py", { clear = true })
vim.api.nvim_create_autocmd("BufReadPost", {
  pattern = "*.py",
  desc = "Reapply folds for Python after Treesitter is ready (scheduled)",
  callback = function()
    vim.schedule(function()
      vim.cmd("normal! zx")
    end)
  end,
  group = grp,
})

Почему это важно

Предсказуемая начальная свёртка делает вид при открытии файла согласованным с вашими настройками. Вам не нужно лишнее нажатие клавиш, и свёртка на основе выражений сразу отражает структуру Treesitter — в том числе в больших файлах Python, где вы ожидаете получить содержательные свёртки «из коробки».

Итоги

Если свёртка выражениями Treesitter не применяется автоматически в буферах Python, отложите обновление свёрток до завершения работы парсера. Используйте BufReadPost и vim.schedule, чтобы выполнить zx, когда цикл событий простаивает. С этой настройкой ваши заданные свёртки будут применяться сразу, как только появится синтаксическое дерево.

Статья основана на вопросе на StackOverflow от Kyle F. Hartzenberg и ответе от Kyle F. Hartzenberg.