feat: add spine-shelves, adapt spine-books for shelf nesting

This commit is contained in:
2026-06-21 22:51:01 -04:00
parent 6e2a796be9
commit 858fc6c13b
3 changed files with 89 additions and 36 deletions
+49 -20
View File
@@ -393,8 +393,31 @@ Returns nil for empty or missing properties."
(goto-char pos)
(org-entry-get nil prop))))
(and val (> (length val) 0) val)))
(defun spine-shelves ()
"Return a list of shelf names (strings), one per level-1 headline.
Includes empty shelves (shelves with no books)."
(if (not (file-exists-p spine-org-file))
(progn
(message "spine-shelves: file not found: %s" spine-org-file)
nil)
(condition-case err
(with-temp-buffer
(insert-file-contents spine-org-file)
(org-mode)
(let ((shelves nil))
(org-element-map (org-element-parse-buffer 'headline) 'headline
(lambda (hl)
(when (= (org-element-property :level hl) 1)
(push (org-element-property :raw-value hl) shelves))))
(nreverse shelves)))
(error
(message "spine-shelves: failed to parse %s: %s"
spine-org-file (error-message-string err))
nil))))
(defun spine-books ()
"Return a list of plists, one per top-level headline in `spine-org-file'.
"Return a list of plists, one per book in `spine-org-file'.
Books are level-2 headlines nested under level-1 shelf headlines.
Returns nil if the file is missing or cannot be parsed."
(if (not (file-exists-p spine-org-file))
(progn
@@ -407,25 +430,31 @@ Returns nil if the file is missing or cannot be parsed."
(let ((books nil))
(org-element-map (org-element-parse-buffer 'headline) 'headline
(lambda (hl)
(when (= (org-element-property :level hl) 1)
(let ((pos (org-element-property :begin hl)))
(push (list :id (spine--prop pos "ID")
:title (org-element-property :title hl)
:status (org-element-property :todo-keyword hl)
:author (spine--prop pos "AUTHOR")
:isbn (spine--prop pos "ISBN")
:cover (spine--prop pos "COVER")
:format (spine--prop pos "FORMAT")
:rec_by (spine--prop pos "REC_BY")
:rec_note (spine--prop pos "REC_NOTE")
:rating (spine--prop pos "RATING")
:added (spine--prop pos "ADDED")
:last_modified (spine--prop pos "LAST_MODIFIED")
:tags (org-element-property :tags hl)
:notes (spine--extract-notes
(org-element-property :begin hl)
(org-element-property :end hl)))
books)))))
(let ((level (org-element-property :level hl)))
(when (= level 2)
(let* ((pos (org-element-property :begin hl))
(parent (org-element-property :parent hl))
(shelf (and parent
(= (org-element-property :level parent) 1)
(org-element-property :raw-value parent))))
(when shelf
(push (list :id (spine--prop pos "ID")
:title (org-element-property :title hl)
:author (spine--prop pos "AUTHOR")
:isbn (spine--prop pos "ISBN")
:cover (spine--prop pos "COVER")
:format (spine--prop pos "FORMAT")
:rec_by (spine--prop pos "REC_BY")
:rec_note (spine--prop pos "REC_NOTE")
:rating (spine--prop pos "RATING")
:added (spine--prop pos "ADDED")
:last_modified (spine--prop pos "LAST_MODIFIED")
:tags (org-element-property :tags hl)
:notes (spine--extract-notes
(org-element-property :begin hl)
(org-element-property :end hl))
:shelf shelf)
books)))))))
(nreverse books)))
(error
(message "spine-books: failed to parse %s: %s"