aboutsummaryrefslogtreecommitdiff
path: root/pearl.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-25 22:35:12 -0500
committerCraig Jennings <c@cjennings.net>2026-05-25 22:35:12 -0500
commit6924c27963ddd25f91ea3a1a02d462f0badabfc4 (patch)
tree9e87d325ee4155d9fce20da1dd9b17155acbaf29 /pearl.el
parent504133e600a197b50f05d3916264433ee9e4cb12 (diff)
downloadpearl-6924c27963ddd25f91ea3a1a02d462f0badabfc4.tar.gz
pearl-6924c27963ddd25f91ea3a1a02d462f0badabfc4.zip
feat(render): rebuild the derived #+TODO header on merge refresh
A same-source refresh updates issue subtrees in place and keeps retained dirty subtrees the merge skips, so the #+TODO line has to stay honest across it -- a refresh can surface a new state, and a kept heading's keyword must still be declared. pearl--update-derived-todo-header scans the final displayed buffer (every LINEAR-ID heading's TODO keyword + :LINEAR-STATE-TYPE: drawer), unions that with the fetched teams' states, and rewrites the line via pearl--derive-todo-line. Scanning the final buffer rather than the fetched issue list is what covers the retained subtrees. A legacy heading with no state-type drawer is classified by org-done-keywords. pearl--merge-query-result calls it after the merge, beside the source-header update. The merge and integration test setups stub pearl--team-states so the gather stays network-free.
Diffstat (limited to 'pearl.el')
-rw-r--r--pearl.el31
1 files changed, 31 insertions, 0 deletions
diff --git a/pearl.el b/pearl.el
index 19166db..c61180c 100644
--- a/pearl.el
+++ b/pearl.el
@@ -4063,6 +4063,36 @@ provenance advances on a refresh."
(when (re-search-forward "^#\\+LINEAR-TRUNCATED: .*$" nil t)
(replace-match (format "#+LINEAR-TRUNCATED: %s" (if truncated "yes" "no")) t t))))
+(defun pearl--update-derived-todo-header (states)
+ "Rewrite the buffer's `#+TODO:' line from the final headings plus STATES.
+Scans every Linear issue heading (carrying `LINEAR-ID') for its TODO keyword
+and `:LINEAR-STATE-TYPE:' drawer, unions those with STATES (the fetched teams'
+states, see `pearl--gather-header-states'), and rewrites the line via
+`pearl--derive-todo-line'. Scanning the final buffer -- not just the fetched
+issue list -- keeps retained / skipped dirty subtrees declared. A heading with
+no state-type drawer (a legacy render) is classified by `org-done-keywords'.
+Creates the line after `#+STARTUP:' if it is absent."
+ (save-excursion
+ (let (scanned)
+ (goto-char (point-min))
+ (while (re-search-forward "^\\*+ " nil t)
+ (when (org-entry-get nil "LINEAR-ID")
+ (let ((kw (org-get-todo-state))
+ (type (org-entry-get nil "LINEAR-STATE-TYPE")))
+ (when kw
+ (push (list :name kw
+ :type (cond ((and type (not (string-empty-p type))) type)
+ ((member kw org-done-keywords) "completed")
+ (t nil)))
+ scanned)))))
+ (let ((line (pearl--derive-todo-line (append states (nreverse scanned)))))
+ (goto-char (point-min))
+ (if (re-search-forward "^#\\+TODO: .*$" nil t)
+ (replace-match (format "#+TODO: %s" line) t t)
+ (when (re-search-forward "^#\\+STARTUP: .*$" nil t)
+ (end-of-line)
+ (insert (format "\n#+TODO: %s" line))))))))
+
(defun pearl--merge-query-result (result source)
"Merge a query RESULT into the current buffer by `LINEAR-ID', tagged with SOURCE.
The same-source refresh counterpart to `pearl--render-query-result': rather than
@@ -4083,6 +4113,7 @@ of the replace path."
(truncated (pearl--query-result-truncated-p result))
(counts (pearl--merge-issues-into-buffer issues)))
(pearl--update-source-header (length issues) truncated)
+ (pearl--update-derived-todo-header (pearl--gather-header-states issues))
(pearl-highlight-comments)
(pearl--restore-page-visibility)
(pearl--surface-buffer (current-buffer))