www-gem words

Exploring my neovim config

Published on

vim I’ve always been fascinated by the power of customizing my development environment to suit my needs. For text editor, this journey began with vim and now neovim. I use it for notes taking, to organize my thoughts in a zettle way, for coding, and to write emails (with neomutt ).

Over time, I’ve refined my Neovim setup to create a powerful and efficient workflow. In this blog post, we’ll embark on a detailed exploration of my current Neovim configuration, examining each line and explaining its purpose and benefits.

Whether you’re a new neovim user or a more advanced user, you may find some valuable insights as we go through the lines. Vim users can also easily steal some ideas to port them into their config.

╭── The plugin manager

I use some plugins with neovim, so my config starts with calling the plugin manager. In my case, it’s lazy.nvim .

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

This is the default lazy.nvim configuration.

Now, the way I manage my plugins is to call them from a plugins.lua file located at $HOME/.config/nvim/lua
The specific command varies from plugin to plugin but the most basic version is something like this for the nvim-surround for example: {"kylechui/nvim-surround", config = function() require("config.nvim-surround") end,}

This command will call the config file for this plugin which is stored in $HOME/.config/nvim/lua/config This is how all my plugins are configured. It allow for a cleaner plugin configuration file, and quick removal/addition of plugins.

╭── Keeping your plugins up to date

One thing I like to do is to auto update my plugins when the plugins config file is saved. This is achieved with these lines:

vim.api.nvim_create_autocmd("BufWrite", {
    pattern = {"*plugins.lua"},
    command = "Lazy! install | Lazy! update | Lazy! sync"
})

Here is a bonus trick to automatically update your plugins in the background at your system boot. Add this to be executed at startup (in my case its the .xinitrc file):

nvim --headless "+Lazy! sync" +qa &

╭── The general configuration

The next part of my configuration file is dedicated to what I call the general neovim configuration. That means everything related to the basic neovim functioning and default customization.

The basis

First I define the leader key which is used in my keybindings by calling :

vim.g.mapleader = ","

Then I want my terminal to use full 24-bit colors:

vim.o.termguicolors = true

Next, I set the python provider:

vim.cmd [[let g:python3_host_prog = '/usr/bin/python3']]

Here are some general formatting options:

vim.o.nocompatible = true
vim.o.completeopt = menu,menuone,noselect -- activate autocompletion. I use the nvim-cmp plugin instead
vim.o.noerrorbells = true -- remove error bells
vim.o.novisualbell = true -- remove error visual bells
vim.o.showmode = false -- hide mode indicator. I don't use it since I have cmdheight set to 0, so it has no effect
vim.o.syntax = true -- automatically detect the syntax highlighting. I use the treesitter plugin instead
vim.o.wrap = true -- wrap lines
vim.cmd [[ set list lcs=tab:\│\ ]] -- display vertical lines to show indentation. I use the indent-blankline plugin instead
vim.o.ai = true -- auto indent
vim.o.backup = false -- don't create a backup file when writing an existing file
vim.o.clipboard = 'unnamedplus' -- makes all clipboards communicate well together
vim.o.conceallevel = 2 -- concealed text is completely hidden unless it has custom replacement character defined
vim.o.cursorline = true -- highlight the text line of the cursor
vim.o.encoding = "utf-8" -- already set to utf-8 by default
vim.o.expandtab = true -- insert spaces for <Tab>
vim.o.formatoptions = "w" -- trailing white space indicates a paragraph continues in the next line
vim.o.hlsearch = true -- highlight search results
vim.o.ignorecase = true -- make searching case insensitive
vim.o.incsearch = true -- make search act like search in modern browsers
vim.o.linebreak = true -- wrap lines at word boundaries rather than splitting up words
vim.o.mouse = "a" -- enable mouse
vim.o.shiftwidth = 4 -- 1 tab = 4 spaces
vim.o.si = true -- smart indent
vim.o.smartcase = true -- smartcase search... unless the query has capital letters.
vim.o.smarttab = true -- tab respects 'tabstop', 'shiftwidth', and 'softtabstop'
vim.o.softtabstop = 4 -- 1 tab = 4 spaces
vim.o.swapfile = false -- don't use a swapfile for the buffer
vim.o.tabstop = 4 -- 1 tab = 4 spaces
vim.o.tm = 500	-- wait time for 2-character keybindings
vim.o.undodir = os.getenv("HOME") .. "/.vim/undodir" -- directory for undo files
vim.o.undofile = true -- return the name of the undo file that would be used for a file with name {name} when writing

Get rid of the useless stuff

In case you did not know, neovim load quite some builtin plugins by default. Not that neovim starting time is below the threshold of human perception so this won’t make any difference but it’s simply cleaner and good practice to not load anything you don’t use. I have disabled the ones I don’t use like that:

vim.g.loaded_2html_plugin = 1
vim.g.loaded_gzip = 1
vim.g.loaded_logiPat = 1
vim.g.loaded_remote_plugins = 1
vim.g.loaded_rrhelper = 1
vim.g.loaded_shada_plugin = 1
vim.g.loaded_tarPlugin = 1
vim.g.loaded_zipPlugin = 1

Here is the list of all builtin plugins and what they’re used for:

Go hybrid, it’s not as trendy as full electric but meh

Something I couldn’t live without anymore is the hybrid mode . Activating it will let you know where you are in the file by using the absolute number for your cursor line and number lines relatively to your cursor line.

vim.api.nvim_create_autocmd({"BufEnter", "FocusGained", "WinEnter"}, {
    pattern = {"*"},
    command = "set nu | set rnu"
})

