;;; org-index-capture --- dynamic block example -*- lexical-binding: t; -*- ;;; Blog: https://one-octet.dev/posts/org-mode-dynamic-index.html (require 'org) (declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated)) (declare-function org-element-parse-buffer "org-element" (&optional granularity visible-only keep-deferred)) (declare-function org-element-property "org-element-ast" (property node)) (defun org-index-extract-infos (file-path) "Extract all information for an index from FILE-PATH." (with-temp-buffer (insert-file-contents file-path) (append (org-element-map (org-element-parse-buffer) 'keyword (lambda (keyword) (list (org-element-property :key keyword) (org-element-property :value keyword)))) (list (list "FILE-PATH" file-path) (list "FILE-FULL-PATH" (expand-file-name file-path)))))) (defun org-index-list-files-path (directory) "Get the DIRECTORY based names of all `.org' files in DIRECTORY." (mapcar (lambda (file-name) (file-name-concat directory file-name)) (directory-files directory nil ".org$"))) (defun org-index-sort-file-alists (alists) "Sort a list of `.org' information alists by date." (sort alists (lambda (file-a file-b) (string> (cadr (assoc "DATE" file-a)) (cadr (assoc "DATE" file-b)))))) (defun org-index-spec-make (alist) "Call `format-spec-make' using an `.org' file ALIST." (let ((author (or (cadr (assoc "AUTHOR" alist)) "NO-AUTHOR")) (creator (or (cadr (assoc "CREATOR" alist)) "NO-CREATOR")) (date (or (cadr (assoc "DATE" alist)) "1999.12.31")) (description (or (cadr (assoc "DESCRIPTION" alist)) "NO-DESCRIPTION")) (file-full-path (cadr (assoc "FILE-FULL-PATH" alist))) (file-path (cadr (assoc "FILE-PATH" alist))) (language (or (cadr (assoc "LANGUAGE" alist)) "NO-LANGUAGE")) (options (or (cadr (assoc "OPTIONS" alist)) "NO-OPTIONS")) (title (or (cadr (assoc "TITLE" alist)) "NO-TITLE"))) (format-spec-make ?a author ?c creator ?d date ?D description ?l language ?o options ?P file-full-path ?p file-path ?t title))) (defun org-dblock-write:index (params) "Write a directory index." (let ((directory (or (plist-get params :directory ) (file-name-directory (buffer-file-name)))) (sort-recent (plist-get params :sort-recent)) (fmt (or (plist-get params :format) "- %d: [[file:%p][%t]]")) (alists nil)) (if sort-recent (setq alists (org-index-sort-file-alists (mapcar 'org-index-extract-infos (org-index-list-files-path directory)))) (setq alists (mapcar 'org-index-extract-infos (org-index-list-files-path directory)))) (mapcar (lambda (alist) (insert (format-spec fmt (org-index-spec-make alist)) "\n")) alists))) ;;;###autoload (defun org-index-insert-dblock (directory) "Create a dynamic block capturing a directory index." (interactive (list (string-replace (file-name-directory (buffer-file-name)) "" (expand-file-name (read-directory-name "Directory to index: "))))) (org-create-dblock (list :name "index" :directory directory))) (org-dynamic-block-define "index" 'org-index-insert-dblock) (provide 'org-index-capture) ;;; org-index-capture.el ends here