My nifty vimrc
· Byron Torres
To resuscitate this blog and my writing skills, today I’d like to go through my vimrc and all-around vim configuration. Perhaps it might spark inspiration for you. You can see it in full here:
- Mirror:
- https://git.torresjrjr.com/.vim/files.html
- SourceHut:
- https://git.sr.ht/~torresjrjr/.vim/tree
This is a git repository of the ~/.vim
directory, which live at my $HOME and holds everything. In here we have:
.gitignore
README.md
bin/setup
gvimrc
vimrc
Let’s go through a typical set up of this config. This walkthrough will assume that you’ll used the :help
command if you see something you don’t understand. Take a look at my bin/setup
file, the first thing I run after cloning.
#!/bin/sh -eu
echo >&2 'local.vim ...'
grep -sq 'VimrcLocal' after/plugin/local.vim || {
mkdir -p after/plugin
printf 'VimrcLocal " %s\n' "$(id -un)@$(uname -n)" \
>> after/plugin/local.vim
}
echo >&2 'vim-plug ...'
curl -sSfL -o autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
It’s a shell script which when executed from within ~/.vim
does two things:
Creates a file at
~/.vim/after/plugin/local.vim
and writes a line like this:VimrcLocal user@host
. I’ll explain why soon.Downloads the vim-plug script from GitHub to
~/.vim/autoload/plug.vim
.
Junegunn’s vim-plug is a minimalist vim plugin manager. I would actually prefer to use something more minimal which uses vim 8.0’s packages feature, but sometimes I come across an old machine, and vim-plug does just fine for now.
:help packages
I immediately use vim-plug next. I open vim and I don’t run the :PlugUpgrade
command, since I’ve already just installed the latest version of vim-plug. I do run :PlugUpdate
, which updates my plugins, which are listed at the end of my vimrc. Take a look at it:
" Plugin Manager: junegunn/vim-plug
if !filereadable(expand('~/.vim/autoload/plug.vim')) | finish | endif
call plug#begin('~/.vim/plugged')
Plug 'https://github.com/junegunn/fzf.git', { 'do': { -> fzf#install() } }
Plug 'https://github.com/junegunn/fzf.vim.git'
Plug 'https://github.com/tpope/vim-surround.git'
call plug#end()
let g:plug_window='0tabnew'
People say you should put your plugins up first so they don’t mess with the rest of your config. I say, don’t install crap plugins which mess with the rest of your config.
I first check if vim-plug is installed (it might not be if I’m another user like root, or some other weird case), and if so, I declare 3 primary plugins. I also let g:plug_window='0tabnew'
so that those vim-plug commands run in a separate tab at index 0, to keep things tidy.
I have the vim-surround plugin installed, which allows me to modify and delete pairs of parentheses, brackets, braces, quotes, etc. easily. Such basic functionality feels like it’s missing from vim, so there I have it. I’ve also heard good things about vim-sandwich.
The fzf and fzf.vim plugins come as a couple. They give me superpowers. fzf is an excellent interactive fuzzy finder. I use it everywhere in my shell scripts and in tmux.
Note, it is possible to get similar but less versatile functionality with plain vim and globing, using the find command, tab completion, and setting path+=**
. See:
:help cmdline-completion
:help starstar
:help 'path'
What about the inferred secondary plugins? Where do they go? In ~/.vim/after/plugin/local.vim
of course. This path is specified in my .gitignore
file, so the local.vim
file can be modified in peace. This filepath leverages vim’s initialisation process, which sources certain default filepaths, specified by the runtimepath
. See:
:help 'runtimepath'
:help afer-directory
This means the local.vim
file is sourced after my vimrc
file, so I can add or override anything there. I can have per-machine config. You can get very creative with the runtimepath
if used well, so use it! Take a peak at my .gitignore
file for a clue for other interesting paths.
But I’ve also got another trick up my sleeve. Take a look at around the middle of my vimrc.
command VV <mods> new $MYVIMRC
command VL <mods> new $MYVIMRCLOCAL
command VimrcLocal let $MYVIMRCLOCAL = expand('<sfile>:p:~')
Remember :VimrcLocal
from the setup script? This was placed in the local.vim
file. When vim initialises, it runs that command, which sets $MYVIMRCLOCAL
to the filepath of the file where that command is written. That’s what expanding <sfile>
(“source file”) provides. I also create the short and handy :VL
command, which opens that file using that variable. Wanna write a quick vim script hack? :VL
. Boom. Cool, eh?
In local.vim
files scattered across multiple machines, I have a few of these hacks. Usually, they contain an override list of plugins, some mappings, and a function or two. Just some bits and bobs which are subject to change, or which fix a quirk of the local machine. Here’s one of my local.vim
files with some annotations.
VimrcLocal " b@ace
set visualbell
" update website
nnoremap <Leader>uu :0tab term ++close ../bin/update<CR><C-W>g<Tab>
nnoremap <Leader>uU :0tab term ++open ../bin/update<CR>
nnoremap <Leader>ua :0tab term ++close ../bin/update all<CR><C-W>g<Tab>
nnoremap <Leader>uA :0tab term ++open ../bin/update all<CR>
" hare run (https://harelang.org)
nnoremap <leader>h <C-W>j:close <bar> w <bar> term ++rows=15 ++open hare run<CR>
nnoremap <leader>H <C-W>l:close <bar> w <bar> vert term ++open hare run<CR>
nnoremap <leader>m :w <bar> make<CR>
" email scissor snip annotation (for editing emails in aerc(1))
vnoremap <Leader>s s-%<-<esc>
" source $HOME/code/projects/vim/birck.vim/plugin/birck.vim
" local plugins (relisting the primary plugins first)
call plug#begin('~/.vim/plugged')
Plug 'https://github.com/junegunn/fzf.git', #{ do:{ -> fzf#install() } }
Plug 'https://github.com/junegunn/fzf.vim.git'
Plug 'https://github.com/machakann/vim-sandwich.git'
" additions:
Plug 'https://github.com/fatih/vim-go', #{ do:':GoUpdateBinaries' }
" this was dumb
Plug 'https://git.sr.ht/~torresjrjr/birck.vim', #{ on:'Birck' }
Plug 'https://git.sr.ht/~sircmpwn/hare.vim'
Plug 'https://git.sr.ht/~torresjrjr/gemini.vim'
Plug 'https://github.com/kalekundert/vim-nestedtext'
call plug#end()
" refresh logfile periodically
command -bang LogMode :call FnLogMode("<bang>")
function FnLogMode(bang)
if a:bang != '!'
set cursorline autoread
let g:logticker = timer_start(
\ 5 * 1000,
\ { -> execute('checktime | norm zz') },
\ #{repeat: -1}
\ )
else
call timer_stop(g:logticker)
set nocursorline
endif
endfunction
au FileType help :set nu
au FileType help :exe "norm <C-W>T"
" ...
As for my actual vimrc file, I’ll talk only about the interesting things which deserve an explanation or which can’t be explained with :help
.
au BufReadPost * exe "norm! g`\""
This is an autocommand, a command which runs everytime a buffer is read into memory (i.e. when you open a file). The command runs a normal mode key sequence which brings the cursor to the last known position the last time the file was open. This line actually comes as part of defaults.vim
, which I probably should use, but don’t. Meh.
:help g`
:help defaults.vim
Here are some nifty one-liners. This one makes opening the command-line window easier. Get used to editing commands on the fly.
noremap Q q:
This one overrides the refresh command and causes annoying search highlights to temporarily turn off.
noremap <C-L> :nohls<CR><C-L>
These two are just feel like obvious missing functionality.
noremap <C-J> :bnext<CR>
noremap <C-K> :bprev<CR>
These two in combination with the :vimgrep
and :copen
commands make detective work in a big project fun.
noremap <C-N> :cnext<CR>
noremap <C-P> :cprev<CR>
:help :vimgrep
:help quickfix-window
These “mapleader” mappings are sort of alien at first look.
let mapleader="\<space>"
noremap <leader><tab> :.term sh<CR>
noremap <leader>q :.!sh<CR>
noremap <leader>r :.r !
noremap <leader>w :.w !
The <leader>q
mapping in particular is very versatile. The way it’s used, in the current buffer or in a spare one, you write shell commands. Maybe something like:
date -I
mv -v file1 convoluted/dir/path/file1
mv -v file2 convoluted/dir/path/file2
Then, visually select what you want to execute (vip
), press <leader>q
, and the output of those commands replace the text. Using vim as part of a cohesive system works wonders. You can imagine similar examples with the other mappings.
These two come from fzf.vim, and create a fuzzy menu for files and buffers. Great for navigation.
noremap <leader>f :Files<CR>
noremap <leader>b :Buffers<CR>
Oh, and the sacred escape mapping. The one and only true way.
noremap! kj <esc>
Which can also be configured in your shell, by the way. Either using an .inputrc file, or a few of these commands according to your shell:
# bash
set -o vi # vi mode
bind '"kj":vi-movement-mode'
# zsh
bindkey -v # vi mode
bindkey "kj" vi-cmd-mode
The great thing about kj
compared to other sequences like jj
or jk
is:
The
kj
sequence is just as highly unlikely to appear in writing (sorry, Felix Kjellberg).In normal touch-typing posture, your middle finger is generally the longest, so
k
is naturally the first key you press. Roll over your right hand slightly to the left, and you naturally pressj
. It’s the most natural key sequence.In the shell, if you’re using a vi mode, you use
k
andj
to go up and down in command-line history. If you are unsure whether you’re in normal or insert mode, you can presskj
and not worry. If in insert mode, you’ll go into normal mode. If in normal mode, you’ll go right back to the current command. This allows you to develop a muscle memory which makes editing and using old shell commands easy.
And that’s it. I hope you’re learned something which can apply to you.