出先でのメモ、どうしてますか?
せっかくなのでスマホで済ませませんか?
せっかくなのでdroidVim使いませんか?
というわけで、出先のメモをdroidVimで取る事が増えてきたんですが、新規バッファを勝手に保存するにはバッファ名を何とかするためにちょっと手がかかるじゃないですか?というわけでなんとかしたいんです。出来れば17時になんとかしてくれると嬉しいんです。が、android端末だとvimの+clientserver
どころかcronもない。あるのは+timers
だけ。いつ起動したのか不明なままタイマーの終了時刻を指定なんて出来ない…ので、さて書きましょう。
で、出来たものがこちらになる…と思うんですけど。
よりよい方法が見つかったのであちらに別エントリをご用意しました。
scriptencoding utf-8
command -bang -nargs=* -complete=command Vialarm call vialarm#main(<q-args>, '<bang>')
scriptencoding utf-8
let s:oneshots = []
function! s:init() abort
try
let s:alarms = split(execute('autocmd vialarm'), '[\n]')[2:]
call map(s:alarms, { _, val -> matchstr(val,'\d\d:\d\d') })
catch /E216/
let s:alarms = []
endtry
if exists('s:vialarmTimer')
call timer_stop(s:vialarmTimer)
endif
let s:vialarmTimer = timer_start((60-(localtime()%60))*1000, function('s:getAlarm'))
if v:vim_did_enter
echo 'Vialarm: initialized.'
endif
endfunction
function! s:getAlarm(timer) abort
let now = strftime('%H:%M', localtime())
if count(s:alarms + s:oneshots, now) > 0
execute 'doautocmd User' now
if match(s:oneshots, now) > -1
call filter(s:oneshots, { key, val -> val !=# now })
endif
endif
if timer_info(s:vialarmTimer)[0].repeat > 0
let s:vialarmTimer = timer_start(60000, function('s:getAlarm'), {'repeat':-1})
endif
endfunction
function! s:addOneshot(time, command) abort
try
if match(a:time, '^\d\d:\d\d$') < 0
throw 'vialarm_E01'
endif
if matchstr(a:time, '^\d\d') > 23 || matchstr(a:time, '\d\d$') > 59
throw 'vialarm_E02'
endif
if a:command ==# ''
throw 'vialarm_E03'
endif
execute 'autocmd vialarm User' a:time '++once' a:command
call add(s:oneshots, a:time)
echo printf('[vialarm] Added alarm in %s.', a:time)
catch /vialarm_E01/
echoerr '[vialarm] Error: Time format must be ''HH:MM''.'
catch /vialarm_E02/
echoerr '[vialarm] Error: No such time. :('
catch /vialarm_E03/
echoerr '[vialarm] Error: Command is empty.'
endtry
endfunction
function! vialarm#main(args, isBang) abort
if a:isBang ==# ''
if a:args !=# ''
let time = matchstr(a:args, '^\S*')
let command = matchstr(a:args, '\s\zs.*$')
call s:addOneshot(time, command)
else
echo 'You can also get vialarm info from ":autocmd vialarm".'
autocmd vialarm
endif
else
call s:init()
endif
endfunction
function! vialarm#stop() abort
call timer_stop(s:vialarmTimer)
echo 'Vialarm was stopped. execute '':Vialarm'' if you want start vialarm again.'
endfunction
function! vialarm#getTimerInfo() abort
return timer_info(s:vialarmTimer)[0]
endfunction
function! vialarm#peek() abort
return s:alarms + s:oneshots
endfunction
トリセツ
出来上がったばかりで「win機で動いたし問題なく動くやろポチー」したものがこちらです。当初、クラスを作ってめちゃめちゃ頑張っていたのですが、その結果86400000msecのタイマーを回そうとしてwin以外の端末にめちゃめちゃ嫌な顔1をされました。何が起こっているのかわからなかったのでvim-jpのslackで質問したところ、良いアドバイスを頂きまして、結果シンプルな方法で実装できました。人柱希望も兼ねて取り急ぎの使い方を…
…なんて読むん?
ヴィアラームでえーんじゃないですか?そう読む根性があれば/(vi|alar)m/と読んでくれるといいと思います。
コマンドの登録
実行したいコマンドは、autocmd
のvialarm
グループに登録する形になります。こんな感じです…
augroup vialarm
autocmd!
autocmd User 23:00 echo 'テレホ開始'
autocmd User 08:00 echo 'テレホ終了'
augroup END
Vialarm!
autocmdの設定後にスクリプト側で:Vialarm!
をしていますが、これによってvialarmが初期化され、タイマーが起動します。
:Vialarm
コマンド
コマンドの引数は:Vialarm[!] [{time} {command}]
となっています。
引数付きの起動の場合、内部では{time}引数のチェックの後に:autocmd vialarm User {time} ++once {command}
が実行されます。その後、vialarmのautocmd発火管理のためにスクリプト内部の変数に発火時間を登録するので、スクリプト外部からタイマーを登録しても発火しないどころか、この状態から:Vialarm
すると二回目以降の発火でエラーが出ます。
引数なしの起動の場合、単純に:autocmd vialarm
の結果を出力します。そもそもこのスクリプト自体が何かを管理してこねくり回したるというより、Vim自体が管理出来るものはそちらに任せたい、という発想なのでどちらでもいいかなと思います。
bang付で起動した場合、前述の通りautocmdの登録を再チェックします。引数は無視されます。
もうちょっと解説
s:init()
関数
:autocmd vialarm
の結果を切り出して内部リストs:alarms
に起動時間を文字列として保管しています。
このテキストがそのままautocmdのvialarm
グループのパターンになっているのでVimの機能とうまく連動する反面、この手順のせいでvialarm
グループ以外のグループが使用できなかったり、アラームの追加の度に初期化手順としてこの関数を呼び出さなければならない、等のデメリットも抱えています。前述の通り「Vimが管理出来るものは管理してもらう」ようにした結果ですが、不細工ではあるのでスマートにautocmdと連動しながらやれるいい方法があればいいのですが…
s:getAlarm()
関数
localtime()
をvialarm
グループのパターン文字列にして、内部変数s:alarms
と後述のs:oneshots
リストに含まれていれば:doautocmd
で発火という手順を取っています。:vialarm
外からonceなコマンドを登録するとおかしなことになるのはs:oneshots
に発火時間が登録されていないのが原因です。また、:autocmd vialarm
をしても出力からonce付きかどうかが判断できないので、この状態から初期化するとs:alarms
に保管した時刻のautocmdが消えている、というケースが起こります。うーん…
s:addOneshot()
関数
前述の通り引数のチェックをしてからautocmdの登録と、onceなiautocmdだけを管理する内部リストs:oneshots
に時刻を保管しています。前述の通りトラブルの素です。
vialarm#main()
関数
コマンド:Vialarm
のための関数です。
ワガママですが機能の少ないスクリプトにいちいち:VialarmInit
、:VialarmOneshot
みたいな長いコマンドを用意する気になれなかったので機能を詰めてしまいました。良くない方法なのですが、実際に機能が増えてなんとかする必要があるかどうか…
その他の関数
とりあえず止めときたい時のvialarm#stop()
関数、ちょっとタイマー確認したい時用のvialarm#getTimerInfo()
関数、vialarmが発火時間を把握しているか確認したい時用のvialarm#peek()
関数です。
ぶっちゃけエラー確認の名残でまぁ、使う人が困ったらこの辺あるほうが助かるでしょ?的に残しています。
余談
vim-jpの皆様から「droidVimのためにvim内部でcronを実装する」という発想に、半笑いで「頑張れ」と励ましを受けていい気になってるので、細かいところを精査してgitに登録しておこうかなと思っています。
-
具体的に「そんな長いタイマー使わせるか」と、残り時間がめちゃめちゃ短いものに差し替えられました。 ↩