blob: 0fe6be642ab9d1139e0304e02e175330d80580d9 (
plain)
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
|
;;; external-open.el --- Open Files Using Default OS Handler -*- lexical-binding: t; coding: utf-8; -*-
;; author Craig Jennings <c@cjennings.net>
;;
;;; Commentary:
;;
;; This library provides a simple mechanism for opening files with specific
;; extensions using your operating system’s default application rather than
;; visiting them in an Emacs buffer. It offers:
;;
;; • A simple method to run a command on the current buffer's file
;; "C-c x o" bound to cj/open-this-file-with
;; • A customizable list =default-open-extensions= of file‐type suffixes
;; (e.g. “pdf”, “docx”, “png”) that should be handled externally.
;; • A function =default-open-file= (and its helper commands) which will
;; launch the matching file in the OS’s default MIME handler.
;; • Integration with =find-file-hook= so that any file whose extension
;; appears in =default-open-extensions= is automatically opened externally
;; upon visit.
;; • Optional interactive commands for manually invoking an external open on
;; point or on a user-chosen file.
;;
;;; Code:
(require 'system-utils) ;; for xdg-open and others
(require 'host-environment) ;; environment information functions
(require 'cl-lib)
(defgroup external-open nil
"Open certain files with the OS default handler."
:group 'files)
(defcustom default-open-extensions
'(
;; Video
"\\.3g2\\'" "\\.3gp\\'" "\\.asf\\'" "\\.avi\\'" "\\.divx\\'" "\\.dv\\'"
"\\.f4v\\'" "\\.flv\\'" "\\.m1v\\'" "\\.m2ts\\'" "\\.m2v\\'" "\\.m4v\\'"
"\\.mkv\\'" "\\.mov\\'" "\\.mpe\\'" "\\.mpeg\\'" "\\.mpg\\'" "\\.mp4\\'"
"\\.mts\\'" "\\.ogv\\'" "\\.rm\\'" "\\.rmvb\\'" "\\.ts\\'" "\\.vob\\'"
"\\.webm\\'" "\\.wmv\\'"
;; Audio
"\\.aac\\'" "\\.ac3\\'" "\\.aif\\'" "\\.aifc\\'" "\\.aiff\\'"
"\\.alac\\'" "\\.amr\\'" "\\.ape\\'" "\\.caf\\'"
"\\.dff\\'" "\\.dsf\\'" "\\.flac\\'" "\\.m4a\\'" "\\.mka\\'"
"\\.mid\\'" "\\.midi\\'" "\\.mp2\\'" "\\.mp3\\'" "\\.oga\\'"
"\\.ogg\\'" "\\.opus\\'" "\\.ra\\'" "\\.spx\\'" "\\.wav\\'"
"\\.wave\\'" "\\.weba\\'" "\\.wma\\'"
;; Microsoft Word
"\\.docx?\\'" "\\.docm\\'"
"\\.dotx?\\'" "\\.dotm\\'"
"\\.rtf\\'"
;; Microsoft Excel
"\\.xlsx?\\'" "\\.xlsm\\'" "\\.xlsb\\'"
"\\.xltx?\\'" "\\.xltm\\'"
;; Microsoft PowerPoint
"\\.pptx?\\'" "\\.pptm\\'"
"\\.ppsx?\\'" "\\.ppsm\\'"
"\\.potx?\\'" "\\.potm\\'"
;; Microsoft OneNote / Visio / Project / Access / Publisher
"\\.one\\'" "\\.onepkg\\'" "\\.onetoc2\\'"
"\\.vsdx?\\'" "\\.vsdm\\'" "\\.vstx?\\'" "\\.vstm\\'" "\\.vssx?\\'" "\\.vssm\\'"
"\\.mpp\\'" "\\.mpt\\'"
"\\.mdb\\'" "\\.accdb\\'" "\\.accde\\'" "\\.accdr\\'" "\\.accdt\\'"
"\\.pub\\'"
;; OpenDocument (LibreOffice/OpenOffice)
"\\.odt\\'" "\\.ott\\'"
"\\.ods\\'" "\\.ots\\'"
"\\.odp\\'" "\\.otp\\'"
"\\.odg\\'" "\\.otg\\'"
"\\.odm\\'" "\\.odf\\'"
;; Flat OpenDocument variants
"\\.fodt\\'" "\\.fods\\'" "\\.fodp\\'"
;; Apple iWork
"\\.pages\\'" "\\.numbers\\'" "\\.key\\'"
;; Microsoft’s fixed-layout formats
"\\.xps\\'" "\\.oxps\\'"
)
"Regexps matching file extensions that should be opened externally."
:type '(repeat (regexp :tag "File extension regexp"))
:group 'external-open)
;; ------------------------------- Open File With ------------------------------
;; TASK: Add this to buffer custom functions
(defun cj/open-this-file-with (command)
"Open this buffer's file with COMMAND, detached from Emacs."
(interactive "MOpen with program: ")
(unless buffer-file-name
(user-error "Current buffer is not visiting a file"))
(let ((file (expand-file-name buffer-file-name)))
(cond
;; Windows: launch via ShellExecute so the child isn't tied to Emacs.
((env-windows-p)
(w32-shell-execute "open" command (format "\"%s\"" file)))
;; POSIX: disown with nohup + background. No child remains.
(t
(call-process-shell-command
(format "nohup %s %s >/dev/null 2>&1 &"
command (shell-quote-argument file))
nil 0)))))
(global-set-key (kbd "C-c x o") #'cj/open-this-file-with)
;; -------------------- Open Files With Default File Handler -------------------
(defun cj/find-file-auto (orig-fun &rest args)
"If file has an extension in `default-open-extensions', open externally.
Else call ORIG-FUN with ARGS."
(let* ((file (car args))
(case-fold-search t))
(if (and (stringp file)
(cl-some (lambda (re) (string-match-p re file))
default-open-extensions))
(cj/xdg-open file)
(apply orig-fun args))))
;; Make advice idempotent if you reevaluate this form.
(advice-remove 'find-file #'cj/find-file-auto)
(advice-add 'find-file :around #'cj/find-file-auto)
(provide 'external-open)
;;; external-open.el ends here.
|