440 lines
13 KiB
VimL
440 lines
13 KiB
VimL
|
|
" 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>
|