vim vimrc vimscript vimプラグイン」タグアーカイブ

はじめてのvimscriptとvimプラグインの作成

前提

以下の環境で作業を行った

要素 バージョン
debian 8.6
vim 7.4
neobundle 7.2.051
git 2.11.0

概要

vimは長らく使ってきたけどvimscriptを書いたことがない私が、vimscriptの基本を勉強し、簡単なvimプラグインを作成、NeoBundleでインストールできる状態にしたお話。

Vim scriptとは

Vim上で動作するスクリプトのこと。

:set filetype=html

のような、コマンドモードで”:”から始まるコマンドの集合。

最も身近なvimscriptは.vimrcで、これはvim起動時に読み込まれるvimscriptである。
つまり、vimscriptの書き方を知っていれば、より効果的なvimrcを記述することができる。

Vim script入門

vim scriptの入門は、以下を参考にした。
Vim script と vimrc の正しい書き方

本記事は簡単に基本文法を紹介する程度にするので、具体的に学びたい場合は各種参考ページを利用すること。

実行方法

vimscriptは基本的にscript.vimのように、拡張子をvimにしてファイルを作成する。
vimファイルはvimコマンドから実行することもできるが、多分以下のように、スクリプトをvimで編集中にsourceコマンドを叩くのが一番早い。

変数と代入

vim scriptには変数宣言の概念はなく、変数への代入と同時に変数が作成される模様。
let で変数への代入を行う。echoを見てもわかるように、シェルスクリプトに似ていると感じる。

let number = 1
let string = "hoge"
let array  = [1,2,3,4,5]

echo number
echo string
echo array

実行結果

1
hoge
[1, 2, 3, 4, 5]

条件分岐

ifとelseifとendifを使う。これもシェルスクリプトと同じ。

let number = 1
if number == 1
  echo "number is 1"
elseif number == 2
  echo "number is 2"
else
  echo "other"
endif

繰り返し

for文とwhile文が使える。while文の中でletを用いて変数を再宣言しているように見えるが、vim scriptにおけるletは代入のコマンドなので代入する場合は必ず必要。

for i in [1,2,3]
  echo i
endfor

let n = 0
while n < 10
  echo n
  let n += 1
endwhile

関数

関数もシェルスクリプトと同じようだが、気をつけたい所が2点。

まず、function! と”!”をつけることで、同名の関数が既にロード済みの場合上書きすることができる。こうしないと、同じバッファ内で複数回同じスクリプトを読んだ時に、同名の関数が既に定義済みだよとエラーが出るので”!”を付ける必要がある。

次に、関数の引数であるnumberを参照するために”a:”を頭につけている。これは変数のスコープの指定を表し、aとつけることでnumberは関数内にあるnumberであることを表す。aの他に、グローバルスコープ用の接頭辞やスクリプトファイル内スコープ用の接頭辞があるので、必要に応じて書き換える必要がある。

function! Double(number)
  return a:number * 2
endfunction

let result = Double(10)
echo Double(10)

vimプラグインの作成

本記事では、簡単すぎず難しくもないシンプルなvimプラグインを実装する。

今回作成するプラグインは、:RandDateTimeと入力することで、1980年から、2017年までの範囲内のランダムな日付を、Y-M-D H:m:sの形式でカーソル位置に挿入するコマンドを利用できるようにするプラグインで、まったくもって実用性はないが、個人的にもう少し改良して実用的なものにする予定。

ディレクトリ構成

作成するプラグイン名をmy-vim-pluginとした場合、同名のディレクトリを作成し、その中にpluginというディレクトリを作成、さらにその下にvim scriptを記述する。my-vim-plugin直下にはREADMEを入れたりするみたいだが、今回はスクリプトファイル一つで完結させるので、ディレクトリ構成は下記のようになる。

vagrant$ tree my-vim-plugin/
my-vim-plugin/
└── plugin
    └── script.vim

1 directory, 1 file

乱数を生成する関数

まず、ランダムな日付を生成するために、指定した範囲内での数値をランダムに戻す関数を実装する。
残念ながら、vim scriptには乱数を生成するための仕組みは存在しない。とは言え、今回は時刻データを用いて擬似的な乱数を生成することにした。

