The current logic of org-map-region uses two searches.

In the first, the point is left after the asterisks of the heading.
In the subsequent calls, the point is correctly set to the first
asterisk of the headline (this is done by outline-next-heading).

Make sure the first header is processed the same way as the rest.

  * lisp/org.el (org-map-region): Add a call to goto-char to go
  to the beginning of the match after the first search.

  * testing/lisp/test-org.el (test-org/map-region): add tests
  for org-map-region
---
 lisp/org.el              |  5 +++-
 testing/lisp/test-org.el | 49 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/lisp/org.el b/lisp/org.el
index a98adf927..2aaaa50ef 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -7007,13 +7007,16 @@ After top level, it switches back to sibling level."
        (funcall fun)))))
 
 (defun org-map-region (fun beg end)
-  "Call FUN for every heading between BEG and END."
+  "Call FUN for every heading between BEG and END.
+The point is placed at the beginning of each heading
+(including any *) before FUN is called."
   (let ((org-ignore-region t))
     (save-excursion
       (setq end (copy-marker end))
       (goto-char beg)
       (when (and (re-search-forward org-outline-regexp-bol nil t)
                 (< (point) end))
+        (goto-char (match-beginning 0))
        (funcall fun))
       (while (and (progn
                    (outline-next-heading)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 36dea35b7..fc5b9345c 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -3161,6 +3161,55 @@ Let’s stop here
       (should (equal '("two")
                      (org-element-cache-map (lambda (el) (org-element-property 
:title el)) :next-re "TODO"))))))
 
+(ert-deftest test-org/map-region ()
+  "Test `org-map-region'."
+
+  (let
+      ;; org-map-region does not return anything so we need to
+      ;; wrap it in a function that saves the return value
+      ;; of the function applied to each header, and returns
+      ;; the results of applying such function as a list
+      ((org-map-region-with-results (lambda (fn)
+                                      (let (results)
+                                        (org-map-region
+                                         (lambda ()
+                                           (let ((result (funcall fn)))
+                                             (push result results)))
+                                         (point-min) (point-max))
+                                        (nreverse results)))))
+
+  (dolist (org-element-use-cache '(t nil))
+    ;; Full match.
+    (should
+     (equal (list 1 11)
+           (org-test-with-temp-text "* Level 1\n** Level 2"
+                                    (org-map-region-with-results #'point))))
+    (should
+     (equal (list 3 23)
+           (org-test-with-temp-text "\n\n* Level 1\nSome text\n** Level 2"
+                                    (org-map-region-with-results #'point))))
+    (should
+     (equal (list 2 45 88 127 166)
+            (org-test-with-temp-text "
+* H1 :BAR:
+:PROPERTIES:
+:TEST-FOO: 1
+:END:
+* H2 :FOO:
+:PROPERTIES:
+:TEST-FOO: 2
+:END:
+* H3 :BAR:
+:PROPERTIES:
+:-FOO: 1
+:END:
+* H4 :FOO:
+:PROPERTIES:
+:-FOO: 2
+:END:
+* H5 :TEST:"
+                                     (org-map-region-with-results 
#'point)))))))
+
 (ert-deftest test-org/edit-headline ()
   "Test `org-edit-headline' specifications."
   (should
-- 
2.43.0


Reply via email to