"" 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.
"   <https://vim-team.pages.debian.net/vim/> 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
" <https://git.rcrnstn.net/rcrnstn/vim-unobtrusive-comment>.

" Section headings are not written in the help |notation|, but are instead
" indicated with multiple contiguous comment characters. They can form folds
" with <https://git.rcrnstn.net/rcrnstn/vim-unobtrusive-fold>.

""" 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 `<Space>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
" - <https://en.wikipedia.org/wiki/Code_page_437>
" - <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tty/vt/cp437.uni>
" - `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, |<Leader>| is hard-coded to <Space> 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
" <http://ftp.vim.org/pub/vim/patches/9.0/9.0.0640>: 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 <https://github.com/tpope/vim-flagship>? 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 <http://ftp.vim.org/pub/vim/patches/8.2/8.2.2569>.
" 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:·
" <http://ftp.vim.org/pub/vim/patches/8.1/8.1.0759>: 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 "\<SPACE\>" 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()`
    " `<type>://` 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 `<type>://<path>/.<dot>//<obj>` with
      " `<type>:<obj>` where any leading 40 character hex string (hash) in
      " `<obj>` 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
  " <http://ftp.vim.org/pub/vim/patches/8.2/8.2.1347>: Cannot easily get the
  " script ID. Support expand('<SID>').
  " if has('patch-8.2.1347')
  "   let &statusline = '%{'.expandcmd('<SID>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
  " <https://github.com/neovim/neovim/issues/11974#issuecomment-1658464993>
  if has('nvim')
    lua ffi = require "ffi" ffi.cdef "int t_colors" ffi.C.t_colors = 16
  endif
endif
set notermguicolors
" <https://github.com/termstandard/colors#truecolor-detection>
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
" <http://ftp.vim.org/pub/vim/patches/8.2/8.2.4325>: '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
" <http://ftp.vim.org/pub/vim/patches/8.1/8.1.0360>: 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'|:
" <https://github.com/vim/vim/issues/629#issuecomment-443293282>. This zip file
" contains a patch to <https://www.openoffice.org/lingucomponent/MyThes-1.zip>
" and the results of running the patched code on the included files, as well as
" the licenses. `MyThes-1.zip` is linked from
" <https://www.openoffice.org/lingucomponent/thesaurus.html> and seems to be
" some version of <https://github.com/hunspell/mythes>. 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
" <https://wordnet.princeton.edu/related-projects> for "thesaurus" gives
" several projects. <https://wordvis.com> looks good (try e.g. "completion").
" Another search hit,
" <https://www2.seas.gwu.edu/~simhaweb/software/jword/index.html>, 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
" - <https://wordnet.princeton.edu/download/current-version>
" - <https://wordnetcode.princeton.edu/wn3.1.dict.tar.gz>
" Data hosted on Project Gutenberg
" - <https://www.gutenberg.org/ebooks/3202>  (2002) Moby Thesaurus List
" - <https://www.gutenberg.org/ebooks/10681> (2004) Roget's Thesaurus of English Words and Phrases
" - <https://www.gutenberg.org/ebooks/22>    (1991) Roget's Thesaurus
" - <https://www.gutenberg.org/ebooks/38390> (2011) A Dictionary of English Synonymes [...]
" - <https://www.gutenberg.org/ebooks/51155> (2016) A Complete Dictionary of Synonyms and Antonyms
" For Swedish, there is
" - <https://folkets-lexikon.csc.kth.se/synlex.html>
" - <https://spraakbanken.gu.se/en/resources/saldo>
"
" Other assorted resources:
" - <https://github.com/dahu/vimdictive>
" - <https://dict.org>

""" 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 <Space>?        :verbose nmap <lt>Space>
nnoremap <Space><Space>? :verbose nmap <lt>Space><lt>Space>

""" Escape
" Overwrites the default |i_CTRL-C|, |v_CTRL-C|. See
" <https://vim.fandom.com/wiki/Avoid_the_escape_key>.
inoremap <C-C> <Esc>
xnoremap <C-C> <Esc>

""" Novice
" We 

""" Windows

"""" Focus
nnoremap <C-H> <C-W>h
nnoremap <C-J> <C-W>j
nnoremap <C-K> <C-W>k
nnoremap <C-L> <C-W>l
tnoremap <C-H> <C-W>h
tnoremap <C-J> <C-W>j
tnoremap <C-K> <C-W>k
tnoremap <C-L> <C-W>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 <C-W>N :vnew<CR>

"""" Resize
" Resize height to number of lines in buffer.
nnoremap <expr> <C-W>? line('$')."<C-W>_"

""" Clear and redraw
" <C-L> is overridden above, so use a <Leader> mapping.
nnoremap <Space><C-L> <C-L>

