Pull to refresh
155.28
AvitoTech
У нас живут ваши объявления

Чего ждать от NeoVim: особенности редактора

Reading time 9 min
Views 40K

Привет! Я Антон Губарев, инженер команды Platform as a Service (PaaS) в Авито. Долгое время я пользовался IDE от JetBrains, затем пересел на VS Code. Последние несколько лет работаю с кодом только в NeoVim — адаптировал его под себя и перестал использовать другие IDE.

Я не фанат ни одного из редакторов или IDE и не буду пытаться убедить вас перейти с привычной платформы на NeoVim. Я только расскажу, к чему готовиться человеку, который привык работать в JetBrains или VS Code и планирует попробовать NeoVim.

NeoVim – это ответвление от Vim, которое привносит некоторые важные преимущества. Я для себя остановился именно на NeoVim, и поэтому большая часть статьи именно в этом контексте. Однако много чего из материала можно отнести к Vim.

NeoVim не работает «из коробки», в нём нужно прописывать конфигурацию, биндить комбинации клавиш, собирать подходящие плагины. Но в итоге получается идеальный редактор: в нём всё устроено так, как нужно конкретному разработчику. 

В основе статьи — моё выступление на Golang Evrone Meetup. В видео вы найдёте больше деталей о плагинах, работе команд и комбинаций. А все исходники есть в репозитории на GitHub.

Буферы, окна и табы вместо привычных вкладок

Когда новый пользователь открывает NeoVim, первое, что может запутать, — отсутствие вкладок. В большинстве редакторов каждый файл находится в отдельной вкладке внутри одного окна IDE. В NeoVim вместо этого есть три сущности: буферы, окна и табы. 

Буфер — отдельный открытый файл. Один и тот же файл можно просматривать и редактировать в нескольких буферах. При этом все изменения в одном из них будут автоматически и мгновенно отображаться в остальных.

Буферы можно объединять в окна, а окна — в табы. Можно открыть несколько табов NeoVim, внутри каждого из них — несколько окон, а в каждом окне — разные комбинации файлов в буферах.

Файлы в буферах, буферы в окнах, окна в табах
Файлы в буферах, буферы в окнах, окна в табах

Чтобы начать пользоваться этим, нужно сначала постичь дзен NeoVim. У меня пока не получилось, поэтому использую специальный плагин — о нём рассказываю ниже.

Управление без мышки, только на клавиатуре

В NeoVim нет поддержки мыши, работать приходится только на клавиатуре. Причём даже навигация по коду выполняется не привычными стрелками, а клавишами HJKL. Когда создавался Vi (прародитель NeoVim) клавиатура его автора Билла Джоя выглядела вот так:

Клавиатура Билла Джоя
Клавиатура Билла Джоя

Преимущества управления в NeoVim поймут те, кто владеет десятипальцевым методом печати. Смысл в том, что большинство комбинаций находятся на буквенных и цифровых сочетаниях,  поэтому во время работы почти не нужно смещать кисти.  

Поначалу может быть сложно с таким управлением, но со временем привыкаешь. Я довёл навыки до автоматизма за две недели и увидел разницу в скорости работы с мышью и без.

Команды NeoVim

В NeoVim есть команды для практически любой ситуации, которая возникает во время работы. Их можно комбинировать и вызывать подряд. Иногда это выглядит довольно сложно, но в командах получается быстро разобраться и запомнить, что за чем следует. 

Допустим, нужно найти все foo в коде и заменить их на bar. При этом перед каждой заменой спрашивать подтверждение. Команда для этого:

:s/foo/bar/gc

Если нужно искать в диапазоне от 3-й до 10-й строки, а подтверждения не требуется, то получим:

:3,10s/foo/bar/g

Список базовых команд можно посмотреть в документации NeoVim или набрать :h, чтобы вызвать подсказку. 

Для постоянно повторяющихся действий можно биндить команды или даже их последовательности — привязывать к конкретной клавише или комбинации. Это сильно экономит время во время работы.

Комбинации клавиш для быстрых действий

