-
-
Notifications
You must be signed in to change notification settings - Fork 78
/
dired-open.el
251 lines (196 loc) · 8.55 KB
/
dired-open.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
;;; dired-open.el --- Open files from dired using using custom actions
;; Copyright (C) 2014-2015 Matúš Goljer
;; Author: Matúš Goljer <matus.goljer@gmail.com>
;; Maintainer: Matúš Goljer <matus.goljer@gmail.com>
;; Keywords: files
;; Version: 0.0.1
;; Created: 14th February 2014
;; Package-Requires: ((dash "2.5.0") (dired-hacks-utils "0.0.1") (emacs "24"))
;; URL: https://github.com/Fuco1/dired-hacks
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; While Emacs already has the `auto-mode-alist', this is often
;; insufficient. Many times, you want to open media files, pdfs or
;; other documents with an external application. There's remedy for
;; that too, namely `dired-guess-shell-alist-user', but that is still
;; not as convenient as just hitting enter.
;; This package adds a mechanism to add "hooks" to `dired-find-file'
;; that will run before Emacs tries its own mechanisms to open the
;; file, thus enabling you to launch other application or code and
;; suspend the default behaviour.
;; By default, two additional methods are enabled,
;; `dired-open-by-extension' and `dired-open-subdir'.
;; This package also provides other convenient hooks:
;;
;; * `dired-open-xdg' - try to open the file using `xdg-open'
;; * `dired-open-guess-shell-alist' - try to open the file by
;; launching applications from `dired-guess-shell-alist-user'
;; * `dired-open-call-function-by-extension' - call an elisp function
;; based on extension.
;;
;; These are not used by default.
;; You can customize the list of functions to try by customizing
;; `dired-open-functions'.
;; To fall back to the default `dired-find-file', you can provide the
;; prefix argument (usually C-u) to the `dired-open-file' function.
;; This is useful for example when you configure html files to be
;; opened in browser and you want to edit the file instead of view it.
;; Note also that this package can handle calls when point is not on a
;; line representing a file---an example hook is provided to open a
;; subdirectory under point if point is on the subdir line, see
;; `dired-open-subdir'.
;; If you write your own handler, make sure they do *not* throw errors
;; but instead return nil if they can't proceed.
;; See https://github.com/Fuco1/dired-hacks for the entire collection.
;;; Code:
(require 'dired-x)
(require 'dired-hacks-utils)
(require 'dash)
(defgroup dired-open ()
"Open files from dired using using custom actions."
:group 'dired-hacks
:prefix "dired-open-")
(defcustom dired-open-functions '(dired-open-by-extension dired-open-subdir)
"List of functions to try to open a file.
Each function should accept no arguments and should retrieve the
filename and/or other context by itself. Each function should
return non-nil value if it succeeded in opening the file."
:type 'hook
:group 'dired-open)
(defcustom dired-open-find-file-function #'dired-find-file
"A function that will be used if none of the `dired-open-functions' succeeded."
:type 'function
:group 'dired-open)
(defcustom dired-open-extensions nil
"Alist of extensions mapping to a programs to run them in.
The filename is appended after the program."
:type '(alist
:key-type (string :tag "Extension")
:value-type (string :tag "Program"))
:group 'dired-open)
(defcustom dired-open-extensions-elisp nil
"Alist of extensions mapping to an elisp function to be called.
The filename is passed as the only argument to the function."
:type '(alist
:key-type (string :tag "Extension")
:value-type (function :tag "Function"))
:group 'dired-open)
(defcustom dired-open-use-nohup t
"If non-nil, use nohup to keep external processes alive.
See man page `nohup(1)'.
This only affects the built-in handlers."
:type 'boolean
:group 'dired-open)
(defcustom dired-open-query-before-exit t
"If non-nil, ask the user if they want to kill any external
processes started by `dired-open-file' when they exit Emacs.
This only affects the built-in handlers."
:type 'boolean
:group 'dired-open)
(defun dired-open--start-process (file command)
"Open FILE with COMMAND.
FILE is string, path to the file you want to open. It is
resolved with `file-truename'.
Note that FILE should not be \"shell escaped\", that is handled
by this function if the shell is invoked.
COMMAND is a string representing the command to run. If you want
to call it with any switches, these should be included in this
string as well."
(let ((process
(apply 'start-process "dired-open" nil
(if dired-open-use-nohup
(list "sh" "-c"
(concat
"nohup "
command
" "
(shell-quote-argument (file-truename file))
" 2>&1 >/dev/null"))
(append (split-string command " ")
(list (file-truename file)))))))
(when (and process
(not dired-open-query-before-exit))
(set-process-query-on-exit-flag process nil))
process))
;;; file opening procedures
(defun dired-open-xdg ()
"Try to run `xdg-open' to open the file under point."
(interactive)
(if (executable-find "xdg-open")
(let ((file (ignore-errors (dired-get-file-for-visit))))
(call-process-shell-command (concat "xdg-open '" (file-truename file) "'"))
nil)))
(defun dired-open-by-extension ()
"Open a file according to its extension.
The mappings from extensions to applications is specified by
`dired-open-extensions'."
(interactive)
(let ((file (ignore-errors (dired-get-file-for-visit)))
process)
(when (and file
(not (file-directory-p file)))
(--each-while dired-open-extensions (not process)
(when (string-match-p (concat "\\." (regexp-quote (car it)) "\\'") file)
(setq process (dired-open--start-process file (cdr it)))))
process)))
(defun dired-open-guess-shell-alist ()
"Open the file under point in an application suggested by
`dired-guess-shell-alist-user'."
(interactive)
(let ((file (ignore-errors (dired-get-file-for-visit)))
process)
(when (and file
(not (file-directory-p file)))
(--each-while dired-guess-shell-alist-user (not process)
(when (string-match-p (car it) file)
(setq process (dired-open--start-process file (eval (cadr it)))))))
process))
(defun dired-open-call-function-by-extension ()
"Call an elisp function on file according to its extension.
The mappings from extensions to applications is specified by
`dired-open-extensions-elisp'."
(interactive)
(-when-let (file (dired-utils-get-filename))
(when (not (file-directory-p file))
(--when-let (dired-utils-match-filename-extension file dired-open-extensions-elisp)
(funcall (cdr it) file)
it))))
;;; non-file opening procedures
(defun dired-open-subdir ()
"If point is on a subdir line, open the directory under point
in a new buffer.
For example, if the point is on line
/home/us|er/downloads
the directory /home/user is opened in new buffer."
(interactive)
(-when-let (subdir (dired-get-subdir))
(if (or (bolp) (eolp))
(find-file subdir)
(-when-let (end (save-excursion (re-search-forward "[/:]" (line-end-position) t)))
(let ((path (buffer-substring-no-properties
(+ 2 (line-beginning-position))
(1- end))))
(find-file path))))))
;;; main
;;;###autoload
(defun dired-open-file (&optional arg)
"Try `dired-open-functions' to open the thing under point.
That can be either file or any other line of dired listing.
If no function succeeded, run `dired-find-file' normally.
With \\[universal-argument], run `dired-find-file' normally."
(interactive "P")
(when (or arg
(not (run-hook-with-args-until-success 'dired-open-functions)))
(funcall dired-open-find-file-function)))
(define-key dired-mode-map [remap dired-find-file] 'dired-open-file)
(provide 'dired-open)
;;; dired-open.el ends here