TIM Labs

kanaによるエントリー一覧

問題

僕が一番 Vim のタブページをうまく使えるんだ(カレントディレクトリ編) で述べたように、 Vimのタブページはとても便利ですが、 ちょっと工夫するだけでさらに便利になります。

ところで、一般に Vim を使うような方は黒い画面を眺める機会が多いと思います。 また、端末をより便利に使うために GNU screen 等の端末マルチプレクサーを使用している方も多くいるはずです。 すると、 Vim でソースコードを編集しつつ、時折端末に切り替えて作業する......ということが多発します。

この時、以下の点で不便に感じることがあります。

  • 端末をアクティブにすることが面倒(GUI 版の Vim を使用している場合)。
  • 作業用のウィンドウに切り替えることが面倒(GNU screen 内で Vim を起動している場合)。
  • シェルで作業ができる状態になったとしても、 Vim で注目しているカレントディレクトリに移動する手間がかかって面倒。

特に最後の点が大問題で、 せっかくタブページ毎にカレントディレクトリを設定できるようにして便利さを高めたのにもかかわらず、 端末を使おうとするとその便利さも片手落ちという状態です。

つまり、 作業用のシェルにフォーカスを移し、さらにシェルのカレントディレクトリを Vim で注目しているディレクトリに切り替える ことが簡単にできるようにしたいものです。 しかし、一体全体どうすればよいのでしょうか。

問題

Vimのタブページ はとても便利です。 例えばウィンドウを分割して複数のファイルを並行してにらめっこしていたとしましょう。 その最中に誰かからの依頼等で全く関係のない作業をやることになったとします。 このとき、

  1. 新しいタブページを開いて、
  2. そこで作業を進めて、
  3. 作業が完了したらタブページを閉じて、
  4. 先程まで行っていたにらめっこ作業を再開する

という形で中断された作業をスムーズに再開することができます。 やりましたね。

しかし、これだけではまだタブページを全力全開で活用しきっているとは言えないのではないでしょうか。

問題

一旦 Vim がメキメキと使えるようになるとプラグインもバリバリと導入したくなります。 最初は数個のプラグインを試しに使っているだけだったのが、 時を経るにつれて数多のプラグインを使うようになってくるものです。

こうなると問題になるのはプラグインの管理です。 数が増えてくると新しいものをインストールするのもインストール済みのものを新しいバージョンに更新するのも億劫になります。

そこで必要になるのが Vim プラグインを管理するためのツールです。 この手のツールは2008年8月に公開された vim-pathogen が最古だと思われますが、 とりわけ民衆に認知され始めたのは YAPC::Asia 2009 で発表 された Vimana からだと思います。

この手の Vim プラグインマネージャーは既にいくつかの実装が存在するものの、いずれも欠点が存在します。

問題

Vim の正規表現 はとても豊富で、 ベーシックなもの から アドバンスドなもの までかなりの道具が揃っています。 これを駆使すればありとあらゆる......は言い過ぎですが、 世の中のテキスト処理の九分九厘を快適に行うことができます。

ただ、あまりにも数が多いので、一体どこから覚えていけばいいのか困りますし、 :help に記載されているものの中には今後の人生で活用する機会があるかどうか分からないものもあります。 手っ取り早く正規表現力を高めて生産性を向上するには一先ずどれを覚えれば良いのでしょうか。

問題

世の中にはシンタックスハイライトを行うツールが既に多数存在しています。例えば以下のようなものがあります:

メジャーな言語やフォーマットなら標準でシンタックスハイライトの設定が同梱されていますが、 ニッチな言語やフォーマットだとまずそのような設定は存在しません。 それならば独自に設定を書けばいいのですが、 大抵のツールでは構文の定義方法が特定のパーツに該当する正規表現を並べるだけなので、 言語によっては構文の妥当な記述が不可能な場合もあります。

となると独自に実装せざるを得ません。 例えば Vim の :help ドキュメントを良い感じに Web ブラウザ上で見るためのツール を作って Heroku で動かそうと思った場合、 Vim の :help ドキュメントの構文のいい加減さ から、まず既存のツールを利用してのシンタックスハイライトはできません。 さらにこのツールの場合はリンク周りもあれこれ面倒を見る必要があるため、 ますます既存のツールの利用はできません。

