From ee1971cca1eb079043501a1015370fce68e1858b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 1 Nov 2026 09:50:10 +0800 Subject: mac: vimrc and tmux.conf work in progress --- mac/tmux.conf | 28 ++++++- mac/vimrc | 264 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 260 insertions(+), 32 deletions(-) diff --git a/mac/tmux.conf b/mac/tmux.conf index fc4b931..5afc0b5 100644 --- a/mac/tmux.conf +++ b/mac/tmux.conf @@ -4,8 +4,30 @@ 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 "pbcopy" -bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-no-clear "pbcopy" -bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-no-clear "pbcopy" +# 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 index c45cfc9..6de4092 100644 --- a/mac/vimrc +++ b/mac/vimrc @@ -1,51 +1,257 @@ +" ~/.vimrc (macOS + Linux) — self-contained; source of truth: dev_env/mac/vimrc +" +" 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 +" ts/js: npm i -g typescript@6 typescript-language-server (Linux: sudo npm …) +" (TypeScript 7 ships no tsserver, which typescript-language-server needs) +" python: curl -LsSf https://astral.sh/uv/install.sh | sh +" uv tool install ruff && uv tool install ty +" search: ripgrep (for :Rg); Fedora: dnf install vim-enhanced ripgrep +" +" 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 -" Enable line wrapping -set wrap -" Optional: Make wrapped lines easier to read by indenting them -set breakindent -set breakindentopt=shift:1 -syntax on -set autoindent +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 mouse=a +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 noswapfile +set regexpengine=2 +set completeopt=menu,menuone,noinsert,noselect -set autoindent -set smartindent -filetype plugin indent on +set mouse=a +if has('mouse_sgr') + set ttymouse=sgr +endif -set laststatus=2 " permanent status bar -set statusline=%F " shows full file path +set undofile +set undodir=~/.vim/undo +if !isdirectory($HOME . '/.vim/undo') + call mkdir($HOME . '/.vim/undo', 'p') +endif -if (has("termguicolors")) +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 -"colorscheme evening -" Ctrl+A: Move to the beginning of the line in INSERT mode -inoremap +autocmd InsertLeave * silent! update -" Ctrl+E: Move to the end of the line in INSERT mode +inoremap 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 clipboard=unnamed,unnamedplus -setlocal regexpengine=2 -set hlsearch -set foldcolumn=12 " Creates a small left margin of 2 columns -set noswapfile -autocmd InsertLeave * silent! update +nnoremap :nohlsearch + +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 + +nnoremap :Files +nnoremap rg :Rg +nnoremap b :Buffers +nnoremap n :NERDTreeToggle + +" 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 +function! s:ts_root(server_info) abort + let l:buf = lsp#utils#get_buffer_path() + for l:f in ['tsconfig.json', 'package.json'] + let l:dir = lsp#utils#find_nearest_parent_file_directory(l:buf, l:f) + if !empty(l:dir) + return lsp#utils#path_to_uri(l:dir) + endif + endfor + return lsp#utils#path_to_uri(getcwd()) +endfunction +if executable('typescript-language-server') + au User lsp_setup call lsp#register_server({ + \ 'name': 'typescript-language-server', + \ 'cmd': {server_info -> ['typescript-language-server', '--stdio']}, + \ 'allowlist': ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'], + \ 'root_uri': function('s:ts_root'), + \ }) +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 + +" 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_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 + +nnoremap k :call ToggleHover() +nnoremap gd :LspDefinition +nnoremap gi :LspImplementation +nnoremap rn :LspRename +nnoremap a :LspCodeAction +nnoremap f :LspDocumentFormat +nnoremap l :lopen +nnoremap D :LspDocumentation +nnoremap e :call ToggleDiagnostic() + +" 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 +nnoremap em :call ExpandMacro() +inoremap +inoremap +inoremap pumvisible() ? "\" : "\" +inoremap pumvisible() ? "\" : "\" +inoremap pumvisible() ? "\" : "\" -- cgit