作成した関数は以下の通り。GetRandom関数は、minとmaxを引数にとり、min <= n <= max の範囲のランダムな整数を戻す。
reltime関数は、現在のUNIXTIMEを秒とマイクロ秒の配列で戻す。配列の二番目の要素がマイクロ秒になるので、その数値を使って擬似乱数を生成している。

function! GetRandom(min, max)
  return reltime()[1] % (a:max - a:min + 1) + a:min
endfunction

ランダムな日付を取得する関数

前項で作成したGetRandom関数を用いて、1980年から2017年までのランダムな日付文字列を戻す、GetRandomDate関数を以下のように実装する。なお、今回は実装の簡略化のため、日は1日から28日までの確実に存在する日付のみを対象とするようにした。

function! GetRandomDate()
  return GetRandom(1980, 2017) . '-' . GetRandom(1, 12) . '-' . GetRandom(1, 28)
endfunction

ランダムな日付時刻を取得する関数

同様に、前項で作成したGetRandomDate関数を用いて、ランダムな時刻までを含めて戻すGetRandomDateTime関数を以下のように実装する

function! GetRandomDateTime()
  return GetRandomDate() . ' ' . GetRandom(0, 23) . ':' . GetRandom(0, 59) . ':' . GetRandom(0, 59)
endfunction

ランダムな日付時刻をカーソル位置に挿入する関数

前項で作成したGetRandomDateTime関数は、ランダム日付時刻を取得するだけなので、新たにInsertRandomDateTime関数を以下のように実装する。
executeコマンドは、以降の任意のコマンドをvim上で実行するためのコマンド。:normalは以降のコマンドをノーマルモードで実行するコマンド。aでカーソルの次の位置から挿入モードを開始、そしてdatetimeを入力するという手はずになる。

function! InsertRandomDateTime()
  let datetime = GetRandomDateTime()
  execute ":normal a" . datetime
endfunction

作成した関数を呼び出すコマンドを定義

ここまででランダム日付時刻文字列を挿入する仕組みができあがったので、それを呼び出すためのコマンドを定義する。コマンドの定義にはcomman関数を用い、コマンド名と実行内容を引数に設定する。(callは、指定した関数を実行するコマンド)

command! RandDateTime call InsertRandomDateTime()

動作確認

以上で、以下のような自作のvimプラグインが完成した。

function! GetRandom(min, max)
  return reltime()[1] % (a:max - a:min + 1) + a:min
endfunction

function! GetRandomDate()
  return GetRandom(1980, 2017) . '-' . GetRandom(1, 12) . '-' . GetRandom(1, 28)
endfunction

function! GetRandomDateTime()
  return GetRandomDate() . ' ' . GetRandom(0, 23) . ':' . GetRandom(0, 59) . ':' . GetRandom(0, 59)
endfunction

function! InsertRandomDateTime()
  let datetime = GetRandomDateTime()
  execute ":normal a" . datetime
endfunction

command! RandDateTime call InsertRandomDateTime()

これをsourceコマンドで読み込むことで、現在開いているバッファ上でRandDateTimeコマンドが利用できるようになる。

NeoBundleでインストールできるようにする

NeoBundleはvimプラグインを管理するためのツールで、決まったディレクトリ構成でGithub上にVimプラグインを公開すれば、誰でも手軽にそれをインストールできるようになる。

今回は既にNeoBundleを想定したディレクトリ構成にしているので、これをこのままGithub上にあげる。
Sa2Knight/my-vim-plugin

この状態で、既にNeoBundleが利用できる状態のvimrcに以下を追加

NeoBundle "Sa2Knight/my-vim-plugin"

vimを起動すると、以下のようにインストールするかの確認が出るので、Yを入力する

Not installed bundles:  ['my-vim-plugin']
Press ENTER or type command to continue
Install bundles now?
(y)es, [N]o:

Github経由で先程作成した自作プラグインがインストールされ、次回起動時から自動で:RandDateTimeコマンドが使えるようになる。

所感

vimscript及びvimプラグインの非常に基礎的な部分にようやく手を付けられたので、今後もっと深い部分を勉強して実用的なプラグインを作りたい。とりあえず今回作ったプラグインには以下のような改良点があるので、それを実装しつつノウハウを見つけていく。

  • ランダムの範囲を外から指定できるようにする
  • 日付時刻文字列のフォーマットを外から指定できるようにする
  • 乱数値が一桁になった場合0で詰めるようにする
  • コマンドを叩かずともキーバインドですぐに実行できるようにする