Last winter, on a Saturday, I decided to tweak my vimrc. I'd been putting it off for a long time since I felt spending time on improving and cleaning up vimrc was too luxurious; it'd be fun, but it wouldn't pay back the invested time.
This time I determined to do myself a favor and enjoy spending a day or two working on my vimrc.
I ended up spending more than three months and making around 500 commits to my vimrc, and I still work on it from time to time – I didn't intend it, but coincidentally it's precisely 500 commits when I try to publish this article.
I've learned more than I'd learned in the past ten years during this time.
It was a fun and frustrating journey. I want to share what I've learned along the way and how happy I am now.
I started by deleting unused plugins and configurations, and then I looked for plugins to fix and improve a few things that were bothering me.
Once I got started working on it, I couldn't stop. Learning how things work and improving my daily workflows with the new knowledge was too fun and exciting.
To better understand vim help pages and plugin documents, I started checking out books.
Especially, Learning the vi and Vim Editors was good to brush up on my vim skills, and The VimL Primer was helpful to learn vimscript.
I could access all the books freely thanks to the O'Reilly membership that my employer provides me.
I checked out chapters that interested me instead of reading cover to cover. Reading books with the membership feels like listening to music with a music streaming service instead of having to buy CDs.
I read a few awesome guys' vimrc, such as
I learned many things from the books and vimrcs, but I still often had to read help pages and Stack Overflow threads to achieve what I wanted.
I learned that Vimscript is not that hard once you're good with vim.
When I learn a new programming language, I start writing tests with it. This method is called Test-Driven Learning.
Writing test code is an excellent way to learn a programming language because
Vader is a vimscript testing framework that is written by Junegunn. This is my Test-Driven Learning project using Vader: Github project.
I wrote a vim plugin Mintabline to solve a problem that Neovim doesn't show the tabline properly for terminal buffers.
I feel powerful after learning vimscript.
I used to spend hours and days to find out the reason for a vim issue, but now It usually takes no more than 30 minutes using the methods that I'll introduce soon.
Knowing how to fix issues is the key to utilizing vim to its full power – which is valid for any other technology.
I used to keep my plugin list lean to avoid randomly happening errors and slowness; I often didn't know where I should start to troubleshoot a problem.
I've tried tens of plugins for the past few months and faced loads of issues; it sometimes took a few weeks to resolve.
Vim plugins are powerful since they can control vim without having to reside in a sandbox or container, but this makes it harder to debug an issue.
:message
When you face an issue, the first thing you should do is to read error messages carefully, but an error message could quickly disappear when you do some actions in vim. In this case, you can use the :message
command to bring the message back.
However, it's impossible to easily copy the message or have the result window open while troubleshooting. You can load messages into a buffer using this function to tackle this problem (source wiki).
function! TabMessage(cmd)
redir => message
silent execute a:cmd
redir END
if empty(message)
echoerr "no output"
else
tabnew
setlocal buftype=nofile bufhidden=wipe noswapfile nobuflisted nomodified
silent put=message
endif
endfunction
command! -nargs=+ -complete=command TabMessage call TabMessage(<q-args>)
Once you have the code in your vimrc you can open a buffer with messages like this:
:TabMessage message
or simply
:TabMessage mes
You can use this function not only for messages but also for any other commands. For example, you will get a buffer list in a new buffer when you run this:
:TabMessage ls
You can try a manual binary search when it's not possible to find the root cause by just reading error messages, which is usually the case.
With this approach, you can search for problematic code by commenting out plugins, functions, or options – half of the code at a time to make it efficient.
To make this task even easier, I've modularized my vimrc (code link).
I can quickly turn on and off a specific module with this structure.
These would be steps to debug:
This approach can be applied to any issue when there's no clue.
git bisect
to find a problematic commitgit bisect
is another way to perform a binary search to pinpoint the cause of an issue.
It works like this:
# Start git bisect
$ git bisect start
# Tell Git that the current commit is bad
$ git bisect bad
# Say, 100 commits ago it was good
$ git bisect good HEAD~100
# Git starts bisecting.
# Run vim and see if the current commit is good
# and tell Git.
$ git bisect good
# Run this until git pinpoints
# the problematic commit.
$ git bisect bad
# Finish git bisect:
$ git bisect reset
It takes only 7 trials for 100 commits (math.log(100, 2) ≈ 6.65
).
I prefer this approach to the previous one since it requires less manual work, although I have to check out my Korean blog post about git bisect
whenever I run it to recall the commands for each step.
Surprisingly, reading documentation often works. You can first check out the vim help document of the plugin and see if it has relevant information.
If the plugin doesn't have a help document, visit the GitHub page of the plugin and see if there are any related issues.
To quickly open the GitHub page of a Plug line in vimrc, I use a function and command similar to this:
function! s:open_plug_gh()
let line = getline('.')
let line = trim(line)
let plug_regex = '\vPlug [''"](.{-})[''"].*'
let path = substitute(line, plug_regex, '\1', '')
let url = 'https://github.com/' .. path
exec "!open '"..url.."'"
endfunction
nnoremap <Leader>op :call <SID>open_plug_gh()<CR><CR>
For example, if you run the function on the line below, vim will open the GitHub page of fzf.vim
in the browser.
Plug 'junegunn/fzf.vim'
If you can't find any meaningful information from the documentation, you can check out the plugin's code. A vim package manager downloads plugin files into your machine, so it's easy to explore the files.
I have a mapping to find installed plugin files quickly with fzf
since I do it often.
nnoremap <leader>fpl :FZF ~/.vim/plugged<CR>
You can try fixing the issue directly in the downloaded files and see if it's fixed by reloading plugins or rerunning vim.
Sometimes I had to fix issues only for my environment. For example, the vim-bbye plugin and the close-buffer plugin conflict, but I wanted to use both. I just forked vim-bbye and deleted the conflicting code.
Lastly, you can look for help in a few places.
I once struggled with an issue with Conjure. I thought asking and waiting for an answer would take too long, but it turned out that it was a quicker way to solve the issue.
Looking for a Discord community is another way. I use coc-metals for Scala and was able to get help from the Discord server.
There are fantastic plugin maintainers out there.
Stack Overflow can be helpful as well. Once I even found out the answer to my issue while writing a question.
I once made a joke saying that I'll reach a break-even point for the time investment that I made if I write code until 80. That's an exaggeration, but it'll take a long time if I only calculate the saved seconds to recover the time investment.
But, we automate not to save time but to save mental energy; I can try more things with the same time and energy, which reduces problem-solving time.
And, I'm going to write code until after 80 anyway.
fzf
and nvim-tree
I've tried file managers like ranger and lf, but I did not use them due to a few issues.
After I started using vim as a file manager with fzf and nvim-tree, I realized that this is the file manager that I've been looking for. I can move around and search for tens of projects easily.
tmux
I haven't been using tmux
or screen
since they have conflicting keyboard shortcuts with command-line keyboard shortcuts.
When I needed to split a window and open a new session, I could do it with iTerm2 shortcuts. This approach has downsides, but worked okay for me.
With nvim, you can use terminals with customized mappings. Also, it's much easier to search, move around, and copy terminal outputs within nvim terminals.
I've been using tig and the command-line git client with lots of shell aliases and functions.
I'm now using vim-fugitive with vim-rhubarb, git-messenger, gv, fzf (again), and vim-signify. It took a long time to set things up and get used to them, but now the old ways feel ancient.
These are plugins that I use to write code.
I didn't have many chances to write a large amount of code using vim after I set up these, but they are at least helpful in writing scripts and navigating codebases.
It wasn't always easy to set things up. I spent weeks learning, customizing, and troubleshooting when I started using coc-metals
and conjure
. I faced a Lua version issue when I tried using coc-lua
. Nothing was free but they were worth it.
I've put the plugins for coding in a separate vimrc module (devplugins.vim) so that I can exclude them in the server environment.
The most successful people do not have the most productive development environment.
Once I started working on this, I didn't want to do anything else. Sometimes I was obsessed with minor improvements that wouldn't affect my productivity. Still, I think I'm far from mastering vim, and there are tons of things that I can try to tweak my productivity.
I learned a lot even from the effort that didn't work out, but it's essential to know the cost I spend on improving productivity – if I want to be genuinely productive.