Especially when doing document conversions, it’s pretty helpful to be able to view files in viewers other than Emacs. This library facilitates that.
Around version 9, Org mode began to support opening file links with external viewers via the org-file-apps
variable. The only documentation for this variable seems to be via describe-variable
.
org-file-apps
is an alist of special values and strings. One special value, system
, specifies an external opener, and the documentation suggests to “[u]se the system command for opening files, like ‘open’.” System-specific versions of this variable, such as org-file-apps-macos
, exist for major platforms. The system opener can be used as the default opener for file apps if an entry (t . system)
is defined in org-file-apps
, and the user can specify using the external opener on a per-use based by applying a double prefix argument to org-open-link
.
This behavior provides an alternative to the functionality provided with pg-open-register-org-link
. It does not provide an Emacs-wide alternative to opening files with an external viewer, such as that provided by pg-open-file
and pg-open-dir
.
Furthermore, the semantics of pg-open
and Org with respect to opening links with an external viewer are somewhat different. Org’s approach is to define system-wide defaults per file type, where pg-open
encourages the designation of specific links as being opened by Emacs or the system external viewer based on the URL schema/link type.
These methods may be used together. This may be useful when moving from one method to another and supporting legacy link types. It may also be helpful as a way to override an extension-based setting in org-file-apps
. In most cases, supporting both methods is likely to be unnecessarily complicated.
;;; pg-open.el --- Open files with the system viewer
;; Copyright (C) 2021 Phil Groce
;; Author: Phil Groce <[email protected]>
;; Version: 0.6.9
;; Keywords: shell
(require 'dired)
All major operating systems have facilities for opening files with registered applications. pg-open-file
and pg-open-dir
efficiently select a file in Emacs and open it with the system facility.
(defun pg-open-opener ()
"Return the general-purpose application opener on this system."
(cond
((eq system-type 'darwin) "open")
((or (eq system-type 'windows-nt)
(eq system-type 'cygwin)) "explorer")
;; Assume some kind of unix system
(t "xdg-open")))
(defun pg-open-thing (thing)
(shell-command (format "%s \"%s\"" (pg-open-opener) thing)))
;;;###autoload
(defun pg-open-dir (dir)
"Open a directory in the default handler of the current window
system, if one is defined."
(interactive "DOpen directory (external): ")
(pg-open-thing dir))
;;;###autoload
(defun pg-open-file (f)
"Open a file in the default handler of the current window
system, if one is defined."
(interactive "fOpen file (external): ")
(pg-open-thing f))
;;;###autoload
(defun pg-open-file-dired ()
"Open a file in dired mode using the default handler of the
current window system, if one is defined."
(interactive)
(let ((file-list (dired-get-marked-files)))
(dired-do-shell-command (pg-open-opener) nil file-list)))
By default, Org mode opens file URL’s (i.e., those with the file
URL type, like file:/usr/local/share/doc/emacs/index.html
) in Emacs. However, Org mode also permits user definition of additional URL types, such that the user can control what happens when clicking on org-mode links with the defined URL type. Using this, we can define a URL type for files that uses pg-open-file
.
Org mode requires two functions for this purpose: An function for opening the URL, and a function for doing completion when creating the URL. pg-open-file
works as an opener; the following can be used as a completion function.
pg-open
provides a function to do all the work of associating pg-open
with an org-link: pg-open-register-org-link
. With no arguments, this argument registers the extfile
type to represent files that should be opened externally to Emacs. pg-open-register-org-link
takes an argument that will override this value if desired.
(defvar pg-open--org-url-scheme "extfile"
"URL scheme registered to handle files with `pg-open'. Used by
`pg-open--link-complete-fn' to build the org-link, and by
`pg-open-register-org-link' to register the URL type.
This variable is only used internally by those functions;
changing it is not advised. To change the link type used, call
`pg-open-register-org-link' with a different name for the link
type.")
(defun pg-open--link-complete-fn (&optional arg)
"Create an externally-opened file link using completion."
;; (This is just org-link-complete-file with the serial numbers
;; filed off.)
(let ((url-scheme (concat pg-open--org-url-scheme ":"))
(file (read-file-name "File: "))
(pwd (file-name-as-directory (expand-file-name ".")))
(pwd1 (file-name-as-directory (abbreviate-file-name
(expand-file-name ".")))))
(cond ((equal arg '(16))
(concat url-scheme
(abbreviate-file-name (expand-file-name file))))
((string-match
(concat "^" (regexp-quote pwd1) "\\(.+\\)") file)
(concat url-scheme (match-string 1 file)))
((string-match
(concat "^" (regexp-quote pwd) "\\(.+\\)")
(expand-file-name file))
(concat url-scheme
(match-string 1 (expand-file-name file))))
(t (concat url-scheme file)))))
(declare-function org-link-set-parameters "ol")
(defun pg-open-register-org-link (&optional link-type)
"Define an org link type (i.e., URL scheme) similar to file:,
that uses `pg-open-file' to open the file instead of Emacs's
default facility. This scheme is useful for files like PDF files
that Emacs can read, but that the user may prefer to read with an
external application instead.
LINK-TYPE optionally specifies the name of the link type to be
used. By default, this function will use the type \"extfile\".
This function requires that the org function
`org-link-set-parameters' be defined. This can be ensured by
loading `org-mode' before running this function."
(when link-type
(setq pg-open--org-url-scheme link-type))
(org-link-set-parameters pg-open--org-url-scheme
:follow #'pg-open-file
:complete #'pg-open--link-complete-fn))
(provide 'pg-open)
;;; pg-open.el ends here