Vimの操作では、ノーマルモードとビジュアル選択モードの操作性の統一も大切だ。Vimは文字を入力するモードと、文字を編集するモードが明確に分けられている。そのため、本連載で紹介してきたようなカーソルの移動が可能なのだが、これまでの設定はノーマルモードを前提としたものであったため、ビジュアルモードでは同じように機能しない部分がある。ノーマルモードでのカーソルの高速移動に慣れてくると、同じ操作がビジュアルモードで使えないのはかなりストレスがたまるだろう。今回はこの部分を改善していく。

ビジュアルモードで「(」と「)」で移動

前回、一文単位での移動である「(」と「)」を日本語の句読点に対しても機能するようにする設定を取り上げた。これが使えると、カーソルの移動が少し早くできるようになる。この移動速度に慣れてくると、ビジュアル選択モードでも同じように移動できるようにしたいと思うはずだ。

本連載では以前、「vis」での一文選択を日本語文に対しても機能するように拡張する設定を取り上げたが、visだと一文が全選択されてしまう。だが、カーソルのあるところからその文末まで、または、カーソルのあるところから文頭までを選択してコピーしたり、削除したりしたいこともある。そんなとき、visでは選択範囲が広すぎるのだ。

となると、ビジュアル選択モードに入ってから「(」または「)」で句読点へカーソルが移動してくれると嬉しい。そこで、ここでは次の設定で機能を追加した。

vnoremap ( hh?\(。\\|、\\|[.:][ \t\n]\)<CR>l
vnoremap ) /\(。\\|、\\|[.:][ \t\n]\\|^\n\)<CR>

動作を確認してみよう。まず、「Ctrl」+「v」で文字単位のビジュアル選択モードへ移行する。

「Ctrl」+「v」で文字単位のビジュアル選択モードへ移行

次に「)」を入力する。デフォルトでは「)」を入力しても意図したようには動かないのだが、上記を設定していれば、次のように日本語の次の句読点へカーソルが移動する。

「)」で次の句読点へカーソルが移動する

「)」で次の句読点へカーソルが移動する

「)」で次の句読点へカーソルが移動する

