Watch
1
0
Fork
You've already forked dotfiles
0

Add configuration for vim

- Add ~/.config/vim/vimrc
  + Configure basic stuff
  + Activate spell checks
  + Activate syntax highlighting and ruler
  + Show whitespace characters
  + Add various snippets for mouse handling,
    toggling line numbers, and search
  + Set `vim`-related directories inside
   ~/.config/vim and ~/.local/state/vim
- Integrate extra settings for Python files
  in ~/.config/vim/after/ftplugin/python.vim
  and ~/.config/vim/after/syntax/python.vim
- Add `vim` artifacts in ~/.config/git/ignore
This commit is contained in:
Alexander Hess 2026-08-03 02:02:37 +02:00
commit 8f7cef086b
Signed by: alexander
GPG key ID: B46EDB4DFC26805D
12 changed files with 611 additions and 1 deletions

View file

@ -12,3 +12,24 @@ __pycache__/
.venv/ .venv/
venv/ venv/
.env .env
# Vim
# Source: https://github.com/github/gitignore/blob/main/Global/Vim.gitignore
#
# Swap
[._]*.s[a-v][a-z]
!*.svg
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

14
.config/readline/inputrc Normal file
View file

@ -0,0 +1,14 @@
# Enable vi-style line editing for all readline apps
set editing-mode vi
# Indicate the current mode via the cursor shape
set show-mode-in-prompt on
set vi-cmd-mode-string "\1\e[2 q\2" # steady block
set vi-ins-mode-string "\1\e[5 q\2" # blinking bar
# Ctrl-L clears the screen in command mode only
# => Bind in insert mode as well
set keymap vi-insert
"\C-l": clear-screen
set keymap vi

View file

@ -3,6 +3,22 @@
# Aliases used in all kinds of shells # Aliases used in all kinds of shells
# Make this file work standalone in non-interactive `bash` (e.g., ":!cmd" in `vim`)
if [ -n "${BASH_VERSION:-}" ]; then
shopt -s expand_aliases
fi
if ! command -v _command_exists >/dev/null 2>&1; then
# This utility is supposed to be defined only in ~/.profile but copied here
# to make `$BASH_ENV` work in `vim`, which sources this file only
_command_exists() {
command -v "$1" 1>/dev/null 2>&1
}
fi
# Manage the bare `git` repository in ~/ holding the dotfiles # Manage the bare `git` repository in ~/ holding the dotfiles
alias dotfiles='git --git-dir=$DOTFILES_DIR --work-tree=$HOME' alias dotfiles='git --git-dir=$DOTFILES_DIR --work-tree=$HOME'
@ -164,3 +180,11 @@ if _command_exists pyenv; then
alias pyvers='pyenv versions --skip-aliases' alias pyvers='pyenv versions --skip-aliases'
alias pywhich='pyenv which' alias pywhich='pyenv which'
fi fi
if _command_exists vim; then
alias v=vim
_command_exists sudoedit && alias sv=sudoedit
fi

View file

