集中力5倍!小よく大を制するミニマリストEmacs


この記事は Emacs Advent Calendar jp: 2011 の11日目です.

昨日はta2_o2p さん、明日は id:handlename さんです。

去年、一昨年とインストーラ系を連続して紹介してきましたが、
今年はうってかわって身近な例を取り上げたいと思います。

僕は最近、Windows XPネットブック
使っていることが多いです。

あれだけ忌み嫌っていたWindowsの方が自宅の
Linuxマシンよりも稼働率が高いのです。

数年前の自分が聞いたらびっくりしてることでしょう。

この前はネットブックにEmacsをインストール
する話をしましたが、今回は小さい画面で
Emacsを使うノウハウをお送りします。

最近はパソコンといえばノートパソコンが普通の
ようなので、これからする話は役立つと思います。

とにかく、小さいパソコンでEmacsを使う場合、
最大の問題点となるのが、画面表示です。

画面が小さいので、1画面に表示
できる情報量が少ないです。

その状態で画面分割をしてしまうと表示領域が
狹くなり、とてもストレスがたまります。

上下分割だと行数が少なすぎるし、
左右分割だと桁数が足りなくて、
折り返されてしまいます。

では、桁数確保のためにフォントを小さくすると
今度は目が疲れてしまい、現実的ではありません。

小さい画面でEmacsを有効に使う秘訣は、
画面分割をせずに、原則的に1フレームにつき
1ウィンドウで使うことです。

そういうと、他のバッファが見られないじゃないかと
言う人がいるんですが、それは運用でカバーします。

僕の長い経験上、分割された2つのバッファにおいて
両方を同時に編集することはほとんどありませんでした。

どちらか一方のバッファを閲覧しながら、
他方のバッファを編集する形がほとんどでした。

もしどうしても複数のバッファを同時に編集したい
ならば、そのときはやむをえず画面分割します。

でも、基本的には1フレーム1ウィンドウで
十分間に合っています。

この方式でEmacsを使うことによって
かつてないほどの集中力が得られました!

大画面を使うよりも効率的に作業ができてしまいます。

もちろん画像編集などでは困りますが、
文章やプログラムを書くことにおいては
小さい画面でこのノウハウを使う方が
集中力が出ました。

それでは、どのようにして実現するのかというと、
以下のステップになります。

1. 変数を設定する
2. 直前のバッファに切り換えるコマンドを定義する
3. display-bufferで表示されるバッファをview-modeにする

それでは、小よく大を制する
ミニマリストEmacsの世界へご案内します。

変数を設定する


画面分割を抑止するには、
次の変数を設定するだけです。

(setq pop-up-windows nil)

なお、popwin.elなどでdisplay-buffer-functionを
設定している場合は一時的にnilにしてください。

(setq display-buffer-function nil)

この状態でC-x 1で1ウィンドウにして
以下の式を評価してください。

(display-buffer (other-buffer))

すると、画面分割されずにバッファが切り替わります。

「はい、これで終わりです」
と言うと、物を投げられそうなので、
もう少し時間をください。

直前のバッファに切り換えるコマンド


単にpop-up-windowsを設定しただけでは、
まだ使い勝手がよくありません。

なぜなら、直前のバッファに切り換える
動作がすぐにできないからです。

1フレーム1ウィンドウで効率よく作業するには
どうしても直前のバッファに瞬時に切り換える
ことができないと話になりません。

逆に、このコマンドさえ使えれば、
十分に使いやすくなるのです。

僕は自宅では24インチの巨大なディスプレイで
作業をしているのですが、そのディスプレイ
全体を見るということがそんなになかったのです。

視力が悪いので作業中はメガネが必要です。

そして、メガネをかけていると、
曇ったりして気になるのです。

こういうちょっとしたことでも
集中力は途切れてしまいます。

主にネットブックで作業するようになると、
画面全体をメガネなしで見られるようになり、
この1フレーム1ウィンドウのノウハウを活用して
効率よく作業することができるようになりました。

おまけに、1つのバッファしか表示されていない状態だと、
そのバッファでの作業に集中できることがわかりました。

