Another try at using Neovim more seriously

Advent of Code is always a good opportunity to pick up a new programming language, or do anything else to alter your workflow for a bit.

Privately, I've mostly been writing Rust recently (2-3 years?), and for that I've been using Visual Studio Code, because it's insanely painless to get started with, and I honestly don't really value deep customization that much usually. I just want it to work, and I'll get used to the warts.

Well, in a bid to change that fact about myself, I want to do all of the next AoC in Neovim[1]. Specifically nvim-qt, because Windows (for now). Though I also have a laptop that runs a *nix, and I will sync my config over there eventually and see how it goes.

To be quite honest, I've used both vim and nvim in the past, but even then, mostly just running on defaults and maybe three plugins. Before I started using Obsidian, I tried vimwiki for largely the exact same thing. I also administer a remote machine, doing all my editing through vim there.

Overall, I don't think I'm very good at controlling vim, but I'm not really intimidated by it either. I just never majorly customized it. I explicitly don't want to use a distribution like LazyVim or AstroNvim, because I think I'll be more effective in overcoming my config-complacency by adding things proactively, rather than than removing them from a complete package.

So, that's my plan for AoC2023: Solve the problems, note down things that annoyed me (both config/tooling-wise and my-ability-to-vim-wise), then consciously fix them. Also, write a post about doing that every day.

I do want to have a functioning editor for when I start, so I'm gonna set up at least some sane basics (like line numbers) and an LSP. That's this post.

...or it would be, but kickstart.nvim exists. It's more or less a single-file config "template" that gives you something to work with beyond an empty init.lua.

So instead of starting from nothing, I'll spend this time making it my own a little bit.

First: I don't like very big config files

As of time of writing, kickstart.nvims init.lua is 600 lines; though a lot of that is helpful explanatory comments and section headers. So let's go through it, stripping comments we don't need, and breaking sections up into separate files. Big scary blocks I'll just shuffle away without worrying about them just yet.

After I complete my first side quest: Pasting things from clipboard into Nvim.

Splitting stuff up.

Now that I've pasted in my very own copy of kickstart.nvim, let's get started. First order of business would honestly be taking all the sections (separated by nice comment section headers), and splitting them out into separate files.

Scanning through it, roughly there's six sections or so:

I think require is how you load extra files, so I quickly :help require:

from :help require

Modules are searched for under the directories specified in 'runtimepath, in the order they appear. (...)

:help runtimepath lists a dozen or so sub-folders it tries to load from, the most appropriate one seems to be lua/. Soooo, one :!mkdir lua and :e lua/settings.lua later, I can clumsily delete the basic config lines from my init.lua and put them in lua/settings.lua instead; and replace the entire thing with a simple require('settings'). I'll be honest, I used visual mode for this one... but one day I'll probably be better at just hitting a magic keystroke sequence to copy it all.

Anyway, do that five more times, and I end up with this (after a short detour learning about modelines):

-- Install `lazy` package manager, if missing.
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', -- latest stable release
    lazypath,
  }
end
vim.opt.rtp:prepend(lazypath)

-- Set leader key as early as possible.
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '

-- Load packages.
require('lazy').setup('plugins')

-- Load my configurations.
require('settings')
require('keymaps')
require('telescope-config')
require('treesitter-config')
require('lsp-config')

-- A `modeline`. When placed within 5 lines of the end or start of the document,
-- it's active and applies the settings listed after the colon, only for this buffer.
-- Frankly, I cannot imagine ever using this feature.
-- vim: ts=2 sts=2 sw=2 et

Factoring out the humongous plugin listing that it comes with by default took reading the README for lazy.nvim—it mentions that the argument to .setup() can be either a Lua table (as was the case in the original kickstart.nvim), or a string referring to the module to load instead. Then inside that module (lua/plugins.lua in my case), you just return the table that was inlined instead:

return {
	'tpope/vim-fugitive',
	'tpope/vim-rhubarb',
	
	...
}

Funnily enough, reading lazy.nvims README also showed me it was the source of snippet for bootstrapping/installing lazy present in kickstart.nvim. Going back and reading first sources is really paying off for me getting a general overview already.

Now it's time to finally launch the thing with a config!

And immediately, Windows happens lol.

A bunch of plugins start installing themselves, I think Mason pops up by itself? And after a couple seconds, I get an error message. treesitter can't find a C compiler, which it needs. I end up installing msys2, then gcc through pacman. Aaaand adding that to $PATH.

Restarting.

Now treesitter correctly installs itself. I think. It installs a bunch of stuff like grammars for C++ or JavaScript, which I don't really want or need for now. I'm sure it says to download them in the config somewhere. I'll get around to it.

Aaaand it's there!

Pasted image 20231109165006.png

That definitely looks like a code editor.

And then some more issues

Though, immediately, I get some annoying message asking me stuff I don't understand:
Pasted image 20231109165340.png

I say 3, aaaand
Pasted image 20231109165357.png

Alright. That's an LSP thing, so off to lua/lsp-config.lua I go.

local servers = {
  -- clangd = {},
  -- gopls = {},
  -- pyright = {},
  -- rust_analyzer = {},
  -- tsserver = {},
  -- html = { filetypes = { 'html', 'twig', 'hbs'} },

  lua_ls = {
    Lua = {
      workspace = { checkThirdParty = false },
      telemetry = { enable = false },
    },
  },
}

I should read more about how exactly this whole configuration works, but thankfully kickstart.nvim already had the whole Lua.workspace.checkThirdParty thing set up, just... with the wrong value, I guess?? Maybe it changed recently, and I just had bad luck or something. Changing that false to "Disable" did fix my issue. And while I'm here, I uncommented rust_analyzer, too, since I wanna be writing Rust.

And that's the minimum viable setup, I guess

Pasted image 20231109170329.png

Yeah, that's pretty close to all I need, actually. One thing that's Rust-specific, is that it seems to be using cargo check instead of cargo clippy to produce diagnostics. That's something I'll have to look into. And I noticed it doesn't autoformat, though the comments in kickstart.nvim mentioned something about that.

But for now, I'm satisfied. I started a repo with my config, maybe I'll push it to my github at some point—and that's where I leave it until December (or until I do old Advent of Codes because I can't wait).

Open points this session

  • go through all plugins included in kickstart.nvim; learn about them, or prune them
  • enable format-on-save for code files
  • use cargo clippy to produce diagnostics
  • review motions so I don't hold j/k to navigate through files


  1. Literally the first word you see on their page is "hyperextensible", lmao. ↩︎