Nothing but Programming

My Daily Programming Life...

XVimの紹介

Vim Advent Calendar 2012 27日目の記事です。

XcodeでのVimキーバインドを実現するXVimの開発経緯と使い方をご紹介したいと思います。まず初めにURLだけ貼っておきます。
https://github.com/Jugglershu/XVim

さて、本題。ちょうど今から1年前、僕は友達ともにiOS向けアプリの作成を趣味で行っていました。当時僕はMacはあまり使ったことがなく、Objective-Cの個性あふれるシンタックスに苦しんでおりました。iOSアプリの開発なので、当然Xcodeを使うわけですが、シンタックスに苦しんでいるところへXcodeではデフォルトだとVimのキーバインドではなくEmacsのそれとなっており、そのままだと使い物になりません。

Vimのキーバインドにするにはどうしたらいいのか友人に聞いたら、「ない」との答え・・・またまた〜、そんなことないでしょ〜。まあきっとどこかの誰かがそういうの作ってくれてるよと、軽い気持ちでググり、いまどきVimキーバインドがないなんて、そんなわけが・・・「ほんまや!」

というわけで、ないわけです。とても不思議です。世界中のVimプログラマはどうやってiOSアプリを書いているのでしょうか。確かにVimオンリーで書くという選択肢もありそうです。実際、Cocoa.vimなるプラグインもあった(いまも動く?)ようですが、ちょっと使ってみた感じだとあまりうまく動きませんでした。なにより、Xcodeの補完機能や、その場でエラー・ワーニングチェックなどiOSアプリを書く上では非常に有効な機能を捨ててしまうのはあまりにも惜しいです。

しかし、なぜXcode向けのVimキーバインド(プラグイン)が存在しないのか。もしかするとXcodeがプラグインに正式に対応していないというのが大きな理由かもしれません。実際プラグインの作り方に関するドキュメントなどはどこにもありません。幸い、これまでWindowsでAPIフックなどのプログラムを書いてきたこともあり、自分で作ったライブラリさえXcodeプロセスに読み込ませられれば、あとはなんとかできる自信があったため、とにかくその方法を探しました。実はXcodeは内部的にはプラグインのサポートがされているようで、その機能を使うことでXcodeのプラグインを読み込ませることができることがわかりました。

ただし、分かっているのは作ったプラグインがロードされ、初期化時に特定のメソッドが呼び出されることだけです。ここからはひたすら試行錯誤。幸いObjective-Cは動的バインド言語であり、Xcodeのクラスやメソッドの名前などはすべて確認することができ、またどのようなメソッドが呼び出されているかも簡単なコードを追加するだけで確認できます。また、特定のクラスのメソッドのフックもバイナリを直接編集することなくObjective-Cのライタイムを頼って実現することができます。

そこから2週間ほどかけてとりあえず動くものを作りました。うれしいことに、テキスト編集自体の機能はNSTextViewやXcodeが内部的に使っているDVTSourceTextViewクラスに実装されており、キーイベントに対応させてそれらを呼び出すことで基本的な編集機能は実装できました。

それからいろいろな方の協力もあり、現在は以下の様にステータスバーがあったり、太いカーソルになったりと見た目も良くなっております。
だいぶ、Vimの話からずれてしまいました。ここからはXVimの便利な機能をご紹介したいと思います。XVimはVimのキーバインドを実現するためのものなので、Xcode上でVimのキーバインドで編集できることはごく標準的な機能です(まだまだ対応していないバインドはありますが)。一方でXcode特有の機能を使うために実装したXVim特有のコマンドもあります。

