I have a notes files with a bunch of headlines containing various notes written over several years. Many of these notes are short and would belong in a system like org-roam, but there hasn’t been an easy way to migrate these notes into org-roam. So I’ve gone ahead and written a function org-roam-create-note-from-headline:
(defun org-roam-create-note-from-headline ()
"Create an Org-roam note from the current headline and jump to it.
Normally, insert the headline’s title using the ’#title:’ file-level property
and delete the Org-mode headline. However, if the current headline has a
Org-mode properties drawer already, keep the headline and don’t insert
‘#+title:'. Org-roam can extract the title from both kinds of notes, but using
‘#+title:’ is a bit cleaner for a short note, which Org-roam encourages."
(interactive)
(let ((title (nth 4 (org-heading-components)))
(has-properties (org-get-property-block)))
(org-cut-subtree)
(org-roam-find-file title nil nil 'no-confirm)
(org-paste-subtree)
(unless has-properties
(kill-line)
(while (outline-next-heading)
(org-promote)))
(goto-char (point-min))
(when has-properties
(kill-line)
(kill-line))))
Thanks for sharing. I think that’s useful, in particular if you use org-roam more like a wiki rahter than like a zettelkasten. Sometimes things get too large and deserve their own file.
This is pretty cool and almost exactly what I needed. As soon as I started using org-roam (which is just a few hours ago) I felt grasping for something like this. Thank you for this snippet. I see how this can be improved:
#+roam_tags can be set (based on org tags of the heading and roam_tags of the original file)
if there any links to the heading, they could be replaced with the link to the new note
drawer into a #+created: [2021-01-05 Tue 13:59] file property at the top :).
Of course, if this property drawer exists (which it does for almost all of my notes) it would be nice if it would still convert the heading itself to #+title: xxx at the top of the file.
I also had various issues with org-roam-extract-subtree, so I needed to use this snippet. I’ve modified it a bit for my use case, as I need to do multiple headlines at once and don’t need to worry about properties:
(defun gsgx/org-roam-create-note-from-headline ()
"Create an Org-roam note from the current headline if it doesn't
exist without jumping to it"
(let* ((title (nth 4 (org-heading-components)))
;; Read in the name of the node, with the title filled in
;; TODO: How can I just use the title without user input?
(node (org-roam-node-read title)))
;; Skip the node if it already exists
(if (org-roam-node-file node)
(message "Skipping %s, node already exists" title)
;; Without this the subsequent kills seem to be grouped together, not
;; sure why
(kill-new "")
;; Cut the subtree from the original file
(org-cut-subtree)
;; Create the new capture file
(org-roam-capture- :node node)
;; Paste in the subtree
(org-paste-subtree)
;; Removing the heading from new node
(kill-whole-line)
;; Finalizing the capture will save and close the capture buffer
(org-capture-finalize nil)
;; Because we've deleted a subtree, we need the following line to make the
;; `org-map-entries' call continue from the right place
(setq org-map-continue-from
(org-element-property :begin (org-element-at-point))))))
(defun gsgx/org-roam-create-note-from-headlines ()
(interactive)
(if (region-active-p)
;; `region-start-level' means we'll map over only headlines that are at
;; the same level as the first headline in the region. This may or may not
;; be what you want
(org-map-entries
'gsgx/org-roam-create-note-from-headline t 'region-start-level)
;; If no region was selected, just create the note from the current headline
(gsgx/org-roam-create-note-from-headline)))
Hopefully that helps someone who comes across this thread. I would like to improve this by having an argument that I can set to not even prompt me for the titles at all if I’m sure the headline titles work, but I wasn’t able to figure out how to do that.