beginner – WOMOIWIW: What Org-mode Opens is What I Want


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.

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.

link expansion

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.