Некоторые действия с кодом закреплены за конкретными комбинациями клавиш. Для работы не надо запоминать их все. Достаточно 30–40 штук — они чаще всего используются в повседневных задачах. Например, я редактирую код с помощью комбинаций:

  • dd — удалить строку;

  • a A i I o O — начать редактирование (с конца строки или сначала, со следующего символа или предыдущего);

  • w b — перемещать по словам;

  • gg G — перейти в начало файла;

  • — переместить курсор на метку х;

  • <n>С или <n>99 — перейти на строку номер <n>;

  • diw — удалить слово под курсором;

  • de — удалить символы с текущего до конца слова, включая пробел;

  • [{}] — переместиться назад по тексту к открывающей скобке текущего блока кода;

  • % — перейти от открывающей скобки к закрывающей, при повторном нажатии — перейти обратно;

  • u U — изменить регистр выделенных символов на нижний.

Конфигурация: настроить можно почти всё

Конфигурируемость — это одно из главных преимуществ, которое делает NeoVim удобным и полезным.

Обычно конфигурация в редакторах кода описывается в формате JSON или XML. Их возможности ограничены синтаксисом, поэтому тонкая настройка не всегда удаётся. Самые популярные IDE вроде VS Code или JetBrains настраиваются через графический интерфейс — в нём ещё меньше возможностей для настроек, только то, что допускают разработчики софта.

В NeoVim все настройки и плагины пишутся на языке программирования Lua (есть поддержка VimScript). Это полноценный кодинг, а не просто выбор опций: можно задать условия, при которых будет работать та или иная версия конфигурации. Например, настроить новую комбинацию клавиш так, чтобы она закрывала терминал, только если процесс в нем завершен.

local on_attach = function(client, bufnr)
	local function buf_set_keymap(...)
		vim.api.nvim_buf_set_keymap(bufnr, ...)
	end
	-- Enable completion triggered by <c-x><c-o>
	vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')

	-- Mappings.
	buf_set_keymap("n", "gD", "<cmd>lua vim.lsp.buf.declaration()<CR>", opts)
	buf_set_keymap("n", "gd", "<cmd>Telescope lsp_definitions<CR>", opts)
	buf_set_keymap("n", "K", "<cmd>lua vim.lsp.buf.hover()<CR>", opts)
	buf_set_keymap("n", "gi", "<cmd>Telescope lsp_implementations<CR>", opts)
	buf_set_keymap("n", "<C-k>", "<cmd>lua vim.lsp.buf.signature_help()<CR>", opts)
	buf_set_keymap("n", "<leader>D", "<cmd>Telescope lsp_type_definitions<CR>", opts)
	buf_set_keymap("n", "<leader>rn", "<cmd>lua vim.lsp.buf.rename()<CR>", opts)
	buf_set_keymap("n", "<leader>ca", "<cmd>lua vim.lsp.buf.code_action()<CR>", opts)
	buf_set_keymap("n", "gr", "<cmd>Telescope lsp_references<CR>", opts)

	if client.resolved_capabilities.document_formatting then
		vim.cmd([[
			augroup formatting
				autocmd! * <buffer>
				autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()
			augroup END
		]])
	end
end

  -- Setup lspconfig.
local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())

-- Replace <YOUR_LSP_SERVER> with each lsp server you've enabled.
require('lspconfig')['gopls'].setup {
	capabilities = capabilities,
	on_attach = on_attach
}
require('lspconfig')['pyright'].setup {
	capabilities = capabilities,
	on_attach = on_attach
}

У конфигурации на Lua есть один большой минус: если в коде ошибка, то NeoVim может не открыться. Я редактирую код конфигурации в одном NeoVim. Потом открываю второй в другой сессии и проверяю в нём, что всё работает корректно. Если NeoVim не запускается, у меня остаётся рабочая версия без изменений в конфигурации — в ней исправляю ошибки. Иначе придётся зайти через другой редактор, чтобы откатить изменения.

Свою конфигурацию NeoVim я написал сам, но использовал полезные фишки из дотфайлов разработчиков на GitHub:

Если не хотите заниматься настройкой и разбираться с Lua, используйте одну из готовых сборок. Из наиболее распространённых выделю:

  • AstroVim

  • SpaceVim

  • LunarVim

  • NvChad

В готовых конфигурациях уже есть всё необходимое, чтобы пользоваться NeoVim как обычной IDE. Но по-настоящему вы ощутите гибкость этого редактора, когда настроите его и подберёте плагины самостоятельно.

LSP: протокол языкового сервера

