Creating an org-roam note from an existing headline

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))))
10 Likes

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 super handy!!! Thanks!

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

A further improvement could be to convert the

:PROPERTIES: 
:CREATED:  [2021-01-05 Tue 13:59]
:END:

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.

How to implement this in V2?
Thanks.

I tried to change

(org-roam-find-file title nil nil 'no-confirm)

to

        (org-roam-node-find 'other-window title nil)

and it works.

3 Likes

I think this new feature just added may provide this functionality now: (feat): add org-roam-extract-subtree (#1710) · org-roam/org-roam@829ee68 · GitHub

Not working with my current setup though…

  1. It didn’t use my template, so the file name is still YearMonthDate.xxx.org
  2. Error message: non-existent agenda file [R]emove from list or [A]bort?

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.