diff options
| -rw-r--r-- | pearl.el | 50 | ||||
| -rw-r--r-- | tests/test-pearl-output.el | 51 |
2 files changed, 92 insertions, 9 deletions
@@ -228,6 +228,16 @@ leave the buffer expanded after updates." :type 'boolean :group 'pearl) +(defcustom pearl-help-header t + "When non-nil, render a folded \"* Pearl\" heading atop each Linear file. +The heading collapses on open (via a `:VISIBILITY: folded' property) and carries +the edit-then-save reminder, the keymap cues, and the priority-cookie legend, so +the help is discoverable without a dozen lines of `#' preamble cluttering the +buffer. It has no `LINEAR-ID', so save and merge ignore it. Set to nil to omit +it entirely, leaving just the machine `#+' keywords." + :type 'boolean + :group 'pearl) + (defcustom pearl-title-case-headings nil "When non-nil, render issue titles in the heading in smart title case. The default is nil: titles render verbatim, exactly as Linear stores them, so @@ -3603,6 +3613,35 @@ only add the ones a failed team fetch left out." issues)))) (append team-states issue-states)))) +;; Forward declaration: `pearl-keymap-prefix' is a defcustom in the keymap +;; section below, but the help header (rendered here) reads it for the key cues. +(defvar pearl-keymap-prefix) + +(defun pearl--help-header-string () + "Return the folded \"* Pearl\" heading shown atop a Linear file. +A collapsible section (`:VISIBILITY: folded') carrying the edit-then-save +reminder, a note that the section is local-only, the keymap cues, the +priority-cookie legend, and how to hide it. The cues reflect the live +`pearl-keymap-prefix' so a customized prefix shows the right chords; a nil +prefix (no binding) points at \\[execute-extended-command] instead. The +heading carries no `LINEAR-ID', so save and merge skip it (they walk only +`LINEAR-ID'-bearing subtrees -- see `pearl--issue-subtree-markers')." + (let ((p pearl-keymap-prefix)) + (concat + "* Pearl\n" + ":PROPERTIES:\n" + ":VISIBILITY: folded\n" + ":END:\n" + "Edit issues in this buffer, then save to push your changes to Linear.\n" + "This section is never sent to Linear. It is local help text only.\n" + (if p + (concat + (format "Inline edits (title, body, comments, [#A] cookie) save with %s s. %s S saves all.\n" p p) + (format "State / assignee / labels: %s e s / e a / e l. %s g refreshes; %s m menu; %s f s pick source.\n" p p p p)) + "No prefix is bound (pearl-keymap-prefix is nil); reach commands via M-x (e.g. M-x pearl-save-issue, M-x pearl-menu).\n") + "Priority cookies: [#A] Urgent, [#B] High, [#C] Medium, [#D] Low, no cookie = None.\n" + "Hide this section: set pearl-help-header to nil (M-x customize-variable RET pearl-help-header).\n"))) + (defun pearl--build-org-content (issues &optional source truncated states account) "Build the Org content string for the linear org file from ISSUES. SOURCE is the active-source descriptor recorded in the header so a later @@ -3631,13 +3670,12 @@ no side effects." (insert (format "#+LINEAR-FILTER: %s\n" (pearl--summarize-filter filter))) (insert (format "#+LINEAR-COUNT: %d\n" (length issues))) (insert (format "#+LINEAR-TRUNCATED: %s\n" (if truncated "yes" "no"))) - ;; Affordance preamble (org comments -- not rendered content). Keys - ;; reference the live `pearl-mode' keymap so the cues match what the - ;; user actually types, not the M-x longhand. - (insert "#\n") - (insert "# Inline edits (title, body, comments, [#A] cookie) save with C-; L s. C-; L S saves all.\n") - (insert "# State / assignee / labels: C-; L e s / e a / e l. C-; L g refreshes; C-; L m menu; C-; L f s pick source.\n") + ;; Optional folded help heading carries the cues + priority legend in a + ;; collapsible section (`pearl-help-header'), replacing the raw `#' + ;; affordance comments. It has no `LINEAR-ID', so sync/merge skip it. (insert "\n") + (when pearl-help-header + (insert (pearl--help-header-string))) ;; Single top-level parent so the issues are sortable as a group ;; (org-sort on this heading) instead of orphan headings, and so a diff --git a/tests/test-pearl-output.el b/tests/test-pearl-output.el index e3208e3..7edc29d 100644 --- a/tests/test-pearl-output.el +++ b/tests/test-pearl-output.el @@ -51,9 +51,10 @@ (should (string-match-p "^#\\+title: Linear — My open issues$" out)) (should (string-match-p "^#\\+LINEAR-SOURCE: " out)) (should (string-match-p "^#\\+LINEAR-COUNT: 0$" out)) - ;; affordance preamble is present as org comments, not content -- match - ;; the save cue loosely so a future rephrase doesn't break the test - (should (string-match-p "^# .*save" out)))) + ;; the help header (default on) carries the cues now; the raw `#' affordance + ;; comments are gone + (should (string-match-p "^\\* Pearl$" out)) + (should-not (string-match-p "^# " out)))) (ert-deftest test-pearl-build-org-content-source-roundtrips () "The serialized source in the header reads back to the original plist." @@ -81,6 +82,50 @@ independent of `pearl-title-case-headings' (which governs issue headings only)." (should (string-match-p "^#\\+title: Linear — my open bugs$" (pearl--build-org-content '() source)))))) +;;; --build-org-content help header (`pearl-help-header') + +(ert-deftest test-pearl-help-header-string-content () + "The help header is a folded \"* Pearl\" heading carrying the +edit-then-save reminder, key cues, the priority legend, and how to hide it. +It has no LINEAR-ID, so save and merge skip it." + (let ((s (pearl--help-header-string))) + (should (string-match-p "^\\* Pearl$" s)) + (should (string-match-p "^:VISIBILITY: folded$" s)) + (should (string-match-p "save" s)) ; edit-then-save / key cue + (should (string-match-p "\\[#A\\] Urgent" s)) ; priority legend ends... + (should (string-match-p "\\[#D\\] Low" s)) ; ...A through D + (should (string-match-p "never sent to Linear" s)) ; local-only note + (should (string-match-p "pearl-help-header" s)) ; how to turn it off + (should-not (string-match-p "LINEAR-ID" s)))) + +(ert-deftest test-pearl-help-header-reflects-prefix () + "The key cues use the live `pearl-keymap-prefix'; a nil prefix points at M-x +instead of printing a bogus chord." + (let ((pearl-keymap-prefix "C-c l")) + (should (string-match-p "C-c l s" (pearl--help-header-string)))) + (let* ((pearl-keymap-prefix nil) + (s (pearl--help-header-string))) + (should (string-match-p "M-x" s)) + (should-not (string-match-p "nil s" s)))) + +(ert-deftest test-pearl-build-org-content-help-header-on () + "With `pearl-help-header' non-nil, the folded help heading is emitted and the +raw `#' affordance comments are not." + (let* ((pearl-help-header t) + (out (pearl--build-org-content '() '(:type filter :name "X" :filter nil)))) + (should (string-match-p "^\\* Pearl$" out)) + (should (string-match-p "^:VISIBILITY: folded$" out)) + (should-not (string-match-p "^# " out)))) + +(ert-deftest test-pearl-build-org-content-help-header-off () + "With `pearl-help-header' nil, neither the help heading nor the `#' comments +appear -- a minimal preamble of just the machine keywords." + (let* ((pearl-help-header nil) + (out (pearl--build-org-content '() '(:type filter :name "X" :filter nil)))) + (should-not (string-match-p "Pearl" out)) + (should-not (string-match-p "^# " out)) + (should (string-match-p "^#\\+title:" out)))) + ;;; --read-active-source (ert-deftest test-pearl-read-active-source-absent () |
