aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pearl.el44
-rw-r--r--tests/test-pearl-sort.el10
2 files changed, 46 insertions, 8 deletions
diff --git a/pearl.el b/pearl.el
index 945c9af..1cf4b53 100644
--- a/pearl.el
+++ b/pearl.el
@@ -6813,13 +6813,18 @@ order. The keyed sort is stable, so equal keys keep document order."
(ordered (cl-stable-sort (copy-sequence keyed) cmp)))
(append ordered unkeyed))))
-(defun pearl--reorder-issue-subtrees (sort order)
+(defun pearl--reorder-issue-subtrees (sort order &optional id-ranks)
"Reorder the active view's issue subtrees in place by SORT in ORDER.
Moves whole subtrees byte-for-byte -- descriptions, comments, drawers, and any
unsaved local edits survive unchanged -- rather than reconstructing them from
parsed data. A non-issue level-2 subtree (no `LINEAR-ID') is kept and sorted
last. Returns t on success, `grouped' when issues sit below level 2 (a grouped
-view, unsupported in v1), or `no-issues' when there are none."
+view, unsupported in v1), or `no-issues' when there are none.
+
+ID-RANKS, when non-nil, is a hash mapping `LINEAR-ID' to an integer position;
+each subtree is keyed by its rank and laid out ascending, so the buffer matches
+a server-fetched order (SORT/ORDER are then ignored). This is how the
+server-side sort reorders the merged buffer without losing local edits."
(save-excursion
(let ((markers (pearl--issue-subtree-markers)))
(cond
@@ -6845,7 +6850,8 @@ view, unsupported in v1), or `no-issues' when there are none."
(end (save-excursion (org-end-of-subtree t t) (point)))
(id (org-entry-get nil "LINEAR-ID"))
(key (and id (not (string-empty-p id))
- (pearl--issue-sort-key sort)))
+ (if id-ranks (gethash id id-ranks)
+ (pearl--issue-sort-key sort))))
(text (buffer-substring-no-properties beg end)))
(unless block-beg (setq block-beg beg))
(setq block-end end)
@@ -6854,7 +6860,8 @@ view, unsupported in v1), or `no-issues' when there are none."
(goto-char end))
(goto-char (save-excursion (org-end-of-subtree t t) (point)))))
(when (and block-beg block-end)
- (let ((sorted (pearl--sort-subtree-entries (nreverse children) order)))
+ (let ((sorted (pearl--sort-subtree-entries
+ (nreverse children) (if id-ranks 'asc order))))
(delete-region block-beg block-end)
(goto-char block-beg)
(insert (mapconcat (lambda (c) (plist-get c :text)) sorted ""))))
@@ -6877,11 +6884,30 @@ toggle until you sort it."
(cons 'updated 'desc)
(cons s (if (eq o 'asc) 'desc 'asc))))))
+(defun pearl--issue-id-ranks (result source)
+ "Build a `LINEAR-ID' -> position hash from RESULT, in SOURCE's sort order.
+The fetched nodes are normalized and sorted the same way the render path sorts
+them, so the hash captures the order the server returned for the new `orderBy'."
+ (let* ((eff (pearl--effective-sort-order source))
+ (issues (pearl--sort-issues
+ (mapcar #'pearl--normalize-issue
+ (pearl--query-result-issues result))
+ (car eff) (cdr eff)))
+ (ranks (make-hash-table :test 'equal))
+ (i 0))
+ (dolist (issue issues)
+ (puthash (plist-get issue :id) i ranks)
+ (setq i (1+ i)))
+ ranks))
+
(defun pearl--apply-server-sort (source sort order)
"Refetch SOURCE (a filter) with the server `orderBy' for SORT, then persist.
-On success merges the result and writes the updated `#+LINEAR-SOURCE' header; on
-an empty/failed fetch it leaves the header unchanged and reports the merge
-outcome, so a failed sort never advertises an order the buffer doesn't show."
+Merges the result (so unpushed edits survive), reorders the buffer's issue
+subtrees to the fetched order -- a merge alone updates issues in place and never
+moves them, so the new order would be invisible -- and writes the updated
+`#+LINEAR-SOURCE' header. An empty/failed fetch leaves the header unchanged and
+just reports the merge outcome, so a failed sort never advertises an order the
+buffer doesn't show."
(let ((buffer (current-buffer)))
(pearl--progress "Refetching %s ordered by %s %s..."
(pearl--source-name source) sort order)
@@ -6892,7 +6918,9 @@ outcome, so a failed sort never advertises an order the buffer doesn't show."
(with-current-buffer buffer
(pcase (pearl--query-result-status result)
('ok
- (pearl--merge-query-result result source)
+ (let ((ranks (pearl--issue-id-ranks result source)))
+ (pearl--merge-query-result result source)
+ (pearl--reorder-issue-subtrees nil 'asc ranks))
(pearl--write-linear-source-header source)
(message "Refetched %s ordered by %s %sending"
(pearl--source-name source) sort
diff --git a/tests/test-pearl-sort.el b/tests/test-pearl-sort.el
index 6527989..a5292b3 100644
--- a/tests/test-pearl-sort.el
+++ b/tests/test-pearl-sort.el
@@ -191,6 +191,16 @@ leaving its server :sort (an IssueSortInput) untouched."
(goto-char (point-min))
(should (search-forward "A stray local heading" nil t))))
+(ert-deftest test-pearl-reorder-by-id-ranks ()
+ "With an id-rank hash, subtrees are laid out in the ranked order (server sort)."
+ (test-pearl-sort--in-org test-pearl-sort--flat
+ (let ((ranks (make-hash-table :test 'equal)))
+ ;; Rank i1 before i2, the reverse of the buffer's i2,i1 order.
+ (puthash "i1" 0 ranks)
+ (puthash "i2" 1 ranks)
+ (should (eq (pearl--reorder-issue-subtrees nil 'asc ranks) t))
+ (should (equal (test-pearl-sort--issue-ids) '("i1" "i2"))))))
+
;;; toggle target
(ert-deftest test-pearl-toggle-target-filter-no-sort-defaults ()