3

I got

;; Opens files externally from dired.
(defun dired-open-file ()
  "In dired, open the file named on this line."
  (interactive)
  (let* ((file (dired-get-filename nil t)))
    (message "Opening %s" file)
    (call-process "xdg-open" nil 0 nil file)
    (message "Opened %s" file)))
(eval-after-load "dired"
    '(define-key dired-mode-map (kbd "C-c o") 'dired-open-file))

in my init file so I can use dired to open PDFs, videos, etc. in applications like Evince or MPV. However, because I'm stupid, I keep hitting RET when wanting to view a PDF or watch a video which then causes Emacs to freeze.

How can I make dired only open files the way it currently does when I hit RET if xdg-open would open it with Emacs, and otherwise invoke xdg-open to open the file externally?

Note that I don't always want xdg-open to be invoked when I hit RET on file because that'd mean a new Emacs instance would be launched every time I open a simple text file via dired.


There is an answer which enables distinction based on the file name extension. This, however, does not work for me as I have many text files without any file name extension but also pictures and PDFs without file name extensions. xdg-open (and thereby file browsers like Nautilus) manage to open these files using the correct application because xdg-open can distinguish file type based on their magic numbers.

2
  • The answer I gave is not about matching file-name extensions. It's about matching file names - any and all parts of the name. You can even list individual full file names, for example. But yes, if you cannot know anything about the file or its intended use from the name then that simple solution won't help you.
    – Drew
    Commented Sep 27, 2017 at 14:29
  • See xah-open-in-external-app here.
    – Drew
    Commented Sep 27, 2017 at 14:40

2 Answers 2

2

You can use the file command to get the mime type and a list of supported types to automagically open a file in emacs if supported and with an external editor if not. A rough implementation is

(defvar supported-mime-types
  '("text/english"
    "text/plain"
    "text/x-makefile"
    "text/x-c++hdr"
    "text/x-c++src"
    "text/x-chdr"
    "text/x-csrc"
    "text/x-java"
    "text/x-moc"
    "text/x-pascal"
    "text/x-tcl"
    "text/x-tex"
    "application/x-shellscript"
    "text/x-c"
    "text/x-c++"
    "inode/directory"))

(load "subr-x")

(defun get-mimetype (filepath)
  (string-trim
   (shell-command-to-string (concat "file -b --mime-type "
                                    (shell-quote-argument filepath))))

(defun dired-find-file-dwim ()
  (interactive)
  (let ((file (dired-get-filename nil t)))
    (if (member (get-mimetype file) supported-mime-types)
        (find-file file)
      (call-process "xdg-open" nil 0 nil file))))

(with-eval-after-load 'dired
  (define-key dired-mode-map (kbd "RET") #'dired-find-file-dwim))

The choice of better names and the documentation is left as an exercise to the reader.

5
  • Thank you very much! "Unfortunately", Emacs supports editing tons of file types. I find it easier to list everything I don't want dired to open in Emacs. To achieve this, I slightly modified your code and changed the list: pastebin.com/8QWYpCA2 It works like a charm for all kinds of text files I want to edit in Emacs, for folders I want to open in dired, and images I want to open externally. However, for some reason, PDFs are still opened in Emacs even though I listed them just like I listed PNG and JPEG. Can you tell me what my mistake is?
    – UTF-8
    Commented Feb 24, 2018 at 15:28
  • If I start emacs -q, paste your code in the scratch buffer and evaluate it PDFs are opened in evince, the default viewer. What is the output of file with those files? What is your default viewer, the one that starts when you open them from the file browser?
    – matteol
    Commented Feb 24, 2018 at 16:11
  • It's super weird. I did the same as you did but it still opens PDFs in Emacs. But I discovered something: I checked 5 PDFs which are opened in Emacs. But I also checked 1 PDF which is opened externally. I got the mime type for PDFs by executing file -b --mime-type on one of the PDFs which are opened in Emacs. By now, I added "image/gif" to the list but the only GIF I have on my PC is still opened in Emacs.
    – UTF-8
    Commented Feb 24, 2018 at 16:38
  • I now copied all those files to a different directory and named them single letters of the English alphabet (not even a file name extension, just a single letter). On all of these files, it behaves as expected. I think it's something about the file names. Edit: It's not about the file names. It changes behavior depending on the directory, even with the exact same file name. I think it behaves as expected if and only if there is no space in the entire path.
    – UTF-8
    Commented Feb 24, 2018 at 16:40
  • 2
    There was a bug in get-mimetype, the argument must be quoted. See the updated answer for a correct version.
    – matteol
    Commented Feb 24, 2018 at 16:55
0

Customize option dired-guess-shell-alist-user, then use ! (dired-do-shell-command) to act on a file or directory in a Dired listing. The default action will do what you want, according to your dired-guess-shell-alist-user settings. (You need to load standard libraries dired-aux.el and dired-x.el for this feature.)

C-h v tells you:

dired-guess-shell-alist-user is a variable defined in dired-x.el.

Its value is nil

Documentation:

User-defined alist of rules for suggested commands.

These rules take precedence over the predefined rules in the variable dired-guess-shell-alist-default (to which they are prepended).

Each element of this list looks like

(REGEXP COMMAND...)

where each COMMAND can either be a string or a Lisp expression that evaluates to a string. This expression can access the file name as the variable file.

If several COMMANDs are given, the first one will be the default and the rest will be added temporarily to the history and can be retrieved with M-x previous-history-element (M-) .

The variable dired-guess-shell-case-fold-search controls whether REGEXP is matched case-sensitively.

You can set this variable in your ~/.emacs. For example, to add rules for .foo and .bar files, write

(setq dired-guess-shell-alist-user
    '(("\\.foo\\'" "FOO-COMMAND")
      ("\\.bar\\'"
       (if condition
          "BAR-COMMAND-1"
        "BAR-COMMAND-2"))))

You can customize this variable.

4
  • I added an explanation as to why this doesn't work to the question.
    – UTF-8
    Commented Sep 26, 2017 at 18:33
  • 1
    There's dired-open (part of dired-hacks) to ease this process and simply use the RET key. (seen on wikemacs)
    – Ehvince
    Commented Sep 27, 2017 at 7:34
  • @Ehvince Thank you. That text seems to me like what I need to is to install the package dired-open and then possibly customize the variable dired-open-functions to be some value. After installing dired-open (and restarting Emacs), customize-variable doesn't know dired-open-functions. RET in dired doesn't seem to be linked to dired-open because RET still does the same thing as before, whereas the function dired-open always opens the file like xdg-open would, it seems to me. So it launches a new Emacs instance for text files. I'd like them to be opened in the same one.
    – UTF-8
    Commented Sep 27, 2017 at 15:10
  • give openwith a try, this is the one I know actually. But not sure if it will help since your files have no extension.
    – Ehvince
    Commented Sep 27, 2017 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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