■ Emacs 全般で利用できる設定


【お知らせ】


<2019/04/16 追記>
helm-lisp-commands 関数に引数を追加し、複数のコマンドを作成できるようにしました。

<2019/04/11 追記>
ghq や hub を利用する例を追加しました。

<2018/10/22 追記>
lisp-commands の S式を progn を使って複数式をまとめて書いた場合、helm の表示が整形されるように対応しました。

<2018/10/21 追記>
helm-epeco に query をサポートしました。

【本題】


helm で peco もどきのコマンドを使うための設定です。次で紹介されているような peco の機能を Emacs でも実現したくて作りました。

以下の設定を有効にしてご利用ください。
(defun helm-epeco (command &optional filter query)
  (interactive "sCommand: \nsFilter: ")
  (helm :sources (helm-build-sync-source "Epeco"
                   :candidates (lambda ()
                                 (split-string (shell-command-to-string command) "\n" t))
                   :candidate-number-limit 10000
                   :action (helm-make-actions
                            "Output" (lambda (candidate)
                                       (insert (with-temp-buffer
                                                 (dolist (cand (helm-marked-candidates))
                                                   (insert (format "%s\n" cand)))
                                                 (when (and filter (not (string= filter "")))
                                                   (shell-command-on-region (point-min) (point-max) filter nil t))
                                                 (goto-char (point-min))
                                                 (when (re-search-forward "\n\\'" nil t)
                                                   (replace-match ""))
                                                 (buffer-string)))))
                   :migemo t)
        :input query
        :prompt "[MIGEMO] pattern: "
        :buffer "*helm epeco*"))

shell-mode のバッファ上での利用を想定しており、以下のような動きとなります。

例えば、次の設定を shell-mode のバッファ上で評価した場合、
(helm-epeco "ps -ef" "awk '{print $2}' | xargs -r echo kill" "ps")
次のコマンドを shell で動かすことと同じイメージの動きとなります。
ps -ef | peco --query ps | awk '{print $2}' | xargs -r echo kill
具体的には、
command(例では、ps -ef)を実行 ※ command は、helm-epeco を実行するバッファのロケーション(tramp で接続している場合は、その接続先)で動作する
 -> command の出力結果を helm で選択(複数行選択可) ※ helm-epeco に query の指定があれば、helm の検索条件の初期値として利用する
  -> helm の選択行に対し、filter(例では、kill コマンドを生成)を実行 ※ filter は、ローカル側(Emacs を起動しているホスト)で動作する
   -> カーソルの位置に挿入


利用方法は以下のとおりとなります。

方法1)M-x helm-epeco で起動する

command と filter の入力を求められますので、command に実行するコマンドを、filter にフィルタで利用するコマンドを入力してください。filter は改行にて入力を省略できます。

方法2)次のような Emacs lisp を書き、shellバッファ等で M-: で入力して評価する
(helm-epeco "echo -e 'test1\ntest2'")
(helm-epeco "ps -ef" "awk '{print $2}' | xargs -r echo kill")
(helm-epeco "ghq list -p" "xargs -r -L 1 echo cd")
(helm-epeco "ghq list" "cut -d '/' -f 2,3 | xargs -r -L 1 echo hub browse")
(helm-epeco "docker ps -a" "awk '{print $1}' | xargs -r echo docker container start")
(helm-epeco "docker images" "awk '{print $3}' | xargs -r echo docker image rm")
※ ghq、hub の説明は次のページを参照してください。hub browse で利用する BROWSER 環境変数の設定は、cygstart、wslstart、xdg-open などを指定するのが便利かと思います。

方法3)方法2で作成したコマンドを以下のようにキーに登録して利用する
(define-key helm-command-map (kbd "C-k")
  (lambda ()
    (interactive)
    (helm-epeco "ps -ef" "awk '{print $2}' | xargs -r echo kill")))
※ 上記の場合は、helm-command-prefix のキー+ C-k で起動します。

方法4)方法2で作成したコマンドを以下のように helm で選択して実行できるようにする(キーの設定に迷うことがなくなり、お勧めです)
(defun helm-lisp-commands (lisp-commands)
  (helm :sources (helm-build-sync-source "Lisp Commands"
                   :candidates (lambda ()
                                 ;; S式を整形して表示する場合
                                 (mapcar (lambda (x)
                                           (replace-regexp-in-string
                                            "\n\\'" ""
                                            (pp-to-string x))) lisp-commands))
                                 ;; S式を一行で表示する場合
                                 ;; (mapcar (lambda (x) (format "%S" x)) lisp-commands))
                   :candidate-number-limit 1000
                   :multiline t
                   :action (helm-make-actions
                            "Eval" (lambda (candidate)
                                     (eval (read candidate))))
                   :migemo t)
        :prompt "[MIGEMO] pattern: "
        :buffer "*helm lisp commands*"))

(setq lisp-commands-for-shell-mode
      '((helm-epeco "ps -ef" "awk '{print $2}' | xargs -r echo kill")
        (helm-epeco "ghq list -p" "xargs -r -L 1 echo cd")
        (helm-epeco "ghq list" "cut -d '/' -f 2,3 | xargs -r -L 1 echo hub browse")
        (helm-epeco "docker ps -a" "awk '{print $1}' | xargs -r echo docker container start")
        (helm-epeco "docker images" "awk '{print $3}' | xargs -r echo docker image rm")))

(define-key shell-mode-map (kbd (concat helm-command-prefix-key " C-l"))
  (lambda ()
    (interactive)
    (helm-lisp-commands lisp-commands-for-shell-mode)))
※ 上記の場合は、helm-command-prefix のキー+ C-l で起動します。

以上となります。

この設定を考える前には、次のようなものも作っていました。ただし、本設定の方が tramp の接続先にコマンドを配置する必要がないなど、お手軽に使えると思います。

一方、本設定の helm-epeco では、command に history のような shell の内部コマンドは利用できません。上記の epeco コマンドでは、epeco にパイプで接続するコマンドとして history を実行する形となりますので、利用可能です。history を利用するためであれば次の設定も公開していますので、併せての利用を検討ください。

また、本設定の helm-epeco は非同期に動作しません。(command の出力が全て終わってから helm が起動します。)peco は非同期に動作しますし、上記 GitHub で紹介している epeco-async も tramp の接続先では動作しませんが非同期に動作します。

このように helm-epeco はお手軽に使える代わりに利用する上での制約もありますので、ご留意ください。


<変更履歴>
  • 2018/10/20 このページを作成した。
  • 2018/10/21 helm-epeco に query をサポートした。
  • 2018/10/22 lisp-commands の S式を progn を使って複数式をまとめて書いた場合、helm の表示が整形されるように対応した。
  • 2019/04/16 helm-lisp-commands 関数に引数を追加し、複数のコマンドを作成できるようにした。