前回は「di”」「di’」「di]」のような操作方法でクォーテーションや括弧のなかを一気に削除する方法を紹介した。機能としては削除だが、操作の本質はビジュアル選択だ。「d」が選択と削除の両方を意味している。こうしたことからもわかるように、Vimではビジュアル選択のスキルを高めることが操作効率を引き上げる上で大切だ。

今回はこのビジュアル選択機能の一つとして「一文」を選択する方法を取り上げる。実は、この機能は英語のテキストに対しては効果的なのだが、日本語に対してはいまいち役に立たない。今回は模擬的にだが、日本語でも似たような処理ができるようにカスタマイズする方法を紹介する。

「vis」で「一文」選択

まず、本来の動作を見てみよう。次のような英文があったとする。

今回使用するサンプルテキスト

カーソルをテキストのどこかに置いた状態で、「vis」と入力すると、次のように「一文」が選択される。

「vis」で一文選択

「vis」は「v」は「ビジュアル選択」、「i」は「内側を」、「s」は「一文の」という指定となり、「一文の内側をビジュアル選択せよ」という命令になる。一文をまるごと削除する場合はサクッと削除するよりも、いったん選択された範囲を確認してから削除したいことが多い。「vis」はその点で便利な命令だ。

前回説明したように範囲は「i」ではなく「a」も使える。その場合には「vas」ということになる。選択範囲が「vis」のときより広がる。用途と好みの問題ではあるが、個人的には「vis」のほうが使い勝手はよいのではないかと思う。とりあえず、今回は「vis」で見ていこう。

当然ながら、同じ機能は日本語でも同じように動作してほしい。ということで、同じことを日本語のテキストでやってみよう。例として、次のようなテキストを使用する。

サンプルとして使う日本語テキスト

実行すると次のようになる。

「vis」の実行結果

一文ではなく、ほとんど一段落が選択されてしまっている。「vis」というか「vip」だ。「v」は「ビジュアル選択」、「i」は「内側を」、「p」は「段落の」という意味だ。本来なら、次のような選択が行われてほしい。

期待する結果

では、日本語でも似たような選択が行われるようにしてみよう。

「viS」で「一文」選択

結論から書くと、設定などで「vis」の動作を想定通りにすることはできない、という調査結果になった(もしかするとできるかもしれないが、本稿執筆時点ではそこは見つけられなかった。どうも処理がハードコーディングされているような印象がある)。

ということで、模擬的に日本語に対して「vis」が機能するようなショートカットを設定することにする。要するに、カーソルがある位置から以下のような処理をすればよい。

  1. その文の先頭に移動
  2. ビジュアル選択を開始
  3. その文の末尾に移動


これらの処理については、これまでに本連載で取り上げてきた内容を使えば実現できる。上記をもう少し詳しくすると、次のような手順となる。

  1. 1つ前の文の末尾に移動
  2. 一文後方へ移動
  3. ビジュアル選択を開始
  4. その文の末尾に移動


ここではざっくりと「1つ前の文の末尾」を「『。』または空行、または『>』」と定義することにする。すると、次のような設定で「日本語の一文」を何となく選択できるようになる。

nmap viS ?\(>\\|。\\|\n\n\)<CR>l<C-v>/。<CR>

これだと影響範囲が広すぎるということであれば、特定のファイルのときにだけこの設定が機能するようにするなど、範囲を絞ればよいだろう。以下は、「typescript.xml」のときだけ「viS」で日本語の一文を選択する設定だ。

autocmd BufNewFile,BufRead typescript.xml nmap viS ?\(>\\|。\\|\n\n\)<CR>l<C-v>/。<CR>

この設定を有効にした状態で「viS」を実行すると次のようになる。

「viS」の実行結果

できれば「vis」で上記のように機能してほしかったのだが、それについては別の方法を考えることにする。とりあえずは第一歩としてこの設定でよしとしよう。正規表現の指定がかなり甘いので、使っていて不便を感じるようならここから徐々に正規表現で補足する範囲を広げていけばよいだろう。最初から完璧を追い求めず、とりあえず動作するようにしてから細かく改修していく。これが設定をうまく成長させていくポイントである。

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

プラグインを使うために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