近頃の開発環境 : Mosh、z、tmux、Emacs、Perl について

昨日は年始の挨拶ついでに ELPA について脈絡もなく突然書きましたが、引き続き近頃の開発環境についてもだらだらと書いてみよう。

Mosh

mosh というと一部の人間はひげなんとかさんが開発しているモナー的なあれを思い浮かべるかもしれないがそうではなく、mobile shell のことである。

思い切り簡略化して言うと「快適なssh」。回線が不安定な所でもエコー遅延など全く気にせず使えるし、Mac をスリープさせて復帰させたときもリモートホストにそのまま繋がりっぱなしのように見せかけてくれたりする。

詳しくはこの辺を。

インストールはリモートとローカル両方に必要ですが、まあ大概パッケージがあると思います。EC2 の Amazon Linux でも yum レポジトリの EPEL を有効にすれば yum install mosh で OK。Macbrew install で入ります。

.zshrc に

# mosh
compdef mosh=ssh

と書いておくと幸せになれますね。

あんまり関係ないですが EC2 と ssh で言うと、合わせて ec2ssh (http://blog.mirakui.com/entry/20101205/1291551625) なんかも使うとインスタンスの public DNS 名が変わって mosh できねー、という場合も無問題です。

z

去年の夏にハッカソンにいったときに autojump (https://github.com/joelthelion/autojump) という便利コマンドを教えてもらった。ディレクトリ移動の履歴を管理してくれていて、結構深い階層へのディレクトリ移動も「j hogehoge」とかで良い感じに補完してくれるもの。

最近は python いらずの z というのもあります。自分はなんとなくこっちの方が導入がスムーズでした。brew instal z して .zshrc に

# z
. `brew --prefix`/etc/profile.d/z.sh
function precmd () {
   z --add "$(pwd -P)"
}

で ok。

/Users/naoya/git/RubyMotion/Diablo3 とかの深いディレクトリにいくときも「z Dia」程度に入れて Tab おせばあら不思議、移動完了です。他にもいくつかオプションがあるようですが、自分は z hogehoge くらいしか使ってないですね。

Emacs

昨日 ELPA で elisp を管理する的なことを書きました。それ以外に Emacs で特筆すべき最近の話というのはあんまりない。

popwin.el + anything.el

ないですが、いろいろ elisp を入れてその場では「うお、これはライフチェンジング!!」と興奮するものの気づけば全く使わず仕舞い・・・なんてことが9割方の中、ここ一年くらいでちゃんと定着したものといえば anything.el + popwin.el の組み合わせ。強いて挙げるならこの辺でしょうか。

左半分のペインが emacs で、その下半分が popwin の中に anything を起動しているところ。

popwin.el (http://d.hatena.ne.jp/m2ym/20110120/1295524932) は Emacs のおかしなウィンドウ分割を快適にしてくれるもの、anything.el はもう今更いうことなしですね (最近は helm というのがあるらしいです)。

この捗る感じを言葉で伝えようとするとなかなか難しいのですが、以前に開いたファイルを再度開こうとしてファイル名がうろ覚えで minibuffer の中で苦戦してみたり、バッファを switch しようとしてバッファ名が思い出せなかったり、そうこうしているうちに勝手に画面が分割されて面倒だなとおもいつつそれを閉じたり・・・ということがなくなる。

自分の場合 C-x b、つまりバッファ切り替えそのものを

(define-key global-map (kbd "\C-x b") 'anything)

として anything で置き換えてしまって、Emacs 中におけるファイルやバッファ操作などはすべて C-x b で済ませるようにしていますからこれはちゃんと定着して良い感じにライフチェンジングとなりました。

anything を初めて導入したときは便利だけで定着するに至らなかったのですが、popwin.el と組み合わせることで身体化された感じです。両者の導入はここで書くと長くなるのでググってください。

tmux

最近 GNU screen から tmux に変えました。特に乗り換える積極的な理由があったわけではなかったのですが、何となくで変えてみたらば tmux の方がいろいろとかゆい所に手が届いていた、という感じです。おそらく開発版の screen を入れてあれやこれやを設定すれば、ほぼ大差のない使い勝手にできるんだとは思います。

screen の時は画面分割はあまり積極的には使っていなかった所、tmux ではするようになりました。

  • C-t (自分は tmux メタキーが C-t です), Space でレイアウトを組み込みの中から簡単に選択できる
  • マウスでの操作が便利 (ペイン選択と、ドラッグでの領域サイズの変更)

の 2 点があるのがそれをするようになった理由かも。先のスクショの通り、左に emacs、右にシェルという感じで使うことが多いです。

emacsclient

画面分割と emacs と言えば、環境変数 EDITOR = emacsclient にしてやって、エディタでの編集が必要な場面は vi とかではなく起動している emacs でやる・・・というテクニックがありますよね。これも screen の頃はウィンドウが切り替わって頭が混乱するので使ってなかったのですが、左ペインに常に emacs を起動した状態だとスムーズに使えるというので、導入しました。

init.el にこんな感じで書いてやって

;; init.el
(require 'server)
(unless (server-running-p)
  (server-start))

.zshrc には

# .zshrc
# emacsclient
export EDITOR="emacsclient"
alias e='emacsclient'

と書くと。

さっきも brew create の流れで formula を書くときに、ruby スクリプトを emacsclient で編集できて幸せな気持ちになりました。

last-pane を C-t C-t

tmux の設定で一つこうしているというのは last-pane の選択。

screen では C-t C-t (つまりメタキー連打) で一つ前のウィンドウと行ったり来たり、というのを頻繁にやっていた。C-t はもう何万回連打したかわからない、というほどに。

tmux では同キーに、ウィンドウの切り替えではなくペインの切り替えを割り当ててます。先に書いたとおり、画面分割して右にシェル用のペインを置いているので切り替えはウィンドウではなくペインのほうがありがたい。これを設定しとかないと、ペイン移動が C-t o でサイクルとかやや不便な感じでつらい。

# .tmux.conf
unbind ^A
bind ^t last-pane

というような感じで使っていると、tmux ではウィンドウをほとんど新しく生成しないようになりました。基本は 1ウィンドウに3 ペインくらい開いておいて、そのウィンドウだけで事足ります。IRC をターミナルでやってる人とかはまた違ってくるのかも。

ペインを popwin 風に

tmux と言えば

特にライフチェンジングだなとおもったのは、分割窓を Emacs でいうところの popwin.el 的に使う方法だ。man をチラ見したり、辞書を引いたりそういうレベルで気軽にpaneをつくれ、man を終了するとpaneが自動で閉じるといった具合に便利に使える。

と typester さんが書いてたんだけど、これってどういうことかなあと思って調べてたら man に例があって、split-window は引数にコマンドが取れて、そのコマンドの出力を一時的なペインで開くことができるんですな。

# .tmux.conf
bind-key w command-prompt "split-window -p 65 'exec man %%'"

と書いとくと、C-t w で man を (自分の場合は less で) みれる。終わって less を quit すると、ペインが閉じる。まあこのユースケースの場合自分はついつい Emacs 内で woman で済ませてしまうのだけど、マニュアル以外に何かほかのことに応用できる気がしました。

Perl

最近またちょこちょこと Perl を書いてるのですがこちらも特に目新しいことはないですかね。perlbrew、cpanm、Plack、Proclet、Carton あたりを使うようになって以前より開発スタイルが幾分新しくなったなあとは感じます。

昔作ったレガシーなウェブアプリケーションを、先日 EC2 に移行させたのですが Carton (http://search.cpan.org/dist/carton/) が非常に便利でした。miyagawa さんが作った、Perl 版の Bundler みたいなものですね。

Perl 製のウェブアプリケーションが古くなってくると「あれ、このアプリでどんなモジュール使ってたっけなあ」というのが非常に頭の痛い問題になってくるんですが、Carton でモジュールを管理するようにしてやると Makefile.PL の中にすべて一覧化されてだいぶ気楽になりますし、何より実行環境を箱庭化する carton exec を使うことで、自分のローカルには入ってるけどウェブアプリケーションにはバンドルできてない、みたいなミスをなくすことができる。

手始めにレガシーな CGIPlack 化してやってから carton exec で plackup し、実際に動かしエラーを見ながら足りないモジュールを一つずつ Makefile.PL に追加していく。終わったところで EC2 に git clone したのち carton install で全く同じ構成のモジュール環境のインストールを自動化することができてよかった。

perlbrew + Carton と flymake

perlbrew を使っていると perl 環境がシステムグローバルなものからユーザーローカルなものになるし、Carton だとさらにそれが箱庭になる。となると flymake のライブラリのロードパスがだいぶ面倒なことになりそうなのは、flymake を使っている人なら容易に想像がつく。

以下は elisp 10級なオレによるその対応版 cperl-mode 向け flymake 設定。なんか使ってないものも入ってそうだけど。perlbrew.el と Project::Libs が必要 (http://blog.kentarok.org/entry/20110413/1302671667) です。

;; init.el
(require 'perlbrew)
(perlbrew-switch "perl-5.17.6")

(defvar flymake-perl-err-line-patterns
  '(("\\(.*\\) at \\([^ \n]+\\) line \\([0-9]+\\)[,.\n]" 2 3 nil 1)))

(defconst flymake-allowed-perl-file-name-masks
  '(("\\.pl$" flymake-perl-init)
    ("\\.pm$" flymake-perl-init)
    ("\\.t$" flymake-perl-init)
    ("\\.psgi$" flymake-perl-init)))

(defun flymake-perl-init ()
  (let* ((temp-file (flymake-init-create-temp-buffer-copy
                     'flymake-create-temp-inplace))
         (local-file (file-relative-name
                      temp-file
                      (file-name-directory buffer-file-name))))
    ; (list "perl" (list "-wc" local-file))))
    (list (perlbrew-get-current-perl-path) (list "-MProject::Libs" "-wc" local-file))))

;; Carton対応 : http://pokutuna.hatenablog.com/entry/2012/11/18/143140
(defun my:find-climb-up (filename &optional current-dir)
  (setq current-dir (or current-dir (file-name-directory(buffer-file-name))))
  (if (string= current-dir (directory-file-name current-dir))
      nil
    (let ((filepath (concat current-dir filename)))
      (if (file-exists-p filepath)
          filepath
        (my:find-climb-up filename
                          (file-name-directory (directory-file-name current-dir))))
      )))

;; よくある感じのflymake-perlの設定にlocal/lib/perl5/を追加
(defadvice flymake-perl-init (after add-carton-inc-flymake-advice activate)
  (let ((lib-dir (my:find-climb-up "local/lib/perl5/")))
    (if (stringp lib-dir)
        (setf (nth 1 ad-return-value)
              (pushnew (format "%s%s" "-I" lib-dir) (nth 1 ad-return-value))))
    ))

(add-hook 'cperl-mode-hook (lambda () (flymake-mode t)))

まとめ

色々書いていたら予想どおり脈絡もなくだらだらとしてしまいましたが、ざっくりまとめると

という感じでしょうか・・・ 古くさいツールに関して自虐ネタをかますようになったらオッサンの証拠ですので今後は自重したいと思います。