最も個人的に使う頻度が高いのが、メニューの特定のコマンドを呼び出すコマンド
:xccmd
です。Cocoaアプリではメニュー内の各項目にはAction名が割り振られているため、そのアクション名を指定することでメニュー上にあるコマンドはすべて実行することができます。たとえば、
:xccmd jumpToNextCounterpart
と入力した場合、.hと.mファイルの切り替えを行います。切り替えのためだけにこれを打ち込むのはばかばかしいので、XVimの.vimrcにあたる、.xvimrcファイルを作成し
nmap <c-n> :xccmd jumpToNextCounterpart<CR>
と書いておきます。これでによって、この機能を呼び出すことができるのです。
xccmdの引数部分はhttps://github.com/JugglerShu/XVim/blob/master/Documents/Developers/MenuActionList.txt
に一覧があります。

ほかには、開発時によく行うビルドと実行はそれぞれ

:make
:run
で起動できるようになっています。もちろん上に書いたように自分の好きなキーを割り当ててもよいです。メニューのショートカットについてはXcode自体に設定があるのでそれを利用しても良いですが、XVimで設定するとnmap, imapなどに対応している点が良い点だと思います(と言いつつ、個人的にはこれら違いによるショートカットを使っていなかったりしますが。)

もう一つ有効な機能として、
:xhelp
コマンドがあります。これはクイックヘルプを呼び出すために実装してあるコマンドです(:xccmdコマンドでも実行できます。)これは現在のカーソル位置にあるメソッドやクラスのヘルプをその場に表示してくれるというもので、引数の意味などを確認するのにとても便利です。

一部のキーバインドは、Vimの定義ではなくXcodeの機能で置き換えれています。"gd"が、典型的な例で、Vimでは"Goto locfal Declaration."ということで、宣言へのジャンプとなっていますが、これはXVimではXcodeの"Jump to Definition"を呼び出すようになっています。そのため、定義部分で入力すれば、宣言部分にジャンプしますし、宣言部分で入力すれば定義部分にジャンプします。またカーソル下のファイル名のオープンはVimではgfですが、Xcodeではこの"Jump to Definition”でジャンプできるため、ファイル名上にカーソルを置いて、"gd"コマンドでヘッダファイルのオープンなどができます。

このように、Vimキーバインドプラグインですが、Xcodeとうまく連携するようにということを意識して作成しています。現在の機能一覧は以下を参照してください。
FeatureList

さて、XVimの開発状況なのですが、最近ちょっと滞っております。というのもこれ以上機能を追加しようとすると少しバグが多く発生するような状況になってきており、少し根本的な設計見直しが必要と判断しているためです。現在github上の別ブランチにて作業をしておりmasterはあまり更新されておりません。ただし、比較的安定しているはずですので、しばらくはそちらを利用していただくのが良いと思います。

今後開発予定の機能としては、矩形選択、検索・置換機能の強化が大きなところとなりそうです。対応コマンドの充実などやりたいことはたくさんあるのですが、なかなか前に進めない状況だったりもします。

なお、XcodeのVimキーバインドとして、最近ライバルが出現しました。http://viciousapp.com/がそれです。ちなみに20ドルだそうです。負けないように頑張りたいと思います。

Vim Advent Calendar 2012 明日は@sonotsさんです。

WordPressに

WordPressにこのwebサイト全体を移行しようかと考え中。
もう、最近HTMLとか自分で編集するの面倒になってきた。

KeyRemap4MacBookProとControl,CommandキーとMacVimと

数ヶ月前からMacBookAirを使い始めて、Commandキーでいろいろな操作ができるショートカットがいまいち
気に入らず、今まで通りControlキー(左手の小指)でショートカットを利用したいと思い、
ControlとCommandキーを入れ替えることにした。

方法は簡単でKeyRemap4MacBookProをインストールして、あとは設定で
”Change Control_L Key"の中から、Command_Lと入れ替えるものにチェックを入れれれば良い。

でも、いろいろオプションがあって、たとえば、"Emacs以外は入れ替え"とか、"TerminalとChromeとRemoteDesctop以外は入れ替え"とかがある。

僕はTerminalとかはそのままControlキーはControlキーとして使いたかったので
"except Terminal,Virtual Machine, RDC"
のものを選択した。

