I’ve tried to implement a way to take into account files with multiple nodes.
Is it something like this?
An org file:
:PROPERTIES:
:ID: 114b9147-47c4-4008-9cba-fcb8338f2e98
:END:
#+title: Org-Roam test
Testing stuff with org-roam.
It contains both [[id:foo][Foo]] and [[id:bar][Bar]].
* Foo
:PROPERTIES:
:ID: foo
:END:
Part of [[id:bar][Bar]].
* Bar
:PROPERTIES:
:ID: bar
:END:
Part of [[id:foo][Foo]]
(defun collect-backlinks-string (backend)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
;; Sort the nodes by the point to avoid errors when inserting the
;; references
(nodes-in-file (--sort (< (org-roam-node-point it)
(org-roam-node-point other))
(-filter (lambda (node)
(s-equals?
(org-roam-node-file node)
source-file))
(org-roam-node-list))))
;; Nodes don't store the last position so, get the next node position
;; and subtract one character
(nodes-start-position (-map (lambda (node) (org-roam-node-point node))
nodes-in-file))
(nodes-end-position (-concat (-map (lambda (next-node-position)
(- next-node-position 1))
(-drop 1 nodes-start-position))
(list (point-max))))
;; Keep track of the current-node index
(current-node 0)
;; Keep track of the amount of text added
(character-count 0))
(dolist (node nodes-in-file)
(when (org-roam-backlinks-get node)
;; Go to the end of the node and don't forget about previously inserted
;; text
(goto-char (+ (nth current-node nodes-end-position) character-count))
;; Add the references as a subtree of the node
(setq heading (format "\n\n%s References\n\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
;; Also count the new lines (4)
(setq character-count (+ 4 character-count (string-width heading)))
(insert heading)
(dolist (backlink (org-roam-backlinks-get node))
(let* ((source-node (org-roam-backlink-source-node backlink))
(point (org-roam-backlink-point backlink))
(text (-last-item (org-roam-node-preview
(org-roam-node-file source-node)
point)))
(references (format "- [[./%s][%s]]: %s\n\n"
(file-relative-name (org-roam-node-file source-node))
(org-roam-node-title source-node)
text)))
;; Also count the new lines (2)
(setq character-count (+ 2 character-count (string-width references)))
(insert references))))
(setq current-node (+ current-node 1)))))
Edit: I forgot to add (when (org-roam-node-at-point) so here it is the code again:
Edit 2: You need both dash.el and s.el packages.
(defun collect-backlinks-string (backend)
(when (org-roam-node-at-point)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
;; Sort the nodes by the point to avoid errors when inserting the
;; references
(nodes-in-file (--sort (< (org-roam-node-point it)
(org-roam-node-point other))
(-filter (lambda (node)
(s-equals?
(org-roam-node-file node)
source-file))
(org-roam-node-list))))
;; Nodes don't store the last position so, get the next node position
;; and subtract one character
(nodes-start-position (-map (lambda (node) (org-roam-node-point node))
nodes-in-file))
(nodes-end-position (-concat (-map (lambda (next-node-position)
(- next-node-position 1))
(-drop 1 nodes-start-position))
(list (point-max))))
;; Keep track of the current-node index
(current-node 0)
;; Keep track of the amount of text added
(character-count 0))
(dolist (node nodes-in-file)
(when (org-roam-backlinks-get node)
;; Go to the end of the node and don't forget about previously inserted
;; text
(goto-char (+ (nth current-node nodes-end-position) character-count))
;; Add the references as a subtree of the node
(setq heading (format "\n\n%s References\n\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
;; Also count the new lines (4)
(setq character-count (+ 4 character-count (string-width heading)))
(insert heading)
(dolist (backlink (org-roam-backlinks-get node))
(let* ((source-node (org-roam-backlink-source-node backlink))
(point (org-roam-backlink-point backlink))
(text (-last-item (org-roam-node-preview
(org-roam-node-file source-node)
point)))
(references (format "- [[./%s][%s]]: %s\n\n"
(file-relative-name (org-roam-node-file source-node))
(org-roam-node-title source-node)
text)))
;; Also count the new lines (2)
(setq character-count (+ 2 character-count (string-width references)))
(insert references))))
(setq current-node (+ current-node 1))))))
Yes, the name changed and now it doesn’t return a list, just text. I guess it is fixed now:
(defun collect-backlinks-string (backend)
(when (org-roam-node-at-point)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
;; Sort the nodes by the point to avoid errors when inserting the
;; references
(nodes-in-file (--sort (< (org-roam-node-point it)
(org-roam-node-point other))
(-filter (lambda (node)
(s-equals?
(org-roam-node-file node)
source-file))
(org-roam-node-list))))
;; Nodes don't store the last position so, get the next node position
;; and subtract one character
(nodes-start-position (-map (lambda (node) (org-roam-node-point node))
nodes-in-file))
(nodes-end-position (-concat (-map (lambda (next-node-position)
(- next-node-position 1))
(-drop 1 nodes-start-position))
(list (point-max))))
;; Keep track of the current-node index
(current-node 0)
;; Keep track of the amount of text added
(character-count 0))
(dolist (node nodes-in-file)
(when (org-roam-backlinks-get node)
;; Go to the end of the node and don't forget about previously inserted
;; text
(goto-char (+ (nth current-node nodes-end-position) character-count))
;; Add the references as a subtree of the node
(setq heading (format "\n\n%s References\n\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
;; Also count the new lines (4)
(setq character-count (+ 4 character-count (string-width heading)))
(insert heading)
(dolist (backlink (org-roam-backlinks-get node))
(let* ((source-node (org-roam-backlink-source-node backlink))
(point (org-roam-backlink-point backlink))
(text (org-roam-get-preview
(org-roam-node-file source-node)
point))
(references (format "- [[./%s][%s]]: %s\n\n"
(file-relative-name (org-roam-node-file source-node))
(org-roam-node-title source-node)
text)))
;; Also count the new lines (2)
(setq character-count (+ 2 character-count (string-width references)))
(insert references))))
(setq current-node (+ current-node 1))))))
Changed (setq character-count (+ 4 to (setq character-count (+ 3 because now it only has 3 new lines.
(defun collect-backlinks-string (backend)
(when (org-roam-node-at-point)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
;; Sort the nodes by the point to avoid errors when inserting the
;; references
(nodes-in-file (--sort (< (org-roam-node-point it)
(org-roam-node-point other))
(-filter (lambda (node)
(s-equals?
(org-roam-node-file node)
source-file))
(org-roam-node-list))))
;; Nodes don't store the last position so, get the next node position
;; and subtract one character
(nodes-start-position (-map (lambda (node) (org-roam-node-point node))
nodes-in-file))
(nodes-end-position (-concat (-map (lambda (next-node-position)
(- next-node-position 1))
(-drop 1 nodes-start-position))
(list (point-max))))
;; Keep track of the current-node index
(current-node 0)
;; Keep track of the amount of text added
(character-count 0))
(dolist (node nodes-in-file)
(when (org-roam-backlinks-get node)
;; Go to the end of the node and don't forget about previously inserted
;; text
(goto-char (+ (nth current-node nodes-end-position) character-count))
;; Add the references as a subtree of the node
(setq heading (format "\n\n%s References\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
;; Count the characters and count the new lines (4)
(setq character-count (+ 3 character-count (string-width heading)))
(insert heading)
;; Insert properties drawer
(setq properties-drawer ":PROPERTIES:\n:HTML_CONTAINER_CLASS: references\n:END:\n")
;; Count the characters and count the new lines (3)
(setq character-count (+ 3 character-count (string-width properties-drawer)))
(insert properties-drawer)
(dolist (backlink (org-roam-backlinks-get node))
(let* ((source-node (org-roam-backlink-source-node backlink))
(point (org-roam-backlink-point backlink))
(text (org-roam-get-preview
(org-roam-node-file source-node)
point))
(references (format "- [[./%s][%s]]: %s\n\n"
(file-relative-name (org-roam-node-file source-node))
(org-roam-node-title source-node)
text)))
;; Also count the new lines (2)
(setq character-count (+ 2 character-count (string-width references)))
(insert references))))
(setq current-node (+ current-node 1))))))
The function changed. Now it’s called: org-roam-preview-get-contents
I’ve changed the code accordingly and removed new lines from preview, to avoid creating new paragraphs.
(defun collect-backlinks-string (backend)
(when (org-roam-node-at-point)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
;; Sort the nodes by the point to avoid errors when inserting the
;; references
(nodes-in-file (--sort (< (org-roam-node-point it)
(org-roam-node-point other))
(-filter (lambda (node)
(s-equals?
(org-roam-node-file node)
source-file))
(org-roam-node-list))))
;; Nodes don't store the last position so, get the next node position
;; and subtract one character
(nodes-start-position (-map (lambda (node) (org-roam-node-point node))
nodes-in-file))
(nodes-end-position (-concat (-map (lambda (next-node-position)
(- next-node-position 1))
(-drop 1 nodes-start-position))
(list (point-max))))
;; Keep track of the current-node index
(current-node 0)
;; Keep track of the amount of text added
(character-count 0))
(dolist (node nodes-in-file)
(when (org-roam-backlinks-get node)
;; Go to the end of the node and don't forget about previously inserted
;; text
(goto-char (+ (nth current-node nodes-end-position) character-count))
;; Add the references as a subtree of the node
(setq heading (format "\n\n%s References\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
;; Count the characters and count the new lines (4)
(setq character-count (+ 3 character-count (string-width heading)))
(insert heading)
;; Insert properties drawer
(setq properties-drawer ":PROPERTIES:\n:HTML_CONTAINER_CLASS: references\n:END:\n")
;; Count the characters and count the new lines (3)
(setq character-count (+ 3 character-count (string-width properties-drawer)))
(insert properties-drawer)
(dolist (backlink (org-roam-backlinks-get node))
(let* ((source-node (org-roam-backlink-source-node backlink))
(point (org-roam-backlink-point backlink))
(text (s-replace "\n" " " (org-roam-preview-get-contents
(org-roam-node-file source-node)
point)))
(references (format "- [[./%s][%s]]: %s\n\n"
(file-relative-name (org-roam-node-file source-node))
(org-roam-node-title source-node)
text)))
;; Also count the new lines (2)
(setq character-count (+ 2 character-count (string-width references)))
(insert references))))
(setq current-node (+ current-node 1))))))
Hey, this function is nice, thanks! I’m using it on my website.
I’ve fixed a few bugs in the way it works below. Namely, a node’s end position isn’t necessarily the next node, but rather the next same-level headline (if there is one.) Also, I changed it to sort in decreasing order of the node’s end position (which removes the need for manually counting characters entirely,) and then insert backlinks in that order.
A couple other tweaks I added:
the outline path, as is displayed in the org-roam-buffer. You can take this out if desired.
backlinks link directly to the node ID, rather than to the source file.
(defun collect-backlinks-string (backend)
(when (org-roam-node-at-point)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
(nodes-in-file (--filter (s-equals? (org-roam-node-file it) source-file)
(org-roam-node-list)))
(nodes-start-position (-map 'org-roam-node-point nodes-in-file))
;; Nodes don't store the last position, so get the next headline position
;; and subtract one character (or, if no next headline, get point-max)
(nodes-end-position (-map (lambda (nodes-start-position)
(goto-char nodes-start-position)
(if (org-before-first-heading-p) ;; file node
(point-max)
(call-interactively
'org-forward-heading-same-level)
(if (> (point) nodes-start-position)
(- (point) 1) ;; successfully found next
(point-max)))) ;; there was no next
nodes-start-position))
;; sort in order of decreasing end position
(nodes-in-file-sorted (->> (-zip nodes-in-file nodes-end-position)
(--sort (> (cdr it) (cdr other))))))
(dolist (node-and-end nodes-in-file-sorted)
(-let (((node . end-position) node-and-end))
(when (org-roam-backlinks-get node)
(goto-char end-position)
;; Add the references as a subtree of the node
(setq heading (format "\n\n%s References\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
(insert heading)
(setq properties-drawer ":PROPERTIES:\n:HTML_CONTAINER_CLASS: references\n:END:\n")
(insert properties-drawer)
(dolist (backlink (org-roam-backlinks-get node))
(let* ((source-node (org-roam-backlink-source-node backlink))
(properties (org-roam-backlink-properties backlink))
(outline (when-let ((outline (plist-get properties :outline)))
(mapconcat #'org-link-display-format outline " > ")))
(point (org-roam-backlink-point backlink))
(text (s-replace "\n" " " (org-roam-preview-get-contents
(org-roam-node-file source-node)
point)))
(reference (format "%s [[id:%s][%s]]\n%s\n%s\n\n"
(s-repeat (+ (org-roam-node-level node) 2) "*")
(org-roam-node-id source-node)
(org-roam-node-title source-node)
(if outline (format "%s (/%s/)"
(s-repeat (+ (org-roam-node-level node) 3) "*") outline) "")
text)))
(insert reference)))))))))