Suppose we have an org file that looks something like this:
* Heading 1
* Sub-heading
These are some notes
Bleep blop blerf
And we would like to refile the sub-heading, but include a link to it in the original location. The intended outcome is something that looks like this:
* Heading 1
* [[Link to sub-heading's new location][Sub-heading]]
* New Heading
* A different sub-heading that isn't the one we refiled
* Sub-heading
These are some notes
Bleep blob blerf
This can be accomplished in a fairly straightforward way by using org ids when generating links and setting up an advice function to run before the refile process. In order to use org ids when generating org links, the variable org-id-link-to-use-id
must be set to true.
The advice function and setup looks like so:
(defun org-refile--insert-link ( &rest _ )
(org-back-to-heading)
(let* ((refile-region-marker (point-marker))
(source-link (org-store-link nil)))
(org-insert-heading)
(insert source-link)
(goto-char refile-region-marker)))
(advice-add 'org-refile
:before
#'org-refile--insert-link)
The function org-refile--insert-link
will be passed the same arguments that will be passed to org-refile
so we accept them and ignore them. First we move the pointer to the start of the current heading with org-back-to-heading
, and we save the marker for the current point and the link for the current heading. This link will use the id of the heading (or create an id if one doesn’t exist yet), so the link will work no matter where we refile it to. This is particularly useful, because later if we refile the heading again this link will still point to it in it’s newer location. Next, we insert a new heading (see note at end about this), and insert the link that we stored. Finally, go back to the saved pointer location. org-refile
will be called immediately after this function finishes, and returning the pointer to the original location ensures that we refile the correct subtree.
The advice block really just states that before org-refile
is run, we should run org-refile--insert-link
. Advice functions apply globally. I don’t know much about it, I used the elisp advice documentation to figure it out.
Note regarding org-insert-heading
: My setup automatically respects content when I call org-insert-heading
, so this works for me. In other situations, it may be necessary to call org-insert-heading-respect-content
or org-insert-heading-after-current
.
The above advice works great for me, except that it runs all of the time. There is one particular situation where I don’t want to leave a link behind: when I’m refiling from my inbox file. The easiest way that I could think of to circumvent this one use case was by wrapping the bulk of the function in an unless
clause, which checked the buffer-file-name
variable to see if it ended with the value inbox.org
(checking only the string suffix is good for me because I always name the file inbox.org but it is located in different places on different machines).
The updated function looks like so:
(defun org-refile--insert-link ( &rest _ )
(unless (string-suffix-p "inbox.org" buffer-file-name)
(org-back-to-heading)
(let* ((refile-region-marker (point-marker))
(source-link (org-store-link nil)))
(org-insert-heading)
(insert source-link)
(goto-char refile-region-marker))))
hi! I think
org-id-link-to-use-id
is namedorg-id-link-to-org-use-id
now (i'm running 9.3.7 at the moment).When i update that, i get a curious situation where what actually happens on refile is kind of the inverse of what you describe here: a backlink to the original subtree location is created at the refile point, not the other way around.
Have you encountered this?