""" 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 <expr> j v:count ? 'j' : 'gj'
nnoremap <expr> 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 (<C-L>) 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
" <https://www.gnu.org/prep/standards/standards.html#index-formfeed>.
nnoremap  ]] ]]%%m'vipo<Esc>''
nnoremap g]] ]]%%m'vipozt<Esc>''
nnoremap  [[ [[%%m'vipo<Esc>''
nnoremap g[[ [[%%m'vipozt<Esc>''
nnoremap  ][ ][m'%vipo<Esc>''
nnoremap g][ ][m'%vipozt<Esc>''
nnoremap  [] []m'%vipo<Esc>''
nnoremap g[] []m'%vipozt<Esc>''

""" Search and edit file
" See also |ctrlp|.
nnoremap <C-P> :edit **/**<Left>

""" 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 <expr> A mode() !=# 'V' ? 'A' : ':normal A'
" xnoremap <expr> I mode() !=# 'V' ? 'I' : ':normal I'

" End of pair opened at end of current line.
" TODO: These are overridden by `matchit`, use `<Space>%` instead?
nnoremap <silent> g% :<C-U>norm!  $%<CR>
onoremap <silent> g% :<C-U>norm!  $%<CR>
xnoremap <silent> g% :<C-U>norm! v$%<CR>

""" 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<C-O>
nnoremap z} }kzb<C-O>

""" Visual mode repeat
" See also `visualrepeat`.
xnoremap . :normal! .<CR>

""" Clipboard
" Yank file and line number of cursor.
nnoremap <silent> <Space>yy :let @+ = expand('%:p') . ':' . line('.')<CR>

""" `$MYVIMRC`
nnoremap <silent> <Space><Space>ve :edit   $MYVIMRC<CR>:lcd %:h<CR>
nnoremap <silent> <Space><Space>vs :source $MYVIMRC<CR>

""" Keyword
" nnoremap K :silent execute "normal! K" \| redraw!<CR>
" nnoremap <silent> K :terminal ++close <C-R>=&keywordprg<CR> <cword><CR>
" 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 <Leader> (i.e. <Space>)?
" TODO: We do have a `<Space>K` `FileType vim,help` mapping.
nnoremap <silent> gK  :helpgrep \<<C-R><C-W>\><CR>
xnoremap <silent> gK y:helpgrep <C-R>"<CR>

""" Run
" TODO: Standardize where the output goes.

" File (with output in pager).
nnoremap <silent> <Space><Space>% :!%:p:S \| less -FR<CR>
nnoremap <silent> <Space><Space># :!#:p:S \| less -FR<CR>

" Command (with output in new window).
nnoremap <Space><Space>! :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"<CR>
nnoremap ]Q :clast  \| silent! execute "cprev\|cnext"<CR>

"""" 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 <expr> <Space><Space>q
\ empty(filter(getwininfo(),
\   'getbufvar(v:val.bufnr, "&buftype") ==# "quickfix"'
\ ))
\   ? ':botright copen<CR>'
\   : ':botright cclose<CR>'

""" Make
" TODO: |dispatch| provides default mappings, try to emulate them?
" TODO: Should these be <silent> as well?
" TODO: Use the |terminal-debugger| instead of a make target?
nnoremap <Space><Space>m% :!make %:r:S \| less -FR<CR>
nnoremap <Space><Space>m# :!make #:r:S \| less -FR<CR>
nnoremap <Space><Space>mm :!make       \| less -FR<CR>
nnoremap <Space><Space>ma :!make all   \| less -FR<CR>
nnoremap <Space><Space>mt :!make test  \| less -FR<CR>
nnoremap <Space><Space>md :!make debug \| less -FR<CR>
if has('quickfix')
  nnoremap <Space><Space>m% :silent make! %:r:S \| redraw!<CR>
  nnoremap <Space><Space>m# :silent make! #:r:S \| redraw!<CR>
  nnoremap <Space><Space>mm :silent make!       \| redraw!<CR>
  nnoremap <Space><Space>ma :silent make! all   \| redraw!<CR>
  nnoremap <Space><Space>mt :silent make! test  \| redraw!<CR>
endif
if has('terminal')
  nnoremap <Space><Space>md :terminal make debug<CR>
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 <silent> ZZ :quit<CR>
nnoremap <silent> ZQ :quit<CR>

"""" 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 <http://ftp.vim.org/pub/vim/patches/8.2/8.2.4242>.
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 <expr> gp '`[' . getregtype()[0] . '`]'
endif

"""" Restrict Visual mode substitutions to the selected text
xnoremap <Space>s :s/\%V

"""" Visual movement
xnoremap <C-H> hoho
xnoremap <C-J> jojo
xnoremap <C-K> koko
xnoremap <C-L> lolo

""" Mouse

"""" Scroll vertically by single lines
" As suggested in |scroll-mouse-wheel|.
nnoremap <ScrollWheelUp>        <C-Y>
xnoremap <ScrollWheelUp>        <C-Y>
inoremap <ScrollWheelUp>   <C-O><C-Y>
nnoremap <ScrollWheelDown>      <C-E>
xnoremap <ScrollWheelDown>      <C-E>
inoremap <ScrollWheelDown> <C-O><C-E>

"""" Scroll horizontally by single columns with <C-Scrollwheel>
nnoremap <C-ScrollWheelUp>        zh
xnoremap <C-ScrollWheelUp>        zh
inoremap <C-ScrollWheelUp>   <C-O>zh
nnoremap <C-ScrollWheelDown>      zl
xnoremap <C-ScrollWheelDown>      zl
inoremap <C-ScrollWheelDown> <C-O>zl
nnoremap <ScrollWheelLeft>        zh
xnoremap <ScrollWheelLeft>        zh
inoremap <ScrollWheelLeft>   <C-O>zh
nnoremap <ScrollWheelRight>       zl
xnoremap <ScrollWheelRight>       zl
inoremap <ScrollWheelRight>  <C-O>zl

"""" Toggle folds with <2-RightMouse>
" noremap   <RightMouse> <LeftMouse>za
noremap <2-RightMouse> <LeftMouse>za
" noremap <3-RightMouse> <LeftMouse>za
" noremap <4-RightMouse> <LeftMouse>za

""" Command-line

" Take already written text into account when searching history.
cnoremap <C-P> <Up>
cnoremap <C-N> <Down>
" Try not to interfere with |'wildmenu'|.
if has('eval')
  cnoremap <expr> <C-P> wildmenumode() ? "\<C-P>" : "\<Up>"
  cnoremap <expr> <C-N> wildmenumode() ? "\<C-N>" : "\<Down>"
endif

""" Wildmenu

" <Left> and <Right> moves the cursor, not the completion selection. As
" suggested in 'wildmenu'.
cnoremap <Left>  <Space><BS><Left>
cnoremap <Right> <Space><BS><Right>

""" Terminal

" TODO: Call a function that:
" - Find (the first) buffer with `&buftype ==# 'terminal'`
"   - If it doesn't have window, create one and make it focused
"   - Switch to the terminal buffer
" - If none, do `:terminal`
" NOTE: The proceedure described above is more or less `:Start` from
" `vim-dispatch`.
nnoremap <Space><Space>t :terminal<CR>

""" Macros

" Repeat the macro in register `q` on current line, lines covered by visual
" selection, or line covered by text object.
nnoremap          QQ @q
xnoremap <silent> Q  :normal! @q<CR>
nnoremap <silent> Q  :set operatorfunc=<SID>Q<CR>g@
if has('eval')
  function! s:Q(...) abort
    '[,']normal! @q
  endfunction
endif

""" Diffs

" Toggle 'diffopt' ignores
" See also |unimpaired|'s |yod|. Mapping suffix based on the `diff` flags.
" TODO: `&diffopt=~'iwhite'` will match if `&diffopt` contains e.g.
" `iwhiteall`, use `'\(^\|,\)iwhite\(,\|$\)'` instead. Introduce a helper
" function that does the wrapping. Update: We're trying out word boundaries.
" TODO: Maybe even introduce a helper that does the toggling. Look at
" `vim-unimpaired` implementation?
nnoremap yoDB :set diffopt<C-R>=&diffopt=~'\<iblank\>'   ?'-':'+'<CR>=iblank<CR>
nnoremap yoDi :set diffopt<C-R>=&diffopt=~'\<icase\>'    ?'-':'+'<CR>=icase<CR>
nnoremap yoDb :set diffopt<C-R>=&diffopt=~'\<iwhite\>'   ?'-':'+'<CR>=iwhite<CR>
nnoremap yoDw :set diffopt<C-R>=&diffopt=~'\<iwhiteall\>'?'-':'+'<CR>=iwhiteall<CR>
nnoremap yoDZ :set diffopt<C-R>=&diffopt=~'\<iwhiteeol\>'?'-':'+'<CR>=iwhiteeol<CR>

nnoremap <expr> yoDB ':set diffopt'.(&diffopt=~'\<iblank\>'   ?'-':'+').'=iblank<CR>'
nnoremap <expr> yoDi ':set diffopt'.(&diffopt=~'\<icase\>'    ?'-':'+').'=icase<CR>'
nnoremap <expr> yoDb ':set diffopt'.(&diffopt=~'\<iwhite\>'   ?'-':'+').'=iwhite<CR>'
nnoremap <expr> yoDw ':set diffopt'.(&diffopt=~'\<iwhiteall\>'?'-':'+').'=iwhiteall<CR>'
nnoremap <expr> yoDZ ':set diffopt'.(&diffopt=~'\<iwhiteeol\>'?'-':'+').'=iwhiteeol<CR>'

" Windows
" This could be smarter. E.g. Look if *any* window has 'diff' set and save them
" in a list before doing `:diffoff`; restore for all windows when doing
" `:diffthis`.
nnoremap <Space><Space>dw :windo diff<C-R>=&diff?'off':'this'<CR><CR>

" Current file.
" Se also |:DiffOrig|.
" TODO: Use a buffer?
" TODO: `--old-group-format "$(TODO)"`
" <https://gist.github.com/romainl/7198a63faffdadd741e4ae81ae6dd9e6>
nnoremap <silent> <Space><Space>dff :w !
\ diff -u --color=always %:S -
\ \| tail -n +3
\ \| less -FR
\ <CR>
nnoremap <silent> <Space><Space>dfw :w !
\ diff -u %:S -
\ \| tail -n +3
\ \| wdiff -d -n
\   -w "$([ "$(tput colors)" -ge 16 ] && tput setaf  9 \|\| { tput bold; tput setaf 1; })"
\   -y "$([ "$(tput colors)" -ge 16 ] && tput setaf 10 \|\| { tput bold; tput setaf 2; })"
\   -x "$([ "$(tput colors)" -ge 16 ] && tput setaf 15 \|\| { tput bold; tput setaf 7; })"
\   -z "$([ "$(tput colors)" -ge 16 ] && tput setaf 15 \|\| { tput bold; tput setaf 7; })"
\ \| less -FR
\ <CR>
" Requires `vim-fugitive`.
nmap <silent> <Space><Space>dg :Gdiffsplit<CR>

" Analogous with |dp| |do| in Normal mode.
" TODO: Is adding a `diffupdate` at the end useful?
nnoremap <silent> <Space>dpp :.diffput<CR>
nnoremap <silent> <Space>doo :.diffget<CR>
xnoremap <silent> <Space>dp  :diffput<CR>
xnoremap <silent> <Space>do  :diffget<CR>
nnoremap <silent> <Space>dp  :set operatorfunc=<SID>diffput<CR>g@
nnoremap <silent> <Space>do  :set operatorfunc=<SID>diffget<CR>g@
if has('eval')
  function! s:diffput(...) abort
    '[,']diffput
  endfunction
  function! s:diffget(...) abort
    '[,']diffget
  endfunction
endif

""" Formatting

" Format comment (only). NOTE that this requires a `gc` comment text object
" plugin.
nmap gQ gqgc
nmap gW gwgc

""" Folds

" Focus current fold.
nnoremap zV zMzv

" Focus next/previous fold. Overwrites default move to start/end of
" next/previous fold.
" TODO: |c_CTRL-R_=| requires |+eval|?
nnoremap <silent> zj m<zjzvm>gvzc'>zv
nnoremap <silent> zk zvzckVzvV:<C-R>=prevnonblank(line('.'))<CR><CR>zvzcVoVzv

" Open/close nested folds recursively. Overwrites default open/close fold under
" cursor recursively.
nnoremap <silent> zO zvzczO
nnoremap <silent> zC zvzcV:foldclose!<CR>zvzc

""" Spelling

" Correct last misspelled word with first suggestion without moving the cursor.
" See also |compl-spelling|.
nnoremap z?               [s1z=<C-O>
inoremap <C-L> <C-G>u<Esc>[s1z=`]a<C-G>u

""" Visual mode operators

" Allows for visual selection of text objects that share a name with an
" operator.
nnoremap <silent> <Space>v     :set operatorfunc=<SID>v<CR>g@
nnoremap <silent> <Space>V     :set operatorfunc=<SID>V<CR>g@
nnoremap <silent> <Space><C-V> :set operatorfunc=<SID>CV<CR>g@
if has('eval')
  function! s:v(...) abort
    execute 'normal!' '`[v`]'
  endfunction
  function! s:V(...) abort
    execute 'normal!' '`[V`]'
  endfunction
  function! s:CV(...) abort
    execute 'normal!' "`[\<C-V>`]"
  endfunction
endif

""" Align operator
" Requires the `column` program (in `bsdextrautils` on Debian).
" See also |lion|.
" TODO: Does not support |count|, |quote|.
" TODO: When re-sourcing the vimrc, this overrides the previous |vim-lion|
" mappings.
xnoremap gl !column -t -o ' '<CR>
if has('eval')
  nnoremap gl :set operatorfunc=<SID>gl<CR>g@
  function! s:gl(...) abort
    '[,']!column -t -o ' '
  endfunction
endif

""" Normalize date to ISO 8601
" Requires the `date` program (in `coreutils` on Debian).
" See |:visual_example|.
" See also |abolish-coercion|.
" This is hacky but works without |+eval|.
xnoremap crd <Esc>`>a<CR><Esc>`<i<CR><Esc>!!date +'\%Y-\%m-\%d' -f -<CR>kJJ

""" Go to string in file under cursor

" Make |gF| support `filename:/search string/`.
"
" The built-in |gF|, and related |CTRL-W_F|, |CTRL-W_gF|, commands extend |gf|
" by also parsing trailing `:{lineno}` (specifically, any non-filename
" character optionally surrounded by whitespace, or the literal string
" ` line `, followed by a decimal number). This corresponds to common output of
" compilers/interpreters/linters. It also happens to roughly correspond to
" _one_ of the common (or, for [POSIX.1-2017][], allowed) outputs of `ctags`.
" It does not however allow the _other_ format: search strings. Let's fix that.
"
" Search strings are very useful when you're not sure what version of the file
" someone else might have, so it's hard to give an accurate line number, but
" the chance is high that a search string will yield the correct location. It
" also conveys more information to someone not wanting to _follow_ the
" reference right now than a line number would.
"
" Vim actually allows _any_ Ex command in tag files (subject to |'exrc'| and
" |'secure'|, see |tag-security|), not just line numbers and search strings,
" see |tags-file-format|. The mapping below only accepts what amounts to
" possibly anchored non-regex search strings, for three reasons: 1) security,
" 2) implementation simplicity, 3) it is what [POSIX.1-2017][] `ctags` / `ex` /
" `vi` supports.
"
" It is important that we lean on the built-in mappings as much as possible
" since they include functionality based on <count>, |'isfname'|, |'path'|,
" |'suffixesadd'|, and |'includeexpr'|, special handling of escaped spaces and
" trailing punctuation, as well as home directory and environment variable
" expansion (|expandcmd|) that would be hard to re-implement, see |gf|. Note
" that the built-in mappings only trigger when the cursor is on or before the
" _filename_, not on the address. |gF| falls back to |gf| functionality without
" giving an error message if a trailing line number can't be found, which we
" mimic. |gF| also doesn't give an error (or any) message if the given line
" number is not present in the file (it goes to the closest one, i.e. the first
" or last line), so we use a "soft" |:echo| instead of "hard" |:echoerr| to
" tell the user if the search string was not found.
"
" [POSIX.1-2017]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ctags.html#tag_20_27_12

" TODO: I think the regex is not quite right, like it triggers even when the
" cursor is not on the filename. Maybe we need to put the |/\%>| in a group, or
" adjust the `col('.')-1`. I lost the test case, sorry.

" TODO: Clarify/expand separators. POSIX mandates tabs as separators for tag
" files, document that we use the |gF| rules instead, after we implement them
" completely (` line `). Also hardcode to allow : as separator, because
" ftplugin/perl.vim:/^set isfname/ adds : to the set of valid filenames
" 'isfname' (a global option!). Document this too.

" TODO: POSIX allows / and ? as pattern delimiters, we only allow /. Fix this
" and document.

" TODO: POSIX allows the anchors ^ and $ to be used independently. It looks
" like we do too, but it is not clear from the documentation.

" TODO: Also support `file:line:col`? Generalize the separator to conform to
" the |gF| rules, but change ` line ` to ` col(umn)? `.

" TODO: We are accruing features. Split into plugin? `vim-unobtrusive-gf`?

" TODO: The excessive string concatenation is done for readability, but it
" might be better (and faster) as comments? Those can get out of sync though...

if has('eval')
  nnoremap <silent> gF      :<C-U>call <SID>gf("gF")<CR>
  nnoremap <silent> <C-W>F  :<C-U>call <SID>gf("\<lt>C-W>F")<CR>
  nnoremap <silent> <C-W>gF :<C-U>call <SID>gf("\<lt>C-W>gF")<CR>
  let s:isfname = &isfname
  function! s:gf(cmd) abort
    let back   = '\\\\'
    let slash  = '\\\/'
    let dollar = '\$\/\@!'
    let other  = '[^\/$]'
    let chr    = '\%(' . join([back, slash, dollar, other], '\|') . '\)'
    let str    = '\(' . chr . '\+\)'
    let start  = '\(\^\?\)'
    let end    = '\(\$\?\)'
    let addr   = '\/' . start . str . end . '\/'
    let after  = '\%>' . (col('.')-1) . 'c'
    let file   = '[[:fname:]]'
    let sep    = '[[:space:]]*[^[:fname:][:digit:]/][[:space:]]*'
    let pat    = '\m' . after . file . sep . addr
    " echo pat
    let v:errmsg = ""
    try
      " See |myvimrc-isfname-bug|. Save the current 'isfname' and then set it
      " to the one we set in our vimrc earlier (i.e. our actual preference).
      let isfname  = &isfname
      let &isfname = s:isfname
      let match  = matchlist(getline('.'), pat)
      silent! execute 'normal!' (v:count1 . a:cmd)
    finally
      " If the above `execute` triggered a change in 'isfname' through a loaded
      " ftplugin, let that change stand. Otherwise revert our earlier override.
      if &isfname ==# s:isfname
        let &isfname = isfname
      endif
    endtry
    if v:errmsg != ""
      echohl Error | echomsg v:errmsg | echohl None
    elseif !empty(match)
      let start = match[1]
      let str   = match[2]
      let end   = match[3]
      let pat   = '\C' . '\m' . start . '\V' . escape(str, '\') . '\m' . end
      call cursor(1, 1)
      if search(pat, 'c')
        normal! zz
      else
        echo "Can't find search string /" . start . str . end . "/"
      endif
    endif
  endfunction
endif

"" Commands

""" Fill quickfix with output from system command
" TODO: |<q-args>| does not seem to escape quotes, so things like `Cexpr echo
" 'hello'` does not work. Probably passing it to a function so that we get a
" argument variable to call |shellescape()| on is required?
" TODO: How does this compare to |dispatch|?
command! -bang -nargs=+ -complete=shellcmd Cexpr
\ execute (empty(<q-bang>)?'cexpr':'cgetexpr') 'system(expandcmd(' escape(<q-args>, "'") '))'

""" Run `git ls` and preview commits on cursor move
" TODO: |fugitive|'s |Gclog| probably covers this use case. Remove?
" Requires |fugitive| and a `ls`/`lsa` Git alias.
command! -bar -nargs=0 Gls
\ Git ls |
\ execute 'nmap <silent> <buffer> j j:silent vert Gpedit <cword><CR><C-W>=' |
\ execute 'nmap <silent> <buffer> k k:silent vert Gpedit <cword><CR><C-W>=' |
\ execute 'autocmd vimrc BufUnload <buffer> pclose' |
command! -bar -nargs=0 Glsa
\ Git lsa |
\ execute 'nmap <silent> <buffer> j j:silent vert Gpedit <cword><CR><C-W>=' |
\ execute 'nmap <silent> <buffer> k k:silent vert Gpedit <cword><CR><C-W>=' |
\ execute 'autocmd vimrc BufUnload <buffer> pclose' |

""" Diff
" See also |start-vimdiff|, |:DiffOrig|.
" TODO: This is unfinished.
if has('eval')
  command! -nargs=* -complete=file Diff call s:diff(<q-args>)
  function! s:diff(arglist) abort
    " windo diffoff
    " if a:0
    "   let args = map(
    "   \   flatten(map(
    "   \     copy(a:000),
    "   \     'expand(v:val, 1, 1)'
    "   \   )),
    "   \   'fnameescape(v:val)'
    "   \ )
    "   execute 'args' join(args)
    if a:arglist
      execute 'args' arglist
      vertical all
      windo diffthis
    else
      " let filetype    = &filetype
      " let swapfile    = &swapfile
      diffthis
      aboveleft vertical new
      diffthis
      " let &filetype   = filetype
      " let &swapfile   = swapfile
      let &filetype   = getbufvar('#', '&filetype')
      let &swapfile   = getbufvar('#', '&swapfile')
      let &buftype    = 'nofile'
      let &bufhidden  = 'wipe'
      let &modifiable = 1 " Even `bh=wipe` doesn't forget the `noma` below.
      execute 'file' 'diff://' . expand('#:p')
      read ++edit #
      1 delete _
      let &modifiable = 0
      wincmd p
    endif
  endfunction
endif

"" Auto commands

""" Options

"""" 'background'
" Vim likes to automatically reset |'background'| at inopportune moments (like
" at `$VIMRUNTIME/colors/default.vim:/set bg&/`).
if has('eval')
  let s:background = &background
  autocmd vimrc OptionSet background let &background = s:background
endif

"""" 'ttyscroll'
" *myvimrc-autocmd-ttyscroll*
" Unconditionally enable |'ttyscroll'| (by setting it to its default value
" `999`) if there are no vertical splits (so terminal scrolling can actually be
" used without downsides) and restore if there are vertical splits. Makes the
" most noticeable difference when doing |CTRL-U| |CTRL-D| in a Linux virtual
" terminal.
" TODO: When we set it to `999` we could store the current value in
" `s:ttyscrol` so that manual adjustments won't be lost.
if has('eval')
  let s:ttyscroll = &ttyscroll
  autocmd vimrc VimEnter,WinEnter *
  \ let &ttyscroll = empty(filter(getwininfo(), 'v:val.wincol != 1'))
  \   ? 999
  \   : s:ttyscroll
endif

""" Save/load view
" Roughly as suggested in |:loadview|, load auto-saved view when opening
" buffer. Better (TODO: how?) than |last-position-jump|, |restore-cursor|. See
" also |'viewoptions'|.
" TODO: Use a (configurable?) `[nr]` argument to the `{mk,load}view` and
" reserve the use of the no argument version for the user.
" TODO: We used to also match on `Buf{Write,Read}` but that generated "E32: No
" file name" when e.g. discarding changes with `X` in the `vim-fugitive` `:Git`
" window.
autocmd vimrc BufWinLeave *
\ if !empty(expand('<afile>')) && empty(&buftype) |
\   mkview |
\ endif |
autocmd vimrc BufWinEnter *
\ if !empty(expand('<afile>')) && empty(&buftype) |
\   loadview |
\ endif |

""" Unclutter special buffers
" TODO: This needs to be moved to after the plugins for it to work in e.g.
" fugitive?
if !has('nvim')
  autocmd vimrc BufReadPost,TerminalWinOpen *
  \ if !empty(&buftype) |
  \   silent setlocal nolist nospell |
  \ endif |
endif

""" Fixate terminal height
autocmd vimrc TerminalWinOpen *
\ set winfixheight |
\ 15wincmd _ |

""" QuickFix/Location list

"""" Open window automatically
" See
" - <https://github.com/tpope/vim-dispatch/issues/145>
" - <https://github.com/tpope/vim-dispatch/issues/254>
" - <https://github.com/tpope/vim-dispatch/issues/310>
" TODO: These `vim-dispatch` issues might have been fixed?
" TODO: Maybe this can be fixed by switching back to the previous window after
" opening the quickfix window? Make sure to only switch to the previous if we
" actually opened the window.
" autocmd vimrc QuickFixCmdPost [^l]* nested botright cwindow
" autocmd vimrc QuickFixCmdPost   l*  nested botright lwindow
" autocmd vimrc QuickFixCmdPost  {{make,{,vim}grep{,add}},c{,get,add}{expr,file,buffer}} nested botright cwindow
" autocmd vimrc QuickFixCmdPost {l{make,{,vim}grep{,add}},l{,get,add}{expr,file,buffer}} nested botright lwindow

"""" Conceal column numbers
autocmd vimrc BufReadPost quickfix
\ setlocal conceallevel=2 concealcursor=nvic nonumber |
\ syntax match qfColumnNr / col \d\+/ containedin=qfLineNr transparent conceal |

""" Highlight all searches
" |'incsearch'| only highlights the first match.
if has('eval')
  autocmd vimrc CmdlineEnter /,\? let s:hlsearch =  &hlsearch | set hlsearch
  autocmd vimrc CmdlineLeave /,\? let  &hlsearch = s:hlsearch
endif

""" Terminal folding
" If a line (like a shell prompt) starts with `│` it will define a fold start.
" TODO: More "shell integrations":
" - <https://wezterm.org/shell-integration.html>
" - <https://learn.microsoft.com/en-us/windows/terminal/tutorials/shell-integration>
"   - OSC 133: Prompt.
"     - <https://per.bothner.com/blog/2019/shell-integration-proposal/
"     - <https://gitlab.freedesktop.org/Per_Bothner/specifications/blob/master/proposals/semantic-prompts.md>
"   - OSC 7: Current directory.
"     - <https://lacamb.re/blog/osc7_in_neovim_third_time.html>
autocmd vimrc TerminalWinOpen *
\ set foldlevel=0 |
\ set foldmethod=expr |
\ set foldexpr=getline(v:lnum)=~'^│'?'>1':'=' |
autocmd vimrc CursorMoved *
\ if &buftype ==# 'terminal' |
\   let &foldmethod = &foldmethod |
\ endif |

""" Find other Vim instance when swap file exists
" Only for Unix-likes. POSIX-compliant. Sends signal `USR1` which when received
" outputs a `BEL` to the terminal, hopefully making the terminal emulator mark
" the window as urgent, helping the user find it. Under X11 if `xdotool` is
" installed one alternative might be `!xdotool set_window --urgency 1
" $WINDOWID`. |swapinfo| was added in `8.1.0313` and |SigUSR1| in `8.2.0952`.
if has('eval') && executable('kill')
  autocmd vimrc SwapExists *
  \ let s:swapinfo = swapinfo(v:swapname) |
  \ if s:swapinfo['host'] == hostname() |
  \   call system('kill -USR1 ' . s:swapinfo['pid']) |
  \ endif |
  autocmd vimrc SigUSR1 * silent execute "!printf '\\a'" | silent redraw!
endif

"" Colors
" See |cterm-colors|, |gui-colors|.
" See |colortest.vim|, |hitest.vim|.

" TODO: If/when we break this out to a separate file/plugin, we might want to
" link to stuff like this:
" - <https://jeffkreeftmeijer.com/vim-16-color/>
" - <https://github.com/jeffkreeftmeijer/vim-dim>

" This creates problems, we should just make sure files opened with
" `vim.desktop` get the colors. It looks like `.desktop` files with
" `Terminal=true` makes the terminal source `~/.profile` but not `~/.shrc`
" (which makes sense in a way, it's `Terminal`, not `Shell`).
if $_ ==# '/usr/bin/xdg-open'
  silent !. ~/.shrc.d/color.sh
  " redraw!
endif

" TODO: Document that |autocmd| is available in "tiny" (i.e. always) but
" |+syntax| requires "normal", so doing it the way we're doing it makes sure it
" works everywhere. This is especially important for interface highlights
" (|listchars|).

" TODO: This is getting ridiculous. |:syntax-on| suggests we can just use
" |:highlight| outside any |autocmd|s and it will work. Look into this.

" TODO: Support `has('gui_running')` and |'termguicolors'|. Look into e.g.
" |g:terminal_ansi_colors|.

""" General
" Transparent background, grayscale interface. Sensible errors/warnings,
" spellchecking, diffs, comments, and todos. See |:hi-normal| and
" |:hi-normal-cterm|.

if !has('gui_running')
  autocmd vimrc ColorScheme * nested
  \ highlight! Normal guibg=NONE |
endif

" TODO: Look at all in |'highlight'| (in particular |hl-Search|) and |highlight-default|.
" TODO: This could be done by setting |'highlight'| instead of monkying around
" with highlight groups. Unclear if better.
" TODO: For all chromatic colors, use `Light*`.
" TODO: Remove all `White` except `Normal` from this list and link them to `Normal`?
" TODO: Remove all `Gray` except `Comment` from this list and link them to `Comment`?
" NOTE: When |hl-StatusLine| is a |:hi-link| Vim cannot determine if it's equal
" to |hl-StatusLineNC| and uses "^^^". Same for `StatusLineTerm{,NC}`. See
" `$VIMSRC/src/drawscreen.c:550` (at the bottom of the function
" `win_redr_status`) in the Vim source.
" TODO: Since the Linux console cannot use `DarkGray` as a background, consider
" using any other `Dark*` color (e.g. `DarkYellow`) as (non-`reverse`) `Visual`
" `ctermbg` with `ctermfg=NONE`. NOTE: This requires that we standardize on
" `__color_set_uniform_diff_low`. `__color_set_uniform_diff_high` works (in
" fact is required) for `cterm=reverse ctermfg=NONE ctermbg=NONE`.
" TODO: Why do we need the `term=NONE`? Is this tested ever? Is it used in
" `vim.tiny`?

" TODO: Vim requests the background color from the terinal with the escape
" sequence |t_RB| and sets |'background'| when it gets the response
" (|v:termrbgresp|). That response may come after |vimrc| has completed, so if
" you want to do |:colorscheme| do it in a `autocmd vimrc OptionSet
" background`.

" autocmd vimrc ColorScheme * nested
" \ highlight Normal font='Monospace Regular 10'

" Emphasis levels, in ascending order:
"
" | Highlight       | Use case                           | Example        |
" | `Comment`       | Non-highlighted (interface, menus) | `StatusLineNC` |
" | `Normal`        | Non-highlighted (interface, menus) | `StatusLine`   |
" | `Visual`        | Highlighted     (buffer text)      | `Search`       |
" | `cterm=reverse` | Highlighted     (buffer text)      | `IncSearch`    |

" \ set background=dark | runtime syntax/syncolor.vim |
autocmd vimrc ColorScheme * nested
\ highlight Normal           term=NONE   cterm=NONE   gui=NONE              ctermbg=NONE        ctermfg=White      |
\ highlight Comment          term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=DarkGray   |
\ highlight Todo             term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=White      |
\ highlight Error            term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=LightRed   |
\ highlight SpellBad         term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=LightRed   |
\ highlight SpellOther       term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=LightRed   |
\ highlight Visual           term=NONE   cterm=NONE   gui=NONE              ctermbg=LightGray   ctermfg=NONE       |
\ highlight Search           term=NONE   cterm=NONE   gui=NONE              ctermbg=DarkBlue    ctermfg=NONE       |
\ highlight IncSearch        term=NONE   cterm=NONE   gui=NONE              ctermbg=DarkYellow  ctermfg=NONE       |
\ highlight DiffAdd          term=NONE   cterm=NONE   gui=NONE              ctermbg=DarkGreen   ctermfg=NONE       |
\ highlight DiffDelete       term=NONE   cterm=NONE   gui=NONE              ctermbg=DarkRed     ctermfg=NONE       |
\ highlight DiffChange       term=NONE   cterm=NONE   gui=NONE              ctermbg=DarkBlue    ctermfg=NONE       |
\ highlight DiffText         term=NONE   cterm=NONE   gui=NONE              ctermbg=DarkYellow  ctermfg=NONE       |
\ highlight diffAdded        term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=LightGreen |
\ highlight diffRemoved      term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=LightRed   |
\ highlight StatusLine       term=NONE   cterm=NONE   gui=NONE              ctermbg=NONE        ctermfg=White      |
\ highlight StatusLineTerm   term=NONE   cterm=NONE   gui=NONE              ctermbg=NONE        ctermfg=White      |
\ highlight StatusLineNC     term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=DarkGray   |
\ highlight StatusLineTermNC term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=DarkGray   |
\ highlight Ignore           term=NONE   cterm=NONE   gui=NONE   guibg=NONE ctermbg=NONE        ctermfg=Black      |
" \ highlight DiffAdd          term=NONE   cterm=NONE   gui=NONE              ctermbg=LightGreen  ctermfg=Black      |
" \ highlight DiffDelete       term=NONE   cterm=NONE   gui=NONE              ctermbg=LightRed    ctermfg=Black      |
" \ highlight DiffChange       term=NONE   cterm=NONE   gui=NONE              ctermbg=LightBlue   ctermfg=Black      |
" \ highlight DiffText         term=NONE   cterm=NONE   gui=NONE              ctermbg=LightYellow ctermfg=Black      |

""" No bold
" Remove all `cterm=bold` set by default in `$VIMRUNTIME/syntax/syncolor.vim`.
" The Linux virtual console does typically not have a bold font.
autocmd vimrc ColorScheme * nested
\ highlight Identifier cterm=NONE ctermfg=LightCyan |

""" No underline
" Remove all `cterm=underline` set by default in `$VIMRUNTIME/syntax/syncolor.vim`.
" `Underlined` is linked to by `htmlLink` and `mkdLink` by default and is too
" cluttered for my taste.
autocmd vimrc ColorScheme * nested
\ highlight Underlined cterm=NONE |

""" Wildmenu

" set highlight-=s:StatusLine
" set highlight-=S:StatusLineNC
" set highlight-=z:StatusLineTerm
" set highlight-=Z:StatusLineTermNC
" set highlight+=s:Normal,z:Normal,S:Comment,Z:Comment

autocmd vimrc ColorScheme * nested
\ highlight! link TabLineSel     Normal |
\ highlight! link ToolbarButton  Comment |
" \ highlight! link StatusLine     Normal |
" \ highlight! link StatusLineTerm Normal |

""" Interface
autocmd vimrc ColorScheme * nested
\ highlight! link TabLine          Comment |
\ highlight! link TabLineFill      Comment |
\ highlight! link ToolbarLine      Comment |
\ highlight! link VertSplit        Comment |
\ highlight! link LineNr           Comment |
\ highlight! link Folded           Comment |
\ highlight! link FoldColumn       Comment |
\ highlight! link SignColumn       Comment |
" \ highlight! link StatusLineNC     Comment |
" \ highlight! link StatusLineTermNC Comment |

""" Errors/warnings
autocmd vimrc ColorScheme * nested
\ highlight! link ErrorMsg   Error |
\ highlight! link WarningMsg Error |

""" Spell
autocmd vimrc ColorScheme * nested
\ highlight! link SpellCap   SpellOther |
\ highlight! link SpellRare  SpellOther |
\ highlight! link SpellLocal SpellOther |

""" Visual
autocmd vimrc ColorScheme * nested
\ highlight! link VisualNOS    Visual |
\ highlight! link ColorColumn  Visual |
\ highlight! link CursorColumn Visual |
\ highlight! link CursorLine   Visual |
\ highlight! link CursorLineNr Visual |

""" Wildmenu
autocmd vimrc ColorScheme * nested
\ highlight! link WildMenu Search |

""" Popup menu
autocmd vimrc ColorScheme * nested
\ highlight Pmenu      ctermbg=LightGray ctermfg=DarkGray |
\ highlight PmenuSel   ctermbg=LightGray ctermfg=White    |
\ highlight PmenuSbar  ctermbg=DarkGray                   |
\ highlight PmenuThumb ctermbg=White                      |

""" Ignore
" See |'highlight'|. By default links to |NonText|.
autocmd vimrc ColorScheme * nested
\ highlight! link EndOfBuffer Ignore |

""" Non-Ignore
" By default links to |Ignore|.
autocmd vimrc ColorScheme * nested
\ highlight! link helpIgnore   Comment    |
\ highlight! link helpBacktick helpIgnore |
\ highlight! link helpBar      helpIgnore |
\ highlight! link helpStar     helpIgnore |

""" Non-document characters
" See |'highlight'|, |'showbreak'|, |'listchars'| `extends` `precedes` `eol`.
autocmd vimrc ColorScheme * nested
\ highlight! link NonText Special |

""" Document non-printables characters
" See |'highlight'|, |'listchars'| `tab`, `space`, `trail`, `nbsp`.
autocmd vimrc ColorScheme * nested
\ highlight! link SpecialKey Comment |

""" Document conceal replacement characters
autocmd vimrc ColorScheme * nested
\ highlight! link Conceal Identifier |

""" Debug
autocmd vimrc ColorScheme * nested
\ highlight debugPC         term=reverse cterm=NONE gui=NONE guibg=DarkBlue ctermbg=DarkBlue guifg=NONE     ctermfg=NONE     |
\ highlight debugBreakpoint term=NONE    cterm=NONE gui=NONE guibg=NONE     ctermbg=NONE     guifg=LightRed ctermfg=LightRed |

" TODO: Filetype-specific customizations probably should trigger on some other
" event.

""" Markdown delimiters
autocmd vimrc ColorScheme * nested
\ highlight! link mkdDelimiter     Comment |
\ highlight! link mkdCodeDelimiter Comment |
\ highlight! link mkdCodeStart     Comment |
\ highlight! link mkdCodeEnd       Comment |

""" Vim comments
autocmd vimrc ColorScheme * nested
\ highlight! link vimCommentString vimComment |
\ highlight! link vimCommentTitle  vimComment |

""" Apply
" TODO: Move `syntax enable` to someplace before this?
" TODO: `doautocmd vimrc ColorScheme` worked for everything except when using
" Vim as `$MANPAGER`. Not sure if this is a problem with `vimrc` or
" `.vim/ftplugin/man.vim`. We don't always `has('eval')` though.
" echom &background
" if has('eval')
"   colorscheme default
" endif
" echom &background
doautocmd vimrc ColorScheme
" autocmd vimrc VimEnter  *               nested doautocmd vimrc ColorScheme
" autocmd vimrc OptionSet background,t_Co nested doautocmd vimrc ColorScheme

"" Plugins

if has('eval')

  """ |mapleader|
  let g:mapleader      = "\<Space>"
  let g:localmapleader = "\<Space>"

  """ GitAdd
  " <https://git.rcrnstn.net/rcrnstn/vim-gitadd>

  " Make sure to not enable |:syntax| and |:filetype| until all plugins that
  " interact with them have been added! See |:packadd|.
  command! -bar -bang -nargs=+ GitAdd call s:GitAdd(<q-bang>, <f-args>)
  function! s:GitAdd(bang, url, ...) abort
    let name = split(a:url, '/')[-1]
    let dir = split(&packpath, ',')[0] . '/pack/gitadd/opt/' . name
    if !isdirectory(dir)
      silent execute '!git clone --recurse-submodules' join(a:000) a:url dir
      if isdirectory(dir . '/doc')
        execute 'helptags' dir . '/doc'
      endif
    endif
    if empty(a:bang)
      execute 'packadd!' name
    endif
  endfunction

  """ Interface

  """" `tpope/vim-flagship`
  " GitAdd https://github.com/tpope/vim-flagship

  """" `chrisbra/NrrwRgn`
  GitAdd https://github.com/chrisbra/NrrwRgn

  let g:nrrw_topbot_leftright = 'belowright'

  xmap <Space><Space>nr <Plug>NrrwrgnDo

  """ File type

  """" Built-in `vim`

  " See |ft-vim-indent|.
  let g:vim_indent_cont = 0

  """" Built-in `sh`

  " See |ft-sh-syntax|.
  let g:is_posix = 1

  """" Built-in `man`

  " See |ft-man-plugin|.
  let g:ft_man_folding_enable = 1

  " TODO: Should be easy to create a |'foldexpr'| that's better than the
  " built-in `foldmethod=indent`.
  " function s:manfold() abort
  "   foldlevel()
  " endfunction

  " We're using our own |:ManHelp| instead.
  " runtime ftplugin/man.vim
  " setglobal keywordprg=:Man

  """" Built-in `asm`

  " See |ft-asm-syntax|.
  let g:filetype_i = 'asm'

  """" `tpope/vim-markdown`
  " GitAdd https://github.com/tpope/vim-markdown

  """" `preservim/vim-markdown`
  GitAdd https://github.com/preservim/vim-markdown

  let g:vim_markdown_no_default_key_mappings = 1
  let g:vim_markdown_toc_autofit = 1
  let g:vim_markdown_math = 1
  let g:vim_markdown_frontmatter = 1
  let g:vim_markdown_no_extensions_in_markdown = 1
  let g:vim_markdown_auto_insert_bullets = 0
  let g:vim_markdown_new_list_item_indent = 0

  " " Defaults.
  " " |vim-markdown-fenced-code-block-languages|
  " let g:vim_markdown_fenced_languages = [
  " \   "c++=cpp",
  " \   "viml=vim",
  " \   "bash=sh",
  " \   "ini=dosini",
  " \ ]
  " " Additions.
  " call add(g:vim_markdown_fenced_languages, [
  " \   "diff",
  " \ ])


  " <https://github.com/preservim/vim-markdown/issues/232>
  " let g:vim_markdown_new_list_item_indent = 0
  " let g:vim_markdown_auto_insert_bullets = 0

  """" `vim-pandoc/vim-pandoc-syntax`
  " GitAdd https://github.com/vim-pandoc/vim-pandoc-syntax

  " Roughly as recommended in the readme.
  " autocmd vimrc BufNewFile,BufFilePre,BufRead *.md set filetype=markdown.pandoc

  """" `gpanders/vim-medieval`
  GitAdd https://github.com/gpanders/vim-medieval

  " Roughly as suggested in `vim-medieval/README.md:/Create a mapping/` (but
  " not mentioned in |medieval.txt|...).
  autocmd vimrc FileType markdown
  \ nmap <buffer> <Space>! <Plug>(medieval-eval)

  " TODO: Add `octave` (and `matlab`?). We probably need flags to prevent
  " opening a GUI, does medieval support flags?
  let g:medieval_langs = ['sh', 'bash', 'python=python3']

  """" `sheerun/vim-polyglot`
  GitAdd https://github.com/sheerun/vim-polyglot

  let g:polyglot_disabled = [
  \   'sensible',
  \   'autoindent',
  \   'markdown',
  \ ]

  """" `tpope/vim-sleuth`
  GitAdd https://github.com/tpope/vim-sleuth

  """" `tpope/vim-apathy`
  " GitAdd https://github.com/tpope/vim-apathy

  """" `tpope/vim-scriptease`
  GitAdd https://github.com/tpope/vim-scriptease

  """" `jyscao/vim-greprtpscr`
  GitAdd! https://github.com/jyscao/vim-greprtpscr

  """" `vimwiki/vimwiki`
  " GitAdd https://github.com/vimwiki/vimwiki

  " let g:vimwiki_list = [{
  " \   'path':   '~/notes/',
  " \   'syntax': 'markdown',
  " \   'ext':    '.md',
  " \ }]

  """" `mhinz/vim-rfc`
  GitAdd https://github.com/mhinz/vim-rfc

  """" `HiPhish/info.vim`
  GitAdd https://gitlab.com/HiPhish/info.vim

  """ Normal mode

  """" Built-in `matchit`

  packadd! matchit

  """" `tpope/vim-unimpaired`
  GitAdd https://github.com/tpope/vim-unimpaired

  " Also search for `map[a-z]* \+\S*[][]` in this file.

  " <https://github.com/tpope/vim-unimpaired/issues?q=conceal>
  " <https://github.com/tpope/vim-unimpaired/issues/105>
  " <https://github.com/tpope/vim-unimpaired/pull/152>
  nnoremap yoe :setlocal conceallevel=<C-R>=&conceallevel == 0 ? 2 : 0<CR><CR>
  nnoremap [oe :setlocal conceallevel=<C-R>=&conceallevel == 0 ? 0 : &conceallevel - 1<CR><CR>
  nnoremap ]oe :setlocal conceallevel=<C-R>=&conceallevel == 2 ? 2 : &conceallevel + 1<CR><CR>

  " See also `tpope/sleuth.vim`
  " function! s:textwidth() abort
  "   return max(map(range(1, line('$')), 'len(getline(v:val))'))
  " endfunction
  " let g:textwidth = &textwidth
  " nnoremap yoT :setlocal textwidth=<C-R>=&textwidth == g:textwidth ? <SID>textwidth() : g:textwidth<CR><CR>

  """" `tpope/vim-repeat`
  " Does `vim-repeat` have to go at the beginning to activate integrations with
  " other plugins (e.g. `vim-unimpaired`)?
  GitAdd https://github.com/tpope/vim-repeat

  """" `inkarkat/vim-visualrepeat`
  " NOTE: Requires <https://github.com/inkarkat/vim-ingo-library>
  GitAdd https://github.com/inkarkat/vim-visualrepeat

  """" `tpope/vim-characterize`
  GitAdd https://github.com/tpope/vim-characterize

  """" `chrisbra/unicode.vim`
  " GitAdd https://github.com/chrisbra/unicode.vim

  """" `haya14busa/vim-asterisk`
  GitAdd https://github.com/haya14busa/vim-asterisk

  " As suggested by |asterisk-key-mappings|, except the mappings that don't
  " move the cursor set |'hlsearch'|.
  map            * <Plug>(asterisk-*)
  map            # <Plug>(asterisk-#)
  map           g* <Plug>(asterisk-g*)
  map           g# <Plug>(asterisk-g#)
  map <silent>  z* <Plug>(asterisk-z*): set hlsearch<CR>
  map <silent>  z# <Plug>(asterisk-z#): set hlsearch<CR>
  map <silent> gz* <Plug>(asterisk-gz*):set hlsearch<CR>
  map <silent> gz# <Plug>(asterisk-gz#):set hlsearch<CR>

  """" `AndrewRadev/splitjoin.vim`
  GitAdd https://github.com/AndrewRadev/splitjoin.vim

  " TODO: According to |splitjoin_align| this requires either of the Tabular or
  " Align plugins. We currently prefer vim-lion. Create a pull request?
  " let g:splitjoin_align = 1

  """" `AndrewRadev/switch.vim`
  GitAdd https://github.com/AndrewRadev/switch.vim

  " TODO: The ones with `\%#` |cursor-position| does not work, probably because
  " `switch.vim` tries to match it at some other position internally. Searching
  " manually yields the expected matches though.
  let g:switch_custom_definitions = [{
  \   '\<0\>': '1',
  \   '\<1\>': '0',
  \   '\<OFF\>': 'ON',
  \   '\<Off\>': 'On',
  \   '\<off\>': 'on',
  \   '\<ON\>': 'OFF',
  \   '\<On\>': 'Off',
  \   '\<on\>': 'off',
  \   '\<NO\>': 'YES',
  \   '\<No\>': 'Yes',
  \   '\<no\>': 'yes',
  \   '\<YES\>': 'NO',
  \   '\<Yes\>': 'No',
  \   '\<yes\>': 'no',
  \   '\(\w*\)FALSE\(\w*\)': '\1TRUE\2',
  \   '\(\w*\)False\(\w*\)': '\1True\2',
  \   '\(\w*\)false\(\w*\)': '\1true\2',
  \   '\(\w*\)TRUE\(\w*\)': '\1FALSE\2',
  \   '\(\w*\)True\(\w*\)': '\1False\2',
  \   '\(\w*\)true\(\w*\)': '\1false\2',
  \   '\(\<set\%(g\|global\)\? *\) \%(no\)\([a-z]\+\)\( \|$\)':    '\1 \2\3',
  \   '\(\<set\%(g\|global\)\? *\) \%(no\)\@!\([a-z]\+\)\( \|$\)': '\1 no\2\3',
  \   '\(\<set\%(g\|global\)\? *.*\) \%(no\)\([a-z]*\%#[a-z]*\)\( \|$\)':    '\1 \2\3',
  \   '\(\<set\%(g\|global\)\? *.*\) \%(no\)\@!\([a-z]*\%#[a-z]*\)\( \|$\)': '\1 no\2\3',
  \ }]

  """" `terryma/vim-smooth-scroll`
  " GitAdd https://github.com/terryma/vim-smooth-scroll

  " Kind of useful when pair programming to give some indication of navigation,
  " mostly annoying when not.

  " Roughly as suggested in the readme.
  " noremap <silent> <C-U> :call smooth_scroll#up(  &scroll*1, 20, &scroll/4)<CR>
  " noremap <silent> <C-D> :call smooth_scroll#down(&scroll*1, 20, &scroll/4)<CR>
  " noremap <silent> <C-B> :call smooth_scroll#up(  &scroll*2, 20, &scroll/2)<CR>
  " noremap <silent> <C-F> :call smooth_scroll#down(&scroll*2, 20, &scroll/2)<CR>

  """ Insert mode

  """" `tpope/vim-endwise`
  GitAdd https://github.com/tpope/vim-endwise

  """" `jiangmiao/auto-pairs`
  GitAdd https://github.com/jiangmiao/auto-pairs

  " TODO: Look at "Swedish Character Conflict" in the documentation.

  " Disable meta mappings.
  let g:AutoPairsShortcutToggle     = ''
  let g:AutoPairsShortcutFastWrap   = ''
  let g:AutoPairsShortcutJump       = ''
  let g:AutoPairsShortcutBackInsert = ''

  " Don't jump around too much. See |autopairs-options|.
  let g:AutoPairsCenterLine = 0
  let g:AutoPairsMultilineClose = 0

  """ Command-line mode

  """" `tpope/vim-rsi`
  " GitAdd https://github.com/tpope/vim-rsi

  """" `tpope/vim-abolish`
  GitAdd https://github.com/tpope/vim-abolish

  """ Visual mode

  """" `dahu/Severalections`
  GitAdd https://github.com/dahu/Severalections

  """ Operators

  """" `tpope/vim-surround`
  GitAdd https://github.com/tpope/vim-surround

  """" `tpope/vim-commentary`
  GitAdd https://github.com/tpope/vim-commentary

  """" `tommcdo/vim-lion`
  GitAdd https://github.com/tommcdo/vim-lion

  let g:lion_squeeze_spaces = 1

  """" `tommcdo/vim-nowchangethat`
  GitAdd https://github.com/tommcdo/vim-nowchangethat

  """ Motions / text objects
  " TODO: Check that none of the later ones are not already defined by
  " `targets.vim`.

  """" `wellle/targets.vim`
  GitAdd https://github.com/wellle/targets.vim

  " `targets` just pairs up quotes from the beginning of line, which is often
  " not what we want (especially in shell scripts).
  xnoremap i" i"
  xnoremap i' i'
  xnoremap i` i`
  onoremap i" i"
  onoremap i' i'
  onoremap i` i`

  """" `bkad/CamelCaseMotion`
  GitAdd https://github.com/bkad/CamelCaseMotion

  let g:camelcasemotion_key = '<Space>'

  """" `tommcdo/vim-exchange`
  GitAdd https://github.com/tommcdo/vim-exchange

  """" `qstrahl/vim-dentures`
  " TODO: I'm not happy with this, replace.
  GitAdd https://github.com/qstrahl/vim-dentures

  """" `kana/vim-textobj-user`
  GitAdd https://github.com/kana/vim-textobj-user

  " All later plugins in the "Motions / text objects" category depend on this
  " plugin. See <https://github.com/kana/vim-textobj-user/wiki>.

  """" `kana/vim-textobj-entire`
  GitAdd https://github.com/kana/vim-textobj-entire

  """" `kana/vim-textobj-line`
  GitAdd https://github.com/kana/vim-textobj-line

  " As suggested in |textobj-line-default-key-mappings|, except `{a,i}l` is
  " taken by `targets` "last", so we use uppercase `{a,i}L`.
  let g:textobj_line_no_default_key_mappings = 1
  omap aL <Plug>(textobj-line-a)
  omap iL <Plug>(textobj-line-i)
  xmap aL <Plug>(textobj-line-a)
  xmap iL <Plug>(textobj-line-i)

  """" `kana/vim-textobj-fold`
  GitAdd https://github.com/kana/vim-textobj-fold

  """" `kana/vim-textobj-syntax`
  GitAdd https://github.com/kana/vim-textobj-syntax

  """" `kana/vim-textobj-function`
  GitAdd https://github.com/kana/vim-textobj-function

  """" `idbrii/textobj-word-column.vim`
  GitAdd https://github.com/idbrii/textobj-word-column.vim

  """" `adriaanzon/vim-textobj-matchit`
  GitAdd https://github.com/adriaanzon/vim-textobj-matchit

  """" `rhysd/vim-textobj-continuous-line`
  GitAdd https://github.com/rhysd/vim-textobj-continuous-line

  """" `rsrchboy/vim-textobj-heredocs`
  GitAdd https://github.com/rsrchboy/vim-textobj-heredocs

  """ Colors

  """" `gruvbox-community/gruvbox`
  " TODO: This is a stopgap untill we make a Vim colorscheme from
  " <https://notes/rcrnstn/colors.md>.
  GitAdd https://github.com/gruvbox-community/gruvbox

  let g:gruvbox_contrast_dark = 'hard'
  let g:gruvbox_invert_selection = 0

  autocmd vimrc OptionSet termguicolors nested
  \ execute 'colorscheme' &termguicolors ? 'gruvbox' : 'default' |

  doautocmd OptionSet termguicolors

  """ Windows

  """" `moll/vim-bbye`
  GitAdd https://github.com/moll/vim-bbye

  """" `chrisbra/NrrwRgn`
  " GitAdd https://github.com/chrisbra/NrrwRgn
  " TODO: I have not evaluated this yet. If it isn't to our liking there is
  " also <https://github.com/chrisbra/NrrwRgn#similar-work>.

  """ Folds

  """" `rcrnstn/vim-unobtrusive-fold`
  GitAdd https://git.rcrnstn.net/rcrnstn/vim-unobtrusive-fold

  " As suggested in |unobtrusive_fold#text()|.
  set foldtext=unobtrusive_fold#text()

  " As suggested in |unobtrusive-fold-example|.
  " autocmd vimrc FileType *        UnobtrusiveFoldComment
  " autocmd vimrc FileType markdown UnobtrusiveFoldChar #

  let g:vim_markdown_folding_disabled = 1

  """ Undo

  """" `mbbill/undotree`
  GitAdd https://github.com/mbbill/undotree

  nnoremap <silent> <Space><Space>u :UndotreeToggle<CR>

  " let g:undotree_HighlightChangedText = 0
  " let g:undotree_DiffCommand = 'diff -u'

  """ QuickFix

  """" Built-in `cfilter`
  packadd! cfilter

  """" `romainl/vim-qf`
  " GitAdd https://github.com/romainl/vim-qf

  " let g:qf_mapping_ack_style = 1
  " let g:qf_shorten_path = 1

  " TODO: Check if this causes problems, see the "Open the QuickFix/Location
  " list window" section.
  " let g:qf_auto_open_quickfix = 0

  """ Spell

  """" `inkarkat/vim-SpellCheck`
  " NOTE: Requires <https://github.com/inkarkat/vim-ingo-library>
  " GitAdd https://github.com/inkarkat/vim-SpellCheck

  """ Syntax

  """" `inkarkat/vim-SyntaxRange`
  " NOTE: Requires <https://github.com/inkarkat/vim-ingo-library>
  " GitAdd https://github.com/inkarkat/vim-SyntaxRange

  """" `ypcrts/securemodelines`
  " GitAdd https://github.com/ypcrts/securemodelines
  " TODO: Last updated 2019 as of this writing, can we be sure this is really
  " more secure than the built-in sandbox? Besides, we currently disable
  " modelines with `set modelines=0`.

  " let g:secure_modelines_verbose = 1

  """ Modelines

  """" `ypcrts/securemodelines`
  " GitAdd https://github.com/ypcrts/securemodelines
  " TODO: Last updated 2019 as of this writing, can we be sure this is really
  " more secure than the built-in sandbox? Besides, we currently disable
  " modelines with `set modelines=0`.

  " let g:secure_modelines_verbose = 1

  """ Environment interaction

  """" Built-in `netrw`

  let g:netrw_home      = '~/.cache/vim/netrw'
  let g:netrw_banner    = 0
  let g:netrw_liststyle = 3 " Tree.

  " Netrw versions (roughly) 162h to 170 break `gx`. Never download the remote
  " file to a temporary. See
  " - <https://github.com/vim/vim/issues/1386>
  " - <https://github.com/vim/vim/issues/4738>
  " - <https://github.com/vim/vim/pull/7188>
  let g:netrw_nogx = 1
  nnoremap <silent> gx  :call netrw#BrowseX(netrw#GX(), 0)<CR>
  xnoremap <silent> gx y:call netrw#BrowseX(@",         0)<CR>

  """" `bogado/file-line`
  GitAdd https://github.com/bogado/file-line

  """" `ctrlpvim/ctrlp.vim`
  GitAdd https://github.com/ctrlpvim/ctrlp.vim

  " <https://github.com/kien/ctrlp.vim/issues/337>
  " <https://github.com/ctrlpvim/ctrlp.vim/pull/316>
  " <https://github.com/ctrlpvim/ctrlp.vim/issues/450>
  " NOTE: Highly dependent on non-documented ctrlp internals.
  function! s:ctrlp_syntax() abort
    execute 'autocmd vimrc OptionSet cursorline'
    \  'if winnr() == ' winnr() ' && &cursorline |'
    \  '  set nocursorline |'
    \  'endif |'
    syntax     region CtrlPLine      start="^"   end="$"
    syntax     region CtrlPLineSel   start="\%#" end="$"
    highlight! link   CtrlPLine      Comment
    highlight! link   CtrlPLineSel   Normal
    " TODO: `&term ==# 'linux'` does not do `underline`.
    highlight         CtrlPPrtCursor term=underline cterm=underline
  endfunction
  " We need to define the `Syntax` `autocmd` after `ctrlp` has been loaded, but
  " also when we reload the `vimrc`.
  " TODO: This still does not work when reloading the `vimrc`.
  autocmd vimrc VimEnter *
  \ autocmd vimrc Syntax ctrlp call s:ctrlp_syntax()
  autocmd vimrc Syntax ctrlp call s:ctrlp_syntax()

  let g:ctrlp_working_path_mode = ''
  let g:ctrlp_match_current_file = 1
  let g:ctrlp_follow_symlinks = 2
  let g:ctrlp_show_hidden = 1
  let g:ctrlp_status_func = ''
  let g:ctrlp_line_prefix = ''
  let g:ctrlp_use_caching = 0
  let g:ctrlp_use_readdir = 0 " Respect 'wildignore'.
  let g:ctrlp_max_files = 0 " Default 10000.
  let g:ctrlp_max_depth = 40 " Default 40.
  let g:ctrlp_match_window = 'max:20' " Default 'max:10'
  " let g:ctrlp_user_command_async = 1
  if has('unix')
    " TODO: Set `grepprg` to something similar, that skips things in
    " 'wildignore'.
    let s:wildignore      = split(&wildignore, ',')
    let s:wildignore_name = filter(copy(s:wildignore), '!count(v:val, "/")')
    let s:wildignore_path = filter(copy(s:wildignore), ' count(v:val, "/")')

    " \ ' ' . join(map(s:wildignore_name, '"-o -name " . shellescape(v:val)')) .
    " \ ' ' . join(map(s:wildignore_path, '"-o -path " . shellescape(v:val)')) .
    let g:ctrlp_user_command =
    \ 'find' .
    \ (!g:ctrlp_follow_symlinks ? '' : ' -L') .
    \ ' %s' .
    \ ' -mount' .
    \ ' -maxdepth ' . g:ctrlp_max_depth .
    \ ' \( -false' .
    \ (g:ctrlp_show_hidden ? '' : ' -o -name ".*"') .
    \ ' ' . join(map(s:wildignore_name, '"-o -name " . v:val')) .
    \ ' ' . join(map(s:wildignore_path, '"-o -path " . v:val')) .
    \ ' \) -prune -o' .
    \ ' -type f -print' .
    \ ' 2> /dev/null' .
    \ (!g:ctrlp_max_files ? '' : ' | head -n ' . g:ctrlp_max_files)


    " \ ' -type d -exec test -d {}/.git \; -prune -o' .
    " \ ' -exec grep -Il . {} +' .
    " \ ' \(' .
    " \ (g:ctrlp_follow_symlinks == 2 ? ' -o -type l' : '') .
    " \ ' \)' .

    " function! s:ctrl_find_prefix(prefix, list) abort
    "   return map(copy(a:list), '["' . a:prefix . '", shellescape(v:val)]')
    " endfunction
    " function! s:ctrl_find_join(join, list) abort
    "   return ['\('] + eval(join(a:list, '+["' . a:join . '"]+')) + ['\)']
    " endfunction
    " let g:ctrlp_user_command = join(
    " \   ['find', (!g:ctrlp_follow_symlinks ? '' : '-L'), '%s'] +
    " \   ['-maxdepth', g:ctrlp_max_depth] +
    " \   s:ctrl_find_join('-o',
    " \     s:ctrl_find_prefix('-name', g:ctrlp_show_hidden ? [] : ['.*']) +
    " \     s:ctrl_find_prefix('-name', s:wildignore_name) +
    " \     s:ctrl_find_prefix('-path', s:wildignore_path)
    " \   ) + ['-prune', '-o'] +
    " \   ['-type', 'f', '-print'] +
    " \   ['2>', '/dev/null'] +
    " \   (!g:ctrlp_max_files ? [] : ['|', 'head', '-n', g:ctrlp_max_files])
    " \ )

  endif

  """" `mhinz/vim-tree`
  " GitAdd https://github.com/mhinz/vim-tree

  " As suggesteed in the readme.
  " autocmd vimrc FileType tree
  " \ setlocal foldmethod=expr

  """" `tpope/vim-fugitive`
  GitAdd https://github.com/tpope/vim-fugitive

  " autocmd vimrc FileType fugitiveblame call fugitive#MapJumps()

  " http://vimcasts.org/episodes/fugitive-vim-browsing-the-git-object-database/
  " TODO: Do we want `bufhidden=wipe`?
  autocmd vimrc BufReadPost fugitive://* setlocal bufhidden=delete

  autocmd vimrc FileType git set foldmethod=expr foldexpr=GitFoldexpr()
  function! GitFoldexpr() abort
    let line = getline(v:lnum)
    if line =~# '^diff --git'
      return '>1'
    elseif line =~# '@@ [-+0-9, ]\+ @@'
      return '>2'
    endif
    return '='
  endfunction

  " TODO: Add some `git log -L` (see man page) mapping.

  """" `samoshkin/vim-mergetool`
  " GitAdd https://github.com/samoshkin/vim-mergetool

  """" `tpope/vim-rhubarb`
  " GitHub extension for `tpope/vim-fugitive`. We mostly use it for |:GBrowse|.
  GitAdd https://github.com/tpope/vim-rhubarb

  """" `tommcdo/vim-fugitive-blame-ext`
  GitAdd https://github.com/tommcdo/vim-fugitive-blame-ext

  """" `iberianpig/tig-explorer.vim`
  " GitAdd https://github.com/iberianpig/tig-explorer.vim

  """" `tpope/vim-dispatch`
  GitAdd https://github.com/tpope/vim-dispatch

  " TODO: Put this in a QuickFix section?

  " See also |dispatch-maps|.
  " TODO: Stick to the default mappings.
  nnoremap <Space><Space>m% :Make %:r:S<CR>
  nnoremap <Space><Space>m# :Make #:r:S<CR>
  nnoremap <Space><Space>mm :Make<CR>
  nnoremap <Space><Space>ma :Make all<CR>
  nnoremap <Space><Space>mt :Make test<CR>

  " https://github.com/tpope/vim-dispatch/issues/203
  function! s:focus_start_set() abort
    if get(b:, 'start_local', 1)
      let b:start_local = get(b:, 'start')
    endif
    let b:start = s:start
  endfunction
  function! s:focus_start_unset() abort
    if type(get(b:, 'start_local')) == type('')
      let b:start = b:start_local
    else
      unlet! b:start
    endif
    unlet! b:start_local
  endfunction
  function! s:focus_start(bang, command) abort
    if !empty(a:command)
      let s:start = a:command
      augroup vimrc_dispatch_start
        autocmd!
      augroup END
      silent bufdo call s:focus_start_set()
      augroup vimrc_dispatch_start
        autocmd BufEnter * call s:focus_start_set()
      augroup END
      echo 'Set global default to :Start' s:start
    elseif a:bang
      unlet! s:start
      augroup vimrc_dispatch_start
        autocmd!
      augroup END
      silent bufdo call s:focus_start_unset()
      if type(get(b:, 'start')) == type('')
        echo 'Reverted default to :Start' b:start
      else
        echo 'Reverted default to :Start'
      endif
    else
      if type(get(s:, 'start')) == type('')
        echo 'Global focus is :Start' s:start
      elseif type(get(b:, 'start')) == type('')
        echo 'Buffer default is :Start' b:start
      else
        echo 'Global default is :Start'
      endif
    endif
  endfunction
  command! -bang -nargs=* -range=-1 -complete=customlist,dispatch#command_complete FocusStart
  \ call s:focus_start(<bang>0, <q-args>)

  """" `tpope/vim-eunuch`
  GitAdd https://github.com/tpope/vim-eunuch

  """" `tpope/vim-projectionist`
  GitAdd https://github.com/tpope/vim-projectionist

  """" `tpope/vim-vinegar`
  " GitAdd https://github.com/tpope/vim-vinegar

  """ Debugging

  """" Built-in `termdebug`
  " See |terminal-debug|.

  packadd! termdebug
  " TODO: Add mappings.
  " TODO: Remove |:Winbar|.
  " TODO: Do some |termdebug-customizing|, especially |termdebug_shortcuts|.
  " TODO: Add some signs? See |myvimrc-characters|.

  " let g:termdebug_popup = 0

  nnoremap <Space>dd :Termdebug<CR>

  " `<Space>do` conflicts with our |do| ("diff obtain", |:diffget|)
  " text-object.
  " nnoremap <Space>db :Break<CR>
  " nnoremap <Space>dB :Clear<CR>
  " nnoremap <Space>di :Step<CR>
  " nnoremap <Space>do :Over<CR>
  " nnoremap <Space>df :Finish<CR>
  " nnoremap <Space>dr :Run<CR>
  " nnoremap <Space>da :Arguments<CR>
  " nnoremap <Space>ds :Stop<CR>
  " nnoremap <Space>dc :Continue<CR>
  " nnoremap <Space>de :Evaluate<CR>

  " nnoremap <Space>dg :Gdb<CR>
  " nnoremap <Space>ds :Source<CR>
  " nnoremap <Space>da :Asm<CR>

  " nnoremap <Space>dw :Winbar<CR>

  """" `puremourning/vimspector`
  " GitAdd https://github.com/puremourning/vimspector

  " Debug Adapter Protocol (DAP) client. See
  " - <https://microsoft.github.io/debug-adapter-protocol/>
  " - <https://sourceware.org/gdb/current/onlinedocs/gdb.html/Debugger-Adapter-Protocol.html>

  " <https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation>

  """" `vim-vdebug/vdebug`
  " GitAdd https://github.com/vim-vdebug/vdebug

  " Supports PHP, Python, Ruby, Perl, Tcl and NodeJS.

  " Common DeBugGer Protocol (DBGP) client. See
  " <https://en.wikipedia.org/wiki/DBGp>.

  """ LSP

  " We have not decided on a LSP/linting plugin. Wishlist:
  " - Compatible with at least Vim 8.
  " - Not require dozens of other plugins to configure/make usable.
  " Some things to check out, roughly in the order of looking like a good fit.
  " - <https://github.com/dense-analysis/ale>
  " - <https://github.com/prabirshrestha/vim-lsp>
  " - <https://github.com/yegappan/lsp>

endif " has('eval')

"" Syntax and file type
if has('gui_running')
  " See |'background'|.
  " TODO: Causes the GUI cursor to be misplaced?
  " gui
endif
if has('eval')
  filetype plugin indent on
  syntax enable
endif

"" File type detection
" See |new-filetype|.

if has('eval')

  autocmd! filetypedetect BufRead,BufNewFile *.Xresources setf xdefaults
  autocmd! filetypedetect BufRead,BufNewFile *.gdb*init   setf gdb
  autocmd! filetypedetect BufRead,BufNewFile *.asy        setf cpp
  autocmd! filetypedetect BufRead,BufNewFile *.gvpr       setf c

endif

"" Language specific |help|

if has('eval')
  function! s:help(cmd, arg, filetype) abort
    for i in range(len(a:cmd))
      let a:cmd[i] = join(
      \   map(split(a:cmd[i], '%s', 1), 'shellescape(v:val)'),
      \   shellescape(a:arg)
      \ )
    endfor
    silent help
    silent let old_shellredir = &shellredir
    silent let &shellredir    = '>'
    silent setlocal buftype=nofile bufhidden=delete noswapfile
    silent setlocal noreadonly modifiable
    silent execute 'file' a:arg
    silent keepjumps normal! ggdG
    silent execute 'read' '!' join(a:cmd)
    silent keepjumps normal! ggdd
    silent execute 'setlocal' 'filetype=' . a:filetype
    silent setlocal readonly nomodifiable nomodified
    silent setlocal buftype=help
    " silent let &shellredir = old_shellredir
  endfunction
endif

command! -nargs=1 -complete=shellcmd ManHelp
\ call s:help(['man', '%s'], <q-args>, 'man')

command! -nargs=1 PythonHelp
\ call s:help(['python3', '-m', 'pydoc', '%s'], <q-args>, 'python')

command! -nargs=1 OctaveHelp
\ call s:help(['octave', '--eval', 'help("%s")'], <q-args>, 'octave')

" TODO: Would be cool to define one for `filetype` `gitconfig` that does
" `ManHelp git-config` and then `/^ {7}\zs` with the first two components of
" section words and `<cword>`.

"" File type overrides

" TODO: We want 'path' entries to be in priority order.

" TODO: It looks to me that this should be a global static default instead of a
" local dynamic override.
autocmd vimrc FileType *
\ setlocal keywordprg=:ManHelp |

" TODO: This needs work. We globally override `fillchars` `stl`/`stlnc` and
" `statusline` but don't take into account file type plugins setting their own
" `statusline`.
autocmd vimrc FileType *
\ setlocal statusline=%{Statusline()} |

" See also |scriptease| (which sets both 'keywordprg' and 'path', TODO but
" posssibly only in `vim` files, not `help` files).
autocmd vimrc FileType vim,help
\ execute "nnoremap <buffer> <Space>K :helpgrep <C-R><C-A><CR>" |
\ setlocal keywordprg=:help |
\ setlocal path=.,$VIMRUNTIME,$VIMSRC,$HOME/.vim,$HOME/.vim/pack/*/* |
\ if has('eval') |
\   execute 'setlocal' 'path+=' . fnamemodify($MYVIMRC, ':h') |
\   execute 'setlocal' 'path+=' . fnamemodify($MYVIMRC, ':h') . '/pack/*/*' |
\ endif |
\ setlocal path+=,

