25203


`M-!` でシェルコマンドが実行できますが、この際のミニバッファでの入力の `TAB` 補完を独自定義できますか？

具体的には、 rake コマンドの補完が zsh ではできているが、emacs
のコマンド実行の際にはできていないのを不便に感じたので、どうにかしたいと思い質問しています。


CC BY-SA 3.0
2016-05-25T12:47:28.190
25165
2016-05-26T15:28:59.963
754
question
emacs elisp
shell-command で独自補完を定義するには？


[pcmpl-args.el: Enhanced shell completion in
Emacs](https://github.com/JonWaltman/pcmpl-args.el) というパッケージがあります。ELPA
repository に登録されていますので package-install や el-get でインストールできます。

具体的なコードは以下になります。

[code]

    (require 'pcmpl-args)
    
    (defun pcomplete/rake ()
      (require 'cl-extra)
      (pcmpl-args-pcomplete
       (pcmpl-args-cached 'rake t
         (pcmpl-args-make-argspecs
          (append
           (pcmpl-args-extract-argspecs-from-manpage "rake")
           `((argument * (("RAKE-TARGET"
              (:eval
               (let ((rakefile
                      (cl-some 'file-exists-p '("rakefile" "Rakefile" "rakefile.rb" "Rakefile.rb"))))
                 (when rakefile
                   (let* ((cache (pcmpl-args-cache-get 'rake-targets))
                          (ac (assoc (expand-file-name default-directory) cache))
                          (comp (if ac (cdr ac)
                                  (cl-remove-if '(lambda (tg) (string= tg ""))
                                                (split-string
                                                 (shell-command-to-string "rake -sT 2>/dev/null | cut -d' ' -f2") "\n")))))
                     (unless ac
                       (pcmpl-args-cache-put 'rake-targets
                                             (append cache
                                                     (list (append
                                                            (list (expand-file-name default-directory))
                                                            comp))) t))
                     (completion-table-in-turn comp))))))))))))))
    
[/code]

`default-directory` に `Rakefile` などがあるかどうかを確認して、あれば `rake -sT ...` を実行して rule
target を抽出します。抽出結果(補完候補)は `default-directory` とペアにして cache に登録しておきます。

これで zsh の様な補完が可能になるのですが、いくつか問題があります。まず、Rakefile の変更に対応していません。現状では cache
の寿命がデフォルトで 10 秒ですので、その間隔内では齟齬が生じる事になります。Rakefile の timestamp や message digest
を記録しておくなどして cache を適宜更新する様に変更すると良いかもしれません。次に、default-directory 内にある Rakefile
のみを対象にしている点です。つまり、rake の `-f` option で別のディレクトリにある Rakefile
を指定した場合、期待する動作になりません。


CC BY-SA 3.0
2016-05-26T15:22:33.003
25203
2016-05-26T15:28:59.963
2016-05-26T15:28:59.963
25165
answer