Первое, что нужно сделать при настройке NeoVim, — установить языковой сервер, который поддерживает Language server protocol (LSP). Это протокол, который позволяет удобно работать с кодом, независимо от вашего языка программирования. Почитайте на сайте langserver.org, как он работает, и посмотрите список доступных языков. 

Для NeoVim есть плагины, которые позволяют интегрировать работу с языковым сервером. Это поможет видеть в коде:

  • список ошибок — например, некорректные названия переменных;

  • предупреждения о коде, который никогда не сработает;

  • действия с кодом — предложение импортировать пакеты, которых не хватает для корректной работы.

Ещё есть навигация по коду, поддержка автокомплита и другие функции. Я не нашёл того, чего не хватало бы по сравнению с другими IDE. Кстати, в VS Code используется тот же LSP для интеграции с вашим языковым сервером.

Плагины: превращаем редактор кода в полноценную IDE

Установить плагины можно через несколько разных менеджеров, я пользуюсь VimPlug. Он минималистичный и довольно простой, хотя и требует вручную описать все плагины, которые нужно добавить в сборку NeoVim. Зато настройка плагинов получается более гибкой: можно указать конкретные версии или выполнить дополнительные команды, например make. Вот пример как выглядит мой набор плагинов:

call plug#begin()
	" Language
	Plug 'neovim/nvim-lspconfig'
	Plug 'hrsh7th/cmp-nvim-lsp'
	Plug 'hrsh7th/cmp-buffer'
	Plug 'hrsh7th/cmp-path'
	Plug 'hrsh7th/cmp-cmdline'
	Plug 'hrsh7th/nvim-cmp'
	Plug 'j-hui/fidget.nvim'
	Plug 'L3MON4D3/LuaSnip'
	Plug 'saadparwaiz1/cmp_luasnip'
	Plug 'rafamadriz/friendly-snippets'
	Plug 'ray-x/lsp_signature.nvim'
	
	" Debug and test
	Plug 'mfussenegger/nvim-dap'
	Plug 'leoluz/nvim-dap-go'
	Plug 'rcarriga/nvim-dap-ui'
	Plug 'nvim-neotest/neotest'
	Plug 'nvim-neotest/neotest-go'

	" Base
	Plug 'folke/todo-comments.nvim'
	Plug 'akinsho/toggleterm.nvim'
	Plug 'antoinemadec/FixCursorHold.nvim'

	" View 
	Plug 'nvim-lualine/lualine.nvim'
	Plug 'kyazdani42/nvim-web-devicons'
	Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
	Plug 'lukas-reineke/indent-blankline.nvim'
	Plug 'Mofiqul/vscode.nvim'
	
	" Navigation
	Plug 'kyazdani42/nvim-tree.lua'
	Plug 'nvim-lua/plenary.nvim'
	Plug 'nvim-telescope/telescope.nvim'
	Plug 'nvim-telescope/telescope-fzf-native.nvim', {'do': 'make'}
	Plug 'karb94/neoscroll.nvim'
   Plug 'akinsho/bufferline.nvim'
	Plug 'preservim/tagbar'

	" Git
	Plug 'ThePrimeagen/git-worktree.nvim'
	Plug 'TimUntersberger/neogit'
	Plug 'lewis6991/gitsigns.nvim'

	" Edit
	Plug 'tpope/vim-surround'
	Plug 'windwp/nvim-autopairs'
	Plug 'numToStr/Comment.nvim'

	" Misc
	Plug 'renerocksai/telekasten.nvim'
call plug#end()

Когда список готов, нужно вызвать команду :PlugInstall — она устанавливает или обновляет плагины.

В Сети есть подборки, в которых собраны десятки тысяч плагинов на любой вкус и задачи, например эти три: 

Среди всего множества есть плагины, которые я рекомендую установить всем, кто только начинает использовать NeoVim.

Автокомплит. Функция есть в нескольких плагинах — выбирайте, какой понравится: Nvim-cmp, coc.nvim, YouCompleteMe. Они различаются технологиями, реализацией и UI, но работают примерно одинаково. 

VimGo. Самый популярный на GitHub плагин для Go. Решает характерные задачи: установка, запуск тестов, дебаггеров, добавление импорта, запуск линтера. Я его не использую — сам накрутил все нужные мне возможности по отдельности. Но это потребовало больше времени.

