Configuring Neovim From Scratch

March 22, 2024 - 7 min read

It has been a long time since I last blogged, but lots of things have changed since then. I have recently swapped over to Neovim as my primary editor, and I am excited to share the process with everyone!

Inspiration

In my daily perusal of Youtube, I stumbled upon an entertaining developer that streams on Twitch, he is known as the thePrimeagen. He was a strong advocate for using Neovim and even has a series to cover on Vim basic motions, as well as a walkthrough on how to set up Neovim from scratch.

As such, I decided to dive into configuring my own Neovim editor during my free time. Throughout the process, I have sought inspirations from multiple developer's dotfiles settings.

What is Neovim

Let's start off with a basic explanation of Vim. Vim is a modal editor, it has various modes to achieve different functionalities, mainly NORMAL (default), INSERT and VISUAL. This effectively separates navigation, editing and highlighting operations into different modes.

Neovim is an upgrade to Vim, it is a fork of Vim that supports users to utilize Lua for scipting, as an alternative to VimScript. This greatly reduces the barrier of entry for users to customize their config files, and organize their files through utilizing Neovim's runtimepath and other nifty custom directory lookup such as ftdetect.

The Neovim experience will definitely require a package manager for you to manage, install, update and clear your plugins according to your needs. I decided to using lazy.nvim, created by folke.

Configuring with Lazy

Lazy nvim is an awesome package manager. It follows a simple pattern and here are the steps to get started with it:

1: Setup & installation

Firstly, in your init.lua, copy the following code snippet. This ensures that lazy is installed into the desired lazypath, and added to Neovim's runtimepath. This allows lazy to intervene in Neovim's Initialization process (view lazy's startup sequence) .

local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git",
    "--branch=stable", -- latest stable release
    lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

2: Setting up plugins directory

In your nvim config directory, create a plugins directory in the path ./lua/plugins. This directory will be used to better organize your Neovim plugins while using lazy, view how to structure your plugins.

Once that is done, add the following code below the code snippet mentioned above:

require("lazy").setup("plugins")

-- OR, if you wish to extend the plugins table to add extra functionalities not included in plugin directory
local plugins = {
  -- any additional plugins you like to include without creating a specific *.lua file for it
}
require("lazy").setup({ plugins, { import = "plugins" } })

Lazy will automatically merge all the Lua tables returned by the Lua files in the plugins directory, where each file will be returning a Lua table containing the Plugin Spec for desired plugin. This consolidated specs table will be passed into the setup() of lazy.

3: Creating your lua plugin file

Within our plugins directory, we will define our individual plugin files. Let's set up a colorscheme! I will create a file, called colorscheme.lua that will return a Lua table.

return {
  "catppuccin/nvim",
  priority = 1000,
  config = function()
    require("catppuccin").setup({
      integrations = {
        harpoon = true,
        telescope = true,
        mason = true,
        cmp = true,
        native_lsp = { enabled = true },
        neotree = true,
      }
    })
    vim.cmd.colorscheme("catppuccin-macchiato")
  end
}

4: Configuring lazy loading

The cool thing about lazy is that it allows your plugins to be lazy loaded based on factors such as event, cmd (command), ft and keys. This is really cool as it allows you to make intuitive lazy loading according to your desires in a simple way.

For example, I configured my linting plugin to load during the BufReadPost event, which makes sense as I will only require lsp capability upon opening a file in buffer.

return {
  "neovim/nvim-lspconfig",
  event = { "BufReadPost" },
  cmd = { "LspInfo", "LspInstall", "LspUninstall", "Mason" },
}

It's cool to see how lazy, as well as other Neovim plugins, provide users the ability to have control over the granulity of their IDE configurations!

Neovim vs VS Code

Both are amazing code editors, they just differ in the way you install and customize the plugin. In VSCode, you will browse the plugin marketplace for your ideal plugins, and configure their setting in the settings.json file. However, I feel that it does not grant granular control over the editor experience.

Whereas for Neovim, you can browse github for any plugin repository, and configure the installation and settings via lazy or Neovim's vim library. You can also control when does the plugin run, for which file type etc. The level of control is on another level, and you are also free to extend plugin functionalities to your liking, making the editor truly personalized. Which is also why TJ Devries terms Neovim as a PDE (Personal-Developer-Editor) rather than IDE.

Power of Vim Motions

Vim motions are amazing, they just make coding so much more fun. When you type a sequence of keypress to achieve your desired outcome, it feels so good. It reduces your reliance on a mouse, and through constant practice and usage, you get really fast at it. It feels like it gamifies coding.

Moving forward

I am quite pleased with my current Neovim configuration. I have all my IDE config stored in my dotfile repository, ready to port over to any other machines if required. However, I would like to automate the process even more in the future, automating installation of necessory binaries and programs, and creating the necessary symlinks required for my dotfile configuration. I am not sure when I will accomplish it, but I set myself a goal to complete the automation work before I can purchase a new laptop in the future 😒