Vim パワーはテキストオブジェクトと繰り返しが7割 - 脱初心者の一歩
7割だって? ( 要出典 )
タイトルは勢いです
が、それでも vim の力のかなりの部分を占めるテキストオブジェクト等と繰り返しについて紹介します
diw: 1 単語を消すとかyab: かっこの中をコピーみたいな、ちょっと長くてしかもめちゃめちゃバリエーションがあって覚えづらそうなやつのことね
わかれば大したことないコマンドなのにパワーがすげーので、ぜひ使ってみよう!
本記事は以下の三本立てでお送りします
- 範囲指定操作
- テキストオブジェクト
- 繰り返し
範囲指定操作
テキストオブジェクトと言いつつ本当はテキストオブジェクトではないんだけど似ている ( そして強力! ) 機能も一緒に紹介するぞ
数字に続いてまたタイトル詐欺だったごめんよ :p
これには厳密な名前はないと思うので、便宜上範囲指定操作と言います
基本
基本的には次の二手で成立する
操作 + カーソル移動
操作例
| キー | 挙動 | 覚え方 |
|---|---|---|
| v | 選択 | visual |
| d | 削除 | delete |
| y | コピー | yank ( vim ではコピーをヤンクと言う ) |
| c | 削除しつつインサートモード | んー、なんだろう? 極めて便利なのでこれだけは指に覚えさせてしまおう |
カーソル移動例
| キー | 挙動 | 補足 |
|---|---|---|
| h j k l | 上下左右 | 数字をつけるとその分動く4hや10k等 |
| ^ と $ | 行頭と行末 | |
| gg と G | 先頭行と末尾行 | |
| n | 次にヒットする検索ワードの先頭にジャンプする | 検索は/等で行う |
| % | 対応しているかっこにジャンプする | |
| f | カーソルより右の指定の文字に上にジャンプする | abcde の a にカーソルがあるときにfdでカーソルがdの上になる |
| t | カーソルより右の指定の文字の左にジャンプする | abcde の a にカーソルがあるときにtdでカーソルがcの上になる |
カーソル移動はそもそも重宝するので、それぞれ単体を普段から使う様にしておこう
細かい説明
基本的には次の二手で成立する
操作+カーソル移動
範囲指定操作は、操作を、カーソルのある位置を始点とし、カーソル移動でジャンプする先を終点として実行する、という挙動になる
下のテキストを例に説明しよう
mailService.apply(body)
reportService
ちなみに undo はuで redo はctrl + rだ、がんがん手元で試してみよう
例 $
. にカーソルがあるときにd$とすると削除 + ここから行末なのでこうなる
mailService
reportService
dではなくてyだとコピーなのでy$で.apply(body)がクリップボードに入る
下の行でpすればこうなる
mailService.apply(body)
reportService.apply(body)
例 %
( にカーソルがあるときにd%すると削除 + 対応するかっこまでなのでこうなる
mailService.apply
reportService
例 h j k l
mail の m にカーソルを合わせてd4lで削除 + 4 右なのでこうなる
ちなみにd4lは 3 文字だけど、4lで一手なのでd4lは二手ね
Service.apply
reportService
ただ、文字数を意識するのは辛いので、大抵の場合は後述する t / f の方を推奨する
他には例えば 1 行目でdjすると削除 + 下の行なのでこうなる
カーソル移動を2jとかにすれば任意行をdできる
例 t / f
body の b にカーソルを合わせてdt)すると削除 + )の左までなので body が消える
mailService.apply()
reportService
mail の m にカーソルを合わせてdf.すると削除 + .の上までなのでmailService.が消える
apply(body)
reportService
例 c
同じく mail の m にカーソルを合わせてctSなら削除しつつインサートモード + Sの左までなので mail を消しつつ字を書ける
ctSsmsとでもすると、こうなる
smsService.apply(body)
reportService
整理
こうやって砕いて見ると普段使っているv, y, d, cとf, tの組み合わせくらいしかないので、ビビるほど覚えることが多いわけではないでしょう?
自分がどの範囲を選んだのか確認したいときは、操作をvにするとわかりやすいぞ
テキストオブジェクト
二手の操作には少し慣れただろうか?
続くテキストオブジェクトは、一定のルールで指定されるひとまとまりのテキストのことで、操作は三手で行われる
基本
操作 + 囲み方 + 範囲で成立する
操作例
ここは範囲指定操作の操作と全く同じなので、問題ないだろう
囲み方
これは抜粋ではなくてこれで全部だ
| キー | 挙動 | 覚え方 |
|---|---|---|
| i | 範囲の内側 | inner |
| a | 範囲の境を含む | 冠詞の a 余談だけどさっき調べるまで勝手に around だと思ってた |
範囲
| キー | 挙動 | 補足 / 覚え方 |
|---|---|---|
| w | 単語 | word |
| 記号 | それで囲まれた範囲 | " や ( や [ や ` 等 |
| t | タグ | tag |
他にもいくつかあるので興味がある人は:tab h text-objectsでマニュアルを見てみよう
細かい説明
操作+囲み方+範囲で成立する
また例を見ながら上の説明を噛み砕いてみる
今度はこんなテキストを用意しよう
List<String> xs = ["<h2>$title</h2>", 'Hey John', 'thanks']
print(render(xs, 'welcome'))
例 w
thanks の上どこかにカーソルがあるときにdiwとすると削除 + 内側 + 単語なのでこうなる
List<String> xs = ["<h2>$title</h2>", 'Hey John', '']
print(render(xs, 'welcome'))
冒頭で例に出したdiw: 1 単語消すってのはこれのことだ
ちなみに 1 単語消えるので Hey の上で実行するとこうなってしまう
List<String> xs = ["<h2>$title</h2>", ' John', 'thanks']
print(render(xs, 'welcome'))
例 記号
Hey の上どこかにカーソルがあるときにdi'とすると削除 + 内側 + 'なのでこうなる
List<String> xs = ["<h2>$title</h2>", '', 'thanks']
print(render(xs, 'welcome'))
title あたりにカーソルを置いてdi"とするとこうなるし
List<String> xs = ["", 'Hey John', 'thanks']
print(render(xs, 'welcome'))
同じ位置でdi[とすればこうなる
List<String> xs = []
print(render(xs, 'welcome'))
String の上でci<Objectとすれば削除してインサートモード + 内側 + < + Objectなのでこうなる
List<Object> xs = ["<h2>$title</h2>", 'Hey John', 'thanks']
print(render(xs, 'welcome'))
これは大変便利だ、vim を使うならぜひとも習得したい
例 i / a
thanks の上どこかにカーソルがあるときのdi'は上で述べた通りこうなる
List<String> xs = ["<h2>$title</h2>", 'Hey John', '']
print(render(xs, 'welcome'))
同じ場所でda'だと'も含むのでこうなる
List<String> xs = ["<h2>$title</h2>", 'Hey John',]
print(render(xs, 'welcome'))
ちなみにさっきの Hey の上でのdiwをdawにすると単語の境 ( = 半角空白 ) も消えるのでこうなる
List<String> xs = ["<h2>$title</h2>", 'John', 'thanks']
print(render(xs, 'welcome'))
ただ多分iの方がよっぽと多用するのでaはついでくらいで覚えておけば良いと思う
例 タグ
title の上どこかにカーソルがあるときにditとすると削除 + 内側 + タグなのでこうなる
List<String> xs = ["<h2></h2>", 'Hey John', 'thanks']
print(render(xs, 'welcome'))
これは拡張子とかは関係なく文字列がそうなっていれば使える
人によっては重宝する、かもしれない
例 入れ子
入れ子になっていても結構良い感じで動いてくれる
welcome の上でdi(をすればこうなるし
List<String> xs = ["<h2>$title</h2>", 'Hey John', 'thanks']
print(render())
render の上でdi(ならこうなる
List<String> xs = ["<h2>$title</h2>", 'Hey John', 'thanks']
print()
この辺は数回自分の手で試せば感覚でわかるはずだ
カーソルを色々動かしながらvi(とva(をやって目で見てみることをおすすめする
繰り返し
最後に繰り返しだ、もう覚えるキーはあとたった 1 つだけだ
.を押すと直前の1 操作が今のカーソルの場所で実行される
わかりやすい例だと、xで字を消した後は.でも字が消せたり、a,でカンマを入力した後は.でカンマが入力できる
んで、察しの良い方ならa,の例でお気づきかもしれないが、1 操作は 1 キーではないのだ
範囲指定操作やテキストオブジェクトの操作も1 操作なのだ
最後にこんなテキストでいくつか例を示そう
mailService.send(body, "user@com")
smsService.notice(body, "user@corp")
例えばカーソルが 1 行目左端にあるとして、df.でmailServiceが消えるのは先に述べた通りだが、
そのままカーソルを下に持って行き.を押すと今度はdf.の繰り返しによりsmsServiceが消える
send(body, "user@com")
notice(body, "user@corp")
違う文字だけど.で同じ様に処理できているのがわかるだろうか
じゃあ今度は body の b の上でct,messageとしてみよう
変数名を body から message に変えたい、という感じだ
ん?変数名を変えるならエディタのリファクタリング機能で良い?
一理あるな ( 速さと手数は慣れ次第だが )
じゃあ com と corp を org に変えるということにしよう
1 行目の com の c の上でct"orgとして、カーソルをjで下に動かして.してみよう
mailService.send(body, "user@org")
smsService.notice(body, "user@corg")
...うまくいかないな?
これは繰り返したい操作の結果がカーソル位置に左右されているからだ
ct"は範囲指定部がtなので、カーソルより左はそのまま残ってしまう
対処方法は大きく 2 つある
1 つは/coで co を検索してnで co にカーソルをジャンプさせながら.する方法だ
c にカーソルが乗ればct"で正しく選択できる
ただしこれはうまくジャンプできる単語でかつどちらも"で区切られていないとうまくいかない
もう 1 つはテキストオブジェクトを使う方法だ
ct"orgではなくciworgであれば1 単語を消して org と打つのでカーソルが単語に乗ってさえいれば問題ない
mailService.send(body, "user@org")
smsService.notice(body, "user@org")
thanks の上どこかにカーソルがあるときに
diwと...
実はテキストオブジェクトの説明ではずっと上どこかと言っていたのだが、こういう違いがあるのだ
( の中をdi(で消してみたり、" の中をdi"で消してみたり、dt@で @ の前を消してみたりして、そのあと別の行で.をしてみよう
コツは繰り返すつもりなら次の箇所への移動方法を考えて、上手に移動するか適当に移動しても大丈夫な操作にするか考えてみることだ
余談
vim のテキストオブジェクトはただ文字リテラルだけを見ているので、関連のない別の変数とか文字列の様な文法上関係ない単語でも同じ様に処理することができる
エディタの単語置換が有効な時も当然あるが、文法を考慮せずただ同じ字として扱った方がうまくいく場面も少なからずある
例えば mailService と smsService を mailModule と smsModule に変えたい、みたいなときは/Sしてnしながらct.Moduleとでもした方がよっぽど早い
普段からカーソルをどこに置いてどこまでを指定すると自分の望む都合の良い限定さになるか考えてみると良い
慣れると頭使わなくてもできる様になるぞ
タッチタイピングと一緒だ
▲ 余談
テキストオブジェクトじゃあなくても上手に使えば.はいつでも使えるので、上手に使ってみよう
例えばただ/functionとnでメソッドを転々とジャンプしながらO@Overrideと.でアノテーションを片っ端からつけていく、とかもできるからね
締め
範囲指定操作、テキストオブジェクト、繰り返し
どうですか?
難しいですか?
vim は投資だと思うんですよね
真剣に使おうと思ったらどうしたって数週間はメモ帳よりたどたどしくなります
ただそれを超えて二次関数的に生産性が上がるか上がらないかは、この 3 つの習得で決まります
勢いだけで適当言ってきましたが、これだけは断言できます
ひとりでも興味を持ってくれる人が増えれば嬉しいです
こちらもよろしく
Vim および作業効率化でいくつかネタをしたためている、こっちも見てもらえると嬉しいな
- Vim ( & ideavim ) キーマップ設定ガイド
- Vim パワーはテキストオブジェクトと繰り返しが7割 - 脱初心者の一歩 ( 本記事 )
- Vim でテキストを "見る" - テキストを読む tips ( 仮 )
- タブを支配せよ ( 仮 )