Vim-test. Плагин, который запускает тест под курсором или весь файл, поддерживает добавление аргументов после go test.

Nvim-tree. Плагин для навигации, в котором есть всё необходимое: поиск, копирование, вставка, предпросмотр, возможность конфигурировать эти и другие функции. 

Telescope. Инструмент для работы со списками файлов — я не нашёл ему аналогов ни в одной IDE. Поддерживает сортировки, гибкий поиск внутри файла (live grep, rip grep), fuzzy-поиск и предпросмотр. В официальном вики плагина больше 60 расширений к нему.

Сниппеты. Есть много вариантов плагинов, которые добавляют привычные подсказки при наборе кода. Самые известные — luasnip, vi msnippets, friendly snippets.

Bufferline. Для тех, кто не проникся идеей буферов и табов, есть плагин, который превращает их в простые вкладки. С ним можно сортировать, перемещать и группировать вкладки. Ещё просматривать число ошибок и предупреждений в каждой из них. 

Git. Я нашёл для себя удобные варианты для разных задач: 

  • gitsigns показывает исправленные строки и blame изменений. Через вызов горячих клавиш можно посмотреть, что конкретно поменяли в коде. 

  • diffview — плагин для сравнения версий кода. Умеет открывать диффы для разного числа коммитов через ссылки, хеши и названия веток. Чтобы не вбивать их вручную, придётся дорабатывать и настраивать под себя команды.

Плагины выше делают NeoVim похожим на VS Code. Но есть такие, которые добавляют новую функциональность — я не встречал её ни в одной IDE. Ниже пример работы плагина surround, который быстрыми сочетаниями клавиш обрамляет одно слово или сочетание под курсором в другие символы или набор символов:

Было

"Hello world!"

{ Hello } world!

Hello world! [Hello]

Стало

'Hello world!'

({ Hello } world!)

world!

Сочетание

cs"'

yss)

ysiw

Почему я пользуюсь NeoVim и кому ещё он подойдёт

Для меня важное преимущество NeoVim перед другими IDE и редакторами — возможность запрограммировать на Lua любой плагин под свои узкие задачи. Например, я написал плагин для Curl, чтобы собирать и выполнять HTTP запросы из NeoVim не выходя из редактора. Храню в каждом проекте файл с самыми частыми запросами, а сам файл в глобальном gitignore.

local utils = require("selfext.utils")

function execCurl()
	local command = utils.getCurrentParagraph()

	local Terminal = require('toggleterm.terminal').Terminal
	local run = Terminal:new({
		cmd = command,
		hidden = true,
		direction = "float",
		close_on_exit = false,
	   on_open = function(term)
		  vim.api.nvim_buf_set_keymap(term.bufnr, "t", "q", "<cmd>close<CR>", {noremap = true, silent = true})
		end,
	})

	run:toggle()

end

utils.keymap("n", "<S-e>", "<cmd>lua execCurl()<CR>")

Плагин работает просто: 

  • берёт строку под курсором;

  • в цикле ищет первую после курсора пустую строку;

  • создаёт буфер из n строк от той, где стоит курсор, до пустой;

  • копирует буфер в терминал с командой на выполнение.

При этом не нужно совершать лишних движений — я сразу получаю в терминале результат работы кода, не использую Postman или подобные решения и мне не нужно перекладывать руку на мышь. Дальше с выводом терминала можно работать как с обычным буфером: искать, копировать или грепать. 

Я советую разобраться в языке Lua всем: он довольно простой, учится быстро, при этом применяется во многих других продуктах, кроме NeoVim. Например, на нём можно расширять возможности nginx.

Советую попробовать NeoVim, если: 

  • вам не хватает возможностей IDE;

  • вы хотите больше работать без мыши;

  • готовы конфигурировать редактор под себя;

  • вам требуется больше интеграции с консольными инструментами.

Не стоит пробовать NeoVim, если:

  • у вас нет навыка десятипальцевой печати;

  • вы предпочитаете, чтобы всё работало «из коробки»;

  • вам достаточно функций текущей IDE;

  • вы хотите работать только в графическом интерфейсе.

Tags:
Hubs:
+20
Comments 41
Comments Comments 41

Articles

Information

Website
avito.tech
Registered
Founded
2007
Employees
5,001–10,000 employees
Location
Россия