Currently i have around 25 org files each with multiple headings. Everytime i want to go over my notes, i need to remember the name of the file and open it (faster if its in buffer, else i need to go to the org folder)

Is there a way to generate a TOC of files/heading1 for all files under the org-directory and place it under an index.org?

P.S: Google did not return any good package!

2 Answers 2


There are a couple of options. I would not create a permanent index.org file, because it would take some extra effort to keep it up to to date. If you don't mind installing f.el and helm, here is a dynamic way to get a list of all the headings.

(defun org-toc ()
  "Generate a table of contents for org-files in this directory."
  (let ((org-agenda-files (f-entries "." (lambda (f) (f-ext? f "org")) t)))

If you really want a buffer with headlines, you could try this approach to make a temporary buffer with links to the headlines:

(defun org-toc ()
  (let ((headings (delq nil (loop for f in (f-entries "." (lambda (f) (f-ext? f "org")) t)
                  (with-current-buffer (find-file-noselect f)
                     (lambda ()
                       (when (> 2 (car (org-heading-components)))
                     (cons f (nth 4 (org-heading-components)))))))))))
    (switch-to-buffer (get-buffer-create "*toc*"))
    (loop for (file . heading) in headings 
      (insert (format "* [[%s::*%s]]\n" file heading)))))
  • Thanks! I tried the second solution and spacemacs just hangs! Its clocking at 98% since sometime now..
    – coderain1
    Dec 19, 2016 at 18:09
  • Please note, i have around 28 files totaling to 1.2MB
    – coderain1
    Dec 19, 2016 at 18:11
  • It takes about 10 seconds to list 3541 headings from 35 files that are 2.4MB in size on my mac. It is for sure not the speediest method. Dec 19, 2016 at 23:17
  • sorry my bad. The script was running from my home dir and hence taking too long.
    – coderain1
    Dec 20, 2016 at 4:07
  • That is because the f-files function works recursively in that function! Dec 20, 2016 at 13:18

Here is another approach I have used:

(defun org-toc ()
  (let ((files (f-entries "." (lambda (f) (f-ext? f "org")) t))
    (headlines '())
    (loop for file in files do
        (insert-file-contents file) 
        (goto-char (point-min))
        (while (re-search-forward org-heading-regexp nil t)
          (cl-pushnew (list
               (format "%-80s (%s)"
                   (match-string 0)
                   (file-name-nondirectory file))
               :file file
               :position (match-beginning 0))
    (setq choice
      (completing-read "Headline: " (reverse headlines)))
    (find-file (plist-get (cdr (assoc choice headlines)) :file))
    (goto-char (plist-get (cdr (assoc choice headlines)) :position))))

if you have helm or ivy setup to do completion, it should use them to help pick the headlines. This takes about 4-5 seconds for me on about 3500 headlines.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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