Ihor Radchenko <[email protected]> writes:
> Björn Kettunen <[email protected]> writes:
>
>>> Then, you can additionally introduce something like
>>> (file-with-archives "file1" "file2" ...). If you want to implement such
>>> feature, it is probably best to combine this patch and the extra addition.
>>
>> Thats's a good I think I have a patch.
>> How is the multifile parameter supposed be handled in such an instance?
>> I made it so that in case the parameter was consp that it uses the
>> multifile parameter.
>
> That makes sense.
>
>> Ideally I would like that similar to single file-with-archices that
>> archive and the their files are shown as one file.
>> But not sure how to do that.
>
> That's possible to do by combining the results coming from file and its
> archives together.
How would you do this?
>> With the current patch now the archive handling is moved to be done
>> after the files have been expanded so it's just called once for all
>> cases.
>> The side effect is that if a user uses a function it can now also return
>> the scope and let org-mode expand the returned scope if it isn't already
>> expanded.
>
> This should be documented.
>
>> * doc/org-manual.org: Document.
>> * etc/ORG-NEWS: Announce
Will do.
> You need to document the extra features you added.
>
>> - ((or `nil `file `subtree `tree
>> + ((or `nil `file `subtree `tree `file-with-archives
>> (and (pred symbolp)
>> + (pred (not symbolp))
>
> What is the purpose of this? It will always evaluate to nil.
that's a leftover of debugging removed.
Here's my updated patch:
>From b7c690a70a3f4676cffd91895858224072d3815c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Kettunen?= <[email protected]>
Date: Tue, 3 Mar 2026 22:47:12 +0200
Subject: [PATCH 1/2] org-clock: clock-report be able to also take directories
* lisp/org-clock.el (org-dblock-write:clocktable): Expand
files in directories if any of the entries in scope is a directory.
Just like in org-agenda-files.
* lisp/org.el (org-file-list-expand):
(org-agenda-files): Refactor file expansion into separate function.
* doc/org-manual.org: Document.
* etc/ORG-NEWS: Announce
---
doc/org-manual.org | 2 +-
etc/ORG-NEWS | 9 +++++++++
lisp/org-clock.el | 46 +++++++++++++++++++++++++++-------------------
lisp/org.el | 20 +++++++++++++-------
4 files changed, 50 insertions(+), 27 deletions(-)
diff --git a/doc/org-manual.org b/doc/org-manual.org
index 9c4c27877..cfcf8994b 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -7109,7 +7109,7 @@ *** The clock table
| =treeN= | the surrounding level N tree, for example =tree3= |
| =tree= | the surrounding level 1 tree |
| =agenda= | all agenda files |
- | =("file" ...)= | scan these files |
+ | =("file" "dir" "...)= | scan these files or files in directories |
| =FUNCTION= | scan files returned by calling {{{var(FUNCTION)}}} with no argument |
| =file-with-archives= | current file and its archives |
| =agenda-with-archives= | all agenda files, including archives |
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 40fa1e6aa..ca18ab041 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -40,6 +40,11 @@ into a "Tags" section and a "Priorities" section.
Priorities can now be increased, decreased, set to the default, and
set interactively from the priority context menus.
+*** Clocktable option =:scope (file)= list of files can now also include directories
+
+The scope option can now also include directories in the list of files.
+Files are resolved just like in ~org-agenda-files~.
+
** New and changed options
# Changes dealing with changing default values of customizations,
@@ -60,6 +65,10 @@ variable.
Given the completed and total number of tasks, format the percent
cookie =[N%]=.
+*** New function ~org-agenda-directory-files-recursively~
+
+Expand list of flies according to ~org-agenda-file-regexp~.
+
** Removed or renamed functions and variables
** Miscellaneous
diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index ce2d23a9b..4d8abc04d 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -2677,22 +2677,19 @@ (defun org-dblock-write:clocktable (params)
(catch 'exit
(let* ((scope (plist-get params :scope))
(base-buffer (org-base-buffer (current-buffer)))
+ (scope (or (and (functionp scope)
+ (funcall scope))
+ scope))
(files (pcase scope
- (`agenda
+ ((or `agenda `agenda-with-archives)
(org-agenda-files t))
- (`agenda-with-archives
- (org-add-archive-files (org-agenda-files t)))
- (`file-with-archives
- (let ((base-file (buffer-file-name base-buffer)))
- (and base-file
- (org-add-archive-files (list base-file)))))
- ((or `nil `file `subtree `tree
+ ((or `nil `file `subtree `tree `file-with-archives
(and (pred symbolp)
(guard (string-match "\\`tree\\([0-9]+\\)\\'"
(symbol-name scope)))))
- base-buffer)
- ((pred functionp) (funcall scope))
+ (list (buffer-file-name base-buffer)))
((pred consp) scope)
+ ((pred stringp) scope) ;; To not break previous function calls here
(_ (user-error "Unknown scope: %S" scope))))
(block (plist-get params :block))
(ts (plist-get params :tstart))
@@ -2704,7 +2701,25 @@ (defun org-dblock-write:clocktable (params)
(formatter (or (plist-get params :formatter)
org-clock-clocktable-formatter
'org-clocktable-write-default))
- cc)
+ (multifile
+ ;; Even though `file-with-archives' can consist of
+ ;; multiple files, we consider this is one extended file
+ ;; instead.
+ (and (not hide-files)
+ (consp files)
+ (not (eq scope 'file-with-archives))))
+ cc)
+
+ (when (consp files)
+ (when-let* ((cons-scope (car files))
+ (cons-scope (and (symbolp cons-scope)
+ cons-scope)))
+ (setq scope cons-scope)
+ (setq files (cdr files)))
+ (setq files (org-agenda-directory-files-recursively files)))
+ (pcase scope ((or `agenda-with-archives `file-with-archives)
+ (setq files (org-add-archive-files files))))
+
;; Check if we need to do steps
(when block
;; Get the range text for the header
@@ -2750,14 +2765,7 @@ (defun org-dblock-write:clocktable (params)
level)
(throw 'exit nil))))
(org-narrow-to-subtree))))
- (list (org-clock-get-table-data nil params)))))
- (multifile
- ;; Even though `file-with-archives' can consist of
- ;; multiple files, we consider this is one extended file
- ;; instead.
- (and (not hide-files)
- (consp files)
- (not (eq scope 'file-with-archives)))))
+ (list (org-clock-get-table-data nil params))))))
(funcall formatter
origin
diff --git a/lisp/org.el b/lisp/org.el
index fc51d4ba3..05607bd2c 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -15977,6 +15977,18 @@ (defun org-switchb (&optional arg)
(mapcar #'list (mapcar #'buffer-name blist))
nil t))))
+
+(defun org-agenda-directory-files-recursively (files)
+ "Expand list of FILES according to `org-agenda-file-regexp'."
+ (apply 'append
+ (mapcar (lambda (f)
+ (if (file-directory-p f)
+ (directory-files
+ f t org-agenda-file-regexp)
+ (list (expand-file-name f org-directory))))
+ files)))
+
+
(defun org-agenda-files (&optional unrestricted archives)
"Get the list of agenda files.
Optional UNRESTRICTED means return the full list even if a restriction
@@ -15990,13 +16002,7 @@ (defun org-agenda-files (&optional unrestricted archives)
((stringp org-agenda-files) (org-read-agenda-file-list))
((listp org-agenda-files) org-agenda-files)
(t (error "Invalid value of `org-agenda-files'")))))
- (setq files (apply 'append
- (mapcar (lambda (f)
- (if (file-directory-p f)
- (directory-files
- f t org-agenda-file-regexp)
- (list (expand-file-name f org-directory))))
- files)))
+ (setq files (org-agenda-directory-files-recursively files))
(when org-agenda-skip-unavailable-files
(setq files (delq nil
(mapcar (lambda (file)
--
2.53.0
Some tests still fail I haven't figured out why.
Here's an example test that fails:
Updating dynamic block ‘clocktable’ at line 1...
Test test-org-clock/clocktable/compact backtrace:
signal(wrong-type-argument (stringp nil))
apply(signal (wrong-type-argument (stringp nil)))
(setq value-9617 (apply fn-9615 args-9616))
(unwind-protect (setq value-9617 (apply fn-9615 args-9616)) (setq fo
(if (unwind-protect (setq value-9617 (apply fn-9615 args-9616)) (set
(let (form-description-9619) (if (unwind-protect (setq value-9617 (a
(let ((value-9617 'ert-form-evaluation-aborted-9618)) (let (form-des
(let* ((fn-9615 #'equal) (args-9616 (condition-case err (list "| Hea
#f(lambda () [t] (let* ((fn-9615 #'equal) (args-9616 (condition-case
#f(compiled-function () #<bytecode -0x367f8568e19929>)()
handler-bind-1(#f(compiled-function () #<bytecode -0x367f8568e19929>
ert--run-test-internal(#s(ert--test-execution-info :test #s(ert-test
ert-run-test(#s(ert-test :name test-org-clock/clocktable/compact :do
ert-run-or-rerun-test(#s(ert--stats :selector "\\(org\\|ob\\|ox\\)"
ert-run-tests("\\(org\\|ob\\|ox\\)" #f(compiled-function (event-type
ert-run-tests-batch("\\(org\\|ob\\|ox\\)")
ert-run-tests-batch-and-exit("\\(org\\|ob\\|ox\\)")
(let ((org-id-track-globally t) (org-test-selector (if org-test-sele
org-test-run-batch-tests("\\(org\\|ob\\|ox\\)")
eval((org-test-run-batch-tests org-test-select-re) t)
command-line-1(("--eval" "(setq vc-handled-backends nil org-startup-
command-line()
normal-top-level()