Vim パワーはテキストオブジェクトと繰り返しが７割 - 脱初心者の一歩

Vim パワーはテキストオブジェクトと繰り返しが７割 - 脱初心者の一歩

７割だって？ ( 要出典 )
タイトルは勢いです

が、それでも vim の力のかなりの部分を占めるテキストオブジェクト等と繰り返しについて紹介します

`diw`: 1 単語を消すとか`yab`: かっこの中をコピーみたいな、ちょっと長くてしかもめちゃめちゃバリエーションがあって覚えづらそうなやつのことね

わかれば大したことないコマンドなのにパワーがすげーので、ぜひ使ってみよう！

本記事は以下の三本立てでお送りします

+ 範囲指定操作
+ テキストオブジェクト
+ 繰り返し

## 範囲指定操作
テキストオブジェクトと言いつつ本当はテキストオブジェクトではないんだけど似ている ( そして強力！ ) 機能も一緒に紹介するぞ
数字に続いてまたタイトル詐欺だったごめんよ :p

これには厳密な名前はないと思うので、便宜上範囲指定操作と言います

### 基本
基本的には次の**二手**で成立する

`操作` + `カーソル移動`

操作例

キー | 挙動                       | 覚え方                                                                
:--  | :--                        | :--                                                                   
v    | 選択                       | visual                                                                
d    | 削除                       | delete                                                                
y    | コピー                     | yank ( vim ではコピーをヤンクと言う )                                 
c    | 削除しつつインサートモード | んー、なんだろう？<br>極めて便利なのでこれだけは指に覚えさせてしまおう

カーソル移動例

キー    | 挙動                                             | 補足                                                                
:--     | :--                                              | :--                                                                 
h j k l | 上下左右                                         | 数字をつけるとその分動く<br>`4h`や`10k`等
^ と $  | 行頭と行末                                       | 
gg と G | 先頭行と末尾行                                   | 
n       | 次にヒットする検索ワードの先頭にジャンプする     | 検索は`/`等で行う                                                   
%       | 対応しているかっこにジャンプする                 | 
f       | カーソルより右の指定の文字に**上**にジャンプする | abcde の a にカーソルがあるときに<br>`fd`でカーソルが**d**の上になる
t       | カーソルより右の指定の文字の**左**にジャンプする | abcde の a にカーソルがあるときに<br>`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<br>余談だけどさっき調べるまで勝手に 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@`で @ の前を消してみたりして、そのあと別の行で`.`をしてみよう

コツは繰り返すつもりなら次の箇所への移動方法を考えて、**上手に移動する**か**適当に移動しても大丈夫な操作にする**か考えてみることだ

<details><summary>余談</summary><div>

vim のテキストオブジェクトはただ**文字リテラル**だけを見ているので、**関連のない別の変数**とか**文字列**の様な文法上関係ない単語でも同じ様に処理することができる
エディタの単語置換が有効な時も当然あるが、文法を考慮せずただ同じ字として扱った方がうまくいく場面も少なからずある

例えば mailService と smsService を mailModule と smsModule に変えたい、みたいなときは`/S`して`n`しながら`ct.Module`とでもした方がよっぽど早い

普段から**カーソルをどこに置いてどこまでを指定すると自分の望む都合の良い限定さになるか**考えてみると良い

慣れると頭使わなくてもできる様になるぞ
タッチタイピングと一緒だ

▲ 余談

</div></details>

テキストオブジェクトじゃあなくても上手に使えば`.`はいつでも使えるので、上手に使ってみよう

例えばただ`/function`と`n`でメソッドを転々とジャンプしながら`O@Override`と`.`でアノテーションを片っ端からつけていく、とかもできるからね

## 締め
範囲指定操作、テキストオブジェクト、繰り返し

どうですか？
難しいですか？

vim は投資だと思うんですよね

真剣に使おうと思ったらどうしたって数週間はメモ帳よりたどたどしくなります

ただそれを超えて二次関数的に生産性が上がるか上がらないかは、この 3 つの習得で決まります
勢いだけで適当言ってきましたが、これだけは断言できます

ひとりでも興味を持ってくれる人が増えれば嬉しいです

## こちらもよろしく
Vim および作業効率化でいくつかネタをしたためている、こっちも見てもらえると嬉しいな

+ [Vim ( & ideavim ) キーマップ設定ガイド](https://qiita.com/suzuki-hoge/items/8efa1203a3ccb7b0a2ec)
+ Vim パワーはテキストオブジェクトと繰り返しが７割 - 脱初心者の一歩 ( 本記事 )
+ Vim でテキストを "見る" - テキストを読む tips ( 仮 )
+ タブを支配せよ ( 仮 )