diff options
Diffstat (limited to 'pearl.el')
| -rw-r--r-- | pearl.el | 76 |
1 files changed, 54 insertions, 22 deletions
@@ -3340,42 +3340,74 @@ with just what the user set." (when project (list :project project)) (when labels (list :labels labels)))) +(defconst pearl--filter-none "[ None. ]" + "Sentinel candidate shown at the top of `pearl--read-filter-interactively' +prompts. Picking it drops the dimension from the filter, the same as +opting out. Always rendered first so \"no constraint\" is a visible +choice rather than a hidden empty-input idiom.") + +(defun pearl--filter-none-value-p (value) + "Return non-nil when VALUE means \"no constraint\" for a filter dimension. +A nil, an empty string, or `pearl--filter-none' all mean the user opted +out of constraining this dimension." + (or (null value) + (and (stringp value) + (or (string-empty-p value) + (string= value pearl--filter-none))))) + +(defun pearl--with-none (candidates) + "Return CANDIDATES with `pearl--filter-none' prepended as the first option." + (cons pearl--filter-none candidates)) + (defun pearl--read-filter-interactively () "Build a filter plist by completing over the chosen team's fetched dimensions. Picks a team first (scoping the rest), then offers open-only, state, project, -labels, and assignee. The assignee prompt offers `me' (the viewer), -`member' (a specific teammate, resolved to an id), or `any' (no scoping). -Empty answers drop the dimension." +labels, and assignee. Each prompt carries `pearl--filter-none' as the topmost +choice; selecting it drops that dimension. The assignee prompt offers `me' +(the viewer), `member' (a specific teammate, resolved to an id), or +`pearl--filter-none' (no scoping)." (let* ((teams (pearl--all-teams)) - (team-name (completing-read "Team (empty for any): " - (mapcar (lambda (tm) (cdr (assoc 'name tm))) teams) - nil nil)) - (team-id (and (not (string-empty-p team-name)) + (team-name (completing-read + "Team: " + (pearl--with-none + (mapcar (lambda (tm) (cdr (assoc 'name tm))) teams)) + nil t)) + (team-id (and (not (pearl--filter-none-value-p team-name)) (pearl--get-team-id-by-name team-name))) (open (y-or-n-p "Open issues only? ")) (state (and team-id (let ((s (completing-read - "State (empty for any): " - (mapcar (lambda (st) (cdr (assoc 'name st))) - (pearl--team-states team-id)) - nil nil))) - (unless (string-empty-p s) s)))) + "State: " + (pearl--with-none + (mapcar (lambda (st) (cdr (assoc 'name st))) + (pearl--team-states team-id))) + nil t))) + (unless (pearl--filter-none-value-p s) s)))) (project (and team-id (let ((p (completing-read - "Project (empty for any): " - (pearl--team-collection-names 'projects team-id) - nil nil))) + "Project: " + (pearl--with-none + (pearl--team-collection-names 'projects team-id)) + nil t))) ;; The :project filter compiles to project.id.eq, so ;; resolve the picked name to its id before passing. - (unless (string-empty-p p) + (unless (pearl--filter-none-value-p p) (pearl--resolve-team-id 'projects p team-id))))) (labels (and team-id - (completing-read-multiple - "Labels (comma-separated, empty for none): " - (pearl--team-collection-names 'labels team-id)))) + ;; `[ None. ]' alongside real labels is redundant, so + ;; filter the sentinel out of the result. A result of + ;; only-sentinel (or empty) means "no labels." + (cl-remove pearl--filter-none + (completing-read-multiple + "Labels (comma-separated): " + (pearl--with-none + (pearl--team-collection-names 'labels team-id)) + nil t) + :test #'string=))) (assignee (let ((choice (completing-read - "Assignee (me / member / any): " - '("me" "member" "any") nil t "any"))) + "Assignee: " + (list pearl--filter-none "me" "member") + nil t))) (pcase choice ("me" :me) ("member" @@ -3390,7 +3422,7 @@ Empty answers drop the dimension." ;; The :team filter compiles to team.key.eq, so look the picked name up ;; in the teams alist and pass its key (not the display name). (pearl--assemble-filter - (and (not (string-empty-p team-name)) + (and (not (pearl--filter-none-value-p team-name)) (cdr (assoc 'key (cl-find team-name teams :key (lambda (tm) (cdr (assoc 'name tm))) |
