ujimushiの日記: T-code's night 第五夜 「君と付き合うまでのすれ違い」 手探りのauto-tc-complete
auto-complete-modeは,補完候補が見つかった時点でポップアップメニューが 表示され,カーソルキーで選択するという非常に分かりやすい操作性です。
特に設定ファイルの編集等でEmacs lispのファイルを編集している時には,非常に便利だと実感します。
ただ,auto-complete-modeにも不満があります。基本的にEmacsの単語の区切り単位でしか補完しません。 そのため,日本語の文章のようにスペースを入れない文章を書く時は補完が効かず, 行の先頭の時だけものすごい長いフレーズの候補が表示されるという動作になってしまいます。 日本語の文章のテキストファイルを作る時は不便ですね。
それなら,tc-completeとauto-complete-modeのいいとこ取りが出来ればベスト。
で,何かいい情報が無いかと探していると,auto-completeの拡張方法について 制作者が紹介している記事がありました。 それではその内容, 「Emacsのトラノマキ」連載第10回「auto-completeを拡張しよう」 を確認していきます。
記事の内容をざっと流し読みして,次のように理解しました。
- 「candidate」属性に候補リストを放り込む
- Emacsの単語区切り以外で区切るので「prefix」に一致している部分を放り込む
さて,前夜のところで「tcode-complete-candidate-list」の内容が
((333 . "ダメ") "ダメよ~" "ダメダメ")
のようになっていることを確認しました。ならば,
- “ダメ” → prefix
- (“ダメよ~” “ダメダメ”) → candidate
ってすればイケるかも。と思ったのが長い苦難の日々の始まりでした…。
ということで,まずprefix属性とcandidate属性を作ってみましょう。carとかcdrを使えば 「tcode-complete-candidate-list」から簡単に取り出せるようです。 そして返り値は最後の式の評価値のようです。 スクラッチバッファで色々試してみながら 次のように実装しました。
(defun tcode-ac-prefix ()
"auto-completeに一致する正規表現を渡す"
(cdr (car tcode-complete-candidate-list)))
(defun tcode-ac-candidate ()
"auto-completeに候補リストを渡す"
(cdr tcode-complete-candidate-list))
そして,「tcode-complete-display」には表示機能とか時間待ちとか余分な機能がついているので, 新たに関数を作ってそれを使うことにします。 何とか格闘しながら最終的にはこうなりました。
(defun tcode-ac-complete-set ()
"Auto-complete用に完全候補リストを設定する。
バッファローカルの変数である
`tcode-complete-candidate-list' に格納される。
`tcode-complete-display' を参考にした"
(let* ((candidates (tcode-complete-search-candidate
(tcode-complete-scan-backward)))
(prefix (cdr (car candidates))))
(if (< (length prefix) tcode-complete-min-context-length)
(setq tcode-complete-candidate-list nil)
(setq tcode-complete-candidate-list candidates))
))
- ローカル変数「candidates」に一旦「tcode-complete-candidate-list」にコピーする内容を代入
- さっきのところでいう「ダメ」に相当する文字列をローカル変数「prefix」に代入
- 「prefix」の文字数が「tcode-complete-min-context-length」未満なら 「tcode-complete-candidate-list」にnilを代入。 それ以外の時は,「candidates」を代入
という流れ。
また,さっき作った関数を呼び出すところも新たに作ります。 時間待ちを除いただけですね。
(defun tcode-ac-complete-set-function ()
"`post-command' にフックしてバックグラウンドで候補リストを作成する。
`tcode-complete-display-function' を参考にした。"
(if (and (tcode-on-p)
(memq last-command tcode-input-command-list))
(tcode-ac-complete-set)))
そして,auto-complete-modeに渡す拡張機能のセットを定義します。
(defvar ac-source-tcode-complete
'(
(prefix . tcode-ac-prefix)
(candidates . tcode-ac-candidate)
(symbol . "T"))
"tc-completeの機能を使ったAuto-Completeの補完機能")
確認のためにシンボルを付けます。当然「T」です。言わずもがなですね。(auto-completeの命名規約からは外れています) そして最後にフックする関数を変更します。
;(add-hook 'post-command-hook 'tcode-complete-display-function)
; 従来のtc-complete表示の代わりにauto-complete用の候補作成ルーチンをhookする
(add-hook 'post-command-hook 'tcode-ac-complete-set-function)
.tcの内容も確認します。tcode-ready-hookで「tc-complete」をロードします。
(setq tcode-complete-max-candidate-count 10) ; 最大10件の候補を出す
(setq tcode-complete-min-context-length 2) ; 最小2文字
(setq tcode-complete-max-context-length 8) ; 最長8文字前まで見る
(setq tcode-complete-mazegaki-prefix-length 3) ; 交ぜ書き変換辞書の中を確認する文字数
(add-hook 'tcode-ready-hook
(function
(lambda ()
(require 'tc-complete)
)))
そしてEmacsを立ち上が直して…あれ,あれ,うまくいかない…
何回も色々変えてやってみますが,うまくいきません。で,もう一度記事の読み直し。
prefix属性に関数を指定した場合は、その関数の返り値を補完対象の開始位置とします。
え~。完全に見落としてた。そこでさっきの「tcode-ac-prefix」関数を修正します。
(defun tcode-ac-prefix ()
"auto-completeに開始位置を渡す"
(car (car tcode-complete-candidate-list)))
実質cdrをcarに変えただけですね。それでは試してみましょう。(何度Emacsを立ち上げ直したことか)
そんなん言うてもダメ
│ダメよ~ T│
│ダメダメ T│
└────────┘
おお~。でけた~。やった~。
今夜はここまで。動作を確認した内容は次回に説明します。
T-code's night 第五夜 「君と付き合うまでのすれ違い」 手探りのauto-tc-complete More ログイン