aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-06 12:05:04 -0500
committerCraig Jennings <c@cjennings.net>2026-06-06 12:05:04 -0500
commitb58944592afc9109718f5c0431fb337e68729715 (patch)
tree5836ab60d24e7e029b6b12112ea4fbea4c9b333e
parentadee44df124e8983084737c50e9c767edd70aea8 (diff)
downloadpearl-b58944592afc9109718f5c0431fb337e68729715.tar.gz
pearl-b58944592afc9109718f5c0431fb337e68729715.zip
fix(views): re-fold the page after an interactive sort
Setting a sort order opened every issue, drawer, and comment in the buffer. The reorder moves subtrees by deleting the issue block and re-inserting it, and freshly inserted Org text comes in fully expanded. The fold state lives in overlays that delete-and-insert drops. Neither sort path re-folded afterward, unlike the full-render and merge paths, which already do. Both sort paths now call pearl--restore-page-visibility after a successful reorder, re-folding the page to its #+STARTUP visibility so it stays the scannable outline it was before the sort. A refusal (a grouped buffer or a failed refetch) moves nothing, so it doesn't re-fold. The behavior honors pearl-fold-after-update like the other repopulating paths.
-rw-r--r--pearl.el8
-rw-r--r--tests/test-pearl-sort.el66
2 files changed, 74 insertions, 0 deletions
diff --git a/pearl.el b/pearl.el
index d2941aa..593cf47 100644
--- a/pearl.el
+++ b/pearl.el
@@ -6973,6 +6973,10 @@ buffer doesn't show."
(let ((ranks (pearl--issue-id-ranks result source)))
(pearl--merge-query-result result source)
(pearl--reorder-issue-subtrees nil 'asc ranks))
+ ;; The reorder rewrites the issue block with delete+insert, which
+ ;; drops the fold overlays and leaves every subtree expanded.
+ ;; Re-fold so the page stays the scannable outline it was.
+ (pearl--restore-page-visibility)
(pearl--write-linear-source-header source)
(message "Refetched %s ordered by %s %sending"
(pearl--source-name source) sort
@@ -7002,6 +7006,10 @@ Returns `client', `server', `grouped', or `no-issues'."
(message "No issues to sort in this buffer")
'no-issues)
(_
+ ;; The reorder rewrites the issue block with delete+insert, which
+ ;; drops the fold overlays and leaves every subtree expanded. Re-fold
+ ;; so the page stays the scannable outline it was before the sort.
+ (pearl--restore-page-visibility)
(pearl--write-linear-source-header updated)
(message "Sorted current buffer by %s %sending"
sort (if (eq order 'asc) "asc" "desc"))
diff --git a/tests/test-pearl-sort.el b/tests/test-pearl-sort.el
index 8833675..ffa15e8 100644
--- a/tests/test-pearl-sort.el
+++ b/tests/test-pearl-sort.el
@@ -312,5 +312,71 @@ would persist an unreadable #+LINEAR-SOURCE and break refresh/sort."
'updated 'asc)
(should (null (plist-get (pearl--read-active-source) :sort))))))
+;;; fold restoration after a reorder
+
+;; The reorder rewrites the issue block with delete+insert, which drops the
+;; fold/visibility overlays so every subtree comes back expanded. Both sort
+;; paths must re-fold afterward (pearl--restore-page-visibility) so the page
+;; stays the scannable, folded outline it was before the sort. A refusal
+;; (grouped buffer, failed refetch) moves nothing and must not re-fold.
+
+(ert-deftest test-pearl-apply-sort-client-restores-fold ()
+ "A successful client reorder re-folds the page."
+ (test-pearl-sort--in-org test-pearl-sort--flat
+ (let (restored)
+ (cl-letf (((symbol-function 'pearl--restore-page-visibility)
+ (lambda () (setq restored t))))
+ (should (eq (pearl--apply-sort
+ '(:type filter :name "My open issues" :filter (:open t))
+ 'priority 'asc)
+ 'client))
+ (should restored)))))
+
+(ert-deftest test-pearl-apply-sort-grouped-skips-fold-restore ()
+ "A grouped refusal moves nothing, so it does not re-fold."
+ (test-pearl-sort--in-org
+ (concat
+ "#+LINEAR-SOURCE: (:type filter :name \"x\" :filter nil)\n\n"
+ "* x\n"
+ "** Group\n"
+ "*** TODO [#A] SE-1: Alpha\n:PROPERTIES:\n:LINEAR-ID: i1\n:END:\n")
+ (let (restored)
+ (cl-letf (((symbol-function 'pearl--restore-page-visibility)
+ (lambda () (setq restored t))))
+ (pearl--apply-sort '(:type filter :name "x" :filter nil) 'priority 'asc)
+ (should-not restored)))))
+
+(ert-deftest test-pearl-apply-server-sort-ok-restores-fold ()
+ "A successful server refetch re-folds the reordered page."
+ (test-pearl-sort--in-org test-pearl-sort--flat
+ (let (restored)
+ (cl-letf (((symbol-function 'pearl--query-issues-async)
+ (lambda (_filter callback &optional _order-by)
+ (funcall callback '(:status ok :issues nil :truncated nil))))
+ ((symbol-function 'pearl--merge-query-result) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--progress) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--restore-page-visibility)
+ (lambda () (setq restored t))))
+ (pearl--apply-server-sort
+ '(:type filter :name "My open issues" :filter (:open t) :sort updated :order asc)
+ 'updated 'asc)
+ (should restored)))))
+
+(ert-deftest test-pearl-apply-server-sort-failure-skips-fold-restore ()
+ "A failed server refetch moves nothing, so it does not re-fold."
+ (test-pearl-sort--in-org test-pearl-sort--flat
+ (let (restored)
+ (cl-letf (((symbol-function 'pearl--query-issues-async)
+ (lambda (_filter callback &optional _order-by)
+ (funcall callback '(:status error :message "boom"))))
+ ((symbol-function 'pearl--merge-query-result) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--progress) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--restore-page-visibility)
+ (lambda () (setq restored t))))
+ (pearl--apply-server-sort
+ '(:type filter :name "My open issues" :filter (:open t) :sort updated :order asc)
+ 'updated 'asc)
+ (should-not restored)))))
+
(provide 'test-pearl-sort)
;;; test-pearl-sort.el ends here