matlabでもel-doc

みなさん、EmacsからMatlab使ってますか?

そもそも、Matlabが結構高価なソフトなのでユーザ数はそこまで多くないと思いますし、さらにその中でMatlabEmacsからつかってやろうって人は大分少なくなるんじゃないかなぁと思いますが、そんなことはお構いなしです。

一応、以前MatlabEmacsからつかう際の設定について
http://d.hatena.ne.jp/uhiaha888/20100815/1281888552
に書いたのでもし「EmacsからMatlabを使いたいけどどうすりゃいいのかわからん」みたいな人がいたら読んでみたらいいかもしれませんね。

今回書いたコードがmatlab.elに依存してるので、もし使いたいという奇特な方がいらっしゃいましたら上記リンクのmatlab.elの設定をしておいてください。

さて、Emacsにはeldocという大変便利な拡張があります。

これは、scratchバッファや.elファイルを開いているバッファにおいて、ポイント下のEmacs Lispの関数の引数が何であるかをミニバッファに表示するもので、詳しくは例えばid:rubikitch氏の
http://d.hatena.ne.jp/rubikitch/20090207/1233936430
の記事などを参照していただければその魅力を理解していただけると思います。

この機能が便利だということで、CやPerlなど様々な言語で使用できるようになっています。
http://www.emacswiki.org/emacs-en/ElDoc

先日このeldocをess-modeから使うためのess-eldoc.el
https://svn.r-project.org/ESS/trunk/lisp/ess-eldoc.el
なる拡張の存在を知ったので、「Matlabでも使いたい!」と思って適当に作ってみました。

ほぼess-eldoc.elからのコピペです。

Rだと引数リストを返してくれる関数があるのですが、Matlabでは見つけられなかったのでwhich関数で関数が定義されている.mファイルのパスを取得し、そのファイルの最初の行("..."で連結されてる場合はそれも考慮)を持ってきて、それを表示させるようにしてます。

なので、.mファイルの最初の行にコメントとか書いちゃってると適切な情報が表示されない可能性があります。

また、which関数をmatlab.elにあるmatlab-shell-collect-command-output関数を使って呼び出しているのですが、これを呼ぶとmatlab-shellの*MATLAB*バッファ内のカーソル位置がリセットされちゃうので*MATLAB*バッファ内では使えません。

;; matlab-eldoc.el
(defun matlab-eldoc ()
  (interactive)
  (let ((doc nil)
	name)
    (setq name (matlab-guess-fun))
    (unless (= (length name) 0)
      (setq doc (matlab-args-get name)))
    (unless doc
      (save-excursion
	(condition-case nil
	    (progn
	      (up-list -1)
	      (setq name (matlab-guess-fun))
	      (setq doc (matlab-args-get name)))
	  (error nil) )))
    (if doc
	(concat name ": " doc)
      doc)))

(defsubst matlab-guess-fun ()
  "Guess what the function at point is."
  ;; Derived from Man-default-man-entry in man.el
  (let (word)
    (save-excursion
      (skip-chars-backward "-a-zA-Z0-9._+:")
      (let ((start (point)))
	(skip-chars-forward "-a-zA-Z0-9._+:")
	(setq word (buffer-substring-no-properties start (point)))))
      word))
    

(defun matlab-args-get (name)
  (let 
      ((fln (matlab-shell-collect-command-output 
	     (concat "which('" name "')"))))
    (setq fln (replace-regexp-in-string "\n" "" fln))
    (cond 
     ((equal fln (concat name " is a variable."))
      nil)
     ((not (file-exists-p fln))
      nil)
     (t 
      (save-excursion
	(set-buffer (get-buffer-create " *matlab-eldoc temp* "))
	(insert-file-contents fln nil 0 150)
	(let* ((beg (point-min))
	       (end (progn (end-of-line) (point)))
	       (temp-end end)
	       (temp-string (buffer-substring beg end))
	       (temp-args temp-string))
	  (while (string-match "\\.\\.\\." temp-string)
	    (setq temp-end end)
	    (setq end (progn (forward-line) (end-of-line) (point)))
	    (setq temp-string (buffer-substring (1+ temp-end) end))
	    (setq temp-args (buffer-substring beg end)))
	  (kill-buffer " *matlab-eldoc temp* ")
	  temp-args))))))
    
(defun matlab-use-eldoc ()
  (interactive)
  (set (make-local-variable 'eldoc-documentation-function) 'matlab-eldoc)
  (eldoc-mode t))

(add-hook 'matlab-mode-hook 'matlab-use-eldoc)
(provide 'matlab-eldoc)

正味ひどい出来ですが、僕がこの機能を使いたいので、だれか優しい人が改良してくれることを期待しつつ、公開します。

明日もうちょっと直してみて、そのうちgithubかどっかにあげなおします。