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 ( 仮 )
- タブを支配せよ ( 仮 )