続々auto-complete.elで悩む

どもです。
これを作ってて作るのにはあれがあれば便利だけどあれが未完成でそれを作る
のにはどれどれが必要でどれどれにはバグがあってバグを修正するのにはこれ
が必要でというような状態になっててにっちもさっちもいかん。
わっかになっているというかウロボロスというか輪廻転生というか急がば回れというか。
回ってたらスタートにもどるという感じ。
まあよくわかんということですわ。


どうもこないだのmy-auto-complete-toggle-sourceの挙動がよろしくないよう
なので,少し改良。見た目は改良されてない感じ。
ac-prefixがnilになって一生nilのまま過ごしてしまいそうな感触だったので,
自分で,各情報源のプリフィクス関数を呼び出してprefixを取得して,それを
ac-prefixに設定しちゃうという強引な感じ。


この関数を呼んだときの流れで呼び出されるac-start関数をedebugで追ってみたところ,
prefixの取得はうまくいっていて,ac-prefixにsetqしているんだけど,
なぜかac-prefixがnilになっているという不思議現象。というかたぶんワタシが
いらんことしてるというか,規約違反みたいことしてるんだろうと予想。
なので,少々強引な方法を使わせてもらってます。

※2013/02/22 (Fri) 18:43 修正

prefixが文字列だった場合に対応

