| 2003年4月28日 | 初稿 |
| 2008年8月28日 | VS2008での状況について追記 |
Visual Studio .NET(以下VS.NET)のキー操作をEmacsに近づけようとするときに問題になるのがC-SPCに代表されるマーク・リージョン操作です。VS.NETのキーカスタマイズは2ストロークのキー割り当てに対応していますし、ほとんどの機能がキーに割り当て可能になっています。そのためたいていの操作は簡単にEmacs風にカスタマイズできます。しかしEmacsのマーク・リージョン操作はVS.NETに該当するコマンドがないため単純なキーカスタマイズだけでは実現できません。以前のVisual StudioにはC-SPCも含めてもろもろをEmacs風にできるepsilon互換設定があったのですが、VS.NETからは見あたらなくなってしまいました。
(2008-08-28追記)Visual Studio 2005やVisual Studio 2008になって、キーボードスキームとしてEmacsが選べるようになりました。VS2005は色々と不具合があったのですが、VS2008ではだいぶまともになり、この記事のような荒技は不要になったと思います。XKeymacs等と併用すれば、ダイアログ等でもある程度Emacsキーバインドで操作できます。それでも出力ウィンドウでPageUpやPageDownが出来ない等の問題が若干残ります。
この記事ではEmacsのマーク・リージョン操作を実現するVS.NET用のマクロを紹介します。このマクロによってできることを次に挙げます。
また、このマクロによってできないことを次に挙げます。
これらの機能を提供するマクロやVS.NET既存のコマンドを好みのキーに割り当てることで、Emacs風のキー操作が実現可能です。
記事の最後ではこのマクロの動作原理やVS.NET用マクロの簡単な作り方について紹介します。
マクロのソースを以下に示します。
新しくマクロプロジェクトを作ってもいいですし、MyMacrosあたりにぶち込んでもかまいません。ソースの内容をIDE内に登録して呼び出せるようにします。
新しくマクロプロジェクトを作るには、VS.NETを起動し、メニューの「ツール(T)」→「マクロ(M)」→「新しいマクロプロジェクト(W)」で適当な名前のマクロプロジェクトを作成してください。私の場合 EmacsKeyBinding にしました。
マクロプロジェクトの中にソースの内容を入れるには、マクロエクスプローラからプロジェクトを右クリックして、「新しいモジュール(D)」を選択。ファイル名を EmacsKeyBinding としてください。できたファイルの中にソースの内容を上書きしてください。
次に「ツール(T)」→「オプション(O)」の「環境」→「キーボード」でマクロをキーに割り当てます。
本マクロで定義しているプロシージャとEmacs風キー操作との対応を下表に示します。
| プロシージャ | キー |
|---|---|
| EmacsKeyBinding.SetMarkCommand | C-SPC または C-@ |
| EmacsKeyBinding.KeyboardQuit | C-g |
| EmacsKeyBinding.ForwardChar | C-f ※注 |
| EmacsKeyBinding.BackwardChar | C-b ※注 |
| EmacsKeyBinding.NextLine | C-n ※注 |
| EmacsKeyBinding.PreviousLine | C-p ※注 |
| EmacsKeyBinding.KillRingSave | M-w |
| EmacsKeyBinding.KillRegion | C-w |
| EmacsKeyBinding.KillRectangle | C-x r k といきたいけど無理なので適当に |
| EmacsKeyBinding.ExchangePointAndMark | C-x C-x |
※注:カーソル移動系のプロシージャ(ForwardChar、BackwardChar、NextLine、PreviousLine)は暫定マークモードを使用しない場合は割り当てなくても構いません。割り当てるとカーソル移動のたびにマクロが呼び出されるため、エディタの動作がやや遅くなってしまいます。ただし、割り当てない場合はカーソル移動時の選択範囲表示ができなくなります。どちらにせよリージョン操作には影響ありません。
よりEmacs風にするため、既存のコマンドに対するキー割り当ても変更しましょう。次に私が行っているキー割り当てを示します。
| コマンド | キー |
|---|---|
| 編集.貼り付け | C-y |
| 編集.1画面分下へ移動 | C-v |
| 編集.1画面分上へ移動 | M-v |
| 編集.ドキュメントの開始位置に移動 | M-< |
| 編集.ドキュメントの終了位置に移動 | M-> |
| 編集.行頭に移動 | C-a |
| 編集.行末に移動 | C-e |
| 編集.次の単語に移動 | M-f |
| 編集.前の単語に移動 | M-b |
| 編集.インクリメンタル検索 | C-s |
| 編集.前を検索 | C-r |
| 編集.右に1文字移動 | C-f |
| 編集.左に1文字移動 | C-b |
| 編集.1行下へ | C-n |
| 編集.1行上へ | C-p |
| 編集.1語削除 | C-h |
| 編集.EOLまで削除 | C-k |
| 編集.削除 | C-d |
| 編集.単語の最後まで削除 | M-d |
| 編集.元に戻す | C-/ |
| 編集.行が中央になるまでスクロール | C-l |
| 編集.行に改行を挿入 | C-m |
| 表示.次のタスク | C-x C-` |
| 表示.コマンドウィンドウ | M-x |
| ファイル.ファイルを開く | C-x C-f |
| ファイル.閉じる | C-x k |
| ファイル.選択されたファイルを上書き保存 | C-x C-s |
| ウィンドウ.ウィンドウ | C-x C-b |
一部のキーは既に登録されている2ストロークキーと衝突します(C-rやC-k、C-m等)。あらかじめ衝突するキー割り当ては削除してください。設定ダイアログが使いにくくて大変ですが。
最後に、暫定マークモード(リージョンの強調表示)を行いたい場合はソース中のtransientMarkMode定数の値をTrueに変更してください。暫定マークモードは次のような動作を行います。
このマクロは各種ダイアログやマクロIDEには効果ありません。我慢できない場合は、設定が少々複雑になりますがXKeymacsのようなキーカスタマイズツールと併用するとある程度改善されます。
XKeymacsの設定では、「Microsoft Visual Studio .NET (devenv.exe)」に対するプロパティを変更します。設定のポイントを以下に挙げます。
まずはEmacsKeyBindingモジュール内の各要素について解説します。名前はできるだけEmacsの同等の要素に対応させています。
クラス一覧
変数一覧
定数一覧
プロシージャ一覧
ほとんどの機能はDTE.ActiveDocument.Selectionを操作することで実現できます。(参考:TextSelection オブジェクトのプロパティ、メソッド、およびイベント)
例えばSetMarkCommandで現在の位置を取得するにはDTE.ActiveDocument.Selection.ActivePointプロパティで取得できます。ActivePointはVirtualPointを返すので、画面の影響を受けないEditPointにしてからマークリングに格納しています。
これができれば、最後に記録したマークと現在位置の間にリージョンが形成でき、KillRingSaveやKillRegionが呼び出されたときにリージョンにあわせて選択範囲を再設定して、コピーやカット処理が可能になります。KillRingSaveやKillRegionは呼び出されたときに毎回自分で選択範囲を再設定するので、その前に選択範囲がリージョンと同じになっている必要はありません。
大事なのは選択範囲の維持(つまり暫定マークモード)はおまけであるということです。普通、Emacsのリージョン指定はWindowsアプリでは選択範囲の指定に置き換えられます。しかし数多くある移動コマンドすべてに対応して、常に選択範囲を維持するのはなかなか困難を伴います。検索コマンドやマウスのクリックなど多種多様な方法でカーソルは移動します。それらに対して、C-SPCが押された後だったら選択範囲の拡張をして、そうでなければ普通の移動をするというような処理を挟むのは難しいでしょう。外部のキーカスタマイズソフトを使えばシフトキーを押し下げてエミュレートできます。しかし、思わぬところでキー操作がおかしくなるトラブルが発生する場合があります。せっかくマクロを使ってエディタの内部情報を取得できるのですから、ちゃんとEmacsのマーク・リージョンの概念を実装した方がいいでしょう(ただ、ダイアログ等はこのマクロが効かないので、そのへんは外部のキーカスタマイズソフトのお世話になりたいところです)。マクロを使えばこれまで選択範囲を使って擬似的にしか表現できなかったマーク・リージョン操作が選択範囲やボタンの押し下げ状態に依存せず実現できることが分かります。
以下にここで紹介したマクロの課題を挙げます。
今回初めて自分でVS.NETのマクロを作成したわけですが、最初は何を参考にすればいいかも分からず戸惑いました。そこでマクロを作る際のヒントを残しておきます。
簡単な方法はMSDN Libraryにも書いてありますが「ツール(T)」→「マクロ(M)」→「TemporaryMacro の記録(C)」を使ってマクロを記録し、その記録されたマクロ記述MyMacrosのRecordingModuleあたりを参考にして、希望の動作がどのように記述できるかを調べます。
大体はどこかのオブジェクトへのメソッド呼び出しになっているはずです。オブジェクト名やメソッド名をMSDN Libraryで引いて、理解を深めます。同じオブジェクトのほかのプロパティやメソッドに使えそうなものが無いか調べます。
そうして調べたプロパティやメソッド呼び出しをうまく制御するようなプログラムを組みます。Visual Basicの文法を知らなければならないので、言語仕様やサンプルマクロ(Samples)を参考にします。
MSDN Library内の参考になる部分を次に挙げます。