1

When I hit RETURN on a filename in Dired, I'd like Emacs to automatically do one of several things, depending on the file's type:

1 - If it's a text file, I'd like to open the file in a new buffer in Emacs.

2 - If the filetype is listed in a user-configurable list of filetypes and external viewers, open the file in an external viewer associated with that filetype.

3 - If the filetype is not text and not in the above list, return an error saying the file is of an unknown type.

I've found some answers (1, 2) that advise using dired-read-shell-command and either ! or & to open files with external viewers.

The above answers are insufficient for my needs, because:

1 - They require multiple keystrokes (at least ! or & and then RETURN at the prompt), while I want this done with just a single keystroke, without any prompting.

2 - They won't open text files in Emacs, but will instead try to use external viewers for everything. I want text files opened in Emacs.

3 - They dispatch based on filename extensions, while I want to dispatch based on filetype (ie. as a result of using either the "file" or "mimetype" command on linux)

4 - They don't error-out if the filetype is not found in the list of known filetypes.

1

The following is a macOS specific solution as it needs file(1) and open(1). Feel free to adjust the code.

(defun chunyang-file-mime-type (file)
  "Return mime-type of FILE."
  ;; file(1) doesn't fail in such case
  (unless (file-readable-p file)
    (error "%s: No such file or no read permission" file))
  (with-temp-buffer
    (let ((exit (call-process "file" nil t nil "--brief" "--mime-type" file)))
      ;; Remove the final newline if any
      (when (eq (char-before) ?\n)
        (delete-char -1))
      (if (zerop exit)
          (buffer-string)
        (error "file: %s: %s" file (buffer-string))))))

(defun chunyang-open-file-with-app (file app)
  "Open FILE with APP."
  ;; open(1) exists immediately and does not block Emacs
  (call-process "open" nil nil nil "-a" app file))

(defvar chunyang-mime-type-app-alist
  '(("video/mp4" . "VLC")
    ("application/pdf" . "Firefox")))

(defun chunyang-open-file (file)
  "Open FILE base on its mime-type."
  (interactive "f")
  (setq file (expand-file-name file))
  (let ((mime-type (chunyang-file-mime-type file)))
    (if (string-prefix-p "text/" mime-type)
        (find-file file)
      (let ((app (cdr (assoc mime-type chunyang-mime-type-app-alist))))
        (if app
            (chunyang-open-file-with-app file app)
          (user-error "No app is associated with %s" mime-type))))))

(defun chunyang-dired-open-file ()
  (interactive)
  (chunyang-open-file (dired-get-file-for-visit)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.