(defun my-auto-complete-toggle-source ()
  (interactive)
  (let* ((completing-functions
          (loop for source in ac-sources ;(remove 'ac-source-dictionary ac-sources)
                for candidates-func = (cdr (assoc 'candidates (symbol-value source)))
                for prefix-func = (cdr (assoc 'prefix (symbol-value source)))
                for ac-prefix = (or ac-prefix
                                    (cond
                                     ((stringp prefix-func)
                                      (let ((beg
                                             (save-excursion
                                               (if (re-search-backward prefix-func
                                                                       (line-beginning-position)
                                                                       t)
                                                   (or (match-end 1)
                                                       (match-end 0))
                                                 nil))))
                                        (when beg
                                          (buffer-substring-no-properties
                                           beg (point)))))
                                     ((and (symbolp prefix-func)
                                           (fboundp prefix-func))
                                      (buffer-substring-no-properties
                                       (funcall prefix-func)
                                       (point)))
                                     (t
                                      nil)))
                ;; do (message "ac-prefix=%s, prefix=%s, source=%s"
                ;;             ac-prefix prefix (symbol-name source))
                when (and candidates-func
                          (symbolp candidates-func)
                          (fboundp candidates-func)
                          (funcall candidates-func))
                collect (intern-soft (replace-regexp-in-string "^ac-source"
                                                               "ac-complete"
                                                               (format "%s" source))))))
    (when completing-functions
      (my-auto-complete-toggle-source-1 completing-functions))))

また変な挙動に気づいたら修正する予定。
関係ないけどctagsのソースのスタイルをきもいと思ってしまったワタシは
ゴミですね。

続auto-complete.elで悩む

どもです。

昨日の続き。
なんとかできたっちゃーできたんですが,お客様満足度100%ではないなりよ。

簡単でもデモ

なんとなーく補完候補が切り替わってるのがわかると思います。

コード

やってることは,ac-sources内の情報源のcandidate関数を片っ端から呼び出して
nilを返さなかったら,その情報源に対応するac-complete-ほにゃらら関数を収集。
んで,トグルキーが押される度にそれらの関数を順番に呼んでいく,というもの。
変数my-auto-complete-index-in-ac-sourcesは,
補完候補を返すことが可能な関数群におけるインデックスです。
今思うと名前間違ってるよな−。
そしていじくると自動でバッファローカルになる変数です。

(setq my-auto-complete-index-in-ac-sources 0)
(make-variable-buffer-local 'my-auto-complete-index-in-ac-sources)

(defun my-auto-complete-toggle-source ()
  (interactive)
  (let ((completing-functions
         (loop for source in (remove 'ac-source-dictionary ac-sources)
               for candidates-func = (cdr (assoc 'candidates (symbol-value source)))
               when (and candidates-func
                         (symbolp candidates-func)
                         (fboundp candidates-func)
                         (funcall candidates-func))
               collect (intern-soft (replace-regexp-in-string "^ac-source"
                                                              "ac-complete"
                                                              (format "%s" source))))))
    (my-auto-complete-toggle-source-1 completing-functions)))

(defun my-auto-complete-toggle-source-1 (completing-functions)
  (prog1 (funcall (nth (% my-auto-complete-index-in-ac-sources
                          (length completing-functions))
                       completing-functions))
    (setq my-auto-complete-index-in-ac-sources
          (% (incf my-auto-complete-index-in-ac-sources)
             (length completing-functions)))))

んであとは,

(define-key ac-menu-map (kbd "\C-c\C-v") 'my-auto-complete-toggle-source)
(define-key java-mode-map (kbd "\C-c\C-v") 'my-auto-complete-toggle-source)

とかで,空いているキーに割り当ててればOK。

ちなみに,

(setq ac-use-menu-map t)

しておかないとダメだと思う。


まあとりあえず版で,これから改良できたらいいなと。
ちょっと便利になったからちょっとだけうれしいです。

auto-complete.el で悩む

どもです。

auto-complete.el のすごさ,ありがたさはいわずもがな。
いや言っておこう。ありがとう!
んで悩んでいることが。


理想から言うと,補完候補が表示されている最中にあるキーを押すことにより,
情報源が切り替わってほしい,ということです。


情報源が複数あり,現在の文字入力状態において,プリフィクスがかぶってい
ると, 2つ以上の情報源からの補完が可能な状況というのがございます。んで
ソースを追ってはないのですが,現状はどれか1つの情報源からの補完候補しか
表示されないはず。よく分からんと思うので例。


ac-sources => '(ac-source-a ac-source-b)
ac-source-a => '("candidate-a-1" "candidate-a-2")
ac-source-b => '("candidate-b-1" "candidate-b-2")
んで,ac-source-aとac-source-bのプリフィクスは同じと仮定。


「cand」と入力した時点で("candidate-a-1" "candidate-a-2")か
("candidate-b-1" "candidate-b-2")のどちらかしか補完候補として上がってこ
ない,ということです。


んでですね,("candidate-a-1""candidate-a-2")が
補完候補として上がっているときに,あるキーを押すことで,
補完候補が("candidate-b-1" "candidate-b-2")に切り替わったらすばらしい
と思いませんか?
いやさ,すばらしすぎますね。


情報源を定義したと同時に,その情報源に対応するinteractiveな補完用関数が
定義されるのですが,これを一々,M-xから呼び出すのはしんどい。
しかしこの機能は大変すばらしい。


せっかくこんなすばらしい機能があるのに使わないのはもったいない。
ここで「あるキー」をトグルキーと呼ぶことにします。
補完候補が表示されている状態で,トグルキーを押す。
ac-sources内の,現在の入力状態に対して補完可能な別の情報源からの補完候補を表示。
こういうことをやりたいのです。


で,なんとなくだけど,たぶん出来るはず。
だって手動でできるから。
auto-complete.el に手を入れるか,自分で関数作ってac-menu-mapでキーに割り当てるか。
思いつく選択肢はこの2つ。
もしくは本家にfeature requestを出して待機モードに入るか。


まあ,そもそもプリフィクスがかぶるような情報源を
つくる自分が悪いということなんですがね!

imenu で ert-deftest に飛びたい

飛んで飛んで飛んで飛んで笑って笑って笑ってわらーう〜う〜。


ert-deftest で定義されたテストの関数? に imenu でジャンプしたいのですが!
ポイントは以下。

(push '(nil "^(ert-deftest \\([^()]+\\) ()$" 1)
        imenu-generic-expression)

push しているリストの中身はというと,まあ info に書いてあるんですけど,
最初の nil は imenu したときに表示されるカテゴリみたいなもん。
nil にしておけば,トップに表示される。
次は正規表現。索引付けしてほしい箇所にマッチする正規表現
その次の 1 というのは,正規表現中のグループの番号。
この部分が imenu したときに表示されると。
テストと通常の関数がたくさんある場合は,
最初の nil を "Tests" とかにしてもいいかもですね。
もちろん正規表現は自分のスタイルに合わせないと意味ナスです。
私は

(ert-deftest test-this-is-just-an-example ()
  ;; ...
)

というスタイルで書いているので上記のような正規表現にしています。

で,これを emacs-lisp-mode-hook に追加しておけばよしと。
私の場合は,以下のようにしています。

(defun my-emacs-lisp-mode-hook-func ()
  (interactive)
  (local-set-key "\M-I" 'imenu)
  (make-local-variable 'indent-tabs-mode)
  (setq show-trailing-whitespace t)
  (setq indent-tabs-mode nil)
  (set (make-local-variable 'dabbrev-check-all-buffers) nil)
  (set (make-local-variable 'dabbrev-select-buffers-function)
       'my-dabbrev-select-elisp-mode-buffers)
  (when (and (require 'auto-complete nil t) (require 'auto-complete-config nil t))
    (make-local-variable 'ac-sources)
    (setq ac-auto-start 2)
    (setq ac-sources '(ac-source-words-in-same-mode-buffers
                       ac-source-imenu
                       ac-source-functions
                       ac-source-symbols
                       ;ac-source-dabbrev
                       ;ac-source-abbrev
                       )))
  (auto-revert-mode 1)
  (push '(nil "^(ert-deftest \\([^()]+\\) ()$" 1)
        imenu-generic-expression))

(add-hook 'emacs-lisp-mode-hook 'my-emacs-lisp-mode-hook-func)

テスト関数みたいなんが増えてきて大変だったので,
調べて解決してみました。

コトシモヨロシコ

明けましておめでとうございます。
去年はあまり日記を書かなかったので,その流れで行くと今年も少なめかも。


というか精神的に非常に調子が悪いのでちょいやばめ。
というわけで今年もよろしくお願いします。

コトシモヨロシコってカタカナで書くとトウモロコシに見えた。
関係ないけど。

pushd をうまく打てないゴミカスな僕

どーもです。


突然ですが,pushd,dirs 使ってますか?
ワタシは使ってるというか,これがないと死亡コースなわけでして。
しかし!!!
よく,pushd を pusdh とか色々タイプミスするぽんこつなワタシでして。
そこで,エイリアスして,

alias pd='pushd'
alias ds='dirs'

としているわけです。


teratermCygwin に接続して使用している分には,すごく快適。
タイプミスのしようがない。万々歳!
と思ってたのもつかの間,Emacs 内の shell で使用すると
問題が発生したわけです。
pd を使うと,Emacs 内でのカレントディレクトリが変更されないのです。
pd してから M-x pwd とかするとわかります。
これは非常に困ります。ファイル名の補完も効かなくなります。
この問題に気づいて放置したのが1年以上前だったはず。
それ以降 Emacs 内の shell では,pushd を使用していました。苦痛やで。


んでですね,昨日ふと解決策を調べてみるかと思い,
shell.el を開いて,んなわけないよねーとか思いながら,pushd で検索してみたのです。

そしたらがびーん。

(defcustom shell-pushd-regexp "pushd"
  "Regexp to match subshell commands equivalent to pushd."
  :type 'regexp
  :group 'shell-directories)

なんかいきなりそれっぽいの見つかっちゃったんですけど−。

ちゅうわけで,以下を .emacs とかに書き書き。

(setq shell-pushd-regexp "\\(pushd\\|pd\\)")

これで今のところ万事解決しています。
他にもおもしろそうな変数が用意されているようなので,
またそのうち見てみるかもです。


まとめると,

と。

mcomplete.el のバグを直せたかも

どーもです。

ちょっと前に,バグを修正して気をよくしたのもつかの間,
新たに別のエラーが発生するというなんとも悲しい状態に。
こんなん。

Wrong type argument: sequencep, t

で,今回は現実逃避のために,このエラーの原因を探ってみました。


原因はというと,#'mcomplete-substr-method-try-completion で,
#'try-completion の結果と文字列を #'concat でつなぐところ。
#'try-completion が `t' を返しちゃったら,こける。
行った修正はというと,t が帰ってきたら,"" と,そうでない場合は,
#'try-completion の結果と,文字列を結合するというやっつけ仕事。
以下修正後の関数。

(defun mcomplete-substr-method-try-completion (str abort-on-input)
  "`try-completion' for substring match method of `mcomplete-mode'."
  (let* ((completions (mcomplete-all-completions str abort-on-input)))
    (cond
     ((null completions)                ; 0 candidate
      nil)

     ((null (cdr completions))          ; 1 candidate
      (if (string= str (car completions))
          t
        (car completions)))

     (t                                 ; multiple candidates
      (let* ((regexp (regexp-quote str))
             (tails-alist (mapcar #'(lambda (item)
                                      (string-match regexp item)
                                      (list (substring item (match-end 0))))
                                  completions))
             (tail (try-completion "" tails-alist)))
        (concat str (if (eq t tail)
                        ""
                      tail)))))))


んで以下,diff。
github で管理したいんだけど,どうしたらいいんだろう。
オリジナルは github にないから,悩みどころ。

diff --git a/mcomplete.el b/mcomplete.el
index d00e548..4e3cde9 100644
--- a/mcomplete.el
+++ b/mcomplete.el
@@ -1305,8 +1305,11 @@ Otherwise try to complete it."
              (tails-alist (mapcar #'(lambda (item)
                                       (string-match regexp item)
                                       (list (substring item (match-end 0))))
-                                  completions)))
-        (concat str (try-completion "" tails-alist)))))))
+                                  completions))
+             (tail (try-completion "" tails-alist)))
+        (concat str (if (eq t tail)
+                        ""
+                      tail)))))))


 (defface mcomplete-substr-method-fixed-part-face


今さっき修正したばっかりなので,これで解決したかどうかわからんが,
しばらくこれで使ってみます。
とりあえずだとしても,なおって良かった!
いぇい。