どうも、ご無沙汰しておりません Vimmer + Pythonista、 略して Vist... やめよう、こっちは縁起が悪い。 この記事は Vim Advent Calendar 2015 の 24日目の記事となります。
はじめに
自分が Mac OS X および Linux しか持っていないため、Windows で動かなかったらごめんなさい。
とりあえず書こうと思っていたものが未完成なので、他ではあまり見たことがない ~/.vimrc
のお便利設定をまとめます。
不要なデフォルトプラグインを止める
しょっぱなからぶっ飛んだ設定ですが、僕は不要なデフォルトプラグインをすべて止めています。 以下設定
let g:loaded_gzip = 1 let g:loaded_tar = 1 let g:loaded_tarPlugin = 1 let g:loaded_zip = 1 let g:loaded_zipPlugin = 1 let g:loaded_rrhelper = 1 let g:loaded_2html_plugin = 1 let g:loaded_vimball = 1 let g:loaded_vimballPlugin = 1 let g:loaded_getscript = 1 let g:loaded_getscriptPlugin = 1 let g:loaded_netrw = 1 let g:loaded_netrwPlugin = 1 let g:loaded_netrwSettings = 1 let g:loaded_netrwFileHandlers = 1
これを ~/.vimrc
の先頭に記載することで、以下のプラグインの読み込みが(完全には)行われません。
- GZip ファイルの展開・閲覧
- Tar ファイルの展開・閲覧
- Zip ファイルの閲覧・展開
--remote-wait
とかTOhtml
- Vimball の展開とかインストールとか
GetLatestVimScripts
コマンドとかNetrw
系全部
これらのプラグインは個人的には全く使わないため切ってあります。 「えーこんなの絶対使わないよ」という方は切ってもいいかも(デフォルトプラグインなのでサイドエフェクトがある可能性があります。バグっても自己責任で!)
GUI Vim 以外で matchparen をやめる
()
の対応などを表示してくれる matchparen
という機能ですが Terminal 上ではカーソルがとても見にくくなるため切ってあります。以下設定
if !has('gui_running') let g:loaded_matchparen = 1 endif
Vim 内部で利用する PATH
の設定
「Terminal から起動した場合は動くけど GUI で起動した場合は動かない」などの現象はだいたいこれです。
僕は関数を定義して指定されたパスが存在し、 $PATH
に存在していない場合のみ追加するようにしています。
これにより複数の OS でパスが異なる場合でもよしなに設定できます。
function! AddPath(pathlist) abort " {{{ let pathlist = split($PATH, s:delimiter) for path in map(filter(a:pathlist, 'v:val'), 'expand(v:val)') if isdirectory(path) && index(pathlist, path) == -1 call insert(pathlist, path, 0) endif endfor let $PATH = join(pathlist, s:delimiter) endfunction " }}} call AddPath([ \ '/usr/local/texlive/2013/bin/x86_64-linux', \ '/usr/local/texlive/2013/bin/x86_64-darwin', \ '~/.pyenv/bin', \ '~/.plenv/bin', \ '~/.rbenv/bin', \ '~/.ndenv/bin', \ '~/.pyenv/shims', \ '~/.plenv/shims', \ '~/.rbenv/shims', \ '~/.ndenv/shims', \ '~/.anyenv/envs/pyenv/bin', \ '~/.anyenv/envs/plenv/bin', \ '~/.anyenv/envs/rbenv/bin', \ '~/.anyenv/envs/ndenv/bin', \ '~/.anyenv/envs/pyenv/shims', \ '~/.anyenv/envs/plenv/shims', \ '~/.anyenv/envs/rbenv/shims', \ '~/.anyenv/envs/ndenv/shims', \ '~/.cabal/bin', \ '~/.vim/bundle/vim-themis/bin', \])
マウスのミドルクリックによる貼付けをやめる
たまにクリックしてしまってとても面倒なので完全に切ります
map <MiddleMouse> <Nop> map <2-MiddleMouse> <Nop> map <3-MiddleMouse> <Nop> map <4-MiddleMouse> <Nop> imap <MiddleMouse> <Nop> imap <2-MiddleMouse> <Nop> imap <3-MiddleMouse> <Nop> imap <4-MiddleMouse> <Nop>
入力モードでは Emacs 的な移動を可能にする
大体 Normal モードで移動するように体が慣れていますが、ターミナルなどでは Emacs キーバインディングが一般的なので入力中でも Emacs 風キーバインドで移動できるようにしています。
inoremap <C-a> <C-o>^ inoremap <C-e> <C-o>$ inoremap <C-f> <C-o>w inoremap <C-b> <C-o>b inoremap <C-d> <C-o>x " Emacs 的じゃないけど、これも inoremap <C-h> <C-o>h inoremap <C-j> <C-o>j inoremap <C-k> <C-o>k inoremap <C-l> <C-o>l
ウィンドウサイズを簡単に変更
デフォルトでは Ctrl-w <
と、連続で押すのは大変なので Shift
+ めったに使わない矢印キーを割り当てています。
nnoremap <S-Left> <C-w><<CR> nnoremap <S-Right> <C-w>><CR> nnoremap <S-Up> <C-w>-<CR> nnoremap <S-Down> <C-w>+<CR>
トグル系マッピング
設定のトグル用に <Leader>s
(Switch) を割り当てています。
ここで味噌なのは <Leader>s
に <Plug>(my-switch)
をまず割り当てて、その後 <Plug>(my-switch)
を使って実際のマッピングを割り当てているところです。こうすることにより :map
でマッピングを表示した際にわかりやすくなります(あと prefix 変えたくなったら簡単に変えられる)。
nnoremap <Plug>(my-switch) <Nop> nmap <Leader>s <Plug>(my-switch) nnoremap <silent> <Plug>(my-switch)s :<C-u>setl spell! spell?<CR> nnoremap <silent> <Plug>(my-switch)l :<C-u>setl list! list?<CR> nnoremap <silent> <Plug>(my-switch)t :<C-u>setl expandtab! expandtab?<CR> nnoremap <silent> <Plug>(my-switch)w :<C-u>setl wrap! wrap?<CR> nnoremap <silent> <Plug>(my-switch)p :<C-u>setl paste! paste?<CR> nnoremap <silent> <Plug>(my-switch)b :<C-u>setl scrollbind! scrollbind?<CR> nnoremap <silent> <Plug>(my-switch)y :call <SID>toggle_syntax()<CR> function! s:toggle_syntax() abort if exists('g:syntax_on') syntax off redraw echo 'syntax off' else syntax on redraw echo 'syntax on' endif endfunction
w!! で sudo で保存
便利すぎるので、結構他でも見ますが紹介。僕の場合は sudo.vim
で上書きしてしまいますが、プラグインをインストールしていない環境でも簡易的に使えるように設定してあります。
cabbr w!! w !sudo tee > /dev/null %
いい感じに Vim の mkview/loadview 機能を自動化する
Vim の mkview/loadview 機能を使うことで折りたたみのレベルやカーソル位置などを保持しておくことができ、上手に使うと便利なのですが、自動化すると誤爆が多いため手動で行っている方も多いと思います。 以下のように view が使えるか?使うべきか?みたいな関数を噛ませてやるといい感じに自動化出来ます。
function! s:is_view_available() abort " {{{ if !&buflisted || &buftype !=# '' return 0 elseif !filewritable(expand('%:p')) return 0 endif return 1 endfunction " }}} function! s:mkview() abort " {{{ if s:is_view_available() silent! mkview endif endfunction " }}} function! s:loadview() abort " {{{ if s:is_view_available() silent! loadview endif endfunction " }}} autocmd MyAutoCmd BufWinLeave ?* call s:mkview() autocmd MyAutoCmd BufReadPost ?* call s:loadview()
設定ファイルのリロード
Vim は設定ファイルを書くために利用する時間が比較的に(飛躍的に)長いエディタなので、ささっと書いてささっと試せるようにリロードをマッピングで行えるようにしています。
リロード中に関数の上書きが出来ないので has('vim_starting')
で囲っています。
if has('vim_starting') function s:reload_vimrc() abort execute printf('source %s', $MYVIMRC) if has('gui_running') execute printf('source %s', $MYGVIMRC) endif redraw echo printf('.vimrc/.gvimrc has reloaded (%s).', strftime('%c')) endfunction endif nmap <silent> <Plug>(my-reload-vimrc) :<C-u>call <SID>reload_vimrc()<CR> nmap <Leader><Leader>r <Plug>(my-reload-vimrc)
特定ファイルの親ディレクトリーにワンステップで移動する
特定ファイルの親ディレクトリーに移動したいことって結構あると思います。
手で打ってもいいのですが、割と面倒なので関数化しています。
また my-workon-post
というユーザー定義 autocmd を呼び出すことで任意の処理を実行できるようにしています(それを利用して Vimfiler のディレクトリを変更するようにしてる)。
function! s:workon(dir, bang) abort let dir = (a:dir ==# '' ? expand('%') : a:dir) " convert filename to directory if required if filereadable(dir) let dir = fnamemodify(expand(dir),':p:h') else let dir = fnamemodify(dir, ':p') endif " change directory to specified directory if isdirectory(dir) silent execute 'cd ' . fnameescape(dir) if a:bang ==# '' redraw | echo 'Working on: '.dir if v:version > 703 || (v:version == 703 && has('patch438')) doautocmd <nomodeline> MyAutoCmd User my-workon-post else doautocmd MyAutoCmd User my-workon-post endif endif endif endfunction autocmd MyAutoCmd VimEnter * call s:workon(expand('<afile>'), 1) command! -nargs=? -complete=dir -bang Workon call s:workon('<args>', '<bang>')
これを記載すると autocmd MyAutoCmd VimEnter * ...
の部分によって起動時にファイルが指定されていたら、そのファイルの親ディレクトリに自動的に移動します。また :Workon
というコマンドを定義しているので編集中のバッファで :Workon
とすると編集中のバッファの親ディレクトリに移動できます。
また、先にも述べましたが my-workon-post
というユーザー適宜 autocmd を呼び出しているので、例えば下記のように利用することが出来ます。このあたりは s:workon()
に書いても良いのですが、直交性を配慮した結果です。
" :Workon コマンド後などに Vimfiler が開いていた場合は :Workon で移動したディレクトリに " Vimfiler も移動する function! s:cd_all_vimfiler(path) abort let current_nr = winnr() try for winnr in filter(range(1, winnr('$')), \ "getwinvar(v:val, '&filetype') ==# 'vimfiler'") call vimfiler#util#winmove(winnr) call vimfiler#mappings#cd(a:path) endfor finally call vimfiler#util#winmove(current_nr) endtry endfunction autocmd MyAutoCmd User my-workon-post call s:cd_all_vimfiler(getcwd())
(番外編)Unite menu をむっちゃ便利にする
Shougoware 代表作である unite.vim には固定メニューを表示するための menu
ソースがあるのですが、ヘルプなどに書いてある方法はコマンドを直接実行する方法なので「あ、このファイル split で開きたい」とかに対応できません(ぜんぶ edit xxxxxx
って書くから)。
そこで以下の様なヘルパー関数を定義します。
function! s:register_filemenu(name, description, precursors) abort " {{{ " find the length of the longest name let max_length = max(map( \ filter(deepcopy(a:precursors), 'len(v:val) > 1'), \ 'len(v:val[0])' \)) let format = printf('%%-%ds : %%s', max_length) let candidates = [] for precursor in a:precursors if len(precursor) == 1 call add(candidates, [ \ precursor[0], \ '', \]) elseif len(precursor) >= 2 let name = precursor[0] let desc = precursor[1] let path = get(precursor, 2, '') let path = resolve(expand(empty(path) ? desc : path)) let kind = isdirectory(path) ? 'directory' : 'file' call add(candidates, [ \ printf(format, name, desc), \ path, \]) else let msg = printf( \ 'A candidate precursor must has 1 or more than two terms : %s', \ string(precursor) \) call add(candidates, [ \ 'ERROR : ' . msg, \ '', \]) endif endfor let menu = {} let menu.candidates = candidates let menu.description = a:description let menu.separator_length = max(map( \ deepcopy(candidates), \ 'len(v:val[0])', \)) if menu.separator_length % 2 != 0 let menu.separator_length += 1 endif function! menu.map(key, value) abort let word = a:value[0] let path = a:value[1] if empty(path) if word ==# '-' let word = repeat('-', self.separator_length) else let length = self.separator_length - (len(word) + 3) let word = printf('- %s %s', word, repeat('-', length)) endif return { \ 'word': '', \ 'abbr': word, \ 'kind': 'common', \ 'is_dummy': 1, \} else let kind = isdirectory(path) ? 'directory' : 'file' let directory = isdirectory(path) ? path : fnamemodify(path, ':h') return { \ 'word': word, \ 'abbr': printf('[%s] %s', toupper(kind[0]), word), \ 'kind': kind, \ 'action__path': path, \ 'action__directory': directory, \} endif endfunction " register to 'g:unite_source_menu_menus' let g:unite_source_menu_menus = get(g:, 'unite_source_menu_menus', {}) let g:unite_source_menu_menus[a:name] = menu endfunction " }}} " 使用例 call s:register_filemenu('shortcut', 'Shortcut menu', [ \ ['vim'], \ [ \ 'vimrc', \ fnamemodify(resolve($MYVIMRC), ':~'), \ ], \ [ \ 'gvimrc', \ fnamemodify(resolve($MYGVIMRC), ':~'), \ ], \ [ \ 'vimshrc', \ '~/.vim/vimshrc', \ ], \ [ \ 'vim', \ '~/.vim', \ ], \ [ \ 'bundle', \ '~/.vim/bundle', \ ], \ [ \ 'ftplugin', \ '~/.vim/ftplugin', \ ], \ ['zsh'], \ [ \ 'zshrc', \ '~/.config/zsh/.zshrc', \ ], \ [ \ 'rc/theme.dust.zsh', \ '~/.config/zsh/rc/theme.dust.zsh', \ ], \ [ \ 'rc/configure.applications.zsh', \ '~/.config/zsh/rc/configure.applications.zsh', \ ], \ [ \ 'zsh', \ '~/.config/zsh', \ ], \ ['tmux'], \ [ \ 'tmux.conf', \ '~/.tmux.conf', \ ], \ [ \ 'tmux-powerlinerc', \ '~/.tmux-powerlinerc', \ ], \ [ \ 'tmux-powerline.conf', \ '~/.config/tmux/tmux-powerline.conf', \ ], \ [ \ 'tmux', \ '~/.config/tmux', \ ], \ ['others'], \ [ \ 'gitconfig', \ '~/.gitconfig', \ ], \ [ \ 'gitignore', \ '~/.gitignore', \ ], \ [ \ 'pymolrc', \ '~/.pymolrc', \ ], \ [ \ 'vimperatorrc', \ '~/.vimperatorrc', \ ], \ [ \ 'latexmkrc', \ '~/.latexmkrc', \ ], \ [ \ 'jupyter custom.css', \ '~/.jupyter/custom/custom.css', \ ], \ [ \ 'jupyter custom.js', \ '~/.jupyter/custom/custom.js', \ ], \])
このヘルパー関数を利用して作成した Unite menu
では、Unite file
や Unite directory
のように right
アクションなどが有効になります。Unite menu
でファイルの開き方を指定したくて困っていた方は是非試してみてください。
おわりに
雑だ。だがあと 2 min しかない。仕方がない