Solist Work Blog

ソフトウェアエンジニア / IT navigator

Mark Ringを活用する

Emacsはきちんと設定されていれば、IDEとほぼ同じくらいになります。さらにはIDEにはないようなものもあってそれらをうまく使いこなせるようになるとIDEよりも便利になります。 この記事ではそういう意味で重要だと思うのにあまり知られていないものについて書いていきたいと思います。

Emacsにはマークリングというものがあります。デフォルトでは16個のマーク位置をmark ringに保存しています。 mark ringというだけあってデータ構造リングバッファです。この値は増やすこともできますが、人間の短期記憶に置いておける量はこれくらいでちょうどよいと思うので、デフォルトの16個でよいのではないでしょうか。1 mark ringを使うとあとで戻るかもしれない場所にマーキングするという使い方ができるのですが、あまり使われていないように感じます。 というのもEmacsはデフォルトで遠くに飛ぶような動作をすると勝手にマーキングしてくれますので、 散歩している犬のように自分でマーキングしようと思わないのは自然な人間の思考だと思います。 まずは、マーキングを意識しながら勝手にEmacsがつけてくれるマークリングを堪能してみましょう。

Emacsが勝手につけてくれるマークリングを最大限に活用できるパッケージがback-buttonです。

M-x package-install back-button

init.el

;; back-button
(back-button-mode 1)

これで使える状態になります。

 C-x C-<SPC>    `global-mark-ring'操作用
 C-x C-<left>   `global-mark-ring'左へ
 C-x C-<right>  `global-mark-ring'右へ
 C-x <SPC>      buffer-localな`mark-ring'操作用
 C-x <left>     buffer-localな`mark-ring'左へ
 C-x <right>    buffer-localな`mark-ring'右へ

操作方法はこのようになっています。

C-x C-<SPC>
C-x <SPC>

これだけ覚えておけば十分使えます。

C-x <SPC>

を押してみます。ミニバッファにlocal marksとあるのがそれです。 マークリングが4つあるうちの最初にいることがわかります。

マークリング2つめはこれです。

M->

で最終行に移動したのでEmacsが勝手に最終行にジャンプする直前にいたはじめの画像の場所をマークしてくれたからマークリングの一つめに勝手にマークがついていたのです。

マークリングの3つめです。最終行に移動したあとswiperでeasy-hugo-postdirで検索して移動したのでEmacsが勝手に検索した直前にいた場所(最終行)をマーキングしてくれています。

マークリングの4つめです。swiperでdefmacroを検索して移動したのでEmacsが勝手に検索した直前にいた場所(1171行目のeasy-hugo-postdir)をマーキングしてくれています。

マークリングが16個ある状態の様子です。マークリングが可視化されているので手に取るように状態がわかります。

マークリングにはglobal-mark-ringというものもあります。今まで見てきたものはバッファローカルなマークリングだったのです。 global-mark-ringもデフォルトで16個です。人間の短期記憶に置いておける量はこれくらいでちょうどよいと思うのでこのままでよいと思います。バッファをまたいでマーキングされているのでバッファをまたいで使用することができます。

ブログを書いている場所にもマーキングされていました。

メモにもマーキングされています。 人間の短期記憶に置いておけるmark-ringの16個とglobal-mark-ringの16個をうまく使いこなせば作業効率はあがります。 頭の中にある「さっきのアレ」を呼び出す方法としてはこの方法は優れていると思います。 back-buttonは今まで書いてきた説明とは違い

C-x <SPC>

このコマンドを実行するとマークリングを左に移動してゆきます。人間の「さっきのアレ」というのはマークリングを遡る方法とデータ構造が同じなのでマークリングを遡る方法がよいのです。いったんback-buttonが発動すると<SPC>を連打するだけでマークリングを遡ることができます。C-<SPC>を連打するだけでglobal-mark-ringを遡ることができます。<SPC>C-<SPC>をチャンポンに実行することもできるので「さっきのアレ」をチャンポンに呼び出すことができます。「さっきのアレ」をなんとなく呼び出しやすくなっているのです。

一手前に戻る汎用的な方法

