Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

emacs dired and openwith

Tags:

emacs

elisp

dired

I want to configure emacs to use external applications when I open image files from dired mode.

On the other hand, I also want to use inline images in emacs buffers.

To open files in external applications I use openwith.el package http://www.emacswiki.org/emacs/OpenWith

The problem with the openwith minor mode is that it is global and when it is enabled by a dired-mode-hook

(add-hook 'dired-mode-hook
          (lambda ()
            (setq truncate-lines t)
            (openwith-mode t)
            ))

it acts everywhere and all inline images in emacs buffers are opened in external applications.

I tried to change

:global t 

to

:global nil 

in the openwith.el, but it somehow disables openwith mode completely.

So, my question is: How to tell emacs to use openwith minor mode only with dired buffer and not anywhere else?

Thanks.

like image 815
I Rom Avatar asked Jun 27 '12 01:06

I Rom


2 Answers

The way openwith-mode works is a bit peculiar: it really assumes that you use it either globally or not at all. What you want here, however, is to use it locally, namely only from within a dired buffer.

That cannot be achieved very easily, but here is a way.

Open your the source file of openwith-mode, openwith.el. Then scroll all the way down until you get to the definition of the actual minor mode. Then comment out that definition by placing a semicolon at the beginning of each line:

;;;###autoload
; (define-minor-mode openwith-mode
;   "Automatically open files with external programs."
;   :lighter ""
;   :global t
;   (if openwith-mode
;       (progn
;         ;; register `openwith-file-handler' for all files
;         (put 'openwith-file-handler 'safe-magic t)
;         (put 'openwith-file-handler 'operations '(insert-file-contents))
;         (add-to-list 'file-name-handler-alist '("" . openwith-file-handler)))
;     (setq file-name-handler-alist
;           (delete '("" . openwith-file-handler) file-name-handler-alist))))

Then underneath this code (but before (provide 'openwith)), insert the following code:

(defvar openwith-mode nil)

(mapc (lambda (function) 
        (ad-add-advice function 
                       '(dired-openwith nil t (advice . (lambda () (let ((openwith-mode t)) ad-do-it))))
                       'around 0))
      '(dired-find-alternate-file 
        dired-find-file 
        dired-find-file-other-window
        dired-mouse-find-file-other-window
        dired-view-file))

(put 'openwith-file-handler 'safe-magic t)
(put 'openwith-file-handler 'operations '(insert-file-contents))
(add-to-list 'file-name-handler-alist '("" . openwith-file-handler))

This code does a few things.

First, it defines a variable called openwith-mode. This variable is used inside one of the functions of openwith-mode that decides whether to use an external application or not. Normally, a variable like that is provided automatically by Emacs when you define a minor-mode -- however since we just commented out the definition of the actual minor mode above, we explicitly re-introduce this variable here.

The purpose of the variable is to work as a kind of switch through which we can control whether or not an image file should be inlined or passed to an external viewer.

Next we have the (mapc ...) expression. What we do here is iterate over a list of five functions:

  • dired-find-alternate-file
  • dired-find-file
  • dired-find-file-other-window
  • dired-mouse-find-file-other-window
  • dired-view-file

which are functions dired provides for opening a file. To each of these function we add a small amount of code in a technique called advising: what (ad-add-advice...) does is set the variable openwith-mode to t whenever one of these five functions is called. Outside of the function call, the variable remains set to nil.

This has the effect that whenever you use one of dired's functions to open a file, the function of openwith-mode that is responsible for calling external applications sees the variable set to t and promptly tries to open an external application if it knows one. The reason we have to jump through such hoops is that the same openwith-mode function is also called whenever you open an image file using C-x C-f - that's just the way openwith-mode is implemented.

(NB: unfortunately we cannot just use the current major-mode as a switch because this will be the major-mode of a new buffer that will have been created already for the file to open. It is always fundamental-mode.)

Finally, the last three lines are just copy & paste from the minor-mode definition we commented out before. They say that the function I've been mentioning a lot already and that is responsible for calling external applications -- called open-with-filehandler -- is a so-called file-handler. It does not really do anything special for actual access to the file, hence we're setting safe-magic for that function to t. Also, we declare that the operation insert-file-contents is handled by our function in a non-trivial way. (See here for more information on these properties.)

And at the very last, we actually install the file handler.


Important: The openwith-mode documenation recommends that you put the following two lines into your .emacs file:

(require 'openwith)
(openwith-mode t)

Now that there is no minor-mode openwith-mode any more (after we commented its definition out), please make sure that you remove the second line, e.g. by commenting it out too:

;; (openwith-mode t)

After re-starting Emacs, if you open an image file with dired, it should open in an external application; if you open it via C-x C-f it will be inlined in a buffer.

like image 188
Thomas Avatar answered Oct 12 '22 00:10

Thomas


I don't use openwith. I use the function defined at

http://ergoemacs.org/emacs/emacs_dired_open_file_in_ext_apps.html

like image 40
rvf0068 Avatar answered Oct 12 '22 01:10

rvf0068