制御記号付きのmake-processの出力をいい感じに処理する方法

Intro

Emacsからプロセスを実行できます。

実行する方法はいろいろありますが、最も低レベルな関数は make-process です。

最もユーザーに近い関数は shell-command で、 M-! に割り当てられています。 この関数はコマンド文字列を渡すだけで、お手軽に実行できます。

make-process

make-processは非同期プロセスを作成します。

必須のオプションは :name で、プロセスオブジェクトを返却します。 さらに :buffer, :command, :coding, :connection-type, :noquery, :stop, :filter, :sentinel, :stderr オプションが存在し、細かくプロセスの管理方法を制御できます。

基本的に :name, :buffer, :command を指定することになります。

(setq proc
      (make-process
       :name "sh"
       :buffer (get-buffer-create "*proc*")
       :command '("hub" "clone" "conao3/celpa")))

(progn
  (delete-process proc)
  (shell-command "rm -rf celpa"))

ただ、コマンドから送られてきた文字列を素朴に出力するため、gitのように CR を利用してリッチに出力するコマンドの出力は盛大に壊れます。

CR を解釈しながら賢く処理をすれば良いのですが、とても面倒です。

Emacs-jpで助けを求めると、eshellの関数を利用すれば良いのではという助言があり、調べてみると、きちんと出力できました。

eshellの仕組みに乗っかるので、 eshell-mode にしなければフィルター関数を使えないかと思いましたが、よく見ればいくつかのマーカーを設定するだけで使えました。

(with-current-buffer (get-buffer-create "*proc*")
  (set (make-local-variable 'eshell-last-input-start) (point-marker))
  (set (make-local-variable 'eshell-last-input-end) (point-marker))
  (set (make-local-variable 'eshell-last-output-start) (point-marker))
  (set (make-local-variable 'eshell-last-output-end) (point-marker))
  (set (make-local-variable 'eshell-last-output-block-begin) (point)))

(setq proc
      (make-process
       :name "sh"
       :buffer (get-buffer-create "*proc*")
       :command '("hub" "clone" "conao3/celpa")
       :filter (lambda (proc string)
                 (eshell-output-filter proc string))))

(progn
  (delete-process proc)
  (shell-command "rm -rf celpa"))

まとめ

Emacsは良いエディタで、Elispは強力ですが、GitをElispで実装するのは現実的ではありません。

このように外部コマンドとElispを有効に協調させながら作業しやすい環境を整えていきましょう。