'How to extract titles as links from a list of Org files in a directory

I have a list of org files under a directory:

> org-file1.org
> org-file2.org
> org-file3.org
> ...
> org-fileN.org

I want to extract their titles (using #+title tag) as links as follows:

 [[file:org-file1.org][title for org file 1]]
 [[file:org-file2.org][title for org file 2]]
 [[file:org-file3.org][title for org file 3]]
 ...
 [[file:org-fileN.org][title for org file N]]

How can I extract these as a list using Emacs Lisp?



Solution 1:[1]

You could use the org-element-api for this, but in this case it is probably easier/faster to simply search by regexp using looking-at-p. To get all files in a directory, there is directory-files(-recursively). Then finally you could achieve it using the following function:

(defun extract-org-directory-titles-as-list (&optional dir)
  (interactive "D")
  (print
   (delete nil
           (let ((case-fold-search t))
             (mapcar (lambda (f)
                       (when (string-match "org$" f)
                         (with-temp-buffer
                           (insert-file-contents-literally
                            (concat (file-name-as-directory dir) f))
                           (while (and (not (looking-at-p "#\\+TITLE:"))
                                       (not (eobp)))
                             (forward-line))
                           (when (not (eobp))
                             (cons f (substring (thing-at-point 'line) 9 -1))))))
                     (directory-files dir))))))

(defun insert-directory-org-file-titles (&optional dir)
  (interactive "D")
  (let ((files-titles (extract-org-directory-titles-as-list dir)))
    (dolist (ft files-titles)
      (insert (concat "[[file:" (car ft)"][" (cdr ft) "]]\n")))))

If you prefer to just have it as a (a)list, like you asked, then just use extract-org-directory-titles-as-list.

Solution 2:[2]

Here's some code that can be used on a single file to get the title. It's part of an org mode file that can be used for testing. Save it as an Org mode file, visit it in Emacs and type C-c C-c on the code block. Change the title and evaluate the code block again.

#+OPTIONS: toc:nil
#+TITLE: This is a very nice title, don't you think?
#+AUTHOR: A.U. Thor

* foo
bar


* Code

#+begin_src elisp :results drawer
  (save-excursion
    (goto-char (point-min))
    (let ((case-fold-search t))
      (search-forward-regexp "^#\\+TITLE:")
      (org-element-property :value (org-element-context (org-element-at-point)))))
#+end_src


#+RESULTS:
:results:
This is a very nice title, don't you think?
:end:

The code block goes to the beginning of the buffer, searches forward for the string #+TITLE: at the beginning of a line (the search is case-insensitive) and then uses some functions from the org-element library to parse the buffer at point, get the context and get the :value property out of it.

In order to make it into a complete solution, you have to:

  • make it more robust: check that this is a title keyword not just some random junk that happened to satisfy the match, although that's unlikely; but better safe than sorry.

  • wrap it in a loop that does a find-file on every file in the directory and gets the title for each file and returns a list of tuples: (filename title) that you can easily use to create your links.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2