Hi again,

I solved this for myself overriding org-map-entries. Implementing it was simpler than expected. Let's see if we can agree on how to get this into the core.

See attached a patch with a proposal to open the discussion. Sorry if I did a mess with indenting the function, which has tabs and spaces altogether.

I needed to add a save-excursion-window function to avoid being in another file [1] after executing the function org-map-entries. I put it close to the save-excursion function. I hope that does not break anything else in the function.

Next is just a fun-call method, and here we could go for two ways:

Option 1 (what is proposed in the patch). more user friendly but more restricted     Do the org-link-open-from-string inside the function and prepare to parse the subtree, easier to use, of course, limited to that. But org-link is very extensible itself. [2]

Option 2. less user friendly but more freedom
    Just do the funcall (then, less lines). In the docstring we could add an example of how to use it (example where a long example is provided with such freedom, see var calendar-intermonth-text)     This is, by the way, more similar to how works file+function (but without the file argument) in org-capture [3]

I think Option 1 it is better for completeness and consistency.

With this, we got a powerful and extensible way to get to "any subtree of any file", but just one file. For those who might need it: how to extend something like this for multiple files? I would not like to do this, but just to let you know future work or how to continue. We evaluate the function and we expect a string, if we have a list instead, then, this might be handled with a loop to get all the strings inside the list.



This is by the way, an approach to speedup processing on certain scenarios: when all data is in a subtree inside a file


Cheers,
pinmacs


[1] Because in my scenario I have a file named "queries" which contain some propview blocks; the data-diary is in another one

[2]
https://orgmode.org/guide/Hyperlinks.html
https://orgmode.org/manual/Handling-Links.html

[3] https://orgmode.org/manual/Template-elements.html
From aff7c87d7782145131e05e28ed6021ca28754f1c Mon Sep 17 00:00:00 2001
From: pinmacs <pinm...@cas.cat>
Date: Wed, 20 Aug 2025 13:03:00 +0200
Subject: [PATCH] org-map-entries: add scope function

---
 lisp/org/org.el | 115 +++++++++++++++++++++++++-----------------------
 1 file changed, 61 insertions(+), 54 deletions(-)

diff --git a/lisp/org/org.el b/lisp/org/org.el
index 9051bf6c8c9..ba92d34d842 100644
--- a/lisp/org/org.el
+++ b/lisp/org/org.el
@@ -12615,60 +12615,67 @@ org-map-entries
        (t (setq matcher (if match (cdr (org-make-tags-matcher match)) t))))
 
       (save-excursion
-	(save-restriction
-	  (cond ((eq scope 'tree)
-		 (org-back-to-heading t)
-		 (org-narrow-to-subtree)
-		 (setq scope nil))
-		((and (or (eq scope 'region) (eq scope 'region-start-level))
-		      (org-region-active-p))
-		 ;; If needed, set start-level to a string like "2"
-		 (when start-level
-		   (save-excursion
-		     (goto-char (region-beginning))
-		     (unless (org-at-heading-p) (outline-next-heading))
-		     (setq start-level (org-current-level))))
-		 (narrow-to-region (region-beginning)
-				   (save-excursion
-				     (goto-char (region-end))
-				     (unless (and (bolp) (org-at-heading-p))
-				       (outline-next-heading))
-				     (point)))
-		 (setq scope nil)))
-
-	  (if (not scope)
-	      (progn
-                ;; Agenda expects a file buffer.  Skip over refreshing
-                ;; agenda cache for non-file buffers.
-                (when buffer-file-name
-		  (org-agenda-prepare-buffers
-		   (and buffer-file-name (list (current-buffer)))))
-		(setq res
-		      (org-scan-tags
-		       func matcher org--matcher-tags-todo-only start-level)))
-	    ;; Get the right scope
-	    (cond
-	     ((and scope (listp scope) (symbolp (car scope)))
-	      (setq scope (eval scope t)))
-	     ((eq scope 'agenda)
-	      (setq scope (org-agenda-files t)))
-	     ((eq scope 'agenda-with-archives)
-	      (setq scope (org-agenda-files t))
-	      (setq scope (org-add-archive-files scope)))
-	     ((eq scope 'file)
-	      (setq scope (and buffer-file-name (list buffer-file-name))))
-	     ((eq scope 'file-with-archives)
-	      (setq scope (org-add-archive-files (list (buffer-file-name))))))
-	    (org-agenda-prepare-buffers scope)
-	    (dolist (file scope)
-	      (with-current-buffer (org-find-base-buffer-visiting file)
-		(org-with-wide-buffer
-		 (goto-char (point-min))
-		 (setq res
-		       (append
-			res
-			(org-scan-tags
-			 func matcher org--matcher-tags-todo-only)))))))))
+        (save-window-excursion
+	  (save-restriction
+	    (cond ((eq scope 'tree)
+		   (org-back-to-heading t)
+		   (org-narrow-to-subtree)
+		   (setq scope nil))
+		  ((functionp scope)
+		   (let ((result (funcall scope)))
+		     (org-link-open-from-string result))
+		   (org-back-to-heading t)
+		   (org-narrow-to-subtree)
+		   (setq scope nil))
+		  ((and (or (eq scope 'region) (eq scope 'region-start-level))
+		        (org-region-active-p))
+		   ;; If needed, set start-level to a string like "2"
+		   (when start-level
+		     (save-excursion
+		       (goto-char (region-beginning))
+		       (unless (org-at-heading-p) (outline-next-heading))
+		       (setq start-level (org-current-level))))
+		   (narrow-to-region (region-beginning)
+				     (save-excursion
+				       (goto-char (region-end))
+				       (unless (and (bolp) (org-at-heading-p))
+				         (outline-next-heading))
+				       (point)))
+		   (setq scope nil)))
+
+	    (if (not scope)
+	        (progn
+                  ;; Agenda expects a file buffer.  Skip over refreshing
+                  ;; agenda cache for non-file buffers.
+                  (when buffer-file-name
+		    (org-agenda-prepare-buffers
+		     (and buffer-file-name (list (current-buffer)))))
+		  (setq res
+		        (org-scan-tags
+		         func matcher org--matcher-tags-todo-only start-level)))
+	      ;; Get the right scope
+	      (cond
+	       ((and scope (listp scope) (symbolp (car scope)))
+	        (setq scope (eval scope t)))
+	       ((eq scope 'agenda)
+	        (setq scope (org-agenda-files t)))
+	       ((eq scope 'agenda-with-archives)
+	        (setq scope (org-agenda-files t))
+	        (setq scope (org-add-archive-files scope)))
+	       ((eq scope 'file)
+	        (setq scope (and buffer-file-name (list buffer-file-name))))
+	       ((eq scope 'file-with-archives)
+	        (setq scope (org-add-archive-files (list (buffer-file-name))))))
+	      (org-agenda-prepare-buffers scope)
+	      (dolist (file scope)
+	        (with-current-buffer (org-find-base-buffer-visiting file)
+		  (org-with-wide-buffer
+		   (goto-char (point-min))
+		   (setq res
+		         (append
+			  res
+			  (org-scan-tags
+			   func matcher org--matcher-tags-todo-only))))))))))
       res)))
 
 ;;; Properties API
-- 
2.39.5

Reply via email to