vimのmark機能を使いやすくする

creation date 2015/02/14 18:10 last date modified 2015/02/14 21:02 | category: | tags: Vim
スポンサード リンク

vimのマークってあんまり活用してなかったなぁ。ということで使いやすいように .vimrc を改造したのでメモ。今回は

  • showmarksプラグインでマークを可視化
  • マークするアルファベットを自動的に決める
  • マークを一覧表示する
  • 次のマーク,前のマークに移動する
  • マークを一括削除する

ことが楽にできるようになった。さて,お膳立てはしたので後は習慣になるかどうか。

プラグインvim-showmarksの導入

マークがなんで使いづらいかって,どこに何をマークしたのかいちいち覚えていられないから。そこでわかりやすいように常にマーク位置と記号を表示してくれるプラグイン vim-showmarks を導入した。

ちなみに,vimには ShowMarksshowmarks の2つのプラグインがあって非常に紛らわしい。今回導入したのは全部小文字でマイナーな showmarks の方。 ShowMarks では他のプラグインとの相性のせいか,マークの位置を正しく認識できなかった 。

  1. ShowMarks - Visually shows the location of marks. : vim online : いろんなところで解説されてる有名なやつ
  2. showmarks - Display marks as signs (needs +sign feat ) : vim online : 今回扱う超マイナーなやつ

インストールは例によってNeoBundleを使う。

Neobundle 'jacquesbh/vim-showmarks'

使えるようになるEXコマンドは次のとおり。マークの変更はCursorholdイベント発生時に反映されるようだ。 updatetime は短めに設定しておこう。

:DoShowMarks
現在のバッファでマークを表示する
:DoShowMarks!
全てのバッファでマークを表示する
:NoShowMarks
現在のバッファでマークを表示しない
:NoShowMarks!
全てのバッファでマークを表示しない
:[count]ShowMarksOnce
[count]回のCursorholdイベントの間だけマークを表示する。(Cursorholdはautocomdのイベント。例えば, set updatetime=5000 でCursorholdは5000ミリ秒おきに発生するようになる)
:[count]PreviewMarks
[count]回のCursorholdイベントの間だけ,画面上部に:marks のように別ウィンドウで一覧表示する。

デフォルトでは起動時にマークを表示しないので,.vimrcにautocmdを書いて対応する。

" 起動時にマーク表示
aug show-marks-sync
        au!
        au BufReadPost * sil! DoShowMarks
aug END

また, let g:showmarks_marks = "abcdef" で表示されるマークを限定できる。ココらへんは ShowMarks と一緒かな。

自動的にマークする文字を決定する関数をつくる

vimのマーク機能をできるだけ活用してみる - Make 鮫 noise では, a~z のマークを順番に使ってくれる関数 AutoMarkrement() を制作してくれている。これは便利なのでそのまま使わせていただくことにする。ただし,マッピングだけは nnoremap <silent>[Mark]m :<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR> として, vim-showmarks を呼び出して瞬時に反映させるようにした。

これにより, mm のキー入力で自動的にマークできるようになった。

"----------------------------------------------
" vimのマーク機能をできるだけ活用してみる - Make 鮫 noise
" http://saihoooooooo.hatenablog.com/entry/2013/04/30/001908
" mを押すことで現在位置に対して自動的にアルファベットを割り振る
"----------------------------------------------
" マーク設定 : {{{
" 基本マップ
nnoremap [Mark] <Nop>
nmap m [Mark]
" 現在位置をマーク
if !exists('g:markrement_char')
    let g:markrement_char = [
    \     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    \     'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
    \ ]
endif
nnoremap <silent>[Mark]m :<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
function! s:AutoMarkrement()
    if !exists('b:markrement_pos')
        let b:markrement_pos = 0
    else
        let b:markrement_pos = (b:markrement_pos + 1) % len(g:markrement_char)
    endif
    execute 'mark' g:markrement_char[b:markrement_pos]
    echo 'marked' g:markrement_char[b:markrement_pos]
endfunction

これだけではアレなので便利そうな機能を幾つか追加してみる。AutoMarkrement() は愚直にaから順番にマークしていくだけ。以下で定義する :SetNextMarkChar を使えば, :SetNextMarkChar s で次にセットするアルファベットをsに変更できる。

" 次にマークする文字を設定するExコマンドを定義
command! -nargs=? SetNextMarkChar call s:set_next_mark_char(<f-args>)
function! s:set_next_mark_char(...)
  if a:0 >= 1
    let b:markrement_pos=index(g:markrement_char,a:1)-1
  else
    echo "Next:".g:markrement_char[b:markrement_pos+1]
  end
endfunction

