カーソル位置の情報で遊ぼう。

Emacs Advent Calendar jp: 2010 の13日目というわけで、相撲大好き Emacser の tomoya です。どすこんばんわ (2日連続のネタです)。
昨日12日目は荒川智則さんのいつもの操作がなんなのか調べる方法でした。明日は kwappa さんになります。
ちなみに、去年は Mac の Emacs 23.1 以降でOS標準辞書を使ってみるというネタでした。今年はこれを更に分解して、Elisp で遊んでみたいと思います。

カーソルについて。

さて、パソコンの UI には、主にマウスカーソルとキャレットカーソル*1がありますが、エディタにおいてはキャレットカーソルは最も重要な情報源です。
RPG でいうところの操作キャラの現在地で、これを見失うと操作不能になりますし、調べるとアイテムが見つかったりします。
そんな現在地ですが、Emacs では文字の入力位置という事以外にも様々な役割りで使われています。

カーソル位置から得られる情報。

ぱっと思いつくカーソル位置情報は以下のものが挙げられます。

  • 行番号
  • カラム番号
  • 文字情報 (文字コード、フォントなど)

また、その他にも Emacs 特有の情報としては、

  • フェイス (文字装飾)
  • カレントバッファ (現在地バッファ)
  • カレントウィンドウ (アクティブなウィンドウ)

などなどがあります。そして、更にこれらの情報と紐付けられる情報へと広がっていきます。

what-cursor-position (C-x =) と M-x describe-char

試しに適当な場所で、"C-x =" というキーバインドを押してみると、what-cursor-position というコマンドが発動して、ミニバッファにカーソル位置から得られる情報が表示されます。

また、M-x describe-char というコマンドを利用すると、更に詳細な情報が *Help* バッファに表示されます。なお、これは "C-x =" に前置引数を与えた "C-u C-x =" でも実行できます。

こういった、カーソル位置から得られる情報を元に、Emacs では様々な便利コマンドが作られているのです。

カーソル位置の情報をぶんどる。

ぶんどるは FF5 のアビリティの中で最も好きなもののひとつです。また、FF6ではロックの名前をトモヤに換えてセリスといちゃいちゃしていました。
さて、what-cursor-position コマンドで得られる情報などは、即ち自分でコマンドを作る際に利用できるという意味でもあります。興味のある方は M-x describe-function RET what-cursor-position RET から、simple.el のソースを見ることができますので、そちらを見てみると良いでしょう。どうやって情報を取得しているのか良く分かります。また、simple.el には他にも超絶参考になる関数がいっぱいありますので、Elisp による Emacs 拡張には無くてはならない存在です。
例えば line-number-at-pos という関数を使うと、現在のカーソルの行番号を得ることができます。これを使って、クリップボードに現在の行番号をコピーしてみたり、ツールチップに表示したりして遊んでみましょう。

カーソル位置の行番号を取得する。

まずは関数を定義してみましょう。

(defun copy-current-line-number ()
  "現在行番号をコピーするコマンド"
  (interactive)
  (kill-new (number-to-string (line-number-at-pos))))

(defun popup-current-line-number ()
  "現在行番号をツールチップに表示するコマンド
要 pupup.el"
  (interactive)
  (popup-tip (number-to-string (line-number-at-pos))))

上の2つの関数を評価すると、copy-current-line-number と popup-current-line-number というコマンドが使えるようになります。ただし、ツールチップに表示するコマンドの方は別途 Auto Complete Mode に付属する pupup.el が必要になります。
まず、M-x copy-current-line-number を実行するとカーソル位置の行番号クリップボードに入りますので、C-y で貼り付けられるようになります。
次に M-x popup-current-line-number を実行すると、今度は現在の行番号がその場でツールチップで表示されます。

おおむね、こういった形で Emacs 上であればカーソル位置から得られる情報を使って遊ぶことができます。

その他、簡単に取得できるカーソル位置情報

関数名 説明
(point) 現在ポイント*2
(line-number-at-pos) 現在行番号
(current-column) 現在カラム番号
(following-char) カーソル位置の文字*3

という感じです。

カーソル位置の文字をいい感じに取得する thing-at-point

Emacs には thing-at-point という本体付属ライブラリがあり、カーソル上にある情報をいい感じに取得してくれる大変便利な関数を提供してくれます。さっそくこれを使って遊んでみましょう。

URL にカーソルがあるとき、それをマークアップしてみる。

今度はちょっとだけ難しいかもしれませんが、カーソル位置にある URL を HTML でマークアップしたかったとしましょう。
実現したいことは、

  • カーソル位置から URL だけを取得する。
  • 取得した文字を変換する。

という2つが実現できればやれそうです。これは、simple.el と同じ標準ライブラリの thingatpt.el に定義されている関数を使うと実現可能になります。

(defun create-hyper-link-at-point-url ()
  "カーソル位置のURLを HTML でマークアップする"
  (interactive)
  (let* ((bounds (bounds-of-thing-at-point 'url))
         (start (car bounds))
         (end (cdr bounds))
         (link (format "<a href=\"%s\"></a>" (thing-at-point 'url))))
    (delete-region start end)
    (insert link)))

こんな感じで create-hyper-link-at-point-url という関数を作ると、Emacs 上に http://example.com/hoge.html というアドレスがあった場合、そのアドレスの上で M-x create-hyper-link-at-point-url というコマンドを実行すると、<a href="http://example.com/hoge.html"></a> という感じに変換してくれます。


このコマンドで使っている thing-at-point 関数は指定した引数のモノを抽出してくれる便利な関数で、bounds-of-thing-at-point 関数はそれのリージョン範囲をドット対で返してくれる関数です。
なお、引数で使えるモノは、ドキュメントによると以下のものが挙げられています。

  • symbol
  • list
  • sexp
  • defun
  • filename
  • url
  • email
  • word
  • sentence
  • whitespace
  • line
  • page

纏め。

そんなわけで、ちょっと長くなってしまいましたが、まだあまり Elisp に慣れていない人が、これを参考にカーソル位置にある色んな情報を使って遊んでもらえれば嬉しいです。
それではまた。

*1:文字入力位置ですね。

*2:バッファ内のカーソルの絶対位置

*3:ただし数値なので、文字にするには char-to-string での変換が必要。