My Vim configuration, with a one-command install. Companion to dot_tmux — the two are set up to work together, and to stay out of the way of an agentic editor (Claude Code) rewriting files underneath an open buffer.
git clone https://github.com/sarsanaee/dotvim.git ~/dotvim
cd ~/dotvim && ./install.sh
vimThat is the whole procedure on a new machine. install.sh is idempotent —
re-run it any time — and it backs up anything it replaces into
~/.dotfiles-backup/<timestamp>/.
| Flag | Effect |
|---|---|
--dry-run |
Print every action, change nothing |
--no-deps |
Don't touch the package manager |
--no-plugins |
Skip vim-plug and the plugin downloads |
./uninstall.sh removes the symlinks; --purge also deletes the plugins.
Your undo history in ~/.vim/undodir is never touched.
vimrc -> ~/.vimrc (symlink)
spell/en.utf-8.add -> ~/.vim/spell/en.utf-8.add (symlink)
install.sh installer
uninstall.sh reverses the installer
setup.sh forwards to install.sh (old name)
Nothing is copied, so editing vimrc in this repo takes effect immediately and
git status shows what you changed. Words you add with zg land in
spell/en.utf-8.add in the repo, so your dictionary follows you to the next
machine. Host-specific settings go in ~/.vimrc.local, which the vimrc sources
if present and git never sees.
The problem: an agent rewrites a file you have open, and Vim keeps showing you
the old contents until you force a reload — or worse, greets you with
W11: Warning: File has changed since editing started and makes you choose.
Three settings handle it.
set noswapfile " no E325 ATTENTION prompt when a file is touched externally
set autoread " permission to reload a buffer with no unsaved changes
set undofile " an agent's rewrite is not a one-way door: `u` still worksautoread on its own does nothing in a terminal — it is only permission. Vim
still has to be told to look, with checktime. That happens on three paths:
- Switching tmux panes. dot_tmux sets
focus-events on; the vimrc asks the terminal to report focus (see below). Coming back from the pane running Claude Code refreshes the buffer as you arrive. - A one-second timer. The backstop, and in practice the one that does the
work — focus events proved unreliable once ALE and clangd were running.
checktimeis astat()per loaded buffer, far too cheap to notice. <leader>r, when you want to force it.
The timer only fires in normal mode, so a buffer is never swapped out from under you mid-keystroke.
Your unsaved edits are safe. autoread reloads only buffers with no
unsaved changes. If you have edited the buffer and an agent rewrites the file,
Vim keeps your version and tells you the two have diverged — it will not
silently pick a winner. Verified with a modified buffer and an external
rewrite: the unsaved work survived intact.
Ubuntu builds Vim with +terminfo, and neither the xterm-256color nor the
tmux-256color entry carries a focus-reporting capability. So t_fe comes up
empty, Vim never sends the "start reporting focus" sequence, and
FocusGained / FocusLost never fire at all — no matter what tmux is
configured to do. The vimrc supplies the missing half:
let &t_fe = "\<Esc>[?1004h"
let &t_fd = "\<Esc>[?1004l"
execute "set <FocusGained>=\<Esc>[I" " or the replies arrive as literal "[I"
execute "set <FocusLost>=\<Esc>[O"| Concern | Resolution |
|---|---|
| Leader vs prefix | Vim's leader is Space, tmux's prefix is Ctrl+Space. No overlap. |
| Mouse | tmux has mouse on. set mouse=a hands clicks to Vim while it runs: drag selects, wheel scrolls the buffer, dragging a split border resizes it. Hold Shift to bypass Vim and get the terminal's own selection for copying — the same escape hatch dot_tmux documents. |
| Wide panes | ttymouse=sgr. The legacy mouse protocol cannot express a column past 223, so without it clicks on the right of a wide pane land in the wrong place. |
| Cursor shape | tmux 3.4 parses DECSCUSR and re-emits it through the outer terminal's Ss/Se, so the raw t_SI/t_EI sequences are correct inside and outside tmux. No DCS passthrough wrapper needed. |
| True colour | dot_tmux forwards RGB (terminal-overrides ...:RGB) and COLORTERM=truecolor survives into the pane, so termguicolors is switched on when that variable says the terminal can take it. Inside tmux Vim must be handed the RGB escapes by hand (t_8f/t_8b) — terminfo does not advertise them. |
| Alternate screen | dot_tmux's smcup@:rmcup@ override applies to tmux ↔ the outer terminal, not to Vim ↔ tmux. Vim still restores your shell on :q. Checked, not assumed. |
Alt+Up |
Bound by tmux at the root level for copy mode, so Vim never sees it. Nothing here uses it. |
Six settings in the old vimrc did nothing at all. They are worth listing because each one failed silently — no error, just no effect.
| Was | Problem |
|---|---|
if executable('rb') |
Typo for rg, so g:rg_derive_root was never set and :Rg searched from the cwd instead of the repo root. |
g:ctrlp_user_commad |
Missing an n. CtrlP never saw it. |
g:ctrlp_user_cache |
Not an option. The real name is g:ctrlp_use_caching. |
set smartcase |
Without set ignorecase, smartcase has nothing to refine and does nothing. |
b:ale_fixers |
Buffer-local in a vimrc, so it applied to at most the first buffer — and it named prettier and eslint, two JavaScript tools, for C. |
highlight ColorColumn above plug#end() |
:colorscheme resets every highlight group, so the override was wiped before it was ever drawn. |
Two more were actively harmful:
autocmd VimEnter * silent !echo -ne "\e[2 q"spawned a shell on every start, forcing a full redraw — and underdash(Ubuntu's/bin/sh)echo -neprints a literal-ne.t_EIalready does the job.setup.shranrm $HOME/.vimrcwith no backup, thencp, so any local edits were destroyed on every run. It also passedcurl -k— certificate verification disabled for a script that is downloaded and then executed — and never installed Vim itself, which is how a machine ends up runningvim.tinywith no+clipboardand no plugins.
.gitmodules declared six bundle/* pathogen submodules. There were no
gitlink entries in the index and no bundle/ directory: the repo moved to
vim-plug years ago and the file had been dead weight since. Removed.
Leader is Space.
| Key | Action |
|---|---|
<leader> h j k l |
Move between splits |
<leader> + / - |
Widen / narrow the split |
<leader> m |
Maximize this split, toggle |
<leader> pv |
Open the file explorer in a 30-column split |
| Key | Action |
|---|---|
Ctrl+p |
Fuzzy file finder (CtrlP, backed by ripgrep) |
<leader> ps |
:Rg — search the repo |
<leader> tt |
Tagbar, the file's symbol outline |
<leader> u |
Undo tree |
| Key | Action |
|---|---|
<leader> gd |
Go to definition |
<leader> gr |
Find references |
<leader> gh |
Hover / type info |
<leader> gf |
Fix (clang-format, honouring a project .clang-format) |
[w / ]w |
Previous / next diagnostic |
These fill the slots the commented-out YouCompleteMe mappings used to hold. ALE + clangd needs no compile step, unlike YCM.
Deliberately not set clipboard=unnamedplus: that routes every delete and
change through the system clipboard too, so dd silently destroys whatever you
copied from the browser.
| Key | Action |
|---|---|
<leader> y |
Yank to the system clipboard |
<leader> Y |
Yank to end of line |
<leader> v |
Paste from the system clipboard (mirrors tmux's prefix Ctrl+v) |
| Key | Action |
|---|---|
<leader> s |
Toggle spell check in this buffer |
<leader> r |
Force a re-read of files changed on disk |
<leader> Space |
Clear search highlight |
Spell check runs on prose only — markdown, text, git commits, LaTeX, mail. It
used to be global, which underlined every identifier in C and C++. <leader>s
turns it on anywhere you want it.
- Requires Vim 8.2+. Check with
vim --version.vim.tiny(what Ubuntu gives you asviout of the box) will not do — the installer tests for+clipboardrather than for the binary, so it catches that case. set nocompatibleis deliberate. Plainvimimplies it when a user vimrc exists, butvim -u path/to/vimrcdoes not — and tagbar, ctrlp, vim-maximizer and vim-ripgrep all begin withif &cp | finish, so without it they load and silently define none of their commands.- netrw's banner is off (
g:netrw_banner = 0). It was2, which is not a valid value for a boolean and just meant "on". Set it to1to bring it back. install.shfinishes by asking the running Vim what it actually ended up with —&autoread,&mouse, whether each plugin defined its commands — and re-loads the config a second time underTERM=tmux-256color. It reports what is true, not what the config file asked for.