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!


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 '16 at 18:09
  • Please note, i have around 28 files totaling to 1.2MB – coderain1 Dec 19 '16 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. – John Kitchin Dec 19 '16 at 23:17
  • sorry my bad. The script was running from my home dir and hence taking too long. – coderain1 Dec 20 '16 at 4:07
  • That is because the f-files function works recursively in that function! – John Kitchin Dec 20 '16 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, privacy policy and cookie policy

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