Hello Ihor,
Thanks for the review!
I've made the adjustments you've asked for.
Tests pass at every step in the patch series on emacs 30.2 (TZ="UTC").
Testing the series as a whole, the tests pass on emacs 28, 29, and 30.2
(TZ="UTC").
Also tested the final commit with emacs 30.2 with TZ="Europe/Istanbul"
and TZ="America/New_York".
I also fixed one other bug I found. In the previous patch series,
`test-org-clock/clocktable/inlinetask/insert' ended up with two columns
after the final patch when it should have had three. This took me
forever to track down. Turns out `org-clock-sum' must always return an
integer, not a float so the following fixed it
#+begin_src diff
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -2104,7 +2104,7 @@ org-clock-sum
(floor (org-time-convert-to-integer
(time-since org-clock-start-time))
60)))
- ((pred floatp) range)
+ ((pred floatp) (floor range))
(`(,time1 . ,time2)
(let* ((ts (float-time time1))
(te (float-time time2))
#+end_src
Ihor Radchenko <[email protected]> writes:
> Morgan Smith <[email protected]> writes:
>
>> + (dolist (org-clock-report-include-clocking-task '(nil t))
>> + (dolist (actually-clock-in '(nil t))
>> + ;; Without leading characters then `org-clock-hd-marker' doesn't
>> + ;; get updated when clocktable is inserted and test fails.
>
> Maybe worth marking this with FIXME:
> Also, you can try fixing this bug.
> See 32.5 Marker Insertion Types and `org-agenda-new-marker'.
Marked it. I've looked into this previously but it is currently low on the
priority list
>> + (unless (org-element-type-p headline '(headline inlinetask))
>> + (error "Argument must be a headline"))
>
> ... or inlinetask.
Fixed it.
>> + (and
>> + (org-element-contents-begin headline) ;; empty headline
>
> Maybe non-empty?
>
Changed to:
+ (and
+ (org-element-contents-begin headline) ;; nil for empty headlines
It's to weed out empty headlines. Lower down in the :to-pos argument I
use the contents-begin so this prevents an error
>> + ;; XXX: using these arguments would be more intuitive
>
> FIXME:
Done.
>> Subject: [PATCH 11/12] Rewrite `org-clock-get-table-data' using org-element
>> ...
>> + (org-element-cache-map
>
> This will likely slow things down.
> The original code uses `next-single-property-change' which will
> automatically skip headings that do not contain clocks (as determine by
> `org-clock-sum'). The new code will again go through every single
> headline and inlinetask.
I put back the `next-single-property-change' stuff.
>> + (lambda (elm)
>> + (when-let*
>> + ((time
>> + (let ((time (get-text-property (org-element-begin elm)
>> + :org-clock-minutes)))
>> + (when (and time (> time 0))
>> + time)))
>
> Can simply do
> (time (get-text-property ...))
> (time (when (> time 0) time))
That's so clean! Thank you! I was not pleased with how I did it before.
>> + (list level
>> + title
>> + (and tags (org-element-property :tags elm))
>
> This is not quite the same as `org-get-tags' (without LOCAL argument).
Switched it back to `org-get-tags'.
>> + (and timestamp
>> + (cl-some
>> + (lambda (p) (org-entry-get (org-element-begin elm) p))
>
> You can pass ELM directly to `org-entry-get'.
Thanks! Done!
>> Subject: [PATCH 12/12] Clocktables: Indent inline tasks under their heading
>>
>> * lisp/org-clock.el (lisp/org-clock.el): Inrease the level of inline
>> tasks by 1.
>> * testing/lisp/test-org-clock.el
>> (test-org-clock/clocktable/inlinetask/insert)
>> (test-org-clock/clocktable/inlinetask/open-clock): Adjust tests to new
>> behavior.
>
> Please explain the rationale for the new behavior in the commit
> message. Also, it is a user-facing change (table presentation) and
> should be announced in ORG-NEWS.
Done!
>From 8e5760421e11c17a4a96fb6604ead7a0066f36d9 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Wed, 17 Apr 2024 17:51:35 -0400
Subject: [PATCH 01/12] Testing: Test clock times without timestamps
* testing/lisp/test-org-clock.el (test-org-clock/clocktable/insert):
Add a clock time that does not include timestamps.
---
testing/lisp/test-org-clock.el | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 8a196ee96..62e4d7507 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -345,13 +345,12 @@ test-org-clock/clocktable/insert
(equal
"| Headline | Time |
|--------------+--------|
-| *Total time* | *1:00* |
+| *Total time* | *2:00* |
|--------------+--------|
-| H1 | 1:00 |"
+| H1 | 2:00 |"
(org-test-with-temp-text "* H1\n<point>"
- (insert (org-test-clock-create-clock ". 1:00" ". 2:00"))
-
- (goto-line 2)
+ (insert (org-test-clock-create-clock ". 1:00" ". 2:00")
+ "CLOCK: => 1:00\n")
(require 'org-clock)
(org-dynamic-block-insert-dblock "clocktable")
base-commit: fb550976faa6b3330ca4381026bd9f3adda2da13
--
2.51.0
>From 2031a53e25cba504b5c5dbaa63525fa162fffeeb Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Wed, 8 May 2024 10:36:07 -0400
Subject: [PATCH 02/12] Testing: New test test-org-clock/clocktable/open-clock
* testing/lisp/test-org-clock.el
(test-org-clock/clocktable/open-clock): New test.
---
testing/lisp/test-org-clock.el | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 62e4d7507..84fcdeb2f 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -362,6 +362,40 @@ test-org-clock/clocktable/insert
(point) (progn (search-forward "#+END:") (line-end-position 0))))
(delete-region (point) (search-forward "#+END:\n")))))))
+(ert-deftest test-org-clock/clocktable/open-clock ()
+ "Test open clocks.
+Open clocks should be ignored unless it is clocked in and
+`org-clock-report-include-clocking-task' is t."
+ (let ((time-reported "| Headline | Time |
+|--------------+--------|
+| *Total time* | *1:00* |
+|--------------+--------|
+| H1 | 1:00 |")
+ (time-not-reported "| Headline | Time |
+|--------------+--------|
+| *Total time* | *0:00* |"))
+ (dolist (org-clock-report-include-clocking-task '(nil t))
+ (dolist (actually-clock-in '(nil t))
+ ;; FIXME: Without leading characters then
+ ;; `org-clock-hd-marker' doesn't get updated when clocktable
+ ;; is inserted and test fails.
+ (org-test-with-temp-text "\n*<point> H1\n"
+ (should
+ (equal
+ (if (and org-clock-report-include-clocking-task
+ actually-clock-in)
+ time-reported
+ time-not-reported)
+ (progn
+ (if actually-clock-in
+ (org-clock-in nil (- (float-time) (* 60 60)))
+ (goto-char (point-max))
+ (insert (org-test-clock-create-clock "-1h")))
+ ;; Unless tstart and tend are fully specified it doesn't work
+ (test-org-clock-clocktable-contents ":tstart \"<-2d>\" :tend \"<tomorrow>\""))))
+ (when actually-clock-in
+ (org-clock-cancel)))))))
+
(ert-deftest test-org-clock/clocktable/ranges ()
"Test ranges in Clock table."
;; Relative time: Previous two days.
--
2.51.0
>From 35e5dfd24288c2562c8f2ad065b87b295033fc4c Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Sat, 9 Aug 2025 14:04:07 -0400
Subject: [PATCH 03/12] Testing: Add tests for clocktables from inline tasks
* testing/lisp/test-org-clock.el
(test-org-clock/clocktable/inlinetask/insert)
(test-org-clock/clocktable/inlinetask/no-heading)
(test-org-clock/clocktable/inlinetask/open-clock): New tests
---
testing/lisp/test-org-clock.el | 81 ++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 84fcdeb2f..59cbfed8b 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -1498,5 +1498,86 @@ test-org-clock/special-range
cases))))
(should-not failed)))
+;;; Inline tasks clocktable
+
+(require 'org-inlinetask)
+
+(ert-deftest test-org-clock/clocktable/inlinetask/insert ()
+ "Test insert clocktable on an inline task."
+ (should
+ (equal
+ "| Headline | Time |
+|--------------+--------|
+| *Total time* | *2:00* |
+|--------------+--------|
+| H1 | 2:00 |
+| I | 2:00 |"
+ (let ((org-inlinetask-min-level 5))
+ (org-test-with-temp-text "* H1
+***** I
+<point>
+***** END
+foo"
+ (insert (org-test-clock-create-clock ". 1:00" ". 2:00")
+ "CLOCK: => 1:00\n")
+ (test-org-clock-clocktable-contents ""))))))
+
+(ert-deftest test-org-clock/clocktable/inlinetask/no-heading ()
+ "Test insert clocktable on an inline task not under a heading."
+ ;; (wrong-type-argument number-or-marker-p nil)
+ :expected-result :failed
+ (should
+ (equal
+ "| Headline | Time |
+|--------------+--------|
+| *Total time* | *2:00* |
+|--------------+--------|
+| I | 2:00 |"
+ (let ((org-inlinetask-min-level 5))
+ (org-test-with-temp-text "***** I
+<point>
+***** END
+foo"
+ (insert (org-test-clock-create-clock ". 1:00" ". 2:00")
+ "CLOCK: => 1:00\n")
+ (test-org-clock-clocktable-contents ""))))))
+
+(ert-deftest test-org-clock/clocktable/inlinetask/open-clock ()
+ "Test open clocks on an inline task.
+Open clocks should be ignored unless it is clocked in and
+`org-clock-report-include-clocking-task' is t."
+ (let ((time-reported "| Headline | Time |
+|--------------+--------|
+| *Total time* | *1:00* |
+|--------------+--------|
+| H1 | 1:00 |
+| I | 1:00 |")
+ (time-not-reported "| Headline | Time |
+|--------------+--------|
+| *Total time* | *0:00* |")
+ (org-inlinetask-min-level 5))
+ (dolist (org-clock-report-include-clocking-task '(nil t))
+ (dolist (actually-clock-in '(nil t))
+ (org-test-with-temp-text
+ "* H1
+***** I
+<point>
+***** END
+foo"
+ (should
+ (equal
+ (if (and org-clock-report-include-clocking-task
+ actually-clock-in)
+ time-reported
+ time-not-reported)
+ (progn
+ (if actually-clock-in
+ (org-clock-in nil (- (float-time) (* 60 60)))
+ (insert (org-test-clock-create-clock "-1h")))
+ ;; Unless tstart and tend are fully specified it doesn't work
+ (test-org-clock-clocktable-contents ":tstart \"<-2d>\" :tend \"<tomorrow>\""))))
+ (when actually-clock-in
+ (org-clock-cancel)))))))
+
(provide 'test-org-clock)
;;; test-org-clock.el end here
--
2.51.0
>From 96bb3142704452941614648abf15bfb4cb140a2e Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Sat, 9 Aug 2025 14:09:20 -0400
Subject: [PATCH 04/12] Testing: test clocktable with malformed clock lines
* testing/lisp/test-org-clock.el
(test-org-clock/clocktable/malformed-clock-lines): New test
---
testing/lisp/test-org-clock.el | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 59cbfed8b..0828b8bc0 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -1317,6 +1317,21 @@ test-org-clock/clocktable/hidefiles
(test-org-clock-clocktable-contents
(format ":hidefiles t :scope (lambda () (list %S))" the-file))))))))
+(ert-deftest test-org-clock/clocktable/malformed-clock-lines ()
+ "Test clocktable with malformed clock lines."
+ (let (org-warning)
+ (cl-letf* (((symbol-function #'org-display-warning)
+ (lambda (message) (setq org-warning message))))
+ (should
+ (equal
+ "| Headline | Time |
+|--------------+--------|
+| *Total time* | *0:00* |"
+ (org-test-with-temp-text "* H1
+CLOCK: [2012-01-01 sun. 00rr:04]--[2012-01-01 sun. 00:05] => 0:01"
+ (test-org-clock-clocktable-contents ""))))
+ (should (string-prefix-p "org-clock-sum: Ignoring invalid" org-warning)))))
+
;;; Mode line
(ert-deftest test-org-clock/mode-line ()
--
2.51.0
>From 699e1d02279452d7f8d805c4e5774b622f3d34b6 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Sat, 30 Aug 2025 13:22:51 -0400
Subject: [PATCH 05/12] Testing: Add test for clocktable with :subtree scope
* testing/lisp/test-org-clock.el (test-org-clock-clocktable-contents):
Add and implement new argument to insert clocktable at point instead
of the beggining of the buffer.
(test-org-clock/clocktable/scope): Add a test for when :scope is
subtree.
---
testing/lisp/test-org-clock.el | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 0828b8bc0..4806bdae8 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -62,17 +62,21 @@ org-test-clock-create-clock
(/ (mod sec-diff 3600) 60))))
"\n")))
-(defun test-org-clock-clocktable-contents (options &optional initial)
+(defun test-org-clock-clocktable-contents (options &optional initial no-move)
"Return contents of a Clock table for current buffer
OPTIONS is a string of Clock table options. Optional argument
INITIAL is a string specifying initial contents within the Clock
table.
+When NO-MOVE is non-nil, then place the clocktable at point instead of
+the beginning of the buffer.
+
Caption is ignored in contents. The clocktable doesn't appear in
the buffer."
(declare (indent 2))
- (goto-char (point-min))
+ (unless no-move
+ (goto-char (point-min)))
(save-excursion
(insert "#+BEGIN: clocktable " options "\n")
(when initial (insert initial))
@@ -503,7 +507,22 @@ test-org-clock/clocktable/scope
(let ((the-file (buffer-file-name)))
(org-test-with-temp-text-in-file ""
(test-org-clock-clocktable-contents
- (format ":scope (lambda () (list %S))" the-file))))))))
+ (format ":scope (lambda () (list %S))" the-file)))))))
+ ;; Test "subtree" scope.
+ (should
+ (string-equal
+ "| Headline | Time | |
+|------------------+--------+------|
+| *Total time* | *1:00* | |
+|------------------+--------+------|
+| \\_ subtree Test | | 1:00 |"
+ (org-test-with-temp-text
+ "* Test
+CLOCK: [2012-03-29 Thu 8:00]--[2012-03-29 Thu 16:40] => 8:40
+** subtree Test
+<point>
+CLOCK: [2012-03-29 Thu 16:00]--[2012-03-29 Thu 17:00] => 1:00"
+ (test-org-clock-clocktable-contents ":scope subtree" nil t)))))
(ert-deftest test-org-clock/clocktable/maxlevel ()
"Test \":maxlevel\" parameter in Clock table."
--
2.51.0
>From d1cfd483d3581654c7e7f81819437383f5200dae Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Fri, 24 Oct 2025 18:14:17 -0400
Subject: [PATCH 06/12] Testing: Add test for clocktable with :timestamp set
* testing/lisp/test-org-clock.el
(test-org-clock/clocktable/timestamp): New test.
---
testing/lisp/test-org-clock.el | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 4806bdae8..fe1ade01c 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -480,6 +480,33 @@ test-org-clock/clocktable/tags
(goto-line 4)
(test-org-clock-clocktable-contents ":tags t :indent nil")))))
+(ert-deftest test-org-clock/clocktable/timestamp ()
+ "Test \":timestamp\" parameter in Clock table."
+ (should
+ (equal
+ "| Timestamp | Headline | Time |
+|------------------------+--------------------+--------|
+| | *Total time* | *4:00* |
+|------------------------+--------------------+--------|
+| <2025-10-24 Fri 10:00> | scheduled | 1:00 |
+| <2025-10-24 Fri 10:00> | deadline | 1:00 |
+| <2025-10-24 Fri 10:00> | timestamp | 1:00 |
+| [2025-10-24 Fri 10:00] | inactive timestamp | 1:00 |"
+ (org-test-with-temp-text "* scheduled
+SCHEDULED: <2025-10-24 Fri 10:00>
+CLOCK: [2025-10-23 Thu 12:00]--[2025-10-23 Thu 13:00] => 1:00
+* deadline
+DEADLINE: <2025-10-24 Fri 10:00>
+CLOCK: [2025-10-23 Thu 12:00]--[2025-10-23 Thu 13:00] => 1:00
+* timestamp
+<2025-10-24 Fri 10:00>
+CLOCK: [2025-10-23 Thu 12:00]--[2025-10-23 Thu 13:00] => 1:00
+* inactive timestamp
+[2025-10-24 Fri 10:00]
+CLOCK: [2025-10-23 Thu 12:00]--[2025-10-23 Thu 13:00] => 1:00
+"
+ (test-org-clock-clocktable-contents ":timestamp t")))))
+
(ert-deftest test-org-clock/clocktable/scope ()
"Test \":scope\" parameter in Clock table."
;; Test `file-with-archives' scope. In particular, preserve "TBLFM"
--
2.51.0
>From a378f568b6208904c243a7d464d1341017a3779b Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Mon, 20 Oct 2025 16:33:47 -0400
Subject: [PATCH 07/12] Testing: Test org-columns with CLOCKSUM and CLOCKSUM_T
properties
* testing/lisp/test-org-colview.el
(test-org-colview/column-property/clocksum)
(test-org-colview/column-property/clocksum_t): New tests.
---
testing/lisp/test-org-colview.el | 41 ++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/testing/lisp/test-org-colview.el b/testing/lisp/test-org-colview.el
index 623ee6283..681f4fe48 100644
--- a/testing/lisp/test-org-colview.el
+++ b/testing/lisp/test-org-colview.el
@@ -1399,6 +1399,47 @@ test-org-colview/columns-next-allowed-value
(list (get-char-property (- (point) 1) 'org-columns-value)
(get-char-property (point) 'org-columns-value))))))))
+(ert-deftest test-org-colview/column-property/clocksum ()
+ "Test `org-columns' display of the CLOCKSUM property."
+ (org-test-with-temp-text
+ "* H
+CLOCK: [2022-11-03 06:00]--[2022-11-03 06:03] => 0:03
+** S1
+CLOCK: [2022-11-03 06:03]--[2022-11-03 06:05] => 0:02
+** S2
+empty
+** S3
+CLOCK: [2022-11-03 06:05]--[2022-11-03 06:06] => 0:01"
+ (let ((org-columns-default-format "%CLOCKSUM"))
+ (org-columns))
+ (should
+ (equal
+ '("0:06" "0:02" "" "0:01")
+ (org-map-entries
+ (lambda ()
+ (get-char-property (point) 'org-columns-value-modified)))))))
+
+(ert-deftest test-org-colview/column-property/clocksum_t ()
+ "Test `org-columns' display of the CLOCKSUM_T property."
+ (org-test-at-time "<2022-11-03>"
+ (org-test-with-temp-text
+ "* H
+CLOCK: [2022-11-02 12:00]--[2022-11-03 02:00] => 14:00
+** S1
+CLOCK: [2022-11-03 23:50]--[2022-11-04 01:50] => 2:00
+** S2
+empty
+** S3
+CLOCK: [2022-11-03 06:05]--[2022-11-03 06:06] => 0:01
+"
+ (let ((org-columns-default-format "%CLOCKSUM_T"))
+ (org-columns))
+ (should
+ (equal
+ '("2:11" "0:10" "" "0:01")
+ (org-map-entries
+ (lambda ()
+ (get-char-property (point) 'org-columns-value-modified))))))))
;;; Dynamic block
--
2.51.0
>From 587165ba696a2c5cd8f9408062f6126e1dc3d883 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Tue, 21 Oct 2025 13:15:19 -0400
Subject: [PATCH 08/12] Testing: Add test-org-clock/org-clock-sum
* testing/lisp/test-org-clock.el (test-org-clock/org-clock-sum): New
test.
---
testing/lisp/test-org-clock.el | 54 ++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index fe1ade01c..75a6c7b33 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -340,6 +340,60 @@ test-org-clock/clock-drawer-dwim
(org-ctrl-c-ctrl-c)
(buffer-string))))))
+
+;;; org-clock-sum
+
+(ert-deftest test-org-clock/org-clock-sum ()
+ "Test `org-clock-sum'."
+ (org-test-at-time "<2025-10-18 12:00>"
+ (cl-flet ((org-test-get-clock-minutes (text-property)
+ (org-map-entries
+ (lambda ()
+ (get-char-property (point) text-property)))))
+ (org-test-with-temp-text
+ "* This is test
+CLOCK: [2025-10-18 Sat 09:00]--[2025-10-18 Sat 10:00] => 1:00
+*************** Here
+:LOGBOOK:
+CLOCK: [2025-10-18 Sat 13:00]
+CLOCK: [2025-10-18 Sat 10:00]--[2025-10-18 Sat 11:00] => 1:00
+CLOCK: [2025-10-18 Sat 13:00]
+CLOCK: [2025-10-18 Sat 14:43]
+:END:
+The open clocks here are fake outs.
+*************** END"
+ (require 'org-inlinetask)
+ (org-clock-sum)
+ (should
+ (eq 120 org-clock-file-total-minutes))
+ (should
+ (equal
+ '(120 60)
+ (org-test-get-clock-minutes :org-clock-minutes)))
+ ;; Test including the current clocking task. Requires tstart and
+ ;; tend to be set so just use `org-clock-sum-today'.
+ (let ((org-clock-report-include-clocking-task t))
+ (org-clock-in nil (time-subtract nil (* 2 60 60)))
+ (org-clock-sum-today)
+ (should
+ (eq 240 org-clock-file-total-minutes))
+ (should
+ (equal
+ '(240 60)
+ (org-test-get-clock-minutes :org-clock-minutes-today)))
+ (org-clock-cancel)
+ ;; Test open clock on inline task
+ (search-forward "Here")
+ (org-clock-in nil (time-subtract nil (* 3 60 60)))
+ (org-clock-sum-today)
+ (should
+ (eq 300 org-clock-file-total-minutes))
+ (should
+ (equal
+ '(300 240)
+ (org-test-get-clock-minutes :org-clock-minutes-today))))))))
+
+
;;; Clocktable
--
2.51.0
>From 841b379247499f41d9ea26056a7647183e4eba1b Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Thu, 23 Oct 2025 15:31:17 -0400
Subject: [PATCH 09/12] Testing: Add test for `org-clock-sum' in source blocks
* testing/lisp/test-org-clock.el
(test-org-clock/org-clock-sum-source-block): New test
---
testing/lisp/test-org-clock.el | 47 ++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 75a6c7b33..1a15938a7 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -393,6 +393,53 @@ test-org-clock/org-clock-sum
'(300 240)
(org-test-get-clock-minutes :org-clock-minutes-today))))))))
+(ert-deftest test-org-clock/org-clock-sum-source-block ()
+ "Test `org-clock-sum' with source blocks."
+ (org-test-at-time "<2025-10-18 15:00>"
+ (cl-flet ((org-test-get-clock-minutes (text-property)
+ (org-map-entries
+ (lambda ()
+ (get-char-property (point) text-property)))))
+ (org-test-with-temp-text
+ "* This is test
+CLOCK: [2025-10-18 Sat 09:00]--[2025-10-18 Sat 10:00] => 1:00
+#+begin_src org
+<point>
+,* foo
+CLOCK: [2025-10-18 Sat 10:00]--[2025-10-18 Sat 11:00] => 1:00
+,** subfoo
+CLOCK: [2025-10-18 Sat 11:00]--[2025-10-18 Sat 12:00] => 1:00
+,* bar
+CLOCK: [2025-10-18 Sat 12:00]--[2025-10-18 Sat 13:00] => 1:00
+,** subbar
+CLOCK: [2025-10-18 Sat 13:00]--[2025-10-18 Sat 14:00] => 1:00
+#+end_src
+CLOCK: [2025-10-18 Sat 14:00]--[2025-10-18 Sat 15:00] => 1:00
+"
+ (org-clock-sum)
+ (should
+ (eq 120 org-clock-file-total-minutes))
+ (should
+ (equal
+ '(120)
+ (org-test-get-clock-minutes :org-clock-minutes)))
+ ;; Test when editing source block
+ (org-edit-special)
+ (org-clock-sum)
+ (should
+ (eq 240 org-clock-file-total-minutes))
+ (should
+ (equal
+ '(120 60 120 60)
+ (org-test-get-clock-minutes :org-clock-minutes)))
+ (org-edit-src-exit)
+ ;; After exiting we still have the original results
+ (should
+ (eq 120 org-clock-file-total-minutes))
+ (should
+ (equal
+ '(120)
+ (org-test-get-clock-minutes :org-clock-minutes)))))))
;;; Clocktable
--
2.51.0
>From 5bb43d7c8b2434952a2dccabe6f2c616c44b69b9 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Thu, 11 Apr 2024 12:23:21 -0400
Subject: [PATCH 10/12] lisp/org-clock.el (org-clock-sum): Rewrite using
element api
* lisp/org-clock.el (org-clock-sum): Rewrite using element api.
(org--clock-ranges): New function.
---
lisp/org-clock.el | 220 +++++++++++++++++++++++++---------------------
1 file changed, 119 insertions(+), 101 deletions(-)
diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index 8b1752384..e0154e1df 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -33,15 +33,13 @@
(require 'cl-lib)
(require 'org)
+(require 'org-element)
(declare-function calendar-iso-to-absolute "cal-iso" (date))
(declare-function notifications-notify "notifications" (&rest params))
(declare-function org-element-property "org-element-ast" (property node))
-(declare-function org-element-contents-end "org-element" (node))
-(declare-function org-element-end "org-element" (node))
(declare-function org-element-type "org-element-ast" (node &optional anonymous))
(declare-function org-element-type-p "org-element-ast" (node types))
-(defvar org-element-use-cache)
(declare-function org-inlinetask-at-task-p "org-inlinetask" ())
(declare-function org-inlinetask-goto-beginning "org-inlinetask" ())
(declare-function org-inlinetask-goto-end "org-inlinetask" ())
@@ -2069,105 +2067,68 @@ org-clock-sum
HEADLINE-FILTER is a zero-arg function that, if specified, is called for
each headline in the time range with point at the headline. Headlines for
which HEADLINE-FILTER returns nil are excluded from the clock summation.
-PROPNAME lets you set a custom text property instead of :org-clock-minutes."
+PROPNAME lets you set a custom text property instead of :org-clock-minutes.
+
+Clocking entries that are open (as in don't have an end time) that are
+not the current clocking entry will be ignored."
(with-silent-modifications
- (let* ((re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*"
- org-clock-string
- "[ \t]*\\(?:\\(\\[.*?\\]\\)-+\\(\\[.*?\\]\\)\\|=>[ \t]+\\([0-9]+\\):\\([0-9]+\\)\\)"))
- (lmax 30)
- (ltimes (make-vector lmax 0))
- (level 0)
- (tstart (cond ((stringp tstart) (org-time-string-to-seconds tstart))
- ((consp tstart) (float-time tstart))
- (t tstart)))
- (tend (cond ((stringp tend) (org-time-string-to-seconds tend))
- ((consp tend) (float-time tend))
- (t tend)))
- (t1 0)
- time)
- (remove-text-properties (point-min) (point-max)
- `(,(or propname :org-clock-minutes) t
- :org-clock-force-headline-inclusion t))
- (save-excursion
- (goto-char (point-max))
- (while (re-search-backward re nil t)
- (let* ((element (save-match-data (org-element-at-point)))
- (element-type (org-element-type element)))
- (cond
- ((and (eq element-type 'clock) (match-end 2))
- ;; Two time stamps.
- (condition-case nil
- (let* ((timestamp (org-element-property :value element))
- (ts (float-time
- (org-encode-time
- (list 0
- (org-element-property :minute-start timestamp)
- (org-element-property :hour-start timestamp)
- (org-element-property :day-start timestamp)
- (org-element-property :month-start timestamp)
- (org-element-property :year-start timestamp)
- nil -1 nil))))
- (te (float-time
- (org-encode-time
- (list 0
- (org-element-property :minute-end timestamp)
- (org-element-property :hour-end timestamp)
- (org-element-property :day-end timestamp)
- (org-element-property :month-end timestamp)
- (org-element-property :year-end timestamp)
- nil -1 nil))))
- (dt (- (if tend (min te tend) te)
- (if tstart (max ts tstart) ts))))
- (when (> dt 0) (cl-incf t1 (floor dt 60))))
- (error
- (org-display-warning (format "org-clock-sum: Ignoring invalid %s" (org-current-line-string))))))
- ((match-end 4)
- ;; A naked time.
- (setq t1 (+ t1 (string-to-number (match-string 5))
- (* 60 (string-to-number (match-string 4))))))
- ((memq element-type '(headline inlinetask)) ;A headline
- ;; Add the currently clocking item time to the total.
- (when (and org-clock-report-include-clocking-task
- (eq (org-clocking-buffer) (current-buffer))
- (eq (marker-position org-clock-hd-marker) (point))
- tstart
- tend
- (>= (float-time org-clock-start-time) tstart)
- (<= (float-time org-clock-start-time) tend))
- (let ((time (floor (org-time-convert-to-integer
- (time-since org-clock-start-time))
- 60)))
- (setq t1 (+ t1 time))))
- (let* ((headline-forced
- (get-text-property (point)
- :org-clock-force-headline-inclusion))
- (headline-included
- (or (null headline-filter)
- (save-excursion
- (save-match-data (funcall headline-filter))))))
- (setq level (- (match-end 1) (match-beginning 1)))
- (when (>= level lmax)
- (setq ltimes (vconcat ltimes (make-vector lmax 0)) lmax (* 2 lmax)))
- (when (or (> t1 0) (> (aref ltimes level) 0))
- (when (or headline-included headline-forced)
- (if headline-included
- (cl-loop for l from 0 to level do
- (aset ltimes l (+ (aref ltimes l) t1))))
- (setq time (aref ltimes level))
- (goto-char (match-beginning 0))
- (put-text-property (point) (line-end-position)
- (or propname :org-clock-minutes) time)
- (when headline-filter
- (save-excursion
- (save-match-data
- (while (org-up-heading-safe)
- (put-text-property
- (point) (line-end-position)
- :org-clock-force-headline-inclusion t))))))
- (setq t1 0)
- (cl-loop for l from level to (1- lmax) do
- (aset ltimes l 0))))))))
- (setq org-clock-file-total-minutes (aref ltimes 0))))))
+ (let ((tstart (cond ((stringp tstart) (org-time-string-to-seconds tstart))
+ ((consp tstart) (float-time tstart))
+ (t tstart)))
+ (tend (cond ((stringp tend) (org-time-string-to-seconds tend))
+ ((consp tend) (float-time tend))
+ (t tend)))
+ (propname (or propname :org-clock-minutes))
+ (t1 0)
+ (total 0)
+ time)
+ (remove-text-properties (point-min) (point-max) `(,propname t))
+ (org-element-cache-map
+ (lambda (headline-or-inlinetask)
+ (when (or (null headline-filter)
+ (save-excursion
+ (funcall headline-filter)))
+ (mapc
+ (lambda (range)
+ (setq time
+ (pcase range
+ (`(,_ . (open . ,buffer-position))
+ (when (and org-clock-report-include-clocking-task
+ (eq (org-clocking-buffer) (current-buffer))
+ (eq (marker-position org-clock-marker)
+ buffer-position)
+ tstart
+ tend
+ (>= (float-time org-clock-start-time) tstart)
+ (<= (float-time org-clock-start-time) tend))
+ (floor (org-time-convert-to-integer
+ (time-since org-clock-start-time))
+ 60)))
+ ((pred floatp) (floor range))
+ (`(,time1 . ,time2)
+ (let* ((ts (float-time time1))
+ (te (float-time time2))
+ (dt (- (if tend (min te tend) te)
+ (if tstart (max ts tstart) ts))))
+ (floor dt 60)))))
+ (when (and time (> time 0)) (cl-incf t1 time)))
+ (org--clock-ranges headline-or-inlinetask))
+ (when (> t1 0)
+ (setq total (+ total t1))
+ (org-element-lineage-map headline-or-inlinetask
+ (lambda (parent)
+ (when (<= (point-min) (org-element-begin parent))
+ (put-text-property
+ (org-element-begin parent) (1- (org-element-contents-begin parent))
+ propname
+ (+ t1 (or (get-text-property
+ (org-element-begin parent)
+ propname)
+ 0)))))
+ '(headline inlinetask) t))
+ (setq t1 0)))
+ :narrow t)
+ (setq org-clock-file-total-minutes total))))
(defun org-clock-sum-current-item (&optional tstart)
"Return time, clocked on current item in total."
@@ -2182,6 +2143,63 @@ org-clock-sum-current-item
(org-clock-sum tstart)
org-clock-file-total-minutes)))
+(defun org--clock-ranges (headline)
+ "Return a list of clock ranges of HEADLINE.
+Does not recurse into subheadings.
+Ranges are in one of these formats:
+ (time . time)
+ (time . (\\='open . buffer-position)) The clock does not have an end time
+ float The number of minutes as a float"
+ (unless (org-element-type-p headline '(headline inlinetask))
+ (error "Argument must be a headline or inlinetask"))
+ (and
+ (org-element-contents-begin headline) ;; nil for empty headlines
+ (or
+ (org-element-cache-get-key headline :clock-ranges)
+ (let ((clock-ranges
+ (org-element-cache-map
+ (lambda (elem)
+ (when (org-element-type-p elem 'clock)
+ (if-let* ((timestamp (org-element-property :value elem)))
+ (progn
+ (if
+ (and
+ (org-element-property :minute-start timestamp)
+ (org-element-property :hour-start timestamp)
+ (org-element-property :day-start timestamp)
+ (org-element-property :month-start timestamp)
+ (org-element-property :year-start timestamp)
+ ;; In org-element, when the end doesn't exist, it is set to the start.
+ ;; This means we can't check that the end is fully specified.
+ ;; (org-element-property :minute-end timestamp)
+ ;; (org-element-property :hour-end timestamp)
+ ;; (org-element-property :day-end timestamp)
+ ;; (org-element-property :month-end timestamp)
+ ;; (org-element-property :year-end timestamp)
+ )
+ (cons (org-timestamp-to-time timestamp)
+ (if (eq 'running (org-element-property :status elem))
+ (cons 'open (org-element-property :end timestamp))
+ (org-timestamp-to-time timestamp t)))
+ (org-display-warning
+ (format "org-clock-sum: Ignoring invalid timestamp: %s"
+ (org-element-property :raw-value timestamp)))))
+ (when (org-element-property :duration elem)
+ (org-duration-to-minutes (org-element-property :duration elem))))))
+ ;; FIXME: using these arguments would be more intuitive
+ ;; but don't seem to work due to bugs in
+ ;; `org-element-cache-map'
+ ;; :restrict-elements '(clock)
+ ;; :after-element headline
+ :granularity 'element
+ :next-re org-element-clock-line-re
+ :from-pos (org-element-contents-begin headline)
+ :to-pos (save-excursion
+ (goto-char (org-element-begin headline))
+ (org-entry-end-position)))))
+ (org-element-cache-store-key headline :clock-ranges clock-ranges)
+ clock-ranges))))
+
;;;###autoload
(defun org-clock-display (&optional arg)
"Show subtree times in the entire buffer.
--
2.51.0
>From 0b9723b22b4d5e825a9e8fc055ddafa099d8302a Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Fri, 24 Oct 2025 14:44:59 -0400
Subject: [PATCH 11/12] Rewrite `org-clock-get-table-data' using org-element
* lisp/org-clock.el (org-clock-get-table-data): Rewrite using
org-element.
* testing/lisp/test-org-clock.el
(test-org-clock/clocktable/inlinetask/no-heading): Expect this test to
pass now.
---
lisp/org-clock.el | 170 +++++++++++++++++----------------
testing/lisp/test-org-clock.el | 2 -
2 files changed, 89 insertions(+), 83 deletions(-)
diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index e0154e1df..dada29457 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -3154,95 +3154,103 @@ org-clock-get-table-data
entry, any of SCHEDULED, DEADLINE, NORMAL, or first inactive,
in this sequence.
TIME: The sum of all time spend in this tree, in minutes. This time
- will of cause be restricted to the time block and tags match
+ will of course be restricted to the time block and tags match
specified in PARAMS.
PROPERTIES: The list properties specified in the `:properties' parameter
along with their value, as an alist following the pattern
(NAME . VALUE)."
(let* ((maxlevel (or (plist-get params :maxlevel) 3))
- (timestamp (plist-get params :timestamp))
- (ts (plist-get params :tstart))
- (te (plist-get params :tend))
- (ws (plist-get params :wstart))
- (ms (plist-get params :mstart))
- (block (plist-get params :block))
- (link (plist-get params :link))
- (tags (plist-get params :tags))
- (match (plist-get params :match))
- (properties (plist-get params :properties))
- (inherit-property-p (plist-get params :inherit-props))
- (matcher (and match (cdr (org-make-tags-matcher match))))
- cc st p tbl)
+ (timestamp (plist-get params :timestamp))
+ (ts (plist-get params :tstart))
+ (te (plist-get params :tend))
+ (ws (plist-get params :wstart))
+ (ms (plist-get params :mstart))
+ (block (plist-get params :block))
+ (link (plist-get params :link))
+ (tags (plist-get params :tags))
+ (match (plist-get params :match))
+ (properties (plist-get params :properties))
+ (inherit-property-p (plist-get params :inherit-props))
+ (matcher (and match (cdr (org-make-tags-matcher match))))
+ (tbl '())
+ cc)
- (setq org-clock-file-total-minutes nil)
(when block
(setq cc (org-clock-special-range block nil t ws ms)
- ts (car cc)
- te (nth 1 cc)))
- (when (integerp ts) (setq ts (calendar-gregorian-from-absolute ts)))
- (when (integerp te) (setq te (calendar-gregorian-from-absolute te)))
- (when (and ts (listp ts))
- (setq ts (format "%4d-%02d-%02d" (nth 2 ts) (car ts) (nth 1 ts))))
- (when (and te (listp te))
- (setq te (format "%4d-%02d-%02d" (nth 2 te) (car te) (nth 1 te))))
- ;; Now the times are strings we can parse.
- (if ts (setq ts (org-matcher-time ts)))
- (if te (setq te (org-matcher-time te)))
- (save-excursion
- (org-clock-sum ts te
- (when matcher
- (lambda ()
- (let* ((todo (org-get-todo-state))
- (tags-list (org-get-tags))
- (org-scanner-tags tags-list)
- (org-trust-scanner-tags t)
- (level (org-current-level)))
- (funcall matcher todo tags-list level)))))
- (goto-char (point-min))
- (setq st t)
- (while (or (and (bobp) (prog1 st (setq st nil))
- (get-text-property (point) :org-clock-minutes)
- (setq p (point-min)))
- (setq p (next-single-property-change
- (point) :org-clock-minutes)))
- (goto-char p)
- (let ((time (get-text-property p :org-clock-minutes)))
- (when (and time (> time 0) (org-at-heading-p))
- (let ((level (org-reduced-level (org-current-level))))
- (when (<= level maxlevel)
- (let* ((headline (org-get-heading t t t t))
- (hdl
- (if (not link) headline
- (let ((search
- (org-link-heading-search-string headline)))
- (org-link-make-string
- (if (not (buffer-file-name)) search
- (format "file:%s::%s" (buffer-file-name) search))
- ;; Prune statistics cookies. Replace
- ;; links with their description, or
- ;; a plain link if there is none.
- (org-trim
- (org-link-display-format
- (replace-regexp-in-string
- "\\[[0-9]*\\(?:%\\|/[0-9]*\\)\\]" ""
- headline)))))))
- (tgs (and tags (org-get-tags)))
- (tsp
- (and timestamp
- (cl-some (lambda (p) (org-entry-get (point) p))
- '("SCHEDULED" "DEADLINE" "TIMESTAMP"
- "TIMESTAMP_IA"))))
- (props
- (and properties
- (delq nil
- (mapcar
- (lambda (p)
- (let ((v (org-entry-get
- (point) p inherit-property-p)))
- (and v (cons p v))))
- properties)))))
- (push (list level hdl tgs tsp time props) tbl)))))))
- (list file org-clock-file-total-minutes (nreverse tbl)))))
+ ts (car cc)
+ te (nth 1 cc)))
+ (when (and ts (or (integerp ts) (listp ts)))
+ (setq ts (org-time-from-absolute ts)))
+ (when (and te (or (integerp te) (listp te)))
+ (setq te (org-time-from-absolute te)))
+ (when (stringp ts) (setq ts (org-matcher-time ts)))
+ (when (stringp te) (setq te (org-matcher-time te)))
+ (org-clock-sum ts te
+ (when matcher
+ (lambda ()
+ (let* ((todo (org-get-todo-state))
+ (tags-list (org-get-tags))
+ (org-scanner-tags tags-list)
+ (org-trust-scanner-tags t)
+ (level (org-current-level)))
+ (funcall matcher todo tags-list level)))))
+ (let ((run-one-time t)
+ (position (point-min)))
+ (while (or (when run-one-time (setq run-one-time nil) t)
+ (setq position
+ (next-single-property-change
+ position :org-clock-minutes)))
+ (when-let*
+ ((time
+ (get-text-property position :org-clock-minutes))
+ (time (when (> time 0) time))
+ (elm (org-element-at-point position))
+ (elm (and (org-element-type-p elm '(headline inlinetask)) elm))
+ (level
+ (if (eq 'headline (org-element-type elm))
+ (org-element-property :level elm)
+ ;; inline task
+ (or (org-element-lineage-map elm
+ (lambda (elm)
+ (org-element-property :level elm))
+ '(headline) nil t)
+ 1)))
+ (level (when (<= level maxlevel) level))
+ (title
+ (let ((headline (org-element-property :title elm)))
+ (if (not link) headline
+ (let ((search
+ (org-link-heading-search-string headline)))
+ (org-link-make-string
+ (if (not (buffer-file-name)) search
+ (format "file:%s::%s" (buffer-file-name) search))
+ ;; Prune statistics cookies. Replace
+ ;; links with their description, or
+ ;; a plain link if there is none.
+ (org-trim
+ (org-link-display-format
+ (replace-regexp-in-string
+ "\\[[0-9]*\\(?:%\\|/[0-9]*\\)\\]" ""
+ headline)))))))))
+ (push
+ (list level
+ title
+ (and tags (org-get-tags elm))
+ (and timestamp
+ (cl-some
+ (lambda (p) (org-entry-get elm p))
+ '("SCHEDULED" "DEADLINE" "TIMESTAMP" "TIMESTAMP_IA")))
+ time
+ (and properties
+ (delq nil
+ (mapcar
+ (lambda (p)
+ (let ((v (org-entry-get
+ elm p inherit-property-p)))
+ (and v (cons p v))))
+ properties))))
+ tbl))))
+ (list file org-clock-file-total-minutes (nreverse tbl))))
;; Saving and loading the clock
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 1a15938a7..9992f1da6 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -1686,8 +1686,6 @@ test-org-clock/clocktable/inlinetask/insert
(ert-deftest test-org-clock/clocktable/inlinetask/no-heading ()
"Test insert clocktable on an inline task not under a heading."
- ;; (wrong-type-argument number-or-marker-p nil)
- :expected-result :failed
(should
(equal
"| Headline | Time |
--
2.51.0
>From 4b7313056df6d796c2b5209a41b1dd7cc462380b Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Fri, 24 Oct 2025 16:47:44 -0400
Subject: [PATCH 12/12] Clocktables: Indent inline tasks under their heading
Previously an inline task under a heading would appear in the
clocktable as:
| Headline | Time |
|--------------+--------|
| *Total time* | *1:00* |
|--------------+--------|
| Parent | 1:00 |
| Inline | 1:00 |
But now it appears as:
| Headline | Time | |
|--------------+--------+------|
| *Total time* | *1:00* | |
|--------------+--------+------|
| Parent | 1:00 | |
| \\_ Inline | | 1:00 |
This is more intuitive as it treats inline tasks the same way
subheadings are treated.
* etc/ORG-NEWS: Add news entry.
* lisp/org-clock.el (lisp/org-clock.el): Increase the level of inline
tasks by 1.
* testing/lisp/test-org-clock.el
(test-org-clock/clocktable/inlinetask/insert)
(test-org-clock/clocktable/inlinetask/open-clock): Adjust tests to new
behavior.
---
etc/ORG-NEWS | 10 ++++++++++
lisp/org-clock.el | 11 ++++++-----
testing/lisp/test-org-clock.el | 25 +++++++++++++------------
3 files changed, 29 insertions(+), 17 deletions(-)
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 87fbf8f06..0365a99b9 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -114,6 +114,16 @@ To use the old behavior and export active timestamps only without
diary timestamps, users can set ~org-export-with-timestamps~ and
~org-icalendar-with-timestamps~ to ~active-exclude-diary~.
+*** Inline tasks in a clocktable will be indented to a level below their heading
+
+Previously a clocktable that contained an inline task would show the
+task at the same indentation level as the heading that contains it.
+Now the inline task is indented to be one lower then its parent
+heading.
+
+This also fixes a bug where creating a clocktable that includes an
+inline task before any headings would cause an error.
+
*** =ob-calc.el=: Vector and matrix are now inserted as Org tables by default
~ob-calc~ now formats vector and matrix results as Org tables. This
diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index dada29457..079f0491f 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -3210,11 +3210,12 @@ org-clock-get-table-data
(if (eq 'headline (org-element-type elm))
(org-element-property :level elm)
;; inline task
- (or (org-element-lineage-map elm
- (lambda (elm)
- (org-element-property :level elm))
- '(headline) nil t)
- 1)))
+ (1+
+ (or (org-element-lineage-map elm
+ (lambda (elm)
+ (org-element-property :level elm))
+ '(headline) nil t)
+ 0))))
(level (when (<= level maxlevel) level))
(title
(let ((headline (org-element-property :title elm)))
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 9992f1da6..9ab8a1b67 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -1668,12 +1668,12 @@ test-org-clock/clocktable/inlinetask/insert
"Test insert clocktable on an inline task."
(should
(equal
- "| Headline | Time |
-|--------------+--------|
-| *Total time* | *2:00* |
-|--------------+--------|
-| H1 | 2:00 |
-| I | 2:00 |"
+ "| Headline | Time | |
+|--------------+--------+------|
+| *Total time* | *2:00* | |
+|--------------+--------+------|
+| H1 | 2:00 | |
+| \\_ I | | 2:00 |"
(let ((org-inlinetask-min-level 5))
(org-test-with-temp-text "* H1
***** I
@@ -1706,12 +1706,13 @@ test-org-clock/clocktable/inlinetask/open-clock
"Test open clocks on an inline task.
Open clocks should be ignored unless it is clocked in and
`org-clock-report-include-clocking-task' is t."
- (let ((time-reported "| Headline | Time |
-|--------------+--------|
-| *Total time* | *1:00* |
-|--------------+--------|
-| H1 | 1:00 |
-| I | 1:00 |")
+ (let ((time-reported
+ "| Headline | Time | |
+|--------------+--------+------|
+| *Total time* | *1:00* | |
+|--------------+--------+------|
+| H1 | 1:00 | |
+| \\_ I | | 1:00 |")
(time-not-reported "| Headline | Time |
|--------------+--------|
| *Total time* | *0:00* |")
--
2.51.0