現在のバッファで使われている関数の所在を調べる

clパッケージの関数を使っていることを示すのにファイルの冒頭に"require 'cl"が合った方がわかりやすいと思った。
clパッケージに限らずrequireが必要なファイルをすべてファイルの冒頭に書いて置けば、あとで使いやすいかもしれないと思った。
いちいち中身を見るのは面倒なので、emacsに訊けないか調べてみた。

調べた方法(手順)

C-h f で関数のリファレンスを見ている時に、

"describe-function is an interactive compiled Lisp function in`help-fns.el'.

こんな感じの表示があるから、多分内部でどうにかして調べているはず。
なので

  1. C-h fで調べたい関数名を入力するとhelpが表示される
    1. C-h f = describe-function*1
    2. describe-functionのヘルプからhelp-fns.elを開く*2
    3. describe-function-1を呼び出している
  2. describe-function-1を読む
    1. (symbol-file function 'defun)という部分を発見

symbol-fileを使えば、関数がどこで定義されているか分かりそう。あと、そのままだと表示が長いのでdescribe-simplify-lib-file-nameで縮めてたみたい。

つくれそうだ

以下の手順を踏めば作れそう

  1. buffer内に含まれる単語を取得(カッコとか外す感じで)
  2. 関数として認識されているかでフィルタリング
  3. ファイル名と対応をつける(たぶん、そんなに多くないからhash-tableとかいらない)

実際のコード

(require 'cl)

(defun current-buffer-words ()
  (save-excursion
    (goto-char (point-min))
    (let (re)
      (while (re-search-forward "[^)( \n\t\'\"\;]+" nil t 1)
	(let ((s (substring-no-properties (match-string 0))))
	  (push (intern s) re)))
      (remove-duplicates re))))

(defun word->alist (words)
  (let	(re)
    (dolist (w words)
      (when (fboundp w) ;(or (fboundp w) (boundp w)) ;;symbol-fileに'defvarもつければ変数も取得可
	(let* ((file (describe-simplify-lib-file-name (symbol-file w 'defun)))
	       (seq (assoc file re)))
 	  (if seq
 	      (setcdr seq (cons w (cdr seq)))
	    (push (list file w) re))
	  )))
    re))

(defun display-used-functions () (interactive)
  (let ((l (word->alist (current-buffer-words))))
    (with-output-to-temp-buffer "*Used Functions"
      (dolist (fns l)
	(unless (eq (car fns) 'nil)
	  (princ (format "====%s=====\n" (car fns)))
	  (princ (cdr fns))
	  (princ "\n")))
      )))

;;(display-used-functions)

実行結果(例)

====cl.el=====
(push)
====cl-seq.el=====
(remove-duplicates)
====cl-macs.el=====
(dolist)
====help-fns.el=====
(describe-simplify-lib-file-name)
====subr.el=====
(match-string when symbol-file unless)

*1:C-h k C-h fで調べられる

*2:sudo aptitude install emacs22-el とかが必要かもしれない