@ -26,6 +26,13 @@ export DOTFILES_DIR="$XDG_DATA_HOME/dotfiles" # also set in ~/.local/bin/dotfil
[ -t 0 ] && export GPG_TTY=$(tty) [ -t 0 ] && export GPG_TTY=$(tty)
if _command_exists vim; then
export EDITOR=vim
export SUDO_EDITOR="$EDITOR"
export VISUAL="$EDITOR"
fi
export PAGER=less export PAGER=less
export LESS="--chop-long-lines --ignore-case --LONG-PROMPT --no-init --raw-control-chars --status-column --quit-if-one-screen" export LESS="--chop-long-lines --ignore-case --LONG-PROMPT --no-init --raw-control-chars --status-column --quit-if-one-screen"
@ -47,9 +54,11 @@ unset _bat
# Move common tools' config and cache files into XDG directories # Move common tools' config and cache files into XDG directories
export BAT_CONFIG_PATH="$XDG_CONFIG_HOME/bat/config" export BAT_CONFIG_PATH="$XDG_CONFIG_HOME/bat/config"
export INPUTRC="$XDG_CONFIG_HOME/readline/inputrc"
export LESSHISTFILE="$XDG_STATE_HOME/less/history" export LESSHISTFILE="$XDG_STATE_HOME/less/history"
export PSQLRC="$XDG_CONFIG_HOME/psql/psqlrc" export PSQLRC="$XDG_CONFIG_HOME/psql/psqlrc"
export PYENV_ROOT="$XDG_DATA_HOME/pyenv" export PYENV_ROOT="$XDG_DATA_HOME/pyenv"
export PYTHONPYCACHEPREFIX="$XDG_CACHE_HOME/python" export PYTHONPYCACHEPREFIX="$XDG_CACHE_HOME/python"
export PYTHONSTARTUP="$XDG_CONFIG_HOME/python/startup.py" export PYTHONSTARTUP="$XDG_CONFIG_HOME/python/startup.py"
export PYTHON_HISTORY="$XDG_STATE_HOME/python/history" export PYTHON_HISTORY="$XDG_STATE_HOME/python/history"
export VIMINIT='let $MYVIMRC="'"$XDG_CONFIG_HOME"'/vim/vimrc" | source $MYVIMRC'

View file

