diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-25 22:23:43 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-25 22:23:43 -0500 |
| commit | 768c9c0122130d44e9243f22f7681677623b713a (patch) | |
| tree | 81d234dde8117dafb9fd67b4b90d918108801c9d /pearl.el | |
| parent | c9ff295f02c355f889d1605853c148bbce404d52 (diff) | |
| download | pearl-768c9c0122130d44e9243f22f7681677623b713a.tar.gz pearl-768c9c0122130d44e9243f22f7681677623b713a.zip | |
feat(render): add the pure helpers for workspace-derived TODO keywords
Two pure functions behind the coming workspace-faithful #+TODO line. pearl--state-name-to-keyword slugifies a Linear state name to an Org keyword: upcased, non-alphanumeric runs collapsed to a single hyphen, edges trimmed, Unicode letters kept (so "Ångström" survives), and an all-symbol name falls back to TODO. pearl--derive-todo-line turns an ordered state list into the "ACTIVE... | DONE..." string: completed/canceled/duplicate types go after the bar, the rest before, order preserved within each side, de-duplicated by slug, with the hardcoded default returned for an empty list.
There's no caller yet -- the render integration and the gather pipeline land next. This is the derivation half of the keyword spec. The sync-back half it described is superseded by save-model-v2 (state reconciles at save) and the separate keyword-cycle task.
Diffstat (limited to 'pearl.el')
| -rw-r--r-- | pearl.el | 35 |
1 files changed, 35 insertions, 0 deletions
@@ -1534,6 +1534,41 @@ STATE is the Linear state string." (or (cdr (assoc state pearl-state-to-todo-mapping)) "TODO")) ; Default fallback +(defun pearl--state-name-to-keyword (name) + "Slugify a Linear state NAME to an Org TODO keyword. +Upcased, each run of non-alphanumerics replaced by a single hyphen (alnum is +Unicode-aware, so accented and non-Latin letters survive), with leading and +trailing hyphens trimmed. An all-symbol name (empty slug) falls back to +\"TODO\". This is the workspace-faithful replacement for the static +`pearl-state-to-todo-mapping' lookup." + (let ((slug (string-trim + (replace-regexp-in-string "[^[:alnum:]]+" "-" (upcase (or name ""))) + "-+" "-+"))) + (if (string-empty-p slug) "TODO" slug))) + +(defun pearl--derive-todo-line (states) + "Build the `#+TODO:' keyword string from an ordered STATES list. +Each state is a plist with at least :name and :type. States whose :type is +completed / canceled / duplicate go after the `|' (the done side); the rest go +before it. The caller's order is preserved within each side; keywords are +slugified (`pearl--state-name-to-keyword') and de-duplicated by slug, first-seen +winning. An empty STATES list returns the hardcoded default so the rendered +file always has a valid `#+TODO:' line." + (if (null states) + "TODO IN-PROGRESS IN-REVIEW BACKLOG BLOCKED | DONE" + (let ((active '()) (done '()) (seen (make-hash-table :test 'equal))) + (dolist (s states) + (let ((kw (pearl--state-name-to-keyword (plist-get s :name)))) + (unless (gethash kw seen) + (puthash kw t seen) + (if (member (plist-get s :type) '("completed" "canceled" "duplicate")) + (push kw done) + (push kw active))))) + (let ((active-str (string-join (nreverse active) " ")) + (done-str (string-join (nreverse done) " "))) + (concat active-str " |" + (if (string-empty-p done-str) "" (concat " " done-str))))))) + (defun pearl--get-todo-states-pattern () "Return the regex pattern matching the Org TODO states. Built from the org keywords in `pearl-state-to-todo-mapping' and |
