2

In Org mode, when I open a link (C-c C-o) to a file such as file:something.pdf, the file is opened with my system PDF viewer. However, if the link contains a wildcard, such as file:3_o*.pdf, Emacs opens it through dired instead (also mentioned in https://emacs.stackexchange.com/a/51365), and selecting the pdf opens it within Emacs.

I would instead have it open the first match of that pattern in the system application - and don't highlight it in red. I'd also be fine with a custom link type if this can't be done otherwise.

2

Emacs lisp newbie here. As far as I know there are two ways to change this behaviour without changing Org's source code: either a custom link type, or an advice around org-link-open (see (elisp) Advising Functions).

Custom link type

I've opted for the former, as the latter changed between Org versions (you need an advice around org-link-open in Org 9.3.6 an and advice around org-open-at-point in 9.1.9):

(require 'org)

(org-add-link-type "first" 'org-first-open)

(defun org-first-open (pattern)
  "Open the first file that matches PATTERN.
PATTERN should match at least one file."
  (let ((candidates (file-expand-wildcards pattern)))
    (if candidates (org-open-file (car candidates))
      (error "No file matches \"%s\"" pattern))))

You can now use first:3_o*.pdf to get the first result of the wildcard expansion.

The manual has some more information on custom types, for example customization options. Note that the current guide uses plists to define the link type via org-add-link-type, but that's a recent(?) addition. I opted for the earlier, still valid syntax from 9.1.

Advice for org-link-open

This advice was tested in Org version 9.3.6 and 9.4-19. It will definitely not work in 9.1.9. Other versions are untested.

(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)
| improve this answer | |
  • Thanks! Actually, I'd love to hear about the advice version (simply for the latest org version) - that way, the links would still be usable without my config, just not as convenient. – Xerus Oct 16 at 19:25
  • @Xerus err, let me try to recreate that. That was my first solution, but it didn't work on the old (org-version). Might take some time, as I need to update Emacs on this machine first. – Zeta Oct 16 at 20:05
  • @Xerus added the (advice-add ...) variant. Keep in mind that this is probably the third time I wrote emacs lisp that's not part of a (use-package) in my configuration, so it's probably not idiomatic elisp. I'm especially not sure about whether org-element-copy is actually necessary. – Zeta Oct 16 at 20:34
  • 1
    Cross-stack-exchange note: I've asked for a review of the code above on CR: codereview.stackexchange.com/q/250783/21002 – Zeta Oct 17 at 9:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.