@ -0,0 +1,99 @@
" Set the search path to the folder of the current file and its sub-folders
setlocal path=.,**
" Exclude Python's compiled files from searches
setlocal wildignore=*/__pycache__/*,*.pyc
" Format the current file with `black`
function! s:RunBlack()
update
let l:out = system('black ' . shellescape(expand('%')))
if v:shell_error
echohl ErrorMsg | echo split(l:out, '\n')[0] | echohl NONE
else
let s:autoread = &autoread
set autoread
silent! checktime
let &autoread = s:autoread
endif
endfunction
nnoremap <buffer><silent><leader>b :call <SID>RunBlack()<cr>
" Resolve Python imports to files,
" so that `gf` jumps to the module under the cursor
"
" Examples, with the cursor on the marked word:
" - `from lalib.elements import galois` => lalib/elements/galois.py
" - `from lalib.elements import ...` => lalib/elements/__init__.py
" - `import lalib.config` => lalib/config.py
"
if !exists('*s:PyGoToFile')
" Build the dotted module path for the word under the cursor
function! s:PyModuleUnderCursor()
let l:word = expand('<cword>')
let l:from = matchstr(getline('.'), '^\s*from\s\+\zs[A-Za-z0-9_.]\+')
let l:imp = matchstr(getline('.'), '^\s*import\s\+\zs[A-Za-z0-9_.]\+')
let l:base = !empty(l:from) ? l:from : l:imp
if empty(l:base)
return ''
endif
let l:parts = split(l:base, '\.')
let l:idx = index(l:parts, l:word)
if l:idx >= 0 " Cursor within the dotted prefix => Truncate there
return join(l:parts[0:l:idx], '/')
elseif !empty(l:from) " Cursor on an imported name => Append it
return join(l:parts, '/') . '/' . l:word
endif
return ''
endfunction
function! s:PyFindFile(mod)
for l:candidate in [a:mod . '.py', a:mod . '/__init__.py']
let l:found = findfile(l:candidate) " search the `path`
if !empty(l:found)
return l:found
endif
endfor
return ''
endfunction
function! s:PyGoToFile()
let l:mod = s:PyModuleUnderCursor()
let l:file = empty(l:mod) ? '' : s:PyFindFile(l:mod)
if empty(l:file)
echohl ErrorMsg | echo 'No module file for: ' . l:mod | echohl NONE
return
endif
execute 'edit ' . fnameescape(l:file)
endfunction
endif
" <c-o> jumps back
nnoremap <buffer><silent> gf :call <SID>PyGoToFile()<cr>
" Find `def`s and `class`es with [d and [D
setlocal define=^\\s*\\<\\(def\\|class\\)\\>
" Indentation settings come from ~/.config/vim/vimrc, and
" `smartindent` is left off as it interferes with VIM's Python indent script
" Auto-wrap lines after 88 characters, which is PEP8's limit plus 10%,
" a more relaxed boundary which occasionally may be used
setlocal textwidth=88
" Make column 80 red to indicate PEP8's maximum allowed line length
setlocal colorcolumn=80
" Show line numbers by default for .py files
let b:show_numbers=1
call ShowLineNumbers()

View file

@ -0,0 +1,3 @@
" Give every character beyond 80 columns a red background
syntax match pythonOverLength /\%>80v.\+/ containedin=ALL
highlight link pythonOverLength ErrorMsg

View file

440
.config/vim/vimrc Normal file
View file

@ -0,0 +1,440 @@
" Use VIM improved mode
set nocompatible
" Ensure we work with Unicode
set encoding=utf-8
scriptencoding utf-8
" Disable VIM's startup message
set shortmess+=I
" Number of remembered undo steps
set undolevels=1000
" Ensure the XDG variables exist even when `vim` is started
" outside a login shell (e.g., from a desktop launcher)
let $XDG_CONFIG_HOME = get(environ(), 'XDG_CONFIG_HOME', $HOME . '/.config')
let $XDG_STATE_HOME = get(environ(), 'XDG_STATE_HOME', $HOME . '/.local/state')
set runtimepath^=$XDG_CONFIG_HOME/vim
set runtimepath+=$XDG_CONFIG_HOME/vim/after
" Set environment variables for convenient usage
let $RC = expand('$XDG_CONFIG_HOME/vim/vimrc')
let $RTP=split(&runtimepath, ',')[0]
" Make :!cmd use `bash` and load the shell aliases
set shell=/bin/bash
let $BASH_ENV = expand("$XDG_CONFIG_HOME/shell/aliases")
" Detect the file's type and load the corresponding plugin and indent files
filetype plugin indent on
" Enable syntax highlighting
syntax on
" Configure tab behavior
set expandtab " Insert spaces instead of tab characters
set tabstop=4 " Width of an existing tab character
set shiftwidth=4 " Width of one >> or << indent step
set softtabstop=4 " Width inserted/removed by <tab> and <bs>
set shiftround " Round indents to a multiple of shiftwidth
set autoindent " Keep the previous line's indent on a new line
vnoremap <tab> >gv
vnoremap <s-tab> <gv
" Allow backspace to delete characters in insert mode
" beyond the start of the insertion and end of lines
set backspace=start,eol,indent
" Hide buffers instead of closing them, which means we can have
" unwritten changes to a file and open a new one with :e,
" without having to write the changes first
set hidden
" Set to the folder of the current file and its sub-folders
" (this may need to be adapted for large project folders)
set path=.,**
" Store all vim-related working files in the ~/.local/state/vim folder
"
" Note: `viminfo` is a plain string option, so `:set` does not expand
" environment variables in it (unlike the path options below)
" => Build the value via `execute` to expand $XDG_STATE_HOME
execute 'set viminfo+=n' . $XDG_STATE_HOME . '/vim/viminfo'
"
" Use dedicated folders to store temporary backup, swap, and undo files
" (Note: // means that `vim` adapts names automatically to avoid duplicates)
set backupdir=$XDG_STATE_HOME/vim/backup//
set directory=$XDG_STATE_HOME/vim/swap//
set undodir=$XDG_STATE_HOME/vim/undo//
set undofile
"
" To disable any of the temporary files, uncomment one of the following
" set nobackup
" set nowritebackup
" set noswapfile
" set noundofile
" Show the filename in the terminal window's title bar
set title
" Do not wrap lines when showing text
set nowrap
" This avoids a problem of losing data upon insertion in old VIMs
set wrapmargin=0
" Show spelling mistakes in italics
augroup spelling
autocmd!
autocmd FileType markdown,text,gitcommit setlocal spell spelllang=en_us,de_de
augroup END
augroup spell_highlight
autocmd!
autocmd ColorScheme * hi clear SpellBad | hi clear SpellCap
\ | hi clear SpellRare | hi clear SpellLocal
\ | hi SpellBad cterm=italic
augroup END
" Make search highlights readable
" => Explicit `ctermfg` with each `ctermbg`
" because the palette only guarantees contrast
" of each ANSI hue against the background
augroup search_highlight
autocmd!
autocmd ColorScheme * hi Search cterm=bold ctermfg=black ctermbg=yellow
\ | hi IncSearch cterm=bold,reverse ctermfg=yellow ctermbg=black
\ | hi CurSearch cterm=bold ctermfg=black ctermbg=cyan
augroup END
" Make warnings and errors in the status bar stand out
augroup message_highlight
autocmd!
autocmd ColorScheme * hi WarningMsg cterm=bold,reverse ctermfg=yellow ctermbg=black
\ | hi ErrorMsg cterm=bold,reverse ctermfg=red ctermbg=black
augroup END
" Apply all ColorScheme overrides above on startup
silent! doautocmd ColorScheme
" Show whitespace characters
set listchars=tab:»»,extends:,precedes:,nbsp,trail
set list
" Highlight matching brackets
set showmatch
set matchpairs+=<:>
" Always show the status bar at the bottom
set laststatus=2
" Three lines for commands and messages, so that
" longer warnings fit without a "Press ENTER" prompt
set cmdheight=3
" Show current position in status bar
set ruler
set rulerformat=%=%l/%L\ %c\ (%P)
" Show commands in status bar
set showcmd
" If in non-normal mode, show the mode in the status bar
set showmode
" Better copy and paste behavior (needs vim-gtk3 installed)
if has('clipboard')
set clipboard=unnamed,unnamedplus
endif
" Make : and ; synonyms
nnoremap ; :
" Use \ and <space> as the <leader> keys and lower time to enter key sequences
let mapleader='\'
set timeoutlen=750
" Make <space> the <leader> in visual mode as well
nmap <space> \
vmap <space> \
" Q normally goes into Ex mode
nmap Q <Nop>
" Write with `sudo` rights with w!! or wq!!
function! SudoWrite() abort
" `silent` to skip the "Press ENTER" prompt
silent write !sudo tee % >/dev/null
if v:shell_error
" Repaint first, then show the error, so that it is not wiped
redraw!
echohl ErrorMsg | echomsg 'sudo write failed' | echohl None
else
" Keep the undo history intact
set nomodified
" Suppress the "File has changed ..." warning
" (Note: With `autoread` on, `checktime` syncs the timestamp silently;
" without it, a hidden but blocking W11 prompt would appear)
let s:autoread = &autoread
set autoread
silent! checktime %
let &autoread = s:autoread
" Repaint last to cancel any pending "Press ENTER" state
redraw!
echo ''
endif
endfunction
"
command! -bar SudoWrite call SudoWrite()
cnoreabbrev w!! silent SudoWrite
"
" Quit only if the `sudo` write did not fail
command! -bar SudoWriteQuit call SudoWrite() | if !v:shell_error | quit | endif
cnoreabbrev wq!! silent SudoWriteQuit
" Fix mouse issues with Alacritty terminal
" Source: https://wiki.archlinux.org/title/Alacritty#Mouse_not_working_properly_in_Vim
set ttymouse=sgr
" Make the cursor easier to see
let &t_EI = "\e[2 q" " normal mode: steady block
let &t_SI = "\e[1 q" " insert mode: blinking block
let &t_SR = "\e[2 q" " replace mode: steady block
augroup cursor_shape
autocmd!
autocmd VimEnter * silent execute "!echo -ne '\e[2 q'" | redraw!
autocmd VimLeave * silent execute "!echo -ne '\e[1 q'" | redraw!
augroup END
" Enable the mouse for selections, including a toggle for this mode
set mouse=a
let g:mouse_enabled=1
function! ToggleMouse()
if g:mouse_enabled == 1
echo "Mouse OFF"
set mouse=
let g:mouse_enabled=0
else
echo "Mouse ON"
set mouse=a
let g:mouse_enabled=1
endif
endfunction
noremap <silent><leader>m :call ToggleMouse()<cr>
" Enable toggling between:
" - Showing and hiding line numbers (<leader>l), and
" - Absolute and relative numbers (<leader>a) in normal mode
"
" The default is hidden and relative when shown
"
" Note: Both flags are buffer-local ("b:") with global ("g:") fallbacks,
" so that filetype plugins can enable numbers per buffer
" without leaking into other buffers
let g:show_numbers=0
let g:show_absolute_numbers=0
"
function! ShowLineNumbers()
if get(b:, 'show_numbers', get(g:, 'show_numbers', 0))
setlocal number
if get(b:, 'show_absolute_numbers', get(g:, 'show_absolute_numbers', 0))
setlocal norelativenumber
else
setlocal relativenumber
endif
else
setlocal nonumber
setlocal norelativenumber
endif
endfunction
"
function! ToggleLineNumbers()
let b:show_numbers = !get(b:, 'show_numbers', get(g:, 'show_numbers', 0))
call ShowLineNumbers()
endfunction
"
function! ToggleAbsoluteAndRelativeLineNumbers()
let b:show_absolute_numbers = !get(b:, 'show_absolute_numbers', get(g:, 'show_absolute_numbers', 0))
call ShowLineNumbers()
endfunction
"
" Auto-switch between absolute and relative numbering when switching modes
" where insert mode always shows absolute numbers when numbers are shown
augroup numbertoggle
autocmd!
autocmd BufEnter,FocusGained,InsertLeave * call ShowLineNumbers()
autocmd BufLeave,FocusLost,InsertEnter *
\ if get(b:, 'show_numbers', get(g:, 'show_numbers', 0))
\ | setlocal number norelativenumber
\ | endif
augroup END
"
" Key bindings
nnoremap <silent><leader>l :call ToggleLineNumbers()<cr>
nnoremap <silent><leader>a :call ToggleAbsoluteAndRelativeLineNumbers()<cr>
" Show all possible matches above command-line when tab completing
set wildmenu
set wildmode=longest:full,full
" Highlight search results
set hlsearch
" Shortcut to remove current highlighting
nnoremap <silent><leader>h :nohlsearch<cr>:echo<cr>
" Move cursor to result while typing immediately
set incsearch
" Ignore case when searching
set ignorecase
" Upper case search term => case sensitive search
set smartcase
" Highlight the next match in red for 0.25 seconds
function! HighlightNext()
let [bufnum, lnum, col, off] = getpos('.')
let matchlen = strlen(matchstr(strpart(getline('.'),col-1),@/))
let target_pat = '\c\%#\%('.@/.'\)'
let ring = matchadd('ErrorMsg', target_pat, 101)
redraw
exec 'sleep ' . float2nr(250) . 'm'
call matchdelete(ring)
redraw
endfunction
nnoremap <silent>n n:call HighlightNext()<cr>
nnoremap <silent>N N:call HighlightNext()<cr>
" Save and quit, in three symmetric flavors:
" - Quit => <c-q> / <leader>q (~ `:quit`)
" - Save => <c-s> / <leader>w (~ `:update`)
" - Save & Quit => <c-z> / <leader>z (~ `:exit`)
"
" The <c-*> chords work in normal, visual, and insert mode
" whereas the <leader> variants are chord-free alternatives in normal mode
"
" VIM's built-ins remain untouched and complete the picture:
" - ZZ => `:exit` => Save if changed, then quit (~ "Keep changes, and close")
" - ZQ => `:quit!` => Discard changes, then quit (~ "Never mind, just close")
"
" Notes:
" - <c-s> and <c-q> require flow control to be off
" (see `stty -ixon` in ~/.bashrc and ~/.zshrc),
" or they freeze the terminal instead
" - <c-z> no longer suspends VIM into a background job
" - `:exit` writes only if there are changes, then quits
"
nnoremap <c-q> :quit<cr>
vnoremap <c-q> <c-c>:quit<cr>
inoremap <c-q> <esc>:quit<cr>
nnoremap <leader>q :quit<cr>
"
nnoremap <c-s> :update<cr>
vnoremap <c-s> <c-c>:update<cr>
inoremap <c-s> <c-o>:update<cr>
nnoremap <leader>w :update<cr>
"
nnoremap <c-z> :exit<cr>
vnoremap <c-z> <c-c>:exit<cr>
inoremap <c-z> <esc>:exit<cr>
nnoremap <leader>z :exit<cr>
"
" Show a dialog to save changes instead of an error message
set confirm
" Easier switching between tabs
noremap <leader>, <esc>:tabprevious<cr>
noremap <leader>. <esc>:tabnext<cr>
" Arrow keys either (un)indent lines or move them up or down
" (same for blocks of lines in visual mode)
nnoremap <left> <<
nnoremap <right> >>
nnoremap <silent><up> :m-2<cr>
nnoremap <silent><down> :m+<cr>
vnoremap <left> <gv
vnoremap <right> >gv
vnoremap <up> :m'<-2<cr>gv=gv
vnoremap <down> :m'>+1<cr>gv=gv
" Make Y yank the rest of a line, just like C or D work
nnoremap Y y$
" Make X replace the character under the cursor with a space
nnoremap X r<space>
" Alphabetically sort a selection of lines
vnoremap <leader>s :sort<cr>
" Switch two words, just like xp switches two characters
nnoremap <leader>xp dwElp
" Warn if a file was changed on disk by some other process
" => The buffer is NOT reloaded automatically; instead:
" - `:DiffDisk` shows what differs (close with `:q` in the split)
" - `:e` takes the disk version, `:w` keeps the buffer
augroup checktime
autocmd!
autocmd FocusGained,BufEnter * silent! checktime
augroup END
"
" Show a diff between the buffer and its file on disk
" => Close the split with :q; diff mode ends automatically
command! DiffDisk vert new | set buftype=nofile | read ++edit # | 0d_
\ | diffthis | wincmd p | diffthis | wincmd p
\ | autocmd BufWinLeave <buffer> diffoff!
" Jump to the last known cursor position when reopening a file,
" skipping commit messages and invalid positions
augroup restore_position
autocmd!
autocmd BufReadPost * if line("'\"") >= 1 && line("'\"") <= line("$") && &filetype !~# 'commit'
\ | execute "normal! g`\""
\ | endif
augroup END
" Auto-reload ~/.config/vim/vimrc on write for a fast edit loop
" => NOTE: This adds to state rather than replacing it,
" so, for example, removed mappings and options persist
" until VIM is restarted
augroup vimrc
autocmd!
autocmd BufWritePost $MYVIMRC source % | redraw
augroup END
" Key binding to reload ~/.config/vim/vimrc manually
nnoremap <silent><leader>rc :so $MYVIMRC<cr>

View file

View file

View file

View file

@ -41,7 +41,7 @@ The repository is cloned as a bare repository into `~/.local/share/dotfiles`
and its files are checked out into `~/` directly. and its files are checked out into `~/` directly.
Pre-existing files are backed up to `~/.local/state/dotfiles-backups` beforehand. Pre-existing files are backed up to `~/.local/state/dotfiles-backups` beforehand.
Afterwards, the files can be managed with the Afterwards, the files can be managed with the
[`dotfiles`](.config/shell/aliases#L7) alias, [`dotfiles`](.config/shell/aliases#L23) alias,
for example, `dotfiles status` or `dotfiles pull`. for example, `dotfiles status` or `dotfiles pull`.
The above commands install the *main* variant. The above commands install the *main* variant.