ミニバッファ上でワイルドカード展開

Vim から Emacs に移行して (併用はしてますが) 困ったことの一つが、ファイルを開く時 (C-x C-f) にグロブ記号を展開してくれない、ということでした。

厳密には、内部的にはグロブ文字を受け取ってはくれるんです。が、マッチした全てのファイルを開いてしまうんですよね。

それが便利な場合もあるとは思うんですが、そうではなく、ミニバッファ上でタブ・キーを押した時に候補を表示して欲しいのです (デフォルトでは "No match" と表示され、これがどうにも Vim に慣れた身には不便でした)。

ということで、作ってみました:

(defadvice minibuffer-complete (around globexpand activate)
  (let* ((str (substitute-in-file-name (minibuffer-contents)))
         (lst (and (string-match "^\\([^[*?]+\\)[[*?]" str)
                   (file-expand-wildcards str))))
    (if (null lst)
        ad-do-it
      (condition-case nil
          (progn
            (setq str (if (null (cdr lst))
                          (car lst)
                        (completing-read "Select file: "
                                         lst nil nil
                                         (match-string 1 str))))
            (mcomplete-clear-minibuffer)
            (insert str)
            (if (file-directory-p str)
                ad-do-it
              (exit-minibuffer)))
        (quit
         (abort-recursive-edit))))))

完全に mcomplete に依存しています。mcomplete がオンになっていないと動作しませんのでご注意ください。


追記
[20070429]:
マッチするファイルが 0 個または 1 個だった場合の対処を加えました。

[20070508]:
ディレクトリ名を補完した場合は、さらにファイル名の入力を続けられるようにしました。あと、マッチするファイルが 1 個のみだった時の挙動をデフォルトと同じにしました。

[20070509]:
"c:/d:/*" のように、既に入力されたディレクトリを消さずに新しいディレクトリを入力した場合に展開できなかった問題を修正しました。

あと、これは以前から可能だったと思うんですが、"d:/*/*.el" のようにディレクトリを横断してファイルを検索することもできます。