diff options
Diffstat (limited to 'mac/vimrc')
| -rw-r--r-- | mac/vimrc | 155 |
1 files changed, 108 insertions, 47 deletions
| @@ -1,4 +1,14 @@ | |||
| 1 | " ~/.vimrc (macOS + Linux) — self-contained; source of truth: dev_env/mac/vimrc | 1 | " /etc/vim/vimrc — self-contained; source of truth: /v1/dev_env/core/vim.sh |
| 2 | " gt: go to next tab (right, wraps) | ||
| 3 | " gT: go to previous tab (left, wraps) | ||
| 4 | " 2gt: go to tab number 2 (counted from 1 at the left) | ||
| 5 | " :q: close the current window, and the tab too if it was the last window in it | ||
| 6 | " ZZ: save if changed, then close the window (and the tab if it was the last window in it) | ||
| 7 | " ZQ: close the window WITHOUT saving (like :q!), and the tab too if it was the last window in it | ||
| 8 | " g<Tab>: jump to the last-accessed tab (press g, release, then Tab); toggles between two tabs | ||
| 9 | " Ctrl-w w: cycle windows in this tab, e.g. into and out of the K popup (Ctrl-w p toggles back) | ||
| 10 | " 7G12|: go to line 7, column 12 (normal-mode keys, no colon; 3w moves 3 words) | ||
| 11 | " Reset an edited cargo dependency: delete ~/.cargo/registry/src/<index>/<crate>-<ver>/ then run cargo fetch | ||
| 2 | " | 12 | " |
| 3 | " Setup (LSP servers below are optional — each is guarded by executable()): | 13 | " Setup (LSP servers below are optional — each is guarded by executable()): |
| 4 | " plugins: curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ | 14 | " plugins: curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ |
| @@ -6,16 +16,39 @@ | |||
| 6 | " then launch vim and run :PlugInstall | 16 | " then launch vim and run :PlugInstall |
| 7 | " rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y | 17 | " rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y |
| 8 | " rustup component add rust-analyzer | 18 | " rustup component add rust-analyzer |
| 9 | " ts/js: npm i -g typescript@6 typescript-language-server (Linux: sudo npm …) | ||
| 10 | " (TypeScript 7 ships no tsserver, which typescript-language-server needs) | ||
| 11 | " python: curl -LsSf https://astral.sh/uv/install.sh | sh | 19 | " python: curl -LsSf https://astral.sh/uv/install.sh | sh |
| 12 | " uv tool install ruff && uv tool install ty | 20 | " uv tool install ruff && uv tool install pyright |
| 13 | " search: ripgrep (for :Rg); Fedora: dnf install vim-enhanced ripgrep | ||
| 14 | " | 21 | " |
| 15 | " Stop vim from auto-loading defaults.vim, which loads AFTER this file and | 22 | " Stop vim from auto-loading defaults.vim, which loads AFTER this file and |
| 16 | " would otherwise silently override settings here. Everything is in this file. | 23 | " would otherwise silently override settings here. Everything is in this file. |
| 17 | let g:skip_defaults_vim = 1 | 24 | let g:skip_defaults_vim = 1 |
| 18 | 25 | ||
| 26 | " ===== Shortcuts: one line each, <leader> = \ (comment follows the |) ===== | ||
| 27 | inoremap <C-a> <Home>| " insert Ctrl-a: jump to line start | ||
| 28 | inoremap <C-e> <End>| " insert Ctrl-e: jump to line end | ||
| 29 | inoremap <silent><expr> <C-l> pumvisible() ? "\<C-e>" : asyncomplete#force_refresh()| " insert Ctrl-l: toggle completion menu | ||
| 30 | inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"| " in menu Tab: next item | ||
| 31 | inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"| " in menu Shift-Tab: previous item | ||
| 32 | inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<CR>"| " in menu Enter: accept item | ||
| 33 | nnoremap <C-a> 0| " Ctrl-a: jump to line start | ||
| 34 | nnoremap <C-e> $| " Ctrl-e: jump to line end | ||
| 35 | nnoremap <silent> <C-l> :<C-U>nohlsearch<CR><C-l>| " Ctrl-l: clear search highlight | ||
| 36 | nnoremap <silent> <C-p> :<C-U>Files<CR>| " Ctrl-p: fuzzy-find a file | ||
| 37 | nnoremap <silent> <leader>rg :<C-U>Rg<CR>| " \rg: search text in project | ||
| 38 | nnoremap <silent> <leader>b :<C-U>Buffers<CR>| " \b: list open buffers | ||
| 39 | nnoremap <silent> <leader>n :<C-U>NERDTreeToggle<CR>| " \n: toggle file tree | ||
| 40 | nnoremap <silent> gd :<C-U>call <SID>GotoDef()<CR>| " gd: go to definition (Ctrl-o back) | ||
| 41 | nnoremap <silent> gi :<C-U>LspImplementation<CR>| " gi: go to implementation | ||
| 42 | nnoremap <silent> gy :<C-U>tab LspTypeDefinition<CR>| " gy: go to type definition in a new tab (e -> struct DirEntry) | ||
| 43 | nnoremap <silent> <leader>k :<C-U>call ToggleHover()<CR>| " \k: toggle hover docs | ||
| 44 | nnoremap <silent> K :<C-U>call ToggleHover()<CR>| " K: toggle hover docs (same as \k) | ||
| 45 | nnoremap <silent> <leader>D :<C-U>LspDocumentation<CR>| " \D: show documentation | ||
| 46 | nnoremap <silent> <leader>e :<C-U>call ToggleDiagnostic()<CR>| " \e: show error on this line | ||
| 47 | nnoremap <silent> <leader>l :<C-U>lopen<CR>| " \l: list all errors | ||
| 48 | nnoremap <silent> <leader>a :<C-U>LspCodeAction<CR>| " \a: code actions popup | ||
| 49 | nnoremap <silent> <leader>rn :<C-U>LspRename<CR>| " \rn: rename symbol | ||
| 50 | nnoremap <silent> <leader>f :<C-U>LspDocumentFormat<CR>| " \f: format file | ||
| 51 | nnoremap <silent> <leader>em :<C-U>call ExpandMacro()<CR>| " \em: expand macro under cursor | ||
| 19 | 52 | ||
| 20 | 53 | ||
| 21 | augroup netcat_clipboard | 54 | augroup netcat_clipboard |
| @@ -47,6 +80,7 @@ set statusline=%F | |||
| 47 | set wildmenu | 80 | set wildmenu |
| 48 | set splitright | 81 | set splitright |
| 49 | set splitbelow | 82 | set splitbelow |
| 83 | set hidden " gd/LSP jumps reuse the window even with unsaved changes | ||
| 50 | set noswapfile | 84 | set noswapfile |
| 51 | set regexpengine=2 | 85 | set regexpengine=2 |
| 52 | set completeopt=menu,menuone,noinsert,noselect | 86 | set completeopt=menu,menuone,noinsert,noselect |
| @@ -73,11 +107,24 @@ colorscheme zellner | |||
| 73 | 107 | ||
| 74 | autocmd InsertLeave * silent! update | 108 | autocmd InsertLeave * silent! update |
| 75 | 109 | ||
| 76 | inoremap <C-a> <Home> | 110 | " Files rustup and cargo manage (toolchains, downloaded crates) open locked; :setl ma noro to override |
| 77 | inoremap <C-e> <End> | 111 | function! s:LockIfManaged() abort |
| 78 | nnoremap <C-a> 0 | 112 | if !exists('s:managed') |
| 79 | nnoremap <C-e> $ | 113 | let l:cargo = empty($CARGO_HOME) ? expand('~/.cargo') : $CARGO_HOME |
| 80 | nnoremap <silent> <C-l> :<C-U>nohlsearch<CR><C-l> | 114 | let s:managed = [trim(system('rustup show home')), l:cargo . '/registry', l:cargo . '/git'] |
| 115 | endif | ||
| 116 | for l:dir in s:managed | ||
| 117 | if stridx(expand('%:p'), l:dir . '/') == 0 | ||
| 118 | setlocal readonly nomodifiable | ||
| 119 | return | ||
| 120 | endif | ||
| 121 | endfor | ||
| 122 | endfunction | ||
| 123 | augroup managed_readonly | ||
| 124 | autocmd! | ||
| 125 | autocmd BufReadPost * call s:LockIfManaged() | ||
| 126 | augroup END | ||
| 127 | |||
| 81 | 128 | ||
| 82 | call plug#begin('~/.vim/plugged') | 129 | call plug#begin('~/.vim/plugged') |
| 83 | Plug 'prabirshrestha/async.vim' | 130 | Plug 'prabirshrestha/async.vim' |
| @@ -97,10 +144,6 @@ call plug#end() | |||
| 97 | 144 | ||
| 98 | let g:gitgutter_sign_column_always = 0 | 145 | let g:gitgutter_sign_column_always = 0 |
| 99 | 146 | ||
| 100 | nnoremap <silent> <C-p> :<C-U>Files<CR> | ||
| 101 | nnoremap <silent> <leader>rg :<C-U>Rg<CR> | ||
| 102 | nnoremap <silent> <leader>b :<C-U>Buffers<CR> | ||
| 103 | nnoremap <silent> <leader>n :<C-U>NERDTreeToggle<CR> | ||
| 104 | 147 | ||
| 105 | " rust-analyzer via the rustup proxy shim (portable across toolchains/machines) | 148 | " rust-analyzer via the rustup proxy shim (portable across toolchains/machines) |
| 106 | if executable(expand('~/.cargo/bin/rust-analyzer')) | 149 | if executable(expand('~/.cargo/bin/rust-analyzer')) |
| @@ -118,24 +161,6 @@ if executable(expand('~/.cargo/bin/rust-analyzer')) | |||
| 118 | \ }, | 161 | \ }, |
| 119 | \ }) | 162 | \ }) |
| 120 | endif | 163 | endif |
| 121 | function! s:ts_root(server_info) abort | ||
| 122 | let l:buf = lsp#utils#get_buffer_path() | ||
| 123 | for l:f in ['tsconfig.json', 'package.json'] | ||
| 124 | let l:dir = lsp#utils#find_nearest_parent_file_directory(l:buf, l:f) | ||
| 125 | if !empty(l:dir) | ||
| 126 | return lsp#utils#path_to_uri(l:dir) | ||
| 127 | endif | ||
| 128 | endfor | ||
| 129 | return lsp#utils#path_to_uri(getcwd()) | ||
| 130 | endfunction | ||
| 131 | if executable('typescript-language-server') | ||
| 132 | au User lsp_setup call lsp#register_server({ | ||
| 133 | \ 'name': 'typescript-language-server', | ||
| 134 | \ 'cmd': {server_info -> ['typescript-language-server', '--stdio']}, | ||
| 135 | \ 'allowlist': ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'], | ||
| 136 | \ 'root_uri': function('s:ts_root'), | ||
| 137 | \ }) | ||
| 138 | endif | ||
| 139 | if executable(expand('~/.local/bin/ruff')) | 164 | if executable(expand('~/.local/bin/ruff')) |
| 140 | au User lsp_setup call lsp#register_server({ | 165 | au User lsp_setup call lsp#register_server({ |
| 141 | \ 'name': 'ruff', | 166 | \ 'name': 'ruff', |
| @@ -155,6 +180,23 @@ if executable(expand('~/.local/bin/ty')) && executable(s:uv_bin) | |||
| 155 | \ 'allowlist': ['python'], | 180 | \ 'allowlist': ['python'], |
| 156 | \ }) | 181 | \ }) |
| 157 | endif | 182 | endif |
| 183 | " tofu-ls (OpenTofu/Terraform .tf) for go-to-definition | ||
| 184 | if executable(expand('~/.local/bin/tofu-ls')) | ||
| 185 | au User lsp_setup call lsp#register_server({ | ||
| 186 | \ 'name': 'tofu-ls', | ||
| 187 | \ 'cmd': {server_info -> [expand('~/.local/bin/tofu-ls'), 'serve']}, | ||
| 188 | \ 'allowlist': ['terraform', 'terraform-vars'], | ||
| 189 | \ }) | ||
| 190 | endif | ||
| 191 | " gopls (Go) for go-to-definition | ||
| 192 | if executable(expand('~/go/bin/gopls')) | ||
| 193 | au User lsp_setup call lsp#register_server({ | ||
| 194 | \ 'name': 'gopls', | ||
| 195 | \ 'cmd': {server_info -> [expand('~/go/bin/gopls')]}, | ||
| 196 | \ 'allowlist': ['go'], | ||
| 197 | \ 'root_uri': {server_info -> lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'go.mod'))}, | ||
| 198 | \ }) | ||
| 199 | endif | ||
| 158 | 200 | ||
| 159 | " Force per-keystroke LSP sync for Python (vim-lsp only syncs on | 201 | " Force per-keystroke LSP sync for Python (vim-lsp only syncs on |
| 160 | " InsertLeave/TextChanged by default, not while actively typing). | 202 | " InsertLeave/TextChanged by default, not while actively typing). |
| @@ -167,6 +209,7 @@ let g:lsp_diagnostics_signs_enabled = 0 | |||
| 167 | let g:lsp_document_highlight_enabled = 0 | 209 | let g:lsp_document_highlight_enabled = 0 |
| 168 | let g:lsp_preview_float = 0 | 210 | let g:lsp_preview_float = 0 |
| 169 | let g:lsp_hover_ui = 'preview' | 211 | let g:lsp_hover_ui = 'preview' |
| 212 | let g:lsp_code_action_ui = 'float' " \a = one popup menu, not 3 windows | ||
| 170 | let g:lsp_signature_help_enabled = 0 | 213 | let g:lsp_signature_help_enabled = 0 |
| 171 | 214 | ||
| 172 | function! s:set_preview_height() abort | 215 | function! s:set_preview_height() abort |
| @@ -208,15 +251,6 @@ function! ToggleDiagnostic() abort | |||
| 208 | call win_gotoid(l:cur) | 251 | call win_gotoid(l:cur) |
| 209 | endfunction | 252 | endfunction |
| 210 | 253 | ||
| 211 | nnoremap <silent> <leader>k :<C-U>call ToggleHover()<CR> | ||
| 212 | nnoremap <silent> gd :<C-U>LspDefinition<CR> | ||
| 213 | nnoremap <silent> gi :<C-U>LspImplementation<CR> | ||
| 214 | nnoremap <silent> <leader>rn :<C-U>LspRename<CR> | ||
| 215 | nnoremap <silent> <leader>a :<C-U>LspCodeAction<CR> | ||
| 216 | nnoremap <silent> <leader>f :<C-U>LspDocumentFormat<CR> | ||
| 217 | nnoremap <silent> <leader>l :<C-U>lopen<CR> | ||
| 218 | nnoremap <silent> <leader>D :<C-U>LspDocumentation<CR> | ||
| 219 | nnoremap <silent> <leader>e :<C-U>call ToggleDiagnostic()<CR> | ||
| 220 | 254 | ||
| 221 | " rust-analyzer macro expansion (LSP extension, no built-in command). | 255 | " rust-analyzer macro expansion (LSP extension, no built-in command). |
| 222 | function! ExpandMacro() abort | 256 | function! ExpandMacro() abort |
| @@ -247,11 +281,38 @@ function! s:expand_macro_handler(data) abort | |||
| 247 | call append(line('$'), split(l:result['expansion'], "\n")) | 281 | call append(line('$'), split(l:result['expansion'], "\n")) |
| 248 | endfunction | 282 | endfunction |
| 249 | 283 | ||
| 250 | nnoremap <silent> <leader>em :<C-U>call ExpandMacro()<CR> | ||
| 251 | 284 | ||
| 252 | inoremap <silent> <C-Space> <C-x><C-o> | 285 | " gd: say when rust-analyzer is still starting/indexing instead of a false "No definition found" |
| 253 | inoremap <silent> <C-@> <C-x><C-o> | 286 | let g:lsp_work_done_progress_enabled = 1 |
| 254 | inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>" | 287 | function! s:GotoDef() abort |
| 255 | inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>" | 288 | if &filetype ==# 'rust' |
| 256 | inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<CR>" | 289 | let l:st = lsp#get_server_status('rust-analyzer') |
| 290 | if l:st !=# 'running' | ||
| 291 | echo 'rust-analyzer ' . l:st . ', try again in a few seconds' | ||
| 292 | return | ||
| 293 | endif | ||
| 294 | let l:busy = filter(copy(lsp#get_progress()), | ||
| 295 | \ {_, p -> p['server'] ==# 'rust-analyzer' && get(p, 'title', '') !~? 'check'}) | ||
| 296 | if !empty(l:busy) | ||
| 297 | echo 'rust-analyzer still indexing: ' . get(l:busy[0], 'title', '') . ' ' . get(l:busy[0], 'message', '') | ||
| 298 | return | ||
| 299 | endif | ||
| 300 | endif | ||
| 301 | tab LspDefinition | ||
| 302 | endfunction | ||
| 257 | 303 | ||
| 304 | " Numbered tab labels (1:main.rs 2:fs.rs): the number is what Ngt jumps to. | ||
| 305 | function! TabLine() abort | ||
| 306 | let l:s = '' | ||
| 307 | for l:i in range(1, tabpagenr('$')) | ||
| 308 | let l:buf = tabpagebuflist(l:i)[tabpagewinnr(l:i) - 1] | ||
| 309 | let l:name = fnamemodify(bufname(l:buf), ':t') | ||
| 310 | if l:name ==# '' | ||
| 311 | let l:name = '[No Name]' | ||
| 312 | endif | ||
| 313 | let l:hl = l:i == tabpagenr() ? '%#TabLineSel#' : '%#TabLine#' | ||
| 314 | let l:s .= l:hl . ' ' . l:i . ':' . l:name . ' ' | ||
| 315 | endfor | ||
| 316 | return l:s . '%#TabLineFill#' | ||
| 317 | endfunction | ||
| 318 | set tabline=%!TabLine() | ||
