"" My |vimrc|
" TODO: Supply chain attacks are getting more common. We should use (mandate?)
" commit hashes in `GitAdd`. Also, make sure to use a command format that
" ignores tag/branch names (those can be named to look like hashes and are
" mutable). Maybe we deem some "publishers" worthy of trust, Tim Pope tags
" releases of many of his plugins for instance.
" TODO: Do we want to try to integrate with Debian? There seems to be at least
" two ways vim-related files are installed:
" - Purely auxilliary files as part of some other package: Files are installed
" to `/usr/share/vim/addons` which are referenced by files installed to
" `/usr/share/vim/registry`. `vim-addons` from the `vim-addon-manager` can be
" used to (presumably) symlink them into a path that is listed in
" 'runtimepath'.
" - As a standalone `vim-${addon}` package: Files are installed to
" `/usr/share/vim-${addon}`, presumably symlinked into a `start` or `opt`
" 'packpath' during package installation.
" and the `dh_vim-addon` man page in
" the `dh-vim-addon` package contains some further details.
" TODO: |unix| says
"
" > Temporary files (for filtering) are put in "/tmp". If you want to place
" > them somewhere else, set the environment variable $TMPDIR to the directory
" > you prefer.
"
" and since we go to some effort to keep other intermediate files in
" `~/.cache/vim` for privacy, we might want to create a shell alias that does
" `alias vim='TMPDIR="$HOME/.cache/vim/tmp" vim'`, as well as making sure that
" directory exists in shell startup files.
"
" Actually, I'm pretty sure we can do this from inside Vim (requires 'eval'
" though), which might be cleaner (although some dotfiles for other programs
" that do similar things do it in shell startup files). Do both?
"
" NOTE: This is an environment variable and affects child processes, including
" ones run from the `:terminal`.
""" Formatting
" A |'textwidth'| of 79 is used throughout.
" Comments are written in Vim's help |notation|, see |help-writing|. They can
" be highlighted with
" .
" Section headings are not written in the help |notation|, but are instead
" indicated with multiple contiguous comment characters. They can form folds
" with .
""" Compatibility
" TODO: Test with `nvi`, a "bug-for-bug compatible" clone of the original BSD
" `vi`.
" TODO: Test with `VIM_POSIX=1 "$vim"` (for different `$vim`s?). See
" |$VIM_POSIX|.
" This vimrc should work with all configurations of Vim. Manually tested with:
"
" - Versions: TODO: Whatever some old {Debian,Ubuntu,Fedora} ships.
" - Features: small, huge (Debian `apt-get install vim-{tiny,gtk3}`)
" - Locales: C, UTF-8 (`LC_ALL=C{,.UFT-8}`)
" - Colors: 1, 2, 8, 16, 256, termguicolors (`--cmd "set {t_Co=$colors,tgc}"`)
" - UI: TUI, GUI (`{,g}vim`)
" - Terminals: GUI, VTE, Linux, tmux with the correct `Ss` `terminal-override`
" - Platforms: Unix-like
" TODO: `man setfont` says:
"
" > Note: if a font has more than 256 glyphs, only 8 out of 16 colors can be
" > used simultaneously. It can make console perception worse (loss of
" > intensity and even some colors).
"
" The default font on my system has 512 glyphs (`showconsolefont -v`).
" TODO: `man setfont` says, in section "CONSOLE MAPS":
"
" > Several mappings are involved in the path from user program output to
" > console display.
"
" Might be worth reading up on that. If I already did, document.
""" Features
" |:version| says "tiny" features are always present and are therefore used in
" favor other features whenever possible. "Tiny" features include:
"
" - |+multi_byte|
" - |+mouse|
" - |+tag_binary|
" - |+user_commands|
" - |+autocmd|
" - |+localmap|
"
" TODO: What category features are placed in depends on Vim version. Look up
" and create some table.
" Note that trying to set (run-time, but not compile-time) unsupported options
" is silently ignored, and so can be done without checking for support first
" (which is good, since that requires |+eval|). Everything between |:if| and
" |:endif| is also silently ignored if |+eval| is not available. See
" |no-eval-feature|.
""" Patches
" TODO: Instead of checking for patches we might want to standardize on
" `exists()` instead since it is more descriptive and takes the compile-time
" |+feature-list| into account (which would otherwise require
" `has('patch-{patch}') && has('{feature}')`). The version/patch does indicate
" how old the feature is and so how likely to be supported. Use
" `exists('+option')` (not `exists('&option')`, see e.g. |missing-options|) and
" `exists('##event')`
" See |has-patch|.
" |:helpgrep| |c_CTRL-R_CTRL-A| (mapped to `K` below).
" - 8.1.0081
""" Encoding
" |'encoding'| is hard-coded to `utf-8`. However, only characters present in
" Code Page 437 should be used in the interface, to ensure font support on most
" terminals (in particular, the Linux console, via `CHARMAP` in
" `/etc/default/console-setup`). See
" -
" -
" - `man showconsolefont`
" One can find characters by looking at |digraph-table| in a lowest common
" denominator terminal. Some characters tested to work (not all from Code Page
" 437) *myvimrc-characters*:
" - Rectangular: ■█▒░ (not ▌▐▓)
" - Triangular: (not ►◄▲▼)
" - Circular: ●•∙· (not ○☼)
" - Other: ‼︎… (not ⌂♦☺︎☻)
" - Arrows: ‹›«»←→↑↓ (not ↔↕)
" - Box: │─├┤┼ (not ║═)
" - Math: ∩×∆∅
" - Greek: ΔΛλ
""" Re-sourcing
" |setglobal| is used where appropriate so that buffer/window-local options are
" not overridden when re-sourcing.
" A `vimrc` |augroup| is cleared and used for all |autocmd|s. At the time of
" writing, |augroup| messes up the highlighting of |autocmd|s within it, so the
" `vimrc` group name is added to every |autocmd| instead (the addition of the
" group of course makes the highlighting misinterpret it as the
" `vimAutoEventList` and the events instead get the erroneous
" `VimAutoCmdSfxList` highlighting... file a bug?). This can be checked with
" `:g/autocmd`.
""" Mappings
" Don't make any built-in mappings do something completely different! Another
" Vimmer should never be surprised by what happens, unless it is pleasantly
" surprised because the functionality is extended in useful and obvious ways.
" This includes mappings from popular plugins, for which the defaults should be
" kept as far as possible.
" In this file, || is hard-coded to so that mappings work
" without |+eval|. Setting |mapleader| to a different value after the mappings
" have been defined does not change the mappings, so it is not possible to
" "hot-swap" it for pair programming anyway. |mapleader| is still set though,
" because some plugins expect it.
""" Debugging
" See |load-plugins|, |slow-start|.
""" Novice
" |myvimrc-novice|
" The goal is to make it at least workable to pair program with a Vim novice
" using this configuration.
" From more heavy-handed to less:
" - `$VIMRUNTIME/evim.vim`, |easy|, |evim-keys|, |i_CTRL-L|
" - `$VIMRUNTIME/mswin.vim`
" - |:behave|
" TODO: Mark novice-unfriendly option settings as
" |myvimrc-novice-unfriendly-$option|? (My original motivator for this was that
" I thought setting 'hidden' was a bit dangerous, but it is set automatically
" in easy mode!)
"" Helpers
""" Define and clear autocmd group
" TODO: I seem to remember augroups being closed automatically at end of file,
" in which case we could omit the `augroup END` and group name from our
" `autocmd`s.
augroup vimrc
autocmd!
augroup END
""" Functions
if has('eval')
" See |has-patch|.
" TODO: After and including 7.4.237 `has("patch-{major}.{minor}.{patch}")`
" works and is nicer. If we never check for any version before that we might
" remove this.
function! s:has(major, minor, patch) abort
let version = 100 * major + minor
return v:version > version || (v:version == version && has('patch'.patch))
endfunction
" `$VIMSRC/src/option.h:/P_COMMA/`
" `$VIMSRC/src/option.h:/P_FLAGLIST/`
endif
"" Options
" |:let-option|
" Note that even options listed as local (only) have global variants. E.g.
" 'fileencoding' is listed as "local to buffer" but 'fileencodings' says: "Note
" that 'fileencodings' is not used for a new file, the global value of
" 'fileencoding' is used instead. You can set it with: `:setglobal
" fenc=iso-8859-2`".
" `$VIMSRC/src/optiondefs.h:/Options local to a window/`
" `$VIMSRC/src/structs.h:/Options local to a window/`
" `$VIMSRC/src/structs.h:/options that are local to a window/`
" TODO: Use |setglobal| for more things? Note that for options with a non-empty
" default local value, `setglobal` does nothing useful. Worse, for options with
" only a (buffer or window) local value, `setglobal` does nothing, literally.
" Is that a problem, should we even set local values here? Maybe identifying
" them and getting rid of them is a good idea?
" TODO: Maybe source |defaults.vim|? Probably not, but add some documentation?
" TODO: Look at |:options| and maybe follow the same grouping?
""" Reset
" Undoes changes to global options made by any |system-vimrc| or interactively
" (if re-sourced). The value of 'compatible' determines what defaults are
" loaded, but 'nocompatible' is set automatically at startup when a user vimrc
" file (this file!) is found |compatible-default|.
setglobal all&
""" Encoding
" Force Vim's internal encoding to a Unicode encoding.
set encoding=utf-8
""" Buffers
" Allow switching away from unsaved buffers.
set hidden
""" Clipboard
" Integrate with any graphical environment's clipboard.
set clipboard^=unnamedplus
""" Editing
set backspace=indent,eol,start
set virtualedit=block
""" Modeline
" 'modeline' is local to buffer, 'modelines' is global.
" set nomodeline
set modelines=0
""" Navigation
set nostartofline
set sidescroll=1
set sidescrolloff=1
" set scrolloff=1 " Useful for |[[|.
set display=lastline
" : Cannot scroll by screen
" line if a line wraps. Add the 'smoothscroll' option.
if has('patch-9.0.0640')
set smoothscroll " TODO: Local to window.
endif
""" Windows
" Put newly opened windows below and to the right.
" Use |:aboveleft| to override.
set splitbelow
set splitright
""" Interface
set showcmd
" TODO: Also look a bit at ? Maybe
" `flagship#tabcwds()` to summarize the tab working directory.
" TODO: We could split the current `Statusline()` into
" `Statusline{Left,Right}()` and have the new `Statusline()` join them with
" 'fillchars', and `Ruler()` join them with `:`. Since the same functionality
" is also used by 'titlestring' maybe name them something less statusline
" centric? `Bufname(fill, right)`?
" set ruler
" set rulerformat=%=%(%{expand('%:t')}:%)%l
set noruler
set laststatus=1
set showtabline=1
set shortmess+=I " See |:intro|.
set guioptions=cf " Console dialogs, foreground (no fork())
set numberwidth=1 " TODO: Local to window.
""" Interface characters
" See also |myvimrc-characters|.
" If |'t_Co'| is empty (or set to 1) the default behavior of |'fillchars'|
" `stl`/`stlnc` (`^`/`=`) will be used regardless of its setting. Other good
" choices for |'listchars'| `fold` is ` ` and `tab` is: `→ `, `――>`, `├──`,
" `──┤`, `├─┤`.
" TODO: `:help 'fillchars'` says "for `stl` and `stlnc` only single-byte values
" are supported". Test the current (non-ASCII) values against older versions of
" Vim. See also .
" TODO: If Vim cannot tell appart |hl-StatusLine| and |hl-StatusLineNC| (e.g.
" because |hl-StatusLine| is a |hl-link|), (ordinary) spaces and `fillchars`
" `stl` characters for the statusline of the focused window will be replaced
" with `^` (i.e. the default `fillchars` `stl`). Note that this includes the
" single character under the `fillchars` `vert`, which cannot be set with
" |'statusline'|.
set linespace=-1
set list " TODO: Local to window.
set linebreak " TODO: Local to window.
set breakindent " TODO: Local to window.
set breakindentopt=sbr,shift:2 " TODO: Local to window.
set showbreak=» " TODO: Local to window (or global).
set fillchars=diff: ,stl:─,stlnc:─,vert:│,fold:─
" set fillchars=diff: ,stl:_,stlnc:_,vert:│,fold:─
set listchars=tab:├─,trail:•,extends:›,precedes:‹,nbsp:·
" : Showing two characters for
" tab is limited. Allow for a third character for "tab:" in 'listchars'.
if has('patch-8.1.0759')
" set listchars+=tab:├─┤
" set listchars+=tab:├ ┤
set listchars+=tab:│ │
endif
" TODO: `%f` still shows `[No Name]`, is there a way to remove that?
set statusline=%(%f %) " TODO: Local to window (or global).
if has('eval')
" TODO: This works poorly with plugins that have windows with their own
" statusbar, e.g. |undotree.txt|.
" set statusline=%(%{expand('%:.s?^/??')} %)
" `fillchar_status` function in `src/screen.c:4649` of Vim source.
" TODO: Try to use some other non-standard space? Search for "\" in
" |digraphs|.
" set fillchars+=stl: ,stlnc:
set fillchars+=stl: ,stlnc:
" set fillchars+=stl:┘,stlnc:┘
" TODO: Standardize on global functions? Or |autoload|s (but that would break
" our "single file" policy)?
" function! s:statusline() abort
function! s:opt(opt, key) abort
return matchstr(a:opt, '.*\(^\|,\)' . key . ':\zs[^,]*')
return split(a:opt, )
endfunction
function! s:expand() abort
endfunction
function! Statusline() abort
" TODO: The global CWD, `getcwd(-1)`, might be useful. Also indicate
" `haslocaldir()` somehow?
" TODO: Make this expansion relative to the window-local cwd? Also do other
" substitutions, like `$HOME` to `~`, the expanded `$VIMRUMTIME` to the
" string `$VIMRUNTIME`, etc. We used to have `s?^/??`, why?
" TODO: Harmonize with `titlestring`.
" TODO: Generalize `&filetype`, `&buftype` and the matched `bufname()`
" `://` somehow?
if &filetype ==# 'man'
let title = &filetype . ':' . expand('%:t')
" elseif &buftype =~# 'nofile\|nowrite'
" let title = &filetype
elseif &buftype ==# 'help'
let title = &buftype . ':' . expand('%:t:s?.txt$??')
elseif &buftype ==# 'terminal'
" TODO: We sometimes want to use some variation of `expand('%')`, e.g.
" when using the |terminal-debugger|.
" TODO: 'statusline' is not re-evaluated when |term_gettitle()| changes,
" can we do something like `let &statusline = &statusline` in some
" |autocmd| (perhaps one that triggers when the terminal content
" changes)? Worst case |CursorMoved|.
let title = &buftype . ':' . substitute(term_gettitle(bufnr()), '[^:]*:', '', '')
" TODO: Change the `&&` into nested `if`? Note that it has different
" semantics because of the `if`/`elseif`/`else` chain.
elseif &buftype ==# 'quickfix' && !empty(get(w:, 'quickfix_title'))
" Remove leading colon (Ex commands) and trailing parenthesis (e.g.
" |dispatch| job number).
let title = w:quickfix_title
let title = substitute(title, '^:', '', '')
let title = substitute(title, '\s*([^)]\+)\s*$', '', '')
let title = &buftype . ':' . title
else
let title = expand('%')
" fugitive:///home/rcrnstn/repo/.git//hash/dir/file.ext
" fugitive:///home/rcrnstn/repo/.git//hash/dir/file.ext
" TODO: |split()| on `:` (info), `://` (fugitive) and `#` (info), do
" processing, and then `join(filter(parts, '!empty(v:val)'), ':')`.
" Replace names of the form `:///.//` with
" `:` where any leading 40 character hex string (hash) in
" `` is truncated to 7 characters. (Yes, this was designed for
" |fugitive|.)
let parts = matchlist(expand('%'),
\ '^\%(\([^:]\+\)://\)\?\%(\(.*\)//\)\?\(.*\)'
\ )
if !empty(parts)
let [type, prefix, path] = parts[1:3]
if !empty(prefix)
while fnamemodify(prefix, ':t')[:0] ==# '.'
let prefix = fnamemodify(prefix, ':h')
endwhile
let prefix = fnamemodify(prefix . '/', ':~:.:h:t:s?^\.$??')
endif
let path = substitute(path,
\ '^\([0-9a-fA-F]\{7\}\)[0-9a-fA-F]\{33\}\(/.*\|$\)', '\1\2', ''
\ )
let title = join(filter([type, prefix, path], '!empty(v:val)'), ':')
endif
endif
if !empty(title)
let title = substitute(title, ' *$', '', '') . ' '
endif
let title = substitute(title, ' ', ' ', 'g')
" NOTE: `matchstr` looks for the _first_ match.
" TODO: This is slow. Cache the `matchstr` result?
let fillchar = matchstr(&fillchars, '\(^\|,\)stl:\zs[^,]*')
let pad = winwidth(0) - strdisplaywidth(title)
return title . repeat(fillchar, pad)
endfunction
" : Cannot easily get the
" script ID. Support expand('').
" if has('patch-8.2.1347')
" let &statusline = '%{'.expandcmd('statusline()').'}'
" else
set statusline=%{Statusline()} " TODO: Local to window (or global).
" function! g:Statusline() abort
" return s:statusline()
" endfunction
" endif
" TODO: This doesn't seem to do the trick.
autocmd vimrc CursorMoved *
\ if &buftype ==# 'terminal' |
\ let &statusline = &statusline |
\ endif |
endif
""" Colors
" May be overritten later by Vim for terminals with |t_RB|.
set background=dark
set guioptions+=d
if &t_Co > 16
set t_Co=16
"
if has('nvim')
lua ffi = require "ffi" ffi.cdef "int t_colors" ffi.C.t_colors = 16
endif
endif
set notermguicolors
"
if empty($COLORTERM) && !has('vcon')
set notermguicolors
endif
if has('gui_running')
set termguicolors
endif
""" Bell
set belloff=error
""" Title
set title
set titlelen=0
" set titlestring=vi:%{fnamemodify(getcwd(),':~:t:s?^$?/?')}%(:%<%{pathshorten(expand('%:.:s?^/??'))}%)
" TODO: Break out into `Title()` function and harmonize with `statusline`.
" TODO: Ideas from `tpope/vim-flagship`: `hostname()` and `v:servername`.
set titlestring=vi:%{fnamemodify(getcwd(),':~:t:s?^$?/?')}%(:%<%{expand('%:.:s?^/??')}%)
set titleold=
function! s:cwdname() abort
return fnamemodify(getcwd(), ':~:t:s?^$?/?')
endfunction
function! s:bufname() abort
return expand('%:.:s?^/??')
return pathshorten(expand('%:.:s?^/??'))
endfunction
""" Tabs
" When reading files written by others, assume default `tabstop=8`.
" When Writing files, use 4 space indents.
set shiftwidth=4 " TODO: Local to buffer.
set shiftround
set softtabstop=-1 " TODO: Local to buffer.
set expandtab " TODO: Local to buffer.
""" Mouse
" |scroll-mouse-wheel| is generally useful, the rest for |myvimrc-novice|.
set mouse=a
set mousemodel=popup
set selectmode+=mouse
""" Novice
set keymodel=startsel,stopsel
set selectmode+=key
set whichwrap+=<,>,[,]
""" Command-line
" See also Readline's `menu-complete`, documented in `bash(1)`.
set wildmenu
set wildmode=longest:full,full
" See also |'suffixes'|.
" Source control.
" set wildignore+=_*
set wildignore+=.git
" Programming language specific.
set wildignore+=__pycache__,.venv,*.egg-info
set wildignore+=node_modules
" Relative to `$HOME`.
set wildignore+=$HOME/projects/UPSTREAMS
set wildignore+=$HOME/.vim/pack
set wildignore+=$HOME/.config/vim/pack
set wildignore+=$HOME/.config/nvim/pack
set wildignore+=$HOME/.cache
set wildignore+=$HOME/.local/lib
set wildignore+=$HOME/.local/share/man
set wildignore+=$HOME/.local/share/tldr
set wildignore+=$HOME/.mozilla/firefox
set wildignore+=$HOME/.ansible
set wildignore+=$HOME/.vagrant
set wildignore+=$HOME/.local/pipx
set wildignore+=$HOME/.cargo/register
set wildignore+=$HOME/.cargo
set wildignore+=$HOME/.npm
set wildignore+=$HOME/.wine/dosdevices
set wildignore+=$HOME/.wine/drive_*
set wildignore+=$HOME/snap
" : 'wildmenu' only shows few
" matches. Add the "pum" option: use a popup menu to show the matches.
if has('patch-8.2.4325')
set wildoptions+=pum
endif
""" Search
set incsearch
set shortmess-=S
""" Timing
set notimeout
""" Terminal
set ttimeout
set ttimeoutlen=100
set ttyfast
set lazyredraw
" A |'ttyscroll'| of 0 is beneficial on local terminals (especially reduces
" flickering on Linux VT), but might be worse on slow remote connections. Set
" |'writedelay'| to a small value (like 1), and maybe |'redrawtime'| to a very
" large value (like 20000), to see what difference this option makes. See also
" |myvimrc-autocmd-ttyscroll|.
set ttyscroll=0
""" Diffs
set diffopt+=vertical
set diffopt+=foldcolumn:0
" : Using an external diff
" program is slow and inflexible. Include the xdiff library. Use it by default.
if has('patch-8.1.0360')
" set diffopt+=internal
set diffopt+=algorithm:histogram
set diffopt+=indent-heuristic
endif
""" Formatting
" See |formatting|, |auto-format|, |format-comments|, and |fo-table|.
set textwidth=79 " TODO: Local to buffer.
" set winwidth=80
" set colorcolumn=+1 " TODO: Local to window.
set autoindent " TODO: Local to buffer.
set nojoinspaces
" Don't autowrap text (non-comments), recognize lists, remove comment leader
" when joining.
set formatoptions-=t " TODO: Local to buffer.
set formatoptions+=n
set formatoptions+=j
" See also |breakindent|, |breakindentopt|, |showbreak| set above.
set nowrap " TODO: Local to window.
" A custom 'formatprg' produces nicer results but is harder to replicate for
" others and places undue burden on documentation contributors. Having simple
" format rules is more important than slightly prettier formatting.
" TODO: At least make the expressions valid (we're lacking spaces, no?).
" TODO: Local to buffer (or global).
" if executable('par')
" let &formatprg = join(['par', 'T'.&tabstop, 'w'.&textwidth, 'e'])
" let &formatprg = join(['par', 'T'.&tabstop, 'w'.&textwidth, 'e', 'j'])
" elseif executable('fmt')
" let &formatprg = join(['fmt', '-w'.&textwidth, '-g'.&textwidth])
" endif
""" Comments
" set commentstring=#%s " TODO: Local to buffer.
""" Folds
set foldlevelstart=999
" TODO: Local to window.
set foldtext=substitute(getline(v:foldstart),'\\t',repeat(' ',&ts),'g').' '
""" Spelling
" set spell
set spelllang=en_us " TODO: Local to buffer.
set thesaurus=~/.vim/thesaurus/en_us.txt " TODO: Local to buffer (or global).
" TODO: We can probably do something better/cleaner for the thesaurus on our
" own. In particular, publicly available, clearly licensed, version controlled,
" code to generate the vim thesaurus from primary sources.
" The current `~/.vim/thesaurus/en_us.txt` is downloaded from the URL
" documented in |'thesaurus'|:
" . This zip file
" contains a patch to
" and the results of running the patched code on the included files, as well as
" the licenses. `MyThes-1.zip` is linked from
" and seems to be
" some version of . MyThes states in its
" readme that it is based on WordNet 2.0. However, the latest version as of
" this writing is 3.1. When generating a Vim version, it helps to have a
" visualization to guide the heuristics that will probably be needed. Searching
" for "thesaurus" gives
" several projects. looks good (try e.g. "completion").
" Another search hit,
" , states that
" "WordNet's vocabulary is smaller than other "word" databases", so we might
" want to use other sources as well, e.g. Roget's Thesaurus.
"
" WordNet
" -
" -
" Data hosted on Project Gutenberg
" - (2002) Moby Thesaurus List
" - (2004) Roget's Thesaurus of English Words and Phrases
" - (1991) Roget's Thesaurus
" - (2011) A Dictionary of English Synonymes [...]
" - (2016) A Complete Dictionary of Synonyms and Antonyms
" For Swedish, there is
" -
" -
"
" Other assorted resources:
" -
" -
""" Views
" TODO: Per the documentation `curdir` is only supposed to save the
" window-local directory as set by |:lcd|, which I wouldn't mind so much and
" might even make use of. But currently it is being set apparently without my
" involvement (perhaps by some plugin, probably |netrw|) far too often, and
" since we do |:loadview| on every opened file this gets annoying fast.
set viewoptions-=options
set viewoptions-=curdir
set viewoptions+=slash
set viewoptions+=unix
""" Files
set swapfile " TODO: Local to buffer.
set writebackup
set backupcopy=yes " TODO: Local to buffer (or global).
set undofile " TODO: Local to buffer.
set directory=~/.cache/vim/swap//
set backupdir=~/.cache/vim/backup//
set undodir=~/.cache/vim/undo//
set viewdir=~/.cache/vim/view//
set viminfofile=~/.cache/vim/viminfo
if has('eval')
let $TMPDIR = $HOME . '/.cache/vim/tmp'
call mkdir($TMPDIR, 'p')
endif
if has('nvim')
set shadafile=
endif
""" Conceal
set conceallevel=2 " TODO: Local to window.
set concealcursor=n " TODO: Local to window.
""" Platform
set shellslash
""" Filenames
" I generally allow everything (except control codes) in filenames on the
" filesystem. However, only media files (photos or videos) tend to take
" advantage of that, to be more descriptive/preserve some upstream name. Files
" I will want Vim to be able to cross-reference will be text (code or notes)
" with more constrained names. Disallowing some punctuation makes things like
" |gf| more useful.
" NOTE: This is a global option, but some ftplugins (Perl) change it anyway.
" `:grep -R 'isf\(name\)\?' /usr/share/vim/vim82/ftplugin/`
" *myvimrc-isfname-bug*
set isfname-=, " Common file separator.
set isfname-=; " Common file separator.
set isfname-=: " Common file separator (e.g. when using |gf|).
set isfname-=# " URL fragment separator.
""" External tools
" Not POSIX: `-H` (suggested in 'grepprg'), `-I`.
set grepprg=grep\ -nHI " TODO: Local to buffer (or global).
""" Quickfix
set switchbuf=useopen
"" Mappings
" TODO: Categorize into map modes. Use the nonspecific |:map| (|mapmode-nvo|)
" and |:map!| (|mapmode-ic|) more often?
""" Mapleader
" Prefill command line with query to discover mapleader mappings.
nnoremap ? :verbose nmap Space>
nnoremap ? :verbose nmap Space>Space>
""" Escape
" Overwrites the default |i_CTRL-C|, |v_CTRL-C|. See
" .
inoremap
xnoremap
""" Novice
" We
""" Windows
"""" Focus
nnoremap h
nnoremap j
nnoremap k
nnoremap l
tnoremap h
tnoremap j
tnoremap k
tnoremap l
"""" Move
if has('eval')
function! s:winmove(dir) abort
let prefix =
\ dir ~=# '[hj]' ? 'leftabove' :
\ dir ~=# '[kl]' ? 'belowright'
winnr(a:dir)
endfunction
endif
"""" Empty vertical split
" See also |CTRL-W_n|, which does |:new|.
nnoremap N :vnew
"""" Resize
" Resize height to number of lines in buffer.
nnoremap ? line('$')."_"
""" Clear and redraw
" is overridden above, so use a mapping.
nnoremap
""" Movement
" Up down dispaly lines (unless a count is given).
" TODO: Mappings like these that extend builtin functionality (identify them
" and put them in the same place) could be made more formally feature complete
" by doing the same thing for any aliases (listed in `:help j` etc). If the
" functinonality is implemented in other modes (insert and the various visual
" modes) then that should probably be extended too. Document this general
" principle.
nnoremap j v:count ? 'j' : 'gj'
nnoremap k v:count ? 'k' : 'gk'
" When moving to the next/previous '{'/'}' in the first column (function
" body/struct definition start/end), make visible the matching '}'/'{'
" (function body/struct definition end/start) and paragraph containing '{'
" (function prototype/struct name and preceeding comments). A prefix of 'g'
" aligns the paragraph containing '{' to the top of the window.
" TODO: Keep jumps somehow? `:keepjumps normal!`?
" TODO: |]]| says "[count] sections forward or to the next '{' in the first
" column". |section| says "a section begins after a form-feed () in the
" first column and [...]". We completely ignore the form-feed aspect. It's not
" commonly seen in code, but the GNU Coding Standards recommends them
" .
nnoremap ]] ]]%%m'vipo''
nnoremap g]] ]]%%m'vipozt''
nnoremap [[ [[%%m'vipo''
nnoremap g[[ [[%%m'vipozt''
nnoremap ][ ][m'%vipo''
nnoremap g][ ][m'%vipozt''
nnoremap [] []m'%vipo''
nnoremap g[] []m'%vipozt''
""" Search and edit file
" See also |ctrlp|.
nnoremap :edit **/**
""" Visual line insert and append
" TODO: This breaks text object that start with `A` or `I`. Ironically, those
" two characters specifically are prefixes in `wellle/targets.vim`.
" xnoremap A mode() !=# 'V' ? 'A' : ':normal A'
" xnoremap I mode() !=# 'V' ? 'I' : ':normal I'
" End of pair opened at end of current line.
" TODO: These are overridden by `matchit`, use `%` instead?
nnoremap g% :norm! $%
onoremap g% :norm! $%
xnoremap g% :norm! v$%
""" Scrolling
" Put paragraph containing cursor line at the top/bottom of the window.
" TODO: Preserve jump list somehow?
" TODO: If |+eval|, also account for |scrolloff|?
nnoremap z{ {jzt
nnoremap z} }kzb
""" Visual mode repeat
" See also `visualrepeat`.
xnoremap . :normal! .
""" Clipboard
" Yank file and line number of cursor.
nnoremap yy :let @+ = expand('%:p') . ':' . line('.')
""" `$MYVIMRC`
nnoremap ve :edit $MYVIMRC:lcd %:h
nnoremap vs :source $MYVIMRC
""" Keyword
" nnoremap K :silent execute "normal! K" \| redraw!
" nnoremap K :terminal ++close =&keywordprg
" TODO: vnoremap
" TODO: `set keywordprg=:help`
""" Help
" TODO: Make this filetype dependent? Could use |:grep| or |fugitive|'s
" |:Ggrep|. On the other hand, there are other built-in mappings that does
" keyword search in a codebase.
" TODO: Here we use `g` as prefix. Do we do that in other places? Should we not
" use (i.e. )?
" TODO: We do have a `K` `FileType vim,help` mapping.
nnoremap gK :helpgrep \<\>
xnoremap gK y:helpgrep "
""" Run
" TODO: Standardize where the output goes.
" File (with output in pager).
nnoremap % :!%:p:S \| less -FR
nnoremap # :!#:p:S \| less -FR
" Command (with output in new window).
nnoremap ! :new \| .!
""" Quickfix
"""" Go to first/last valid entry
" |:cfirst|, |:clast|, |[Q|, and |]Q| (the last ones being
" `vim-unimpaired` mappings) go to the first/last entry regardless of whether
" it is |quickfix-valid| (includes a file name). I prefer to go to the
" first/last entry that includes a file name (even if I was already
" before/after that entry).
nnoremap [Q :cfirst \| silent! execute "cnext\|cprev"
nnoremap ]Q :clast \| silent! execute "cprev\|cnext"
"""" Toggle Quickfix window
" TODO: Do similar for location list?
" TODO: Make the |botright| global with some |autocmd|? Is it implied by our
" `set split{below,right}`?
nnoremap q
\ empty(filter(getwininfo(),
\ 'getbufvar(v:val.bufnr, "&buftype") ==# "quickfix"'
\ ))
\ ? ':botright copen'
\ : ':botright cclose'
""" Make
" TODO: |dispatch| provides default mappings, try to emulate them?
" TODO: Should these be as well?
" TODO: Use the |terminal-debugger| instead of a make target?
nnoremap m% :!make %:r:S \| less -FR
nnoremap m# :!make #:r:S \| less -FR
nnoremap mm :!make \| less -FR
nnoremap ma :!make all \| less -FR
nnoremap mt :!make test \| less -FR
nnoremap md :!make debug \| less -FR
if has('quickfix')
nnoremap m% :silent make! %:r:S \| redraw!
nnoremap m# :silent make! #:r:S \| redraw!
nnoremap mm :silent make! \| redraw!
nnoremap ma :silent make! all \| redraw!
nnoremap mt :silent make! test \| redraw!
endif
if has('terminal')
nnoremap md :terminal make debug
endif
""" Improve default mappings
"""" Disable unconditional quit from Normal mode
" As warned about in |zz| these may be typed accidentally when Caps Lock is
" enabled and lose data. Redefining them is a bit hostile towards other users,
" but deemed worth it.
nnoremap ZZ :quit
nnoremap ZQ :quit
"""" Yank to end of line, not entire line
" As suggested in |Y|. Analogous with |D| does `d$` and |C| does `c$`.
nnoremap Y y$
"""" Don't include newline in Visual mode `$`.
" TODO: Do you just want `set selection=old`? Nope, the builtin `matchit`
" doesn't work correctly with `selection=old`, this is probably a bug though
" and might get fixed.
" set selection=old
xnoremap $ $h
"""" Visual mode put that doesn't clobber unnamed register
" See also .
xnoremap P pgvy
""" Visual mode
"""" Visually select last pasted text
" TODO: Make a text object as well?
" TODO: I use this relatively often, but it clobbers the built in |gp|. I never
" use that, but it seems useful, so maybe I should? Find another natural
" mapping?
" Analogous with how |gv| visually selects last visually selected text. Default
" to always using ordinary Visual mode. Linewise and blockwise with |+eval|
" support.
nnoremap gp `[v`]
if has('eval')
nnoremap gp '`[' . getregtype()[0] . '`]'
endif
"""" Restrict Visual mode substitutions to the selected text
xnoremap s :s/\%V
"""" Visual movement
xnoremap hoho
xnoremap jojo
xnoremap koko
xnoremap lolo
""" Mouse
"""" Scroll vertically by single lines
" As suggested in |scroll-mouse-wheel|.
nnoremap
xnoremap
inoremap
nnoremap
xnoremap
inoremap
"""" Scroll horizontally by single columns with
nnoremap zh
xnoremap zh
inoremap zh
nnoremap zl
xnoremap zl
inoremap zl
nnoremap zh
xnoremap zh
inoremap zh
nnoremap zl
xnoremap zl
inoremap zl
"""" Toggle folds with <2-RightMouse>
" noremap za
noremap <2-RightMouse> za
" noremap <3-RightMouse> za
" noremap <4-RightMouse> za
""" Command-line
" Take already written text into account when searching history.
cnoremap
cnoremap
" Try not to interfere with |'wildmenu'|.
if has('eval')
cnoremap wildmenumode() ? "\" : "\"
cnoremap wildmenumode() ? "\" : "\"
endif
""" Wildmenu
" and moves the cursor, not the completion selection. As
" suggested in 'wildmenu'.
cnoremap