Pull to refresh

Открываем файлы во внешних приложениях

Reading time 2 min
Views 2.6K
Emacs имеет крутую курву обучения, но чем дальше, тем больше хочется делать в нем все, что можно и нельзя. В частности он обладает большим числом средств для навигации по файловой системе.
Я, например, использую Dired mode, ido, Org mode и закладки. Но существует проблема с открытием файлов во внешних приложениях: pdf в evince, avi в mplayer и т.д. Причем хочется задавать эти связи в одном месте. Emacs не был бы Emacs'ом, если бы не позволял сделать для этого какой-нибудь грязный хак =)

Ассоциации приложений с типами файлов мы зададим в виде списка пар, в которых первый элемент — это строка, разделенных пробелом расширений, а второй — команда для запуска приложения:

(defvar command-list
'(("jpg jpeg png bmp" .
"gqview")
("pdf djvu ps" .
"evince")
("html htm" .
"firefox -new-tab")
("ogv mpg mpeg avi flv
VOB wmv mp4 mov mkv divx
ogm m4v asf rmvb" .
"mplayer -fs")
("doc odf odt rtf" .
"ooffice")))

Из строки расширений сделаем регулярку:

(defun build-re (str)
(let ((re "\\.\\(")
(ext-list (split-string str)))
(dotimes (n (- (length ext-list) 1))
(setq re (concat re (nth n ext-list) "$\\|")))
(setq re (concat re (car (last ext-list)) "$\\)$"))
re))

Напишем функцию, которая получает путь к файлу,

(defun try-open-external (filename)
(let ((success nil))
;; проходит по списку команд,
(dolist (command command-list)
(let ((cmd (cdr command))
;; делает ругулярное выражение из расширений,
(re (build-re (car command))))
;; пытается сопоставить с ним путь.
(when (string-match re filename)
;; В случае успеха запускает программу,
(shell-command-to-string (concat cmd
" "
(shell-quote-argument filename)
" &> /dev/null &"))
(setq success t))))
;; и говорит нашла ли она ассоциацию с файлом.
success))

Теперь надо заставить Emacs запускать нашу функцию, при попытке открыть файл. Документация говорит нам, что фунции типа find-file используют find-file-noselect.

Поэтому мы сохраним системную функцию

(fset 'old-find-file-noselect (symbol-function 'find-file-noselect))

и переопределим ее, чтобы она пыталась открыть файл во внешней программе,

(defun find-file-noselect (filename &optional nowarn rawfile wildcards)
(if (try-open-external filename)
nil
;; а в случае неудачи вызывала системную функцию.
(old-find-file-noselect filename nowarn rawfile wildcards)))

Для Org mode напишем маленькую функцию, чтобы при открытии ссылок
переход в другое окно был только для файлов, открываемых внутри Emacs

(defun my-org-find-file (file)
(when (not (try-open-external file))
(find-file-other-window file)))

и будем использовать ее для всех типов файлов

(org-file-apps (quote ((".*" my-org-find-file file))))


Пока особых проблем с этим не возникало, хотя, например, не работает запуск внешних приложений через tramp, но оно вроде и надо.

Скачать код.

Любая критика и советы, как говорится, велкам.
Tags:
Hubs:
+4
Comments 5
Comments Comments 5

Articles