これでほとんどの場合には満足できたのだけれど、1つだけ満足できない所が。
MacVimでControlとCommandが入れ替わってしまう。
Terminal上で操作するVimは上記設定で問題なくControlキーはそのままになるのだけれど、
MacVimはそうなってくれない。

調べてみると自分で設定を書けるらしい。いろいろ戸惑ったものの、結果以下のようなXMLをprivate.xmlとして保存しておけば大丈夫ということがわかった。これを読み込むとMyKeyMapという項目が追加されるのでそれにチェックすればOK
(この場合、規定で入っているChange Control_L Keyは使わず自分の設定のものだけにチェックつける)


<?xml version="1.0"?>
<item>
<name>KeyRemap4MacBook Developer</name>
<list>
<item>
<name>MyKeyMap</name>
<list>
<item>
<name>Control_L to Command_L (except VI, Terminal, Virtual Machine, RDC)</name>
<identifier>remap.MycontrolL2commandL_extermvm</identifier>
<not>VI, TERMINAL, VIRTUALMACHINE, REMOTEDESKTOPCONNECTION</not>
<autogen>--KeyToKey-- KeyCode::CONTROL_L, KeyCode::COMMAND_L</autogen>
</item>
</list>
</item>
</list>
</item>


HeapSort (C++ Template関数実装)

久しぶりに、プログラミング。
今日はいままで自分で書いたことなかったヒープソート。


#ifndef __HEAPSORT_H__
#define __HEAPSORT_H__

template 
void MaxHeapify( T* target, unsigned int index , unsigned int length) {
	unsigned int l = index<<1;
	unsigned int r = (index<<1)+1;
	unsigned int max = index;
	if( l < length && target[index] < target[l]	){
		max = l;
	}
	if( r < length && target[max] < target[r] ){
		max = r;
	}
	if( index != max ){
		T tmp = target[index];
		target[index] = target[max];
		target[max] = tmp;
		MaxHeapify( target, max, length );	
	}

	return;
}

template 
void BuildMaxHeap( T* target, unsigned int length ){
	for( unsigned int i = (length-1)/2; true ; i-- ){
		MaxHeapify( target, i, length );
		if( 0 == i ){
			break;
		}
	}
}


template 
void HeapSort( T* target, unsigned int length){
	BuildMaxHeap( target , length );
	while(length != 0 ){
		T tmp = target[length-1]; 
		target[length-1] = target[0];
		target[0] = tmp;
		length--;
		MaxHeapify(target, 0, length );
	}
}

#endif



includeしてから次ように使う。


unsigned char buf[] = {5,7,10,8,11};
HeapSort( buf, 5 );
//ここでbuf内はソート済み。

実装時の設計確認

これは個人的なTIPSとして、プログラムを書くときに、詳細設計がきちんとできていて、将来に渡ってメンテナンスしやすいかどうかの判断基準として、気を付けていることのメモ。

二つあって、どちらも同じような目的のために確認している。

1つ目は、後からテストコードを書く場合は、テストコードがどのように実行できるかを想像しながら実装する。すると、テストしたい部分を自然に切り離しながら実装したくなる。自動的にテストしにくい部分がどんどん一か所にまとめられていって、結果として後からいろいろ変更したり、機能追加がしやすくなる。

2つ目は、今追加しようとしている機能をOFFにしようとしたとき、どれくらいの場所を修正しなくてはいけないかを考える。1行コメントアウトするだけでそれができるのがベスト。できなくても、1ファイルの1ブロック(画面内で確認できる範囲)を修正すれば機能をOFFにできればまあ、問題ないレベル。
1ファイルの複数個所、という場合には、まず設計を見直す。OSの仕組みなどにより、どうしてもそうせざる負えない場合には、よくコメントを書いておくなりドキュメントしておくなりしないといけない。
複数ファイルを直さなきゃいけない場合は、1度よく設計を考え直した方がいいかもしれない。もうそれしか方法がないときだけ、コメントを付けて、どことどこに対応関係があるかを書いておく。
複数ファイルの複数個所を直さないと機能をOFFにできないとか、こういう状況になったら、そもそも何かがおかしいと思った方がいい。だいたい1つのファイルに複数の機能が同時に実装されていたり、2つのファイル(モジュール)が互いに依存しあっていたり、機能の定義があいまいだったりするのが原因で、その後その上にいろいろ機能を追加していけばしていくほど、バグは多くなる。

