3

Is there a method to link files without specifying their absolute file name nor their relative file name; just the non-directory part of their file name? I remember stumbling into one when browsing the other day that was defined as a custom link type that maintains some sort of data structure that users can append to and that it is used to resolve links but the name escapes me.

so for example, to refer to a file located ../../blob.txt, all I have to do is [[LINKTYPE:blob.txt]] and this works across files. I would expect I need to add the absolute file name to some list/data structure or some other resolving mechanism.

8
  • In Emacs jargon we do not use "path" the way you used it; hence the edit (and rollback). See the Emacs manual Glossary - see the glossary entry for "File Name", where you see this all explained.
    – Drew
    Nov 13, 2019 at 1:39
  • @Drew I see, I wasn't aware of the different terminology. I already know how to link with just relative file name, it is described in here. What I am looking for is the "file name", or in Emacs terminology the "non-directory part of the file name". Nov 13, 2019 at 2:42
  • You can set the :follow property of a customized link type with org-link-set-parameters. The :follow property is a function that takes the link as argument when you click on it.
    – Tobias
    Nov 13, 2019 at 3:36
  • The nondirectory part of an absolute file name is the relative file name. You can obtain it using function file-name-nondirectory.
    – Drew
    Nov 13, 2019 at 4:54
  • @drew I believe that is not correct. as per this, relative file name is no different than relative path. for example "../../file.org" is a relative file name, while the associated non-directory part of the file name is file.org. you are right though that file-name-nondirectory returns the non directory part of a file name. file-relative-name returns the relative file name. note that file-realtive-name takes two arguments as it is relative. Nov 13, 2019 at 5:42

2 Answers 2

2

You can use org-link-set-parameters to define your own link types as demonstrated in the following Elisp snippet.

The snippet contains a function my-org-open-file which opens the file given as org file link target. If the file name is relative and if the file with that name does not exist my-org-open-file tries to find an existing file by expanding the file name successively with all entries of the path list my-org-path.

Note that you can customize my-org-path. Just call M-x customize-option RET my-org-path RET and insert the paths you need.

If my-org-open-file does not find the file at all a buffer is generated with the original file name as buffer-file-name.

(defcustom my-org-path nil
  "List of paths to try with `my-org-open-file'.
If the LINK argument for `my-org-open-file' is a relative path
and a file with that path does not exist
try successively each of the directory entries of this list
as `default-directory' for expanding the path.
The directory entries can be given relative or absolute."
  :type '(repeat directory)
  :group 'org-link-follow)

(defun my-org-open-file (link)
  "Open LINK with `org-open-file'.
If LINK is given as relative file path also try to find file
with prefixes from `my-org-path'."
  (let* ((el (car (org-element-parse-secondary-string (format "[[file:%s]]" link) '(link))))
     (option (org-element-property :search-option el))
     (path (org-element-property :path el))
     (app (org-element-property :application el))
     (org-path (org-property-or-variable-value 'my-org-path t)))
    (when (stringp org-path)
      (setq org-path (list org-path)))
    (setq path (substitute-in-file-name path))
    (unless (file-name-absolute-p path)
      (cl-dolist (path-prefix org-path)
    (let ((try-path (expand-file-name path path-prefix)))
      (when (file-exists-p try-path)
        (setq path try-path)
        (cl-return)))))
    (apply #'org-open-file
       path
       (cond ((equal app "emacs") 'emacs)
         ((equal app "sys") 'system))
       (cond ((not option) nil)
         ((string-match-p "\\`[0-9]+\\'" option)
          (list (string-to-number option)))
         (t (list nil option))))))

(org-link-set-parameters
 "myfile"
 :follow #'my-org-open-file
 :complete #'org-file-complete-link)

You can set the search path also in the properties drawer of an Org section as demonstrated in the following example:

* My Section
:PROPERTIES:
:org-search-path: ("~/tmp" "/tmp" "/temp")
:END:
1
  • thanks. work like a charm. Nov 21, 2019 at 2:22
1

Late to the party, but I've just been wrestling with this myself. Just to be clear -- because the various edits and unedits to your question were a bit confusing -- I think what you were asking was, using well-established and widely-understood unix terminology:

Is there a way to link to a file by specifying only its basename; i.e. without having to also provide its dirname.

For example:

Is there a way to link to file /pugh/pugh/barney/mcgrew/blob.txt by specifying only blob.txt; i.e. without having to also provide, /pugh/pugh/barney/mcgrew

If that is what you were asking, then you can achieve a form of that by using Org's attachment: style of link. So where, in your question, you had:

[[LINKTYPE:blob.txt]]

you'd actually have:

[[attachment:blob.txt]]

Then when you did C-c C-o ('org-open-at-point') over that, Org would Just Figure Out what the "path" was to get to blob.txt.

The mechanism is that of org-attach, and specifically, org-attach using the ID method (as opposed to the DIR method). Using org-attach like that results in the following:

  1. An ID property is created for the heading from which you want to link to your file
  2. Then, based on that ID, an attachment directory is created
  3. And then your file -- or a copy of, or link to, it -- is placed into that newly-created attachment directory
  4. Finally, an attachment:-style link can be added to variable org-stored-links-p. You can then use org-insert-link, to insert something like [[attachment:blob.txt][blob.txt]] into the body of the heading in which you are working.

That's just a summary of how it works. It's slightly more complex in that there are a few other things that need to be in place to get it to work just as I've described. On the other hand, in practice it can be even simpler than I've described in that you don't even need step #4 if you don't want it. Org can figure out where the attached file is without that [[attachment:blob.txt]] link at all. It can actually get to and open your "attached" file just from the heading itself.

Now, cool and funky though all that is, the (potential) problem, given your specific question, is that although with this mechanism you get to leave it to Org to figure out where the linked file is located, the intention is that you also leave it to Org to decide where that location is in the first place. So you can't easily (although I suspect it's possible) do exactly what you have in your example, and just link to something like: ../../blob.txt. Instead, you'd find that you were linking to something like: ./data/20/221108T085858.791822/blob.txt. Of course it is usually the case that, as discussed, we wouldn't really care about that scary-looking filename, because we would be referring to that file as, at most, something like [[attachment:blob.txt]] or, even better, not at all and just relying instead on the implicit linkage between your file and the Org heading in question. But that is usually the case. Whether it applies in your specific situation, I can't tell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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