Motivation
Previously, on the Emacs stack exchange:
In Org mode, when I open a link (C-c C-o) [...] [that] contains a wildcard, such as
file:3_o*.pdf
, Emacs opens it throughdired
instead [...] within Emacs.I would instead have it open the first match of that pattern in the system application …
The handling of file:
links is hard-coded in Org mode. If there is any wildcard in the given path
, then org-link-open
will nope out of the situation and instead call dired
. And while one could change Org mode's source code in the local elisp files, I remembered that you can change the arguments or results of functions with advices.
Solution
I opted for an advice :around
org-link-open
with the following code:
(defun my-org-expand-file-link (link)
"Expand a pattern in LINK to the first matching file.
Returns the original LINK if no file matches the pattern in LINK or if it is not
a link of type 'file:'. See info node `(org)External links' for more information."
(let ((type (org-element-property :type link))
(path (org-element-property :path link)))
(cond
((equal type "file")
(let ((candidates (file-expand-wildcards path)))
(if candidates
(org-element-put-property (org-element-copy link) :path (car candidates))
link)))
(t link))))
(defun my-org-link-open-advice (orig-fun link &rest args)
"Advice for ORIG-FUN that expands patterns in 'file:' LINKs.
See `my-org-expand-file-link' for more information.
Optional argument ARGS are passed as-is."
(apply orig-fun (my-org-expand-file-link link) args))
(advice-add 'org-link-open :around #'my-org-link-open-advice)
So whenever you open a link with C-c C-o (org-open-at-point
which calls org-link-open
), you end up with a concrete file.
Some remarks
I recommend using a custom link type like first:
or wrapping the code above minor mode, as the advice is somewhat invasive and might surprise users that really want to get a list of matching files instead.
I'm open to any feedback on the elisp code, especially since I'm still a elisp newbie.