毎回 :SetNextMarkChar と入力するのも面倒なので,キーマッピングで対処する。ここらへんベタ書きじゃなくてなんとかスマートな感じにできると嬉しいのだが...

" 次にマークする文字を設定する
nnoremap [Mark]sa :SetNextMarkChar a<CR>
nnoremap [Mark]sb :SetNextMarkChar b<CR>
nnoremap [Mark]sc :SetNextMarkChar c<CR>
nnoremap [Mark]sd :SetNextMarkChar d<CR>
nnoremap [Mark]se :SetNextMarkChar e<CR>
nnoremap [Mark]sf :SetNextMarkChar f<CR>
nnoremap [Mark]sg :SetNextMarkChar g<CR>
nnoremap [Mark]sh :SetNextMarkChar h<CR>
nnoremap [Mark]si :SetNextMarkChar i<CR>
nnoremap [Mark]sj :SetNextMarkChar j<CR>
nnoremap [Mark]sk :SetNextMarkChar k<CR>
nnoremap [Mark]sl :SetNextMarkChar l<CR>
nnoremap [Mark]sm :SetNextMarkChar m<CR>
nnoremap [Mark]sn :SetNextMarkChar n<CR>
nnoremap [Mark]so :SetNextMarkChar o<CR>
nnoremap [Mark]sp :SetNextMarkChar p<CR>
nnoremap [Mark]sq :SetNextMarkChar q<CR>
nnoremap [Mark]sr :SetNextMarkChar r<CR>
nnoremap [Mark]ss :SetNextMarkChar s<CR>
nnoremap [Mark]st :SetNextMarkChar t<CR>
nnoremap [Mark]su :SetNextMarkChar u<CR>
nnoremap [Mark]sv :SetNextMarkChar v<CR>
nnoremap [Mark]sw :SetNextMarkChar w<CR>
nnoremap [Mark]sx :SetNextMarkChar x<CR>
nnoremap [Mark]sy :SetNextMarkChar y<CR>
nnoremap [Mark]sz :SetNextMarkChar z<CR>

同様に, AutoMarkrement() を通して任意のアルファベットでマークしつつ vim-showmarks に反映させるショートカットキーを定義する。これで素のvimで ma の動作は mfa で再現できるようになった。

" 次にマークする文字を設定して,同時にマークする
nnoremap [Mark]fa :SetNextMarkChar a<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fb :SetNextMarkChar b<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fc :SetNextMarkChar c<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fd :SetNextMarkChar d<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fe :SetNextMarkChar e<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]ff :SetNextMarkChar f<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fg :SetNextMarkChar g<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fh :SetNextMarkChar h<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fi :SetNextMarkChar i<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fj :SetNextMarkChar j<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fk :SetNextMarkChar k<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fl :SetNextMarkChar l<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fm :SetNextMarkChar m<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fn :SetNextMarkChar n<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fo :SetNextMarkChar o<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fp :SetNextMarkChar p<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fq :SetNextMarkChar q<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fr :SetNextMarkChar r<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fs :SetNextMarkChar s<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]ft :SetNextMarkChar t<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fu :SetNextMarkChar u<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fv :SetNextMarkChar v<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fw :SetNextMarkChar w<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fx :SetNextMarkChar x<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fy :SetNextMarkChar y<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fz :SetNextMarkChar z<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>

マークを操作するプレフィックスは m と決めたので,マーク間の移動も m を使うことにしよう。

" 次/前のマーク
nnoremap [Mark]n ]`
nnoremap [Mark]p [`

一覧表示ももちろん m から呼び出す。

" vim-showmarksでマークを一覧表示
nnoremap [Mark]l :<C-u>Previewmarks<CR>

こちらは Vimのマークについて - MBA-HACK2 からいただいてきたマッピング。マークをすべて削除して vim-showmarks に反映させる。

" マークの全削除を行うコマンドを設定する
com! -bar MarksDelete sil :delm! | :delm 0-9A-Z | :wv! | :DoShowMarks
nn <silent>[Mark]d :MarksDelete<CR>
" }}}

まとめ

今回の改造をまとめとくと以下のようになる。

"==============================================
" マーク周りの改善
"==============================================
"----------------------------------------------
" jacquesbh/vim-showmarks
"----------------------------------------------
Neobundle 'jacquesbh/vim-showmarks'
" 最初からマークを表示する
aug show-marks-sync
        au!
        au BufReadPost * sil! DoShowMarks