開発をしていて一番よくあるパターンは遠くに移動したときに前に見ていた場所にワンアクションで戻りたいということです。2検索などで遠くに移動する場合は今まで見てきたようにEmacsが勝手につけてくれるマーキングを当てにすればよいですが、ざっとソースコードリーディングをしていると検索はしないが移動しすぎてもといた「さっきのアレ」に戻りたいのに一手で戻れないということがよくあります。

まずソースコード読んでいて何か横道にそれるなぁという時はC-<SPC>を二回押します。これは犬が電柱にマーキングするのと同じように自分で行います。お気に入りの場所に戻ってくるためにマーキングします。森の中で迷子になって遭難しないように来た場所に目印をつけておくのと同じことです。ソースコードの森に迷い込んだらさっきのマークに戻りましょう。 back-buttonを使ってC-x <SPC>で戻るのも悪くないですが、一手戻るだけの時はいいコマンドがあります。3

C-x C-x

exchange-point-and-markというポイントとマークを入れ替えるコマンドで一手で戻れます。 おそらくexchange-point-and-markはリージョンを選択するときにリージョンの開始点を確認・修正するために用意されたコマンドなのだと思うのですが、リージョンを選択するときに開始点を間違えたことは今まで一度もないのでこの用途では私は使ったことがありません。現代においてはほとんどの人がリージョンをハイライトしていると思うのでこの用途では使わないような気がしています。 というわけで一手戻るために使う専用コマンドとしてexchange-point-and-markを改造しましょう。

デフォルトのexchange-point-and-markコマンドはリージョンを修正するためにも使うためにリージョンがハイライトされていて一手前に戻った後リージョンのハイライトを解除するためにC-gを押さなければいけません。リージョンの開始点を修正する目的では使わないと決めたのでC-gを押さなくても一手前にワンアクションで戻れるようにしましょう。

(defun my/exchange-point-and-mark ()
  "No mark active `exchange-point-and-mark'."
  (interactive)
  (exchange-point-and-mark)
  (setq mark-active nil))

(bind-key "C-x C-x" 'my/exchange-point-and-mark)

これでC-x C-xを押すだけで一手前にワンアクションで戻れるようになりました。

exchange-point-and-markが素晴らしいのは現在いるポイントとマークを入れ替えてくれるので C-x C-xで一手前に戻ったあとにもう一度C-x C-xを押すと森の深くのもとにいた場所に戻れます。 ソースコードを行ったり来たりして確認することができるのです。 行ったり来たりしてソースコードを読みたいことは多いのでこの機能は重宝します。 このように一手前を行き来したり「さっきのアレ」にいつでも戻れるようになると気軽に森の奥に進めるようになるのでお勧めです。

counsel-mark-ring

mark ringを移動したい時に使えるものとしてcounsel-mark-ringというものもあります。

このようにmark ringをcounselインターフェースで選択して移動することができます。 戻りたい場所が名前ベースで頭の中にあって名前をつかって移動したい時はこちらのほうが早いです。 「さっきのアレ」が3つ前と頭のなかにある場合もあるし、関数名が頭のなかにある場合もあるので両方使うことになるでしょう。 なんでも一つの方法でうまくやることはできません。たくさんの方法でやることを身に着けないと思考の速度でプログラミングすることはできないのです。

init.el

(bind-key "C-x m" 'counsel-mark-ring)

私はC-x mをcounsel-mark-ringのショートカットに割り当てるくらいには使っています。 デフォルトのC-x mがEmacsでメールを書くモードに割り当てられていますが、私はメールをEmacsで書かないためこのショートカットは無駄ですからC-x mはcounsel-mark-ringにしています。 詳しくは私のdotfilesを参考にしてください。


  1. もしmark ringを16以上にするなら2の冪の値にするとよいでしょう。
  2. ソースコードジャンプで関数定義にM-.で移動したあとは必ず対になるM-,で戻ってこれます。
  3. バッファをまたいで一手前に戻るときはback-buttonがよいでしょう。

タグ一覧

お仕事の依頼はこちらからどうぞ

お仕事の依頼はこちらからどうぞ