diff options
Diffstat (limited to 'pearl.el')
| -rw-r--r-- | pearl.el | 44 |
1 files changed, 42 insertions, 2 deletions
@@ -1535,6 +1535,32 @@ state-name-to-keyword mapping." "-+" "-+"))) (if (string-empty-p slug) "TODO" slug))) +(defun pearl--label-name-to-tag (name) + "Slugify a Linear label NAME to an Org tag. +Downcased, each run of characters outside [[:alnum:]_] replaced by a single +underscore (alnum is Unicode-aware, so accented and non-Latin letters survive), +with leading and trailing underscores trimmed. Org tags allow no hyphens or +spaces, unlike the TODO keywords `pearl--state-name-to-keyword' builds. A name +that slugifies to empty (all punctuation) returns the empty string; the caller +drops it from the heading and keeps it in the `:LINEAR-LABELS:' drawer." + (string-trim + (replace-regexp-in-string "[^[:alnum:]_]+" "_" (downcase (or name ""))) + "_+" "_+")) + +(defun pearl--label-tags (labels) + "Return the ordered, de-duplicated Org tags for a list of LABEL plists. +Each label's `:name' is slugified via `pearl--label-name-to-tag'; empty slugs +are dropped and duplicates removed, first occurrence winning, so two label +names that slugify to the same tag render it once at the first's position." + (let ((seen (make-hash-table :test 'equal)) + (tags '())) + (dolist (label labels) + (let ((tag (pearl--label-name-to-tag (plist-get label :name)))) + (unless (or (string-empty-p tag) (gethash tag seen)) + (puthash tag t seen) + (push tag tags)))) + (nreverse tags))) + (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 @@ -1804,11 +1830,13 @@ identifier prefix -- so an unedited heading is a no-op on title sync)." (plist-get issue :labels) ", ")) (label-ids (mapconcat (lambda (l) (or (plist-get l :id) "")) (plist-get issue :labels) " ")) + (tags (pearl--label-tags (plist-get issue :labels))) + (tag-string (if tags (concat " :" (string-join tags ":") ":") "")) (body-org (pearl--md-to-org description))) (concat - (format "** %s %s%s\n" todo + (format "** %s %s%s%s\n" todo (if (string-empty-p priority) "" (concat priority " ")) - heading-title) + heading-title tag-string) ":PROPERTIES:\n" (format ":LINEAR-ID: %s\n" (or (plist-get issue :id) "")) (format ":LINEAR-IDENTIFIER: %s\n" (or (plist-get issue :identifier) "")) @@ -2747,6 +2775,16 @@ inside an issue subtree." (org-entry-put nil "LINEAR-ASSIGNEE-ID" assignee-id) (message "Set assignee to %s (save to push)" assignee-name)))) +(defun pearl--set-heading-label-tags (tags) + "Replace the Org tags on the issue heading at point with TAGS, a list of strings. +Only the heading's tag set changes: the TODO keyword, priority cookie, title, +identifier prefix, body, and drawer are preserved. Pearl owns the entire +issue-heading tag set, so any hand-added tags are dropped. An empty TAGS list +clears the heading's tags. Point must be on or inside the issue heading." + (save-excursion + (org-back-to-heading t) + (org-set-tags tags))) + ;;;###autoload (defun pearl-edit-labels (label-names) "Set the labels of the issue at point to LABEL-NAMES in the buffer. @@ -2774,6 +2812,8 @@ inside an issue subtree." (org-entry-put nil "LINEAR-LABELS" (format "[%s]" (mapconcat #'identity label-names ", "))) (org-entry-put nil "LINEAR-LABEL-IDS" (string-join label-ids " ")) + (pearl--set-heading-label-tags + (pearl--label-tags (mapcar (lambda (n) (list :name n)) label-names))) (message "Set labels to %s (save to push)" (if label-names (mapconcat #'identity label-names ", ") "none"))))) |
