0

I want to be able to create a function that splits or exports only top headlines starting with * in a org mode file (recursively) into a separate file.

* Title_of_Heading_1     
  Text 1

** Sub-Heading 2
   Text 2

* Title of Heading 2
  Text 3

** Sub-Heading 22
   Text 2

*** Sub-Heading 33
   Text 2

would result in the files splitting only Title of Heading 1 and Title of Heading 2, note that in the filename white space is replaced with _. Splitted files:

=> Title_of_Heading_1.md, which will contain `Title_of_Heading_1` and `Sub-Heading 2`
=> Title_for_Heading_2.md, which will contain `Title of Heading 2`, `Sub-Heading 22` and `Sub-Heading 33`

Related: split every single org headline in a org file to separate md/org files

3
  • 2
    You can export a subtree: position your cursor on the subtree of interest, do C-c C-e and in the export dispatcher, do C-s to toggle the buffer/subtree setting before you specify the backend and destination.
    – NickD
    Commented May 25, 2023 at 18:02
  • Thanks. My file has plenty headers (I was using it as a journal), I wanted to automate the process instead of manually doing.
    – alper
    Commented May 26, 2023 at 5:48
  • If the manual process works, then it's easy to write a function that does that and then invoke the function in a loop (see the answer below - which is a bit ad-hoc for my taste, but the essentials are present), or better (IMO), call org-element-map to do the headline hopping dance and have it call the function on each headline of interest.
    – NickD
    Commented May 26, 2023 at 12:22

1 Answer 1

1

I think the following function would suffice.

(defun split-org-top-headlines ()
"split the current buffer into files under same directory as the buffer, containing 1 top-level subtree each, with slugged_title_filename.org as filename"
  (interactive)
  (goto-char 0)
  (while (outline-next-heading) ;; move on to next heading
    (let* ((name (nth 4 (org-heading-components)))
          (sluged-name (replace-regexp-in-string " " "_" name))
          (begin (point))
          (end (save-excursion (outline-end-of-subtree) (point)))
          )
      (kill-region begin end)
      ;;add guard here if there would be same name headings in your org file
      (with-temp-buffer
        ;; (insert "options you want the files to have, such as toc:nil")
        (yank)
        ;; uncomment following to write to markdown
        ;; (org-md-export-as-markdown)
        (write-file (concat sluged-name ".org")) ;;if to markdown, change ".org" to ".md"
        )
    )
  )
)

it's basically what you would do manually to achieve that effect scripted in elisp. I didn't add dispatch for md/org but everything you need to integrate one should be present.

note: it don't export the first headline if (goto-char 0) would land on it. I'd suggest always have a #+title:Title. Or add your own guard for it.

2
  • It would be better (IMO) to use the org-element API for the scanning plus a function to do the exporting (or refiling for that matter), rather than looping over headlines: it's much more flexible and it also generally helps avoid corner cases (like the one you identify in your last paragraph). Do C-h f org-element-map to start and search this site for this function to see some applications.
    – NickD
    Commented May 26, 2023 at 12:06
  • This solution remove the headline cursor is on
    – alper
    Commented May 26, 2023 at 21:08

Not the answer you're looking for? Browse other questions tagged or ask your own question.