Привет, меня давно тянуло к neovim'у - есть в нем что-то особенное, но после того, как я более менее разобрался с базовыми командами и моя скорость разработки сравнялась со скоростью в VSCode, я обнаружил, что в моей сборке нельзя подебажить nodejs приложения.
А я часто пишу на ноде и хочется иметь такую возможность, а не переключаться каждый раз в vscode.
Скорее всего данный функционал уже есть и работает в готовых сборках, но я не ищу легких путей, потому что основное, за что я полюбил neovim - это его кастомизируемость и возможность собрать все с нуля. Это своеобразный медитативный процесс, как и сам процесс написания кода в нем.
Поэтому я пошел искать информацию и ее пришлось собирать по частям, а в этой заметке - квинтэссенция моих страданий и скитаний по интернету ))
Итак, начну с самого начала, возможно это перерастет в серию статей. Опущу хвалебные рассказы про этот редактор, раз вы читаете эту статью - значит уже в курсе, обо всех его плюсах и минусах.
Установка
Об этом подробно расписано в репозитории, так что тут больше нечего описывать - выбирайте вариант для своей платформы и погнаги дальше.
В качестве плагин-менеджера я буду использовать lazy.nvim, потому что он классный (а если честно я очень поверхностно выбрал именно его), все его преимущества расписаны подробно в репозитории
Структура конфигурации
Вся конфигурация neovim'a хранится в домашней директории в папочке .config (Путь такой ~/.config/nvim)
Я решил настроить свой конфиг с нуля и принял для себя такую структуру:
init.lua- точка входа в конфиг, подключает в себя файлcore.bootstrapcore/- директория, которая содержит в себе подключение:- плагин-менеджера, подключается в
core.bootstrap.lua - всех плагинов в
core.plugins - базовых маппингов в
core.mappings
- плагин-менеджера, подключается в
plugins/- директория, которая будет содержать в себе настройки плагинов в виде файликов, которые в свою очередь будут подключены вinit.lua
К делу. Настройка отладки
Для настройки отладки кода нам потребуется три плагина: Клиент, Адаптер и Отладчик
- nvim-dap реализует клиент для Debug Adapter Protocol. Это позволяет клиенту управлять отладчиком через документированный API. Это открывает возможности для контроля отладочного процесса непосредственно из Neovim, устанавливать точки останова, оценивать значения переменных во время выполнения и многое другое.
- mxsdev/nvim-dap-vscode-js - Этот адаптер позволяет Neovim (через nvim-dap) взаимодействовать с JavaScript. Интересно, что это тот же адаптер, который используется в VS Code.
- microsoft/vscode-js-debug - Это отладчик JavaScript, основанный на DAP. Он поддерживает отладку Node.js, Chrome, Edge, WebView2, расширений для VS Code и многого другого. Этот отладчик стал отладчиком JavaScript по умолчанию в Visual Studio Code начиная с версии 1.46, и постепенно внедряется в саму Visual Studio.
Подключение плагинов
Синтаксис подключения плагинов - можно посмотреть в репозитории Lazy, Я приведу готовый пример подлючения плагинов:
require("lazy").setup({
-- Debug
'mfussenegger/nvim-dap',
{
'mxsdev/nvim-dap-vscode-js',
dependencies = {"mfussenegger/nvim-dap"},
},
{
'microsoft/vscode-js-debug',
lazy = false,
build = "npm install --legacy-peer-deps && npx gulp vsDebugServerBundle && mv dist out"
},
})Конфигурирование плагинов
В этом месте я честно посмотрел конфиг в этом блогe. Там автор поверхостно объясняет каждый из участков кода, поэтому я тут не буду повторяться, к тому же там полно комментариев. Весь этот код удобно разместился в файле plugins/dap.lua - в данном случае я не стал разбивать на три разных файла по кол-ву плагинов, а объединил настройку по функциональности, возможно в будущем я все-таки растащу их по разным файлам.
local dap = require("dap")
-- Set keymaps to control the debugger
vim.keymap.set('n', '<F5>', require 'dap'.continue)
vim.keymap.set('n', '<F10>', require 'dap'.step_over)
vim.keymap.set('n', '<F11>', require 'dap'.step_into)
vim.keymap.set('n', '<F12>', require 'dap'.step_out)
vim.keymap.set('n', '<leader>b', require 'dap'.toggle_breakpoint)
vim.keymap.set('n', '<leader>B', function()
require 'dap'.set_breakpoint(vim.fn.input('Breakpoint condition: '))
end)
-- JS based languages
local DEBUGGER_PATH = vim.fn.stdpath('data') .. "/lazy/vscode-js-debug"
require("dap-vscode-js").setup({
-- node_path = "node", -- Path of node executable. Defaults to $NODE_PATH, and then "node"
debugger_path = DEBUGGER_PATH, -- Path to vscode-js-debug installation.
-- debugger_cmd = { "extension" }, -- Command to use to launch the debug server. Takes precedence over `node_path` and `debugger_path`.
adapters = { 'chrome', 'pwa-node', 'pwa-chrome', 'pwa-msedge', 'node-terminal', 'pwa-extensionHost', 'node', 'chrome' }, -- which adapters to register in nvim-dap
-- log_file_path = "(stdpath cache)/dap_vscode_js.log" -- Path for file logging
-- log_file_level = false -- Logging level for output to file. Set to false to disable file logging.
-- log_console_level = vim.log.levels.ERROR -- Logging level for output to console. Set to false to disable console output.
})
local js_based_languages = { "typescript", "javascript", "typescriptreact" }
for _, language in ipairs(js_based_languages) do
require("dap").configurations[language] = {
{
type = "pwa-node",
request = "launch",
name = "Launch file",
program = "${file}",
cwd = "${workspaceFolder}",
},
{
type = "pwa-node",
request = "attach",
name = "Attach",
processId = require 'dap.utils'.pick_process,
cwd = "${workspaceFolder}",
},
{
type = "pwa-chrome",
request = "launch",
name = "Start Chrome with \"localhost\"",
url = "http://localhost:3000",
webRoot = "${workspaceFolder}",
userDataDir = "${workspaceFolder}/.vscode/vscode-chrome-debug-userdatadir"
}
}
endПроверяем, что все работает
Я тестировал отладку на тестовом express приложении, примерно такого вида
const express = require('express')
const app = express();
app.get('*', (req, res) => {
const headers = req.headers;
res.send('Hello, world with headers: ' + JSON.stringify(headers, null, 2));
});
app.listen(8080, () => console.log('App running at http://localhost:8080'))- Запускаем приложение с флагом
--inspect- в моем случае это выглядит так:node --inspect src/index.js - С помощью
<leader>b(Пробел + b) ставим брекпоинт на строчкеres.send(должна появиться буковкаB) - Нажимаем
<F5>- должно открыться окно, в котором надо выбрать способ дебага (attach), а затем выбрать процесс (можно сориентироваться, по команде, которой вы запускали приложение). После всего этого вы должны увидеть в консоли сообщение о том, что дебаггер подключился к процессу. - Открываем страницу в браузере (
http://localhost:8080) - она должна зависнуть, значит дебаггер сработал, и вы должны увидеть вместоBстрелочку
А сейчас вы, возможно, задаетесь вопросом: "Это всё? А как насчет всего этого крутого функционала, позволяющего анализировать переменные и просматривать текущее состояние? Что это вообще а интерфейс и где все те крутые штуки, которые должен предоставлять отладчик?
Все верно, https://github.com/mfussenegger/nvim-dap - это протокол, который не предоставляет сам по себе крутого UI, зато предоставляет возможности для его подключения. Мы будем использовать rcarriga/nvim-dap-ui
Подключаем UI
Добавляем в подключение плагинов, еще одну строчку
-- core/plugins.lua
{ "rcarriga/nvim-dap-ui", dependencies = {"mfussenegger/nvim-dap"} },И уже по накатанной добавляем еще один кусок кода, который будет отвечать за кусок UI для отладки, со всеми привычными плюшками.
-- plugins/dap.lua
...
-- Debug UI
require("dapui").setup()
local dap, dapui = require("dap"), require("dapui")
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open({})
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close({})
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close({})
end
vim.keymap.set('n', '<leader>ui', require 'dapui'.toggle)Теперь с помощью <leader>ui (Пробел + ui) - мы можем открывать шикарнейший интерфейс для отладки и дебажить как боги. Веселитесь и наслаждайтесь процессом отладки!


