Vim script の <SNR>
バグと戯れる
:source
で読み込んで使っている時には問題が無かったのに、プラグイン化したら動かなくなってしまった。そんな自作プラグイン作成の話。放置してひと月以上が経ってしまった。
エラーメッセージは下記の通り。
Error detected while processing function SeijiSeikanaTweet: line 3: E700: Unknown function: <SNR>11_post_twitter E488: Trailing characters
問題のある部分は SeijiSeikanaTweet()
関数の 3 行目とのこと。<SNR>
で引っぱってきている関数を見付けることができないらしい。
function! SeijiSeikanaTweet() call s:GetTwitVimSNRNumber() call s:ForTwitVimReplaceStringHiraKanji() call function('<SNR>'.s:dict_sid['twitvim.vim'].'_post_twitter')(b:string, 0) endfunction
E488
は引数を付けてはいけない関数に引数を付けてしまっているために生じるエラーらしいが、前段の E700
で未知の関数となっているのだからしょうがないね。<SNR>
についてもうちょい詳しく調べてからの方がいかな……
実験
:scriptnames
で表示してみると、twitvim.vim
は 2 つあるのだけれど、plugin/
以下の twitvim.vim
を対象としているみたいだ(<SNR>_11
なので)。
10: ~/.vim/plugin/twitvim/autoload/twitvim.vim 11: ~/.vim/plugin/twitvim/plugin/twitvim.vim
で、そこには下記のように関数が書かれている。
" Post current line to Twitter. if !exists(":CPosttoTwitter") command CPosttoTwitter :call twitvim#post_twitter(getline('.'), 0) endif
twitvim#
を付けたら良いのだろうか?やってみたけど駄目だったわ。良く見たら post_twitter
自体を呼べないじゃないの。
現実逃避
ちょっと寄り道して <SNR>11_post_twitter
が <SNR>10_post_twitter
となるように文字列を固定させて実験してみたら上手くいった。つまり autoload/twitvim.vim
の方の post_twitter
を指定できれば良いわけだね。
今までずっとスコープやパスが問題なのかと思ってあれこれやってきたけれど、自分の <SNR>
の取得方法がマズかったようだわ。
取得したスクリプト名と番号を収納する変数は辞書形式にしたので、同じスクリプト名のものがあると、後から追加した方で上書きされて、今回の対象の場合は 10 が消されて 11 になってしまうのだ。↓
{'tarPlugin.vim': '21', 'desert.vim': '6', '.vimrc': '1', 'syntax.vim': '2', 'twitvim.vim': '11', 'tohtml.vim': '22', 'filetype.vim': '5', 'getscriptPlugin.vim': '14', 'zipPlugin.vim': '24', 'scripts.vim': '25', 'matchparen.vim': '17', 'logiPat.vim': '16', 'skk.vim': '8', 'gzip.vim': '15', 'skkdict.vim': '9', 'mto.vim': '13', 'vimballPlugin.vim': '23', 'netrwPlugin.vim': '18', 'syncolor.vim': '4', 'rrhelper.vim': '19', 'synload.vim': '3', 'spellfile.vim': '20'}
(この自作スクリプト mto.vim
もプラグイン化した時に、autoload/
と plugin/
で同じ名前で作成してしまったのだけど、やめておいた方がいいのかな?)
解決
<SNR>
とスクリプト名の対を保持する辞書を作成する部分を、下記のように変更して autoload/twitvim.vim
で <SNR>
を取得できるようにした。
let y = split(x, '\s', '')[1] let script_name = split(y, '/', '')[-2].'/'.split(y, '/', '')[-1] let s:dict_sid[script_name] = script_num
呼び出しは下記のようにする。
call function('<SNR>'.s:dict_sid['autoload/twitvim.vim'].'_post_twitter')(b:string, 0)
疑問
冒頭にも書いたのだけれど、:source
で読み込んで使っていた時には問題がなかった。この場合でもスクリプト名から取得できる <SNR>
は plugin/twitvim.vim
の方であったはずなのにエラーなく動作していた。
根本的なところが理解できていないので、まったくわけがわからん。<SNR>
を使うようなスクリプトは今後書かないだろうから、知らなくてもいいのだけれど、やっぱ気持ち悪いよね。
追記
前回の記事を改めて読み返してみると、後半部分で解決の糸口が見えていたのね。ほんのちょっとした視点の違いなのだけれど、そういった「気付く力」が無くなっている……。
現実逃避の部分で書いたことは「何の脈絡もなく突然思い浮かんできたものを試行してみた」。その時の自分はそう思っていたのだけれど、実は意識のどこかに保存されていて、幸いにして今回見付けることが出来たのだと考えることもできる。
ひと月前の時点は「autoload/
と plugin/
には何をどうやって記述していくのか?」「とりあえず動かさなくちゃ」という気持が大きかったので、優先順位が低かったのだ。今回はバグを潰すことがメインの課題だったので、脳内アンテナの指向がそちらに向いたんだね。冷却期間をおいて問題が解決したとも言えるのかな。