という訳で元のソースをパースしていい感じにシンタックスハイライトする仕組みを作る必要があります。 ことパースに関して言えば Ruby には様々な gem が存在しているので、そのどれかを使うことになります。 パース関連で言うと以下のような gem があるのですが:

  • treetop (Lex/Yacc のように独自 DSL を Ruby のコードへコンパイルする必要がある。ダサい)
  • citrus (treetop と同様のダサさ)
  • rsec (Ruby 上の DSL で構文の記述を行う。でもその DSL がダサい)
  • parslet (Ruby 上の DSL で構文の記述を行う。 DSL が超COOOOOOOOOL。構文エラー時の出力もCOOOOOOOOOL)

という感じなので parslet 一択という状態です。 そういう訳で parslet を使って Ruby でシンタックスハイライトを実装してみましょう。

問題

パッチのレビューなどで Git の diff の出力を読む機会はそれなりにあると思います。 その際、 diff で列挙されている内容だけでなく前後のコードも確認するために変更のあったファイルを開くことも多々あるでしょう。

Vim にはこの状況にぴったりのコマンド gf があります。 gf はカーソル下にあるテキストからファイル名らしき文字列を探してそれを開くというコマンドです。 diff の出力には変更のあったファイルのパスが含まれていますから、 そこへカーソルを移動して gf を使えば良いというわけです。

gf はとても便利なコマンドではあるものの、 上記の操作を何度か行っていると不満が募ってきます。 というのも、以下のような手間があるからです:

  • gf を実行するためにパスの書かれている位置までカーソルを移動しなければならない。
  • gf でファイルを開いた後、レビューしたい場所までカーソルを移動しなければならない。

例えばカレントバッファに以下の内容が含まれているとしましょう (左端の数字は行番号なので適宜無視してください):

 1 diff --git a/autoload/gf/diff.vim b/autoload/gf/diff.vim
 2 index 469fdb3..b135316 100644
 3 --- a/autoload/gf/diff.vim
 4 +++ b/autoload/gf/diff.vim
 5 @@ -21,7 +22,7 @@
 6  "     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 7  "
 8  " Interface
 9 -function! gf#diff#go_to_hunk(type)
10 +function! gf#diff#go(type)
11    let d = gf#diff#investigate_the_hunk_under_the_cursor()
12    if d is 0
13      echomsg 'There is no diff hunk to jump.'
14 @@ -113,7 +114,7 @@ function! gf#diff#investigate_the_hunk_under_the_c
15        return 0
16      endif
17      let [d.from_path, d.to_path] = xs
18 +  call setpos('.', original_position)
19 -  call setpos(original_position)
20
21    return d
22  endfunction

このとき:

  • 12行目で gf をすると autoload/gf/diff.vim の25行目まで移動する。
  • 16行目で gf をすると autoload/gf/diff.vim の117行目まで移動する。

のような感じで 手作業によるカーソル移動なしに目的の位置へ移動 できればとても便利です。 このように gf の動作を拡張するにはどうすれば良いでしょうか。

問題

世間一般のエディタではカーソルを行頭や行末に移動するキーが C-a だったり C-e だったり Home だったり End だったりと、 それなりに押下し易い配置になっているのですが、 Vim では何を思ったのか ^$ という謎のキー割り当てになっています。 この謎のキー割り当てにはそれなりの理由があると思われるのですが、 時々不便に思う場合があります。

例えば「カーソル行のテキストをコピーして他のアプリケーションにペーストする」というシチュエーションを考えてみましょう。 コピー自体は "*yy ですぐできるのですが、これでは改行文字が含まれるため、ペーストした際に悲しい結果を招くことが多々あります。 ^vg_"*y0v$h"*y とすれば改行文字抜きでコピーはできるものの、 キー押下難易度の高さにストレスが溜まってしまいます。 どうにかして快適にカーソル行のテキストを選択/処理できないものでしょうか。

問題

