One repository for shared Vim/Neovim, Zsh, Starship, and tmux configuration.
Machine- and organisation-specific shell settings stay in
~/.config/zsh/local.zsh, outside Git.
Git follows the same pattern: shared defaults live in git/config, while
credentials and host-specific overrides stay in ~/.config/git/local.gitconfig.
git clone git@github.com:freeyoung/dotfiles.git ~/dotfiles
bash ~/dotfiles/install
exec zshinstall is idempotent and networked by default. It installs the shared
command-line dependencies, creates symlinks, restores shell/Vim plugins, and
backs up any conflicting target under ~/.dotfiles-backups/<timestamp>/.
Use bash ~/dotfiles/install --links-only when only links and local
configuration should be refreshed, or --skip-plugins when dependencies are
welcome but plugin downloads should wait.
The script links ~/.vim to vim/ for compatibility with existing Vim
tooling. Plugin files are shared under ~/.local/share/vim, while persistent
state is separate under ~/.local/state/vim and ~/.local/state/nvim (or
their XDG equivalents).
On macOS, the installer installs Homebrew when necessary and applies the
shared Brewfile. Linuxbrew uses the same path on Linux. The
installer verifies the GNU userland used by this configuration (gls,
ggrep, gsed, and gawk) and stops if it is incomplete. Install
Linuxbrew on Linux before running the bootstrap; the required formulas are
coreutils, grep, gnu-sed, and gawk. The installer never changes
the login shell.
Copy zsh/local.zsh.example to ~/.config/zsh/local.zsh to add private
machine settings. The installer does this once automatically and preserves it
on later runs.
Shared Zsh settings are loaded in a deliberate order from zsh/modules/:
environment, runtimes, commands, completion, Node/tool integrations, prompt,
plugins, then interactive bindings. zsh/zshrc is only the interactive guard
and loader; keep machine-specific settings in ~/.config/zsh/local.zsh.
On first run, an existing ~/.gitconfig is moved to
~/.config/git/local.gitconfig, then replaced with a link to the shared Git
defaults. This keeps credential helpers and private URL rewrites out of Git.
Run the non-interactive Vim smoke test after installation:
bash ~/dotfiles/scripts/check-vim-config.shVim plugin revisions are recorded in vim/plugins.lock.vim,
which the default installer restores after downloading vim-plug. After
updating plugins intentionally, regenerate the snapshot from the repository
directory with:
:PlugSnapshot! ~/.vim/plugins.lock.vimCommit the resulting change. Restoring the snapshot accesses the network and checks out the pinned revisions.
Both editors share one configuration: ~/.vimrc and ~/.config/nvim/init.vim
(the latter symlinked by install to vim/nvim_init.vim) both
source vim/vimrc. nvim_init.vim extends runtimepath to the
Vim configuration directory first,
since Neovim's default runtimepath doesn't include ~/.vim. A handful of
has('nvim') branches account for real differences between the two (Neovim
dropped the pastetoggle option, so paste toggling is an explicit
<F12> mapping instead; some vim-airline extensions auto-enable under
Neovim regardless of whether their backing library is present and are
explicitly disabled in vim/config/plugins.vim). Neovim is optional — install
links its config the same way regardless of whether nvim is installed.
The configuration is split into vim/config/ by responsibility: options, file
types, plugin settings, LSP, mappings, autocmds, and terminal settings.
freeyoung/vim-tomorrow-theme
(Tomorrow-Night-Bright) is the active default, a fork of
chriskempson/vim-tomorrow-theme
with fixes upstream never made:
- The color-setting logic was gated only on the legacy
&t_Co, which Vim and Neovim can report differently, so Vim silently keptNormalunset while Neovim applied the theme correctly. The fork also checks&termguicolors, so both editors render identically (verified by diffing:highlightoutput directly) without a globalset t_Co=256workaround. - The original shipped no airline theme at all, falling back to
vim-airline-themes' generictomorrow.vim, which — like most community-contributed airline themes — has no dedicated commandline-mode palette and silently reuses Normal's colors. The fork addsautoload/airline/themes/tomorrow_bright.vimwith full mode coverage, including commandline (orange, not Replace's red — less alarming for routine:commands), using the same hex values as the colorscheme itself. - The colorscheme's own
StatusLine/StatusLineNCused a"reverse"attribute (a 2013-era convention predating airline-style per-segment statuslines). Neovim's TUI leaks that into the entire statusline row as unconditional reverse video — including airline's own segment colors and the separator colors airline computes by reading back already-applied group colors — which Vim does not replicate. sonokai's colorscheme, by contrast, never puts"reverse"onStatusLineat all, which is why sonokai-based setups never hit this. Fixed at the source (dropped"reverse", colors swapped to preserve the exact original look without depending on it) and defended invim/config/plugins.vim(has('nvim')only: clearsStatusLine/StatusLineNC's reverse attribute on everyColorSchemeevent, in case some other theme has the same issue). Verified via raw PTY capture, not just:highlightintrospection — the highlight definitions looked correct in isolation even when the actual rendering wasn't.
:colorscheme Tomorrow-Night-Bright | :AirlineTheme tomorrow_brightSeveral other colorschemes (sonokai, onedark.vim, edge, everforest,
gruvbox-material, catppuccin, papercolor-theme, ayu-vim, base16-tomorrow-night
via tinted-vim) were evaluated and rejected along the way — mainly for
missing airline commandline coverage, being Neovim/Lua-only (breaks the
shared-config setup), or, for base16, having Identifier equal Normal
(no color distinction for YAML/Ansible keys). See git history on
vim/config/plugins.vim and vim/plugs.vim for the full trail.
tmux.conf is linked to ~/.tmux.conf. It enables true color,
mouse support, large scrollback, and system clipboard integration. Splits
preserve the current directory; h/j/k/l navigate panes and prefix + r
reloads the configuration.
The status line follows Tomorrow Night Bright: session, host, date, and time use blue, cyan, yellow, and green segments. Time includes seconds and refreshes every second. Copy-mode uses yellow selection with black text.
- Files over 5MB (
g:vim_large_file_bytesinvim/config/autocmds.vim) skip undo history and syntax highlighting on open. synmaxcol=500caps syntax scanning on very long lines (minified JS, data dumps).NERDTreeand the filetype-specific syntax plugins (vim-markdown,vim-javascript,html5.vim,vim-yaml,ansible-vim) are lazy-loaded via vim-plug'son/for, not sourced at startup unless actually used.- LSP diagnostics keep their signs and highlights quiet during Insert mode, then refresh when you return to Normal mode.
%-matching comes from Vim's bundledmatchitpackage (packadd!invim/config/filetypes.vim) instead of a vim-plug-managed checkout.
The leader is explicitly set to \.
| Keys | Mode | Action |
|---|---|---|
F1, \w |
Normal / Insert | Save the current file |
F2, Ctrl-Q |
Normal / Insert | Quit the current window |
F3 |
Normal / Insert | Save and quit |
F5 |
Normal | Toggle NERDTree |
F12 |
Normal / Insert | Toggle paste mode |
Ctrl-H/J/K/L |
Normal | Move between split windows |
Shift-H/L |
Normal | Previous / next tab |
\+, \-, \_, \=, \[, \] |
Normal | Resize / equalize splits |
gd, gD, gr, gi, K |
Normal | LSP definition, declaration, references, implementation, hover |
\rn, \ca, \f |
Normal | LSP rename / code action / manual format |
Tab, Shift-Tab, Enter, Ctrl-Space |
Insert | Select, accept, or manually trigger LSP completion |
:w!! |
Command-line | Write the current buffer through sudo |
j and k move by screen line when wrapping is enabled. vim/config/keymaps.vim
contains general mappings; vim/config/lsp.vim contains LSP and formatting mappings.
\f formats Python with Ruff and formats other supported buffers through LSP. It
never formats automatically on save.
NERDTree remains available through F5 as a directory-oriented view.
termguicolors is enabled when supported.
vim-lsp supplies diagnostics, navigation, completion, code actions, and
formatting. The configured servers are Pyright and Ruff (Python), gopls (Go),
TypeScript Language Server (JavaScript/TypeScript), YAML Language Server, and
ansible-language-server. Language servers are deliberately not bootstrap
dependencies: when a catalogued server is missing, opening its file type
suggests :LspInstallServer; run that command to download it. Ruff is used for
both Python diagnostics and formatting (uv tool install ruff@latest).
ansible-language-server is not in vim-lsp-settings' catalog. Opening an
Ansible buffer without it shows the corresponding on-demand command:
npm install -g @ansible/ansible-language-server.
ansible-vim detects Ansible YAML by path (tasks/, roles/, handlers/,
group_vars/, host_vars/) and by filename (playbook.yml, site.yml,
main.yml, etc.); a supplemental rule in vim/config/filetypes.vim also covers a
generic playbooks/ directory with arbitrary filenames. It sets
filetype=yaml.ansible under Vim and plain filetype=ansible under Neovim
(ansible-vim's own upstream difference); both are handled everywhere they
matter (LSP allowlist, plugin lazy-loading).