Emacsのバージョンを24.4に上げたところ、愛用していた iswitchb
がobsoleteと言われました。移行先として icomplete
を採用。helm
と ido
は、インターフェイスの大幅な変化にどうしても慣れませんでした。
icompele-mode
を使っていると、補完、入力を完了して、制御を返すためのコマンドが複数選べます。
exit-minibuffer
minibuffer-complete-and-exit
minibuffer-force-complete-and-exit
の三つがそうです。
この三つの動作を、空気を読んで使い分けてくれるコマンドが欲しかったので書いてみました。
コード
(icomplete-mode 1)
;; ミニバッファで補完を行いたくないコマンドのリスト
(defvar icomplete-exceptional-command-list
'(dired-create-directory
dired-do-copy
dired-do-rename))
;; 起動時のカーソル位置を保持するための変数
(defvar icomplete-initial-point-max nil)
;; 起動時の補完候補リストを保持するための変数
(defvar icomplete-initial-completions nil)
;; 起動時に呼び出し元のコマンド名を保持するための変数
(defvar icomplete-this-command nil)
;; 起動時に上記の変数に値を代入するフックをかける
(add-hook 'icomplete-minibuffer-setup-hook
(lambda ()
(setq icomplete-initial-point-max (point-max))
(setq icomplete-initial-completions
(completion-all-sorted-completions
(icomplete--field-beg) (icomplete--field-end)))
(setq icomplete-this-command this-command)))
;; 入力完了のコマンド定義
(defun minibuffer-force-complete-and-exit-dwim ()
"Complete the minibuffer with first of the matches and exit."
(interactive)
(cond ((member icomplete-this-command icomplete-exceptional-command-list)
(exit-minibuffer))
((and (eq (point-max) icomplete-initial-point-max)
(equal (car (completion-all-sorted-completions
(icomplete--field-beg) (icomplete--field-end)))
(car icomplete-initial-completions))
minibuffer-default)
;; Use the provided default if there's one (bug#17545).
(minibuffer-complete-and-exit))
(t (minibuffer-force-complete)
(completion--complete-and-exit
(minibuffer-prompt-end) (point-max) #'exit-minibuffer
;; If the previous completion completed to an element which fails
;; test-completion, then we shouldn't exit, but that should be rare.
(lambda () (minibuffer-message "Incomplete"))))))
;; 入力完了をリターンキーにバインド
(define-key icomplete-minibuffer-map [return]
'minibuffer-force-complete-and-exit-dwim)
説明
icomplete-mode
では、minibuffer-force-complete-and-exit
( C-j
)を使うことで、補完候補の先頭を選んで補完し、入力を完了することが出来ます。また、デフォルト値が設定されていて、かつ、ユーザーからの入力がなかった場合は、デフォルト値が選択されます。
しかしながらこの minibuffer-force-complete-and-exit
、時々空気を読んでくれません。例えば、Rubyスクリプトの編集中、ruby-load-file
( C-c C-l
)で実行を試みた時、デフォルト値には編集中のファイル名が設定されていて、かつ、入力はしていなかったにも関わらず、デフォルト値ではなく、補完候補の先頭(大抵の場合"./")が採用されてしまいます。他にも、 dired-mode
でディレクトリの作成( +
)を試みると、「補完できない」と言われて入力が完了できません。
もちろん、そういった時は、 exit-minibuffer
( C-m
)を使うことで、所望の動作が得られます。でも、できたら、自分が使うコマンドは一つだけで、後は良きに計らって欲しい。ということで対症療法的に書いた代物が上記のコードです。
変更点は二つです。
第一に、 ruby-load-file
のように、ミニバッファにプロンプト以外の文字列が最初から入っているタイプのコマンドでも、正しくデフォルト値が採用されるようになりました。第二に、 dired-create-directory
のように、exit-minibuffer
で入力を完了したいコマンドは、icomplete-exceptional-command-list
に登録しておくことで、exit-minibuffer
が呼ばれるようになりました。