RTX1000の設定 - シリアル接続がない場合

自宅でYAMAHAのルータ RTX1000を使っている。
このルータ、業務用だけあってユーザフレンドリでは全くない。
設定はコマンドラインで行うことになっているし、コマンドも技術的なことが分かってないと何もできないぐらい難しい。

さて、このルータ、初期状態だとIPアドレスが振られていない。つまり自宅のネットワークに繋いでも、そこにIP層レベルのアクセスはできない。
じゃあ、どうやってIPアドレスを振るのかといえば、基本的にはシリアルケーブルを繋いで、terminal経由で設定する。
ただ、いまどきシリアル端子なんて持ってるPCの方が少ない。僕の使ってるPCも例外ではなくそんなものはもってない。

以前に設定したときは、USBとシリアルの変換コネクタとケーブルを買ってきて設定した。
今回、もう一つ新しくRTX1000を買って、実家とのVPN接続をさせておこうと考え、設定しようとしたのだが、昔買ったはずのケーブルが見つからない。

で、そういう場合にRARPDを用いてIPアドレスを振る方法があるということを知り、今回やってみた。

基本的には以下のページの通り。
http://www.inohome.net/kuma/blog/archives/2006/12/post_606.html

ただし、1つはまったところがあったのでメモ。
ここで使っているRARPDというソフトウェアが「No unique Registry key for TCP/IP over LAN」というエラーを出して終了してしまう。
レジストリキーが複数あるってことなんだろうけど、よく分からない。
うれしいことに、RAPRDにはソースコードが付いてくる。というわけで見てみると、どうやらEthernetのインターフェースをレジストリから探していて、それが複数見つかった場合にはエラーを出すらしいと言うことが分かった。

確かに、僕のPCにはEthernetアダプタが2つついている。VMWareの仮想デバイスとかもあるから、その辺りも関係しているかもしれない。

ということで、メインのネットワーク以外はすべて無効にした。


こんな感じで。それで起動したらちゃんと動いて、みごとにRTX1000にIPアドレスを振ることができた。

これができればRTX1000を設定するのにシリアル端子は必要ないってことだ。

東北太平洋沖地震

被災状況をテレビで見るのが辛い。
親を亡くした子供、目の前で自分の奥さんが流されてしまった人、自分の娘が流されてしまった人、そんな辛い状況がどこにあるだろうか。見ていられないぐらいに悲しい。

今回は、東京にいながら、その恐怖を感じたせいか、その悲しさがものすごく大きい。
今回もある意味では、僕が感じられることは人ごとレベルでしかないのだろう。
結局東京ではその悲惨さはわからないかもしれない。それでも今まで感じたものよりずっと大きな感情的な動きがある。

一人でも多くの人が助かってほしいと思う。


こういうことを目の当たりにすると、僕たちが普段考えている幸せや、生きていく上で大切なことが何なのかということを考えさせられる。
人が、その人の一生を豊かに、幸せに生きていくと言うことと、自分が今毎日の生活の中で行っていることの間に大きな忘れ去られた空間が広がっているのではないだろうかと感じた。
忘れてしまうのだと思う。

家族とともに、いろんな困難を乗り越えて毎日生きていくこと、生きていけること。それが何より重要なことであり、それがすべてなのだと思った。それ以外のことは、おまけみたいなものなんだ。
feedSubscribe to my feed