Terminal/vim setup
My terminal and vim setup
Terminal
I use alacritty as a terminal and tmux to control 'sessions'/'window groups' rather than using a terminal which has all this stuff built in, or doing things with different folders inside vim itself. I tend to open one terminal per 'project', start tmux in the relevant folder, and then use different tmux windows or panes to do different things. If I open another project, I create a new terminal, probably a new workspace, and keep everything in there. This means I don't really care about vim startup time as I just keep one vim instance open at all times per 'project'.
I use a few of the 'reinvented' unix commands like exa (mainly just for the 'tree' mode), rg, delta, sad, and fd. bat is used extensively in my terminal and vim setup for nice preview windows.
Wherever possible I use a gruvbox dark theme for code highlighting, previews, editors, etc.
fzf-tmux
integration means the fzf
windows are a lot nicer, and I've got it set up such that popup windows in vim are styled (sort of) the same to keep a consistent visual style between the terminal and inside vim. The relevant part inside my .profile
, hacked together from various wiki pages and readmes:
Fzf options
# Make fzf completion apply to exa as well
_fzf_setup_completion path exa
_fzf_setup_completion dir exa
# use fd instead of 'find' for searching
_fzf_compgen_path() {
fd --hidden --follow --exclude ".git" . "$1"
}
_fzf_compgen_dir() {
fd --type d --hidden --follow --exclude ".git" . "$1"
}
export FZF_DEFAULT_COMMAND='fd --type f --strip-cwd-prefix'
export FZF_DEFAULT_OPTS='--color=bg+:#3c3836,bg:#32302f,spinner:#fb4934,hl:#928374,fg:#ebdbb2,header:#928374,info:#8ec07c,pointer:#fb4934,marker:#fb4934,fg+:#ebdbb2,prompt:#fb4934,hl+:#fb4934 --layout=default'
# Use 'bat' for preview windows
BAT_PREVIEW='bat --style=numbers --color=always --line-range :500 {}'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
export FZF_CTRL_T_OPTS="--preview '$BAT_PREVIEW'"
export FZF_ALT_C_COMMAND='fd --type d --strip-cwd-prefix'
# Makes alt-c show a preview of the folder in a tree view
export FZF_ALT_C_OPTS="--preview 'exa --git-ignore --icons --tree {} | head -200'"
export FZF_TMUX=1
export FZF_TMUX_OPTS='-p 80%,60%'
These can be used together in various combinations to make new commands as well. Because of a combination of delta
/bat
, all these commands can be configured to have proper syntax highlighting as well:
New commands
# preview what would happen if $1 was replaced with $2
replace() {
fd --type f | sad --fzf='--disabled' --unified=10 --pager='delta --features=sad-diff' $@
}
# Fancy git diff
gdiff() {
git diff --numstat $@ | $(__fzfcmd) --preview 'git diff -w {3} | delta --features=sad-diff' --preview-window '70%'
}
# git history for $1, ordered by commit
ghist() {
git log --color=always --pretty=oneline --abbrev-commit -- $1 | $(__fzfcmd) --ansi --disabled --preview "git show -w {1} -- $1 | delta --features=sad-diff" --preview-window '80%,down'
}
# fancy git show
gshow() {
git show --numstat --no-notes --pretty=oneline $1 | tail +2 | $(__fzfcmd) --preview "git show -w $1 -- {3} | delta --features=sad-diff" --preview-window '70%'
}
# interactive search, open in vim with enter
rgf() {
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
INITIAL_QUERY="${*:-}"
IFS=: read -ra selected < <(
FZF_DEFAULT_COMMAND="$RG_PREFIX $(printf %q "$INITIAL_QUERY")" \
$(__fzfcmd) --ansi \
--disabled --query "$INITIAL_QUERY" \
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
--delimiter : \
--preview 'bat --style=numbers --color=always --line-range :500 {1} --highlight-line {2}'
)
[ -n "${selected[0]}" ] && vim "${selected[0]}"
}
I can even add new commands, like adding a binding for <C-g>
to show git files in an fzf
window:
Git binding
__fzf_gitfiles() {
git -C "$1" ls-files | $(__fzfcmd) --preview "$BAT_PREVIEW"
}
__fzf_git_widget() {
git_root=$(git rev-parse --show-toplevel 2>/dev/null)
if [ -z "$git_root" ]; then
echo "No git files"
return 1
fi
local selected=$(realpath --relative-to $(pwd) $git_root/"$(__fzf_gitfiles $git_root)")
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$((READLINE_POINT + ${#selected}))
}
bind -m emacs-standard -x '"\C-g": __fzf_git_widget'
bind -m vi-command -x '"\C-g": __fzf_git_widget'
bind -m vi-insert -x '"\C-g": __fzf_git_widget'
Example
vim config
Though I mainly use Intellij, I still use vim (Neovim to be precise) for quite a few things. Not going to go into too much detail about configuration here as everyone has their own plugins, key maps, and so on, but hopefully I can show some plugins which you might not know about.
Autocomplete
I use Coc for completion in vim. I've tried most of the other ones (deoplete
, neocomplete
, ddc
, ycm
, probably a couple of others) and it's the one that both worked the best with nix and has the most plugins. A list of the plugins I'm currently using:
coc-go
coc-yaml
coc-json
coc-fzf
coc-tabnine
coc-pyright
coc-ultisnips
coc-snippets
coc-highlight
coc-clojure
coc-diagnostic
coc-lua
# I don't find the git completions useful
#coc-git
# Not useful as the clojure lsp provides better results and integrates with coc already
#coc-conjure
You can find these just by searching Google, and they're all in the nix packages already so can be used easily.
File tree
I don't find any of the 'IDE style' list of files at the side of the window to be that useful in vim. I use it in Intellij, but in vim I much prefer the netrw-style file explorers. netrw is fine, but I've tried quite a few other ones and found that fern behaves the best, has the most customisations, etc. The plugins I have alongside it:
fern-renderer-nerdfont
fern-nerdfont
fern-hijack
fern-preview
I have these key bindings set up for opening splits:
map <leader>e<C-x> :Fern . -opener=split -reveal=%<CR>
map <leader>e<C-v> :Fern . -opener=vsplit -reveal=%<CR>
map <leader>e<C-t> :Fern . -opener=tabedit -reveal=%<CR>
map <leader>e<CR> :Fern . -opener=edit -reveal=%<CR>
This is similar to other vim key bindings - <C-t>
opens in a new tab, <C-v>
opens in a vertical split, etc.
Searching
I use fzf.vim for this. Again, I've tried a lot of other ones like telescope (and I used to use ctrlp) but this one just tends to work the nicest and can be tuned to behave like fzf
on the command line so I don't need to remember 2 sets of key bindings. It also has integration with CoC via coc-fzf to make all the CoC search windows a lot nicer. An example keybinding:
map <leader>t :Files<CR>
map <leader>g :GFiles<CR>
This mirrors the <C-t>
and my custom <C-g>
keybindings for fzf
in the terminal. I try to have most of the settings mirrored like this between the terminal and vim.
Treesitter
I use nvim-treesitter with all grammars installed (easily done via nix). There is also nvim-treesitter-context which shows a nice little header showing which function you're currently in (if you're editing a massive function). nvim-ts-autotag helps with closing tags in a much more sensible way than a lot of other plugins I've tried. nvim-ts-rainbow (which now seems to be unmaintained) just does rainbow parentheses based on treesitter output.
Session management
There's a lot of plugins that wrap vim's builtin session management, auto-session is a nice simple session manager that does everything by itself in the background. vim-lastplace is another simple one that works nicely with it and just reopens your terminal at the last place in a file.
Lisp things
I got into writing a bit of Fennel recently and tried a load of vim plugins to make development easier. Some ones that were particularly useful:
- nvim-parinfer there's a lot of plugins that provide this behaviour - this one was just the one that seemed to be least buggy.
- vim-sexp-mappings-for-regular-people provides some useful key bindings over the top of vim-sexp which just make it a bit easier to use.
- conjure makes working with any kind of lisp in vim incredibly easy and has integrations with Coc.
Treesitter and nvim-ts-rainbow help a lot with just the syntax highlighting - I don't have syntax on
set in my vimrc.
Other random useful plugins
Other than all the normal ones that everyone already has installed (fugitive, etc.):
- which-key.nvim is a real life changer once you've got it configured. You can document all the weird keybindings you have and easily add new ones.
- vim-peekaboo for seeing what's in your buffers. Works nicely alongside which-key.
- toggleterm.nvim I don't use a terminal separately and normally just open another tmux window or pane, but this can be useful in some circumstances.
- vim-airline and vim-airline-themes.
- vim-visual-multi for multiple cursors.
- undotree for a nice undo tree.
- nvim-autopairs really helps to detect pairs and close them better than any other plugin I tried.
Example
This is a (slightly old) example of what it looks like with these plugins installed:
Creating a reproducible vim installation with nix
Now that I have all the plugins I want to use, and I have my big vimrc, I want it to be easily portable without having to remember to install a load of programs globally, downloading plugins, running install scripts, etc. With nix, I just need to create the .nix file that will create the appropriate derivation. Luckily most of the plugins are in the nix package repo already, but some of them aren't - in that case, it's easy to create a new one:
{ pkgs ? import <nixpkgs> { } }:
let
nvim-parinfer = pkgs.vimUtils.buildVimPlugin {
name = "nvim-parinfer";
src = pkgs.fetchFromGitHub {
owner = "gpanders";
repo = "nvim-parinfer";
rev = "82bce5798993f4fe5ced20e74003b492490b4fe8";
sha256 = "1dzhlaxxf6cf6jcd2a5y97y7li6wkwjhg1q7rny3lkwrf2kxyp0f";
};
};
...
I separate each bit of config out into a separate file:
- The vimrc file
- A
packages.nix
file which can be imported from a shell.nix, in a nixOS configuration.nix, or a project-specific shell.nix, if you really want to have a project-specific vim setup - A
shell.nix
file which can be used on a non-nixos OS to just start this 'reproducible' vim setup
The shell.nix file is just this:
{ pkgs ? import <nixpkgs> { } }:
let
packages = import ./packages.nix {
vimrc = (builtins.readFile /path/to/nix-vimrc);
pkgs = pkgs;
};
in
pkgs.mkShell {
buildInputs = packages.buildInputs;
}
packages.nix is like this:
{ vimrc, pkgs ? import <nixpkgs> { } }:
let
nvim-parinfer = pkgs.vimUtils.buildVimPlugin {
name = "nvim-parinfer";
src = pkgs.fetchFromGitHub {
owner = "gpanders";
repo = "nvim-parinfer";
rev = "82bce5798993f4fe5ced20e74003b492490b4fe8";
sha256 = "1dzhlaxxf6cf6jcd2a5y97y7li6wkwjhg1q7rny3lkwrf2kxyp0f";
};
};
# other custom packages ...
buildInputs = with pkgs; [
git
universal-ctags
fd
bat
delta
fzf
patdiff
# other dependencies ...
neovim
(neovim.override {
vimAlias = true;
configure = {
packages.myPlugins = with pkgs.vimPlugins; {
start = [
(nvim-treesitter.withPlugins (plugins: pkgs.tree-sitter.allGrammars))
nvim-treesitter-context
nvim-ts-rainbow
vim-airline
vim-airline-themes
vim-colorschemes
vim-devicons
glyph-palette
# other plugins ...
];
opt = [ ];
};
customRC = vimrc;
};
}
)
];
in
{ inherit buildInputs; }
Then simply running nix /path/to/shell.nix vim
will start vim. However, this still has a few problems - it's pretty slow to start up, and you can't pass arguments to it as expected because of the way nix shell invocations work. To get around these issues, I use cached-nix-shell and have an alias in my .profile
like this:
vim() {
cached-nix-shell /path/to/shell.nix --exec vim $@
}
Now I just need to install nix on whatever machine I'm running on and run vim
in the terminal and it will download and install all the dependencies for me.