Как я отлаживаю программы в Vim
Опубликовано 2022-01-25 в: Веб-журнал Ярослава
Данный текст также доступен на других языках: English
Мне нравиться использовать отдельные инструменты для разных задач, но также мне нравится когда эти инструменты хорошо интегрируются с моими остальными инструментами. К сожалению я не видел достаточной информации в интернете насчёт возможного процесса отладки программ посредством Vim. Информация, точнее, есть, но она разбросана по всему интернету. И так я решил написать о моём процессе написания и отладки с Vim.
Многие, а может и большинство, IDE уже имеют встроенные функции отладки программ. Но также эти IDE не vim, и чаще всего они включаю в себя куча других сомнительных функций. И нет, «Vim mode» плагины не считаются, большинство из них даже половина функционала Vim'а не включают в себя.
Я обожаю vim (на самом деле использую neovim, кому какая разница), и я считаю его лучшим текстовым редактором. Я даже в полном серьёзе считаю что у Vim'а лучшее UI/UX из любой программы. Да, он далеко не самая интуитивная программа (вставьте мем про выхода из Vim'а), но выучить основные азы не так сложно как кажется зато как только их выучить сложно будет пользоваться чем-то другим. Короче говоря, Vim это не программа а образ жизни.
Проблема заключается в том, что стать продвинутым пользователем вима всё-таки занимает немалого времени. А чтобы эффективно разрабатывать программы, не достаточно просто напечатать код программы. Так же необходимо быстро перемещаться по исходным файлам, быстро исправлять самые банальные ошибки (вроде опечатках), свести к минимуму механические задачи (например с помощью автозаполнения), и конечно, отладка и профилировка.
К счастью как Vim как и Neovim уже несколько лет предоставляют функционал для интегрирования инструментов разработки и отладки с лёгкостью. Так как я пользуюсь Neovim, то соответственно некоторые моменты могут отличаться от ванильного Vim.
Инструменты разработки
В старые добрые времена для каждого редактора/IDE отдельно должны были быть разработаны плагины или компоненты. То есть, для каждой комбинации редактора и языка программирования выполнялся труд по разработке инструмента, что означала что далеко не все редакторы и не все языки имели достаточные инструменты для разработки и отладки.
К счастью, Мелкомягкие поступили совсем не по Мелкомягкому и вместо того чтобы встроить совершенно новую реализацию среды разработки для VSCode, они разработали так называемый Language Server Protocol1, и с помощью Red Hat и других выложили в опен сорс и превратили его в некий стандарт.
Если коротко говоря, то Language Server Protocol (LSP) это просто JSON RPC протокол для общения между сервером, который предоставляет функции для разработки программ, как например, автозаполнение, статический анализ, переход на определение, и т.д., и клиент, то есть, IDE или редактор, который отображает результат всех этих действий. Среди редакторов, которые поддерживают LSP, находится Vim. В Neovim'е это поддержка даже встроена.
LSP
Затем чтобы использовать LSP в neovim, необходимо включить плагин и настроить LSP сервер(а) ваш(его/их) язык(а/ов). Хорошо то, что настройка этого функционала довольно проста, так как есть официальный плагин который уже предоставляет настройки по умолчанию для многих LSP разных языков.
Я пользуюсь vim-plug чтобы управлять своими планинами, поэтому я сначала с его помощью добавил плагин lspconfig:
"...
Plug 'neovim/nvim-lspconfig'
"...
Затем я его настроил под себя, добавляя конфигурации для LSP сервера C, Go, Rust, Python, и Javascript, языки, которыми я чаще всего пользуюсь:
" LSP
set omnifunc=v:lua.vim.lsp.omnifunc
lua require('lspconfig').clangd.setup{filetypes = { "c", "cpp", "objc", "objcpp", "ch" }}
lua require('lspconfig').gopls.setup{}
lua require('lspconfig').pylsp.setup{}
lua require('lspconfig').rls.setup{}
lua require('lspconfig').tsserver.setup{}
" LSP keybinds
nmap <silent> gd <cmd>lua vim.lsp.buf.definition()<CR>
nmap <silent> gD <cmd>lua vim.lsp.buf.declaration()<CR>
nmap <silent> gK <cmd>lua vim.lsp.buf.hover()<CR>
nmap <silent> <leader>n <cmd>lua vim.lsp.buf.rename()<CR>
nmap <silent> <leader>b <cmd>lua vim.lsp.buf.formatting()<CR>
" neovim overrides my omnifunc with whatever ccomplete is, so I use this
autocmd FileType c,ch,header,cpp setlocal omnifunc=v:lua.vim.lsp.omnifunc
Ну и конечно, затем чтобы пользоваться функционалом, необходимо установить LSP сервера. В случае Go и Rust, он уже входит в их официальные тулчейны. Для C необходимо установить clang, насколько я знаю GCC не предоставляет имплементацию сервера LSP.
Привязка клавиш в моей конфигурации позволяют мне переходить к определению с
gd
к декларации с gD
, открыть всплывающее окно с информации имплементации и
документирующими комментариями с gK
, переименовать переменные с <leader>n
(leader в моей конфигурации это пробел), и скормить исходный файл утилиты
форматирования кода (например, gofmt) с <leader>b
.
Другие плагины
LSP предоставляет большинство функций, которые стоит ожидать от IDE, но так же есть и другие плагины, которые улучшают процесс редактирования кода. В том числе:
"...
Plug 'ervandew/supertab'
Plug 'majutsushi/tagbar'
Plug 'tpope/vim-commentary'
"...
Первый из них, supertab, делает более
удобным автозаполнение. По умолчанию автозаполнение omnifunc
привязано к
<C-x><C-o>, что не очень удобно. Обычно в консольных терминалах и в других
редакторах клавиша Tab вызывает функцию автозаполнения, что казалось бы
разумным. Но иногда также есть необходимость вставить символ табуляции вручную.
Для этого и годиться этот плагин. После нажатия на клавишу Tab, он
автоматически, в зависимости от контекста, либо вставляет символ табуляции либо
вызывает автозаполнения.
Далее tagbar. Данный плагин позволяет открывать панель со списком глобальных перемен, определений структур данных, и функции в текущем исходном файле. Для его работы требуются ctags.
Наконец vim-commentary, который предоставляет возможность легко и быстро закомментировать несколько строк одновременно. Не так прямо и полезно как другие плагины, но тем не менее.
Отладка
Рано или поздно в процессе разработки программного обеспечения вы столкнётесь с ошибкой, которая приведёт к неправильно исполнению программы. Можно найти проблему вручную выпяливая код и чеща себе голову. Но есть и способ получше. Можно открыть отладчик и анализировать программу наблюдая за её исполнение пошагово.
Некоторое время, по крайней мере с C и языками поддерживаемые gdb, я просто открывал gdb в другом окне с терминалом и оставлял открытым в другом окне Vim с исходным кодом. Но появились пару новинок в Vim'е, которые сделали этот процесс намного удобнее.
Первая это terminal
, которая как имя и полагает, является терминальным
эмулятором внутри самого вима. Если коротко, то оно позволяет открывать новый
терминал внутри буфера вима. Лично для меня этот функционал сам по себе не очень
полезный, так как я пользуюсь плиточным оконным менеджером, которые мне и так
уже предоставляет возможность быстро и удобно перемещаться по окнам. Тем не
менее, этот плагин делает возможным следующий плагин, который зависит от него.
Звезда нашего шоу — Termdebug
, плагин, который встроенный как в Vim (версия >=
8.1) так и в Neovim. То, что он делает это открывать два буфера, один с консолью
gdb, а второй с выходом отлаживаемой программы. В буфере с консолью пви можно
традиционным способом пользоваться gdb вводя в неё команды, или через команды
вима.
Для того, чтобы начать пользоваться Termbug'ом, необходимо сначала подгрузить плагин:
:packadd termdebug
Затем запускаем его передавая название бинарного файла, который требуется отлаживать:
:Termdebug <путь к бинарнику>
После запуска появятся два буфера, один с выводом отлаживаемой программы и второй с консолью gdb. Оба буфера терминалы. Для того чтобы пользоваться терминальными буферами в виме, нужно перейти в режим вставки чтобы печатать в терминале. Чтобы выйти из режима вставки необходимо ввести <C-\><C-n>. Почему не Esc? А потому, что некоторые терминальные программы пользуются им, например, режим vi в bash или zsh. Ну или например другая истанция вима внутриа вима, почему бы и нет ¯\(ツ)/¯.
После того, как открыли Termdebug, можно запустить отлаживаемую программу
традиционным способом с gdb. Или из самого вима с :Run <аргументы>
. Другие
полезные команды: :Break
для того, чтобы вставить точку остановки в текущей
строке кода, :Clear
чтобы её удалить, :Continue
для того чтобы продолжить
выполнение программы и т.д.
Пример отладки программы в Vim.
Что делает это замечательным это то, что можно наблюдать за процессом отладки
прямиком из исходного файла. Не надо постоянно вводить list
в gdb, можно
просто переходить по файлам и ставить или удалять точки установки.
Конечно, так как по сути отладка-то происходит в gdb, то правила и те же.
Например, необходимо компилировать программу с символами для отладки, т.е. с
флагом -g
.
Послесловие
Это только некоторые плагины, которые помогают мне более эффективно писать и отлаживать программы в виме. Этот текст не является подробным туториалом настройки вима или правильной отладки программ. Им я просто хотел показать как можно удобно и легко пользоваться вимом наряду с другими инструментами для упрощения разработки и отладки ПО. Существует множество информации в интернете насчёт того, как правильно настроить вим, или как отлаживать программы с gdb или другими утилитами как valgrind или ASan.
Если хочется узнать больше про LSP в neovim, советую почитать страницу помощи в
neovim вводя :help lsp
. Аналогично для Termdebugger :help Termdebugger
.
Если вам интересно, то моя конфигурация вима доступна по ссылке https://git.yaroslavps.com/configs/vimrice/
Если вы никогда не пользовались вимом, и вы повелись на то, чтобы его открыть, после того как прочитали этот текст, и вы не можете выйти из него то, эта картинка вам в помощь.
Не полностью соответствует философии Юникс, но по большей мере да, в том плане что это программа, которая делает одну вещь и делает её хорошо. Да, я тоже в шоке, не ожидал такого от Microsoft. Больше информации по LSP можно прочитать здесь: https://microsoft.github.io/language-server-protocol/
https://www.yaroslavps.com/ru/weblog/debugging-in-vim/
© 2018—2024 Yaroslav de la Peña Smirnov.