# 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 through dired 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."
(cond
((equal type "file")
(let ((candidates (file-expand-wildcards path)))
(if candidates
(org-element-put-property (org-element-copy link) :path (car candidates))

Optional argument ARGS are passed as-is."



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.

That's a cool feature I've to say. And the code looks excellent, I suppose you could argue for one or the other wrt. cond vs. if etc. but that's pointless pedantry IMO.

Given that it works flawlessly (tried locally and via tramp as well), I was unsure there was actually anything to say here, but I found two points that might be of interest:

Firstly, while the :around combinator works fine, it might be more appropriate to use :filter-args, since it's really just filtering the arguments:

(defun my-org-link-open-advice (args)
See my-org-expand-file-link' for more information.
Optional argument ARGS are passed as-is."
(list* (my-org-expand-file-link (first args)) (rest args)))



The functionality is absolutely unchanged of course.

Secondly, and this is conflicting with the previous point, I could imagine using the prefix argument (so C-u C-c C-o) to distinguish the two cases of whether to run the original behaviour, or not, like so:

(defun my-org-link-open-advice (orig-fun link &optional arg &rest args)
Optional argument ARGS are passed as-is."

To quote the function docstring for org-link-open:
• Thanks for the review. TIL about the aliases first and rest for car and cdr!
• Right, I actually thought for teaching purposes to use first and rest, normally I go for the shorter ones :) Mar 12 at 20:06