EmacsのDiredで起動するアプリケーションの関連付け(advice-addが使える環境のみ)

advice-addで比較的簡単に設定できるので参考までに公開。
diredの一覧で[ENTER]キーを押すとアプリケーション起動。
関連付け対象外のファイルは通常通り。
コードは以下の通り。本体はdired-open-file-by-app。
※case-fnはユーティリティ。
※2024/6/5: ファイル名に~などが入っているとエラーになる場合の対処(encode-coding-string)を入れました。
※2024/6/6: EXEファイルの起動部分の不具合修正。

(defmacro case-fn (expr fn &rest clauses)
  "Eval EXPR and choose among clauses on that value.
Each clause looks like (KEYLIST BODY...).  EXPR is evaluated and compared
against each key in each KEYLIST; the corresponding BODY is evaluated.
If no clause succeeds, cl-case returns nil.  A single atom may be used in
place of a KEYLIST of one atom.  A KEYLIST of t or `otherwise' is
allowed only in the final clause, and matches if no other keys match.
Key values are compared by `fn'.

example 1: following :-> message/returns \"matched 2\"

(case-fn 2 (lambda (x y) (equal x y))
	 (1 (message \"matched 1\"))
	 (2 (message \"matched 2\"))
	 (t (message \"matched t\")))

example 2: following :-> message/returns \"matched 1\"

(case-fn 2 (lambda (x y) (% x y))
	 (1 (message \"matched 1\"))
	 (2 (message \"matched 2\"))
	 (t (message \"matched t\")))

\n(fn EXPR fn (KEYLIST BODY...)...)"
  (declare (indent 1) (debug (form &rest (sexp body))))
  (macroexp-let2 macroexp-copyable-p temp expr
    (let* ((head-list nil))
      `(cond
        ,@(mapcar
           (lambda (c)
             (cons (cond ((memq (car c) '(t otherwise)) t)
                         ((eq (car c) 'cl--ecase-error-flag)
                          `(error "cl-ecase failed: %s, %s"
                                  ,temp ',(reverse head-list)))
                         ((listp (car c))
                          (setf head-list (append (car c) head-list))
                          `(cl-member ,temp ',(car c) :test ,fn))
                         (t
                          (if (memq (car c) head-list)
                              (error "Duplicate key in case: %s"
                                     (car c)))
                          (push (car c) head-list)
                          `(funcall ,fn ,temp ,(car c))))
                   (or (cdr c) '(nil))))
           clauses)))))


(defun dired-find-file-by-app (oldfunc &rest args)
  (let ((orgfname (dired-get-filename))
        (fname)
        (bsfname))
    (setf bsfname (replace-regexp-in-string "/" "\\\\" orgfname))
    (setf fname   (encode-coding-string orgfname 'cp932))
    (setf bsfname (encode-coding-string bsfname 'cp932))
    (case-fn fname (lambda (y x) (string-match x y))
             ("\\.ts$"     (make-process :name "vlc"        :command (list "C:/Program Files/VideoLAN/VLC/vlc.exe" bsfname)))
             ("\\.mp4$"    (make-process :name "mpc"        :command (list "c:/Program Files/MPC-HC/mpc-hc64.exe" fname)))
             ("\\.dcv$"    (make-process :name "DMMPlayer2" :command (list "C:/Users/yuuic/AppData/Local/Programs/DMM Player v2/DMM Player v2.exe" fname)))
             ("\\.xls.*$"  (make-process :name "EXCEL"      :command (list "C:/Program Files/Microsoft Office/root/Office16/EXCEL.EXE" "/n" fname)))
             ("\\.doc.*$"  (make-process :name "WORD"       :command (list "C:/Program Files/Microsoft Office/root/Office16/WINWORD.EXE" fname)))
             ("\\.exe$"    (make-process :name "EXE"        :command (list orgfname)))
             (t            (apply oldfunc args)))))

(advice-add 'dired-find-file :around 'dired-open-file-by-app)

いいなと思ったら応援しよう!