Reactive Extensions で非同期処理を簡潔に記述する では「キー入力に応じて補完候補を表示する」「ただし補完候補はAjaxで非同期に取得する」という いまどきのWebアプリケーションにならあって当然の機能が、 Reactive Extensions (Rx) を使うことであたかも普通のリスト処理のように記述できることを示しました。

入力補完は例としては単純で分かり易いものの、 もう少し別の例も欲しいところです。 という訳で、 Rx を使うことで コナミコマンド を実装してみましょう。

問題

シェルはお友達です。 一見すると役に立たないように思えるコマンドでも、 組み合わせ次第で複雑な処理をこなすための道具になります。

例えば

  • head - ファイルの先頭10行を出力する。
  • cut - ファイルの各行のうち特定の部分を選んで出力する。
  • shasum - SHA-1 ハッシュ値を出力する。

という、一つ一つでは大して役に立ちそうにないコマンドも、

 head /dev/urandom | shasum | cut -f 1 -d ' '

このように組み合わせることで「ランダムなパスワードを自動生成する」という偉業を達成することができます。

シェルをある程度使っていると上記のようなワンライナーをしばしば入力することがあります。 覚え易いものや短いワンライナーなら即席で入力しても構わないのですが、 先ほどの例のようなものになると少々入力するのが面倒です。 そこで役に立つのがコマンドラインの入力履歴です。 入力履歴があれば、例えば 三か月前に試行錯誤の末に編み出した git filter-branch でリポジトリを消毒するワンライナー であっても、簡単に再実行することができます。

入力履歴は大変便利なのですが、 こと検索においては欠点があります。 コマンド一つ一つが汎用的で小粒なために、 いざ検索しようとすると適切なキーワードが閃きづらい ということです。 パスワード生成の例では shasum が比較的出現頻度の低いコマンドなので、 これをキーワードにして検索すればまだ見つけ易い方ではありますが、 shasum を含む応用例は他にもあるため、 一発で目的のワンライナーに辿り着けることは多くありません。 素早く目的のワンライナーを見つけ出すにはどうすればよいのでしょうか。

問題

今時の若者ならば Heroku 等を利用して手早く Web アプリを作成・公開することが日常茶飯事です。 バックエンドもフロントエンドも今はフレームワークが充実していますから、 高度な処理を簡潔な記述で行うことができます。

しかし非同期処理となると話は別です。 例えばフロントエンドを作るとなると、まず jQuery を使うことになるでしょう。 jQuery は洗練された API で DOM 操作を簡単に行うことができますし、 非同期通信についても $.ajax を使えば煩雑なことほぼ知らずに済みます。 例えば Wikipedia の検索フォームは入力補完が行われるようになっており、 検索フォーム文字が入力されると関連するページのタイトルが候補として表示されます。 このような処理を書くとなると以下のようなコードになるでしょう:

var showCompletionMenu = function (words) {
  ...
};

var completeWords = function (partialWord) {
  $.ajax({
    url: 'http://en.wikipedia.org/w/api.php',
    dataType: 'jsonp',
    data: {
      action: 'opensearch',
      format: 'json',
      limit: 100,
      search: 'foo',
      success: function (data) {
        var words = data.data[1];
        showCompletionMenu(words);
      }
    }
  });
};

var $form = $('#userInput');
$form.keyup(function () {
  completeWords($form.val());
});

このように、イベント処理や非同期通信を行う場合、

  • イベントの監視を始める部分($form.keyup)と発生したイベントを処理する部分(completeWords)
  • リクエストを送信する部分($.ajax)と受信した結果を処理する部分(showCompletionMenu)

を分離し、前者にコールバック関数として後者を渡す形になります。 普通のプログラムならば上から下へ順次処理が行われるのですが、 このように非同期処理が絡むと実行の流れはソースコードの見た目から背離します。 この単純な例ですら非同期処理が2段も積み上がっており、 とても反射的に読んで意味を理解できるソースコードではありません。

ここで本当に行いたいことは補完候補の表示であって、 その表示タイミングや補完候補のデータの出所は重要ではありません。 しかしコールバックという名の中間層を噛まさざるを得ないため、 本質からやや遠ざかったソースコードになっています。 どうにかしてこの状況を打破できないでしょうか。