quickrunパッケージは、 M-x executable-interpret の進化形ともいえるもので、多種多様な言語に対応していて、開発中のプログラムの実行を簡単にしてくれます。

quickrunのレビューはココ に書きました。

機能性には注目していたのですが、僕には不満点がいろいろと目に付いて使っていませんでした。
そこで重い足を上げてquickrunの機能を再整理することで僕的には満足する仕上がりになりました。
これでやっとquickrunの恩恵を受けられて嬉しいです。
以後愛用します。

作者のsyohexさんの考えとは異なるかもしれませんが、このようなquickrunユーザがいるということです。
機能の再整理をするのはとても簡単なことでした。
それはひとえにわかりやすいコードを書いたsyohexさんの腕前によるものです。
感謝いたします。

不満点1:コマンドが多すぎる

僕がquickrunに対して思った不満点は2つです。

1つめはquickrun関連のコマンドが多すぎることです。
M-x quickrun だけでなく、 M-x quickrun-with-argM-x quickrun-shell と、実行関係でも3つのコマンドに分かれている点が我慢なりませんでした。
とくにM-x quickrunはquickすぎて引数・入力なしでいきなり実行してしまいます。
引数を付ける際にはわざわざ別のコマンドM-x quickrun-with-argを実行する必要があります。
しかもバグなのか仕様なのかわかりませんが、M-x quickrun-with-argで指定した引数が quickrun-option-args に反映されていないため、同じ引数で実行する場合はM-x quickrunではなくて再びM-x quickrun-with-argを使う必要があります。
おまけにM-x quickrun-with-argのデフォルト引数が空であり前回のものになっていません。
これらは簡単な変更で解決できるので仕様だと思います。

M-x quickrun-shellは eshell を使って何度も実行する便利な機能です。
とくに標準入力を含むプログラムの場合、いろいろな入力を試せます。
しかし、コマンドライン引数は指定できないという欠点があります。

quickrunには outputter という機構があり、様々な出力方法を指定できるようになっています。
ファイルローカル変数 quickrun-option-outputter にmessageと指定することで、ウィンドウ分割ではなくてエコーエリアに出力結果を出力してくれます。
ちょっとしか出力しないプログラムの開発にはmessage outputterは便利です。
「とにかく黙れ!」って言いたいときはnullを指定します。

でも、現状のoutputterよりも大きな枠で考えるとM-x quickrun-shell(eshell)も実行方法の1つではないでしょうか?
僕的にはdefault/message/nullとともにeshellも付け加えたいと思うところです。

不満点2:ファイルローカル変数

2つめは細かい制御をする際には ファイルローカル変数 を使う必要があることです。
ファイルローカル変数、確かに便利な機能ですが、僕はいまいち好きになれません。
なぜなら、 M-x add-file-local-variable を実行しても M-x normal-modeC-x C-v RET などでわざわざ反映させる必要があるからです。
M-x add-file-local-variableの時点で反映させてもいいのではと疑問に思っています。
それにquickrunという自分の環境に依存した設定をソースコードに書きたくないというのがあります。
特に実行時の引数を指定するquickrun-option-argsをわざわざファイルローカル変数にすると、変更するのも面倒です。
引数や入力が伴う場合、現状では全然quickとは言えないと思います。

解決策!

そこで僕は M-x my-quickrun というたった1つのコマンドでquickrunのほとんどの機能をカバーすることを考えました。
anythingの開発者としては、コマンドは少ないに越したことはないと考えています。
M-x my-quickrunでは、ファイルを開いて初めて実行する際には以下の情報を尋ねるようにしました。

  • qrinput系(qriput, qrinput2,qrinput3などのファイルが存在するとき)はhelmインターフェースによる入力ファイルの選択
    • C-z (persistent-action)により入力ファイルの内容を確かめられる
  • 実行方法(default/message/null/eshell)の選択
  • 引数の入力

それらの情報は バッファローカル変数 になっているので二度目のM-x my-quickrunの実行は文字通りquickです!
C-u M-x my-quickrunでこれらの情報の変更ができます。

たったこれだけの改良で、多くの場合はファイルローカル変数に頼ることもなく、たった1つのコマンドでquickrunの多機能の恩恵が受けられるようになりました。

コード

(require 'quickrun)

;;; 初めにファイルを開いたときに実行方法を決めさせる
;;; outputterとeshellを統合させる
(defvar-local my-quickrun-execute-method nil)
(defvar-local my-quickrun-command 'quickrun)
(defvar my-quickrun-execute-method-alist
  '((default . (lambda () (setq quickrun-option-outputter nil)))
    (message . (lambda () (setq quickrun-option-outputter 'message)))
    (null . (lambda () (setq quickrun-option-outputter 'null)))
    ;; (eshell . (lambda () (setq my-quickrun-command 'quickrun-shell)))
    (screen . (lambda () (setq my-quickrun-command 'quickrun-screen)))))

(defun my-quickrun (arg)
  "俺のquickrun。初めてファイルを開いたとき、入力ファイル、実行方法、引数を尋ねる。
C-uをつけたらそれらをもう一度尋ねる。

入力ファイルを尋ねるのは*.qrinput*が2つ以上存在するときのみ。
helmを使っているのでC-zで入力ファイルの内容を確認できる。"
  (interactive "P")
  (when arg
    (setq my-quickrun-execute-method nil
          my-quickrun-command 'quickrun
          quickrun-option-args nil))
  (my-quickrun/may-ask-stdin-file)
  (my-quickrun/may-ask-execute-method)
  (my-quickrun/may-ask-args)
  (setq current-prefix-arg nil)
  (funcall my-quickrun-command))

(defun my-quickrun/may-ask-stdin-file ()
  (let ((stdin-files (directory-files default-directory nil
                                      (concat (quickrun/stdin-file-name) "*")))
        new-stdin-file)
    (when (and (null my-quickrun-execute-method) (<= 2 (length stdin-files)))
      (setq new-stdin-file
            (save-window-excursion
              (helm :sources 'helm-source-files-in-current-dir
                    :input (concat quickrun/executed-file
                                   (default-value 'quickrun-input-file-extension)))))
      (when new-stdin-file
        (setq-local quickrun-input-file-extension
                    (concat "." (file-name-extension (car new-stdin-file))))))))

(defun my-quickrun/may-ask-execute-method ()
  (setq my-quickrun-execute-method
        (or my-quickrun-execute-method
            (completing-read "Execute method: "
                             (mapcar 'car my-quickrun-execute-method-alist)
                             nil t)))
  (funcall (assoc-default my-quickrun-execute-method my-quickrun-execute-method-alist)))

(defun my-quickrun/may-ask-args ()
  (setq quickrun-option-args
        (or quickrun-option-args
            (read-string "QuickRun Arg: "
                         (car quickrun--with-arg--history)
                         'quickrun--with-arg--history))))

;;; execute in screen
(require 'mylisp-screen)

(defun quickrun-screen ()
  (interactive)
  (let ((quickrun-timeout-seconds nil))
    (advice-add 'quickrun/exec :override 'quickrun/exec--screen)
    (unwind-protect
        (quickrun)
      (advice-remove 'quickrun/exec 'quickrun/exec--screen))))

(defun quickrun/exec--screen (cmd-list &rest them)
  (kill-buffer quickrun/buffer-name)
  (screen (quickrun/concat-commands cmd-list) "->quickrun" t))


(provide 'mylisp-quickrun)

本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。