aug END
"----------------------------------------------
" vimのマーク機能をできるだけ活用してみる - Make 鮫 noise
" http://saihoooooooo.hatenablog.com/entry/2013/04/30/001908
" mを押すことで現在位置に対して自動的にアルファベットを割り振る
"----------------------------------------------
" マーク設定 : {{{
" 基本マップ
nnoremap [Mark] <Nop>
nmap m [Mark]
" 現在位置をマーク
if !exists('g:markrement_char')
    let g:markrement_char = [
    \     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
    \     'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
    \ ]
endif
nnoremap <silent>[Mark]m :<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
function! s:AutoMarkrement()
    if !exists('b:markrement_pos')
        let b:markrement_pos = 0
    else
        let b:markrement_pos = (b:markrement_pos + 1) % len(g:markrement_char)
    endif
    execute 'mark' g:markrement_char[b:markrement_pos]
    echo 'marked' g:markrement_char[b:markrement_pos]
endfunction
" 次にマークする文字を設定するExコマンドを定義
command! -nargs=? SetNextMarkChar call s:set_next_mark_char(<f-args>)
function! s:set_next_mark_char(...)
  if a:0 >= 1
    let b:markrement_pos=index(g:markrement_char,a:1)-1
  else
    echo "Next:".g:markrement_char[b:markrement_pos+1]
  end
endfunction
" 次にマークする文字を設定する
nnoremap [Mark]sa :SetNextMarkChar a<CR>
nnoremap [Mark]sb :SetNextMarkChar b<CR>
nnoremap [Mark]sc :SetNextMarkChar c<CR>
nnoremap [Mark]sd :SetNextMarkChar d<CR>
nnoremap [Mark]se :SetNextMarkChar e<CR>
nnoremap [Mark]sf :SetNextMarkChar f<CR>
nnoremap [Mark]sg :SetNextMarkChar g<CR>
nnoremap [Mark]sh :SetNextMarkChar h<CR>
nnoremap [Mark]si :SetNextMarkChar i<CR>
nnoremap [Mark]sj :SetNextMarkChar j<CR>
nnoremap [Mark]sk :SetNextMarkChar k<CR>
nnoremap [Mark]sl :SetNextMarkChar l<CR>
nnoremap [Mark]sm :SetNextMarkChar m<CR>
nnoremap [Mark]sn :SetNextMarkChar n<CR>
nnoremap [Mark]so :SetNextMarkChar o<CR>
nnoremap [Mark]sp :SetNextMarkChar p<CR>
nnoremap [Mark]sq :SetNextMarkChar q<CR>
nnoremap [Mark]sr :SetNextMarkChar r<CR>
nnoremap [Mark]ss :SetNextMarkChar s<CR>
nnoremap [Mark]st :SetNextMarkChar t<CR>
nnoremap [Mark]su :SetNextMarkChar u<CR>
nnoremap [Mark]sv :SetNextMarkChar v<CR>
nnoremap [Mark]sw :SetNextMarkChar w<CR>
nnoremap [Mark]sx :SetNextMarkChar x<CR>
nnoremap [Mark]sy :SetNextMarkChar y<CR>
nnoremap [Mark]sz :SetNextMarkChar z<CR>
" 次にマークする文字を設定して,同時にマークする
nnoremap [Mark]fa :SetNextMarkChar a<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fb :SetNextMarkChar b<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fc :SetNextMarkChar c<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fd :SetNextMarkChar d<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fe :SetNextMarkChar e<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]ff :SetNextMarkChar f<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fg :SetNextMarkChar g<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fh :SetNextMarkChar h<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fi :SetNextMarkChar i<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fj :SetNextMarkChar j<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fk :SetNextMarkChar k<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fl :SetNextMarkChar l<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fm :SetNextMarkChar m<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fn :SetNextMarkChar n<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fo :SetNextMarkChar o<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fp :SetNextMarkChar p<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fq :SetNextMarkChar q<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fr :SetNextMarkChar r<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fs :SetNextMarkChar s<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]ft :SetNextMarkChar t<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fu :SetNextMarkChar u<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fv :SetNextMarkChar v<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fw :SetNextMarkChar w<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fx :SetNextMarkChar x<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fy :SetNextMarkChar y<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
nnoremap [Mark]fz :SetNextMarkChar z<CR>:<C-u>call <SID>AutoMarkrement()<CR>:DoShowMarks<CR>
" 次/前のマーク
nnoremap [Mark]n ]`
nnoremap [Mark]p [`
" 一覧表示
nnoremap [Mark]l :<C-u>Previewmarks<CR>
" マークの全削除を行うコマンドを設定する
com! -bar MarksDelete sil :delm! | :delm 0-9A-Z | :wv! | :DoShowMarks
nn <silent>[Mark]d :MarksDelete<CR>
" }}}

Comments !

social