Vimの関数呼び出しでは場合によって<SID>とs:を使い分けする必要があるが、実はどのケースであっても<SID>を使えば動くには動く。ただ<SID>というプレフィックスはs:に比べて見辛いため、できればs:を使いたい。ではどの場合にどちらを使うべきか。答えは以下の通り:
s::autocmd、:command、:function/:endfunctionの中で使う(推奨 - <SID>でも動くには動く)。<SID>:map系か:menu系コマンドの中でのみ使う(必須 - s:では動かない)。Vimのカスタマイズに手を出し始めた頃は何故このような使い分けが必要なのか分からなかったが、今、この記事を書いていて思い付いた。:map及び:menuの定義時に与えられた{rhs}だけでは、その中に出現するs:の意味・用途を正確に判定することができないからだ。例えば以下のような定義があったとして:
nmap =x =:=call=func=return nnoremap =: : cnoremap =call call<Space> cnoremap =func s:func() cnoremap =return <Return>
=funcの{rhs} s:func()は、=xから実行された場合には<SID>func()として扱ってほしいが、それ以外のケースではそのままs:func()と扱うべきなのかどうか判断できない。だから明示的に<SID>を用いる必要があるのだ。
またMacBookがブラックアウトしました。先月にMacBookのブラックアウト現象について書いてから久々に発生しました。今回は、というか今回もスリープ中にiPodを外した後の復帰時に発生しました。やはりスリープ中のUSB機器の取り外しが原因なのでしょうか。
ただ気になるのは、今回のブラックアウト以前にもスリープ中にiPodを外したことが何回かあり、そのときにはブラックアウトしなかったということです。iPod取り外し以外で気になることと言えば、今回のブラックアウト発生のスリープ前、iPodの接続時にエラーが発生したことです。でもそのエラーは今まで2回しか経験したことがなく、それに比べればブラックアウトの回数は多いので、関係あるのかないのか。後はiPodを取り外したタイミングくらいしか思い付きません。今回はスリープさせてから取り外すまでの間がいつもより短かったのです。でもこれも関係あるのかないのか。
ううむ、何が何やら。
追加情報: 実稼働時間が9日程でした。いや、あまり関係ないかな。
なんとなくgitのリリースノートを見ていて気付いたのですが:
From v1.6.0, git will by default install dashed form of commands (e.g. "git-commit") outside of users' normal $PATH, and will install only selected commands ("git" itself, and "gitk") in $PATH.
という訳でgit-{subcommand}は非推奨のようです(それも2006年の早期から)。確かに他のバージョン管理システムと比べて変だとは思っていましたけど。でも、元々どういう理由があってあの形式にしていたんでしょうね?
先日、大昔に書いた「vim で任意のオプションの有効・無効を切り替える」にはてなスターが付いていて吹いたのですが、改めて記述内容を見直してさらに吹きました。これはひどい。真似したり参考にしたりしては駄目ですよ! 4年越しの今、修正をするとしたら以下のような感じになります:
function! s:toggle_option(option_name)
execute 'setlocal' a:option_name.'!'
execute 'setlocal' a:option_name.'?'
endfunction
nnoremap <silent> \ :<C-u>call s:toggle_option('wrap')<Return>しかし当時の自分に「何故こう書くべきか」を理解しろというのは少々重荷、かなぁ。一つ一つは大したことではないのだけれど、それら全てを把握しておけというのは難しいです。しかもこの手の知識は:helpに明記されていませんし。だから皆で共有して全体のレベルを底上げできれば良いのではないかと思いました。具体的に言うとvimグループのキーワードなんか使うと良いと思います。さああなたも今すぐ参加! ただいま全手動Vim script修正キャンペーン中です(宣伝)
Vim 7.2aでFloat (浮動小数点数)がサポートされたのですが、これが原因で一部のVim scriptでエラーが発生します。既に修正パッチはリリースされているのですが、それを見て吹きました。
Patch 7.2a.007 Problem: ":let v = 1.2.3" was OK in Vim 7.1, now it gives an error. Solution: Don't look for a floating point number after the "." operator. Files: src/eval.c
ああ、そういうことか。そりゃそうだわ。別にこれに限らず、Vim scriptは微妙なレベルでどうパースされるのか分からないケースがあります。私がよく遭遇する例としては以下のようなものがあります:
let foo = 'echo' execute foo (1 + 3) " Error, treated as calling a function "foo" execute foo ' ' (1 + 3) " Okay
:executeの引数はいちいち文字列結合の演算子(expr-.)を使っていると見難くなるので前者のスタイルに統一しているのですが、上記のケースだと関数呼び出しとしてパースされてしまいます。他にはDictionary-function関連でパース結果が変わるケースもあります。この辺は何と言うか、Vim scriptのカオスな面が表れていますね。
元々Ex commandsは対話用途の代物なので、それを無理に拡張して今の(言語としての) Vim scriptに進んだことは間違ってます。vi cloneから出発し、拡張のための言語を搭載しようとする場合、Ex commandsをベースにするのは一つの選択肢として考えられなくはないのですけどね。
Vim plugin flydiff has been released. This version includes many minor bug fixes.
vim-flydiffを書いている最中に気付いたのですが、getbufvar(bufnr, '')の戻り値が間違っています。変数名を空文字列にすると、バッファbufnrのローカル変数のスコープを表す辞書が返されるべきなのですが、何故か常にカレントバッファのローカル変数のスコープを表す辞書が返されるのです。ソースを見たところ、単純なミスだったのでパッチを書いてvim_devにポストしました。
--- ../vim-7.1.300/src/eval.c 2008-06-26 10:32:40.000000000 +0900
+++ src/eval.c 2008-06-26 10:32:40.000000000 +0900
@@ -9901,16 +9901,13 @@
if (buf != NULL && varname != NULL)
{
+ /* set curbuf to be our buf, temporarily */
+ save_curbuf = curbuf;
+ curbuf = buf;
+
if (*varname == '&') /* buffer-local-option */
{
- /* set curbuf to be our buf, temporarily */
- save_curbuf = curbuf;
- curbuf = buf;
-
get_option_tv(&varname, rettv, TRUE);
-
- /* restore previous notion of curbuf */
- curbuf = save_curbuf;
}
else
{
@@ -9920,10 +9917,13 @@
* find_var_in_ht(). */
varname = (char_u *)"b:" + 2;
/* look up the variable */
- v = find_var_in_ht(&buf->b_vars.dv_hashtab, varname, FALSE);
+ v = find_var_in_ht(&curbuf->b_vars.dv_hashtab, varname, FALSE);
if (v != NULL)
copy_tv(&v->di_tv, rettv);
}
+
+ /* restore previous notion of curbuf */
+ curbuf = save_curbuf;
}
--emsg_off;
Vim 7.2a.002として取り込まれました。まあ些細なミスの修正で、大したことはなかったのですが、いずれはもっとデカい獲物を仕留めてみたいものです。参考: Patch 7.2a.002 - vim_dev | Google Groups
Vim plugin flydiffをリリースしました。
画面の左半分に編集中ファイルを、右半分にgit-diffの該当部分が表示されたら超便利じゃないかなと思った。
というujihisaさんの発言が元ネタです。このコンセプトを実現するとこんな感じになるというサンプル。
個人的にはgit-diffの実行で画面がちらつくのが嫌ですね(でもこれはどうしようもない)。今のところgit限定ですが、他のバージョン管理システムをサポートしても、それのdiffが遅いとストレスが溜まるのではないかと思われます。そういう意味ではgitは良いですね。
同一バッファを異なるウィンドウで別々に:Narrowしたいケースが時々あるので、narrowを修正しようかと思ったのだけど、どうやって実装すればいいかで困った。今のところ、:Widenのための復元情報をバッファ毎に持たせているので(b:narrow_original_state)、それに対して「どのウィンドウで:Narrowされたのか」というキーも含めれば取り敢えずは解決する。ただ、:Narrow済みのバッファを表示しているウィンドウを:splitした場合、新たに作成されたウィンドウが:Narrowされているかどうかが簡単には判別できないし、さらにどのウィンドウを:splitして作られたかが判別できないという問題があるので、完璧とは言えない。そもそも個々のウィンドウはbufnr()のような一意に識別可能な値を持たないので話はさらに面倒になる。
まあ、ウィンドウ(とついでにタブページ)の識別番号については既に対策は用意してあるし、:Narrowされているかどうかの判別も不可能ではないのだけど、:split元の判別はどうしよう。最初は漠然と:autocmd BufEnter等で対処できるだろうと思っていたけれど、そもそもウィンドウがどういう方法(:new、:split、:tabnew等々)で作成されたのかを判別できないので、不可能だということに気付いた。
と、ここまで書いてて、別に:splitに限らずとも、異なるウィンドウで:Narrow済みのバッファを表示させた場合(例えば:{range}Narrow | new | edit #等)に同様の問題があることに気付いた(さらに言えば同一バッファを表示しているウィンドウの各種オプションやfoldsが異なる場合、新たにウィンドウを作成してそこにそのバッファを表示させた場合、どちらの値が引き継がれるかが不明であることにも気付いた。挙動からして、恐らく最後にアクティブだったウィンドウの値が優先して引き継がれている)。ということは「異なるウィンドウで同一バッファを別々に:Narrowする場合、期待通りの:Widenが行われない可能性がある」で妥協しないと駄目なようだ。まあ、「期待通り」でないと言っても、問題が起こるのは&foldmethod == 'manual'のときだけなので、個人的には実害がなくて済む、かなぁ。
Vim特集があると聞いて「Software Design 2008年7月号」を購入しました。最初は立ち読みで済ませようかと思いましたが、id:taku-oさんが記事を書いていると知ったので、これは買わざるを得ないということになりました。という訳で各記事をdisるとします。
g:やs:等の接頭辞のない変数のスコープは文脈によってグローバルかローカルかが変わる。v:)と同じものは後者の変数として扱われる。この点はP143の「定義済変数」の節でも触れていない。l:は関数内部でのみ有効)。スクリプトの実行が終了した後も変数の値は記憶されたままです)も踏まえると、著者の理解がどうなっているのか怪しい。
Vimにはレジスタという変数がありよりは「Vimにはレジスタという概念あり、それも変数として扱える」の方が正確。
:printを使う方が効率的だけど、まあこれはどうでもいいか。:helpを引く際や他との意思疎通の面で困る。==や=~等の各種演算子の後に、'ignorecase'に関する挙動の違いで各々3種類あることを書くべき。autocmd BufRead *.txt call Autosave()でないと動作しない。'keymap'と混同される。:help key-notationを参照すること。:help keymapは'keymap'についての説明であり、key mappingとは全く関係がない。困った。内容的に「nanasi.jp出張版」なのでツッコミを入れる箇所がない。強いて言えば一般的なプラグインのインストール方法について説明があればと思ったが、それは1章の担当ということになるのか。
:pythonと記述すべき。実行結果の標準出力先はステータスバーですとあるが、
'statusline'と混同される恐れがあるので別の用語を用いた方がいい。ただこれは:helpでの表記がはっきりしていない。強いて言えばコマンドライン(the command-line)と呼ぶべき。Pythonスクリプトを標準入力からヒアドキュメントとして受け取りますとの記述もあるが、そもそもPython Interfaceで標準入力は利用できない。
:pyf[ile]や:py[thonと記述されているが、括弧は不要。vimなので、それに合わせて表記すべき。vim.command()の説明でVimモードのコマンドを実行しますとあるが、単に「コマンド」ではEx commandのことなのかNormal modeでのコマンドなのか区別が付かない。そもそも「Vimモード」が意味不明。
:call PostTwitter()。最近は左WindowsマシンでBGM(という名のえろげオートプレイ)を流しつつ右のMacBookで何か作業をしていることが多い気がする。デュアルモニターって便利だなー、と思ったり思わなかったり。
ただモニターに関してはMacBook内蔵のものより左のモニターの方が良いので、できればそちらで作業しつつ、右でBGMを流したいのだけど、どうしたものかな。やはりグレア加工は駄目だ。
Terminal.appがアクティブなときにAquaSKKで日本語入力をしていると、キー入力の一部が失われるのか、しばしば意図しない結果になります。よくあるのはhaと入力したつもりがhが落ちてaのみ入力されたことになり、結果としてあが入力されるパターンです(別にhaに限らず、あらゆるケースで発生し得る様子)。他には変換を確定したはずが何も入力されないというケースもあります。
今まで使用してきた感じではTerminal.app以外では発症していないため、Terminal.app固有の問題か、あるいはTerminal.app内で日本語入力をするのはGNU screen内で起動しているVimに対してしか行なわないといってもいいくらいなので、GNU screenかVimが原因ということになります。何が何やらさっぱり分かりません。
:silent bufdo !git-add %
この辺りにVim使いのセンスというか使い方というかが表れていると感じました。id:secondlifeさんはGNU screenでウィンドウをぽこぽこと作っては各々の中でVimを起動して使っているため、こういうコマンドやyanktmpなどのプラグインが有用なのでしょう。
私の場合、Vimのプロセスは常に一つだけ起動しておき、作業内容毎にtab page(と同時に各々のカレントディレクトリ)を分けておき、コマンドラインでの作業が必要な場合は:suspend (実際には別ウィンドウに切り替えた後、適切なディレクトリにcd)しているので、:bufdoなどは使いものになりませんし、yanktmpは役に立ちません。
先日のVim勉強会#2ではこの辺りの認識の違いを目の当たりにして一人で感心していたのですが、それを抜きにしても、他の方がどういう風にVimを使っているかが気になります。「このプラグインはLife Changingだぜ!」とか「このtipsはイケる!」といった話なら腐るほど目にしますが、使い方自体の話となるとほとんど目にしないので。
MacBookと同時に購入したiPodのおかげで、昔購入したCDを色々と聞いたりするようにはなったし、iTunesはiTunesでそこそこ便利かなと思いはしたのだけれど、全体として自宅外でiPod+添付されてたヘッドホンで聞く割合が増えたことと、iPodに転送するためリッピングしたデータがlossyなためか、耳が退化してきました。今日は何となくちゃんとしたヘッドホンを装備してCDを直接再生してみたのですが、もののみごとにiPodから流れる音と区別が付かなくなりました。むー。
ある意味では、このまま区別が付かない方が幸せなのかも知れませんが、何か悔しい。
先日のKansai.pm懇親会で話したネタその1: EmacsだとC-hにはデフォルトでhelp-command (ヘルプ関連の機能のプレフィックスキー)に割り当てられており、これは色々な意味でありえないのでdelete-backward-charに割り当てなおすことがmy .emacsの記述の第一歩だったりしますが、Vimだと両者を共存させられます。
nnoremap <C-h> :<C-u>help<Space>
普通、:helpを引く際は:h {subject}と入力しますが、上記の設定を用いて1キーで行なえるようにするとかなり便利です。些細な違いですが:helpを引く機会は結構あるので、長い目で見るとなかなかの効果を発揮します。
Kansai.pm第9回ミーティングに参加してきました。実のところPerlについては基本的な知識がversion 4止まりで、さらにここ5年くらいはノータッチだったので全速力でアウェーでしたが、各発表の内容にはついていけましたし、なかなか楽しめました。
特に気になったのはid:hakobe932さんのMooseについての発表です。Mooseについては名前程度なら知っていましたが、Class::MOPが存在していて、それのラッパーだということについては知らなかったので、「だからid:typesterさんとかid:cho45さんがMOPがどうこうと発言していたのか」と一人で納得していたりしました。逆にMOPという単語が出てくるまでは「マクロでできる程度のことなのに何故あれこれ騒がれていたのかな」とばかり思っていました。
他の発表については、説明の分かり易さやPerlの文法の変遷や特定の箇所の実装はどうなっているだろうか等と考えながら聞いていました。人によってはid:naoyaさんのMapReduce関連の発表が目的だったのでしょうけど、個人的には結構前にオリジナルの論文を読んでいたので今一新鮮味がなくて残念でした。
懇親会では色々な方とお話をしましたが、特にid:hakobe932さんとお話しました。Vim勉強会#2に来られていなかったというのもありますが、Vimに限らず色々と。特にMac OS Xでデフォルトのウェブブラウザの設定が可能だということを教えてもらったので5回程インクリメントしました(Windows版Operaだと初回起動時にダイアログが出るのですが、Mac版Operaだと出なかったので、これはSafariを使えというAppleの陰謀なんだろうなぁ、と今まで結構不便な思いをしていました)。
参加前はPerlを知らないと辛いだろうかと思っていましたが、全然そんなことはなかったですし、普段なら全然接触のない方達と色々とお話できたので十分楽しめました。
Vim勉強会#2に参加してきました。Vimを使いこなしたいと頑張る人からVimに取り憑かれた病人まで、とにかくVimについて並々ならぬ思いを抱いた人達が集合していました。参加人数は13名。なかなかに充実したイベントでした。
前半はテキストである「Vi IMproved - Vim完全バイブル」の8章から13章までをextreme readingしました(GUI版限定の解説である10章は除外)。実のところextreme readingは未経験でしたし、発言するにしてもタイミングや内容のレベルの適切さを図り辛かったので、話題の提供は他の方に任せて、私は他の方からの疑問に答える形で読み進めていきました。
以下、当日の流れと主な話題:
:abbreviateが長い:abでも構わない。:printは何に使えるのだろう:globalと組み合わせると便利な場合がある。例: :global//printで現在の検索パターンにマッチする行を表示(限界まで省略すると:g/)。:substitudeって何だろう:sのこと。誰もフルネームで覚えていない。次のコマンドをタイプすると80文字目で改行を行うことになりますは間違い。
'textwidth'と'wrapmargin'は排他的で、前者が優先される。なので本文中の設定では70文字目で改行が行われる。'formatoptions'で行う。:setlocal formatoptions+=mと設定しておく。'formatoptions'やその他のオプションは適宜設定されている。コメントの記号も正しく認識されたりする。:help usr_24でVim can automatically complete words on insertionの一文があるので、原著ではそこから章題を「automatic completion」としているのだろうけど、何をどう間違ったのかcompletionを「完了」と訳してしまったらしい。
i_CTRL-Xの補完あれこれi_CTRL-X_CTRL-Fのファイル名補完はさりげなく便利。i_CTRL-X_CTRL-Eとi_CTRL-X_CTRL-Yは謎すぎる。後半はid:Sixeightさんによるtech talk (Vimとの出会いなど)、id:secondlifeさんによるtech talk (Vimとの出会いなど/雑多なtipsの紹介)、残り時間でid:YAAさんによるlightning talk (vimperatorのデモ)がありました。
ある意味、今回の目玉はid:secondlifeさんのお話なのですが、ところどころ妙な読みがあって吹かざるをえません。enableは「いなぶる」、/\v (very magic)は「べりーまっち」、fuzzyは「ふぃじー」など。最初の2つは納得できなくはないのですが、「ふぃじー」には負けました。他には「アルファベット27文字」が破壊力抜群でした。
id:secondlifeさんは残り時間でご自身の.vimrc紹介では意外とツッコミどころがあって個人的には楽しかったです。特定のキーバインドを無効にするためには<Nop>にマップすべき、検索時のハイライトを一時的に消すには:nohlsearchを使うべきなど。実は他にも指摘できるところはありましたが時間が時間なので黙っておきました。
勉強会後はid:ns9tksさんとid:secondlifeさんとお話したり、11人程で適当なお店に行って一緒に昼食をいただきました。色々とVim関連のお話ができて楽しかったです。
本当はまだまだ話をしたかったのですが午後のKansai.pmの時間が迫っていたので中途半端に終ってしまいました。特にid:ns9tksさんとはあまり話せなかったので悔しいかぎりです。これはもうKansai.vimなどを主催せざるをえませんね!