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)))))))))
It seems like most of the methods here won’t work on --batch mode. See this other topic.
It might be caused by the interactive calls. Does anyone here have any idea on how to fix this?
If you are on V2, org-roam-sql does not exist, and the table named file-links does not, either. Refer to this section of the manual, where this example is mentioned:
(org-roam-db-query [:select * :from nodes])
For correct table and field names, see C-h v of variable org-roam-db--table-schemata. I tink you are going for table links for field dest.
I’m able to run the functions above if I’m already inside Emacs. However I’m unable to generate the backlinks when I try to run the script in --batch mode.
I’m trying to run the following code with the command emacs -q --batch -l publish.el and it does publish my website but it doesn’t generate the backlinks below each note.
(require 'ox-publish)
(require 'org-roam)
(setq org-roam-directory "."
org-id-locations-file ".orgids"
org-id-link-to-org-use-id t
org-id-extra-files (org-roam-list-files)
org-id-track-globally t
org-export-with-broken-links 'mark
;; HTML
org-html-validation-link nil
org-html-home/up-format "<!-- %s --><nav><a href=\"%s\">Index</a></nav>"
org-html-link-home "index.html"
org-html-head-include-scripts nil
org-html-head-include-default-style nil
org-html-head "<link rel=\"stylesheet\" href=\"assets/bamboo.css\">"
publish-directory "html")
(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)
('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)
(-when-let* (((node . end-position) node-and-end)
(backlinks (--filter (->> (org-roam-backlink-source-node it)
(org-roam-node-file)
(s-contains? "private/") (not))
(org-roam-backlinks-get node)))
(heading (format "\n\n%s Backlinks\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
(properties-drawer ":PROPERTIES:\n:HTML_CONTAINER_CLASS: references\n:END:\n"))
(goto-char end-position)
(insert heading)
(insert properties-drawer)
(dolist (backlink backlinks)
(let* ((source-node (org-roam-backlink-source-node backlink))
(source-file (org-roam-node-file source-node))
(properties (org-roam-backlink-properties backlink))
(outline (when-let ((outline (plist-get properties :outline)))
(when (> (length outline) 1)
(mapconcat #'org-link-display-format outline " > "))))
(point (org-roam-backlink-point backlink))
(text (org-roam-preview-get-contents
source-file
point))
(reference (format "\n ----- \n%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))
(label-list (with-temp-buffer
(insert text)
(org-element-map (org-element-parse-buffer) 'footnote-reference
(lambda (reference)
(org-element-property :label reference)))))
(footnote-string-list
(with-temp-buffer
(insert-file-contents source-file)
(-map (lambda (label) (buffer-substring-no-properties
(nth 1 (org-footnote-get-definition label))
(nth 2 (org-footnote-get-definition label))))
label-list))))
(-map (lambda (footnote-string) (insert footnote-string)) footnote-string-list)
(insert reference))))))))
(add-hook 'org-export-before-processing-hook 'collect-backlinks-string)
(setq org-publish-project-alist
(list
(list "org-site:main"
:recursive nil
:exclude ".*gitignore|.*/private/.*" ;; remove private directory
:base-directory org-roam-directory
:publishing-function 'org-html-publish-to-html
:publishing-directory publish-directory
:with-date t
:with-author nil ;; Don't include author name
:with-creator t ;; Include Emacs and Org versions in footer
:with-toc t ;; Include a table of contents
:section-numbers nil ;; Don't include section numbers
:time-stamp-file nil
:auto-sitemap t
:sitemap-filename "index.org"
:sitemap-sort-files 'alphabetically
:sitemap-ignore-case t
:sitemap-title "personal digital garden")
(list "org-site:static"
:base-directory org-roam-directory
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
:recursive t
:publishing-directory publish-directory
:publishing-function 'org-publish-attachment)))
(org-publish-all t)
The —batch option implies -q (and you explicitly adds it in the command line). I’m guessing you would need to explicitly add load-path for org-roam before you require it.
I also had this issue. Here’s how I fixed it:
TLDR: Check if org-roam-db-location is correct.
I figured out the variables nodes-in-file-sorted was nil so (do list never entered. From there, I discovered (org-roam-at-point) was returning little information: #s(org-roam-node nil nil nil nil nil 6a6d5036-8acd-46b3-4550-9e737271d8c0 nil 1 nil nil nil nil nil nil nil nil nil nil) The function needs more data to work.
If you see the documentation of (org-roam-at-point),
This function also returns the node if it has yet to be cached in the
database. In this scenario, only expect :id and :point to be
populated.
This is exactly what was happening. I discovered org-roam-db-location was pointing to the wrong location, because batch mode doesn’t come with my usual configuration.