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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет! Я Антон Губарев, инженер команды 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>;

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

  • 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:

  • mathiasbynens/dotfiles

  • ThePrimeagen/.dotfiles

  • zanshin/dotfiles

  • caarlos0/dotfiles.fish

  • samoshkin/dotfiles

Если не хотите заниматься настройкой и разбираться с 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 — она устанавливает или обновляет плагины.

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

  • Vim Awesome

  • Awesome Neovim

  • Neovimcraft

Среди всего множества есть плагины, которые я рекомендую установить всем, кто только начинает использовать 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;

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

Источник: https://habr.com/ru/company/avito/blog/682962/


Интересные статьи

Интересные статьи

Привет, меня зовут Алексей Белявцев и я ETL-архитектор в X5 Group. Наши объёмы данных соизмеримы с крупнейшими международными компаниями и требуют специального ухода и содержания, что накладывает опре...
При разработке бизнес-логики приложений нужно продумать действия с множествами – с пересечением, разницей массивов или двойной разницей. Недостатки в этом алгоритме могут привести к рискам. Например, ...
История сегодня пойдёт про автосервис в Москве и его продвижении в течении 8 месяцев. Первое знакомство было ещё пару лет назад при странных обстоятельствах. Пришёл автосервис за заявками,...
Со стороны iOS разработка может казаться закрытым клубом. Для работы обязательно нужен компьютер от Apple, экосистему пристально контролирует одна компания. Изнутри тоже иногда слышны противо...
Термин “холобионт” объединяет организм хозяина и его коренных микробов (indibiome), совокупность генетического материала в системе хозяин-микроб дает более точное описание сложной системы и за сч...