aboutsummaryrefslogtreecommitdiff
path: root/content/weblog/2022-01-25_debugging-in-vim/index.ru.md
blob: 23f870e18dfb312c3f25b2c5fa87928c682a6dda (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
+++
title = "Как я отлаживаю программы в Vim"
date = 2022-01-25T19:49:30Z
+++

Мне нравиться использовать отдельные инструменты для разных задач, но также мне
нравится когда эти инструменты хорошо интегрируются с моими остальными
инструментами. К сожалению я не видел достаточной информации в интернете насчёт
возможного процесса отладки программ посредством Vim. Информация, точнее, есть,
но она разбросана по всему интернету. И так я решил написать о моём процессе
написания и отладки с Vim.

<!-- more -->

Многие, а может и большинство, IDE уже имеют встроенные функции отладки
программ. Но также эти IDE не vim, и чаще всего они включаю в себя куча других
сомнительных функций. И нет, «Vim mode» плагины не считаются, большинство из них
даже половина функционала Vim'а не включают в себя.

Я обожаю vim (на самом деле использую neovim, кому какая разница), и я считаю
его лучшим текстовым редактором. Я даже в полном серьёзе считаю что у Vim'а
лучшее UI/UX из любой программы. Да, он далеко не самая интуитивная программа
([вставьте мем про выхода из Vim'а](#exit-vim)), но выучить основные азы не так
сложно как кажется зато как только их выучить сложно будет пользоваться чем-то
другим. Короче говоря, Vim это не программа а образ жизни.

Проблема заключается в том, что стать продвинутым пользователем вима всё-таки
занимает немалого времени. А чтобы эффективно разрабатывать программы, не
достаточно просто напечатать код программы. Так же необходимо быстро
перемещаться по исходным файлам, быстро исправлять самые банальные ошибки (вроде
опечатках), свести к минимуму механические задачи (например с помощью
автозаполнения), и конечно, отладка и профилировка.

К счастью как Vim как и Neovim уже несколько лет предоставляют функционал для
интегрирования инструментов разработки и отладки с лёгкостью. Так как я
пользуюсь Neovim, то соответственно некоторые моменты могут отличаться от
ванильного Vim.

## Инструменты разработки

В старые добрые времена для каждого редактора/IDE отдельно должны были быть
разработаны плагины или компоненты. То есть, для каждой комбинации редактора и
языка программирования выполнялся труд по разработке инструмента, что означала
что далеко не все редакторы и не все языки имели достаточные инструменты для
разработки и отладки.

К счастью, Мелкомягкие поступили совсем не по Мелкомягкому и вместо того чтобы
встроить совершенно новую реализацию среды разработки для VSCode, они
разработали так называемый Language Server Protocol[^1], и с помощью Red Hat и
других выложили в опен сорс и превратили его в некий стандарт.

Если коротко говоря, то Language Server Protocol (LSP) это просто JSON RPC
протокол для общения между сервером, который предоставляет функции для
разработки программ, как например, автозаполнение, статический анализ, переход
на определение, и т.д., и клиент, то есть, IDE или редактор, который отображает
результат всех этих действий. Среди редакторов, которые поддерживают LSP,
находится Vim. В Neovim'е это поддержка даже встроена.

### LSP

Затем чтобы использовать LSP в neovim, необходимо включить плагин и настроить
LSP сервер(а) ваш(его/их) язык(а/ов). Хорошо то, что настройка этого функционала
довольно проста, так как есть официальный плагин который уже предоставляет
настройки по умолчанию для многих LSP разных языков.

Я пользуюсь [vim-plug](https://github.com/junegunn/vim-plug) чтобы управлять
своими планинами, поэтому я сначала с его помощью добавил плагин
[lspconfig](https://github.com/neovim/nvim-lspconfig):

```vim
"...
	Plug 'neovim/nvim-lspconfig'
"...
```

Затем я его настроил под себя, добавляя конфигурации для LSP сервера C, Go,
Rust, Python, и Javascript, языки, которыми я чаще всего пользуюсь:

```vim
" 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, но так же
есть и другие плагины, которые улучшают процесс редактирования кода. В том
числе:

```vim
"...
	Plug 'ervandew/supertab'
	Plug 'majutsushi/tagbar'
	Plug 'tpope/vim-commentary'
"...
```

Первый из них, [supertab](https://github.com/ervandew/supertab), делает более
удобным автозаполнение. По умолчанию автозаполнение `omnifunc` привязано к
\<C-x\>\<C-o\>, что не очень удобно. Обычно в консольных терминалах и в других
редакторах клавиша Tab вызывает функцию автозаполнения, что казалось бы
разумным. Но иногда также есть необходимость вставить символ табуляции вручную.
Для этого и годиться этот плагин. После нажатия на клавишу Tab, он
автоматически, в зависимости от контекста, либо вставляет символ табуляции либо
вызывает автозаполнения.

Далее [tagbar](https://github.com/preservim/tagbar). Данный плагин позволяет
открывать панель со списком глобальных перемен, определений структур данных, и
функции в текущем исходном файле. Для его работы требуются ctags.

Наконец [vim-commentary](https://github.com/tpope/vim-commentary), который
предоставляет возможность легко и быстро закомментировать несколько строк
одновременно. Не так прямо и полезно как другие плагины, но тем не менее.

## Отладка

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

Некоторое время, по крайней мере с C и языками поддерживаемые gdb, я просто
открывал gdb в другом окне с терминалом и оставлял открытым в другом окне Vim с
исходным кодом. Но появились пару новинок в Vim'е, которые сделали этот процесс
намного удобнее.

Первая это `terminal`, которая как имя и полагает, является терминальным
эмулятором внутри самого вима. Если коротко, то оно позволяет открывать новый
терминал внутри буфера вима. Лично для меня этот функционал сам по себе не очень
полезный, так как я пользуюсь плиточным оконным менеджером, которые мне и так
уже предоставляет возможность быстро и удобно перемещаться по окнам. Тем не
менее, этот плагин делает возможным следующий плагин, который зависит от него.

Звезда нашего шоу — `Termdebug`, плагин, который встроенный как в Vim (версия >=
8.1) так и в Neovim. То, что он делает это открывать два буфера, один с консолью
gdb, а второй с выходом отлаживаемой программы. В буфере с консолью пви можно
традиционным способом пользоваться gdb вводя в неё команды, или через команды
вима.

Для того, чтобы начать пользоваться Termbug'ом, необходимо сначала подгрузить
плагин:

```vim
:packadd termdebug
```

Затем запускаем его передавая название бинарного файла, который требуется
отлаживать:

```vim
:Termdebug <путь к бинарнику>
```

После запуска появятся два буфера, один с выводом отлаживаемой программы и
второй с консолью gdb. Оба буфера терминалы. Для того чтобы пользоваться
терминальными буферами в виме, нужно перейти в режим вставки чтобы печатать в
терминале. Чтобы выйти из режима вставки необходимо ввести \<C-\\\>\<C-n\>.
Почему не Esc? А потому, что некоторые терминальные программы пользуются им,
например, режим vi в bash или zsh. Ну или например другая истанция вима внутриа
вима, почему бы и нет ¯\\_(ツ)_/¯.

После того, как открыли Termdebug, можно запустить отлаживаемую программу
традиционным способом с gdb. Или из самого вима с `:Run <аргументы>`. Другие
полезные команды: `:Break` для того, чтобы вставить точку остановки в текущей
строке кода, `:Clear` чтобы её удалить, `:Continue` для того чтобы продолжить
выполнение программы и т.д.

[![Снимок примера отладки программы в Vim](debugging-session.png)](debugging-session.png)
<figcaption>

Пример отладки программы в Vim.
</figcaption>

Что делает это замечательным это то, что можно наблюдать за процессом отладки
прямиком из исходного файла. Не надо постоянно вводить `list` в gdb, можно
просто переходить по файлам и ставить или удалять точки установки.

Конечно, так как по сути отладка-то происходит в gdb, то правила и те же.
Например, необходимо компилировать программу с символами для отладки, т.е. с
флагом `-g`.

## Послесловие

Это только некоторые плагины, которые помогают мне более эффективно писать и
отлаживать программы в виме. Этот текст не является подробным туториалом
настройки вима или правильной отладки программ. Им я просто хотел показать как
можно удобно и легко пользоваться вимом наряду с другими инструментами для
упрощения разработки и отладки ПО. Существует множество информации в интернете
насчёт того, как правильно настроить вим, или как отлаживать программы с gdb или
другими утилитами как valgrind или ASan.

Если хочется узнать больше про LSP в neovim, советую почитать страницу помощи в
neovim вводя `:help lsp`. Аналогично для Termdebugger `:help Termdebugger`.

Если вам интересно, то моя конфигурация вима доступна по ссылке
<https://git.yaroslavps.com/configs/vimrice/>

<figcaption id="exit-vim">

![Как выйти из вима](quit-vim.jpg)

Если вы никогда не пользовались вимом, и вы повелись на то, чтобы его открыть,
после того как прочитали этот текст, и вы не можете выйти из него то, эта
картинка вам в помощь.
</figcaption>

[^1]: Не полностью соответствует философии Юникс, но по большей мере да, в том
  плане что это программа, которая делает одну вещь и делает её хорошо. Да, я
  тоже в шоке, не ожидал такого от Microsoft. Больше информации по LSP можно
  прочитать здесь: <https://microsoft.github.io/language-server-protocol/>