余計な情報が表示されていることでも
ついそれが気になってしまって、
集中力が奪われることに気付きました。

今ではすっかりシンプルな使い方になっています。

それでは、1フレーム1ウィンドウのキーとなる
「直前のバッファに切り換えるコマンド」を紹介します。

一番安直な実装はこうなります。

(defun switch-to-last-buffer ()
(interactive)
(switch-to-buffer (other-buffer)))
(global-set-key (kbd "C-t") 'switch-to-last-buffer)

ついでにC-tに割り当てておきました。

これにより、直前のバッファに「一瞬で」
切り換えられるようになりました。

直前のバッファを一瞥してから、
元のバッファに戻るということが
簡単にできるようになりました。

でも、これだとほんの細かい
ところで不便なんですよね。

・ bury-buffer等でbuffer-listの最後に
なったバッファが選択できない
・ *Completions*など選択したく
ないバッファが選択される

これらの欠点を改良し、除外バッファを
選択しないようにし、かつ、確実に直前の
バッファに切り換えられるようにしました。

;;; last-buffer
(defvar last-buffer-saved nil)
;; last-bufferで選択しないバッファを設定
(defvar last-buffer-exclude-name-regexp
(rx (or "*mplayer*" "*Completions*" "*Org Export/Publishing Help*"
(regexp "^ "))))
(defun record-last-buffer ()
(when (and (one-window-p)
(not (eq (window-buffer) (car last-buffer-saved)))
(not (string-match last-buffer-exclude-name-regexp
(buffer-name (window-buffer)))))
(setq last-buffer-saved
(cons (window-buffer) (car last-buffer-saved)))))
(add-hook 'window-configuration-change-hook 'record-last-buffer)
(defun switch-to-last-buffer ()
(interactive)
(condition-case nil
(switch-to-buffer (cdr last-buffer-saved))
(error (switch-to-buffer (other-buffer)))))

last-buffer-exclude-name-regexpは適宜設定してください。

でも、たま〜にですが画面分割するときもあります。

そのために、画面分割しているときは、
次のウィンドウを選択するようにしました。

状況に応じて挙動を変えるコマンドを定義
するのは現代っ子の必須スキルです(笑)

(defun switch-to-last-buffer-or-other-window ()
(interactive)
(if (one-window-p t)
(switch-to-last-buffer)
(other-window 1)))
(global-set-key (kbd "C-t") 'switch-to-last-buffer-or-other-window)

これで大分使い勝手がよくなりました。

なお、このコマンドの他にもcycle-buffer.elも
導入しておいた方がよいです。

バッファをワンタッチで次々と切り換えるelispです。

これがあれば2つ以上前のバッファにも切り換えらます。

M-x install-elisp-from-emacswiki cycle-buffer.el

でインストールし、以下の設定をします。

(global-set-key (kbd "M-o") 'cycle-buffer)
(global-set-key (kbd "M-O") 'cycle-buffer-backward)

M-oとM-Oでバッファを次々と切り換えられます。

望みのバッファでなければ連打していきます。

view-modeも活用する


これだけでも十分便利なのですが、
さらに使い勝手を上げてみましょう。

display-bufferによって表示されるバッファは、
編集でなく、あくまでも閲覧を目的としています。

閲覧といえば、やっぱりview-modeですよね!

qを押せばあっさり元の画面に戻れます。

display-bufferを呼ぶ際、そのバッファを
view-modeにする設定を加えておきましょう。

ただ、バッファ作成時にview-modeにすると
read-onlyで書き込めなくなるので、その
バッファはinhibit-read-onlyをtにします。

これでview-modeでもバッファは編集可能になります。

この設定にするにはdisplay-buffer-functionを
設定する必要があります。

そのため、popwin.el等ウィンドウ表示の
elispとは相容れないものとなります。

僕のネットブックではpopwin.elは使っていません。

popwin.elではC-gを押したらそのウィンドウは消滅しますが、
この設定においてはC-tやqを押せば消滅します。

1フレーム1ウィンドウでは不要ですが、
大画面のLinux機においてはpopwin.elを
便利に使わせていただいております。

では、その設定を公開します。

;; view-modeにしないバッファ名
(defvar switch-to-view-buffer-except-names (rx (or "mew" "+draft/" "*Completions*" )))
;; view-modeにしないメジャーモード
(defvar switch-to-view-buffer-disable-view-mode '(Info-mode mew-draft-mode))
;; view-modeにしないコマンド
(defvar switch-to-view-buffer-except-commands (rx (or "mew")))
(defun switch-to-view-buffer (buf &optional ignore)
"BUFを1ウィンドウview-modeにして切り換える"
(if anything-in-persistent-action
;; anything.elのpersistent-actionのときは特別扱い
(let (display-buffer-function (pop-up-windows t))
(switch-to-buffer buf))
;; そうでないときは1ウィンドウでview-mode
(switch-to-buffer buf)
(delete-other-windows)
(unless (or (memq major-mode switch-to-view-buffer-disable-view-mode))
(view-mode 1)
(set (make-local-variable 'inhibit-read-only) t))
(selected-window)))
(defvar switch-to-view-buffer-maybe-winconf nil)
(defun switch-to-view-buffer-maybe (buf &optional ignore)
"1ウィンドウview-modeのためのdisplay-buffer-function設定用"
(setq switch-to-view-buffer-maybe-winconf
(current-window-configuration))
;; 除外条件にあてはまる場合はデフォルトのdisplay-buffer
(if (or (string-match switch-to-view-buffer-except-commands (format "%s" this-command))
(string-match switch-to-view-buffer-except-names (format "%s" buf)))
(let (display-buffer-function (pop-up-windows t))
(display-buffer buf))
;; あてはまらない場合は1ウィンドウview-modeで表示
(switch-to-view-buffer buf)))
(defun my-restore-window-configuration ()
"display-bufferを呼ぶ前のウィンドウ構成に戻す"
(interactive)
(if (not switch-to-view-buffer-maybe-winconf)
(bury-buffer)
(set-window-configuration switch-to-view-buffer-maybe-winconf)
(setq switch-to-view-buffer-maybe-winconf nil)))
(setq temp-buffer-show-function 'switch-to-view-buffer-maybe)
(setq display-buffer-function 'switch-to-view-buffer-maybe)
(setq pop-up-windows nil)
(require 'view)
;; view-modeでqを押すとウィンドウ構成を戻す
(defkey view-mode-map "q" 'my-restore-window-configuration)

おまけ


anything.elを使っていて、anythingの選択は画面分割
したい場合には、以下の設定を加えておきます。

(defun anything-display-function-1 (buf)
(let ((pop-up-windows t) display-buffer-function)
(display-buffer buf)))
(setq anything-display-function 'anything-display-function-1)

また、viewer.elでview-modeの使い勝手を
さらに高めることができます。

M-x install-elisp-from-emacswiki viewer.el

でインストールし、以下の設定を加えてください。

(require 'viewer)
;; 書き込めないファイルは常にview-mode
(viewer-stay-in-setup)
;; view-modeのときはモードラインの色を変える
(setq viewer-modeline-color-unwritable "tomato"
viewer-modeline-color-view "orange")
(viewer-change-modeline-color-setup)

おわりに


いかがだったでしょうか?

これで1フレーム1ウィンドウのための
view-modeの設定は終わります。

とりあえず、しばらく試してみてください。

今までとは全く違うEmacs像が見えてきたと思います。

特に文章を書くときには絶大な集中力が得らます。

僕はこれまで長年にわたり膨大な設定を書いてきたの
ですが、人間はひとつのことしか集中できないことを知り、
結局のところ、一番シンプルな解に到達しました。

書籍『Emacs Lispテクニックバイブル』はあなたが
すぐにelispが書けるようにするために書きました。

メルマガ『Emacsの鬼るびきちのココだけの話』は、
読者のリクエストに応じて僕が記事を書き、あなたを
Emacsハッカーに育て上げるために発行しています。

よろしくお願いします。