Vim: To-do list for the rest of 2008

2008-09-28T00:40:54 / vim / comment

Vim: Plugins should provide "named" key sequences to customize hotkeys, not variables

2008-09-27T02:27:35 / vim, script, tips / comment

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:

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:

So that every plugin should provide "named" key sequences like <Plug>(plugin-feature) to customzie hotkeys, not variables like g:plugin_feature_hotkey.

Vim: Gauche interface

2008-09-15T01:23:30 / vim, gauche / comment

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でエディタを作った方が良いですよ。それはそれで茨の道ですけど。

VimM#2前後の行動記録

2008-09-08T23:37:55 / vim, off, mus, bm2dx / comment

2008-09-05

07:30
東京駅に出没。
08:00/10:00
mini-DVI VGAアダプターがなかったので購入しようかと秋葉原に行ってお店開店までマクドナルドで休憩しつつ朝御飯。
Twitterでだべっているとmootohさんからアダプターを貸してくださるとの発言が。出費が回避されて喜ぶ。
10:00/12:00
特に秋葉原に用事はなくなってしまったが、ついでなのでゲームセンターを巡ってIIDX行脚をするなどした。
  1. タイトーステーション - 1台、100円、4曲設定
  2. 東京レジャーランド2号店 - 2台、100円
  3. トライ・アミューズメント - 3台、100円、1台はターンテーブル超軽い。2Fにも旧モニターのものがあるらしいが見てない。1Fはドアがなく開けっぱなしなので空気が良い。
  4. 東京レジャーランド - 3台、100円、酷く臭い
12:00/13:00
某所に移動。いや、移動しようとして駅を間違え道を間違え大変な目に。
17:00/20:00
フリーになる。渋谷に移動。午前のことで既に疲れているがせっかくなのでまたIIDX行脚:
  1. ナムコランド - 2台、100円、臭い。順番待ちのとき、皆イヤホンを付けてプレイしていて意味不明だったのですが、自分の番がまわってきたときにパネル中央のヘッドフォン端子に気付きました。話には聞いたことがあるけれど、実物は初めて見た。試したかったけれどイヤホンを取り出す手間と時間を考えると後続に迷惑だと思って試せませんでした。ちょっと残念。
  2. モナコ - 2台、100円、かなり臭い。隣で旅行客らしき外国人が激しくDDRしていたことしか印象にない。
  3. ポポラーレ - 2台、100円、4曲設定、ちょっと臭い、ターンテーブル表面がつるつるで困った
20:00/20:30
GUHROOVY看板 買物結果
渋谷での本来の目的、GUHROOVYで買い物。DJ CHUCKYさんかっけー。
を購入。通販で済ませても良いのだけど、せっかくですし。惜しむらくは「THE DAY OF HARDCORE VOL.3」の配布が既に終了していたこと。
20:30/22:00
新宿に移動しようとするが渋谷の人波に翻弄され新宿の人波に翻弄され挙げくの果てに新宿駅の片隅で迷子になりかける。もう歩きたくないでござる。
なんとか@ClockWorkStudioさんと合流。久々にDDRなどした。もうだめだ(歩き疲れた体的な意味で)。
22:40
@ClockWorkStudioさん宅に泊めてもらいました。ありがたやありがたや。BEMANI話をしたり、gitの素晴しさを披露したり、Vim話でどんびきされたり、自分のプロンプト(YUKI.N>)に「ないわー、それないわー」と否定されるなどしました。

2008-09-07

