Skip to main content
  1. Posts/

Using Neovim as IDE with NvChad

11 mins· 0 · 0 ·
developing Neovim guide
aams-eam
Author
aams-eam
Cybersecurity and Development Enthusiast.
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.

nvchad-example.webp

This image was obtained from NvChad Github Repository.

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:
     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
    
    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).

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 pressing ctrl+h, this is because it is escaped. Go to settings>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 and p.
  • Cut (i.e., mark for moving) with xand then p 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 directly plugins/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 with vim.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 #

nvim-dap-ui-example.png

This image was obtained from nvim-dap-ui Github Repository.

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 with nvim-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.

Related

Deploying a Kubernetes cluster with containerd and an insecure private Docker registry
7 mins· 0 · 0
kubernetes docker devops containerd guide
Improving the performance of GDBack using Go channels
9 mins· 0 · 0
Go Development Forensics
Debugger Detection Techniques: The Summary of a Summary
4 mins· 0 · 0
malware-analysis reverse-engineering
The Art of Password Cracking: Rainbow Tables
9 mins· 0 · 0
cracking documentation pentesting