想定通りに機能している。今度は「(」の動作を確認してみよう。まず、「Ctrl」+「v」で文字単位のビジュアル選択モードへ移行する。

「Ctrl」+「v」で文字単位のビジュアル選択モードへ移行

「(」で1つ前の句読点へカーソルが移動することを確認できる。

「(」で前の句読点へカーソルが移動する

「)」で次の句読点へカーソルが移動する

「)」で次の句読点へカーソルが移動する

ビジュアル選択モードで選択処理を高速化できるとかなり気持ちが良い。文字の選択は、編集中に行うことが多く、特定の場所から文の先頭まで、または、文末までを選択して削除したり、削除してから別の文に貼り付けたりといった細かい作業が発生する。この選択処理を素早くさばくことができると、かなりの時短になるはずだ。ぜひとも身に付けていただきたい。

ビジュアルモードで「Ctrl」+「A」と「Ctrl」+「E」で移動

前回、行頭に移動する方法と行末に移動する方法として「Ctrl」+「A」と「Ctrl」+「E」を取り上げた。これはVimではデフォルト設定されていないショートカットキーなのだが、これらのキーを行頭/行末への移動ショートカットに設定しているアプリケーションは多い。そのため、Vimでもこの設定を有効にしておくと、ほかのアプリケーションと同じ操作ができるようになるので使いやすくなるだろう、という説明をした。

当然、同じロジックはビジュアル選択モードにおいても当てはまる。ビジュアル選択モードでも「Ctrl」+「A」と「Ctrl」+「E」で行頭/行末へ移動してくれたほうが操作に一貫性が生まれる。この設定は次の通りだ。

vnoremap <C-a> <Home>
vnoremap <C-e> <End>

動作を確認してみよう。まず、「Ctrl」+「v」でビジュアル選択モードへ入る。

「Ctrl」+「v」で文字単位のビジュアル選択モードへ移行

この状態で「Ctrl」+「A」を押せばカーソルは行頭へ移動し、「Ctrl」+「E」を押せばカーソルは行末へ移動する。

「Ctrl」+「A」でカーソルは行頭へ移動

「Ctrl」+「A」でカーソルは行末へ移動

ノーマルモードと同じように動作ていることがわかる

ノーマルモードでのカーソル移動の高速化が進んできたら、次はビジュアル選択モードだ。こちらに関してもノーマルモードと同じ移動ができるように調整していくことで、より効率の良い編集が可能になる。

ビジュアルモードでw qで移動

Vimにおける単語単位の移動は「w」と「b」が代表的だ。「w」で次の単語の頭、「b」で前の単語の頭へ移動していく。前回はほかのショートカットキーと同じように前後の移動を隣のキーに割り当てようということで、「b」の機能を「q」に割り当てた。これで「q」「w」という隣り合ったキーが「前の単語の頭へ移動」「次の単語の頭へ移動」という処理になり、手癖で単語単位の移動が可能になって便利だ、という話をした。

ビジュアル選択モードでも「w」「b」による単語間移動が可能だ。しかし、当然だが「q」では移動できない。ということで、ビジュアル選択モードでも「q」で「b」と同じ移動が可能になるように次の設定を追加する。

vnoremap q b

これでビジュアル選択モードにおいても「w」「q」で単語間を移動できるようになる。ビジュアル選択モードで手早く狙った範囲を選択できると範囲調整でミスをすることも減るし、お薦めの設定だ。

このようにVimではカーソルの移動に関してかなり自由に設定を行うことができる。編集するファイルの種類ごとに移動したい場所というのは変わるものだが、Vimではそれに適したショートカット移動を設定することができる。なかなかここを設定する方は少ないかと思うが、これを設定するとしないとでは、操作の時間が大きく違ってくる。ぜひとも繰り返し必要になる移動パターンを整理し、ショートカットで手早く移動できるように取り組んでもらいたい。

付録: 使っている設定ファイルとセットアップ方法

プラグインを使うためにDeinをセットアップする方法

mkdir -p ~/.cache/dein
cd ~/.cache/dein/
curl https://raw.githubusercontent.com/Shougo/dein.vim/master/bin/installer.sh > installer.sh
sh ./installer.sh .
rm ./installer.sh

本連載で使っている~/.vimrcファイル

"dein Scripts=============================
if &compatible
  set nocompatible               " Be iMproved
endif

" Required:
set runtimepath+=~/.cache/dein/./repos/github.com/Shougo/dein.vim

" Required:
if dein#load_state('~/.cache/dein/.')
  call dein#begin('~/.cache/dein/.')

  " Let dein manage dein
  " Required:
  call dein#add('~/.cache/dein/./repos/github.com/Shougo/dein.vim')

  " Add or remove your plugins here
  call dein#add('junegunn/seoul256.vim')
  call dein#add('vim-airline/vim-airline')
  call dein#add('vim-airline/vim-airline-themes')
  call dein#add('preservim/nerdtree')
  call dein#add('tpope/vim-commentary')
  call dein#add('tpope/vim-fugitive')
  call dein#add('fholgado/minibufexpl.vim')
  call dein#add('dense-analysis/ale')
  call dein#add('junegunn/fzf', {'build': './install --all'})
  call dein#add('junegunn/fzf.vim')
  call dein#add('sheerun/vim-polyglot')
  call dein#add('junegunn/vim-easy-align')

  " Required:
  call dein#end()
  call dein#save_state()
endif

" Required:
filetype plugin indent on
syntax enable

" If you want to install not installed plugins on startup.
if dein#check_install()
  call dein#install()
endif

" seoul256
let g:seoul256_background = 233
colo seoul256

" vim-airline
let g:airline_powerline_fonts = 1
let g:airline_theme = 'molokai'

" NERDTree
"  <C-o> open NERDTree
nnoremap <silent> <C-o> :NERDTreeToggle<CR>

" minibufexpl
nnoremap <silent> bn :<C-u>:bnext<CR>
nnoremap <silent> b1 :<C-u>:b1<CR>
nnoremap <silent> b2 :<C-u>:b2<CR>
nnoremap <silent> b3 :<C-u>:b3<CR>
nnoremap <silent> b4 :<C-u>:b4<CR>
nnoremap <silent> b5 :<C-u>:b5<CR>
nnoremap <silent> b6 :<C-u>:b6<CR>
nnoremap <silent> b7 :<C-u>:b7<CR>
nnoremap <silent> b8 :<C-u>:b8<CR>
nnoremap <silent> b9 :<C-u>:b9<CR>

" fzf
nnoremap <silent> fzf :Files<CR>
nnoremap <silent> ls :Buffers<CR>

" vim-easy-align
xmap ga <Plug>(EasyAlign)
nmap ga <Plug>(EasyAlign)

"End dein Scripts=========================

set number
syntax on
set whichwrap=b,s,[,],<,>,~,h,l
set cursorline
set incsearch
set hlsearch
set ignorecase
nnoremap k gk
nnoremap gk k
nnoremap j gj
nnoremap gj j
nnoremap q b
vnoremap q b
nnoremap <Tab> 15<Right>
nnoremap <S-Tab> 15<Left>
nnoremap vis ?\(。\\|\.\\|^$\\|>\)<CR>lv/\(。\\|\.\\|^$\\|<\\|:\)<CR>
nnoremap >< /<\([^>/]\+\)><\/\1><CR>/<<CR>
nnoremap <> ?<<CR>h?<\([^>/]\+\)><\/\1><CR>/<<CR>
nnoremap ( ?\(。\\|、\\|[.:][ \t\n]\)<CR>h?\(。\\|、\\|[.:][ \t\n]\)<CR>l
vnoremap ( hh?\(。\\|、\\|[.:][ \t\n]\)<CR>l
nnoremap ) /\(。\\|、\\|[.:][ \t\n]\\|^\n\)<CR>l
vnoremap ) /\(。\\|、\\|[.:][ \t\n]\\|^\n\)<CR>
nnoremap "" /""<CR>l
nnoremap '' /''<CR>l
nnoremap :: ?""<CR>l
nnoremap ;; ?''<CR>l
nnoremap <C-a> <Home>
inoremap <C-a> <Home>
cnoremap <C-a> <Home>
vnoremap <C-a> <Home>
nnoremap <C-e> <End>
inoremap <C-e> <End>
cnoremap <C-e> <End>
vnoremap <C-e> <End>