00:00-10:00
@T_Hashさんの家に泊めてもらいました。ありがたやありがたや。@mickey24さんと@draftcodeさんも一緒で、4人でハーゲンダッツを食べたり、Vim話をしたり、本棚の内容で「これはアレすぎる」などと盛り上がったり、泥のように眠るなどしました。朝食は@T_HashさんによるUDON。さすが讃岐クラスター。
10:30-12:00
@T_Hash宅を出発、Hashthonの面々と別れる。その後、なんとなく再び秋葉原に向かう。
昼食にしようかと思ったが人が凄い時間帯になってしまったので、喫茶店で休憩しつつVimM#2の感想記事などを書く。
14:00/19:50
@masa_edwさんを召喚する。特に目的がないので、海鮮丼を食べつつ雑談し、マクドナルドで雑談し、Burger Cafe Asylでアキバーガー2に苦戦しつつ雑談するなどしました。うん、雑談しかしてない。付きあってくださった@masa_edwさん++。ありがたやありがたや。
「VimってPerlとかのインタプリターを内蔵できるよね」「うん」「あんな感じで(Linux)カーネルのモジュールみたいに動的にライブラリをロード・アンロードできないのかな」「ライブラリそのものを相手にするのは無理だけど、動的にライブラリをロードしてその中の関数を呼び出すのはできたはず」「おおお、じゃあそれで何かできないかな」
で、libcall()を使った実験をして大いに盛り上がりました。「引数は1個だけで文字列しか渡せない」「制約きついね」「本格的なFFIは色々と面倒ですし」「それもそうか。じゃあこの中で何かできないかな」
「サンプルにはgetenvgetpidがあるけどなぁ」「(libcなどをnmして調査)」「ああ、putsがある」「どうなる?」「普通に表示されたけど画面崩れるー」
「ほか何かないかな」「fork!」「ちょ」「さすがにこれは怖いから別プロセスのVimを起動」「どうなった?」「画面は普通のままだなぁ、って、カーソルの動きがおかしい」「ああ、入力を両プロセスで取りあってると」「どうも親子交互に渡ってるっぽい。ほら、lを連打してるのにこのカーソルの動き」「へー、そういうのの順序は不定だと思うけど、たまたまなのかな」「さすがに意味不明なので終了させよう」「うん」「おおおお、Vimがクラッシュした!」
「じゃあgetsは?」「おお、unsafeだよって警告がgetsから出た」「へー、そんなの出るんだ」「あれ、キー入力に全然反応しない」「cursesの影響でラインバッファリングじゃなくなってるんじゃない?」「もしそうならフルバッファリングになってて、でもそうするとバッファサイズ以上の入力を与えたのに無反応なのはおかしい」「んー」「多分Vim側で全部奪ってて、getsには何の入力も渡らず、結果として固まってるのかなー」
「んー、引数1個だけはキツいね。バッファを渡す系は全滅か」「そうですよねー」「あ、いや、待てよ。関数に渡される文字列へのポインタって具体的には何?」「んーと、Vim scriptでの値は構造体typval_Tで、その中のv_stringが実際の文字列へのポインタみたいですね」「libcall()で呼び出される関数へはそれが渡されるんだったら、呼び出された関数内部で書き換えるとどうなる?」「お、確かv_stringはほとんどの場合に動的確保されたメモリしか指してないはずだから、書き換えはOKだと思いますよ」「じゃあそれバッファとして使えね?」「それだ! repeat()でメモリ確保 in Vim script」
「あれー、書き換えたはずなのに結果はダメだなぁ。呼び出し前後で変数の内容は変わらず」「文字列値の代入についてのセマンティクスってどうなってる? libcall()の引数に渡されるとき」「んー、ListとDictionaryは参照渡し。他の値は内部的にどうなってるかは知らないなぁ」「ちょっと見てみましょうよ」「えーっと、Vimの関数呼び出し時は、値をcopy_tv()でコピーして、えええー」「どうした」「文字列値は一切合切を動的にメモリ確保してコピーしてる。要は値渡し」「おー、じゃあバッファであるかのように扱われる作戦は無理か」「いや、そんなことよりしょぼすぎるVim scriptの実装を知って凹んだ
20:20
12時間耐久バスツアーの始まり。だがこれに続く15分競歩と35分サイクリングの恐怖を誰も知らない。非常に疲れた。

Vim: Filetype pluginを極める

2008-09-07T21:31:45 / vim / comment

なにかと誤解が非常に多い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はそのfiletype pluginが適用されるべき'filetype'の値と同じ文字列であり、fragは任意の文字列である。fragを含む形式は、同一'filetype'に対して複数のfiletype pluginを用いたい場合のために用意されている。例えばpython/smartchr.vimにはsmartchrを利用した設定を記述しておき、python/textobj.vim独自のtext objectsに関する定義を記述するなどが考えられる。

ロードされるタイミング

Filetype pluginはファイルが開かれた場合などのイベントに応じて自動的にロード(:source)される。具体的にはFileTypeが発生したときにロードされ、FileTypeが発生する条件は以下の通りである:

ロード順序

