{foo,bar,baz} pattern show unexpected hit-enter prompt in windows of Vim. I found this bug on *nix environments such as Mac OS X and Debian on coLinux.y, d and p. Example: Vz-like stackable registers. (2008-10-13 done)Many Vim plugins use variables to allow users to customize hotkeys to do some features. For example, users have to write let g:plugin_feature_hotkey = '<F2>' or something like this into their vimrc to customize the hotkey to do a feature of a plugin.
But this method has the following problems:
g:plugin_feature_hotkey and use the resulted key sequence as the hotkey. In this case, users cannot assign any key sequences to hotkeys.The better way to customize hotkeys is: (1) plugins provide a "named" key sequence for each feature like <Plug>(plugin-feature); (2) users use :map and other commands to map any key sequences to the "named" key sequence.
This method doesn't have the above problems, and it has the following merit for authors of plugins:
" Named key sequences
nnoremap <silent> <Plug>(fakeclip-p)
\ :<C-u>call fakeclip#put('', 'p')<Return>
nnoremap <silent> <Plug>(fakeclip-Y)
\ :<C-u>call fakeclip#yank_Y()<Return>
...
" Default hotkeys
nmap "*p <Plug>(fakeclip-p)
nmap "*Y <Plug>(fakeclip-Y)
nmap "*yy <Plug>(fakeclip-Y)
...
and users can use the named key sequences for their customization as follows:
" Like the one of MS-Windows nmap <C-v> <Plug>(fakeclip-p)(the above examples are excerpted from fakeclip)
So that every plugin should provide "named" key sequences like <Plug>(plugin-feature) to customzie hotkeys, not variables like g:plugin_feature_hotkey.
2008年の抱負の一つで挙げていたVimのGauche interfaceですが、ようやく一通りできました。kana's vim at gauche-interface-0.0.0a - GitHubからダウンロードできます。要Gauche 0.8.13 or later。configure時に--enable-gaucheinterpも忘れないように。
今のところ、(vim-execute)、(vim-eval)、(vim-apply)といった基本的なものだけがあります。GaucheからVimをコントロールする上での便利なAPIはまだありませんが、それは上記の手続きから導出できるはずです(むしろそのつもりで用意していません)。大体、以下のような感じのことができます:
:gauche <<END (vim-execute "new") (vim-eval "&l:modified") ;==> 0 (vim-apply "append" '(1 (foo bar baz))) (vim-eval "&l:modified") ;==> 1 END
絶賛アルファバージョンなので変なコードを書くとクラッシュします。その時はgithubのwikiにコメントしてくださると助かります。
Gauche interfaceを書くにあたって、他言語のinterfaceについても詳しく調べてみたのですが、驚いたことに(vim-apply)相当の機能がどの言語のinterfaceにもありません。これなしに何が書けるというのでしょうか。その代わりにバッファやウィンドウなどに関するAPIは用意されているのですが、どのみちその言語からコントロールできる範囲はそれまでです。せめてVim scriptと同レベルまでは持っていこうという発想はなかったのでしょうか。
Vimの内部APIはかなりカオスで非常に不便です。Gaucheのそれと比較すると、GCの有無を除外しても、色々とひどい。特にListやDictionaryの操作については匙を投げたくなりました。なんせまともなAPIがないか、あっても非公開なんですもの。どうしようもない部分については#ifdef FEAT_GAUCHE/#endifで括って無理矢理公開させるという手に出ました。ごてごてしたところはパフォーマンスを考えると納得できなくはないのですが、それでもVim script関連のコードを書く上であまりにも不便では。
今回、色々とsrc/eval.cの中を読んでみましたが、それはもう卒倒しそうでした。先日のlibcall()祭りでVim scriptの値コピーについての詳細を知ってくらくらしてしまいましたが、それの比ではありません。Bramさんがいなくなったら誰があれをメンテするというのでしょうか。Vimはもうだめだ。自分の中で多少の改善案はあったものの、アレを見ると、もう根本的なところでの改善をしようという気は全く失せました。もうGaucheでエディタを作った方が良いですよ。それはそれで茨の道ですけど。

YUKI.N>)に「ないわー、それないわー」と否定されるなどしました。libcall()を使った実験をして大いに盛り上がりました。「引数は1個だけで文字列しか渡せない」「制約きついね」「本格的なFFIは色々と面倒ですし」「それもそうか。じゃあこの中で何かできないかな」getenvやgetpidがあるけどなぁ」「(libcなどをnmして調査)」「ああ、putsがある」「どうなる?」「普通に表示されたけど画面崩れるー」fork!」「ちょ」「さすがにこれは怖いから別プロセスのVimを起動」「どうなった?」「画面は普通のままだなぁ、って、カーソルの動きがおかしい」「ああ、入力を両プロセスで取りあってると」「どうも親子交互に渡ってるっぽい。ほら、lを連打してるのにこのカーソルの動き」「へー、そういうのの順序は不定だと思うけど、たまたまなのかな」「さすがに意味不明なので終了させよう」「うん」「おおおお、Vimがクラッシュした!」getsは?」「おお、unsafeだよって警告がgetsから出た」「へー、そんなの出るんだ」「あれ、キー入力に全然反応しない」「cursesの影響でラインバッファリングじゃなくなってるんじゃない?」「もしそうならフルバッファリングになってて、でもそうするとバッファサイズ以上の入力を与えたのに無反応なのはおかしい」「んー」「多分Vim側で全部奪ってて、getsには何の入力も渡らず、結果として固まってるのかなー」typval_Tで、その中のv_stringが実際の文字列へのポインタみたいですね」「libcall()で呼び出される関数へはそれが渡されるんだったら、呼び出された関数内部で書き換えるとどうなる?」「お、確かv_stringはほとんどの場合に動的確保されたメモリしか指してないはずだから、書き換えはOKだと思いますよ」「じゃあそれバッファとして使えね?」「それだ! repeat()でメモリ確保 in Vim script」libcall()の引数に渡されるとき」「んー、ListとDictionaryは参照渡し。他の値は内部的にどうなってるかは知らないなぁ」「ちょっと見てみましょうよ」「えーっと、Vimの関数呼び出し時は、値をcopy_tv()でコピーして、えええー」「どうした」「文字列値は一切合切を動的にメモリ確保してコピーしてる。要は値渡し」「おー、じゃあバッファであるかのように扱われる作戦は無理か」「いや、そんなことよりしょぼすぎるVim scriptの実装を知って凹んだ」なにかと誤解が非常に多いVimのfiletype pluginについての解説。VimM#2でfiletype pluginについてスルーしたのは、以下の分量から察してください。
Vimはバッファ毎にその内容の種類、例えばPerl/Python/Rubyスクリプトといった情報が保持されており、それは'filetype'というオプションで表される。Filetype pluginとは'filetype'に応じた機能を提供するためのスクリプトである。その性質上、提供される機能はバッファローカルである。
Vimは標準で多数のfiletype pluginが同梱されている。どのようなものがあるかはVim内で:edit $VIMRUNTIME/ftplugin/を実行してみれば分かる。:help ft-filetype-pluginで'filetype'に対するfiletype pluginのドキュメントが読める。標準添付されているfiletype pluginの多数はいくつかのオプションを設定するだけであるため、敢えてドキュメントがないものが多い。
Vimのfiletype pluginはEmacsで言えばmajor modeや一部のminor modeに近い存在である。あくまで近いのであって、同じものではないことに注意。
各filetype pluginは'runtimepath'のftplugin/ディレクトリ下に配置される。例えばVimに標準添付されているfiletype pluginは全て$VIMRUNTIME/ftplugin/下にある。
自分で独自のfiletype pluginを作成・使用する場合、'runtimepath'に記述されている最初のディレクトリ下のftplugin/ディレクトリにファイルをコピーする('runtimepath'のデフォルト値であれば~/.vim/ftplugin/などである。この値は環境により異なり、また'runtimepath'を変更してる場合はこの限りではない)。場合によってはftplugin/ではなくafter/ftplugin/下にコピーする必要がある(詳細は後述)。
Filetype pluginのファイル名は次の3つのいずれかに合致しなければならない:
filetype.vimfiletype_frag.vimfiletype/frag.vimここでfiletypeはそのfiletype pluginが適用されるべき'filetype'の値と同じ文字列であり、fragは任意の文字列である。fragを含む形式は、同一'filetype'に対して複数のfiletype pluginを用いたい場合のために用意されている。例えばpython/smartchr.vimにはsmartchrを利用した設定を記述しておき、python/textobj.vimに独自のtext objectsに関する定義を記述するなどが考えられる。
Filetype pluginはファイルが開かれた場合などのイベントに応じて自動的にロード(:source)される。具体的にはFileTypeが発生したときにロードされ、FileTypeが発生する条件は以下の通りである:
:setfiletypeなどによる手動'filetype'設定BufEnter、BufNewFile、BufRead、StdinReadPostのいずれかのイベントの発生(ただし'filetype'によって用いられるイベントは微妙に異なる)Filetype pluginは'runtimepath'に記述されたディレクトリから順々に該当するファイルがロードされる。同一ディレクトリ下のファイルに関しては次の順番でロードされる:
filetype.vimfiletype_frag.vimfiletype/frag.vim後者2つについてはfragの辞書式順序でロードされる。
:setlocalを使用する。詳細: Vim: オプションのグローバルな値とローカルな値<buffer>を指定する。詳細: Vim: Key mappingを極める-bufferを指定する。b:varを使う。:bwipeoutによって完全に削除される可能性があることに注意する。また、:bdeleteでは削除されないということにも注意する。b:did_ftpluginによる無効化:editによるバッファ内容のリロード時などによる複数回のロードを抑止したいケース)のため、filetype pluginの冒頭で以下のような記述をする:if exists('b:did_ftplugin')
finish
endif
let b:did_ftplugin = 1このチェック用変数名はb:did_ftpluginでなければならない。b:undo_ftpluginに設定のリセット情報を記述する:setlocal expandtabをするならば、:let b:undo_ftplugin = 'setlocal expandtab<'のようにし、設定した事項をリセットするためのVim scriptコードを文字列としてセットする。g:maplocalleaderでユーザー側が任意に選択できる。ここでは説明のために以下の用語を用いる:
ftplugin/に配置されるfiletype plugin。after/ftplugin/に配置されるfiletype plugin。~/.vim/'runtimepath'のデフォルト値の先頭にあるディレクトリ。具体的な値は環境によって異なる。Filetype pluginを書く場合、どういう目的で書くかによって書き方が異なる。具体的な目的は以下の3つである:
'filetype'についてのfiletype pluginを書きたい~/.vim/ftplugin/が望ましい。~/.vim/ftplugin/にする。b:did_ftpluginを定義し、標準添付のfiletype pluginがロードされることを抑止する。~/.vim/after/ftplugin/にする。b:did_ftpluginのチェックとセットは行わない。何故なら標準添付のfiletype pluginが既にロード済みであるため、必ずb:did_ftpluginは存在し、結果として常にロードが抑止されることになるからだ。同様に他のadditional filetype pluginsに影響が出るため、セットもしてはならない。b:did_ftpluginと同様のチェックは必要である。この場合、b:did_ftplugin_filetype_fragなどの変数を使い、b:did_ftpluginを使ってはならない。b:undo_ftpluginの設定など。それについては後々公開する予定。'filetype'は複数の値を設定できる。例えば:setfiletype c.doxygenなどとピリオドで区切った名前の列によって複数の値を持たせることができる。個人的には、複数の値を持つ場合は「compound filetype」、通常の値の場合は「simple filetype」と呼び分けている。'filetype'がc.doxygenの場合、まずc用のものがロードされ、その後でdoxygen用のものがロードされる。b:did_ftpluginの関係上、活用できないと言っても過言ではない状態である。この辺りもどうにかしたいところ2008-09-06にVimM#2を主催するなどしました。多数のご参加ありがとうございました&おつかれさまでした。主催はとは言いつつも、#2やろうぜと言い出しただけで、会場確保、懇親会の幹事、当日の会場設営などでほとんど何もしてないという駄目っぷりを発揮しました。特に諸準備で奔走してくださったHashさんと会場提供をしてくださった星一さんには非常にお世話になりました。お世話になり過ぎて頭が上がらない状態です。ありがとうございますありがとうございます。他にもbsheepさんにはmini-DVI VGAアダプターを貸していただいたり、sekidoには会場の無線LAN環境のセットアップをしていただいたりと、名前を挙げるときりがありません。本当にありがとうございます。
その代わりと言ってはアレですが、前半後半の両方で発表したり、余った時間で自分のvimrcの解説を行なったり、二次会などで色々と喋りまくるなどしました。普段あまり喋らない方なので、現在、微妙に喉の調子がおかしい気がしなくもないという状態です。その割りにはまだまだ喋り足りない気がするという不思議。
以下、感想などを気の向くがままに:
使い慣れたこれ(Happy Hacking Keyboard Professional2)でないとデモできませんには、こう、RMSを彷彿とさせるものがありました。
:compilerとコンパイラプラグインです。なので、quickfixを知らない人からすると少々分かり辛いものがあったのではないかと思いました。特に初っ端の「vimではとにかくテキストを解析して解析結果を決まったフォーマットで出力するものはCompilerと見なせる」については、全体を知っている人ならともかく、quickfixを知らない人には意味不明だったのではないかと思います。
make用ですが、grepやlint系ツールにも応用できますよね」といったストーリーを最初に提示すれば良かったと思います。:closeするより:onlyで特定のウィンドウ以外を閉じた方が速いと思います。むしろ毎回:cloを入力していたようで驚きました。そこはせめて<C-w>cでは。nowa:ujihisaでエントリーの一覧を表示することができるのですから、ちょっとコードを書くだけですぐに実装できるはずです。~/.inputrcに設定を書いておけば良いじゃないですか」「え、それで有効になるんですか。irbとかも」「そうですよ」「へーーーーーーーーーーー、それが一番Life Changing」非常に疲れており眠い。
VimM#2での私の発表資料は以下の通りです。Operaでフルスクリーンにして表示すると良い感じで見れるのではないかと思います。