From ba58f66332e6a8692d985f4d5478c743fca9bcb7 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 24 May 2026 14:39:01 -0500 Subject: refactor: extract the label-category picker out of pearl-new-issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pearl-new-issue was 129 lines — long but linear (a run of creation prompts), and untested since it is eight interactive reads. I pulled out the one intricate, self-contained piece: the issue-type label selection that groups labels by their " - " category prefix and does the two-stage category-then-label pick. It is now pearl--read-issue-label, called for selected-type. Verbatim move, no logic change; new-issue drops to 93 lines. 353 tests green. --- pearl.el | 81 +++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 37 deletions(-) (limited to 'pearl.el') diff --git a/pearl.el b/pearl.el index 5ce5811..a1f6c9b 100644 --- a/pearl.el +++ b/pearl.el @@ -3136,6 +3136,49 @@ Uses async API for better performance." (message "No team selected")))) ;;;###autoload +(defun pearl--read-issue-label (team-id) + "Prompt for an issue-type label in TEAM-ID, returning its label id (or nil). +Labels are grouped by their \" - \" category prefix; the user picks a category +\(or All), then a label, with a fuzzy match used directly when the typed text +resolves to a single label." + (let* ((issue-types (pearl-get-issue-types team-id)) + (label-names (mapcar #'car issue-types)) + ;; Group labels by category (e.g., "Docs", "Feature", etc.) + (label-categories (let ((categories (make-hash-table :test 'equal))) + (dolist (label label-names) + (when-let* ((parts (split-string label " - " t)) + (category (car parts))) + (puthash category + (cons label (gethash category categories nil)) + categories))) + categories)) + (category-names (hash-table-keys label-categories)) + ;; First select a category, then a specific label + (selected-category (completing-read + "Label category: " + (append '("All") category-names) + nil nil nil nil "All")) + (filtered-labels (if (string= selected-category "All") + label-names + (gethash selected-category label-categories nil))) + (label-prompt (completing-read + (if (string= selected-category "All") + "Label (type for fuzzy search): " + (format "Label in %s category: " selected-category)) + filtered-labels + nil nil nil nil "")) + (matching-labels (when (not (string-empty-p label-prompt)) + (cl-remove-if-not + (lambda (label-name) + (string-match-p (regexp-quote label-prompt) label-name)) + filtered-labels))) + (selected-label-name (if (= (length matching-labels) 1) + (car matching-labels) + (when matching-labels + (completing-read "Select specific label: " matching-labels nil t))))) + (when (and selected-label-name (not (string-empty-p selected-label-name))) + (cdr (assoc selected-label-name issue-types))))) + (defun pearl-new-issue () "Create a new Linear issue with additional attributes." (interactive) @@ -3180,43 +3223,7 @@ Uses async API for better performance." (string-to-number estimate))) ;; Issue type (label) - (issue-types (pearl-get-issue-types team-id)) - (label-names (mapcar #'car issue-types)) - ;; Group labels by category (e.g., "Docs", "Feature", etc.) - (label-categories (let ((categories (make-hash-table :test 'equal))) - (dolist (label label-names) - (when-let* ((parts (split-string label " - " t)) - (category (car parts))) - (puthash category - (cons label (gethash category categories nil)) - categories))) - categories)) - (category-names (hash-table-keys label-categories)) - ;; First select a category, then a specific label - (selected-category (completing-read - "Label category: " - (append '("All") category-names) - nil nil nil nil "All")) - (filtered-labels (if (string= selected-category "All") - label-names - (gethash selected-category label-categories nil))) - (label-prompt (completing-read - (if (string= selected-category "All") - "Label (type for fuzzy search): " - (format "Label in %s category: " selected-category)) - filtered-labels - nil nil nil nil "")) - (matching-labels (when (not (string-empty-p label-prompt)) - (cl-remove-if-not - (lambda (label-name) - (string-match-p (regexp-quote label-prompt) label-name)) - filtered-labels))) - (selected-label-name (if (= (length matching-labels) 1) - (car matching-labels) - (when matching-labels - (completing-read "Select specific label: " matching-labels nil t)))) - (selected-type (when (and selected-label-name (not (string-empty-p selected-label-name))) - (cdr (assoc selected-label-name issue-types)))) + (selected-type (pearl--read-issue-label team-id)) ;; Get project (selected-project (pearl-select-project team-id)) -- cgit v1.2.3