Complete guide to set up Python development environment with debugging capabilities in Neovim using NvChad.
- Ubuntu/Debian-based system (WSL supported)
- Git installed
- Python 3 installed
# Remove old version if exists
sudo apt remove neovim
# Install latest Neovim
curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim.linux64.tar.gz
sudo rm -rf /opt/nvim
sudo tar -C /opt -xzf nvim.linux64.tar.gz
sudo ln -sf /opt/nvim-linux64/bin/nvim /usr/local/bin/nvim
# Verify installation
nvim --version# Backup existing config (if any)
mv ~/.config/nvim ~/.config/nvim.backup
# Install NvChad
git clone https://github.com/NvChad/starter ~/.config/nvim# Launch Neovim
nvim
# In Neovim, install Mason
:MasonInstall# Open Mason interface
:Mason
# Navigate and install:
# - python-lsp-server (or pyright)
# - debugpy
# - ruff (optional: Python linter)Or install via command:
:MasonInstall python-lsp-server debugpy ruffCreate/edit ~/.config/nvim/lua/configs/lspconfig.lua:
require("nvchad.configs.lspconfig").defaults()
-- Python LSP
require'lspconfig'.pyright.setup{}
-- Add other servers as needed
local servers = { "html", "cssls" }
vim.lsp.enable(servers)Create/edit ~/.config/nvim/lua/plugins/init.lua:
return {
{
"stevearc/conform.nvim",
opts = require "configs.conform",
},
{
"neovim/nvim-lspconfig",
config = function()
require "configs.lspconfig"
end,
},
-- Debug Adapter Protocol
{
"mfussenegger/nvim-dap",
config = function()
local dap = require("dap")
-- Python debugpy configuration
dap.adapters.python = {
type = 'executable',
command = vim.fn.stdpath("data") .. '/mason/packages/debugpy/venv/bin/python',
args = { '-m', 'debugpy.adapter' },
}
dap.configurations.python = {
{
type = 'python',
request = 'launch',
name = "Launch file",
program = "${file}",
pythonPath = function()
return '/usr/bin/python3'
end,
},
}
end
},
-- Required dependency for nvim-dap-ui
{
"nvim-neotest/nvim-nio"
},
-- DAP UI
{
"rcarriga/nvim-dap-ui",
dependencies = {"mfussenegger/nvim-dap", "nvim-neotest/nvim-nio"},
config = function()
local dap, dapui = require("dap"), require("dapui")
dapui.setup()
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
end
},
-- Virtual text showing variable values
{
"theHamsta/nvim-dap-virtual-text",
dependencies = {"mfussenegger/nvim-dap"},
config = function()
require("nvim-dap-virtual-text").setup()
end
}
}Add to ~/.config/nvim/init.lua:
-- DAP keybindings using Lua functions
vim.keymap.set("n", "<leader>db", function() require('dap').toggle_breakpoint() end, { desc = "Toggle Breakpoint" })
vim.keymap.set("n", "<leader>dc", function() require('dap').continue() end, { desc = "Start/Continue Debug" })
vim.keymap.set("n", "<leader>ds", function() require('dap').step_over() end, { desc = "Step Over" })
vim.keymap.set("n", "<leader>di", function() require('dap').step_into() end, { desc = "Step Into" })
vim.keymap.set("n", "<leader>do", function() require('dap').step_out() end, { desc = "Step Out" })
vim.keymap.set("n", "<leader>dt", function() require('dap').terminate() end, { desc = "Terminate Debug" })
vim.keymap.set("n", "<leader>du", function() require('dapui').toggle() end, { desc = "Toggle Debug UI" })# Restart Neovim and sync plugins
nvim
:Lazy syncCreate test.py:
def factorial(n):
if n <= 1:
return 1
else:
return n * factorial(n - 1)
def main():
numbers = [3, 4, 5]
for num in numbers:
result = factorial(num) # Set breakpoint here
print(f"Factorial of {num} is {result}")
if __name__ == "__main__":
main()- Open the file:
nvim test.py - Set breakpoint: Move cursor to line 9, press
<leader>db - Start debugging: Press
<leader>dc - Use debug controls:
<leader>ds- Step over<leader>di- Step into function<leader>do- Step out<leader>du- Toggle debug UI<leader>dt- Terminate debugging
| Keybinding | Action |
|---|---|
<leader>db |
Toggle Breakpoint |
<leader>dc |
Start/Continue Debug |
<leader>ds |
Step Over |
<leader>di |
Step Into |
<leader>do |
Step Out |
<leader>dt |
Terminate Debug |
<leader>du |
Toggle Debug UI |
Note: <leader> is typically the Space key in NvChad
:LspInfo:lua print(vim.inspect(require('dap'))):MasonLSP not working:
- Ensure
pyrightorpython-lsp-serveris installed via Mason - Check
:LspInfofor active clients
Debugger not starting:
- Verify
debugpyis installed::Mason - Check debugpy path:
ls ~/.local/share/nvim/mason/packages/debugpy/venv/bin/
Keybindings not working:
- Restart Neovim after configuration changes
- Test with Lua command:
:lua require('dap').toggle_breakpoint()
Your final configuration should look like:
~/.config/nvim/
├── init.lua
├── lua/
│ ├── chadrc.lua
│ ├── configs/
│ │ └── lspconfig.lua
│ └── plugins/
│ └── init.lua
Add to ~/.config/nvim/lua/configs/conform.lua:
return {
formatters_by_ft = {
python = { "black" },
},
format_on_save = {
timeout_ms = 500,
lsp_fallback = true,
},
}Install formatter:
:MasonInstall blackCongratulations! You now have a fully functional Python development environment with debugging capabilities in Neovim. Happy coding! 🐍✨