Add links to nodes yet to be created without interrupting your writing flow

I am writing this simple guide to illustrate how three custom functions kindly contributed by @akashp are helping improve my personal writing flow. Hopefully, it can be a starting point for other readers to improve their own org-roam writing habits.

For reference, the functions development began with this thread, but the back and forth got messy at some point and the thread might feel unwelcoming to newcomers to say the least. So much so that the final discussions were held through DMs, and the code itself ended up on GitHub. This post is meant to centralize most relevant information from all those places and provide a use case for those functions.

We have three functions at our disposal; org-roam-link-find, org-roam-link-replace-all-backlinks, and org-roam-link-rename-all-backlinks. For easier writing, (and reading), I will refer to them respectively as the find, the replace, and the rename functions throught this post.

When we are writing, and this is especially true for creative or technical writing, we sometimes get ‘in the zone’; this special state when everything you want to express suddenly comes effortlessly, and your thoughts go faster than your fingers can actually type. For anyone who experiences this feeling, there is nothing more unpleasant than being interrupted and losing your train of thoughts.

A common interruption in my workflow is what I would call ‘future links’. When I write, I often use links to develop a concept I am using in a sentence in another note or section so I can keep my content clear and focused. Nothing special here, this is the concept of wikipedia links and org-roam-node-insert can handle this job perfectly.

The problem occurs when the node towards which you need a link does not exist yet. You have to stop, think about the right title, maybe tags, choose the right template, create the note, maybe add a task or a reminder to fill in that node later and so on. By the time you get back to the node you were originally working on, your train of thoughts has long left the station.

This is where the find function comes into play. Now I can simply add an empty roam: link using C-x C-l and keep writng till I am done. Later on, when I am finished writing, I can simply fire the find function and visit all those empty links one by one to create the missing nodes. I can even add a generic task to do it later, because the function will lead me directly to the right places, and the context surrounding my ‘future links’ should be more than enough to pick up where I left off.

This can also prove useful for anyone maintaining some habits to review their notes, since the function scans the whole database, you can be sure no empty or broken roam links will ever fall through the cracks.

The replace and rename function brings a significant improvment to the ‘future nodes’ creation process. This is an idea from @akashp that can make the experience less repetitive when you create multiple ‘future links’ pointing to the same ‘not exisiting yet node’ (or ‘future node’). In that case, it is now possible to make up some kind of placeholder name without putting too much toughts into it like future-links for example.

Instead of leaving your future roam: links empty, you can add a simple temporary placeholder name like the example above e.g. [[roam:future-links][some text]]. Now, when you come back to create your ‘future node’, instead of jumping to every single empty future link pointing to it using the find function, you can modify all of them simultaneously in two steps:

  • Create the ‘future node’ with the title future-links,
  • Call the replace function (with point on the new node).

A confirm prompt will then take you to every future link pointing to the current node to replace them with an id: link.

The rename function is a bonus feature allowing you to modify the node’s title, and propagate the change to all backlinks using the old title in their description. It does not exactly fit the use case described above, but can be very useful when maintaining strucutre notes, or using non semantic links e.g. ‘See Future Links’.

From our previous example, imagine you have a link [[id:xxxx][future-links]] pointing to a node called future-links. You can use the rename function with point on the node titled future-links to change its title. When prompted for a new title, input The concept of future links, and the link above will become [[id:xxxx][The concept of future links]].

I hope this code will benefit others the way it helped me improve my daily workflow. A big thanks again to @akashp for all the ideas, and all the elisp heavy lifting.

Happy roaming :+1:

2 Likes

Should ref this specific revision:
[org-roam-link-utils.el · GitHub](Revision n. 12)

I have expanded the utilities to replace the roam to id auto conversion altogether, but it is not needed for this simple case -

Best :+1:

Wanted to update one thing - in the org-roam-link-find we use pattern matching - @dmg has taught me not to use pattern matching when = will suffice -

since we do not have an index on the links.type column = and %% both will do a full table scan - but still just wanted to update that = will suffice and make the code more clean, no need to update if you dont feel like - just posting here if anyone who copies the code wants to make the change

In day to day operation this will provide no noticeable speed advantage - updating for idiomatic purposes (and because I have OCD)

namely - we make the substitution

(defun org-roam-link-find ()
  "Find \"roam:\" links in Org-Roam"
  (interactive)
  (let* ((link-type "roam")
	 (query "select
		 files.title, links.source, links.pos,
			      links.dest, links.properties
		 from links
		 inner join nodes on links.source = nodes.id
		 inner join files on nodes.file = files.file
		 where links.type like $s1")
	 (links (org-roam-db-query query (concat "%" link-type "%")))

to

(defun org-roam-link-find ()
  "Find \"roam:\" links in Org-Roam"
  (interactive)
  (let* ((link-type "roam")
	 (query "select
		 files.title, links.source, links.pos,
			      links.dest, links.properties
		 from links
		 inner join nodes on links.source = nodes.id
		 inner join files on nodes.file = files.file
		 where links.type = $s1")
	 (links (org-roam-db-query query link-type))

More clean and straight forward. Thanks.

1 Like

I did not have time to test yet, but I updated both the gist on github, and my config.

Thanks @akashp for keeping tabs on your contributions to improve the code. Very thorough, as always :+1: