これまで数回に渡ってカーソルを高速に移動させるための設定を取り上げてきた。今回もその流れで、カーソル移動の便利な設定を紹介する。Vimでテキストを編集しているときに、カーソルを自在に移動できるかどうかは作業効率に直接関係する要素だ。ここに学習コストをかけることは、将来に渡って発生するテキスト編集の時間を短縮することにつながる。最初は時間をかけてでも、この部分はマスターしておきたい。

行末/行頭でのカーソルの回り込み

Vimのデフォルトの設定では、行をまたいだカーソルの移動は行われないようになっている。例えば、次のように行末にカーソルがある状態で「l」キーを押すとしよう。気持ちとしては、カーソルは次の行の先頭へ移動してほしいのだが、Vimでは何も起こらない。

行末で「l」キーを押しても、カーソルは次の行へ移動してくれない

これは行頭でも同じだ。行頭で「h」キーを入力しても、デフォルトのVimではうんともすんとも言わない。現在のエディタやワープロソフトの多くは行末や行頭でカーソルキーを押せば前の行や次の行へカーソルが移動する。この動きが現在では自然だ。このため、行で移動が制限されるVimのデフォルトの動きは不自然に思える。

行頭で「h」を押しても前の行へカーソルは移動してくれない

ここで、本連載で以前紹介したカーソル周り込みの設定を使ってみよう。

set whichwrap=b,s,[,],<,>,~,h,l

これでカーソルが行頭や行末で回り込むようになる。

行末で「l」と入力すれば次の行へカーソルが移動する

行頭で「h」と入力すれば前の行へカーソルが移動する

このVimのデフォルト動作は、現在では不自然に感じることが多いと思う。そのため、Vimを解説する際に早々に取り上げたのだが、今回改めてカーソル周りの動きに関して設定を見直してみよう。

行をまたがないカーソルの上下移動

今度は行をまたがないカーソルの移動を見てみよう。次に示すのは、Vimのデフォルトの動作だ。1行が複数行にまたがるような長い行だった場合、「j」や「k」でカーソルを上下方向へ移動させると、当然ながらカーソルは行を超えて次の行や前の行へ移動する。

「j」キーを押す

カーソルは次の行へ移動する

しかし、である。最近のエディタやワープロソフトの動作としては、これは不自然だ。多くの場合、カーソルは見た目上の下方向へ移動し、実際の「次の行」へは移動しない。視覚的に下の部分へカーソルが移動することを期待するのだ。

Vimではビジュアル的に上下に移動するために「gj」と「gk」というキーが用意されている。どちらかと言えばこちらの動作のほうが、感覚的に自然だと思う。そこで、次のように「gj」と「gk」を「j」と「k」として使用する設定を行ってみよう。

nnoremap k gk
nnoremap gk k
nnoremap j gj
nnoremap gj j

これで「j」と「k」が実際には「gj」と「gk」として機能するようになる。こちらの動作のほうが自然に感じるはずだ。

「j」を押す

設定で「gj」が動作するので、ビジュアル的に下の方向へカーソルが移動する

この設定ではデフォルトの「j」と「k」は「gj」と「gk」で機能するようになっているので、行を超えて移動させる場合には「gj」や「gk」を使えばよい。設定してみて何か不都合があれば、設定を削除すればよいだけだ。

テキストによっては、1行の文字列が見た目では複数行に渡ることは珍しくない。特にHTMLではそういうことが多いし、XMLでもそうしたデータは多い。通常のテキストファイルでも、1段落分をそのまま続けて入力することもある。そうした長いテキストを扱う際、この設定は結構役に立つだろう。

タブでお手軽カーソル移動

Vimではノーマルモードで「Tab」キーを押しても何も起こらない。

「Tab」キーを押しても何も起こらない

何の役割も割り振られていないのであれば、「Tab」キーをカーソル移動に当ててみよう。カーソルを移動させる際、ある程度先、もしくは後ろへカーソルを進めたいことがある。「l」や「h」でのカーソルの移動量をもうちょっと増やしたような移動方法がほしいといった感じだ。

その役割を「Tab」キーに担ってもらおう。次の設定で、「Tab」キーで15文字分カーソルが移動するようになる。

nnoremap <Tab> 15<Right>
nnoremap <S-Tab> 15<Left>

これで「Tab」キーで15文字分左へ、「Shift」+「Tab」キーで15文字分右へ移動するようになる。

「Tab」キーを押す

カーソルが15文字分進む。今度は「Shift」+「Tab」キーを押す

カーソルが15文字分戻る

移動する文字数は、自分の好みに応じて増やしたり減らしたりしてもらえればと思う。この方法では、これまで取り上げてきた方法のように自分の求める場所へピンポイントで移動したりはしない。しかし、何となく移動したい場所へ近づくことはできる。この方法が便利なのは、特に考える必要がない点にある。体になじませてしまえば、「進むときと戻るときはとりあえずこのキーを押せばよい」ものとして体が勝手に動くようになるため、結果的に高速に移動できるようになる。

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 <Tab> 15<Right>
nnoremap <S-Tab> 15<Left>
nnoremap vis ?\(。\\|\.\\|^$\\|>\)<CR>lv/\(。\\|\.\\|^$\\|<\\|:\)<CR>
nnoremap "" /""<CR>l
nnoremap '' /''<CR>l
nnoremap NN /<\([^>/]\+\)><\/\1><CR>/<<CR>
nnoremap MM ?<<CR>h?<\([^>/]\+\)><\/\1><CR>/<<CR>