autocmd vimrc FileType man
\ setlocal textwidth=78 |

autocmd vimrc FileType sh
\ setlocal shiftwidth=2 |
\ if has('eval') |
\   execute 'setlocal' 'path=' . substitute($PATH, '[:;]', ',', 'g') |
\ endif |

" `path`s that start with `**` are great if you're in a project directory and
" want to find stuff like CMake's
" `_build/_deps/SOME_DEPENDENCY/include/SOME_PATH`, but not so great if you're
" in `$HOME`.
autocmd vimrc FileType c,cpp,lex,yacc,glsl
\ setlocal commentstring=//\ %s |
\ setlocal matchpairs=(:),{:},[:] |
\ setlocal cinkeys-=0# cinoptions=L0,l1,g0,N-s,E-s,t0,c0,C1,(s,u0,W1s,m1,j1,J1,N999,*999 |
\ setlocal path=.,src,src/*,include,*/_deps/*/include |
autocmd vimrc FileType c,cpp,lex,yacc
\ if has('eval') && executable('uname') |
\   execute 'setlocal' 'path+=' . printf('/lib/modules/%s/build/include', trim(system('uname -r'))) |
\ endif |
\ setlocal path+=/usr/include,/usr/local/include |
autocmd vimrc FileType cpp
\ setlocal matchpairs+=<:> |
\ setlocal path+=/usr/include/c++/* |

autocmd vimrc FileType man
\ setlocal nolist nospell |
\ execute 'nnoremap <buffer> <Space>/ /^ \{4,\}\([-‐]\{2\}\S\+\, \+\)\?\zs[-‐]' |

autocmd vimrc FileType dot
\ setlocal commentstring=//\ %s |

autocmd vimrc FileType gitcommit
\ setlocal nolist spell |

autocmd vimrc FileType markdown
\ setlocal spell complete+=kspell |
" \ setlocal formatoptions-=q |

autocmd vimrc FileType python
\ setlocal keywordprg=:PythonHelp |

autocmd vimrc FileType octave
\ setlocal keywordprg=:OctaveHelp |

" TODO: Might be nice to query some external command for the `path`.
autocmd vimrc FileType tex
\ setlocal path=.,,/usr/share/texlive/texmf-dist/tex/latex/*/ |
\ setlocal suffixesadd=.tex,.cls,.sty |

