Using Neovim as IDE with NvChad
Table of Contents
This post is partially based on the video Turn VIM into a full-featured IDE with only one command. This is a brief guide on how you can use NvChad to configure Neovim as an IDE.
Install Neovim and NvChad #
You can install Neovim in Linux, Windows or macOS from the Neovim page. You can also install it with Homebrew, that is how I installed it in my WSL. First install Homebrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
<Follow the instructions>
Then, install Neovim with:
brew install neovim
This works perfectly in my Windows 10 and Windows 11 WSL. I had problems with my Ubuntu machine because some plugins included the character “^M”. Lazy plugin downloads other plugins using git clone, we can execute git config --global core.autocrlf input
to configure that every time git clone is used, line endings are converted to “CRLF”.
Another problem that I have found is that the sudo command cannot not be used with Neovim because it will not find the executable. You can add the bin folder to the secure_path using sudo visudo
:
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/linuxbrew/.linuxbrew/opt/neovim/bin"
Now you can use sudo nvim
, however it will not load the configuration that we are going to add with NvChad later, to fix that, run sudo -E nvim
. You can create an alias in ~/.bashrc
file: alias snvim="sudo -E nvim"
.
Now we can install NvChad, it comes with really nice pre-configured plugins for Neovim. For Linux:
git clone https://github.com/NvChad/NvChad ~/.config/nvim --depth 1
Or for Windows:
git clone https://github.com/NvChad/NvChad $HOME\\AppData\\Local\\nvim --depth 1
After cloning the repository, we enter in Neovim, a question to install an example custom config will appear, select No. NvChad comes with default configurations and commands, I made some custom configurations that you can download with:
git clone https://github.com/aams-eam/nvchad-aams-custom.git ~/.config/nvim/lua/custom/
Everything is ready now! You can now use Neovim as I do. However, you might want to learn more about some customizations.
Customizations and Fixes #
Cheat Sheet Feature #
Use space c h
to access the cheat sheet, some plugin commands are mentioned here. To close the cheat sheet use the same command.
Additionally, if we press space
and wait, a window will appear with some commands.
Selecting a Theme #
Press space t h
to open the theme searcher, now search for your favorite theme and press enter to use it.
Font problems #
You may have problems with the font when using WSL. If the icons for nvim-tree do not appear correctly, it is possibly because you need to install the font manually. If you enter in Neovim and do :help nvim-tree
, and go to the icons section, it is mentioned that you have to download a nerd font. In this case “Hack Nerd Font”. You can download it
here. Now decide a family and download the .ttf
file, for example download
Regular/HackNerdFont-Regular.ttf. If you are using MobaXterm as I do, you cannot install all fonts, MobaXterm does not work with “variable-pitch” fonts, so your options in MobaXTerm are limited to certain Monospaced/fixed-width fonts like DejaVuSansMono. So instead of the previous font you can download
Regular/HackNerdFontMono-Regular.ttf. Now you install it in Linux by copying it into ~/.local/share/fonts
or in Windows by right-click>Install. Once done you need to configure your terminal to use that font, in MobaXterm you can go to settings/terminal/Default font settings
to establish it as default font, or you can just use it for a specific session Edit session/terminal settings/Terminal font settings
.
Syntax Highlighting #
Neovim comes with tree-sitter preinstalled. To install syntax for a new language we use :TSInstall <language>
, for example :TSInstall python
.
Language Server Protocol (LSP) #
LSP can be used to have autocompletion, code navigation and error checking. Use :Mason
to the see the menu, then you can press enter to install different LSP servers, for example python-lsp-server
. Mason manages the installation, but the servers need to be configured. You can configure python-lsp-server`` by adding the following configuration to
./plugins/config/lspconfig.lua`:
require("lspconfig").pylsp.setup({
on_attach = M.on_attach,
capabilities = M.capabilities,
filetypes = {"python"},
})
You can also add the server to the configuration of Mason to ensure all the necessary components are installed by executing :MasonInstallAll
. In ./plugins/config/mason.lua
add to the options variable the following:
ensure_installed = {
"lua-language-server",
"python-lsp-server", -- Add this line
"black",
},
Formatters #
You can add formatters to Neovim by:
- Installing it with Mason. That is, add it to
ensure_installed
(e.g., black formatter), or execute:MasonInstall black
- Install the plugin none-ls and configure the following:
This will configure your Neovim to use black, and to format every time you save a file. If you do not want to use the formatter every time you save the file, just delete everything but the variable sources inside opts, you can also delete the first line (i.e., the one that creates augroup variable).
local augroup = vim.api.nvim_create_augroup("LspFormatting", {}) local null_ls = require('null-ls') local opts = { sources = { null_ls.builtins.formatting.black, }, on_attach = function(client, bufnr) if client.supports_method("textDocument/formatting") then vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr, }) vim.api.nvim_create_autocmd("BufWritePre", { group = augroup, buffer = bufnr, callback = function() vim.lsp.buf.format({ bufnr = bufnr }) end, }) end end, } return opts
Move Around Windows #
Windows are useful for visualizing multiple files at the same time. You can use:
:vsp
to split the view vertically or:sp
to split the view horizontally.ctrl+<movement keys: hjkl>
to move around windows.:q
to close a window.:on
to close all the windows but the one you are in. While using the WSL in MobaXterm I noticed that the movements around windows may not work when pressingctrl+h
, this is because it is escaped. Go tosettings>Terminal
and uncheck “Backend sends ^H”.
Buffers #
A buffer is the in-memory representation of a file being edited, they are shown as “tabs”, you can have different or the same buffer showing in multiple windows.
You can use space b
to create a new buffer, and use tab
and shift+tab
to navigate buffers.
You can close a buffer with space x
.
File Tree (nvim-tree plugin) #
Click ctrl+n
in order to see the file tree. You can:
- Open a file clicking
enter
. - Mark a file clicking
m
. - Create a new file with key
a
. - Copy and paste files using the keys
c
andp
. - Cut (i.e., mark for moving) with
x
and thenp
for moving. - Rename files using the
r
key.
You can configure to see relative numbers in .config/nvim/lua/plugins/configs/nvimtree.lua
. Modify the following line:
view = {
adaptive_size = true, -- Adapts when showing long filenames or subdirectories.
relativenumber = true, -- Show relative numbers in the file tree pane.
side = "left",
width = 30,
preserve_window_proportions = true,
},
Search for files #
Use space f f
to search for files.
Use space f b
to search for files that are open.
Terminal #
Press space h
to open a horizontal terminal and space v
to open a vertical terminal. You can also use alt+h
and alt+v
. You can create a floating terminal with alt+i
.
Clipboard #
You can use y
to copy and p
to paste. However, if you are using WSL the copied data will not be in the Windows clipboard. In order to fix that, you need to install a clipboard tool. Download
win32yank and copy it somewhere and add it to the PATH, or just copy it in /usr/local/bin
, make sure it is executable chmod +x win32yank.exe
. After that you can also configure Neovim to change from crlf
to lf
adding the following configuration in ~/.config/nvim/lua/custom/init.lua
.
vim.g.clipboard = {
name = "win32yank-wsl",
copy = {
["+"] = "win32yank.exe -i --crlf",
["*"] = "win32yank.exe -i --crlf",
},
paste = {
["+"] = "win32yank.exe -o --lf",
["*"] = "win32yank.exe -o --lf",
},
cache_enabled = true,
}
If you are working remotely via SSH, you can install xclip
. With MobaXTerm and other terminals, it will automatically copy text in your local Windows clipboard when you yank in the remote Neovim.
Other Custom Configurations #
If you want to add any other customization you can do it by modifying the files in ~/.config/nvim/lua/
custom/hadrc.lua
overwriting the default config of NVChad (e.g., plugins and NvChad options). You can also modify directlyplugins/init.lua
.custom/init.lua
used for overwriting Neovim options and commands (i.e., typical Neovim configurations). For example, here you can add to always use relative numbers withvim.o.relativenumber = true
.
Using Neovim with ssh #
I have seen some posts in which they mention using Neovim with SCP to edit remote files with your local installation of Neovim. For example, in this post can_i_use_vim_or_neovim_through_ssh they mention doing something like:
nvim scp://user@myserver[:port]//path/to/file.txt
However, it does not work for me, I have read in some posts like
this one that nvim-tree
plugin disables netrw
by default, and netrw
contains the capabilities in order to do that remote editing. But even by disabling nvim-tree
completely it does not work for me. I normally just mount part of the file system with SSHFS, and then edit those files.
Debugging in Neovim #
You will need to install:
nvim-dap
. Client for DAP protocol.nvim-dap-python
. DAP configurations for python.nvim-dap-ui
. When debugging, it modifies the view with multiple windows that show variables, stack, breakpoints, and more. It is a user interface that works withnvim-dap
.
Inside your init.lua
you will have to add nvim-dap-python
with require('dap-python').setup()
. nvim-dap-python
is configured to use python3
by default, but it looks automatically looks for environment variables such as VIRTUAL_ENV
and CONDA_PREFIX
and use that program instead if they exist. That means that if you activate a Conda environment you will use that python instead of the one that you have configured by default. Make sure that you install Debugpy in the python that you are going to use: pip3 install debugpy
.
You can execute :!which python3
to see the python being used in Neovim.
Just by installing these plugins we can debug python applications. I also included some mappings. You can check them in the configuration.
Persistent Breakpoints #
If we close Neovim, we are going to lose the breakpoints that we set. In order for them to be persistent we will install the plugin persistent-breakpoints.
Remote SSH Debugging #
In nvim-dap-python
the “configurations are general purpose configurations suitable for many use cases, but you may need to customize the configurations - for example if you want to use Docker containers”. That means that we need to add additional configurations if we want to debug an application remotely.
table.insert(require("dap").configurations.python, {
type = 'python';
request = 'attach';
name = 'Attach remote server';
connect = function()
local host = vim.fn.input('Host [127.0.0.1]: ')
host = host ~= '' and host or '127.0.0.1'
local port = tonumber(vim.fn.input('Port [5678]: ')) or 5678
return { host = host, port = port }
end;
cwd = vim.fn.getcwd();
pathMappings = {
{
localRoot = function()
return vim.fn.input("Local code folder:", vim.fn.getcwd(), "file")
end;
remoteRoot = function()
return vim.fn.input("Remote code folder:", "/", "file")
end;
},
},
})
This new configuration will ask you not only for the host and port, but the localRoot and remoteRoot. Now you can:
- Use SSHFS to copy your remote files locally, edit them, and add some breakpoints.
- Execute the remote file using Debugpy
python3 -m debugpy --listen localhost:5678 --wait-for-client main.py
- Choose the previous option “Attach remote server” and fill the host, port, localRoot, and remoteRoot. It is important that you write in remoteRoot where your remote directory resides.
- If the port 5678 is not accessible, you can create an SSH local port forwarding.
You can see in this post Local-and-Remote-Debugging-with-Docker that a similar approach can be used for debugging a docker application.
Debugging Configurations per Project (Using launch.json) #
You can use a .vscode/launch.json
file to create debugging options for every project. Then you can execute :lua require('dap.ext.vscode').load_launchjs()
in order to add that configuration. When debugging you can now choose that configuration. You can add that lua line to be executed with the mapping of DapContinue
, so every time you want to debug, the launch.json is loaded. The following file is a simple example of debug configuration for local debugging of the actual file.
{
"configurations": [
{
"type": "python",
"request": "launch",
"name": "launch.json debug",
"program": "${file}",
"console": "integratedTerminal"
}
]
}
Breakpoint Unverified Error #
This is an error that I saw when trying remote attach. Default nvim-python-dap
remote attach only works when you are executing python scripts in your local computer, if you are trying to attach to a remote machine, for example, using ssh local port forwarding, you will need to be sure you indicate the pathMappings. If you do not, the application will execute, but it will not stop in the breakpoints. If you execute :DapShowLog
you will see that those breakpoints appear as unverified and the source.path
is your local path, but it should be the remote path where the script is.
Launching and Debugging an Application Remotely #
I modified part of nvim-dap-python’s code and created a new plugin that allows you to execute and attach applications remotely with ssh. The plugin establishes an SSH connection, executes Debugpy, creates an SSH tunnel, and then attaches to the application being debugged. I will make it available soon.