作って覚える Vim script


## はじめに

Vimmerの皆さんこんにちは。
Vimに慣れてくると、色々カスタマイズしたくなりますよね。
でも、プラグインを作るのは大げさだし…と思っている人には
.vimrcでVim Scriptを書いてみるのがおすすめです。

### Vim Scriptとは
Vim script は Vim に組み込まれたスクリプト言語です。
vimを開き、 ```:help vim-script``` と打ってみましょう。
>はじめに

>誰もが最初に触れる Vim script は vimrc ファイルです。Vim が起動するときに読
み込まれ、書かれているコマンドが実行されます。それにより好きなように設定を変更
できます。vimrc の中ではすべてのコロンコマンドが使えます (":" で始まるコマンド
のこと。Ex コマンドやコマンドラインコマンドと呼ばれることもある)。

(自分のVimはヘルプが日本語じゃないからやだ！という人はこちらをどうぞ→ [Vimヘルプを日本語化！](https://qiita.com/KeitaNakamura/items/b87322d38fef4da3bac5))

「誰もが最初に触れる Vim script は vimrc ファイルです。」ということで
実はVimを触っていれば自然とVim scriptにお世話になっているのです。
シンタックスハイライトに使うカラースキームも、もとを正せばVim script。
Vimが有りさえすれば、そこが実行環境。というわけで気軽に始めてみましょう。

### こんな人向け
- Vimを触っていて、プラグインも使っているけど、もっと痒いところに手が届く自動化をしてみたい人
- もともと別のプログラム言語を触っているが、Vim scriptは触ったことがない人

### 今回やってみること

1. Vim上でPHP lintを実行する
1. lintでエラーが出た行数をハイライトしてみる
1. 上記を保存時に実行できるようにする

### 環境

OS: Mac OS Sierra(10.12.6)
Vim: 8.0

### 1. Vim上でPHP lintを実行する

これはぶっちゃけいろんな人がやっていますので、
本能の赴くまま、無邪気に作って保存してみましょう。

```~/.vimrc
function! PHPLint()
    let result = system('php -l ' . bufname(""))
    echo result
endfunction
```
→保存・コマンドモードで以下を実行
```:source ~/.vimrc```

```test.php
<?php

$hoge = 1;
aaa // 明らかなシンタックスエラー

echo $hoge;
```
→保存

コマンドモードで
```:call PHPLint()``` を実行

すると、こんな表示が出ます

![スクリーンショット 2017-12-03 16.55.55.png](https://qiita-image-store.s3.amazonaws.com/0/71518/01becdf9-5e8b-4770-5fae-d65ae1d432be.png)

楽しい＼(^o^)／

#### 簡単な解説

関数は```function ~ endfunction```で定義します。

| コマンド・関数名 | 動き | 
|:--:|:--|
|letコマンド|let (変数名) = (値)で変数定義できます。 |
|system()関数|（組み込み）シェルコマンドを実行し、実行結果の文字列を返します。|
|echoコマンド|引数の値を表示|

これらのコマンドや関数については、```:help let``` など、コマンド・関数名などの前に ```:help``` をつけて実行すると詳細なマニュアルが見られます:yum:。

![スクリーンショット 2017-12-03 17.17.09.png](https://qiita-image-store.s3.amazonaws.com/0/71518/93117759-20f9-b743-5797-008b911ebe85.png)

### 2. lintでエラーが出た行数をハイライトしてみる
では、今度はエラーが出た行数をハイライトしてみましょう。
（Syntaxチェックは必ずしも正しい行数を出してくれるわけではないが、今回はlineを信じて表示する）

```~/.vimrc
function! PHPLint()
    let result = system('php -l ' . bufname(""))
    let error_list = matchlist(result, '\(line \)\(\d\+\)', 1)
    if empty(error_list)
        echo "エラーはありませんでした"
        return
    endif
    call matchaddpos("Error",[str2nr(error_list[2])])
    echo result
endfunction
```

→保存・コマンドモードで以下を実行
```:source ~/.vimrc```

| コマンド・関数名 | 動き | 
|:--:|:--|
| matchlist()関数|（組み込み）正規表現でマッチした文字列のリストを返します。|
|str2nr()関数|（組み込み）文字列を数値に変換した結果を返します。|
|matchaddpos()関数|(組み込み）第二引数に指定された行数を強調表示する|

同じく、詳細は```:help (関数名)```で確認！

matchstr()で PHPのSyntaxエラー

```
Parse error: parse error in test.php on line 6
Errors parsing test.php
```

の```line XXX```の```XXX```を正規表現で取得し、その行をハイライトしています。

#### おい、:sourceできないぞ
はい。こんな表示が出たかと思います

```
Error detected while processing /path/to/.vimrc:
line  (行数):
E122: Function PHPLint already exists, add ! to replace it
```

関数は、function!で宣言しないと
再度```:source``` コマンドを実行する事ができません。
リローダブルなvimrcを書くには、

```
function! PHPLint()
...
endfunction
```
とする必要があります(後述のautocmdも同様)。

#### matchaddpos の第一引数について

```:help matchaddpos ```すると、以下のようなシグネチャになっています。

> matchaddpos({group}, {pos}[, {priority}[, {id}[, {dict}]]])

`matchadd` と同じである、ということで、`matchadd` のマニュアルを読んでみると、

{group} = 構文グループ

だとわかりますが、構文グループとはなんぞや:question: 
ということで、さらに `:help group` と実行してみると、謎が解けます。

>構文グループとは、同じ種類の構文アイテムをグループ化したものである。構文グルー
プから強調グループにリンクされ、強調グループに対して色が設定される。構文グルー
プそれ自体は、色や属性を指定するものではない。

なるほど。と、その下を読み進めていると、構文グループが列挙してありました。

![スクリーンショット 2017-12-03 17.52.57.png](https://qiita-image-store.s3.amazonaws.com/0/71518/f8c27c95-5243-aaa1-83ae-d891b0320f0e.png)
（つづく）

今回は下の方にあった、構文グループ `ERROR` を選択し、
指定した行数を `ERROR` と同じ強調色にしています。

こんな感じで、`:help` を使いこなすにはちょっとしたコツが必要ですが
慣れてくるとスルメのようにいい味が出てくるマニュアルです。

### 気を取り直して

コマンドモードで
```:call PHPLint()``` を実行

すると、こうなります。

![スクリーンショット 2017-12-03 18.22.52.png](https://qiita-image-store.s3.amazonaws.com/0/71518/705ff73a-c7a4-b868-3e28-6c4fc5b78e44.png)

### 3. 上記を保存時に実行できるようにする
```~/.vimrc
autocmd! BufWritePost *.php :call PHPLint()
```
→保存・コマンドモードで以下を実行
```:source ~/.vimrc```

例によって ```:help autocmd``` してみると

> :au[tocmd] [group] {event} {pat} [nested] {cmd}

コマンドの定義方法が出てきます。
（Vim script上でコロン(:)コマンドを書くときは、:は不要です。）
引数として必要なのは、```{event}```と```{pat}```と```{cmd}```だということがわかります。

`autocmd` はフックのようなものです。
`{event}`で指定されたイベントが発生したとき、```{pat}``` で指定したファイルに対し、
`{cmd}`で指定したコマンド/関数を実行します。

`{event}` は、あらかじめVimに文字列で指定されています。
というわけで ```:help event``` を見てみましょう。

![スクリーンショット 2017-12-03 18.11.06.png](https://qiita-image-store.s3.amazonaws.com/0/71518/56472fc2-f628-123c-ef9f-0e19e177ce19.png)

いっぱい出てきました。
ここにあるイベント発生時に、任意のコマンドを実行できます。

> BufWritePost

はバッファを保存した後のタイミングで実行するコマンドです。
`test.php` を`:w` で保存してみましょう。```PHPLint()```が保存時に実行されているのがわかりますね。

### おまけ:強調表示を消す方法

コマンド実行すると、強調表示されたままになってしまいます。
`clearmatches()` を実行すると強調表示がクリアされるので、以下のようにすると
シンタックスエラーがなくなれば表示も消えます。

```
function! PHPLint()
    let result = system( 'php -l ' . bufname(""))
    let error_list = matchlist(result, '\(line \)\(\d\+\)', 1)
    if empty(error_list)
        call clearmatches() ' ←こいつを追加
        echo "エラーはありませんでした"
        return
    endif
    call matchaddpos("Error",[str2nr(error_list[2])])
    echo result
endfunction
```
→保存・コマンドモードで以下を実行
```:source ~/.vimrc```

test.phpを正しく編集し、保存してみましょう。
強調表示が消えるはずです。

## 最後に
いかがでしたでしょうか？
今回は「作って覚える」ということで、細かい説明を省き
```:help```を調べながら進めていきました。

Vim scriptと仲良くなるには、```:help```と仲良くなるのが近道です。
楽しいVimライフを! & Merry Christmas!:christmas_tree: