From 708e0c38fdcfd84a0028c4b261d77c4b2f5083df Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 1 Nov 2026 09:55:22 +0800 Subject: core: one vimrc and tmux.conf for Mac and Linux --- core/tmux.conf | 40 +++++-- core/vim.sh | 67 +----------- core/vimrc | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mac/tmux.conf | 38 ------- mac/vimrc | 318 ------------------------------------------------------- 5 files changed, 360 insertions(+), 428 deletions(-) create mode 100644 core/vimrc delete mode 100644 mac/tmux.conf delete mode 100644 mac/vimrc diff --git a/core/tmux.conf b/core/tmux.conf index 465641c..97084f1 100644 --- a/core/tmux.conf +++ b/core/tmux.conf @@ -1,15 +1,41 @@ -# True-color support regardless of what TERM the outer terminal/distro sets -set -g default-terminal "tmux-256color" -set -ga terminal-overrides ",*256col*:Tc" - +# Ctrl-b f: prompt for text, list windows whose name, pane title or pane contents match; Enter jumps +# Ctrl-b w: tree of every window in every session; press f inside it to filter by name, Enter jumps +# Ctrl-b s: tree of sessions (same tree, session view) +# Ctrl-b p: go to the previous window (lower index, wraps); Ctrl-b n goes to the next one +# # Vi mode for copy setw -g mode-keys vi # Mouse selection set -g mouse on +# Persistent pane index labels (top-left of each pane's border) +set -g pane-border-status top +set -g pane-border-format "#P" + +# Explicitly swap current pane with a pane index you type in +bind-key P command-prompt -p "swap with pane #:" "swap-pane -d -t '%%'" +# Manual: :swap-pane -s -t + +# Vim-style pane resizing (prefix + H/J/K/L), magnitude 1 +bind -r H resize-pane -L 1 +bind -r J resize-pane -D 1 +bind -r K resize-pane -U 1 +bind -r L resize-pane -R 1 + # v to start selection, y to yank (like vim) bind-key -T copy-mode-vi v send-keys -X begin-selection -bind-key -T copy-mode-vi y send-keys -X copy-pipe-no-clear "openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1" -bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-no-clear "openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1" -bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-no-clear "openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1" +# macOS: pbcopy. Linux: wl-copy/xclip when usable, else just drain (tmux's own +# buffer + set-clipboard/OSC 52 still apply). Netcat send is the same on both. +if-shell 'uname | grep -q Darwin' { + bind-key -T copy-mode-vi y send-keys -X copy-pipe-no-clear "bash -c 'tee >(pbcopy) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" + bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-no-clear "bash -c 'tee >(pbcopy) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" + bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-no-clear "bash -c 'tee >(pbcopy) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" +} { + # Linux only: true-color regardless of what TERM the outer terminal/distro sets + set -g default-terminal "tmux-256color" + set -ga terminal-overrides ",*256col*:Tc" + bind-key -T copy-mode-vi y send-keys -X copy-pipe-no-clear "bash -c 'tee >(wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || cat >/dev/null) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" + bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-no-clear "bash -c 'tee >(wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || cat >/dev/null) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" + bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-no-clear "bash -c 'tee >(wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || cat >/dev/null) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" +} diff --git a/core/vim.sh b/core/vim.sh index 8041e0e..241673e 100755 --- a/core/vim.sh +++ b/core/vim.sh @@ -2,71 +2,8 @@ cd "$(dirname "${BASH_SOURCE[0]}")" || exit +# Same vimrc as the Mac (core/vimrc); OS differences are guarded inside the file. VIMRC_PATH="/etc/vimrc" [ -d /etc/vim ] && VIMRC_PATH="/etc/vim/vimrc" -CLIP_HOST="p.noml.ch" -CLIP_PORT="5023" -CLIP_CMD="openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 ${CLIP_HOST} ${CLIP_PORT}" -grep -q 'netcat_clipboard' "$VIMRC_PATH" && exit 0 - -cat << EOF >> "$VIMRC_PATH" -augroup netcat_clipboard - au! - au TextYankPost * call system("echo " . shellescape(@") . " | ${CLIP_CMD} >/dev/null 2>&1 &") -augroup END - -" Enable line wrapping -set wrap - -" Optional: Make wrapped lines easier to read by indenting them -set breakindent -set breakindentopt=shift:1 - -syntax on -set mouse=a -set expandtab -set shiftwidth=4 -set softtabstop=4 -set tabstop=4 -set noautoindent -set nosmartindent -filetype plugin indent on - -set laststatus=2 " permanent status bar -set statusline=%F " shows full file path - -if (has("termguicolors")) - " tmux's terminfo entries (tmux-256color/screen-256color) don't define - " setrgbf/setrgbb, so vim has no termcap to build true-color escapes and - " silently emits none. Set them explicitly (standard vim+tmux fix). - if !empty($TMUX) - let &t_8f = "\[38;2;%lu;%lu;%lum" - let &t_8b = "\[48;2;%lu;%lu;%lum" - endif - set termguicolors -endif - -"colorscheme murphy -colorscheme zellner - -" Ctrl+A: Move to the beginning of the line in INSERT mode -inoremap - -" Ctrl+E: Move to the end of the line in INSERT mode -inoremap - -" Ctrl+A: Move to the beginning of the line in NORMAL mode -nnoremap 0 - -" Ctrl+E: Move to the end of the line in NORMAL mode -nnoremap $ - -set number -setlocal regexpengine=2 -set foldcolumn=12 - -set noswapfile -autocmd InsertLeave * silent! update - -EOF +cp vimrc "$VIMRC_PATH" diff --git a/core/vimrc b/core/vimrc new file mode 100644 index 0000000..b4441a5 --- /dev/null +++ b/core/vimrc @@ -0,0 +1,325 @@ +" One vimrc for Mac and Linux. Source of truth: /Users/xs/dev_env/core/vimrc +" Mac: cp core/vimrc ~/.vimrc Linux: sudo bash core/vim.sh (copies it to /etc/vimrc or /etc/vim/vimrc) +" gt: go to next tab (right, wraps) +" gT: go to previous tab (left, wraps) +" 2gt: go to tab number 2 (counted from 1 at the left) +" :q: close the current window, and the tab too if it was the last window in it +" ZZ: save if changed, then close the window (and the tab if it was the last window in it) +" ZQ: close the window WITHOUT saving (like :q!), and the tab too if it was the last window in it +" g: jump to the last-accessed tab (press g, release, then Tab); toggles between two tabs +" Ctrl-w w: cycle windows in this tab, e.g. into and out of the K popup (Ctrl-w p toggles back) +" 7G12|: go to line 7, column 12 (normal-mode keys, no colon; 3w moves 3 words) +" Reset an edited cargo dependency: delete ~/.cargo/registry/src//-/ then run cargo fetch +" +" Setup (LSP servers below are optional — each is guarded by executable()): +" plugins: curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ +" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim +" then launch vim and run :PlugInstall +" rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +" rustup component add rust-analyzer +" python: curl -LsSf https://astral.sh/uv/install.sh | sh +" uv tool install ruff && uv tool install pyright +" +" Stop vim from auto-loading defaults.vim, which loads AFTER this file and +" would otherwise silently override settings here. Everything is in this file. +let g:skip_defaults_vim = 1 + +" ===== Shortcuts: one line each, = \ (comment follows the |) ===== +inoremap | " insert Ctrl-a: jump to line start +inoremap | " insert Ctrl-e: jump to line end +inoremap pumvisible() ? "\" : asyncomplete#force_refresh()| " insert Ctrl-l: toggle completion menu +inoremap pumvisible() ? "\" : "\"| " in menu Tab: next item +inoremap pumvisible() ? "\" : "\"| " in menu Shift-Tab: previous item +inoremap pumvisible() ? "\" : "\"| " in menu Enter: accept item +nnoremap 0| " Ctrl-a: jump to line start +nnoremap $| " Ctrl-e: jump to line end +nnoremap :nohlsearch| " Ctrl-l: clear search highlight +nnoremap :Files| " Ctrl-p: fuzzy-find a file +nnoremap rg :Rg| " \rg: search text in project +nnoremap b :Buffers| " \b: list open buffers +nnoremap n :NERDTreeToggle| " \n: toggle file tree +nnoremap gd :call GotoDef()| " gd: go to definition (Ctrl-o back) +nnoremap gi :LspImplementation| " gi: go to implementation +nnoremap gy :tab LspTypeDefinition| " gy: go to type definition in a new tab (e -> struct DirEntry) +nnoremap k :call ToggleHover()| " \k: toggle hover docs +nnoremap K :call ToggleHover()| " K: toggle hover docs (same as \k) +nnoremap D :LspDocumentation| " \D: show documentation +nnoremap e :call ToggleDiagnostic()| " \e: show error on this line +nnoremap l :lopen| " \l: list all errors +nnoremap a :LspCodeAction| " \a: code actions popup +nnoremap rn :LspRename| " \rn: rename symbol +nnoremap f :LspDocumentFormat| " \f: format file +nnoremap em :call ExpandMacro()| " \em: expand macro under cursor + + +augroup netcat_clipboard +au! +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 &") +augroup END + +filetype plugin indent on + +set number +set wrap +set breakindent +set breakindentopt=shift:1 +set expandtab +set shiftwidth=4 +set softtabstop=4 +set tabstop=4 +set noautoindent +set nosmartindent +set hlsearch +set incsearch +set ignorecase +set smartcase +set scrolloff=8 +set signcolumn=no +set foldcolumn=12 +set laststatus=2 +set statusline=%F +set wildmenu +set splitright +set splitbelow +set hidden " gd/LSP jumps reuse the window even with unsaved changes +set noswapfile +set regexpengine=2 +set completeopt=menu,menuone,noinsert,noselect + +set mouse=a +if has('mouse_sgr') + set ttymouse=sgr +endif + +set undofile +set undodir=~/.vim/undo +if !isdirectory($HOME . '/.vim/undo') + call mkdir($HOME . '/.vim/undo', 'p') +endif + +if has('termguicolors') + if !empty($TMUX) + let &t_8f = "\[38;2;%lu;%lu;%lum" + let &t_8b = "\[48;2;%lu;%lu;%lum" + endif + set termguicolors +endif +colorscheme zellner + +autocmd InsertLeave * silent! update + +" Files rustup and cargo manage (toolchains, downloaded crates) open locked; :setl ma noro to override +function! s:LockIfManaged() abort + if !exists('s:managed') + let l:cargo = empty($CARGO_HOME) ? expand('~/.cargo') : $CARGO_HOME + let s:managed = [l:cargo . '/registry', l:cargo . '/git'] + if executable('rustup') + call add(s:managed, trim(system('rustup show home'))) + endif + endif + for l:dir in s:managed + if stridx(expand('%:p'), l:dir . '/') == 0 + setlocal readonly nomodifiable + return + endif + endfor +endfunction +augroup managed_readonly + autocmd! + autocmd BufReadPost * call s:LockIfManaged() +augroup END + + +" vim-plug is optional: skip the plugin block until plug.vim is installed (see Setup above) +if filereadable(expand('~/.vim/autoload/plug.vim')) +call plug#begin('~/.vim/plugged') +Plug 'prabirshrestha/async.vim' +Plug 'prabirshrestha/vim-lsp' +Plug 'prabirshrestha/asyncomplete.vim' +Plug 'prabirshrestha/asyncomplete-lsp.vim' +Plug 'rust-lang/rust.vim' +Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } +Plug 'junegunn/fzf.vim' +Plug 'tpope/vim-commentary' +Plug 'tpope/vim-surround' +Plug 'tpope/vim-fugitive' +Plug 'airblade/vim-gitgutter' +Plug 'preservim/nerdtree' +Plug 'andymass/vim-matchup' +call plug#end() +endif + +let g:gitgutter_sign_column_always = 0 + + +" rust-analyzer via the rustup proxy shim (portable across toolchains/machines) +if executable(expand('~/.cargo/bin/rust-analyzer')) + au User lsp_setup call lsp#register_server({ + \ 'name': 'rust-analyzer', + \ 'cmd': {server_info -> [expand('~/.cargo/bin/rust-analyzer')]}, + \ 'allowlist': ['rust'], + \ 'root_uri': {server_info -> lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'Cargo.toml'))}, + \ 'initialization_options': { + \ 'completion': { + \ 'autoimport': { 'enable': v:true }, + \ 'postfix': { 'enable': v:true }, + \ }, + \ 'checkOnSave': v:true, + \ }, + \ }) +endif +if executable(expand('~/.local/bin/ruff')) + au User lsp_setup call lsp#register_server({ + \ 'name': 'ruff', + \ 'cmd': {server_info -> [expand('~/.local/bin/ruff'), 'server']}, + \ 'allowlist': ['python'], + \ }) +endif +let s:uv_bin = has('macunix') && executable('/opt/homebrew/bin/uv') + \ ? '/opt/homebrew/bin/uv' + \ : expand('~/.local/bin/uv') +if executable(expand('~/.local/bin/ty')) && executable(s:uv_bin) + " Launched through `uv run` (not the raw path) so it picks up whichever + " project's .venv is active via cwd/VIRTUAL_ENV. + au User lsp_setup call lsp#register_server({ + \ 'name': 'ty', + \ 'cmd': {server_info -> [s:uv_bin, 'run', 'ty', 'server']}, + \ 'allowlist': ['python'], + \ }) +endif +" tofu-ls (OpenTofu/Terraform .tf) for go-to-definition +if executable(expand('~/.local/bin/tofu-ls')) + au User lsp_setup call lsp#register_server({ + \ 'name': 'tofu-ls', + \ 'cmd': {server_info -> [expand('~/.local/bin/tofu-ls'), 'serve']}, + \ 'allowlist': ['terraform', 'terraform-vars'], + \ }) +endif +" gopls (Go) for go-to-definition +if executable(expand('~/go/bin/gopls')) + au User lsp_setup call lsp#register_server({ + \ 'name': 'gopls', + \ 'cmd': {server_info -> [expand('~/go/bin/gopls')]}, + \ 'allowlist': ['go'], + \ 'root_uri': {server_info -> lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'go.mod'))}, + \ }) +endif + +" Force per-keystroke LSP sync for Python (vim-lsp only syncs on +" InsertLeave/TextChanged by default, not while actively typing). +autocmd FileType python autocmd TextChangedI doautocmd TextChanged + +let g:lsp_diagnostics_echo_cursor = 1 +let g:lsp_diagnostics_float_cursor = 0 +let g:lsp_diagnostics_virtual_text_enabled = 0 +let g:lsp_diagnostics_signs_enabled = 0 +let g:lsp_document_highlight_enabled = 0 +let g:lsp_preview_float = 0 +let g:lsp_hover_ui = 'preview' +let g:lsp_code_action_ui = 'float' " \a = one popup menu, not 3 windows +let g:lsp_signature_help_enabled = 0 + +function! s:set_preview_height() abort + let &previewheight = max([10, &lines * 40 / 100]) +endfunction +call s:set_preview_height() +autocmd VimResized * call s:set_preview_height() + +" Toggle hover docs in the preview window. +function! ToggleHover() abort + for l:nr in range(1, winnr('$')) + if getwinvar(l:nr, '&previewwindow') + pclose + return + endif + endfor + LspHover +endfunction + +" Show the current line's diagnostic in a preview window. +function! ToggleDiagnostic() abort + for l:nr in range(1, winnr('$')) + if getwinvar(l:nr, '&previewwindow') + pclose + return + endif + endfor + let l:diag = lsp#internal#diagnostics#under_cursor#get_diagnostic() + if empty(l:diag) + echo 'No diagnostic on this line' + return + endif + let l:cur = win_getid() + botright new + execute 'resize ' . &previewheight + setlocal buftype=nofile bufhidden=wipe noswapfile previewwindow + call setline(1, split(l:diag['message'], "\n")) + setlocal nomodifiable + call win_gotoid(l:cur) +endfunction + + +" rust-analyzer macro expansion (LSP extension, no built-in command). +function! ExpandMacro() abort + let l:params = { + \ 'textDocument': { 'uri': lsp#utils#get_buffer_uri() }, + \ 'position': lsp#get_position(), + \ } + call lsp#send_request('rust-analyzer', { + \ 'method': 'rust-analyzer/expandMacro', + \ 'params': l:params, + \ 'on_notification': function('s:expand_macro_handler'), + \ }) +endfunction + +function! s:expand_macro_handler(data) abort + if !has_key(a:data, 'response') || empty(a:data['response']) + echo 'ExpandMacro: no response / not a macro' + return + endif + let l:result = get(a:data['response'], 'result', v:null) + if l:result is v:null + echo 'ExpandMacro: nothing to expand at cursor' + return + endif + botright new + setlocal buftype=nofile bufhidden=wipe noswapfile filetype=rust + call setline(1, split('// expansion of: ' . l:result['name'], "\n")) + call append(line('$'), split(l:result['expansion'], "\n")) +endfunction + + +" gd: say when rust-analyzer is still starting/indexing instead of a false "No definition found" +let g:lsp_work_done_progress_enabled = 1 +function! s:GotoDef() abort + if &filetype ==# 'rust' + let l:st = lsp#get_server_status('rust-analyzer') + if l:st !=# 'running' + echo 'rust-analyzer ' . l:st . ', try again in a few seconds' + return + endif + let l:busy = filter(copy(lsp#get_progress()), + \ {_, p -> p['server'] ==# 'rust-analyzer' && get(p, 'title', '') !~? 'check'}) + if !empty(l:busy) + echo 'rust-analyzer still indexing: ' . get(l:busy[0], 'title', '') . ' ' . get(l:busy[0], 'message', '') + return + endif + endif + tab LspDefinition +endfunction + +" Numbered tab labels (1:main.rs 2:fs.rs): the number is what Ngt jumps to. +function! TabLine() abort + let l:s = '' + for l:i in range(1, tabpagenr('$')) + let l:buf = tabpagebuflist(l:i)[tabpagewinnr(l:i) - 1] + let l:name = fnamemodify(bufname(l:buf), ':t') + if l:name ==# '' + let l:name = '[No Name]' + endif + let l:hl = l:i == tabpagenr() ? '%#TabLineSel#' : '%#TabLine#' + let l:s .= l:hl . ' ' . l:i . ':' . l:name . ' ' + endfor + return l:s . '%#TabLineFill#' +endfunction +set tabline=%!TabLine() diff --git a/mac/tmux.conf b/mac/tmux.conf deleted file mode 100644 index bd89157..0000000 --- a/mac/tmux.conf +++ /dev/null @@ -1,38 +0,0 @@ -# Ctrl-b f: prompt for text, list windows whose name, pane title or pane contents match; Enter jumps -# Ctrl-b w: tree of every window in every session; press f inside it to filter by name, Enter jumps -# Ctrl-b s: tree of sessions (same tree, session view) -# Ctrl-b p: go to the previous window (lower index, wraps); Ctrl-b n goes to the next one -# -# Vi mode for copy -setw -g mode-keys vi - -# Mouse selection -set -g mouse on - -# Persistent pane index labels (top-left of each pane's border) -set -g pane-border-status top -set -g pane-border-format "#P" - -# Explicitly swap current pane with a pane index you type in -bind-key P command-prompt -p "swap with pane #:" "swap-pane -d -t '%%'" -# Manual: :swap-pane -s -t - -# Vim-style pane resizing (prefix + H/J/K/L), magnitude 1 -bind -r H resize-pane -L 1 -bind -r J resize-pane -D 1 -bind -r K resize-pane -U 1 -bind -r L resize-pane -R 1 - -# v to start selection, y to yank (like vim) -bind-key -T copy-mode-vi v send-keys -X begin-selection -# macOS: pbcopy. Linux: wl-copy/xclip when usable, else just drain (tmux's own -# buffer + set-clipboard/OSC 52 still apply). Netcat send is the same on both. -if-shell 'uname | grep -q Darwin' { - bind-key -T copy-mode-vi y send-keys -X copy-pipe-no-clear "bash -c 'tee >(pbcopy) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" - bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-no-clear "bash -c 'tee >(pbcopy) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" - bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-no-clear "bash -c 'tee >(pbcopy) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" -} { - bind-key -T copy-mode-vi y send-keys -X copy-pipe-no-clear "bash -c 'tee >(wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || cat >/dev/null) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" - bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-no-clear "bash -c 'tee >(wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || cat >/dev/null) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" - bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-no-clear "bash -c 'tee >(wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || cat >/dev/null) | openssl enc -aes-256-cbc -pbkdf2 -pass pass:YourPassword | nc -w 1 p.noml.ch 5023 >/dev/null 2>&1'" -} diff --git a/mac/vimrc b/mac/vimrc deleted file mode 100644 index ba9bd88..0000000 --- a/mac/vimrc +++ /dev/null @@ -1,318 +0,0 @@ -" /etc/vim/vimrc — self-contained; source of truth: /v1/dev_env/core/vim.sh -" gt: go to next tab (right, wraps) -" gT: go to previous tab (left, wraps) -" 2gt: go to tab number 2 (counted from 1 at the left) -" :q: close the current window, and the tab too if it was the last window in it -" ZZ: save if changed, then close the window (and the tab if it was the last window in it) -" ZQ: close the window WITHOUT saving (like :q!), and the tab too if it was the last window in it -" g: jump to the last-accessed tab (press g, release, then Tab); toggles between two tabs -" Ctrl-w w: cycle windows in this tab, e.g. into and out of the K popup (Ctrl-w p toggles back) -" 7G12|: go to line 7, column 12 (normal-mode keys, no colon; 3w moves 3 words) -" Reset an edited cargo dependency: delete ~/.cargo/registry/src//-/ then run cargo fetch -" -" Setup (LSP servers below are optional — each is guarded by executable()): -" plugins: curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ -" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim -" then launch vim and run :PlugInstall -" rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y -" rustup component add rust-analyzer -" python: curl -LsSf https://astral.sh/uv/install.sh | sh -" uv tool install ruff && uv tool install pyright -" -" Stop vim from auto-loading defaults.vim, which loads AFTER this file and -" would otherwise silently override settings here. Everything is in this file. -let g:skip_defaults_vim = 1 - -" ===== Shortcuts: one line each, = \ (comment follows the |) ===== -inoremap | " insert Ctrl-a: jump to line start -inoremap | " insert Ctrl-e: jump to line end -inoremap pumvisible() ? "\" : asyncomplete#force_refresh()| " insert Ctrl-l: toggle completion menu -inoremap pumvisible() ? "\" : "\"| " in menu Tab: next item -inoremap pumvisible() ? "\" : "\"| " in menu Shift-Tab: previous item -inoremap pumvisible() ? "\" : "\"| " in menu Enter: accept item -nnoremap 0| " Ctrl-a: jump to line start -nnoremap $| " Ctrl-e: jump to line end -nnoremap :nohlsearch| " Ctrl-l: clear search highlight -nnoremap :Files| " Ctrl-p: fuzzy-find a file -nnoremap rg :Rg| " \rg: search text in project -nnoremap b :Buffers| " \b: list open buffers -nnoremap n :NERDTreeToggle| " \n: toggle file tree -nnoremap gd :call GotoDef()| " gd: go to definition (Ctrl-o back) -nnoremap gi :LspImplementation| " gi: go to implementation -nnoremap gy :tab LspTypeDefinition| " gy: go to type definition in a new tab (e -> struct DirEntry) -nnoremap k :call ToggleHover()| " \k: toggle hover docs -nnoremap K :call ToggleHover()| " K: toggle hover docs (same as \k) -nnoremap D :LspDocumentation| " \D: show documentation -nnoremap e :call ToggleDiagnostic()| " \e: show error on this line -nnoremap l :lopen| " \l: list all errors -nnoremap a :LspCodeAction| " \a: code actions popup -nnoremap rn :LspRename| " \rn: rename symbol -nnoremap f :LspDocumentFormat| " \f: format file -nnoremap em :call ExpandMacro()| " \em: expand macro under cursor - - -augroup netcat_clipboard -au! -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 &") -augroup END - -filetype plugin indent on - -set number -set wrap -set breakindent -set breakindentopt=shift:1 -set expandtab -set shiftwidth=4 -set softtabstop=4 -set tabstop=4 -set noautoindent -set nosmartindent -set hlsearch -set incsearch -set ignorecase -set smartcase -set scrolloff=8 -set signcolumn=no -set foldcolumn=12 -set laststatus=2 -set statusline=%F -set wildmenu -set splitright -set splitbelow -set hidden " gd/LSP jumps reuse the window even with unsaved changes -set noswapfile -set regexpengine=2 -set completeopt=menu,menuone,noinsert,noselect - -set mouse=a -if has('mouse_sgr') - set ttymouse=sgr -endif - -set undofile -set undodir=~/.vim/undo -if !isdirectory($HOME . '/.vim/undo') - call mkdir($HOME . '/.vim/undo', 'p') -endif - -if has('termguicolors') - if !empty($TMUX) - let &t_8f = "\[38;2;%lu;%lu;%lum" - let &t_8b = "\[48;2;%lu;%lu;%lum" - endif - set termguicolors -endif -colorscheme zellner - -autocmd InsertLeave * silent! update - -" Files rustup and cargo manage (toolchains, downloaded crates) open locked; :setl ma noro to override -function! s:LockIfManaged() abort - if !exists('s:managed') - let l:cargo = empty($CARGO_HOME) ? expand('~/.cargo') : $CARGO_HOME - let s:managed = [trim(system('rustup show home')), l:cargo . '/registry', l:cargo . '/git'] - endif - for l:dir in s:managed - if stridx(expand('%:p'), l:dir . '/') == 0 - setlocal readonly nomodifiable - return - endif - endfor -endfunction -augroup managed_readonly - autocmd! - autocmd BufReadPost * call s:LockIfManaged() -augroup END - - -call plug#begin('~/.vim/plugged') -Plug 'prabirshrestha/async.vim' -Plug 'prabirshrestha/vim-lsp' -Plug 'prabirshrestha/asyncomplete.vim' -Plug 'prabirshrestha/asyncomplete-lsp.vim' -Plug 'rust-lang/rust.vim' -Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } -Plug 'junegunn/fzf.vim' -Plug 'tpope/vim-commentary' -Plug 'tpope/vim-surround' -Plug 'tpope/vim-fugitive' -Plug 'airblade/vim-gitgutter' -Plug 'preservim/nerdtree' -Plug 'andymass/vim-matchup' -call plug#end() - -let g:gitgutter_sign_column_always = 0 - - -" rust-analyzer via the rustup proxy shim (portable across toolchains/machines) -if executable(expand('~/.cargo/bin/rust-analyzer')) - au User lsp_setup call lsp#register_server({ - \ 'name': 'rust-analyzer', - \ 'cmd': {server_info -> [expand('~/.cargo/bin/rust-analyzer')]}, - \ 'allowlist': ['rust'], - \ 'root_uri': {server_info -> lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'Cargo.toml'))}, - \ 'initialization_options': { - \ 'completion': { - \ 'autoimport': { 'enable': v:true }, - \ 'postfix': { 'enable': v:true }, - \ }, - \ 'checkOnSave': v:true, - \ }, - \ }) -endif -if executable(expand('~/.local/bin/ruff')) - au User lsp_setup call lsp#register_server({ - \ 'name': 'ruff', - \ 'cmd': {server_info -> [expand('~/.local/bin/ruff'), 'server']}, - \ 'allowlist': ['python'], - \ }) -endif -let s:uv_bin = has('macunix') && executable('/opt/homebrew/bin/uv') - \ ? '/opt/homebrew/bin/uv' - \ : expand('~/.local/bin/uv') -if executable(expand('~/.local/bin/ty')) && executable(s:uv_bin) - " Launched through `uv run` (not the raw path) so it picks up whichever - " project's .venv is active via cwd/VIRTUAL_ENV. - au User lsp_setup call lsp#register_server({ - \ 'name': 'ty', - \ 'cmd': {server_info -> [s:uv_bin, 'run', 'ty', 'server']}, - \ 'allowlist': ['python'], - \ }) -endif -" tofu-ls (OpenTofu/Terraform .tf) for go-to-definition -if executable(expand('~/.local/bin/tofu-ls')) - au User lsp_setup call lsp#register_server({ - \ 'name': 'tofu-ls', - \ 'cmd': {server_info -> [expand('~/.local/bin/tofu-ls'), 'serve']}, - \ 'allowlist': ['terraform', 'terraform-vars'], - \ }) -endif -" gopls (Go) for go-to-definition -if executable(expand('~/go/bin/gopls')) - au User lsp_setup call lsp#register_server({ - \ 'name': 'gopls', - \ 'cmd': {server_info -> [expand('~/go/bin/gopls')]}, - \ 'allowlist': ['go'], - \ 'root_uri': {server_info -> lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'go.mod'))}, - \ }) -endif - -" Force per-keystroke LSP sync for Python (vim-lsp only syncs on -" InsertLeave/TextChanged by default, not while actively typing). -autocmd FileType python autocmd TextChangedI doautocmd TextChanged - -let g:lsp_diagnostics_echo_cursor = 1 -let g:lsp_diagnostics_float_cursor = 0 -let g:lsp_diagnostics_virtual_text_enabled = 0 -let g:lsp_diagnostics_signs_enabled = 0 -let g:lsp_document_highlight_enabled = 0 -let g:lsp_preview_float = 0 -let g:lsp_hover_ui = 'preview' -let g:lsp_code_action_ui = 'float' " \a = one popup menu, not 3 windows -let g:lsp_signature_help_enabled = 0 - -function! s:set_preview_height() abort - let &previewheight = max([10, &lines * 40 / 100]) -endfunction -call s:set_preview_height() -autocmd VimResized * call s:set_preview_height() - -" Toggle hover docs in the preview window. -function! ToggleHover() abort - for l:nr in range(1, winnr('$')) - if getwinvar(l:nr, '&previewwindow') - pclose - return - endif - endfor - LspHover -endfunction - -" Show the current line's diagnostic in a preview window. -function! ToggleDiagnostic() abort - for l:nr in range(1, winnr('$')) - if getwinvar(l:nr, '&previewwindow') - pclose - return - endif - endfor - let l:diag = lsp#internal#diagnostics#under_cursor#get_diagnostic() - if empty(l:diag) - echo 'No diagnostic on this line' - return - endif - let l:cur = win_getid() - botright new - execute 'resize ' . &previewheight - setlocal buftype=nofile bufhidden=wipe noswapfile previewwindow - call setline(1, split(l:diag['message'], "\n")) - setlocal nomodifiable - call win_gotoid(l:cur) -endfunction - - -" rust-analyzer macro expansion (LSP extension, no built-in command). -function! ExpandMacro() abort - let l:params = { - \ 'textDocument': { 'uri': lsp#utils#get_buffer_uri() }, - \ 'position': lsp#get_position(), - \ } - call lsp#send_request('rust-analyzer', { - \ 'method': 'rust-analyzer/expandMacro', - \ 'params': l:params, - \ 'on_notification': function('s:expand_macro_handler'), - \ }) -endfunction - -function! s:expand_macro_handler(data) abort - if !has_key(a:data, 'response') || empty(a:data['response']) - echo 'ExpandMacro: no response / not a macro' - return - endif - let l:result = get(a:data['response'], 'result', v:null) - if l:result is v:null - echo 'ExpandMacro: nothing to expand at cursor' - return - endif - botright new - setlocal buftype=nofile bufhidden=wipe noswapfile filetype=rust - call setline(1, split('// expansion of: ' . l:result['name'], "\n")) - call append(line('$'), split(l:result['expansion'], "\n")) -endfunction - - -" gd: say when rust-analyzer is still starting/indexing instead of a false "No definition found" -let g:lsp_work_done_progress_enabled = 1 -function! s:GotoDef() abort - if &filetype ==# 'rust' - let l:st = lsp#get_server_status('rust-analyzer') - if l:st !=# 'running' - echo 'rust-analyzer ' . l:st . ', try again in a few seconds' - return - endif - let l:busy = filter(copy(lsp#get_progress()), - \ {_, p -> p['server'] ==# 'rust-analyzer' && get(p, 'title', '') !~? 'check'}) - if !empty(l:busy) - echo 'rust-analyzer still indexing: ' . get(l:busy[0], 'title', '') . ' ' . get(l:busy[0], 'message', '') - return - endif - endif - tab LspDefinition -endfunction - -" Numbered tab labels (1:main.rs 2:fs.rs): the number is what Ngt jumps to. -function! TabLine() abort - let l:s = '' - for l:i in range(1, tabpagenr('$')) - let l:buf = tabpagebuflist(l:i)[tabpagewinnr(l:i) - 1] - let l:name = fnamemodify(bufname(l:buf), ':t') - if l:name ==# '' - let l:name = '[No Name]' - endif - let l:hl = l:i == tabpagenr() ? '%#TabLineSel#' : '%#TabLine#' - let l:s .= l:hl . ' ' . l:i . ':' . l:name . ' ' - endfor - return l:s . '%#TabLineFill#' -endfunction -set tabline=%!TabLine() -- cgit