" Don't insert comment leader after |i_<CR>|, |o|, or |O|.
autocmd vimrc FileType *
\ setlocal formatoptions-=r formatoptions-=o |

" As suggested in |ft-syntax-omni|, use syntax completion if no other
" completion has been defined.
autocmd vimrc FileType *
\ if &omnifunc ==# '' |
\   setlocal omnifunc=syntaxcomplete#Complete |
\ endif |

" If 'filetype' is never set, "universal" autocommand configuration will not be
" applied unless we explicitly trigger it.
doautocmd vimrc FileType *

" TODO: Add a match for `\<TODO\>` (and `\<NOTE\>`?) unconditionally for all
" file types.

"" Terminal overrides
" See |terminal-options|.

""" Cursor
" See |termcap-cursor-shape|, `:helpgrep t_SH`, and "Parameterized Strings" in
" `terminfo(5)`.

if has('cursorshape')
  set noshowmode
  " Cursor visible/invisible
  " On Linux the following is the default, which messes with our cursor
  " shape:
  " - `t_ve = "\033[?25h\033[?0c"`
  " - `t_vi = "\033[?25l\033[?1c"`
  " See
  " - <https://vt100.net/docs/vt510-rm/DECTCEM>
  " - `console_codes(4)`
  let &t_ve = "\033[?25h"
  let &t_vi = "\033[?25l"
  " if $TERM =~? '^xterm\(-\|$\)'
    " See
    " - <https://vt100.net/docs/vt510-rm/DECSCUSR>
    " - `:helpgrep t_SH`
    let &t_EI = "\033[1 q"
    let &t_SI = "\033[5 q"
    let &t_SR = "\033[3 q"
    let &t_SH = "\033[%p1%d q"
  " endif
  if $TERM =~? '^linux\(-\|$\)'
    " See
    " - <https://www.kernel.org/doc/Documentation/admin-guide/vga-softcursor.rst>
    " - `console_codes(4)`
    let &t_EI = "\033[?6c"
    let &t_SI = "\033[?2c"
    let &t_SR = "\033[?2c"
    let &t_SH = "\033[?%?%p1%{3}%<%t%{6}%e%{2}%;%dc"
  endif
endif

""" True-color
" See |xterm-true-color|.

if has('termguicolors') && !has('nvim')
  " In earlier Vim versions, these are the defaults only if `$TERM` is `xterm`.
  let &t_8f = "\033[38;2;%lu;%lu;%lum"
  let &t_8b = "\033[48;2;%lu;%lu;%lum"
  " <http://ftp.vim.org/pub/vim/patches/8.2/8.2.3516>: Terminal window does not
  " have transparent background when 'termguicolors' is used. Fix the
  " background color.
  if !has('patch-8.2.3516')
    let s:termguicolors = &termguicolors
    autocmd vimrc OptionSet termguicolors
    \ let s:termguicolors = &termguicolors
    autocmd vimrc TerminalOpen,WinEnter *
    \ let s:newtermguicolors =
    \   s:termguicolors &&
    \   empty(filter(getwininfo(),
    \     'getbufvar(v:val.bufnr, "&buftype") ==# "terminal"'
    \   )) |
    \ if s:newtermguicolors != &termguicolors |
    \   execute 'set' (s:newtermguicolors ? '' : 'no') . 'termguicolors' |
    \ endif |
  endif
endif