Vim 8.0 で追加された機能 タイマー

Vim 8.0 ではついに Vim script にタイマー機能が追加されました!
この機能を使用することで処理をブロックしないで関数を呼び出すことが出来ます。

[基本的な使い方]

タイマーは timer_start() 関数を使用して任意の関数を n 時間後に呼び出す事が出来ます。

function! s:disp(timer)
    echo "callback"
endfunction

" 第一引数に時間、第二引数に呼び出す関数参照を渡す
" 1000ミリ秒後に s:disp をブロックしないで呼び出す
call timer_start(1000, function("s:disp"))

[タイマーを止める]

タイマーを止めたい場合、timer_start() が返した ID と timer_stop() を利用します。

" タイマーIDを受け取る
function! s:disp(timer)
    echo "callback"
endfunction

let s:id = timer_start(1000, function("s:disp"))

" ID のタイマーを止める
call timer_stop(s:id)

コールバック関数が受け取る timertimer_start() で生成された ID になります。 また、timer_stopall() ですべてのタイマーを止める事が出来ます。

[回数を指定する]

timer_start() の第三引数にリピートする回数を指定する事が出来ます。

let s:counter = { "value" : 0 }
function! s:counter.count(...)
    echo self.value
    let self.value += 1
endfunction

" 1000ms ごとに s:counter.count() を 3回呼び出す
call timer_start(1000, s:counter.count, { "repeat" : 3 })

また、-1 をすることでタイマーを止めるまで呼ばれ続けます。

let s:counter = { "value" : 0 }
function! s:counter.count(timer)
    echo self.value
    let self.value += 1

   " 5回呼ばれたら止める
    if self.value >= 5
        call timer_stop(a:timer)
    endif
endfunction

" 1000ms ごとに s:counter.count() を呼び出し続ける
call timer_start(1000, s:counter.count, { "repeat" : -1 })

かなり便利。

Vim 8.0 で強化された機能 辞書関数

さて、以前の辞書関数は次のように call() と辞書を経由して呼び出す必要がありました。

let s:dict = { "value" : 0 }
function! s:dict.up()
    let self.value += 1
    return self.value
endfunction

let s:f = s:dict.up
echo call(s:f, [], s:dict)
" => 1
echo call(s:f, [], s:dict)
" => 2
echo call(s:f, [], s:dict)
" => 3

Vim 8.0 ではこの縛りが緩和されて、辞書関数を直接呼び出すことができるようになりました。

let s:dict = { "value" : 0 }
function! s:dict.up()
    let self.value += 1
    return self.value
endfunction

let s:f = s:dict.up
echo s:f()
" => 1
echo s:f()
" => 2
echo s:f()
" => 3

めちゃくちゃ便利…。

RSpec で not_change マッチャを定義する

通常、Rspec で『変更されないことを検証する』場合、change マッチャを使用します。

# subject を呼び出しても hoge が変更されないことを検証する
it { is_expected.not_to change{ hoge } }

[not_change マッチャを定義する]

上記のコードでも問題ないのですが、 RSpec::Matchers.define_negated_matcher を使用すると書き方を変えることができます。

# change マッチャの否定を定義する
RSpec::Matchers.define_negated_matcher :not_change, :change

# to を呼び出し、マッチャの名前を変更する
it { is_expected.to not_change{ hoge } }

おもしろい

Ruby の ObjectSpace を利用して、オブジェクト名からクラスオブジェクトを探す

Ruby で例えば "String" という文字列からその名前のクラスオブジェクトを取得したい場合があるとします。 そういう場合は ObjectSpace から逆算して検出することができます。

# クラスオブジェクトの一覧を取得する
classes =ObjectSpace.each_object(::Class)

# クラスオブジェクトの一覧から該当するクラスオブジェクトを探す
target = "String"
result = classes.find { |klass| klass.name == target }
p result == String
# => truk

また、Rails では String#constantize というメソッドが定義されており、上記と同等の機能があります。

"String".constantize   
# => String

[参照]

http://railsdoc.com/references/constantize

Vim 8.0 で追加された Vim script の機能 Lambda

Vim 8.0 ではついに Vim script に Lambda が追加されました! Lambda を定義する場合は { args -> expr } という構文を使用します。

" Lambda は関数参照を返す
" epxr にはその名の通り "式" のみ書くことができる
let F = { a, b -> a + b }

echo F(1, 2)
" => 3

また filter() 関数などに直接 Lambda を渡すことも出来ます。

echo filter(range(1, 10), { index, val -> val % 2 == 0 })
" => [2, 4, 6, 8, 10]

[変数のキャプチャ]

次のように Lambda 外のローカル変数もキャプチャされます。

function! s:main()
    let value = 10
    let F = { a -> value + a }
    echo F(5)
   " => 15
endfunction
call s:main()

[Lambda 内では式しか定義できない]

Lambda 内では式のみ定義できるので、コマンドなどを直接呼び出すことは出来ません。

" このような使い方は出来ない
let Nonumber = { -> set nonumber }

この場合は、同じく Vim 8.0 で追加された execute() 関数を使用します。 execute() は引数のコマンドを実行する関数になります。

" execute() を経由してコマンドを呼び出す
let Nonumber = { -> execute("set nonumber") }

このように execute() を経由することでコマンドを式として呼び出すことが出来ます。

[引数に a: をつける必要はない]

Lambda 内で使用する引数には a: をつける必要はありません。 ですので次のように外部の引数を参照することも出来ます。

function! s:main(arg)
    let F = { a -> a:arg + a }
    echo F(5)
   " => 15
endfunction
call s:main(10)

Vim 8.0 で追加された新しいシンタックス

Vim 8.0 では新しく以下の言語に対応しました。

New Syntax/Indent/FTplugin files:

AVR Assembler (Avra) syntax
Arduino syntax
Bazel syntax and indent and ftplugin
Dockerfile syntax and ftplugin
Eiffel ftplugin
Euphoria 3 and 4 syntax
Go syntax and indent and ftplugin
Godoc syntax
Groovy ftplugin
HGcommit ftplugin
Hog indent and ftplugin
Innovation Data Processing upstream.pt syntax
J syntax and indent and ftplugin
Jproperties ftplugin
Json syntax and indent and ftplugin
Kivy syntax
Less syntax and indent
Mix syntax
Motorola S-Record syntax
R ftplugin
ReStructuredText syntax and indent and ftplugin
Registry ftplugin
Rhelp indent and ftplugin
Rmd (markdown with R code chunks) syntax and indent
Rmd ftplugin
Rnoweb ftplugin
Rnoweb indent
Scala syntax and indent and ftplugin
SystemVerilog syntax and indent and ftplugin
Systemd syntax and indent and ftplugin
Teraterm (TTL) syntax and indent
Text ftplugin
Vroom syntax and indent and ftplugin

メジャーどころだと GoJsonScalaGroovy あたりですかねえ。