Про Vim — Neovim (Lua) PHP IDE
Послесловие
По большому счету единственной незакрытой темой в рубрике «Про Vim» осталась тема об альтернативе Conqueror of Completion для Neovim в Lua исполнении. Причиной перерыва между данной и предыдущей статьей в рубрике стало отсутствие необходимости в этой самой альтернативе. Ну или вернее причина может быть и была: во-первых — что бы удовлетворить любопытство, во-вторых — CoC действительно кажется громоздким и достаточно сложным внутри. Вариант на Lua мне бы дался легче, если бы захотелось что-то в нем подшаманить. Наверное.
С другой стороны, функционально связка CoC + PhpActor относительно плотно покрывает все современные сценарии разработки в не очень навороченных проектах. CoC прекрасно различает вставки HTML, CSS, JavaScript, SQL прямо в одном PHP файле, PhpActor ловко орудует с autoload путями, отлавливает хитрые ошибки, достаточно резво подсказывает сигнатуры, ищет референсы и дает свободно скакать по декларациям.
Тем не менее последний проект, над которым мне приходится трудиться в последнее время, явно не входит в число простых или даже средних. Это достаточно серьезная и разветвленная кодовая база с достаточным количеством зависимостей и, прямо скажем, непростой структурой. И тут некоторые функции начали отваливаться и подтормаживать.
В первую очередь стала спотыкаться диагностика. Мало того что языковой сервер стал подъедать память, иногда он просто стал вылетать без видимых на то оснований.
Во-вторых, при быстром перемещении по коду и вовсе редактор стал вставлять какие-то непонятные символы в исходный код. Что просто никак не совместимо с профессиональным производством.
Сразу необходимо сказать, что вариант с Neovim (Lua) не решил всех проблем. При этом появились другие. Как выяснилось позже, виноват в этом был скорее не Vim и CoC, а сам LSP PhpActor. В деталях выяснить причины такой деградации пока выяснить не удается, и следует также сразу отметить, что и замена LSP на варианты Intelephence или Psalm не явилось выходом из ситуации.
Psalm, как оказалось, годен исключительно в качестве дополнительной диагностической утилиты, с задачей которой для меня лично хватает с головой PHP Stan. А Intelephence, для активации многих необходимых функций требует покупную лицензию, которую из нашей страны приобрести решительно невозможно. Да и подумалось не нужно, раз так.
А подумалось, что можно восполнить недостающие функции отдельными плагинами и приспособлениями. Так вот о них подробнее.
Языковой сервер
Intelephence, по крайней мере в урезанном виде, показывает себя пока лучше, чем PhpActor в части производительности и ресурсоемкости. Устанавливается сервер при наличии npm тривиально:
npm i intelephense -g
И добавляется в Neovim посредством специального плагина nvim-lspconfig. И вспомогательного cmp-nvim-lsp, который подмешивает результаты автодополнения к результатам родного omnifunc
. В моем случае настройка языковых серверов выделена в репозитории в отдельный файл ~/.config/nvim/lua/plugins/nvim-lspconfig.lua
, на содержимом которого, наверное, не стоит подробно останавливаться. Приведу здесь только кусок, который касается непосредственно Intelephence:
-- import lspconfig plugin safely
local lspconfig_status, lspconfig = pcall(require, "lspconfig")
if not lspconfig_status then
return
end
-- import cmp-nvim-lsp plugin safely
local cmp_nvim_lsp_status, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
if not cmp_nvim_lsp_status then
return
end
-- enable keybinds only for when lsp server available
local on_attach = require("plugins.handlers").on_attach
-- used to enable autocompletion (assign to every lsp server config)
local capabilities = cmp_nvim_lsp.default_capabilities()
...
lspconfig.intelephense.setup({
capabilities = capabilities,
filetypes = { "php" },
on_attach = on_attach,
settings = {
intelephense = {
files = {
maxSize = 5000000,
},
},
},
on_init = function(client)
client.server_capabilities.documentFormattingProvider = false
end,
})
...
Здесь последовательно говорится о том, что нужно подтянуть два плагина: lspconfig
— для активации сервера, cmp_nvim_lsp
— для интеграции автодополнения. Затем идет инструкция добавления дополнительных горячих клавиш, только в случае если мы имеем дело с файлом, для которого требуется языковой сервер. Эти сочетания вынесены в отдельный файл .config/nvim/lua/plugins/handlers.lua
. (Не спрашивайте зачем, тоже где-то подсек). Объявляются для удобства две локальные переменные on_attach
и capabilities
. Ну, и, собственно, подключается LSP сервер, если тип файла совпадает с указанным.
Дополнительные настройки и отключение форматирования это мои текущие настройки. Для форматирования, например, у я использую PHP Code Style Fixer, о котором ниже.
Рефакторинг
Главными функциями, недостающими для полноценной работы с кодом в урезанном Intelephence, является отсутствие поддержки переименования и навигации по декларациям. И если второе полноценно решается альтернативными средствами навигации по коду, то в части переименования переменных и выделения каких-то блоков кода в функции и литералы, и наоборот их внедрение обратно в код я столкнулся с определенными проблемами.
Во-первых, единственный плагин, который удалось отыскать для рефакторинга, существенно устарел, и вряд ли он подходит для современного кода полностью. После добавления его, например, при помощи Packer, появляется несколько функций частично закрывающих мои потребности.
...
use("adoy/vim-php-refactoring-toolbox")
...
В целом, в простых ситуациях, неудобств от устаревания пока не обнаружено, и наиболее частые применения функционируют. Однако, всё же неприятно осознавать, что проект мертвый и что следующая версия языка может сделать плагин совершенно бесполезным.
Со стандартной раскладкой плагина можно соглашаться или нет. Я был не согласен, так как пытаюсь подобрать свой семантический подход к сочетаниям клавиш в целом. Что бы отключить родную схему следует установить переменную vim.g.vim_php_refactoring_use_default_mapping
в значение 0
где-нибудь при запуске. Мой вариант находится в файле ~/.config/nvim/lua/plugins/php-refactoring-toolbox.lua
и вызывается из ~/config/nvim/ftplugin/php.lua
.
navigator.lua
По дороге пришлось поменять и GUI плагин навигации и автодополнения, поскольку lspsaga
также перестал поддерживаться и в целом доставлял некие неудобства постоянной сменой API. В отличии от функций рефакторинга тут специфика языка не сильно влияет и выбора плагинов чуть больше. Остановился на ray-x/navigator.lua, который помимо, собственно передвижения по тегам, интегрируются с nvim-lspconfig
. В вызов Packer добавляем:
...
use("ray-x/guihua.lua")
use({
"ray-x/navigator.lua",
requires = {
{ "ray-x/guihua.lua", run = "cd lua/fzy && make" },
{ "neovim/nvim-lspconfig" },
},
})
...
Затем, важно отключить конфигурацию LSP ранее настроенного с помощью nvim-lspconfig
сервера, так как плагин пытается самостоятельно настроить доступ к серверу. Что в моей конфигурации находится в ~/.config/nvim/lua/plugins/navigator.lua
-- import x-ray/navigator safely
local nav_status, nav = pcall(require, "navigator")
if not nav_status then
return
end
nav.setup({
lsp = {
disable_lsp = {
"lua_ls",
"intelephense",
},
},
})
Далее можно было упомянуть всякие дополнительные штучки-дрючки, украшательства, но они общие для всех языковых серверов и немного мимо темы данной статьи. Поэтому к услугам читателя, собственно репозиторий, где это все настроено и работает.
Подсветка синтаксиса
Что касается подсветки, необходимый и достаточный функционал обеспечивается здесь глобально плагином к tree-sitter и не нуждается в дополнительной настройке. Для чего в файле ~/.config/nvim/lua/plugins.lua
добавлена инструкция:
...
-- tree sitter
use({
"nvim-treesitter/nvim-treesitter",
run = function()
require("nvim-treesitter.install").update({ with_sync = true })
end,
})
...
Форматирование
Единственное, о чем следует упомянуть напоследок это функция форматирования, которую я в Intelephence отключил. Во-первых, она почему-то не настраивается совсем никак, или может я не нашел как это сделать. Во-вторых, в моих текущих проектах используется PHP Code Style Fixer, который работает отдельно, и который в общем-то не надо во что бы то ни стало встраивать в редактор. Но, если у вас похожая ситуация, то следующий плагин вам тоже может пригодиться.
Это тоже брошенный проект, тем не менее, альтернатив ему я пока не нашел. А скорее пока и не искал. Называется он null-ls и на самом деле, тоже является неким аналогом Lspconfig, но работает с отдельными функциями, такими как диагностика, форматирование и действия с кодом. На него тоже можно накрутить всяких других полезностей, которые не входят в состав основного языкового сервера. А в частности форматирование. Добавим к плагинам:
...
-- formatting
use({
"jose-elias-alvarez/null-ls.nvim",
config = function()
require("null-ls").setup()
end,
requires = { "nvim-lua/plenary.nvim" },
})
...
И вызовем из ~/.config/nvim/init.lua
файл настроек ~/.config/nvim/lua/plugins/null-ls.lua
следующего содержания:
-- import null-ls plugin safely
local setup, null_ls = pcall(require, "null-ls")
if not setup then
return
end
-- for conciseness
local formatting = null_ls.builtins.formatting -- to setup formatters
local diagnostics = null_ls.builtins.diagnostics -- to setup linters
null_ls.setup({
sources = {
formatting.stylua,
formatting.google_java_format,
diagnostics.eslint,
formatting.phpcsfixer.with({
command = "./tools/php-cs-fixer/vendor/bin/php-cs-fixer"
}),
},
timeout = 100000,
timeout_ms = 100000,
debug = false,
})