pabbrev.elは使うのやめてる…残骸をば

最近tokyo-emacsというEmacs勉強会でpabbrev.elが話題になっているようだ。
俺は毎日EmacsWikiRSSで巡回しているから、昔からpabbrevは知っていたし、使っていた。
ソースを見てみたけど、ありゃ行儀がわるすぎる。まず、hard codingが目立ってあかん。キーバインドがTABに固定されてるし…TABってけっこうかぶってるし、相性的にまずい。ruby-electric-modeとケンカしてしまったし。まぁ、ロード順をなんとかすりゃ直るかもしれんけど。pabbrev-get-previous-bindingを再定義すれば他のキーに割り当てられる。
あと、トップレベルでdefine-keyは勘弁してほしい。ロードしただけでいつの間にか勝手にキーが割り当てられるのは気持ち悪い。関数に入れとこうよ。他のキーに割り当てたら以下を忘れずに。

(define-key pabbrev-mode-map "\t" nil)
(define-key pabbrev-mode-map [tab] nil)


で、候補選択画面が使いづらすぎ。選択時に0〜9と上段のキーを使わせるのはよくない。関数再定義でskkのようにasdf〜で選択できるようにした。

(setq pabbrev-select-keys "asdfghjkl;")
(setq pabbrev-read-only-error nil)
(setq pabbrev-idle-timer-verbose nil)
(loop for c across pabbrev-select-keys do
      (define-key pabbrev-select-mode-map (char-to-string c) 'pabbrev-suggestions-select))

(defun pabbrev-suggestions-select(&optional index)
  "Select one of the numbered suggestions."
  (interactive)
  (let ((insert-index
	 (or index
	     (loop for c across pabbrev-select-keys
                   for i from 0
                   when (= last-command-char  c)
                   do (return i))
             )))
    (if (< insert-index
	   (length pabbrev-suggestions-done-suggestions))
	(pabbrev-suggestions-insert
	 (car
	  (nth insert-index pabbrev-suggestions-done-suggestions))))))

(defun pabbrev-suggestions-buffer(suggestions prefix)
  "Form the suggestions buffer."
  (with-output-to-temp-buffer " *pabbrev suggestions*"
    (setq pabbrev-suggestions-from-buffer (current-buffer))
    (setq pabbrev-suggestions-best-suggestion
	  (car suggestions))
    (setq pabbrev-suggestions-done-suggestions
	  (pabbrev-suggestions-limit-alpha-sort suggestions))
    (setq suggestions pabbrev-suggestions-done-suggestions)
    (let
	((window-width (- (window-width) 1)))
      (save-excursion
	(set-buffer (get-buffer " *pabbrev suggestions*"))
	(pabbrev-suggestions-setup)`
	(princ
	 (concat;;"Current Word: " prefix " "
	  "Max Substring: " (try-completion "" suggestions)
	  "\n"))
	(princ
	 (concat
	  "Best Match: " (car pabbrev-suggestions-best-suggestion)
	  "\n"))
	(if suggestions
	    (loop for i from 0 to 9 do
	      ;; are we less than the suggestions
	      (if (< i (length suggestions))
		  (progn
		    (goto-char (point-max))
		    ;; insert all the suggestions
		    (let ((next-suggestion
			   (concat
                            ;; !!!!!! 
			    ;(number-to-string i)
                            (char-to-string (aref pabbrev-select-keys i))
			    ") "
			    (car (nth i suggestions)) " " ))
			  (line-length
			   (- (line-end-position) (line-beginning-position))))
		      ;; if well. are not on the first suggestion,
		      (if (and (> i 0)
			       ;; and the line will be too long
			       (< window-width
				  (+ line-length (length next-suggestion))))
			  ;; add a new line.
			  (princ "\n"))
		      (princ next-suggestion)
		      (let ((start (- (point) (length next-suggestion))))
			(overlay-put
			 (make-overlay start (+ 2 start))
			 'face 'pabbrev-suggestions-label-face))))))))))
  (shrink-window-if-larger-than-buffer (get-buffer-window " *pabbrev suggestions*")))

んだけど、候補の文字列が長いときに目視で複数の候補を見つけるのがつらいから結局使用をやめた。

anythingと連携してくれたら幸せになれそう。pabbrev-expand-maybeは次のようになっているから、(pabbrev-suggestions-goto-buffer)で選択画面にいくようになってる。で、pabbrev-expansion-suggestionsが候補のようだ。使っている人はanything.elのsourceを書けるだろう。もしかしてid:IMAKADOあたりがやってるかな?

(defun pabbrev-expand-maybe()
  "Expand abbreviation, or run previous command.
If there is no expansion the command returned by
`pabbrev-get-previous-binding' will be run instead."
  (interactive)
  ;; call expand if we can
  (if (and (eq this-command 'pabbrev-expand-maybe)
	   (> (length pabbrev-expansion-suggestions) 1))
      (pabbrev-suggestions-goto-buffer)
    (if pabbrev-expansion
	(pabbrev-expand)
      ;; hopefully this code will actually work as intended now. It's
      ;; been around the house a few times already!
      (let ((prev-binding
             (pabbrev-get-previous-binding)))
        (if (and (fboundp prev-binding)
		 (not (eq prev-binding 'pabbrev-expand-maybe)))
	    (funcall prev-binding))))))

pabbrevなくても、さっき実装したdabbrevの部分マッチがあるから俺はこれで幸せ。

追記

俺はyasnippetをスペースで起動している。勝手にsnippetが展開されることがあるけど、undoがうまく効かないのが難点。そのうち直してくれるかな。

pabbrevで候補をすぐ横に表示してくれる機能はdabbrevにも取り込みたいところ。

dabbrevはいろんな方法があるんだよね。hippie-expandとかac-modeとかdabbrev-expand-multipleとか…
dabbrevは遅いのが唯一の欠点。ボトルネックはC言語で実装してほしい…Emacs界隈でもRubyみたいに「遅い部分は拡張ライブラリで」という動きになってほしいのだが。そのためには簡単に拡張ライブラリを作成できる体制になってほしい。ライセンスが障害になっているらしいけど、PerlRubyの場合とどう違うのだろうか。