Про Vim — Neovim (Lua) PHP IDE

4e22aca570ca3f13e6217eceffb1b0ad.png

Послесловие

По большому счету единственной незакрытой темой в рубрике «Про 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,
})

© Habrahabr.ru