Filetype pluginは'runtimepath'に記述されたディレクトリから順々に該当するファイルがロードされる。同一ディレクトリ下のファイルに関しては次の順番でロードされる:

  1. filetype.vim
  2. filetype_frag.vim
  3. filetype/frag.vim

後者2つについてはfragの辞書式順序でロードされる。

Filetype pluginを書く際の諸注意点

Filetype pluginで行う設定は全てバッファローカルなものにする
オプションを設定する場合、必ず:setlocalを使用する。詳細: Vim: オプションのグローバルな値とローカルな値
Key mappingsやabbreviationsを定義する場合、必ず<buffer>を指定する。詳細: Vim: Key mappingを極める
Exコマンドの定義をする場合、必ず-bufferを指定する。
バッファ固有の情報を記録する場合、バッファローカル変数b:varを使う。
以上の設定は:bwipeoutによって完全に削除される可能性があることに注意する。また、:bdeleteでは削除されないということにも注意する。
b:did_ftpluginによる無効化
諸事情(例えばユーザー側で特定のfiletype pluginのロードを抑止したいケースや、無引数: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コードを文字列としてセットする。
Key mappingsを定義する場合、<LocalLeader>を用いる
Filetype pluginの機能を実行するためのkey mappingsを定義する場合、特に強い理由がない限り、<LocalLeader>で始まるキーシーケンスをmapする。
<LocalLeader>に対応するキーはg:maplocalleaderでユーザー側が任意に選択できる。

要はfiletype pluginはこう書け

ここでは説明のために以下の用語を用いる:

Normal filetype plugin
ftplugin/に配置されるfiletype plugin。
Additional filetype plugin
after/ftplugin/に配置されるfiletype plugin。
~/.vim/
'runtimepath'のデフォルト値の先頭にあるディレクトリ。具体的な値は環境によって異なる。

Filetype pluginを書く場合、どういう目的で書くかによって書き方が異なる。具体的な目的は以下の3つである:

新たな'filetype'についてのfiletype pluginを書きたい
Normal filetype pluginを書く。
インストール場所はどこでもよいが、~/.vim/ftplugin/が望ましい。
標準のfiletype pluginを自分のもので置き換えたい
Normal filetype pluginを書く。
インストール場所は~/.vim/ftplugin/にする。
必ずb:did_ftpluginを定義し、標準添付のfiletype pluginがロードされることを抑止する。
標準のfiletype pluginに加えて自分独自の設定を追加したい
Additional 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を使ってはならない。

その他

VimM#2を主催するなどしました

2008-09-07T13:30:52 / vim, off / comment

2008-09-06にVimM#2を主催するなどしました。多数のご参加ありがとうございました&おつかれさまでした。主催はとは言いつつも、#2やろうぜと言い出しただけで、会場確保、懇親会の幹事、当日の会場設営などでほとんど何もしてないという駄目っぷりを発揮しました。特に諸準備で奔走してくださったHashさんと会場提供をしてくださった星一さんには非常にお世話になりました。お世話になり過ぎて頭が上がらない状態です。ありがとうございますありがとうございます。他にもbsheepさんにはmini-DVI VGAアダプターを貸していただいたり、sekidoには会場の無線LAN環境のセットアップをしていただいたりと、名前を挙げるときりがありません。本当にありがとうございます。

その代わりと言ってはアレですが、前半後半の両方で発表したり、余った時間で自分のvimrcの解説を行なったり、二次会などで色々と喋りまくるなどしました。普段あまり喋らない方なので、現在、微妙に喉の調子がおかしい気がしなくもないという状態です。その割りにはまだまだ喋り足りない気がするという不思議。

以下、感想などを気の向くがままに:

VimM#2本編

第一部「プラグインを使おう編」

kana「プラグインって何? おいしいの?」
maedana「はじめてのコンパイラプラグイン」
ukstudio「How to Rails.vim」
yoshuki「project.vim」
ujihisa「All About Metarw」

第二部「プラグインを作ろう編」

kana「君にも書けるVimプラグイン」
ujihisa「On Metarw」

第三部 - 余った時間でvimrcの解説など

懇親会と二次会

Hashthon

2008-09-07T14:34:56現在

非常に疲れており眠い。

VimM#2 発表資料

2008-09-07T10:04:00 / vim, off / comment

VimM#2での私の発表資料は以下の通りです。Operaでフルスクリーンにして表示すると良い感じで見れるのではないかと思います。