Hi Everyone,

I found that my timvisher-org-refile-done-entries had a bug where it could skip entries if the org buffer had consecutive DONE/CANCELLED entries. I believe this is because of this quote from the manual:

After evaluation, Org moves point to the end of the line that was just processed. Search continues from that point forward. This may not always work as expected under some conditions, such as if the current sub-tree was removed by a previous archiving operation. In such rare circumstances, Org skips the next entry entirely when it should not. To stop Org from such skips, make FUNC set the variable ‘org-map-continue-from’ to a specific buffer position.

I've been doing some experimentation around how to fix this:

Attempt 1

This feels like the simplest and most naive approach:

(defun timvisher-org-refile-done-entry-position
    ()
  (save-excursion
    (goto-char (point-min))
    (- (re-search-forward "^\\* Done") 6)))

(defun timvisher-org-refile-done-entry
    ()
  (org-refile nil
              (current-buffer)
              (list "* Done"
                    (buffer-file-name)
                    nil
                    (timvisher-org-refile-done-entry-position))))

(defun timvisher-org-refile-done-entries
    ()
  (interactive)
  (while (< 0 (length (org-map-entries #'timvisher-org-refile-done-entry
                                       "LEVEL=2/+DONE|+CANCELLED"
                                       nil
                                       'archive)))))

Here we're literally just doing the org-map-entries operation forever until the operation finds nothing to operate on.

I worry about infinite loops and all that here though. It also just seems, well, a little brute force.

Attempt 2

(defun timvisher-org-refile-done-entry
    (point)
  (let ((done-entry-position (timvisher-org-refile-done-entry-position)))
    (when (< done-entry-position point)
      (error (concat "Refile target at %d is located after the Done "
                     "entry at %d. Move the Done entry to the bottom "
                     "of the file.")
             point
             done-entry-position))
    (goto-char point)
    (org-refile nil
                (current-buffer)
                (list "* Done"
                      (buffer-file-name)
                      nil
                      (timvisher-org-refile-done-entry-position)))))

(defun timvisher-org-refile-done-entries
    ()
  (interactive)
  (seq-map #'timvisher-org-refile-done-entry
           (reverse (org-map-entries #'point
                                     "LEVEL=2/+DONE|+CANCELLED"
                                     nil
                                     'archive))))

Here we're recognizing that if we could process the entries in reverse then we'd never have to worry about an entry moving from underneath us as we refile.

This also seems silly:

  1. I'm sure there's a function already that just returns me the matching entries for a given match search. Using the mapping API for seems incorrect.
  2. It requires that the Done entry be below all possible targets which I'd rather not require. My Done entry is typically above my deferred entries and I want to be able to mark deferred entries as cancelled or done and have this work properly.

Overall I actually like this attempt less than the first one.

Attempt 3

I'm convinced that there must be something I can do with the org-map-continue-from var but I'm having a lot of trouble grokking how to use it properly.

My initial attempt looks like this:

(defun timvisher-org-refile-done-entry
    ()
  (let ((entry-begin-point (point)))
    (org-refile nil
                (current-buffer)
                (list "* Done"
                      (buffer-file-name)
                      nil
                      (timvisher-org-refile-done-entry-position)))
    (setq org-map-continue-from entry-begin-point)))

(defun timvisher-org-refile-done-entries
    ()
  (interactive)
  (org-map-entries #'timvisher-org-refile-done-entry
                   "LEVEL=2/+DONE|+CANCELLED"
                   nil
                   'archive))

The idea being that if I do in fact refile an entry I'd actually like the 'search to continue' from where it just was. This 'works' in that it successfully refiles all the entries even if they're consecutive but it also manages to get into an infinite loop because, AFAICT, it ignores the 'archive skipping somehow. I've spent some time in the debugger and I can't figure out yet why it does that.

Any tips on how I might use this variable to achieve my goals?

Thanks in advance!

--

In Christ,

Timmy V.

https://blog.twonegatives.com
http://five.sentenc.es