If you have multiple files open, it may be handy to still use absolute numbers only on files that you have open but are not actively editing. This can be achieved by adding the code below:

vim.api.nvim_create_autocmd({"BufLeave", "FocusLost", "WinLeave"}, {
    pattern = {"*"},
    command = "set nu | set nornu"
})

╭── Customization also means looking good

Here is how to easily apply a theme from a 232 themes collection:

-- apply the tomorrow-night theme using the [nvim-base16 theme](/vim_theme)
vim.cmd [[ colorscheme base16-tomorrow-night
]]

It’s even better with full transparency and some custom colors:

vim.api.nvim_set_hl( 0, "FoldColumn", { bg = "none" } ) -- transparent background for the folded column
vim.api.nvim_set_hl( 0, "Folded", { bg = "#4b4b4b" } ) -- set background color for line used for closed folds
vim.api.nvim_set_hl( 0, "LineNr", { bg = "none" } ) -- transparent background for the line numbers
vim.api.nvim_set_hl( 0, "Normal", { bg = "none" } ) -- transparent background for non-highlighted text
vim.api.nvim_set_hl( 0, "NormalFloat", { bg = "none" } ) -- transparent background for normal text in floating windows
vim.api.nvim_set_hl( 0, "NormalNC", { bg = "none" } ) -- transparent background for normal text
vim.api.nvim_set_hl( 0, "NotifyBackground", { bg = "#000000" } ) -- transparent background for notifications
vim.api.nvim_set_hl( 0, "SignColumn", { bg = "none" } ) -- transparent background for sign column
vim.api.nvim_set_hl( 0, "SpellBad", { fg = "#ff0000", italic=true } ) -- set foreground color for bad spelling
vim.api.nvim_set_hl( 0, "SpellCap", { fg = "#de935f", italic=true } ) -- set foreground color for missing capitalized letters
vim.api.nvim_set_hl( 0, "SpellLocal", { fg = default } ) -- set foreground color for word that is recognized by the spellchecker as one that is used in another region
vim.api.nvim_set_hl( 0, "SpellRare", { fg = default } ) -- set foreground color for rare word 
vim.api.nvim_set_hl( 0, "Visual", { fg = "#000000", bg = "#de935f" } ) -- set colors for visual selection
vim.api.nvim_set_hl( 0, "htmlBold", { bold=true } ) -- color for bold markdown
vim.api.nvim_set_hl( 0, "htmlItalic", { italic=true } ) -- color for italic markdown
vim.api.nvim_set_hl( 0, "htmlStrike", { fg = "#ff0000", strikethrough=true } ) -- color for underline markdown

Even though I don’t use tabs anymore, here is how to customize tabs colors:

vim.api.nvim_create_autocmd("VimEnter", {
    command = "hi TabLineFill gui=bold guibg=none | hi TabLine gui=bold guibg=none guifg=#efefef | hi TabLineSel gui=bold guibg=none guifg=#70c0ba"
})

I now prefer using buffers and the barbar plugin.

╭── Cleaning neovim startup

Using the vim-obsession plugin to autosave my sessions, I don’t want the default screen or empty buffers to show up when I launch neovim. This is achieved with this code:

vim.cmd [[ function! DeleteEmptyBuffers()
    let [i, n; empty] = [1, bufnr('$')]
    while i <= n
        if bufexists(i) && bufname(i) == ''
            call add(empty, i)
        endif
    let i += 1
    endwhile
    if len(empty) > 0
        exe 'bdelete' join(empty)
    endif
        exe 'set cmdheight=0'
    endfunction ]]

vim.cmd [[ au VimEnter * call DeleteEmptyBuffers() ]]

Note that this command also includes exe 'set cmdheight=0' to hide the commandline. This is because I use the fine-cmdline plugin.

╭── Check spelling while writing, only for given filetypes

Having spellchecking on all the time can be a pain. For example it’s useless when writing code. I’ve decided to turn it on only for my emails (filenames starting with neomutt), my mastodon posts (filenames starting with tut) markdown,and text files.

    vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
        pattern = {"neomutt*", "*.md", "*.txt", "tut*"},
        command = "set spell spelllang=en_us"
    })

╭── Auto format given filetypes

I work with taskjuggler and want to auto indent its files (tji and tjp extensions) when I write them:

vim.api.nvim_create_autocmd("BufWrite", {
    pattern = {"*.tji", "*.tjp"},
    command = "execute 'normal gg=Gg;' | write
})
-- gg=G auto indents the entire file
-- g; moves cursor to last change in file

╭── Restore cursor shape when leaving neovim

vim.api.nvim_create_augroup("RestoreCursorShapeOnExit", { clear = true })
vim.api.nvim_create_autocmd("VimLeave", {
    group = "RestoreCursorShapeOnExit",
    command = "set guicursor=a:hor20"
})

╭── Save cursor position on exit

Here is how to save the cursor position when exiting a file . When opening a file, this will position you exactly where you left it:

vim.cmd [[ augroup save_cursor_position
autocmd!
autocmd BufReadPost * call setpos(".", getpos("'\""))
augroup END
]]

╭── What about keybindings?

Listing all my keybindings may not be of great interest to you, and may deserve their own post. Anyway, I have stored them in their own file located in $HOME/.config/nvim/lua and I call it with require('keybindings')


Thanks for your read. Hope it's been useful to you.


Interact with this post using Mastodon or

Comment on wwwgem's post

Copy and paste this URL into the search field of your favourite Fediverse app or the web interface of your Mastodon server.

✄ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈

More food for thoughts? Check other posts about: #(Neo)vim