vim9script set encoding=utf-8 scriptencoding utf-8 # ---------------------------------------------------------- # 基本設定 {{{ set fileencodings=ucs-bom,utf-8,iso-2022-jp,cp932,euc-jp set noexpandtab set tabstop=3 # 意外とありな気がしてきた… set shiftwidth=0 set softtabstop=0 set autoindent set smartindent set breakindent set backspace=indent,start,eol set nf=alpha,hex set virtualedit=block set list set listchars=tab:\|\ ,trail:-,extends:>,precedes:<,nbsp:% set fillchars= set cmdheight=0 set laststatus=2 set noruler set noshowcmd set noshowmode set display=lastline set ambiwidth=double set belloff=all set ttimeoutlen=50 set wildmenu set autochdir set backupskip=/var/tmp/* set undodir=~/.vim/undo set undofile set updatetime=2000 set incsearch set hlsearch nohlsearch augroup vimrc # 新しい自由 au! augroup End #}}} ------------------------------------------------------- # ---------------------------------------------------------- # ユーティリティ {{{ const rtproot = has('win32') ? '~/vimfiles' : '~/.vim' const has_deno = executable('deno') # こんな感じ # MultiCmd nmap,vmap xxx yyy<if-nmap>NNN<if-vmap>VVV<>zzz # ↓ # nmap xxx yyyNNNzzz | vmap xxx yyyVVVzzz def MultiCmd(qargs: string) const [cmds, args] = qargs->split('^\S*\zs') for cmd in cmds->split(',') const a = args ->substitute('<if-' .. cmd .. '>', '<>', 'g') ->substitute('<if-.\{-1,}\(<>\|$\)', '', 'g') ->substitute('<>', '', 'g') execute cmd a endfor enddef command! -nargs=* MultiCmd MultiCmd(<q-args>) # その他 command! -nargs=1 -complete=var Enable <args> = 1 command! -nargs=1 -complete=var Disable <args> = 0 def RemoveEmptyLine(line: number) silent! execute ':' line 's/\s\+$//' silent! execute ':' line 's/^\s*\n//' enddef def BufIsSmth(): bool return &modified || ! empty(bufname()) enddef def IndentStr(expr: any): string return matchstr(getline(expr), '^\s*') enddef # 指定幅以上なら'>'で省略する def TruncToDisplayWidth(str: string, width: number): string return strdisplaywidth(str) <= width ? str : str->matchstr(printf('.*\%%<%dv', width + 1)) .. '>' enddef #}}} ------------------------------------------------------- # ---------------------------------------------------------- # プラグイン {{{ # jetpack {{{ const jetpackfile = expand(rtproot .. '/pack/jetpack/opt/vim-jetpack/plugin/jetpack.vim') const has_jetpack = filereadable(jetpackfile) if ! has_jetpack const jetpackurl = 'https://raw.githubusercontent.com/tani/vim-jetpack/master/plugin/jetpack.vim' system(printf('curl -fsSLo %s --create-dirs %s', jetpackfile, jetpackurl)) endif packadd vim-jetpack jetpack#begin() Jetpack 'tani/vim-jetpack', { 'opt': 1 } Jetpack 'airblade/vim-gitgutter' Jetpack 'alvan/vim-closetag' Jetpack 'ctrlpvim/ctrlp.vim' Jetpack 'cohama/lexima.vim' # 括弧補完 Jetpack 'delphinus/vim-auto-cursorline' Jetpack 'dense-analysis/ale' Jetpack 'easymotion/vim-easymotion' Jetpack 'hrsh7th/vim-vsnip' Jetpack 'hrsh7th/vim-vsnip-integ' Jetpack 'itchyny/lightline.vim' Jetpack 'kana/vim-textobj-user' Jetpack 'LeafCage/vimhelpgenerator' Jetpack 'luochen1990/rainbow' # 虹色括弧 Jetpack 'machakann/vim-sandwich' Jetpack 'mattn/ctrlp-matchfuzzy' Jetpack 'mattn/vim-notification' Jetpack 'matze/vim-move' # 行移動 Jetpack 'mechatroner/rainbow_csv' Jetpack 'michaeljsmith/vim-indent-object' Jetpack 'osyo-manga/vim-textobj-multiblock' Jetpack 'othree/html5.vim' Jetpack 'othree/yajs.vim' Jetpack 'prabirshrestha/asyncomplete-buffer.vim' Jetpack 'prabirshrestha/asyncomplete.vim' Jetpack 'rafamadriz/friendly-snippets' Jetpack 'thinca/vim-portal' Jetpack 'tpope/vim-fugitive' # Gdiffとか Jetpack 'tyru/caw.vim' # コメント化 Jetpack 'yami-beta/asyncomplete-omni.vim' Jetpack 'yegappan/mru' Jetpack 'vim-jp/vital.vim' Jetpack 'utubo/jumpcuorsor.vim' # vimに対応させたやつ(様子見)vim-jetpackだとインストール出来ないかも? Jetpack 'utubo/vim-auto-hide-cmdline' Jetpack 'utubo/vim-colorscheme-girly' Jetpack 'utubo/vim-minviml' Jetpack 'utubo/vim-portal-aim' Jetpack 'utubo/vim-registers-lite' Jetpack 'utubo/vim-reformatdate' Jetpack 'utubo/vim-tabtoslash' # あまり使ってないけど作ったので… Jetpack 'utubo/vim-shrink' Jetpack 'utubo/vim-tablist' Jetpack 'utubo/vim-tabpopupmenu' Jetpack 'utubo/vim-textobj-twochars' if has_deno Jetpack 'vim-denops/denops.vim' Jetpack 'vim-skk/skkeleton' endif jetpack#end() if ! has_jetpack jetpack#sync() endif #}}} # easymotion {{{ Enable g:EasyMotion_smartcase Enable g:EasyMotion_use_migemo Enable g:EasyMotion_enter_jump_first Disable g:EasyMotion_do_mapping g:EasyMotion_keys = 'asdghklqwertyuiopzxcvbnmfjASDGHKLQWERTYUIOPZXCVBNMFJ;' map s <Plug>(ahc)<Plug>(easymotion-s) #}}} # sandwich {{{ g:sandwich#recipes = deepcopy(g:sandwich#default_recipes) g:sandwich#recipes += [ { buns: ["\r", '' ], input: ["\r"], command: ["normal! a\r"] }, { buns: ['', '' ], input: ['q'] }, { buns: ['「', '」'], input: ['k'] }, { buns: ['{ ', ' }'], input: ['{'] }, { buns: ['${', '}' ], input: ['${'] }, { buns: ['%{', '}' ], input: ['%{'] }, { buns: ['CommentString(0)', 'CommentString(1)'], expr: 1, input: ['c'] }, ] def! g:CommentString(index: number): string return &commentstring->split('%s')->get(index, '') enddef Enable g:sandwich_no_default_key_mappings Enable g:operator_sandwich_no_default_key_mappings MultiCmd nmap,vmap Sd <Plug>(operator-sandwich-delete)<if-nmap>ab MultiCmd nmap,vmap Sr <Plug>(operator-sandwich-replace)<if-nmap>ab MultiCmd nmap,vmap Sa <Plug>(operator-sandwich-add)<if-nmap>iw MultiCmd nmap,vmap S <Plug>(operator-sandwich-add)<if-nmap>iw nmap S^ v^S nmap S$ vg_S nmap <expr> SS (matchstr(getline('.'), '[''"]', col('.')) ==# '"') ? 'Sr''' : 'Sr"' # 改行で挟んだあとタブでインデントされると具合が悪くなるので… def FixSandwichPos() var c = g:operator#sandwich#object.cursor if g:fix_sandwich_pos[1] !=# c.inner_head[1] c.inner_head[2] = getline(c.inner_head[1])->match('\S') + 1 c.inner_tail[2] = getline(c.inner_tail[1])->match('$') + 1 endif enddef au vimrc User OperatorSandwichAddPre g:fix_sandwich_pos = getpos('.') au vimrc User OperatorSandwichAddPost FixSandwichPos() # 内側に連続で挟むやつ var big_mac_crown = [] def BigMac(first: bool = true) const c = g:operator#sandwich#object.cursor.inner_head[1 : 2] if first || big_mac_crown !=# c big_mac_crown = c au vimrc User OperatorSandwichAddPost ++once BigMac(false) if first feedkeys('Sa') else setpos("'<", g:operator#sandwich#object.cursor.inner_head) setpos("'>", g:operator#sandwich#object.cursor.inner_tail) feedkeys('gvSa') endif endif enddef nmap Sm viwSm vmap Sm <Cmd>call <SID>BigMac()<CR> # 囲みを削除したら行末空白と空行も削除 def RemoveAirBuns() const c = g:operator#sandwich#object.cursor RemoveEmptyLine(c.tail[1]) RemoveEmptyLine(c.head[1]) enddef au vimrc User OperatorSandwichDeletePost RemoveAirBuns() #}}} # MRU {{{ # デフォルト設定(括弧内にフルパス)だとパスに括弧が含まれているファイルが開けないので、パスに使用されない">"を区切りにする g:MRU_Filename_Format = { formatter: 'fnamemodify(v:val, ":t") . " > " . v:val', parser: '> \zs.*', syntax: '^.\{-}\ze >' } # 数字キーで開く def MRUwithNumKey(use_tab: bool) b:use_tab = use_tab setlocal number redraw if &cmdheight !=# 0 echoh Question echo printf('[1]..[9] => open with a %s.', use_tab ? 'tab' : 'window') echoh None endif const key = use_tab ? 't' : '<CR>' for i in range(1, 9) execute printf('nmap <buffer> <silent> %d :<C-u>%d<CR>%s', i, i, key) endfor enddef def MyMRU() Enable b:auto_cursorline_disabled setlocal cursorline nnoremap <buffer> w <Cmd>call <SID>MRUwithNumKey(!b:use_tab)<CR> nnoremap <buffer> R <Cmd>MruRefresh<CR><Cmd>normal! u nnoremap <buffer> <Esc> <Cmd>q!<CR> MRUwithNumKey(BufIsSmth()) enddef au vimrc FileType mru MyMRU() au vimrc ColorScheme * hi link MruFileName Directory nnoremap <F2> <Cmd>MRUToggle<CR> g:MRU_Exclude_Files = has('win32') ? $TEMP .. '\\.*' : '^/tmp/.*\|^/var/tmp/.*' #}}} # 補完 {{{ def RegisterAsyncompSource(name: string, white: list<string>, black: list<string>) execute printf("asyncomplete#register_source(asyncomplete#sources#%s#get_source_options({ name: '%s', whitelist: %s, blacklist: %s, completor: asyncomplete#sources#%s#completor }))", name, name, white, black, name) enddef RegisterAsyncompSource('omni', ['*'], ['c', 'cpp', 'html']) RegisterAsyncompSource('buffer', ['*'], ['go']) MultiCmd imap,smap <expr> JJ vsnip#expandable() ? '<Plug>(vsnip-expand)' : 'JJ' MultiCmd imap,smap <expr> <C-l> vsnip#available(1) ? '<Plug>(vsnip-expand-or-jump)' : '<C-l>' MultiCmd imap,smap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : pumvisible() ? '<C-n>' : '<Tab>' MultiCmd imap,smap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : pumvisible() ? '<C-p>' : '<S-Tab>' #imap <expr> <CR> pumvisible() ? '<C-y>' : '<CR>' Enable g:lexima_accept_pum_with_enter #}}} # ALE {{{ Enable g:ale_set_quickfix Enable g:ale_fix_on_save Disable g:ale_lint_on_insert_leave Disable g:ale_set_loclist g:ale_sign_error = '🐞' g:ale_sign_warning = '🐝' g:ale_fixers = { typescript: ['deno'] } g:ale_lint_delay = &updatetime nmap <silent> [a <Plug>(ale_previous_wrap) nmap <silent> ]a <Plug>(ale_next_wrap) # cmdheight=0だとALEのホバーメッセージがちらつくのでg:ll_aleに代入してlightlineで表示する Disable g:ale_echo_cursor g:ll_are = '' def ALEEchoCursorCmdHeight0() var loc = ale#util#FindItemAtCursor(bufnr())[1] if !empty(loc) g:ll_ale = loc.type ==# 'E' ? '🐞' : '🐝' g:ll_ale ..= ' ' g:ll_ale ..= get(loc, 'detail', loc.text)->split('\n')[0] ->substitute('^\[[^]]*\] ', '', '') else g:ll_ale = '' endif enddef au vimrc CursorMoved * ALEEchoCursorCmdHeight0() #}}} # lightline {{{ # ヤンクしたやつを表示するやつ g:ll_reg = '' def LLYankPost() var reg = v:event.regcontents ->join('\n') ->substitute('\t', '›', 'g') ->TruncToDisplayWidth(20) g:ll_reg = '📋:' .. reg enddef au vimrc TextYankPost * LLYankPost() # 毎時45分から15分間休憩しようね g:ll_tea_break = '0:00' g:ll_tea_break_opentime = localtime() def! g:VimrcTimer60s(timer: any) const tick = (localtime() - g:ll_tea_break_opentime) / 60 const mm = tick % 60 const tea = mm >= 45 ? '☕🍴🍰' : '' g:ll_tea_break = tea .. printf('%d:%02d', tick / 60, mm) lightline#update() if (mm ==# 45) notification#show(" ☕🍴🍰\nHave a break time !") endif enddef timer_stop(get(g:, 'vimrc_timer_60s', 0)) g:vimrc_timer_60s = timer_start(60000, 'VimrcTimer60s', { repeat: -1 }) # &ff if has('win32') def! g:LLFF(): string return &ff !=# 'dos' ? &ff : '' enddef else def! g:LLFF(): string return &ff ==# 'dos' ? &ff : '' enddef endif # &fenc def! g:LLNotUtf8(): string return &fenc ==# 'utf-8' ? '' : &fenc enddef # lightline設定 g:lightline = { colorscheme: 'wombat', active: { left: [['mode', 'paste'], ['fugitive', 'filename'], ['ale']], right: [['teabreak'], ['ff', 'notutf8', 'li'], ['reg']] }, component: { teabreak: '%{g:ll_tea_break}', reg: '%{g:ll_reg}', ale: '%=%{g:ll_ale}', li: '%2c,%l/%L' }, component_function: { ff: 'LLFF', notutf8: 'LLNotUtf8' }, } # tablineはデフォルト au vimrc VimEnter * set tabline= #}}} # skk {{{ if has_deno if ! empty($SKK_JISYO_DIR) skkeleton#config({ globalJisyo: expand($SKK_JISYO_DIR .. 'SKK-JISYO.L'), userJisyo: expand($SKK_JISYO_DIR .. '.skkeleton'), }) endif skkeleton#config({ eggLikeNewline: true, keepState: true, showCandidatesCount: 1, }) map! <C-j> <Plug>(skkeleton-toggle) endif #}}} # textobj-multiblock {{{ MultiCmd omap,xmap ab <Plug>(textobj-multiblock-a) MultiCmd omap,xmap ib <Plug>(textobj-multiblock-i) g:textobj_multiblock_blocks = [ \ [ "(", ")" ], \ [ "[", "]" ], \ [ "{", "}" ], \ [ '<', '>' ], \ [ '"', '"', 1 ], \ [ "'", "'", 1 ], \ [ ">", "<", 1 ], \ [ "「", "」", 1 ], ] #}}} # Portal {{{ nnoremap <Leader>a <Cmd>PortalAim<CR> nnoremap <Leader>b <Cmd>PortalAim blue<CR> nnoremap <Leader>o <Cmd>PortalAim orange<CR> nnoremap <Leader>r <Cmd>PortalReset<CR> #}}} # ヘルプ作成 {{{ g:vimhelpgenerator_version = '' g:vimhelpgenerator_author = 'Author : utubo' g:vimhelpgenerator_defaultlanguage = 'en' #}}} # cmdline statusline 切り替え {{{ Enable g:auto_hide_cmdline_switch_statusline # statusline非表示→cmdline表示の順にしないとちらつくので応急処置… # 本筋はCmdlineEnterPreを作るかupdate_screen(VALID)をCmdlineEnter発行の後にするしかない? # ソース # https://github.com/vim/vim/blob/master/src/ex_getln.c # static char_u * getcmdline_int() # 前者 # `trigger_cmd_autocmd(firstc == -1 || firstc == NUL ? '-' : firstc, EVENT_CMDLINEENTERPRE)` # を`if (cmdheight0)`の手前で実行する? # `firstc == -1`のくだりは後の行から移動してきてもいいかもしれない # ドキュメント修正も必要 # でもautocmdの中で`cmdheight`を変更されたらどうするの? # 後者 # 理由があって今のタイミングでupdate_screenしているのだから移動できるわけがない… MultiCmd nnoremap,vnoremap : <Plug>(ahc-switch): MultiCmd nnoremap,vnoremap / <Plug>(ahc-switch)<Cmd>noh<CR>/ MultiCmd nnoremap,vnoremap ? <Plug>(ahc-switch)<Cmd>noh<CR>? MultiCmd nmap,vmap ; : nnoremap <Space>; ; nnoremap <Space>: : # 自作プラグイン(vim-registerslite)と被ってしまった… # inoremap <C-r>= <C-o><Plug>(ahc-switch)<C-r>= #}}} # その他 {{{ Enable g:rainbow_active g:auto_cursorline_wait_ms = &updatetime g:ctrlp_match_func = {'match': 'ctrlp_matchfuzzy#matcher'} g:ctrlp_cmd = 'CtrlPMixed' nmap [c <Plug>(ahc)<Plug>(GitGutterPrevHunk) nmap ]c <Plug>(ahc)<Plug>(GitGutterNextHunk) nmap <Space>ga :<C-u>Git add % nmap <Space>gc :<C-u>Git commit -m ''<Left> nmap <Space>gp :<C-u>Git push nnoremap <Space>gv <Cmd>Gvdiffsplit<CR> nnoremap <Space>gd <Cmd>Gdiffsplit<CR> nnoremap <Space>gl <Cmd>Git pull<CR> nnoremap <Space>t <Cmd>call tabpopupmenu#popup()<CR> nnoremap <Space>T <Cmd>call tablist#Show()<CR> MultiCmd nmap,vmap <Space>c <Plug>(caw:hatpos:toggle) MultiCmd nmap,tmap <silent> <C-w><C-s> <Plug>(shrink-height)<C-w>w MultiCmd nmap,tmap <silent> <C-w><C-h> <Plug>(shrink-width)<C-w>w # EasyMotionとどっちを使うか様子見中 noremap <Space>s <Plug>(jumpcursor-jump) #}}} # 開発用 {{{ const localplugins = expand(rtproot .. '/pack/local/opt/*') if localplugins !=# '' &runtimepath = substitute(localplugins, '\n', ',', 'g') .. ',' .. &runtimepath endif #}}} filetype plugin indent on #}}} ------------------------------------------------------- # ---------------------------------------------------------- # コピペ寄せ集め色々 {{{ au vimrc InsertLeave * set nopaste au vimrc BufReadPost *.log* normal! G vnoremap * "vy/\V<Cmd>substitute(escape(@v,'\/'),"\n",'\\n','g')<CR><CR> inoremap kj <Esc>`^ inoremap kk <Esc>`^ inoremap <CR> <CR><C-g>u # https://github.com/astrorobot110/myvimrc/blob/master/vimrc set matchpairs+=(:),「:」,『:』,【:】,[:],<:> # https://github.com/Omochice/dotfiles nnoremap <expr> i len(getline('.')) !=# 0 ? 'i' : '"_cc' nnoremap <expr> a len(getline('.')) !=# 0 ? 'a' : '"_cc' nnoremap <expr> A len(getline('.')) !=# 0 ? 'A' : '"_cc' #}}} ------------------------------------------------------- # ---------------------------------------------------------- # タブ幅やタブ展開を自動設定 {{{ def SetupTabstop() const limit = 100 const org = getpos('.') cursor(1, 1) if !!search('^\t', 'nc', limit) setlocal noexpandtab setlocal tabstop=3 # 意外とありな気がしてきた… elseif !!search('^ \S', 'nc', limit) setlocal expandtab setlocal tabstop=2 elseif !!search('^ \S', 'nc', limit) setlocal expandtab setlocal tabstop=4 endif setpos('.', org) enddef au vimrc BufReadPost * SetupTabstop() #}}} ------------------------------------------------------- # ---------------------------------------------------------- # vimgrep {{{ def VimGrep(keyword: string, ...targets: list<string>) var path = join(targets, ' ') # パスを省略した場合は、同じ拡張子のファイルから探す if empty(path) path = expand('%:e') ==# '' ? '*' : ('*.' .. expand('%:e')) endif # 適宜タブで開く(ただし明示的に「%」を指定したらカレントで開く) const use_tab = BufIsSmth() && path !=# '%' if use_tab tabnew endif # lvimgrepしてなんやかんやして終わり execute printf('silent! lvimgrep %s %s', keyword, path) if ! empty(getloclist(0)) lwindow else echoh ErrorMsg echomsg 'Not found.: ' .. keyword echoh None if use_tab tabnext - tabclose + endif endif enddef command! -nargs=+ VimGrep VimGrep(<f-args>) nmap <Space>/ :<C-u>VimGrep<Space> def SetupQF() nnoremap <buffer> <silent> ; <CR>:silent! normal! zv<CR><C-W>w nnoremap <buffer> <silent> w <C-W><CR>:silent! normal! zv<CR><C-W>w nnoremap <buffer> <silent> t <C-W><CR>:silent! normal! zv<CR><C-W>T nnoremap <buffer> <nowait> q <Cmd>lexpr ''<CR>:q<CR> nnoremap <buffer> f <C-f> nnoremap <buffer> b <C-b> # 様子見中(使わなそうなら削除する) execute printf('nnoremap <buffer> T <C-W><CR><C-W>T%dgt', tabpagenr()) enddef au vimrc FileType qf SetupQF() au vimrc WinEnter * if winnr('$') ==# 1 && &buftype ==# 'quickfix' | q | endif #}}} ------------------------------------------------------- # ---------------------------------------------------------- # diff {{{ set splitright set fillchars+=diff:\ # 削除行は空白文字で埋める # diffモードを自動でoff https://hail2u.net/blog/software/vim-turn-off-diff-mode-automatically.html au vimrc WinEnter * if (winnr('$') ==# 1) && !!getbufvar(winbufnr(0), '&diff') | diffoff | endif #}}} ------------------------------------------------------- # ---------------------------------------------------------- # 日付関係 {{{ g:reformatdate_extend_names = [{ a: ['日', '月', '火', '水', '木', '金', '土'], A: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'], }] inoremap <expr> <F5> strftime('%Y/%m/%d') cnoremap <expr> <F5> strftime('%Y%m%d') nnoremap <F5> <Cmd>call reformatdate#reformat(localtime())<CR> nnoremap <C-a> <Cmd>call reformatdate#inc(v:count)<CR> nnoremap <C-x> <Cmd>call reformatdate#dec(v:count)<CR> nnoremap <Space><F5> /\d\{4\}\/\d\d\/\d\d<CR> #}}} ------------------------------------------------------- # ---------------------------------------------------------- # スマホ用 {{{ # - キーが小さいので押しにくいものはSpaceへマッピング # - スマホでのコーディングは基本的にバグ取り nnoremap <Space>zz <Cmd>q!<CR> # スタックトレースからyankしてソースの該当箇所を探すのを補助 nnoremap <Space>e G?\cErr\\|Exception<CR> nnoremap <Space>y yiw nnoremap <expr> <Space>f (getreg('"') =~ '^\d\+$' ? ':' : '/') .. getreg('"') .. '<CR>' # スマホだと:と/とファンクションキーが遠いので… nmap <Space>. : nmap <Space>, / for i in range(1, 10) execute printf('nmap <Space>%d <F%d>', i % 10, i) endfor nmap <Space><Space>1 <F11> nmap <Space><Space>2 <F12> #}}} ------------------------------------------------------- # ---------------------------------------------------------- # カーソルを行頭に沿わせて移動 {{{ def PutHat(): string const x = getline('.')->match('\S') + 1 if x !=# 0 || !exists('w:my_hat') w:my_hat = col('.') ==# x ? '^' : '' endif return w:my_hat enddef nnoremap <expr> j 'j' .. <SID>PutHat() nnoremap <expr> k 'k' .. <SID>PutHat() #}}} ------------------------------------------------------- # ---------------------------------------------------------- # 折り畳み {{{ # こんなかんじでインデントに合わせて表示📁 {{{ def! g:MyFoldText(): string const src = getline(v:foldstart) const indent = repeat(' ', indent(v:foldstart)) const text = &foldmethod ==# 'indent' ? '' : src->substitute(matchstr(&foldmarker, '^[^,]*'), '', '')->trim() return indent .. text .. '📁' enddef set foldtext=g:MyFoldText() set fillchars+=fold:\ # 折り畳み時の「-」は半角空白 au vimrc ColorScheme * hi! link Folded Delimiter #}}} # ホールドマーカーの前にスペース、後ろに改行を入れる {{{ def Zf() const firstline = min([line('.'), line('v')]) const lastline = max([line('.'), line('v')]) execute ':' firstline 's/\v(\S)?$/\1 /' append(lastline, IndentStr(firstline)) cursor([firstline, 1]) cursor([lastline + 1, 1]) normal! zf enddef vnoremap zf <Cmd>call <SID>Zf()<CR> #}}} # ホールドマーカーを削除したら行末をトリムする {{{ def Zd() if foldclosed(line('.')) ==# -1 normal! zc endif const head = foldclosed(line('.')) const tail = foldclosedend(line('.')) if head ==# -1 return endif const org = getpos('.') normal! zd RemoveEmptyLine(tail) RemoveEmptyLine(head) setpos('.', org) enddef nnoremap zd <Cmd>call <SID>Zd()<CR> #}}} # その他折りたたみ関係 {{{ set foldmethod=marker au vimrc FileType markdown,yaml setlocal foldlevelstart=99 | setlocal foldmethod=indent au vimrc BufReadPost * :silent! normal! zO nnoremap <expr> h (col('.') ==# 1 && 0 < foldlevel('.') ? 'zc' : 'h') nnoremap Z<Tab> <Cmd>set foldmethod=indent<CR> nnoremap Z{ <Cmd>set foldmethod=marker<CR> nnoremap Zy <Cmd>set foldmethod=syntax<CR> #}}} #}}} ------------------------------------------------------- # ---------------------------------------------------------- # ビジュアルモードあれこれ {{{ def KeepingCurPos(expr: string) const cur = getcurpos() execute expr setpos('.', cur) enddef vnoremap u <Cmd>call <SID>KeepingCurPos('undo')<CR> vnoremap <C-R> <Cmd>call <SID>KeepingCurPos('redo')<CR> vnoremap <Tab> <Cmd>normal! >gv<CR> vnoremap <S-Tab> <Cmd>normal! <gv<CR> #}}} # ---------------------------------------------------------- # コマンドモードあれこれ {{{ cnoremap <C-h> <Space><BS><Left> cnoremap <C-l> <Space><BS><Right> cnoremap <expr> <C-r><C-r> trim(@") cnoremap <expr> <C-r><C-e> escape(@", '~^$.*?/\[]') cnoreabbrev cs colorscheme # 「jj」で<CR>、「kk」はキャンセル # ただし保存は片手で「;jj」でもOK(「;wjj」じゃなくていい) cnoremap kk <C-c> cnoremap <expr> jj (empty(getcmdline()) && getcmdtype() ==# ':' ? 'update<CR>' : '<CR>') inoremap ;jj <Esc>`^<Cmd>update<CR> #}}} ------------------------------------------------------- # ---------------------------------------------------------- # terminalとか {{{ if has('win32') command! Powershell :bo terminal ++close pwsh nnoremap SH <Cmd>Powershell<CR> nnoremap <S-F1> <Cmd>silent !start explorer %:p:h<CR> else nnoremap SH <Cmd>bo terminal<CR> endif tnoremap <C-w>; <C-w>: tnoremap <C-w><C-w> <C-w>w tnoremap <C-w><C-q> exit<CR> #}}} ------------------------------------------------------- # ---------------------------------------------------------- # markdownのチェックボックス {{{ def ToggleCheckBox() const a = getline('.') var b = substitute(a, '^\(\s*\)- \[ \]', '\1- [x]', '') # check on if a ==# b b = substitute(a, '^\(\s*\)- \[x\]', '\1- [ ]', '') # check off endif if a ==# b b = substitute(a, '^\(\s*\)\(- \)*', '\1- [ ] ', '') # a new check box endif setline('.', b) var c = getpos('.') c[2] += len(b) - len(a) setpos('.', c) enddef noremap <Space>x <Cmd>call <SID>ToggleCheckBox()<CR> #}}} ------------------------------------------------------- # ---------------------------------------------------------- # バッファの情報を色付きで表示 {{{ def ShowBufInfo(event: string = '') if &ft ==# 'qf' return endif var isReadPost = event ==# 'BufReadPost' if isReadPost && ! filereadable(expand('%')) # プラグインとかが一時的なbufnameを付与して開いた場合は無視する return endif var msg = [] add(msg, ['Title', '"' .. bufname() .. '"']) add(msg, ['Normal', ' ']) if &modified add(msg, ['Delimiter', '[+]']) add(msg, ['Normal', ' ']) endif if !isReadPost add(msg, ['Tag', '[New]']) add(msg, ['Normal', ' ']) endif if &readonly add(msg, ['WarningMsg', '[RO]']) add(msg, ['Normal', ' ']) endif const w = wordcount() if isReadPost || w.bytes !=# 0 add(msg, ['Constant', printf('%dL, %dB', w.bytes ==# 0 ? 0 : line('$'), w.bytes)]) add(msg, ['Normal', ' ']) endif add(msg, ['MoreMsg', printf('%s %s %s', &ff, (empty(&fenc) ? &encoding : &fenc), &ft)]) var msglen = 0 const maxlen = &columns - 2 for i in reverse(range(0, len(msg) - 1)) var s = msg[i][1] var d = strdisplaywidth(s) msglen += d if maxlen < msglen const l = maxlen - msglen + d while !empty(s) && l < strdisplaywidth(s) s = s[1 :] endwhile msg[i][1] = s msg = msg[i : ] insert(msg, ['NonText', '<'], 0) break endif endfor redraw echo '' for m in msg execute 'echohl' m[0] echon m[1] endfor echohl Normal enddef noremap <C-g> <Plug>(ahc)<Cmd>call <SID>ShowBufInfo()<CR> # cmdheight=0にしたら無用になった # au vimrc BufNewFile * ShowBufInfo('BufNewFile') # au vimrc BufReadPost * ShowBufInfo('BufReadPost') #}}} ------------------------------------------------------- # ---------------------------------------------------------- # 閉じる {{{ def Quit(expr: string = '') if ! empty(expr) if winnr() ==# winnr(expr) return endif execute 'wincmd' expr endif if mode() ==# 't' quit! else confirm quit endif enddef nnoremap q <Nop> nnoremap Q q nnoremap qh <Cmd>call <SID>Quit('h')<CR> nnoremap qj <Cmd>call <SID>Quit('j')<CR> nnoremap qk <Cmd>call <SID>Quit('k')<CR> nnoremap ql <Cmd>call <SID>Quit('l')<CR> nnoremap qq <Cmd>call <SID>Quit()<CR> nnoremap q: q: nnoremap q/ q/ nnoremap q? q? #}}} ------------------------------------------------------- # ---------------------------------------------------------- # ファイルを移動して保存 {{{ def MoveFile(newname: string) const oldpath = expand('%') const newpath = expand(newname) if ! empty(oldpath) && filereadable(oldpath) if filereadable(newpath) echoh Error echo 'file "' .. newname .. '" already exists.' echoh None return endif rename(oldpath, newpath) endif execute 'saveas!' newpath # 開き直してMRUに登録 edit enddef command! -nargs=1 -complete=file MoveFile call <SID>MoveFile(<f-args>) cnoreabbrev mv MoveFile #}}} # ---------------------------------------------------------- # vimrc作成用 {{{ # カーソル行を実行するやつ cnoremap <expr> <SID>(exec_line) getline('.')->substitute('^[ \t"#:]\+', '', '') .. '<CR>' nmap g: <Plug>(ahc):<C-u><SID>(exec_line) nmap g9 <Plug>(ahc):<C-u>vim9cmd <SID>(exec_line) vmap g: "vy<Plug>(ahc):<C-u><C-r>=@v<CR><CR> vmap g9 "vy<Plug>(ahc):<C-u>vim9cmd <C-r>=@v<CR><CR> # カーソル位置のハイライトを確認するやつ nnoremap <expr> <Space>gh '<Cmd>hi ' .. synID(line('.'), col('.'), 1)->synIDattr('name')->substitute('^$', 'Normal', '') .. '<CR>' #}}} # ---------------------------------------------------------- # その他細々したの {{{ if has('clipboard') au vimrc FocusGained * @" = @+ au vimrc FocusLost * @+ = @" endif nnoremap <F11> <Cmd>set number!<CR> nnoremap <F12> <Cmd>set wrap!<CR> cnoremap <expr> <SID>(rpl) 's///g \| noh' .. repeat('<Left>', 9) nmap gs :<C-u>%<SID>(rpl) nmap gS :<C-u>%<SID>(rpl)<Cmd>call feedkeys(expand('<cword>')->escape('^$.*?/\[]'), 'ni')<CR><Right> vmap gs :<SID>(rpl) nnoremap Y y$ nnoremap <Space>p $p nnoremap <Space>P ^P nnoremap <Space><Space>p o<Esc>P nnoremap <Space><Space>P O<Esc>p # 分割キーボードで右手親指が<CR>になったので nmap <CR> <Space> # `T`多少潰しても大丈夫だろう… nmap TE :<C-u>tabe<Space> nnoremap TN <Cmd>tabnew<CR> nnoremap TD <Cmd>tabe ./<CR> nnoremap TT <Cmd>silent! tabnext #<CR> onoremap <expr> } '<Esc>m`0' .. v:count1 .. v:operator .. '}' onoremap <expr> { '<Esc>m`V' .. v:count1 .. '{' .. v:operator vnoremap <expr> h mode() ==# 'V' ? '<Esc>h' : 'h' vnoremap <expr> l mode() ==# 'V' ? '<Esc>l' : 'l' vnoremap J j vnoremap K k inoremap kj <Esc>`^ inoremap 「 「」<Left> inoremap 「」 「」<Left> inoremap ( ()<Left> inoremap () ()<Left> au vimrc FileType vim if getline(1) ==# 'vim9script' | &commentstring = '#%s' | endif #}}} ------------------------------------------------------- # ---------------------------------------------------------- # 様子見中 {{{ # 使わなそうなら削除する vnoremap <expr> p '"_s<C-R>' .. v:register .. '<ESC>' vnoremap P p nnoremap <Space>h ^ nnoremap <Space>l $ nnoremap <Space>d "_d nnoremap <Space>n <Cmd>nohlsearch<CR> au vimrc CursorHold * feedkeys(' n') # nohはauで動かない(:help noh) # どっちも<C-w>w。左手オンリーと右手オンリーのマッピング nnoremap <Space>w <C-w>w nnoremap <Space>o <C-w>w # CSVとかのヘッダを固定表示する。ファンクションキーじゃなくてコマンド定義すればいいかな… nnoremap <silent> <F10> <ESC>1<C-w>s:1<CR><C-w>w vnoremap <F10> <ESC>1<C-w>s<C-w>w # US→「"」押しにくい、JIS→「'」押しにくい # デフォルトのMはあまり使わないかなぁ… nnoremap ' " nnoremap m ' nnoremap M m # うーん… inoremap jj <C-o> inoremap jjh <C-o>^ inoremap jjl <C-o>$ inoremap jje <C-o>e<C-o>a inoremap jj; <C-o>$; inoremap jj, <C-o>$, inoremap jj{ <C-o>$ { inoremap jj} <C-o>$ } inoremap jj<CR> <C-o>$<CR> inoremap jjk 「」<Left> inoremap jjx <Cmd>call <SID>ToggleCheckBox()<CR> # これはちょっと押しにくい(自分のキーボードだと) inoremap <M-x> <Cmd>call <SID>ToggleCheckBox()<CR> # 英単語は`q`のあとは必ず`u`だから`q`をプレフィックスにする手もありか? # そもそも`q`が押しにくいか… cnoremap qq <C-f> # syntax固有の追加強調 def ClearMySyntax() for id in get(w:, 'my_syntax', []) matchdelete(id) endfor w:my_syntax = [] enddef def AddMySyntax(group: string, pattern: string) w:my_syntax->add(matchadd(group, pattern)) enddef au vimrc Syntax * ClearMySyntax() # 「==#」とかの存在を忘れないように au vimrc Syntax javascript,vim AddMySyntax('SpellRare', '\s[=!]=\s') # 基本的にnormalは再マッピングさせないように「!」を付ける au vimrc Syntax vim AddMySyntax('SpellRare', '\<normal!\@!') #noremap <F1> <Cmd>smile<CR> #}}} ------------------------------------------------------- # ---------------------------------------------------------- # † あともう1回「これ使ってないな…」と思ったときに消す {{{ nnoremap <Space>a A # 最後の選択範囲を現在行の下に移動する nnoremap <expr> <Space>m '<Cmd>' .. getpos("'<")[1] .. ',' .. getpos("'>")[1] .. 'move ' .. getpos('.')[1] .. '<CR>' #}}} ------------------------------------------------------- # ---------------------------------------------------------- # デフォルトマッピングデー {{{ if strftime('%d') ==# '01' def DMD() notification#show("✨ Today, Let's enjoy the default key mapping ! ✨") imapclear mapclear enddef au vimrc VimEnter * DMD() endif #}}} ------------------------------------------------------- # ---------------------------------------------------------- # 色 {{{ def DefaultColors() g:rainbow_conf = { guifgs: ['#9999ee', '#99ccee', '#99ee99', '#eeee99', '#ee99cc', '#cc99ee'], ctermfgs: ['105', '117', '120', '228', '212', '177'] } g:rcsv_colorpairs = [ ['105', '#9999ee'], ['117', '#99ccee'], ['120', '#99ee99'], ['228', '#eeee99'], ['212', '#ee99cc'], ['177', '#cc99ee'] ] enddef au vimrc ColorSchemePre * DefaultColors() def MyMatches() if exists('w:my_matches') && !empty(getmatches()) return endif w:my_matches = 1 matchadd('String', '「[^」]*」') matchadd('Label', '^\s*■.*$') matchadd('Delimiter', 'WARN\|注意\|注:\|[★※][^\s()()]*') matchadd('Todo', 'TODO') matchadd('Error', 'ERROR') matchadd('Delimiter', '- \[ \]') # 全角空白、半角幅の円記号、文末空白 matchadd('SpellBad', ' \|¥\|\s\+$') # 稀によくtypoする単語(気づいたら追加する) matchadd('SpellBad', 'stlye') enddef au vimrc VimEnter,WinEnter * MyMatches() set t_Co=256 syntax on set background=dark silent! colorscheme girly #}}} ------------------------------------------------------- # ---------------------------------------------------------- # メモ {{{ # <F1> <S-F1>でフォルダを開く(win32) # <F2> MRU # <F3> # <F4> # <F5> 日付関係 # <F6> # <F7> # <F8> # <F9> # <F10> ヘッダ行を表示(あんまり使わない) # <F11> 行番号表示切替 # <F12> 折り返し表示切替 #}}} ------------------------------------------------------- if filereadable(expand('~/.vimrc_local')) source ~/.vimrc_local endif