diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-05 01:38:35 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-05 01:38:35 -0500 |
| commit | 5940d7caf21f5c2b8e322a67f32657dace56fc9a (patch) | |
| tree | d5eb951bed71e2e25c7e45bd14e0fa5605e1c7ff /pearl.el | |
| parent | 3f491e73cdbdc03d652bce81fdfc68eabb313213 (diff) | |
| download | pearl-5940d7caf21f5c2b8e322a67f32657dace56fc9a.tar.gz pearl-5940d7caf21f5c2b8e322a67f32657dace56fc9a.zip | |
fix(views): make a server-side sort actually reorder the buffer
A server-side sort (updated/created) refetched with the new orderBy and merged the result, but the merge updates issues in place by id and never moves them, so the buffer kept its old order and the sort had no visible effect. I caught it dogfooding live: sorting My Open Issues updated-ascending then updated-descending rendered an identical order.
After the refetch I now reorder the buffer's issue subtrees to the fetched order. pearl--issue-id-ranks builds a LINEAR-ID to position map from the sorted result, and pearl--reorder-issue-subtrees takes an optional id-ranking and lays the subtrees out by it. The move is the same byte-for-byte subtree shuffle the client-side sort already uses, so unpushed edits survive. Re-checked live: updated-ascending now comes back the exact reverse of updated-descending.
Diffstat (limited to 'pearl.el')
| -rw-r--r-- | pearl.el | 44 |
1 files changed, 36 insertions, 8 deletions
@@ -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 |
