[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 76ef9bf 13/13: Merge commit '2c5ac0cb808ae6953fbc74cc49724
From: |
Ian Dunn |
Subject: |
[elpa] master 76ef9bf 13/13: Merge commit '2c5ac0cb808ae6953fbc74cc497245dafb51051f' |
Date: |
Sun, 25 Nov 2018 14:09:49 -0500 (EST) |
branch: master
commit 76ef9bf5f892c4c4f5145dcdfec84f922f906892
Merge: 7cacd8a 2c5ac0c
Author: Ian Dunn <address@hidden>
Commit: Ian Dunn <address@hidden>
Merge commit '2c5ac0cb808ae6953fbc74cc497245dafb51051f'
---
packages/org-edna/defaults.mk | 26 -
packages/org-edna/org-edna-tests.el | 1416 ++++++++++++++++++++++++----------
packages/org-edna/org-edna-tests.org | 217 +++++-
packages/org-edna/org-edna.el | 219 +++++-
packages/org-edna/org-edna.info | 527 +++++++++----
packages/org-edna/org-edna.org | 300 +++++--
packages/org-edna/test.mk | 1 +
7 files changed, 2026 insertions(+), 680 deletions(-)
diff --git a/packages/org-edna/defaults.mk b/packages/org-edna/defaults.mk
deleted file mode 100644
index f599ee4..0000000
--- a/packages/org-edna/defaults.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# This is part of org-edna
-#
-# Copyright (C) 2017-2018 Free Software Foundation, Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-emacs = emacs
-
-prefix = /usr/share
-
-org_path = $(prefix)/emacs/site-lisp/org
-
-info_dir = $(prefix)/info
-
-lisp_dir = $(prefix)/emacs/site-lisp/
diff --git a/packages/org-edna/org-edna-tests.el
b/packages/org-edna/org-edna-tests.el
index da766a9..b6adf15 100644
--- a/packages/org-edna/org-edna-tests.el
+++ b/packages/org-edna/org-edna-tests.el
@@ -3,9 +3,6 @@
;; Copyright (C) 2017-2018 Free Software Foundation, Inc.
;; Author: Ian Dunn <address@hidden>
-;; Keywords: convenience, text, org
-;; Version: 1.0
-;; Package-Requires: ((emacs "25.1") (seq "2.19") (org "8.0"))
;; This file is NOT part of GNU Emacs.
@@ -29,6 +26,9 @@
(require 'ert)
(require 'org-id)
+(defvar org-edna-test-inhibit-messages nil
+ "Whether to inhibit messages (apart from ERT messages).")
+
(defconst org-edna-test-dir
(expand-file-name (file-name-directory (or load-file-name
buffer-file-name))))
@@ -64,10 +64,87 @@
(defconst org-edna-test-relative-archived-child
"a4b6131e-0560-4201-86d5-f32b36363431")
(defconst org-edna-test-relative-child-with-done
"4a1d74a2-b032-47da-a823-b32f5cab0aae")
+(defun org-edna-test-restore-test-file ()
+ "Restore the test file back to its original state."
+ (with-current-buffer (get-file-buffer org-edna-test-file)
+ (revert-buffer nil t)))
+
+(defmacro org-edna-protect-test-file (&rest body)
+ (declare (indent 0))
+ `(unwind-protect
+ (progn ,@body)
+ ;; Change the test file back to its original state.
+ (org-edna-test-restore-test-file)))
+
+(defmacro org-edna-test-setup (&rest body)
+ "Common settings for tests."
+ (declare (indent 0))
+ ;; Override `current-time' so we can get a deterministic value
+ `(cl-letf* (((symbol-function 'current-time) (lambda () org-edna-test-time))
+ ;; Only use the test file in the agenda
+ (org-agenda-files `(,org-edna-test-file))
+ ;; Ensure interactive modification of TODO states works.
+ (org-todo-keywords '((sequence "TODO" "|" "DONE")))
+ ;; Only block based on Edna
+ (org-blocker-hook 'org-edna-blocker-function)
+ ;; Only trigger based on Edna
+ (org-trigger-hook 'org-edna-trigger-function)
+ ;; Inhibit messages if indicated
+ (inhibit-message org-edna-test-inhibit-messages))
+ ,@body))
+
+(defmacro org-edna-with-point-at-test-heading (heading-id &rest body)
+ (declare (indent 1))
+ `(org-with-point-at (org-edna-find-test-heading ,heading-id)
+ ,@body))
+
+(defmacro org-edna-with-test-heading (heading-id &rest body)
+ "Establish a test case with test heading HEADING-ID.
+
+HEADING-ID is a UUID string of a heading to use.
+
+Moves point to the heading, protects the test file, sets default
+test settings, then runs BODY."
+ (declare (indent 1))
+ `(org-edna-test-setup
+ (org-edna-protect-test-file
+ (org-edna-with-point-at-test-heading ,heading-id
+ ,@body))))
+
(defun org-edna-find-test-heading (id)
- "Find the test heading with id ID."
+ "Find the test heading with id ID.
+
+This avoids org-id digging into its internal database."
(org-id-find-id-in-file id org-edna-test-file t))
+;; _test exists to give more detailed reports in ERT output.
+(defun org-edna-test-compare-todos (pom expected-state _test)
+ (string-equal (org-entry-get pom "TODO") expected-state))
+
+(defun org-edna-test-change-todo-state (pom new-state)
+ (org-with-point-at pom (org-todo new-state)))
+
+(defun org-edna-test-check-block (pom _test)
+ "Check if the heading at point-or-marker POM is blocked."
+ (org-edna-test-change-todo-state pom "DONE")
+ (org-edna-test-compare-todos pom "TODO" _test))
+
+(defun org-edna-test-mark-done (&rest poms)
+ "Mark all points-or-markers in POMS as DONE."
+ (dolist (pom poms)
+ (org-edna-test-change-todo-state pom "DONE")))
+
+(defun org-edna-test-mark-todo (&rest poms)
+ "Mark all points-or-markers in POMS as TODO."
+ (dolist (pom poms)
+ (org-edna-test-change-todo-state pom "TODO")))
+
+(defun org-edna-test-children-marks ()
+ (org-edna-collect-descendants nil))
+
+
+;;; Parser Tests
+
(ert-deftest org-edna-parse-form-no-arguments ()
(let* ((input-string "test-string")
(parsed (org-edna-parse-string-form input-string)))
@@ -437,48 +514,48 @@
(should (equal output-form expected-form))))
-;; Finders
+;;; Finders
(defsubst org-edna-heading (pom)
(org-with-point-at pom
(org-get-heading t t t t)))
(ert-deftest org-edna-finder/match-single-arg ()
- (let* ((org-agenda-files `(,org-edna-test-file))
- (targets (org-edna-finder/match "test&1")))
- (should (= (length targets) 2))
- (should (string-equal (org-edna-heading (nth 0 targets)) "Tagged Heading
1"))
- (should (string-equal (org-edna-heading (nth 1 targets)) "Tagged Heading
2"))))
+ (org-edna-test-setup
+ (let* ((targets (org-edna-finder/match "test&1")))
+ (should (= (length targets) 2))
+ (should (string-equal (org-edna-heading (nth 0 targets)) "Tagged Heading
1"))
+ (should (string-equal (org-edna-heading (nth 1 targets)) "Tagged Heading
2")))))
(ert-deftest org-edna-finder/ids-single ()
- (let* ((org-agenda-files `(,org-edna-test-file))
- (test-id "caccd0a6-d400-410a-9018-b0635b07a37e")
- (targets (org-edna-finder/ids test-id)))
- (should (= (length targets) 1))
- (should (string-equal (org-edna-heading (nth 0 targets)) "Blocking Test"))
- (should (string-equal (org-entry-get (nth 0 targets) "ID") test-id))))
+ (org-edna-test-setup
+ (let* ((test-id "caccd0a6-d400-410a-9018-b0635b07a37e")
+ (targets (org-edna-finder/ids test-id)))
+ (should (= (length targets) 1))
+ (should (string-equal (org-edna-heading (nth 0 targets)) "Blocking
Test"))
+ (should (string-equal (org-entry-get (nth 0 targets) "ID") test-id)))))
(ert-deftest org-edna-finder/ids-multiple ()
- (let* ((org-agenda-files `(,org-edna-test-file))
- (test-ids '("0d491588-7da3-43c5-b51a-87fbd34f79f7"
- "b010cbad-60dc-46ef-a164-eb155e62cbb2"))
- (targets (apply 'org-edna-finder/ids test-ids)))
- (should (= (length targets) 2))
- (should (string-equal (org-edna-heading (nth 0 targets)) "ID Heading 1"))
- (should (string-equal (org-entry-get (nth 0 targets) "ID") (nth 0
test-ids)))
- (should (string-equal (org-edna-heading (nth 1 targets)) "ID Heading 2"))
- (should (string-equal (org-entry-get (nth 1 targets) "ID") (nth 1
test-ids)))))
+ (org-edna-test-setup
+ (let* ((test-ids '("0d491588-7da3-43c5-b51a-87fbd34f79f7"
+ "b010cbad-60dc-46ef-a164-eb155e62cbb2"))
+ (targets (apply 'org-edna-finder/ids test-ids)))
+ (should (= (length targets) 2))
+ (should (string-equal (org-edna-heading (nth 0 targets)) "ID Heading 1"))
+ (should (string-equal (org-entry-get (nth 0 targets) "ID") (nth 0
test-ids)))
+ (should (string-equal (org-edna-heading (nth 1 targets)) "ID Heading 2"))
+ (should (string-equal (org-entry-get (nth 1 targets) "ID") (nth 1
test-ids))))))
(ert-deftest org-edna-finder/match-blocker ()
- (let* ((org-agenda-files `(,org-edna-test-file))
- (heading (org-id-find "caccd0a6-d400-410a-9018-b0635b07a37e" t))
- (blocker (org-entry-get heading "BLOCKER"))
- blocking-entry)
- (should (string-equal "match(\"test&1\")" blocker))
- (org-with-point-at heading
- (setq blocking-entry (org-edna-process-form blocker 'condition)))
- (should (string-equal (substring-no-properties blocking-entry)
- "TODO Tagged Heading 1 :1:test:"))))
+ (org-edna-test-setup
+ (let* ((heading (org-edna-find-test-heading
"caccd0a6-d400-410a-9018-b0635b07a37e"))
+ (blocker (org-entry-get heading "BLOCKER"))
+ blocking-entry)
+ (should (string-equal "match(\"test&1\")" blocker))
+ (org-with-point-at heading
+ (setq blocking-entry (org-edna-process-form blocker 'condition)))
+ (should (string-equal (substring-no-properties blocking-entry)
+ "TODO Tagged Heading 1 :1:test:")))))
(ert-deftest org-edna-finder/file ()
(let* ((targets (org-edna-finder/file org-edna-test-file)))
@@ -499,16 +576,16 @@
(ert-deftest org-edna-finder/self ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find "82a4ac3d-9565-4f94-bc84-2bbfd8d7d96c" t))
+ (current (org-edna-find-test-heading
"82a4ac3d-9565-4f94-bc84-2bbfd8d7d96c"))
(targets (org-with-point-at current (org-edna-finder/self))))
(should (= (length targets) 1))
(should (equal current (nth 0 targets)))))
(ert-deftest org-edna-finder/siblings ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find org-edna-test-sibling-one-id t))
+ (current (org-edna-find-test-heading org-edna-test-sibling-one-id))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
`(,org-edna-test-sibling-one-id
,org-edna-test-sibling-two-id
,org-edna-test-sibling-three-id)))
@@ -518,9 +595,9 @@
(ert-deftest org-edna-finder/siblings-wrap ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find "72534efa-e932-460b-ae2d-f044a0074815" t))
+ (current (org-edna-find-test-heading
"72534efa-e932-460b-ae2d-f044a0074815"))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
'("06aca55e-ce09-46df-80d7-5b52e55d6505"
"82a4ac3d-9565-4f94-bc84-2bbfd8d7d96c")))
(targets (org-with-point-at current
@@ -530,9 +607,9 @@
(ert-deftest org-edna-finder/rest-of-siblings ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find "72534efa-e932-460b-ae2d-f044a0074815" t))
+ (current (org-edna-find-test-heading
"72534efa-e932-460b-ae2d-f044a0074815"))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
'("06aca55e-ce09-46df-80d7-5b52e55d6505")))
(targets (org-with-point-at current
(org-edna-finder/rest-of-siblings))))
@@ -541,9 +618,9 @@
(ert-deftest org-edna-finder/next-sibling ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find "72534efa-e932-460b-ae2d-f044a0074815" t))
+ (current (org-edna-find-test-heading
"72534efa-e932-460b-ae2d-f044a0074815"))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
'("06aca55e-ce09-46df-80d7-5b52e55d6505")))
(targets (org-with-point-at current
(org-edna-finder/next-sibling))))
@@ -552,9 +629,9 @@
(ert-deftest org-edna-finder/next-sibling-wrap-next ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find org-edna-test-sibling-two-id t))
+ (current (org-edna-find-test-heading org-edna-test-sibling-two-id))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
`(,org-edna-test-sibling-three-id)))
(targets (org-with-point-at current
(org-edna-finder/next-sibling-wrap))))
@@ -563,9 +640,9 @@
(ert-deftest org-edna-finder/next-sibling-wrap-wrap ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find org-edna-test-sibling-three-id t))
+ (current (org-edna-find-test-heading org-edna-test-sibling-three-id))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
`(,org-edna-test-sibling-one-id)))
(targets (org-with-point-at current
(org-edna-finder/next-sibling-wrap))))
@@ -574,9 +651,9 @@
(ert-deftest org-edna-finder/previous-sibling ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find "06aca55e-ce09-46df-80d7-5b52e55d6505" t))
+ (current (org-edna-find-test-heading
"06aca55e-ce09-46df-80d7-5b52e55d6505"))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
'("72534efa-e932-460b-ae2d-f044a0074815")))
(targets (org-with-point-at current
(org-edna-finder/previous-sibling))))
@@ -585,8 +662,8 @@
(ert-deftest org-edna-finder/first-child ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find org-edna-test-parent-id t))
- (first-child (list (org-id-find org-edna-test-sibling-one-id t)))
+ (current (org-edna-find-test-heading org-edna-test-parent-id))
+ (first-child (list (org-edna-find-test-heading
org-edna-test-sibling-one-id)))
(targets (org-with-point-at current
(org-edna-finder/first-child))))
(should (= (length targets) 1))
@@ -594,9 +671,9 @@
(ert-deftest org-edna-finder/children ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find org-edna-test-parent-id t))
+ (current (org-edna-find-test-heading org-edna-test-parent-id))
(children (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
`(,org-edna-test-sibling-one-id
,org-edna-test-sibling-two-id
,org-edna-test-sibling-three-id)))
@@ -607,8 +684,8 @@
(ert-deftest org-edna-finder/parent ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find org-edna-test-sibling-one-id t))
- (parent (list (org-id-find org-edna-test-parent-id t)))
+ (current (org-edna-find-test-heading org-edna-test-sibling-one-id))
+ (parent (list (org-edna-find-test-heading org-edna-test-parent-id)))
(targets (org-with-point-at current
(org-edna-finder/parent))))
(should (= (length targets) 1))
@@ -616,9 +693,9 @@
(ert-deftest org-edna-relatives/from-top ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find org-edna-test-sibling-one-id t))
+ (current (org-edna-find-test-heading org-edna-test-sibling-one-id))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
`(,org-edna-test-sibling-one-id)))
(targets (org-with-point-at current
(org-edna-finder/relatives 'from-top 1))))
@@ -626,9 +703,9 @@
(ert-deftest org-edna-relatives/from-bottom ()
(let* ((org-agenda-files `(,org-edna-test-file))
- (current (org-id-find org-edna-test-sibling-one-id t))
+ (current (org-edna-find-test-heading org-edna-test-sibling-one-id))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
`(,org-edna-test-sibling-three-id)))
(targets (org-with-point-at current
(org-edna-finder/relatives 'from-bottom 1))))
@@ -639,9 +716,9 @@
(target-list `(,org-edna-test-sibling-two-id))
(arg 'forward-wrap)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 1))))
@@ -652,9 +729,9 @@
(target-list `(,org-edna-test-sibling-one-id))
(arg 'forward-wrap)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 1))))
@@ -665,9 +742,9 @@
(target-list `(,org-edna-test-sibling-two-id))
(arg 'forward-no-wrap)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 1))))
@@ -678,9 +755,9 @@
(target-list nil)
(arg 'forward-no-wrap)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg))))
@@ -692,9 +769,9 @@
(arg 'backward-wrap)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg size))))
@@ -706,9 +783,9 @@
(arg 'backward-wrap)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg size))))
@@ -720,9 +797,9 @@
(arg 'backward-no-wrap)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg size))))
@@ -734,9 +811,9 @@
(arg 'backward-no-wrap)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg size))))
@@ -748,9 +825,9 @@
(arg 'walk-up)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg size))))
@@ -761,9 +838,9 @@
(target-list `(,org-edna-test-sibling-one-id))
(arg 'walk-up-with-self)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 1))))
@@ -774,9 +851,9 @@
(target-list `(,org-edna-test-sibling-one-id))
(arg 'walk-down)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 1))))
@@ -787,9 +864,9 @@
(target-list `(,org-edna-test-parent-id))
(arg 'walk-down-with-self)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 1))))
@@ -800,9 +877,9 @@
(target-list `(,org-edna-test-sibling-one-id))
(arg 'walk-down)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 1))))
@@ -821,9 +898,9 @@
(arg 'walk-down)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg size))))
@@ -840,9 +917,9 @@
(arg 'step-down)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg size))))
@@ -853,9 +930,9 @@
(target-list `(,org-edna-test-relative-child-with-todo))
(arg 'step-down)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 'todo-only))))
@@ -867,9 +944,9 @@
,org-edna-test-relative-child-with-done))
(arg 'step-down)
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg 'todo-and-done-only))))
@@ -886,9 +963,9 @@
(filter 'no-comment)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg filter size))))
@@ -905,9 +982,9 @@
(filter 'no-archive)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg filter size))))
@@ -920,9 +997,9 @@
(filter "+ARCHIVE")
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg filter size))))
@@ -939,9 +1016,9 @@
(filter "-ARCHIVE")
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg filter size))))
@@ -956,9 +1033,9 @@
(filter "Child Heading With .*")
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg filter size))))
@@ -976,9 +1053,9 @@
(sort 'reverse-sort)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets (org-with-point-at current
(org-edna-finder/relatives arg sort size))))
@@ -995,9 +1072,9 @@
(arg 'step-down)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list))
(targets ))
(should (equal siblings
@@ -1018,9 +1095,9 @@
(arg 'step-down)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list)))
(should (equal siblings
(org-with-point-at current
@@ -1040,9 +1117,9 @@
(arg 'step-down)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list)))
(should (equal siblings
(org-with-point-at current
@@ -1062,9 +1139,9 @@
(arg 'step-down)
(size (length target-list))
(org-agenda-files `(,org-edna-test-file))
- (current (org-id-find start-marker t))
+ (current (org-edna-find-test-heading start-marker))
(siblings (mapcar
- (lambda (uuid) (org-id-find uuid t))
+ (lambda (uuid) (org-edna-find-test-heading uuid))
target-list)))
(should (equal siblings
(org-with-point-at current
@@ -1073,6 +1150,28 @@
(org-with-point-at current
(org-edna-finder/relatives arg 'deadline-down size))))))
+(ert-deftest org-edna-relatives/sort-timestamp ()
+ (let* ((start-marker org-edna-test-relative-parent-one)
+ (target-list `(,org-edna-test-relative-child-with-todo
+ ,org-edna-test-relative-child-with-done
+ ,org-edna-test-relative-commented-child
+ ,org-edna-test-relative-child-with-children
+ ,org-edna-test-relative-standard-child
+ ,org-edna-test-relative-archived-child))
+ (arg 'step-down)
+ (size (length target-list))
+ (org-agenda-files `(,org-edna-test-file))
+ (current (org-edna-find-test-heading start-marker))
+ (siblings (mapcar
+ (lambda (uuid) (org-edna-find-test-heading uuid))
+ target-list)))
+ (should (equal siblings
+ (org-with-point-at current
+ (org-edna-finder/relatives arg 'timestamp-up size))))
+ (should (equal (nreverse siblings)
+ (org-with-point-at current
+ (org-edna-finder/relatives arg 'timestamp-down size))))))
+
(ert-deftest org-edna-cache/no-entry ()
(let* ((org-edna-finder-use-cache t)
(org-edna--finder-cache (make-hash-table :test 'equal)))
@@ -1129,44 +1228,38 @@
(should (not (org-edna--get-cache-entry 'org-edna-finder/match
'("test&1"))))))))
-;; Actions
+;;; Actions
(ert-deftest org-edna-action/todo-test ()
- (let* ((org-agenda-files `(,org-edna-test-file))
- (target (org-id-find "0d491588-7da3-43c5-b51a-87fbd34f79f7" t)))
- (org-with-point-at target
- (org-edna-action/todo! nil "DONE")
- (should (string-equal (org-entry-get nil "TODO") "DONE"))
- (org-edna-action/todo! nil "TODO")
- (should (string-equal (org-entry-get nil "TODO") "TODO"))
- (org-edna-action/todo! nil 'DONE)
- (should (string-equal (org-entry-get nil "TODO") "DONE"))
- (org-edna-action/todo! nil 'TODO)
- (should (string-equal (org-entry-get nil "TODO") "TODO")))))
+ (org-edna-with-test-heading "0d491588-7da3-43c5-b51a-87fbd34f79f7"
+ (org-edna-action/todo! nil "DONE")
+ (should (string-equal (org-entry-get nil "TODO") "DONE"))
+ (org-edna-action/todo! nil "TODO")
+ (should (string-equal (org-entry-get nil "TODO") "TODO"))
+ (org-edna-action/todo! nil 'DONE)
+ (should (string-equal (org-entry-get nil "TODO") "DONE"))
+ (org-edna-action/todo! nil 'TODO)
+ (should (string-equal (org-entry-get nil "TODO") "TODO"))))
+
+;; Scheduled
(ert-deftest org-edna-action-scheduled/wkdy ()
- ;; Override `current-time' so we can get a deterministic value
- (cl-letf* (((symbol-function 'current-time) (lambda () org-edna-test-time))
- (org-agenda-files `(,org-edna-test-file))
- (target (org-id-find "0d491588-7da3-43c5-b51a-87fbd34f79f7" t)))
- (org-with-point-at target
- (org-edna-action/scheduled! nil "Mon")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-17 Mon>"))
- (org-edna-action/scheduled! nil 'rm)
- (should (not (org-entry-get nil "SCHEDULED")))
- (org-edna-action/scheduled! nil "Mon 9:00")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-17 Mon 09:00>"))
- (org-edna-action/scheduled! nil 'rm)
- (should (not (org-entry-get nil "SCHEDULED"))))))
+ (org-edna-with-test-heading "0d491588-7da3-43c5-b51a-87fbd34f79f7"
+ (org-edna-action/scheduled! nil "Mon")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-17 Mon>"))
+ (org-edna-action/scheduled! nil 'rm)
+ (should (not (org-entry-get nil "SCHEDULED")))
+ (org-edna-action/scheduled! nil "Mon 9:00")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-17 Mon 09:00>"))
+ (org-edna-action/scheduled! nil 'rm)
+ (should (not (org-entry-get nil "SCHEDULED")))))
(ert-deftest org-edna-action-scheduled/cp ()
- (let* ((org-agenda-files `(,org-edna-test-file))
- (target (org-id-find "0d491588-7da3-43c5-b51a-87fbd34f79f7" t))
- (source (org-id-find "97e6b0f0-40c4-464f-b760-6e5ca9744eb5" t))
- (pairs '((cp . rm) (copy . remove) ("cp" . "rm") ("copy" .
"remove"))))
- (org-with-point-at target
+ (org-edna-with-test-heading "0d491588-7da3-43c5-b51a-87fbd34f79f7"
+ (let* ((source (org-edna-find-test-heading
"97e6b0f0-40c4-464f-b760-6e5ca9744eb5"))
+ (pairs '((cp . rm) (copy . remove) ("cp" . "rm") ("copy" .
"remove"))))
(dolist (pair pairs)
(org-edna-action/scheduled! source (car pair))
(should (string-equal (org-entry-get nil "SCHEDULED")
@@ -1175,226 +1268,355 @@
(should (not (org-entry-get nil "SCHEDULED")))))))
(ert-deftest org-edna-action-scheduled/inc ()
- ;; Override `current-time' so we can get a deterministic value
- (cl-letf* (((symbol-function 'current-time) (lambda () org-edna-test-time))
- (org-agenda-files `(,org-edna-test-file))
- (target (org-id-find "97e6b0f0-40c4-464f-b760-6e5ca9744eb5" t)))
- (org-with-point-at target
- ;; Time starts at Jan 15, 2000
- (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:00>"))
- ;; Increment 1 minute
- (org-edna-action/scheduled! nil "+1M")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:01>"))
- ;; Decrement 1 minute
- (org-edna-action/scheduled! nil "-1M")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:00>"))
- ;; +1 day
- (org-edna-action/scheduled! nil "+1d")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-16 Sun 00:00>"))
- ;; +1 hour from current time
- (org-edna-action/scheduled! nil "++1h")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 01:00>"))
- ;; Back to Saturday
- (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:00>"))
- ;; -1 day to Friday
- (org-edna-action/scheduled! nil "-1d")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-14 Fri 00:00>"))
- ;; Increment two days to the next weekday
- (org-edna-action/scheduled! nil "+2wkdy")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-17 Mon 00:00>"))
- ;; Increment one day, expected to land on a weekday
- (org-edna-action/scheduled! nil "+1wkdy")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-18 Tue 00:00>"))
- ;; Move forward 8 days, then backward until we find a weekend
- (org-edna-action/scheduled! nil "+8d -wknd")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-23 Sun 00:00>"))
- ;; Move forward one week, then forward until we find a weekday
- ;; (org-edna-action/scheduled! nil "+1w +wkdy")
- ;; (should (string-equal (org-entry-get nil "SCHEDULED")
- ;; "<2000-01-31 Mon 00:00>"))
- ;; Back to Saturday for other tests
- (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:00>")))))
+ (org-edna-with-test-heading "97e6b0f0-40c4-464f-b760-6e5ca9744eb5"
+ ;; Time starts at Jan 15, 2000
+ (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:00>"))
+ ;; Increment 1 minute
+ (org-edna-action/scheduled! nil "+1M")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:01>"))
+ ;; Decrement 1 minute
+ (org-edna-action/scheduled! nil "-1M")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:00>"))
+ ;; +1 day
+ (org-edna-action/scheduled! nil "+1d")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-16 Sun 00:00>"))
+ ;; +1 hour from current time
+ (org-edna-action/scheduled! nil "++1h")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 01:00>"))
+ ;; Back to Saturday
+ (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:00>"))
+ ;; -1 day to Friday
+ (org-edna-action/scheduled! nil "-1d")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-14 Fri 00:00>"))
+ ;; Increment two days to the next weekday
+ (org-edna-action/scheduled! nil "+2wkdy")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-17 Mon 00:00>"))
+ ;; Increment one day, expected to land on a weekday
+ (org-edna-action/scheduled! nil "+1wkdy")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-18 Tue 00:00>"))
+ ;; Move forward 8 days, then backward until we find a weekend
+ (org-edna-action/scheduled! nil "+8d -wknd")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-23 Sun 00:00>"))
+ ;; Move forward one week, then forward until we find a weekday
+ ;; (org-edna-action/scheduled! nil "+1w +wkdy")
+ ;; (should (string-equal (org-entry-get nil "SCHEDULED")
+ ;; "<2000-01-31 Mon 00:00>"))
+ ;; Back to Saturday for other tests
+ (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:00>"))))
(ert-deftest org-edna-action-scheduled/landing ()
"Test landing arguments to scheduled increment."
- ;; Override `current-time' so we can get a deterministic value
- (cl-letf* (((symbol-function 'current-time) (lambda () org-edna-test-time))
- (org-agenda-files `(,org-edna-test-file))
- (target (org-id-find "97e6b0f0-40c4-464f-b760-6e5ca9744eb5" t)))
- (org-with-point-at target
- ;; Time starts at Jan 15, 2000
- (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:00>"))
- ;; Move forward 10 days, then backward until we find a weekend
- (org-edna-action/scheduled! nil "+10d -wknd")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-23 Sun 00:00>"))
- ;; Move forward one week, then forward until we find a weekday
- (org-edna-action/scheduled! nil "+1w +wkdy")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-31 Mon 00:00>"))
- ;; Back to Saturday for other tests
- (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:00>")))))
+ (org-edna-with-test-heading "97e6b0f0-40c4-464f-b760-6e5ca9744eb5"
+ ;; Time starts at Jan 15, 2000
+ (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:00>"))
+ ;; Move forward 10 days, then backward until we find a weekend
+ (org-edna-action/scheduled! nil "+10d -wknd")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-23 Sun 00:00>"))
+ ;; Move forward one week, then forward until we find a weekday
+ (org-edna-action/scheduled! nil "+1w +wkdy")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-31 Mon 00:00>"))
+ ;; Back to Saturday for other tests
+ (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:00>"))))
(ert-deftest org-edna-action-scheduled/landing-no-hour ()
"Test landing arguments to scheduled increment, without hour."
- ;; Override `current-time' so we can get a deterministic value
- (cl-letf* (((symbol-function 'current-time) (lambda () org-edna-test-time))
- (org-agenda-files `(,org-edna-test-file))
- (target (org-id-find "caf27724-0887-4565-9765-ed2f1edcfb16" t)))
- (org-with-point-at target
- ;; Time starts at Jan 1, 2017
- (org-edna-action/scheduled! nil "2017-01-01 Sun")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2017-01-01 Sun>"))
- ;; Move forward 10 days, then backward until we find a weekend
- (org-edna-action/scheduled! nil "+10d -wknd")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2017-01-08 Sun>"))
- ;; Move forward one week, then forward until we find a weekday
- (org-edna-action/scheduled! nil "+1w +wkdy")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2017-01-16 Mon>"))
- ;; Back to Saturday for other tests
- (org-edna-action/scheduled! nil "2017-01-01 Sun")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2017-01-01 Sun>")))))
+ (org-edna-with-test-heading "caf27724-0887-4565-9765-ed2f1edcfb16"
+ ;; Time starts at Jan 1, 2017
+ (org-edna-action/scheduled! nil "2017-01-01 Sun")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2017-01-01 Sun>"))
+ ;; Move forward 10 days, then backward until we find a weekend
+ (org-edna-action/scheduled! nil "+10d -wknd")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2017-01-08 Sun>"))
+ ;; Move forward one week, then forward until we find a weekday
+ (org-edna-action/scheduled! nil "+1w +wkdy")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2017-01-16 Mon>"))
+ ;; Back to Saturday for other tests
+ (org-edna-action/scheduled! nil "2017-01-01 Sun")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2017-01-01 Sun>"))))
(ert-deftest org-edna-action-scheduled/float ()
- (cl-letf* (((symbol-function 'current-time) (lambda () org-edna-test-time))
- (org-agenda-files `(,org-edna-test-file))
- (target (org-id-find "97e6b0f0-40c4-464f-b760-6e5ca9744eb5" t)))
- (org-with-point-at target
- ;; Time starts at Jan 15, 2000
- (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:00>"))
- ;; The third Tuesday of next month (Feb 15th)
- (org-edna-action/scheduled! nil "float 3 Tue")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-02-15 Tue 00:00>"))
- ;; The second Friday of the following May (May 12th)
- (org-edna-action/scheduled! nil "float 2 5 May")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-05-12 Fri 00:00>"))
- ;; Move forward to the second Wednesday of the next month (June 14th)
- (org-edna-action/scheduled! nil "float 2 Wednesday")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-06-14 Wed 00:00>"))
- ;; Move forward to the first Thursday in the following Jan (Jan 4th,
2001)
- (org-edna-action/scheduled! nil "float 1 4 Jan")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2001-01-04 Thu 00:00>"))
- ;; The fourth Monday in Feb, 2000 (Feb 28th)
- (org-edna-action/scheduled! nil "float ++4 monday")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-02-28 Mon 00:00>"))
- ;; The second Monday after Mar 12th, 2000 (Mar 20th)
- (org-edna-action/scheduled! nil "float 2 monday Mar 12")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-03-20 Mon 00:00>"))
- ;; Back to Saturday for other tests
- (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
- (should (string-equal (org-entry-get nil "SCHEDULED")
- "<2000-01-15 Sat 00:00>")))))
+ (org-edna-with-test-heading "97e6b0f0-40c4-464f-b760-6e5ca9744eb5"
+ ;; Time starts at Jan 15, 2000
+ (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:00>"))
+ ;; The third Tuesday of next month (Feb 15th)
+ (org-edna-action/scheduled! nil "float 3 Tue")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-02-15 Tue 00:00>"))
+ ;; The second Friday of the following May (May 12th)
+ (org-edna-action/scheduled! nil "float 2 5 May")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-05-12 Fri 00:00>"))
+ ;; Move forward to the second Wednesday of the next month (June 14th)
+ (org-edna-action/scheduled! nil "float 2 Wednesday")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-06-14 Wed 00:00>"))
+ ;; Move forward to the first Thursday in the following Jan (Jan 4th, 2001)
+ (org-edna-action/scheduled! nil "float 1 4 Jan")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2001-01-04 Thu 00:00>"))
+ ;; The fourth Monday in Feb, 2000 (Feb 28th)
+ (org-edna-action/scheduled! nil "float ++4 monday")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-02-28 Mon 00:00>"))
+ ;; The second Monday after Mar 12th, 2000 (Mar 20th)
+ (org-edna-action/scheduled! nil "float 2 monday Mar 12")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-03-20 Mon 00:00>"))
+ ;; Back to Saturday for other tests
+ (org-edna-action/scheduled! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "SCHEDULED")
+ "<2000-01-15 Sat 00:00>"))))
+
+(ert-deftest org-edna-action-deadline/wkdy ()
+ (org-edna-with-test-heading "0d491588-7da3-43c5-b51a-87fbd34f79f7"
+ (org-edna-action/deadline! nil "Mon")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-17 Mon>"))
+ (org-edna-action/deadline! nil 'rm)
+ (should (not (org-entry-get nil "DEADLINE")))
+ (org-edna-action/deadline! nil "Mon 9:00")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-17 Mon 09:00>"))
+ (org-edna-action/deadline! nil 'rm)
+ (should (not (org-entry-get nil "DEADLINE")))))
+
+(ert-deftest org-edna-action-deadline/cp ()
+ (org-edna-with-test-heading "0d491588-7da3-43c5-b51a-87fbd34f79f7"
+ (let* ((source (org-edna-find-test-heading
"97e6b0f0-40c4-464f-b760-6e5ca9744eb5"))
+ (pairs '((cp . rm) (copy . remove) ("cp" . "rm") ("copy" .
"remove"))))
+ (dolist (pair pairs)
+ (org-edna-action/deadline! source (car pair))
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))
+ (org-edna-action/deadline! source (cdr pair))
+ (should (not (org-entry-get nil "DEADLINE")))))))
+
+(ert-deftest org-edna-action-deadline/inc ()
+ (org-edna-with-test-heading "97e6b0f0-40c4-464f-b760-6e5ca9744eb5"
+ ;; Time starts at Jan 15, 2000
+ (org-edna-action/deadline! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))
+ ;; Increment 1 minute
+ (org-edna-action/deadline! nil "+1M")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:01>"))
+ ;; Decrement 1 minute
+ (org-edna-action/deadline! nil "-1M")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))
+ ;; +1 day
+ (org-edna-action/deadline! nil "+1d")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-16 Sun 00:00>"))
+ ;; +1 hour from current time
+ (org-edna-action/deadline! nil "++1h")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 01:00>"))
+ ;; Back to Saturday
+ (org-edna-action/deadline! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))
+ ;; -1 day to Friday
+ (org-edna-action/deadline! nil "-1d")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-14 Fri 00:00>"))
+ ;; Increment two days to the next weekday
+ (org-edna-action/deadline! nil "+2wkdy")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-17 Mon 00:00>"))
+ ;; Increment one day, expected to land on a weekday
+ (org-edna-action/deadline! nil "+1wkdy")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-18 Tue 00:00>"))
+ ;; Move forward 8 days, then backward until we find a weekend
+ (org-edna-action/deadline! nil "+8d -wknd")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-23 Sun 00:00>"))
+ ;; Move forward one week, then forward until we find a weekday
+ ;; (org-edna-action/deadline! nil "+1w +wkdy")
+ ;; (should (string-equal (org-entry-get nil "DEADLINE")
+ ;; "<2000-01-31 Mon 00:00>"))
+ ;; Back to Saturday for other tests
+ (org-edna-action/deadline! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))))
+
+(ert-deftest org-edna-action-deadline/landing ()
+ "Test landing arguments to deadline increment."
+ (org-edna-with-test-heading "97e6b0f0-40c4-464f-b760-6e5ca9744eb5"
+ ;; Time starts at Jan 15, 2000
+ (org-edna-action/deadline! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))
+ ;; Move forward 10 days, then backward until we find a weekend
+ (org-edna-action/deadline! nil "+10d -wknd")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-23 Sun 00:00>"))
+ ;; Move forward one week, then forward until we find a weekday
+ (org-edna-action/deadline! nil "+1w +wkdy")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-31 Mon 00:00>"))
+ ;; Back to Saturday for other tests
+ (org-edna-action/deadline! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))))
+
+(ert-deftest org-edna-action-deadline/landing-no-hour ()
+ "Test landing arguments to deadline increment, without hour."
+ (org-edna-with-test-heading "caf27724-0887-4565-9765-ed2f1edcfb16"
+ ;; Time starts at Jan 1, 2017
+ (org-edna-action/deadline! nil "2017-01-01 Sun")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2017-01-01 Sun>"))
+ ;; Move forward 10 days, then backward until we find a weekend
+ (org-edna-action/deadline! nil "+10d -wknd")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2017-01-08 Sun>"))
+ ;; Move forward one week, then forward until we find a weekday
+ (org-edna-action/deadline! nil "+1w +wkdy")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2017-01-16 Mon>"))
+ ;; Back to Saturday for other tests
+ (org-edna-action/deadline! nil "2017-01-01 Sun")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2017-01-01 Sun>"))))
+
+(ert-deftest org-edna-action-deadline/float ()
+ (org-edna-with-test-heading "97e6b0f0-40c4-464f-b760-6e5ca9744eb5"
+ ;; Time starts at Jan 15, 2000
+ (org-edna-action/deadline! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))
+ ;; The third Tuesday of next month (Feb 15th)
+ (org-edna-action/deadline! nil "float 3 Tue")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-02-15 Tue 00:00>"))
+ ;; The second Friday of the following May (May 12th)
+ (org-edna-action/deadline! nil "float 2 5 May")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-05-12 Fri 00:00>"))
+ ;; Move forward to the second Wednesday of the next month (June 14th)
+ (org-edna-action/deadline! nil "float 2 Wednesday")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-06-14 Wed 00:00>"))
+ ;; Move forward to the first Thursday in the following Jan (Jan 4th, 2001)
+ (org-edna-action/deadline! nil "float 1 4 Jan")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2001-01-04 Thu 00:00>"))
+ ;; The fourth Monday in Feb, 2000 (Feb 28th)
+ (org-edna-action/deadline! nil "float ++4 monday")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-02-28 Mon 00:00>"))
+ ;; The second Monday after Mar 12th, 2000 (Mar 20th)
+ (org-edna-action/deadline! nil "float 2 monday Mar 12")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-03-20 Mon 00:00>"))
+ ;; Back to Saturday for other tests
+ (org-edna-action/deadline! nil "2000-01-15 Sat 00:00")
+ (should (string-equal (org-entry-get nil "DEADLINE")
+ "<2000-01-15 Sat 00:00>"))))
(ert-deftest org-edna-action-tag ()
- (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one)))
- (org-with-point-at pom
- (org-edna-action/tag! nil "tag")
- (should (equal (org-get-tags) '("tag")))
- (org-edna-action/tag! nil "")
- (should (equal (org-get-tags) '(""))))))
+ (org-edna-with-test-heading org-edna-test-id-heading-one
+ (org-edna-action/tag! nil "tag")
+ (should (equal (org-get-tags) '("tag")))
+ (org-edna-action/tag! nil "")
+ (should (equal (org-get-tags) '("")))))
(ert-deftest org-edna-action-property ()
- (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one)))
- (org-with-point-at pom
- (org-edna-action/set-property! nil "TEST" "1")
- (should (equal (org-entry-get nil "TEST") "1"))
- (org-edna-action/delete-property! nil "TEST")
- (should-not (org-entry-get nil "TEST")))))
+ (org-edna-with-test-heading org-edna-test-id-heading-one
+ (org-edna-action/set-property! nil "TEST" "1")
+ (should (equal (org-entry-get nil "TEST") "1"))
+ (org-edna-action/delete-property! nil "TEST")
+ (should-not (org-entry-get nil "TEST"))))
(ert-deftest org-edna-action-property/inc-dec ()
- (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one)))
- (org-with-point-at pom
- (org-edna-action/set-property! nil "TEST" "1")
- (should (equal (org-entry-get nil "TEST") "1"))
- (org-edna-action/set-property! nil "TEST" 'inc)
- (should (equal (org-entry-get nil "TEST") "2"))
- (org-edna-action/set-property! nil "TEST" 'dec)
- (should (equal (org-entry-get nil "TEST") "1"))
- (org-edna-action/delete-property! nil "TEST")
- (should-not (org-entry-get nil "TEST"))
- (should-error (org-edna-action/set-property! nil "TEST" 'inc))
- (should-error (org-edna-action/set-property! nil "TEST" 'dec))
- (org-edna-action/set-property! nil "TEST" "a")
- (should (equal (org-entry-get nil "TEST") "a"))
- (should-error (org-edna-action/set-property! nil "TEST" 'inc))
- (should-error (org-edna-action/set-property! nil "TEST" 'dec))
- (org-edna-action/delete-property! nil "TEST")
- (should-not (org-entry-get nil "TEST")))))
+ (org-edna-with-test-heading org-edna-test-id-heading-one
+ (org-edna-action/set-property! nil "TEST" "1")
+ (should (equal (org-entry-get nil "TEST") "1"))
+ (org-edna-action/set-property! nil "TEST" 'inc)
+ (should (equal (org-entry-get nil "TEST") "2"))
+ (org-edna-action/set-property! nil "TEST" 'dec)
+ (should (equal (org-entry-get nil "TEST") "1"))
+ (org-edna-action/delete-property! nil "TEST")
+ (should-not (org-entry-get nil "TEST"))
+ (should-error (org-edna-action/set-property! nil "TEST" 'inc))
+ (should-error (org-edna-action/set-property! nil "TEST" 'dec))
+ (org-edna-action/set-property! nil "TEST" "a")
+ (should (equal (org-entry-get nil "TEST") "a"))
+ (should-error (org-edna-action/set-property! nil "TEST" 'inc))
+ (should-error (org-edna-action/set-property! nil "TEST" 'dec))
+ (org-edna-action/delete-property! nil "TEST")
+ (should-not (org-entry-get nil "TEST"))))
(ert-deftest org-edna-action-property/next-prev ()
- (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one)))
- (org-with-point-at pom
- (org-edna-action/set-property! nil "TEST" "a")
- (should (equal (org-entry-get nil "TEST") "a"))
- (should-error (org-edna-action/set-property! nil "TEST" 'next))
- (should-error (org-edna-action/set-property! nil "TEST" 'prev))
- (should-error (org-edna-action/set-property! nil "TEST" 'previous))
- (org-edna-action/delete-property! nil "TEST")
- (should-not (org-entry-get nil "TEST"))
- ;; Test moving forwards
- (org-edna-action/set-property! nil "COUNTER" "a")
- (should (equal (org-entry-get nil "COUNTER") "a"))
- (org-edna-action/set-property! nil "COUNTER" 'next)
- (should (equal (org-entry-get nil "COUNTER") "b"))
- ;; Test moving forwards past the last one
- (org-edna-action/set-property! nil "COUNTER" "d")
- (should (equal (org-entry-get nil "COUNTER") "d"))
- (org-edna-action/set-property! nil "COUNTER" 'next)
- (should (equal (org-entry-get nil "COUNTER") "a"))
- ;; Test moving backwards past the first one
- (org-edna-action/set-property! nil "COUNTER" 'prev)
- (should (equal (org-entry-get nil "COUNTER") "d"))
- ;; Test moving backwards normally
- (org-edna-action/set-property! nil "COUNTER" 'previous)
- (should (equal (org-entry-get nil "COUNTER") "c"))
- (org-edna-action/delete-property! nil "COUNTER")
- (should-not (org-entry-get nil "COUNTER")))))
+ (org-edna-with-test-heading org-edna-test-id-heading-one
+ (org-edna-action/set-property! nil "TEST" "a")
+ (should (equal (org-entry-get nil "TEST") "a"))
+ (should-error (org-edna-action/set-property! nil "TEST" 'next))
+ (should-error (org-edna-action/set-property! nil "TEST" 'prev))
+ (should-error (org-edna-action/set-property! nil "TEST" 'previous))
+ (org-edna-action/delete-property! nil "TEST")
+ (should-not (org-entry-get nil "TEST"))
+ ;; Test moving forwards
+ (org-edna-action/set-property! nil "COUNTER" "a")
+ (should (equal (org-entry-get nil "COUNTER") "a"))
+ (org-edna-action/set-property! nil "COUNTER" 'next)
+ (should (equal (org-entry-get nil "COUNTER") "b"))
+ ;; Test moving forwards past the last one
+ (org-edna-action/set-property! nil "COUNTER" "d")
+ (should (equal (org-entry-get nil "COUNTER") "d"))
+ (org-edna-action/set-property! nil "COUNTER" 'next)
+ (should (equal (org-entry-get nil "COUNTER") "a"))
+ ;; Test moving backwards past the first one
+ (org-edna-action/set-property! nil "COUNTER" 'prev)
+ (should (equal (org-entry-get nil "COUNTER") "d"))
+ ;; Test moving backwards normally
+ (org-edna-action/set-property! nil "COUNTER" 'previous)
+ (should (equal (org-entry-get nil "COUNTER") "c"))
+ (org-edna-action/delete-property! nil "COUNTER")
+ (should-not (org-entry-get nil "COUNTER"))))
(ert-deftest org-edna-action-clock ()
- (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one)))
- (org-with-point-at pom
- (org-edna-action/clock-in! nil)
- (should (org-clocking-p))
- (should (equal org-clock-hd-marker pom))
- (org-edna-action/clock-out! nil)
- (should-not (org-clocking-p)))))
+ (org-edna-with-test-heading org-edna-test-id-heading-one
+ (org-edna-action/clock-in! nil)
+ (should (org-clocking-p))
+ (should (equal org-clock-hd-marker (point-marker)))
+ (org-edna-action/clock-out! nil)
+ (should-not (org-clocking-p))))
(ert-deftest org-edna-action-priority ()
- (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one))
- (org-lowest-priority ?C)
- (org-highest-priority ?A)
- (org-default-priority ?B))
- (org-with-point-at pom
+ (org-edna-with-test-heading org-edna-test-id-heading-one
+ (let ((org-lowest-priority ?C)
+ (org-highest-priority ?A)
+ (org-default-priority ?B))
(org-edna-action/set-priority! nil "A")
(should (equal (org-entry-get nil "PRIORITY") "A"))
(org-edna-action/set-priority! nil 'down)
@@ -1407,45 +1629,49 @@
(should (equal (org-entry-get nil "PRIORITY") "B")))))
(ert-deftest org-edna-action-effort ()
- (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one)))
- (org-with-point-at pom
- (org-edna-action/set-effort! nil "0:01")
- (should (equal (org-entry-get nil "EFFORT") "0:01"))
- (org-edna-action/set-effort! nil 'increment)
- (should (equal (org-entry-get nil "EFFORT") "0:02"))
- (org-entry-delete nil "EFFORT"))))
+ (org-edna-with-test-heading org-edna-test-id-heading-one
+ (org-edna-action/set-effort! nil "0:01")
+ (should (equal (org-entry-get nil "EFFORT") "0:01"))
+ (org-edna-action/set-effort! nil 'increment)
+ (should (equal (org-entry-get nil "EFFORT") "0:02"))
+ (org-entry-delete nil "EFFORT")))
(ert-deftest org-edna-action-archive ()
- (let ((org-archive-save-context-info '(todo))
- (pom (org-edna-find-test-heading org-edna-test-archive-heading))
- ;; Archive it to the same location
- (org-archive-location "::** Archive")
- (org-edna-prompt-for-archive nil))
- (org-with-point-at pom
+ (org-edna-with-test-heading org-edna-test-archive-heading
+ (let* ((org-archive-save-context-info '(todo))
+ ;; Archive it to the same location
+ (org-archive-location "::** Archive")
+ ;; We're non-interactive, so no prompt.
+ (org-edna-prompt-for-archive nil))
(org-edna-action/archive! nil)
(should (equal (org-entry-get nil "ARCHIVE_TODO") "TODO"))
(org-entry-delete nil "ARCHIVE_TODO"))))
(ert-deftest org-edna-action-chain ()
- (let ((old-pom (org-edna-find-test-heading org-edna-test-id-heading-one))
- (new-pom (org-edna-find-test-heading org-edna-test-id-heading-two)))
- (org-entry-put old-pom "TEST" "1")
- (org-with-point-at new-pom
- (org-edna-action/chain! old-pom "TEST")
- (should (equal (org-entry-get nil "TEST") "1")))
- (org-entry-delete old-pom "TEST")
- (org-entry-delete new-pom "TEST")))
+ (org-edna-test-setup
+ (let ((old-pom (org-edna-find-test-heading org-edna-test-id-heading-one))
+ (new-pom (org-edna-find-test-heading org-edna-test-id-heading-two)))
+ (org-edna-protect-test-file
+ (org-entry-put old-pom "TEST" "1")
+ (org-with-point-at new-pom
+ (org-edna-action/chain! old-pom "TEST")
+ (should (equal (org-entry-get nil "TEST") "1")))
+ (org-entry-delete old-pom "TEST")
+ (org-entry-delete new-pom "TEST")))))
-;; Conditions
+;;; Conditions
(defun org-edna-test-condition-form (func-sym pom-true pom-false block-true
block-false &rest args)
- (org-with-point-at pom-true
- (should-not (apply func-sym t args))
- (should (equal (apply func-sym nil args) block-true)))
- (org-with-point-at pom-false
- (should (equal (apply func-sym t args) block-false))
- (should-not (apply func-sym nil args))))
+ (org-edna-test-setup
+ (let* ((block-true (or block-true (org-with-point-at pom-true
(org-get-heading))))
+ (block-false (or block-false (org-with-point-at pom-false
(org-get-heading)))))
+ (org-with-point-at pom-true
+ (should-not (apply func-sym t args))
+ (should (equal (apply func-sym nil args) block-true)))
+ (org-with-point-at pom-false
+ (should (equal (apply func-sym t args) block-false))
+ (should-not (apply func-sym nil args))))))
(ert-deftest org-edna-condition-done ()
(let* ((pom-done (org-edna-find-test-heading org-edna-test-id-heading-four))
@@ -1522,30 +1748,444 @@
block-true block-false
string)))
+(ert-deftest org-edna-condition/has-tags ()
+ (let* ((pom-true (org-edna-find-test-heading
"0fa0d4dd-40f2-4251-a558-4c6e2898c2df"))
+ (pom-false (org-edna-find-test-heading org-edna-test-id-heading-one))
+ (block-true (org-with-point-at pom-true (org-get-heading)))
+ (block-false (org-with-point-at pom-false (org-get-heading))))
+ (org-edna-test-condition-form 'org-edna-condition/has-tags?
+ pom-true pom-false
+ block-true block-false
+ "test")))
+
+(ert-deftest org-edna-condition/matches-tags ()
+ (org-edna-test-condition-form
+ 'org-edna-condition/matches?
+ (org-edna-find-test-heading "0fa0d4dd-40f2-4251-a558-4c6e2898c2df")
+ (org-edna-find-test-heading org-edna-test-id-heading-one)
+ nil nil
+ "1&test")
+ (org-edna-test-condition-form
+ 'org-edna-condition/matches?
+ (org-edna-find-test-heading org-edna-test-id-heading-four)
+ (org-edna-find-test-heading "0fa0d4dd-40f2-4251-a558-4c6e2898c2df")
+ nil nil
+ "TODO==\"DONE\""))
+
-;; Consideration
+;;; Consideration
+
+(ert-deftest org-edna-consideration/any ()
+ (let ((blocks-all-blocking `("a" "c" "b"))
+ (blocks-some-blocking `("a" nil "b"))
+ (blocks-no-blocking `(nil nil nil)))
+ (should (string-equal (org-edna-handle-consideration 'any
blocks-all-blocking) "a"))
+ (should (string-equal (org-edna-handle-consideration 'any
blocks-some-blocking) "a"))
+ (should (not (org-edna-handle-consideration 'any blocks-no-blocking)))))
(ert-deftest org-edna-consideration/all ()
- (let ((blocks-blocking `("a" nil "b"))
+ (let ((blocks-all-blocking `("a" "c" "b"))
+ (blocks-some-blocking `(nil "c" nil))
(blocks-no-blocking `(nil nil nil)))
- (should (string-equal (org-edna-handle-consideration 'all blocks-blocking)
"a"))
+ (should (string-equal (org-edna-handle-consideration 'all
blocks-all-blocking) "a"))
+ (should (not (org-edna-handle-consideration 'all blocks-some-blocking)))
(should (not (org-edna-handle-consideration 'all blocks-no-blocking)))))
(ert-deftest org-edna-consideration/integer ()
- (let ((blocks-blocking `("a" "c" "b"))
- (blocks-no-blocking `("a" nil "b"))
- (blocks-empty `(nil nil nil)))
- (should (string-equal (org-edna-handle-consideration 1 blocks-blocking)
"a"))
- (should (not (org-edna-handle-consideration 1 blocks-no-blocking)))
- (should (not (org-edna-handle-consideration 1 blocks-empty)))))
+ (let ((blocks-all-blocking `("a" "c" "b"))
+ (blocks-some-blocking `("a" nil "b"))
+ (blocks-no-blocking `(nil nil nil)))
+ (should (string-equal (org-edna-handle-consideration 2
blocks-all-blocking) "a"))
+ (should (string-equal (org-edna-handle-consideration 2
blocks-some-blocking) "a"))
+ (should (not (org-edna-handle-consideration 2 blocks-no-blocking)))))
(ert-deftest org-edna-consideration/float ()
- (let ((blocks-blocking `("a" "c" "b"))
- (blocks-no-blocking `("a" nil "b"))
- (blocks-empty `(nil nil nil)))
- (should (string-equal (org-edna-handle-consideration 0.25 blocks-blocking)
"a"))
- (should (not (org-edna-handle-consideration 0.25 blocks-no-blocking)))
- (should (not (org-edna-handle-consideration 0.25 blocks-empty)))))
+ (let ((blocks-all-blocking `("a" "c" "b"))
+ (blocks-some-blocking `("a" nil "b"))
+ (blocks-no-blocking `(nil nil nil)))
+ (should (string-equal (org-edna-handle-consideration 0.25
blocks-all-blocking) "a"))
+ (should (string-equal (org-edna-handle-consideration 0.25
blocks-some-blocking) "a"))
+ (should (not (org-edna-handle-consideration 0.25 blocks-no-blocking)))))
+
+
+;;; Full Run-through Tests from the Documentation
+
+(defmacro org-edna-doc-test-setup (heading-id &rest body)
+ (declare (indent 1))
+ `(org-edna-with-test-heading ,heading-id
+ (save-restriction
+ ;; Only allow operating on the current tree
+ (org-narrow-to-subtree)
+ ;; Show the entire subtree
+ (outline-show-all)
+ ,@body)))
+
+(ert-deftest org-edna-doc-test/ancestors ()
+ (org-edna-doc-test-setup "24a0c3bb-7e69-4e9e-bb98-5aba2ff17bb1"
+ (pcase-let* ((`(,heading1-pom ,heading2-pom ,heading3-pom ,heading4-pom
,heading5-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify that we can't change the TODO state to DONE
+ (should (org-edna-test-check-block heading5-pom "Initial state of
heading 5"))
+ ;; Change the state at 4 to DONE
+ (org-edna-test-mark-done heading4-pom)
+ ;; Verify that ALL ancestors need to be changed
+ (should (org-edna-test-check-block heading5-pom "Heading 5 after parent
changed"))
+ (org-edna-test-mark-done heading1-pom heading3-pom)
+ ;; Only need 1, 3, and 4 to change 5
+ (should (not (org-edna-test-check-block heading5-pom
+ "Heading 5 after all parents
changed")))
+ ;; Change the state back to TODO on all of them
+ (org-edna-test-mark-todo heading1-pom heading3-pom heading4-pom
heading5-pom))))
+
+(ert-deftest org-edna-doc-test/ancestors-cache ()
+ (let ((org-edna-finder-use-cache t))
+ (org-edna-doc-test-setup "24a0c3bb-7e69-4e9e-bb98-5aba2ff17bb1"
+ (pcase-let* ((`(,heading1-pom ,heading2-pom ,heading3-pom ,heading4-pom
,heading5-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify that we can't change the TODO state to DONE
+ (should (org-edna-test-check-block heading5-pom "Initial state of
heading 5"))
+ ;; Change the state at 4 to DONE
+ (org-edna-test-mark-done heading4-pom)
+ ;; Verify that ALL ancestors need to be changed
+ (should (org-edna-test-check-block heading5-pom "Heading 5 after
parent changed"))
+ (org-edna-test-mark-done heading1-pom heading3-pom)
+ ;; Only need 1, 3, and 4 to change 5
+ (should (not (org-edna-test-check-block heading5-pom
+ "Heading 5 after all parents
changed")))
+ ;; Change the state back to TODO on all of them
+ (org-edna-test-mark-todo heading1-pom heading3-pom heading4-pom
heading5-pom)))))
+
+(ert-deftest org-edna-doc-test/descendants ()
+ (org-edna-doc-test-setup "cc18dc74-00e8-4081-b46f-e36800041fe7"
+ (pcase-let* ((`(,heading1-pom ,heading2-pom ,heading3-pom ,heading4-pom
,heading5-pom)
+ (org-edna-test-children-marks)))
+ (should (org-edna-test-check-block heading1-pom "Heading 1 initial
state"))
+ ;; Change the state at 2 to DONE
+ (org-edna-test-mark-done heading2-pom)
+ ;; Verify that ALL descendants need to be changed
+ (should (org-edna-test-check-block heading1-pom "Heading 1 after
changing 2"))
+ ;; Try 3
+ (org-edna-test-mark-done heading3-pom)
+ ;; Verify that ALL descendants need to be changed
+ (should (org-edna-test-check-block heading1-pom "Heading 1 after
changing 3"))
+ ;; Try 4
+ (org-edna-test-mark-done heading4-pom)
+ ;; Verify that ALL descendants need to be changed
+ (should (org-edna-test-check-block heading1-pom "Heading 1 after
changing 4"))
+ ;; Try 5
+ (org-edna-test-mark-done heading5-pom)
+ ;; Verify that ALL descendants need to be changed
+ (should (not (org-edna-test-check-block heading1-pom "Heading 1 after
changing 5"))))))
+
+(ert-deftest org-edna-doc-test/descendants-cache ()
+ (let ((org-edna-finder-use-cache t))
+ (org-edna-doc-test-setup "cc18dc74-00e8-4081-b46f-e36800041fe7"
+ (pcase-let* ((`(,heading1-pom ,heading2-pom ,heading3-pom ,heading4-pom
,heading5-pom)
+ (org-edna-test-children-marks)))
+ (should (org-edna-test-check-block heading1-pom "Heading 1 initial
state"))
+ ;; Change the state at 2 to DONE
+ (org-edna-test-mark-done heading2-pom)
+ ;; Verify that ALL descendants need to be changed
+ (should (org-edna-test-check-block heading1-pom "Heading 1 after
changing 2"))
+ ;; Try 3
+ (org-edna-test-mark-done heading3-pom)
+ ;; Verify that ALL descendants need to be changed
+ (should (org-edna-test-check-block heading1-pom "Heading 1 after
changing 3"))
+ ;; Try 4
+ (org-edna-test-mark-done heading4-pom)
+ ;; Verify that ALL descendants need to be changed
+ (should (org-edna-test-check-block heading1-pom "Heading 1 after
changing 4"))
+ ;; Try 5
+ (org-edna-test-mark-done heading5-pom)
+ ;; Verify that ALL descendants need to be changed
+ (should (not (org-edna-test-check-block heading1-pom "Heading 1 after
changing 5")))))))
+
+(ert-deftest org-edna-doc-test/laundry ()
+ "Test for the \"laundry\" example in the documentation."
+ (org-edna-doc-test-setup "e57ce099-9f37-47f4-a6bb-61a84eb1fbbe"
+ (pcase-let* ((`(,heading1-pom ,heading2-pom ,heading3-pom ,heading4-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify that headings 2, 3, and 4 are all blocked
+ (should (org-edna-test-check-block heading2-pom
+ "Initial attempt to change heading
2"))
+ (should (org-edna-test-check-block heading3-pom
+ "Initial attempt to change heading
3"))
+ (should (org-edna-test-check-block heading4-pom
+ "Initial attempt to change heading
4"))
+ ;; Mark heading 1 as DONE
+ (should (not (org-edna-test-check-block heading1-pom
+ "Set heading 1 to DONE")))
+ ;; Only heading 2 should have a scheduled time
+ (should (string-equal (org-entry-get heading2-pom "SCHEDULED")
+ "<2000-01-15 Sat 01:00>"))
+ (should (not (org-entry-get heading3-pom "SCHEDULED")))
+ (should (not (org-entry-get heading4-pom "SCHEDULED")))
+ ;; The others should still be blocked.
+ (should (org-edna-test-check-block heading3-pom
+ "Second attempt to change heading 3"))
+ (should (org-edna-test-check-block heading4-pom
+ "Second attempt to change heading 4"))
+ ;; Try changing heading 2
+ (should (not (org-edna-test-check-block heading2-pom
+ "Set heading 2 to DONE")))
+ (should (string-equal (org-entry-get heading3-pom "SCHEDULED")
+ "<2000-01-16 Sun 09:00>"))
+ ;; 4 should still be blocked
+ (should (org-edna-test-check-block heading4-pom
+ "Second attempt to change heading
4")))))
+
+(ert-deftest org-edna-doc-test/laundry-cache ()
+ "Test for the \"laundry\" example in the documentation.
+
+This version enables cache, ensuring that the repeated calls to
+the relative finders all still work while cache is enabled."
+ (let ((org-edna-finder-use-cache t))
+ (org-edna-doc-test-setup "e57ce099-9f37-47f4-a6bb-61a84eb1fbbe"
+ (pcase-let* ((`(,heading1-pom ,heading2-pom ,heading3-pom ,heading4-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify that headings 2, 3, and 4 are all blocked
+ (should (org-edna-test-check-block heading2-pom
+ "Initial attempt to change heading
2"))
+ (should (org-edna-test-check-block heading3-pom
+ "Initial attempt to change heading
3"))
+ (should (org-edna-test-check-block heading4-pom
+ "Initial attempt to change heading
4"))
+ ;; Mark heading 1 as DONE
+ (should (not (org-edna-test-check-block heading1-pom
+ "Set heading 1 to DONE")))
+ ;; Only heading 2 should have a scheduled time
+ (should (string-equal (org-entry-get heading2-pom "SCHEDULED")
+ "<2000-01-15 Sat 01:00>"))
+ (should (not (org-entry-get heading3-pom "SCHEDULED")))
+ (should (not (org-entry-get heading4-pom "SCHEDULED")))
+ ;; The others should still be blocked.
+ (should (org-edna-test-check-block heading3-pom
+ "Second attempt to change heading
3"))
+ (should (org-edna-test-check-block heading4-pom
+ "Second attempt to change heading
4"))
+ ;; Try changing heading 2
+ (should (not (org-edna-test-check-block heading2-pom
+ "Set heading 2 to DONE")))
+ (should (string-equal (org-entry-get heading3-pom "SCHEDULED")
+ "<2000-01-16 Sun 09:00>"))
+ ;; 4 should still be blocked
+ (should (org-edna-test-check-block heading4-pom
+ "Second attempt to change heading
4"))))))
+
+(ert-deftest org-edna-doc-test/nightly ()
+ (org-edna-doc-test-setup "8b6d9820-d943-4622-85c9-4a346e033453"
+ (pcase-let* ((`(,nightly-pom ,lunch-pom ,door-pom ,dog-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify that Nightly is blocked
+ (should (org-edna-test-check-block nightly-pom "Initial Nightly Check"))
+ ;; Check off Lunch, and verify that nightly is still blocked
+ (org-edna-test-mark-done lunch-pom)
+ (should (org-edna-test-check-block nightly-pom "Nightly after Lunch"))
+ ;; Check off Door, and verify that nightly is still blocked
+ (org-edna-test-mark-done door-pom)
+ (should (org-edna-test-check-block nightly-pom "Nightly after Door"))
+ ;; Check off Dog. This should trigger the others.
+ (org-edna-test-mark-done dog-pom)
+ (should (org-edna-test-compare-todos lunch-pom "TODO" "Lunch after
Nightly Trigger"))
+ (should (org-edna-test-compare-todos door-pom "TODO" "Door after Nightly
Trigger"))
+ (should (org-edna-test-compare-todos dog-pom "TODO" "Dog after Nightly
Trigger"))
+ (should (string-equal (org-entry-get nightly-pom "DEADLINE")
+ "<2000-01-16 Sun +1d>")))))
+
+(ert-deftest org-edna-doc-test/nightly-cache ()
+ (let ((org-edna-finder-use-cache t))
+ (org-edna-doc-test-setup "8b6d9820-d943-4622-85c9-4a346e033453"
+ (pcase-let* ((`(,nightly-pom ,lunch-pom ,door-pom ,dog-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify that Nightly is blocked
+ (should (org-edna-test-check-block nightly-pom "Initial Nightly
Check"))
+ ;; Check off Lunch, and verify that nightly is still blocked
+ (org-edna-test-mark-done lunch-pom)
+ (should (org-edna-test-check-block nightly-pom "Nightly after Lunch"))
+ ;; Check off Door, and verify that nightly is still blocked
+ (org-edna-test-mark-done door-pom)
+ (should (org-edna-test-check-block nightly-pom "Nightly after Door"))
+ ;; Check off Dog. This should trigger the others.
+ (org-edna-test-mark-done dog-pom)
+ (should (org-edna-test-compare-todos lunch-pom "TODO" "Lunch after
Nightly Trigger"))
+ (should (org-edna-test-compare-todos door-pom "TODO" "Door after
Nightly Trigger"))
+ (should (org-edna-test-compare-todos dog-pom "TODO" "Dog after Nightly
Trigger"))
+ (should (string-equal (org-entry-get nightly-pom "DEADLINE")
+ "<2000-01-16 Sun +1d>"))))))
+
+(ert-deftest org-edna-doc-test/daily ()
+ (org-edna-doc-test-setup "630805bb-a864-4cdc-9a6f-0f126e887c66"
+ (pcase-let* ((`(,daily-pom ,lunch-pom ,door-pom ,dog-pom)
+ (org-edna-test-children-marks)))
+ ;; Check off Lunch. This should trigger the others.
+ (org-edna-test-mark-done lunch-pom)
+ (should (org-edna-test-compare-todos lunch-pom "TODO" "Lunch after Daily
Trigger"))
+ (should (org-edna-test-compare-todos door-pom "TODO" "Door after Daily
Trigger"))
+ (should (org-edna-test-compare-todos dog-pom "TODO" "Dog after Daily
Trigger"))
+ (should (string-equal (org-entry-get daily-pom "DEADLINE")
+ "<2000-01-16 Sun +1d>"))
+ ;; Check off Door. This should trigger the others.
+ (org-edna-test-mark-done door-pom)
+ (should (org-edna-test-compare-todos lunch-pom "TODO" "Lunch after Door
Trigger"))
+ (should (org-edna-test-compare-todos door-pom "TODO" "Door after Door
Trigger"))
+ (should (org-edna-test-compare-todos dog-pom "TODO" "Dog after Door
Trigger"))
+ (should (string-equal (org-entry-get daily-pom "DEADLINE")
+ "<2000-01-17 Mon +1d>"))
+ ;; Check off Dog. This should trigger the others.
+ (org-edna-test-mark-done dog-pom)
+ (should (org-edna-test-compare-todos lunch-pom "TODO" "Lunch after Dog
Trigger"))
+ (should (org-edna-test-compare-todos door-pom "TODO" "Door after Dog
Trigger"))
+ (should (org-edna-test-compare-todos dog-pom "TODO" "Dog after Dog
Trigger"))
+ (should (string-equal (org-entry-get daily-pom "DEADLINE")
+ "<2000-01-18 Tue +1d>")))))
+
+(ert-deftest org-edna-doc-test/weekly ()
+ (org-edna-doc-test-setup "cf529a5e-1b0c-40c3-8f85-fe2fc4df0ffd"
+ (pcase-let* ((`(,weekly-pom ,lunch-pom ,door-pom ,dog-pom)
+ (org-edna-test-children-marks)))
+ ;; Check off Lunch. This should trigger the others.
+ (org-edna-test-mark-done lunch-pom)
+ (should (org-edna-test-compare-todos lunch-pom "TODO" "Lunch after
Weekly Trigger"))
+ (should (org-edna-test-compare-todos door-pom "TODO" "Door after Weekly
Trigger"))
+ (should (org-edna-test-compare-todos dog-pom "TODO" "Dog after Weekly
Trigger"))
+ (should (string-equal (org-entry-get weekly-pom "DEADLINE")
+ "<2000-01-16 Sun +1d>")))))
+
+(ert-deftest org-edna-doc-test/basic-shower ()
+ (org-edna-doc-test-setup "34d67756-927b-4a21-a62d-7989bd138946"
+ (pcase-let* ((`(,shower-pom ,towels-pom) (org-edna-test-children-marks)))
+ ;; Verify towels is blocked
+ (should (org-edna-test-check-block towels-pom "Initial Towels Check"))
+ ;; Check off "Take Shower" and verify that it incremented the property
+ (org-edna-test-mark-done shower-pom)
+ (should (string-equal (org-entry-get shower-pom "COUNT") "1"))
+ ;; Verify towels is blocked
+ (should (org-edna-test-check-block towels-pom "Towels Check, Count=1"))
+ ;; Check off "Take Shower" and verify that it incremented the property
+ (org-edna-test-mark-done shower-pom)
+ (should (string-equal (org-entry-get shower-pom "COUNT") "2"))
+ ;; Verify towels is blocked
+ (should (org-edna-test-check-block towels-pom "Towels Check, Count=2"))
+ ;; Check off "Take Shower" and verify that it incremented the property
+ (org-edna-test-mark-done shower-pom)
+ (should (string-equal (org-entry-get shower-pom "COUNT") "3"))
+ ;; Verify that towels is no longer blocked.
+ (should (not (org-edna-test-check-block towels-pom "Towels Check,
Count=3")))
+ ;; Verify that the property was reset.
+ (should (string-equal (org-entry-get shower-pom "COUNT") "0")))))
+
+(ert-deftest org-edna-doc-test/snow-shoveling ()
+ (org-edna-doc-test-setup "b1d89bd8-db96-486e-874c-98e2b3a8cbf2"
+ (pcase-let* ((`(,monday-pom ,tuesday-pom ,wednesday-pom ,shovel-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify shovels is blocked
+ (should (org-edna-test-check-block shovel-pom "Initial Shovel Check"))
+
+ ;; Mark Monday as done
+ (org-edna-test-mark-done monday-pom)
+ (should (not (org-edna-test-check-block shovel-pom "Shovel after
changing Monday")))
+ ;; Reset
+ (org-edna-test-mark-todo monday-pom tuesday-pom wednesday-pom shovel-pom)
+
+ ;; Mark Tuesday as done
+ (org-edna-test-mark-done tuesday-pom)
+ (should (not (org-edna-test-check-block shovel-pom "Shovel after
changing Tuesday")))
+
+ ;; Reset
+ (org-edna-test-mark-todo monday-pom tuesday-pom wednesday-pom shovel-pom)
+ ;; Mark Wednesday as done
+ (org-edna-test-mark-done wednesday-pom)
+ (should (not (org-edna-test-check-block shovel-pom "Shovel after
changing Wednesday"))))))
+
+(ert-deftest org-edna-doc-test/consider-fraction ()
+ (org-edna-doc-test-setup "7de5af8b-a226-463f-8360-edd88b99462a"
+ (pcase-let* ((`(,shovel-pom ,room-pom ,vacuum-pom ,lunch-pom ,edna-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify Edna is blocked
+ (should (org-edna-test-check-block edna-pom "Initial Edna Check"))
+
+ ;; Mark Shovel snow as done
+ (org-edna-test-mark-done shovel-pom)
+ ;; Verify Edna is still blocked
+ (should (org-edna-test-check-block edna-pom "Edna Check after Shovel"))
+
+ ;; Mark Vacuum as done
+ (org-edna-test-mark-done vacuum-pom)
+ ;; Verify Edna is still blocked
+ (should (org-edna-test-check-block edna-pom "Edna Check after Vacuum"))
+
+ ;; Mark Room as done
+ (org-edna-test-mark-done room-pom)
+ ;; Verify Edna is no longer blocked
+ (should (not (org-edna-test-check-block edna-pom "Edna Check after
Room"))))))
+
+(ert-deftest org-edna-doc-test/consider-number ()
+ (org-edna-doc-test-setup "b79279f7-be3c-45ac-96dc-6e962a5873d4"
+ (pcase-let* ((`(,shovel-pom ,room-pom ,vacuum-pom ,lunch-pom ,edna-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify Edna is blocked
+ (should (org-edna-test-check-block edna-pom "Initial Edna Check"))
+
+ ;; Mark Shovel snow as done
+ (org-edna-test-mark-done shovel-pom)
+ ;; Verify Edna is still blocked
+ (should (org-edna-test-check-block edna-pom "Edna Check after Shovel"))
+
+ ;; Mark Vacuum as done
+ (org-edna-test-mark-done vacuum-pom)
+ ;; Verify Edna is still blocked
+ (should (org-edna-test-check-block edna-pom "Edna Check after Vacuum"))
+
+ ;; Mark Room as done
+ (org-edna-test-mark-done room-pom)
+ ;; Verify Edna is no longer blocked
+ (should (not (org-edna-test-check-block edna-pom "Edna Check after
Room"))))))
+
+(ert-deftest org-edna-doc-test/has-tags ()
+ (org-edna-doc-test-setup "6885e932-2c3e-4f20-ac22-5f5a0e791d67"
+ (pcase-let* ((`(,first-pom ,second-pom ,third-pom)
+ (org-edna-test-children-marks)))
+ ;; Verify that 3 is blocked
+ (should (org-edna-test-check-block third-pom "Initial Check"))
+
+ ;; Remove the tag from Task 1
+ (org-with-point-at first-pom
+ (org-set-tags-to ""))
+
+ ;; Verify that 3 is still blocked
+ (should (org-edna-test-check-block third-pom "Check after removing
tag1"))
+
+ ;; Remove the tag from Task 2
+ (org-with-point-at second-pom
+ (org-set-tags-to ""))
+
+ ;; Verify that 3 is no longer blocked
+ (should (not (org-edna-test-check-block third-pom "Check after removing
tag2"))))))
+
+(ert-deftest org-edna-doc-test/matches ()
+ (org-edna-doc-test-setup "8170bf82-c2ea-49e8-bd79-97a95176783f"
+ (pcase-let* ((`(,first-pom ,second-pom ,third-pom)
(org-edna-test-children-marks)))
+ ;; Verify that 3 is blocked
+ (should (org-edna-test-check-block third-pom "Initial Check"))
+
+ ;; Set 1 to DONE
+ (org-edna-test-mark-done first-pom)
+
+ ;; Verify that 3 is still blocked
+ (should (org-edna-test-check-block third-pom "Check after First"))
+
+ ;; Set 2 to DONE
+ (org-edna-test-mark-done second-pom)
+
+ ;; Verify that 3 is no longer blocked
+ (should (not (org-edna-test-check-block third-pom "Check after
Second"))))))
+
+(ert-deftest org-edna-doc-test/chain ()
+ (org-edna-doc-test-setup "1bd282ea-9238-47ea-9b4d-dafba19d278b"
+ (pcase-let* ((`(,first-pom ,second-pom) (org-edna-test-children-marks)))
+ ;; Set 1 to DONE
+ (org-edna-test-mark-done first-pom)
+ (should (string-equal (org-entry-get second-pom "COUNT") "2")))))
(provide 'org-edna-tests)
diff --git a/packages/org-edna/org-edna-tests.org
b/packages/org-edna/org-edna-tests.org
index 5e20614..2819700 100644
--- a/packages/org-edna/org-edna-tests.org
+++ b/packages/org-edna/org-edna-tests.org
@@ -1,4 +1,5 @@
#+STARTUP: nologdone
+#+STARTUP: indent
#+PROPERTY: Effort_ALL 0:01 0:02 0:03
#+PROPERTY: COUNTER_ALL a b c d
@@ -21,7 +22,13 @@ along with this program. If not, see
<http://www.gnu.org/licenses/>.
#+END_QUOTE
* Test Pool
** TODO Tagged Heading 1 :1:test:
+:PROPERTIES:
+:ID: 0fa0d4dd-40f2-4251-a558-4c6e2898c2df
+:END:
** TODO Tagged Heading 2 :1:test:
+:PROPERTIES:
+:ID: 30957f69-8c31-4a13-86ff-f0c5026fb65d
+:END:
** TODO ID Heading 1
:PROPERTIES:
:ID: 0d491588-7da3-43c5-b51a-87fbd34f79f7
@@ -33,25 +40,28 @@ along with this program. If not, see
<http://www.gnu.org/licenses/>.
:LOGGING: nil
:END:
** TODO ID Heading 3
-SCHEDULED: <2000-01-15 Sat 00:00>
+DEADLINE: <2000-01-15 Sat 00:00> SCHEDULED: <2000-01-15 Sat 00:00>
:PROPERTIES:
:ID: 97e6b0f0-40c4-464f-b760-6e5ca9744eb5
:END:
+<2000-01-15 Sat 00:00>
** DONE ID Heading 4
:PROPERTIES:
:ID: 7d4d564b-18b2-445c-a0c8-b1b3fb9ad29e
:END:
** Scheduled Headings
*** TODO Scheduled Heading 1
-SCHEDULED: <2017-01-01 Sun>
+DEADLINE: <2017-01-01 Sun> SCHEDULED: <2017-01-01 Sun>
:PROPERTIES:
:ID: caf27724-0887-4565-9765-ed2f1edcfb16
:END:
+<2017-01-01 Sun>
*** TODO Scheduled Heading 2
-SCHEDULED: <2017-01-01 Sun>
+DEADLINE: <2017-01-01 Sun> SCHEDULED: <2017-01-01 Sun>
:PROPERTIES:
:ID: 5594d4f1-b1bb-400f-9f3d-e2f9b43e82c3
:END:
+<2017-01-01 Sun>
** Sibling Headings
:PROPERTIES:
:ID: 21b8f1f5-14e8-4677-873d-69e0389fdc9e
@@ -95,12 +105,14 @@ DEADLINE: <2017-01-07 Sat> SCHEDULED: <2017-01-02 Mon>
:ID: 7c542695-8165-4c8b-b44d-4c12fa009548
:Effort: 0:01
:END:
+<2017-01-02 Mon>
*** [#B] Child Heading with Children
DEADLINE: <2017-01-03 Tue> SCHEDULED: <2017-01-03 Tue>
:PROPERTIES:
:ID: c7a986df-8d89-4509-b086-6db429b5607b
:Effort: 0:03
:END:
+<2017-01-03 Tue>
**** Child Heading One
:PROPERTIES:
:ID: 588bbd29-2e07-437f-b74d-f72459b545a1
@@ -115,25 +127,224 @@ DEADLINE: <2017-01-01 Sun> SCHEDULED: <2017-01-06 Fri>
:ID: 8c0b31a1-af49-473c-92ea-a5c1c3bace33
:Effort: 0:02
:END:
+<2017-01-06 Fri>
*** [#B] COMMENT Commented Child Heading
DEADLINE: <2017-01-08 Sun> SCHEDULED: <2017-01-04 Wed>
:PROPERTIES:
:ID: 0a1b9508-17ce-49c5-8ff3-28a0076374f5
:Effort: 0:06
:END:
+<2017-01-04 Wed>
*** [#A] Archived Child Heading :ARCHIVE:
DEADLINE: <2017-01-02 Mon> SCHEDULED: <2017-01-01 Sun>
:PROPERTIES:
:ID: a4b6131e-0560-4201-86d5-f32b36363431
:Effort: 0:05
:END:
+<2017-01-01 Sun>
*** DONE [#C] Child Heading with DONE
DEADLINE: <2017-01-05 Thu> SCHEDULED: <2017-01-05 Thu>
:PROPERTIES:
:ID: 4a1d74a2-b032-47da-a823-b32f5cab0aae
:Effort: 0:08
:END:
+<2017-01-05 Thu>
** Parent Sub Heading #2
:PROPERTIES:
:ID: 4fe67f03-2b35-4708-8c38-54d2c4dfab81
:END:
+* Documentation Tests
+** Ancestors
+:PROPERTIES:
+:ID: 24a0c3bb-7e69-4e9e-bb98-5aba2ff17bb1
+:END:
+*** TODO Heading 1
+**** TODO Heading 2
+**** TODO Heading 3
+***** TODO Heading 4
+****** TODO Heading 5
+:PROPERTIES:
+:BLOCKER: ancestors
+:END:
+** Descendants
+:PROPERTIES:
+:ID: cc18dc74-00e8-4081-b46f-e36800041fe7
+:END:
+*** TODO Heading 1
+:PROPERTIES:
+:BLOCKER: descendants
+:END:
+**** TODO Heading 2
+**** TODO Heading 3
+***** TODO Heading 4
+****** TODO Heading 5
+** Laundry
+:PROPERTIES:
+:ID: e57ce099-9f37-47f4-a6bb-61a84eb1fbbe
+:END:
+*** TODO Put clothes in washer
+SCHEDULED: <2000-01-15 Sat 00:00>
+:PROPERTIES:
+:TRIGGER: next-sibling scheduled!("++1h")
+:END:
+*** TODO Put clothes in dryer
+:PROPERTIES:
+:TRIGGER: next-sibling scheduled!("Sun 9:00")
+:BLOCKER: previous-sibling
+:END:
+*** TODO Fold laundry
+:PROPERTIES:
+:TRIGGER: next-sibling scheduled!("++1h")
+:BLOCKER: previous-sibling
+:END:
+*** TODO Put clothes away
+:PROPERTIES:
+:TRIGGER: next-sibling scheduled!("++1h")
+:BLOCKER: previous-sibling
+:END:
+** Nightlies - Standard
+:PROPERTIES:
+:ID: 8b6d9820-d943-4622-85c9-4a346e033453
+:END:
+*** TODO Nightly
+DEADLINE: <2000-01-15 Sat +1d>
+:PROPERTIES:
+:ID: 2d94abf9-2d63-46fd-8dc5-cd396555bcfe
+:BLOCKER: match("nightly")
+:TRIGGER: match("nightly") todo!(TODO)
+:END:
+*** TODO Prepare Tomorrow's Lunch :nightly:
+:PROPERTIES:
+:TRIGGER: if match("nightly") then ids(2d94abf9-2d63-46fd-8dc5-cd396555bcfe)
todo!(DONE) endif
+:END:
+*** TODO Lock Back Door :nightly:
+:PROPERTIES:
+:TRIGGER: if match("nightly") then ids(2d94abf9-2d63-46fd-8dc5-cd396555bcfe)
todo!(DONE) endif
+:END:
+*** TODO Feed Dog :nightly:
+:PROPERTIES:
+:TRIGGER: if match("nightly") then ids(2d94abf9-2d63-46fd-8dc5-cd396555bcfe)
todo!(DONE) endif
+:END:
+** Dailies - Consideration
+:PROPERTIES:
+:ID: 630805bb-a864-4cdc-9a6f-0f126e887c66
+:END:
+*** TODO Daily
+DEADLINE: <2000-01-15 Sat +1d>
+:PROPERTIES:
+:ID: 96f7e46c-40c3-4f5b-8f00-81a6e3cb122b
+:TRIGGER: match("daily") todo!(TODO)
+:END:
+*** TODO Prepare Tomorrow's Lunch :daily:
+:PROPERTIES:
+:TRIGGER: if consider(all) match("daily") then
ids(96f7e46c-40c3-4f5b-8f00-81a6e3cb122b) todo!(DONE) endif
+:END:
+*** TODO Lock Back Door :daily:
+:PROPERTIES:
+:TRIGGER: if consider(3) match("daily") then
ids(96f7e46c-40c3-4f5b-8f00-81a6e3cb122b) todo!(DONE) endif
+:END:
+*** TODO Feed Dog :daily:
+:PROPERTIES:
+:TRIGGER: if consider(0.9) match("daily") then
ids(96f7e46c-40c3-4f5b-8f00-81a6e3cb122b) todo!(DONE) endif
+:END:
+** Weeklies - Inverted Conditional
+:PROPERTIES:
+:ID: cf529a5e-1b0c-40c3-8f85-fe2fc4df0ffd
+:END:
+*** TODO Weekly
+DEADLINE: <2000-01-15 Sat +1d>
+:PROPERTIES:
+:ID: 9a0c4b00-64be-4971-a93e-c530cbdd4b2b
+:TRIGGER: match("weekly") todo!(TODO)
+:END:
+*** TODO Prepare Tomorrow's Lunch :weekly:
+:PROPERTIES:
+:TRIGGER: if match("weekly") then else
ids(9a0c4b00-64be-4971-a93e-c530cbdd4b2b) todo!(DONE) endif
+:END:
+*** TODO Lock Back Door :weekly:
+:PROPERTIES:
+:TRIGGER: if match("weekly") then else
ids(9a0c4b00-64be-4971-a93e-c530cbdd4b2b) todo!(DONE) endif
+:END:
+*** TODO Feed Dog :weekly:
+:PROPERTIES:
+:TRIGGER: if match("weekly") then else
ids(9a0c4b00-64be-4971-a93e-c530cbdd4b2b) todo!(DONE) endif
+:END:
+** Basic Shower - No Conditional
+:PROPERTIES:
+:ID: 34d67756-927b-4a21-a62d-7989bd138946
+:END:
+*** TODO Take Shower
+:PROPERTIES:
+:COUNT: 0
+:TRIGGER: self set-property!("COUNT" inc) todo!("TODO")
+:END:
+*** TODO Wash Towels
+:PROPERTIES:
+:BLOCKER: previous-sibling !has-property?("COUNT" "3")
+:TRIGGER: previous-sibling set-property!("COUNT" "0")
+:END:
+** Snow Shoveling
+:PROPERTIES:
+:ID: b1d89bd8-db96-486e-874c-98e2b3a8cbf2
+:END:
+*** TODO Shovel on Monday
+*** TODO Shovel on Tuesday
+*** TODO Shovel on Wednesday
+*** TODO Put shovel away
+:PROPERTIES:
+:BLOCKER: consider(all) rest-of-siblings-wrap
+:END:
+** Work I - Consider Fraction
+:PROPERTIES:
+:ID: 7de5af8b-a226-463f-8360-edd88b99462a
+:END:
+*** TODO Shovel Snow
+*** TODO Clean room
+*** TODO Vacuum
+*** TODO Eat lunch
+*** TODO Work on Edna
+:PROPERTIES:
+:BLOCKER: consider(0.5) rest-of-siblings-wrap
+:END:
+** Work II - Consider Number
+:PROPERTIES:
+:ID: b79279f7-be3c-45ac-96dc-6e962a5873d4
+:END:
+*** TODO Shovel Snow
+*** TODO Clean room
+*** TODO Vacuum
+*** TODO Eat lunch
+*** TODO Work on Edna
+:PROPERTIES:
+:BLOCKER: consider(2) rest-of-siblings-wrap
+:END:
+** Has Tags
+:PROPERTIES:
+:ID: 6885e932-2c3e-4f20-ac22-5f5a0e791d67
+:END:
+*** Task 1 :tag1:
+*** Task 2 :tag3:tag2:
+*** TODO Task 3
+:PROPERTIES:
+:BLOCKER: rest-of-siblings-wrap has-tags?("tag1" "tag2")
+:END:
+** Matches
+:PROPERTIES:
+:ID: 8170bf82-c2ea-49e8-bd79-97a95176783f
+:END:
+*** TODO Task 1
+*** TODO Task 2
+*** TODO Task 3
+:PROPERTIES:
+:BLOCKER: rest-of-siblings-wrap !matches?("TODO==\"DONE\"")
+:END:
+** Chain
+:PROPERTIES:
+:ID: 1bd282ea-9238-47ea-9b4d-dafba19d278b
+:END:
+*** TODO Heading 1
+:PROPERTIES:
+:COUNT: 2
+:TRIGGER: next-sibling chain!("COUNT")
+:END:
+*** TODO Heading 2
diff --git a/packages/org-edna/org-edna.el b/packages/org-edna/org-edna.el
index e271cb0..486cd88 100644
--- a/packages/org-edna/org-edna.el
+++ b/packages/org-edna/org-edna.el
@@ -7,7 +7,7 @@
;; Keywords: convenience, text, org
;; URL: https://savannah.nongnu.org/projects/org-edna-el/
;; Package-Requires: ((emacs "25.1") (seq "2.19") (org "9.0.5"))
-;; Version: 1.0beta8
+;; Version: 1.0
;; This file is part of GNU Emacs.
@@ -96,7 +96,8 @@ Currently, the following are handled:
Everything else is returned as is."
(pcase arg
- ((and (pred symbolp)
+ ((and (pred symbolp) ;; Symbol
+ ;; Name matches `org-uuidgen-p'
(let (pred org-uuidgen-p) (symbol-name arg)))
(symbol-name arg))
(_
@@ -127,10 +128,12 @@ If KEY is an invalid Edna keyword, then return nil."
(cond
;; Just return nil if it's not a symbol
((or (not key)
- (not (symbolp key))))
+ (not (symbolp key)))
+ nil)
((memq key '(consideration consider))
- ;; Function is ignored here
- (cons 'consideration 'identity))
+ ;; Function is ignored here, but `org-edna-describe-keyword' needs this
+ ;; function.
+ (cons 'consideration 'org-edna-handle-consideration))
((string-suffix-p "!" (symbol-name key))
;; Action
(let ((func-sym (intern (format "org-edna-action/%s" key))))
@@ -396,7 +399,7 @@ correspond to internal variables."
,target-var
,consideration-var))))
('consideration
- `(setq ,consideration-var ,(nth 0 args))))))
+ `(setq ,consideration-var ',(nth 0 args))))))
(defun org-edna--expand-sexp-form (form &optional
use-old-scope
@@ -495,7 +498,7 @@ specific form were generated, the results will be
regenerated and
stored in cache.
Minor changes to an Org file, such as setting properties or
-adding unrelated headlines, will be taken into account."
+adding unrelated headings, will be taken into account."
:group 'org-edna
:type 'boolean)
@@ -504,6 +507,24 @@ adding unrelated headlines, will be taken into account."
:group 'org-edna
:type 'number)
+(defvar org-edna-finder-cache-enabled-finders
+ '(org-edna-finder/match
+ org-edna-finder/ids
+ org-edna-finder/olp
+ org-edna-finder/file
+ org-edna-finder/org-file)
+ "List of finders for which cache is enabled.
+
+Only edit this list if you've added custom finders. Many
+finders, specifically relative finders, rely on the context in
+which they're called. For these finders, cache will not work
+properly.
+
+The default state of this list contains the built-in finders for
+which context is irrelevant.
+
+Each entry is the function symbol for the finder.")
+
(defun org-edna--add-to-finder-cache (func-sym args)
(let* ((results (apply func-sym args))
(input (make-org-edna--finder-input :func-sym func-sym
@@ -547,8 +568,12 @@ following reasons:
;; We have an entry created within the allowed interval.
(t entry))))
+(defun org-edna--cache-is-enabled-for-finder (func-sym)
+ (memq func-sym org-edna-finder-cache-enabled-finders))
+
(defun org-edna--handle-finder (func-sym args)
- (if (not org-edna-finder-use-cache)
+ (if (or (not org-edna-finder-use-cache)
+ (not (org-edna--cache-is-enabled-for-finder func-sym)))
;; Not using cache, so use the function directly.
(apply func-sym args)
(let* ((entry (org-edna--get-cache-entry func-sym args)))
@@ -558,6 +583,7 @@ following reasons:
(org-edna--add-to-finder-cache func-sym args)))))
+;;; Interactive Functions
(defmacro org-edna-run (change-plist &rest body)
"Run a TODO state change.
@@ -630,6 +656,7 @@ Remove Edna's workers from `org-trigger-hook' and
(remove-hook 'org-blocker-hook 'org-edna-blocker-function))
+;;; Finders
;; Tag Finder
(defun org-edna-finder/match (match-spec &optional scope skip)
@@ -641,7 +668,11 @@ MATCH-SPEC may be any valid match string; it is passed
straight
into `org-map-entries'.
SCOPE and SKIP are their counterparts in `org-map-entries'.
-SCOPE defaults to agenda, and SKIP defaults to nil.
+SCOPE defaults to agenda, and SKIP defaults to nil. Because of
+the different defaults in SCOPE, the symbol 'buffer may also be
+used. This indicates that scope should be the current buffer,
+honoring any restriction (the equivalent of the nil SCOPE in
+`org-map-entries'.)
* TODO Test
:PROPERTIES:
@@ -650,7 +681,10 @@ SCOPE defaults to agenda, and SKIP defaults to nil.
\"Test\" will block until all entries tagged \"test\" and
\"mine\" in the agenda files are marked DONE."
+ ;; Our default is agenda...
(setq scope (or scope 'agenda))
+ ;; ...but theirs is the buffer
+ (when (eq scope 'buffer) (setq scope nil))
(org-map-entries
;; Find all entries in the agenda files that match the given tag.
(lambda nil (point-marker))
@@ -776,6 +810,14 @@ Return a list of markers for the descendants."
(when-let* ((entry-tags (org-get-tags-at)))
(seq-intersection tags entry-tags)))
+(defun org-edna--get-timestamp-time (pom &optional inherit)
+ "Get the timestamp time as a time tuple, of a format suitable
+for calling org-schedule with, or if there is no timestamp,
+returns nil."
+ (let ((time (org-entry-get pom "TIMESTAMP" inherit)))
+ (when time
+ (apply 'encode-time (org-parse-time-string time)))))
+
(defun org-edna-finder/relatives (&rest options)
"Find some relative of the current heading.
@@ -841,7 +883,9 @@ All arguments are symbols, unless noted otherwise.
- scheduled-up: Scheduled time, farthest first
- scheduled-down: Scheduled time, closest first
- deadline-up: Deadline time, farthest first
-- deadline-down: Deadline time, closest first"
+- deadline-down: Deadline time, closest first
+- timestamp-up: Timestamp time, farthest first
+- timestamp-down: Timestamp time, closest first"
(let (targets
sortfun
reverse-sort
@@ -994,6 +1038,18 @@ All arguments are symbols, unless noted otherwise.
(lambda (lhs rhs)
(let ((time-lhs (org-get-deadline-time lhs))
(time-rhs (org-get-deadline-time rhs)))
+ (time-less-p time-lhs time-rhs)))))
+ ('timestamp-up
+ (setq sortfun
+ (lambda (lhs rhs)
+ (let ((time-lhs (org-edna--get-timestamp-time lhs))
+ (time-rhs (org-edna--get-timestamp-time rhs)))
+ (not (time-less-p time-lhs time-rhs))))))
+ ('timestamp-down
+ (setq sortfun
+ (lambda (lhs rhs)
+ (let ((time-lhs (org-edna--get-timestamp-time lhs))
+ (time-rhs (org-edna--get-timestamp-time rhs)))
(time-less-p time-lhs time-rhs)))))))
(setq filterfuns (nreverse filterfuns))
(when (and targets sortfun)
@@ -1224,6 +1280,7 @@ which ones will and won't work."
(list (point-min-marker))))
+;;; Actions
;; Set TODO state
(defun org-edna-action/todo! (_last-entry new-state)
@@ -1250,11 +1307,18 @@ N is an integer. WHAT can be `day', `month', `year',
`minute',
(org-timestamp-change n what)
(buffer-string)))
+(defun org-edna--property-for-planning-type (type)
+ (pcase type
+ ('scheduled "SCHEDULED")
+ ('deadline "DEADLINE")
+ ('timestamp "TIMESTAMP")
+ (_ "")))
+
(defun org-edna--get-planning-info (what)
"Get the planning info for WHAT.
-WHAT is either 'scheduled or 'deadline."
- (org-entry-get nil (if (eq what 'scheduled) "SCHEDULED" "DEADLINE")))
+WHAT is one of 'scheduled, 'deadline, or 'timestamp."
+ (org-entry-get nil (org-edna--property-for-planning-type what)))
;; Silence the byte-compiler
(defvar parse-time-weekdays)
@@ -1850,6 +1914,7 @@ Does nothing if the source heading has no property
PROPERTY."
(org-entry-put nil property old-prop)))
+;;; Conditions
;; For most conditions, we return true if condition is true and neg is false,
or
;; if condition is false and neg is true:
@@ -1952,57 +2017,104 @@ starting from target's position."
(when (org-xor condition neg)
(format "%s %s in %s" (if neg "Did Not Find" "Found") match
(buffer-name)))))
+(defun org-edna-condition/has-tags? (neg &rest tags)
+ "Check if the target heading has tags.
+
+Edna Syntax: has-tags?(\"tag1\" \"tag2\"...)
+
+Block if the target heading has any of the tags tag1, tag2, etc."
+ (let* ((condition (apply 'org-edna-entry-has-tags-p tags)))
+ (when (org-xor condition neg)
+ (org-get-heading))))
+
+(defun org-edna--heading-matches (match-string)
+ "Return non-nil if the current heading matches MATCH-STRING."
+ (let* ((matcher (cdr (org-make-tags-matcher match-string)))
+ (todo (org-entry-get nil "TODO"))
+ (tags (org-get-tags-at))
+ (level (org-reduced-level (org-outline-level))))
+ (funcall matcher todo tags level)))
+
+(defun org-edna-condition/matches? (neg match-string)
+ "Matches a heading against a match string.
+
+Edna Syntax: matches?(\"MATCH-STRING\")
+
+Blocks if the target heading matches MATCH-STRING.
+
+MATCH-STRING is a valid match string as passed to
+`org-map-entries'."
+ (let* ((condition (org-edna--heading-matches match-string)))
+ (when (org-xor condition neg)
+ (org-get-heading))))
+
+;;; Consideration
(defun org-edna-handle-consideration (consideration blocks)
"Handle consideration CONSIDERATION.
-Edna Syntax: consider(all) [1]
+Edna Syntax: consider(any) [1]
Edna Syntax: consider(N) [2]
Edna Syntax: consider(P) [3]
-Edna Syntax: consider(any) [4]
+Edna Syntax: consider(all) [4]
+
+A blocker can be read as:
+\"If ANY heading in TARGETS matches CONDITION, block this heading\"
+
+The consideration is \"ANY\".
+
+Form 1 blocks only if any target matches the condition. This is
+the default.
-Form 1: consider all targets when evaluating conditions.
-Form 2: consider the condition met if only N of the targets pass.
-Form 3: consider the condition met if only P% of the targets pass.
-Form 4: consider the condition met if any target meets it
+Form 2 blocks only if at least N targets meet the condition. N=1
+is the same as 'any'.
-If CONSIDERATION is nil, default to 'all.
+Form 3 blocks only if *at least* fraction P of the targets meet
+the condition. This should be a decimal value between 0 and 1.
+
+Form 4 blocks only if all targets match the condition.
+
+The default consideration is \"any\".
+
+If CONSIDERATION is nil, default to 'any.
The \"consideration\" keyword is also provided. It functions the
same as \"consider\"."
- ;; BLOCKS is a list of blocking entries; if one isn't blocked, its entry will
- ;; be nil.
- (let ((consideration (or consideration 'all))
- (first-block (seq-find #'identity blocks))
- (total-blocks (seq-length blocks))
- (fulfilled (seq-count #'not blocks)))
+ ;; BLOCKS is a list of entries that meets the blocking condition; if one
isn't
+ ;; blocked, its entry will be nil.
+ (let* ((consideration (or consideration 'any))
+ (first-block (seq-find #'identity blocks))
+ (total-blocks (seq-length blocks))
+ (fulfilled (seq-count #'not blocks))
+ (blocked (- total-blocks fulfilled)))
(pcase consideration
- ('all
- ;; All of them must be fulfilled, so find the first one that isn't.
- first-block)
('any
- ;; Any of them can be fulfilled, so find the first one that is
+ ;; In order to pass, all of them must be fulfilled, so find the first
one
+ ;; that isn't.
+ first-block)
+ ('all
+ ;; All of them must be set to block, so if one of them doesn't block,
the
+ ;; entire entry won't block.
(if (> fulfilled 0)
;; Have one fulfilled
nil
;; None of them are fulfilled
first-block))
((pred integerp)
- ;; A fixed number of them must be fulfilled, so check how many aren't.
- (let* ((fulfilled (seq-count #'not blocks)))
- (if (>= fulfilled consideration)
- nil
- first-block)))
+ ;; A minimum number of them must meet the blocking condition, so check
+ ;; how many block.
+ (if (>= blocked consideration)
+ first-block
+ nil))
((pred floatp)
- ;; A certain percentage of them must be fulfilled
- (let* ((fulfilled (seq-count #'not blocks)))
- (if (>= (/ (float fulfilled) (float total-blocks)) consideration)
- nil
- first-block))))))
+ ;; A certain percentage of them must block for the blocker to block.
+ (let* ((float-blocked (/ (float blocked) (float total-blocks))))
+ (if (>= float-blocked consideration)
+ first-block
+ nil))))))
-
;;; Popout editing
(defvar org-edna-edit-original-marker nil)
@@ -2183,7 +2295,7 @@ SUFFIX is an additional suffix to use when matching
keywords."
"Return a list of all allowed Edna keywords for a blocker."
`(,@(org-edna--collect-finders)
,@(org-edna--collect-conditions)
- "consideration"))
+ "consideration" "consider"))
(defun org-edna-completions-for-trigger ()
"Return a list of all allowed Edna keywords for a trigger."
@@ -2221,7 +2333,32 @@ PRED, and ACTION."
(when-let* ((bounds (bounds-of-thing-at-point 'symbol)))
(list (car bounds) (cdr bounds) 'org-edna-completion-table-function)))
+(defun org-edna-describe-keyword (keyword)
+ "Describe the Org Edna keyword KEYWORD.
+
+KEYWORD should be a string for a keyword recognized by edna.
+
+Displays help for KEYWORD in the Help buffer."
+ (interactive
+ (list
+ (completing-read
+ "Keyword: "
+ `(,@(org-edna--collect-finders)
+ ,@(org-edna--collect-actions)
+ ,@(org-edna--collect-conditions)
+ "consideration" "consider")
+ nil ;; No filter predicate
+ t))) ;; require match
+ ;; help-split-fundoc splits the usage info from the rest of the
documentation.
+ ;; This avoids having another usage line in the keyword documentation that
has
+ ;; nothing to do with how edna expects the function.
+ (pcase-let* ((`(,_type . ,func) (org-edna--function-for-key (intern
keyword)))
+ (`(,_usage . ,doc) (help-split-fundoc (documentation func t)
func)))
+ (with-help-window (help-buffer)
+ (princ doc))))
+
+;;; Bug Reports
(declare-function lm-report-bug "lisp-mnt" (topic))
diff --git a/packages/org-edna/org-edna.info b/packages/org-edna/org-edna.info
index 4f29ebe..abd1b88 100644
--- a/packages/org-edna/org-edna.info
+++ b/packages/org-edna/org-edna.info
@@ -36,6 +36,7 @@ Basic Features
* Finders:: How to find targets
* Actions:: Next steps
+* Getting Help:: Getting some help
Finders
@@ -84,12 +85,14 @@ Advanced Features
Conditions
-* done::
-* headings::
-* todo-state::
-* variable-set::
-* has-property::
-* re-search:: Search for a regular expression
+* Heading is DONE::
+* File Has Headings::
+* Heading TODO State::
+* Lisp Variable Set::
+* Heading Has Property::
+* Regexp Search:: Search for a regular expression
+* Checking Tags:: Matching against a set of tags
+* Matching Headings:: Matching against a match string
* Negating Conditions::
@@ -112,6 +115,7 @@ Contributing
Changelog
+* 1.0: 10.
* 1.0beta8: 10beta8.
* 1.0beta7: 10beta7.
* 1.0beta6: 10beta6.
@@ -314,6 +318,7 @@ The most basic features of Edna are *finders* and *actions*.
* Finders:: How to find targets
* Actions:: Next steps
+* Getting Help:: Getting some help
File: org-edna.info, Node: Finders, Next: Actions, Up: Basic Features
@@ -666,6 +671,8 @@ and for similarity to org-depend.
• scheduled-down: Scheduled time, closest first
• deadline-up: Deadline time, farthest first
• deadline-down: Deadline time, closest first
+ • timestamp-up: Timestamp time, farthest first
+ • timestamp-down: Timestamp time, closest first
Many of the other finders are shorthand for argument combinations of
relative:
@@ -758,7 +765,7 @@ when it reaches the end.
Identical to the *note rest-of-siblings-wrap:: finder.
-File: org-edna.info, Node: Actions, Prev: Finders, Up: Basic Features
+File: org-edna.info, Node: Actions, Next: Getting Help, Prev: Finders, Up:
Basic Features
Actions
=======
@@ -863,19 +870,23 @@ following, PLANNING is either scheduled or deadline.
Examples:
- • scheduled!(“Mon 09:00”) -> Set SCHEDULED to the following Monday at
- 9:00
- • deadline!(“++2h”) -> Set DEADLINE to two hours from now.
- • deadline!(copy) deadline!(“+1h”) -> Copy the source deadline to the
- target, then increment it by an hour.
- • scheduled!(“+1wkdy”) -> Set SCHEDULED to the next weekday
- • scheduled!(“+1d +wkdy”) -> Same as above
- • deadline!(“+1m -wkdy”) -> Set SCHEDULED up one month, but move
- backward to find a weekend
- • scheduled!(“float 2 Tue Feb”) -> Set SCHEDULED to the second
- Tuesday in the following February
- • scheduled!(“float 3 Thu”) -> Set SCHEDULED to the third Thursday in
- the following month
+scheduled!(“Mon 09:00”)
+ Set SCHEDULED to the following Monday at 9:00
+deadline!(“++2h”)
+ Set DEADLINE to two hours from now.
+deadline!(copy) deadline!(“+1h”)
+ Copy the source deadline to the target, then increment it by an
+ hour.
+scheduled!(“+1wkdy”)
+ Set SCHEDULED to the next weekday
+scheduled!(“+1d +wkdy”)
+ Same as above
+deadline!(“+1m -wkdy”)
+ Set DEADLINE up one month, but move backward to find a weekend
+scheduled!(“float 2 Tue Feb”)
+ Set SCHEDULED to the second Tuesday in the following February
+scheduled!(“float 3 Thu”)
+ Set SCHEDULED to the third Thursday in the following month
File: org-edna.info, Node: TODO State, Next: Archive, Prev:
Scheduled/Deadline, Up: Actions
@@ -891,6 +902,23 @@ TODO State
state. It can also be the empty string, in which case the TODO state is
removed.
+ Example:
+
+ * TODO Heading 1
+ :PROPERTIES:
+ :TRIGGER: next-sibling todo!(DONE)
+ :END:
+ * TODO Heading 2
+
+ In this example, when “Heading 1” is marked as DONE, it will also
+mark “Heading 2” as DONE:
+
+ * DONE Heading 1
+ :PROPERTIES:
+ :TRIGGER: next-sibling todo!(DONE)
+ :END:
+ * DONE Heading 2
+
File: org-edna.info, Node: Archive, Next: Chain Property, Prev: TODO State,
Up: Actions
@@ -915,6 +943,28 @@ Chain Property
Copies PROPERTY from the source entry to all targets. Does nothing
if the source heading has no property PROPERTY.
+ Example:
+
+ * TODO Heading 1
+ :PROPERTIES:
+ :COUNTER: 2
+ :TRIGGER: next-sibling chain!("COUNTER")
+ :END:
+ * TODO Heading 2
+
+ In this example, when “Heading 1” is marked as DONE, it will copy its
+COUNTER property to “Heading 2”:
+
+ * DONE Heading 1
+ :PROPERTIES:
+ :COUNTER: 2
+ :TRIGGER: next-sibling chain!("COUNTER")
+ :END:
+ * TODO Heading 2
+ :PROPERTIES:
+ :COUNTER: 2
+ :END:
+
File: org-edna.info, Node: Clocking, Next: Property, Prev: Chain Property,
Up: Actions
@@ -985,7 +1035,7 @@ already set:
* TODO Test
:PROPERTIES:
- :TRIGGER: self set-property("TEST" inc)
+ :TRIGGER: self set-property!("TEST" inc)
:END:
In the above example, if “Test” is set to DONE, Edna will fail to
@@ -997,10 +1047,11 @@ increment the TEST property, since it doesn’t exist.
Examples:
- • set-property!(“COUNTER” “1”) -> Sets the property COUNTER to 1 on
- all targets
- • set-property!(“COUNTER” inc) -> Increments the property COUNTER by
- 1. Following the previous example, it would be 2.
+set-property!(“COUNTER” “1”)
+ Sets the property COUNTER to 1 on all targets
+set-property!(“COUNTER” inc)
+ Increments the property COUNTER by 1. Following the previous
+ example, it would be 2.
File: org-edna.info, Node: Priority, Next: Tag, Prev: Property, Up: Actions
@@ -1045,14 +1096,32 @@ File: org-edna.info, Node: Effort, Prev: Tag, Up:
Actions
Effort
------
- • Syntax: set-effort!(VALUE)
+Modifies the effort of all targets.
- Sets the effort of all targets according to VALUE:
+ • Syntax: set-effort!(“VALUE”)
- • If VALUE is a string, then the effort is set to VALUE
- • If VALUE is an integer, then set the value to the VALUE’th allowed
- effort property
- • If VALUE is the symbol ’increment, increment effort
+ Set the effort of all targets to “VALUE”.
+
+ • Syntax: set-effort!(NUMBER)
+
+ Sets the effort to the NUMBER’th allowed effort property.
+
+ • Syntax: set-effort!(increment)
+
+ Increment the effort value.
+
+
+File: org-edna.info, Node: Getting Help, Prev: Actions, Up: Basic Features
+
+Getting Help
+============
+
+Edna provides help for any keyword with ‘M-x org-edna-describe-keyword’.
+When invoked, a list of keywords (finders, actions, etc.) known to Edna
+will be provided. Select any one to get its description.
+
+ This description includes the syntax and an explanation of what the
+keyword does. Some descriptions also contain examples.
File: org-edna.info, Node: Advanced Features, Next: Extending Edna, Prev:
Basic Features, Up: Top
@@ -1109,29 +1178,31 @@ means block if any target heading isn’t done.
* Menu:
-* done::
-* headings::
-* todo-state::
-* variable-set::
-* has-property::
-* re-search:: Search for a regular expression
+* Heading is DONE::
+* File Has Headings::
+* Heading TODO State::
+* Lisp Variable Set::
+* Heading Has Property::
+* Regexp Search:: Search for a regular expression
+* Checking Tags:: Matching against a set of tags
+* Matching Headings:: Matching against a match string
* Negating Conditions::
-File: org-edna.info, Node: done, Next: headings, Up: Conditions
+File: org-edna.info, Node: Heading is DONE, Next: File Has Headings, Up:
Conditions
-done
-----
+Heading is DONE
+---------------
• Syntax: done?
Blocks the source heading if any target heading is DONE.
-File: org-edna.info, Node: headings, Next: todo-state, Prev: done, Up:
Conditions
+File: org-edna.info, Node: File Has Headings, Next: Heading TODO State,
Prev: Heading is DONE, Up: Conditions
-headings
---------
+File Has Headings
+-----------------
• Syntax: headings?
@@ -1143,10 +1214,10 @@ Org heading. This means that target does not have to
be a heading.
The above example blocks if refile.org has any headings.
-File: org-edna.info, Node: todo-state, Next: variable-set, Prev: headings,
Up: Conditions
+File: org-edna.info, Node: Heading TODO State, Next: Lisp Variable Set,
Prev: File Has Headings, Up: Conditions
-todo-state
-----------
+Heading TODO State
+------------------
• Syntax: todo-state?(STATE)
@@ -1155,10 +1226,10 @@ todo-state
STATE may be a string or a symbol.
-File: org-edna.info, Node: variable-set, Next: has-property, Prev:
todo-state, Up: Conditions
+File: org-edna.info, Node: Lisp Variable Set, Next: Heading Has Property,
Prev: Heading TODO State, Up: Conditions
-variable-set
-------------
+Lisp Variable Set
+-----------------
• Syntax: variable-set?(VARIABLE VALUE)
@@ -1167,24 +1238,45 @@ against VALUE. Block the source heading if VARIABLE =
VALUE.
VARIABLE should be a symbol, and VALUE is any valid lisp expression.
- self variable-set?(test-variable 12)
+ Examples:
+
+self variable-set?(test-variable 12)
+ Blocks if the variable ‘test-variable’ is set to 12.
+self variable-set?(buffer-file-name “org-edna.org”)
+ Blocks if the variable ‘buffer-file-name’ is set to “org-edna.org”.
-File: org-edna.info, Node: has-property, Next: re-search, Prev:
variable-set, Up: Conditions
+File: org-edna.info, Node: Heading Has Property, Next: Regexp Search, Prev:
Lisp Variable Set, Up: Conditions
-has-property
-------------
+Heading Has Property
+--------------------
• Syntax: has-property?(“PROPERTY” “VALUE”)
Tests each target for the property PROPERTY, and blocks if it’s set
to VALUE.
+ Example:
+
+ * TODO Take Shower
+ :PROPERTIES:
+ :COUNT: 1
+ :TRIGGER: self set-property!("COUNT" inc) todo!("TODO")
+ :END:
+ * TODO Wash Towels
+ :PROPERTIES:
+ :BLOCKER: previous-sibling !has-property?("COUNT" "3")
+ :TRIGGER: previous-sibling set-property!("COUNT" "0")
+ :END:
+
+ In this example, “Wash Towels” can’t be completed until the user has
+showered at least three times.
+
-File: org-edna.info, Node: re-search, Next: Negating Conditions, Prev:
has-property, Up: Conditions
+File: org-edna.info, Node: Regexp Search, Next: Checking Tags, Prev:
Heading Has Property, Up: Conditions
-re-search
----------
+Regexp Search
+-------------
• Syntax: re-search?(“REGEXP”)
@@ -1192,10 +1284,59 @@ re-search
in any of the targets.
The targets are expected to be files, although this will work with
-other targets as well.
+other targets as well. When given a target heading, the heading’s file
+will be searched.
+
+
+File: org-edna.info, Node: Checking Tags, Next: Matching Headings, Prev:
Regexp Search, Up: Conditions
+
+Checking Tags
+-------------
+
+ • Syntax: has-tags?(“TAG1” “TAG2” ...)
+
+ Blocks the source heading if any of the target headings have one or
+more of the given tags.
+
+ * TODO Task 1
:tag1:
+ * TODO Task 2
:tag3:tag2:
+ * TODO Task 3
+ :PROPERTIES:
+ :BLOCKER: rest-of-siblings-wrap has-tags?("tag1" "tag2")
+ :END:
+
+ In the above example, Tasks 1 and 2 will block Task 3. Task 1 will
+block it because it contains “tag1” as one of its tags, and likewise for
+Task 2 and “tag2”.
+
+ Note that marking “Task 1” or “Task 2” as DONE will not unblock “Task
+3”. If you want to set up such a system, use the *note match:: finder.
+
+
+File: org-edna.info, Node: Matching Headings, Next: Negating Conditions,
Prev: Checking Tags, Up: Conditions
+
+Matching Headings
+-----------------
+
+ • Syntax: matches?(“MATCH-STRING”)
+
+ Blocks the source heading if any of the target headings match against
+MATCH-STRING.
+
+ MATCH-STRING is a string passed to ‘org-map-entries’.
+
+ * TODO Task 1
+ * TODO Task 2
+ * TODO Task 3
+ :PROPERTIES:
+ :BLOCKER: rest-of-siblings-wrap !matches?("TODO==\"DONE\"")
+ :END:
+
+ In the above example, Tasks 1 and 2 will block Task 3 until they’re
+marked as DONE.
-File: org-edna.info, Node: Negating Conditions, Prev: re-search, Up:
Conditions
+File: org-edna.info, Node: Negating Conditions, Prev: Matching Headings,
Up: Conditions
Negating Conditions
-------------------
@@ -1213,45 +1354,74 @@ File: org-edna.info, Node: Consideration, Next:
Conditional Forms, Prev: Cond
Consideration
=============
-“Consideration” is a special keyword that’s only valid for blockers.
+“Consideration” and “consider” are special keywords that are only valid
+for blockers.
- This says “Allow a task to complete if CONSIDERATION of its targets
-pass the given condition”.
+ A blocker says “If ANY heading in TARGETS meets CONDITION, block this
+task”.
- This keyword can allow specifying only a portion of tasks to
-consider:
+ In order to modify the ANY part of that statement, the ‘consider’
+keyword may be used:
- 1. consider(PERCENT)
- 2. consider(NUMBER)
- 3. consider(all) (Default)
- 4. consider(any)
+ 1. consider(any)
+ 2. consider(all)
+ 3. consider(FRACTION)
+ 4. consider(NUMBER)
- (1) tells the blocker to only consider some portion of the targets.
-If at least PERCENT of them are in a DONE state, allow the task to be
-set to DONE. PERCENT must be a decimal, and doesn’t need to include a
-%-sign.
+ (1) blocks the current task if any target meets the blocking
+condition. This is the default case.
- (2) tells the blocker to only consider NUMBER of the targets.
+ (2) blocks the current task only if all targets meet the blocking
+condition.
- (3) tells the blocker to consider all following targets.
+ * Shovel Snow
+ ** TODO Shovel on Monday
+ ** TODO Shovel on Tuesday
+ ** TODO Shovel on Wednesday
+ ** TODO Put shovel away
+ :PROPERTIES:
+ :BLOCKER: consider(all) rest-of-siblings-wrap
+ :END:
- (4) tells the blocker to allow passage if any of the targets pass.
+ The above example blocks “Put shovel away” so long as all of the
+siblings are still marked TODO.
- A consideration must be specified before the conditions to which it
-applies:
+ (3) blocks the current task if at least FRACTION of the targets meet
+the blocking condition.
- consider(0.5) siblings match("find_me") consider(all) !done?
+ * Work
+ ** TODO Shovel Snow
+ ** TODO Clean room
+ ** TODO Vacuum
+ ** TODO Eat lunch
+ ** TODO Work on Edna
+ :PROPERTIES:
+ :BLOCKER: consider(0.5) rest-of-siblings-wrap
+ :END:
+
+ The above example blocks “Work on Edna” so long as at least half of
+the siblings are marked TODO. This means that three of them must be
+completed before development can begin on Edna.
- The above code will allow task completion if at least half the
-siblings are complete, and all tasks tagged “find_me” are complete.
+ (4) blocks the current task if at least NUMBER of the targets meet
+the blocking condition.
- consider(1) ids(ID1 ID2 ID3) consider(2) ids(ID3 ID4 ID5 ID6)
+ * Work
+ ** TODO Shovel Snow
+ ** TODO Clean room
+ ** TODO Vacuum
+ ** TODO Eat lunch
+ ** TODO Work on Edna
+ :PROPERTIES:
+ :BLOCKER: consider(2) rest-of-siblings-wrap
+ :END:
- The above code will allow task completion if at least one of ID1,
-ID2, and ID3 are complete, and at least two of ID3, ID4, ID5, and ID6
-are complete.
+ The above example blocks “Work on Edna” so long as two of the
+siblings are marked TODO. This means that NUMBER=1 is the same as
+specifying ‘any’.
- If no consideration is given, ALL is assumed.
+ A consideration must be specified before the conditions to which it
+applies.
Both “consider” and “consideration” are valid keywords; they both
mean the same thing.
@@ -1305,18 +1475,18 @@ it won’t trigger the original until the last one is
marked DONE.
Occasionally, you may find that you’d rather execute a form if the
condition *would* block. There are two options.
- The first is confusing: use ‘consider(any)’. This will tell Edna to
-pass so long as one of the targets meets the condition. This is the
-opposite of Edna’s standard operation, which only allows passage if all
-targets meet the condition.
+ The first is to use ‘consider(all)’. This will tell Edna to block
+only if all of the targets meets the condition, and thus not block if at
+least one of them does not meet the condition. This is the opposite of
+Edna’s standard operation, which only allows passage if all targets meet
+the condition.
* TODO Prepare Tomorrow's Lunch
:nightly:
:PROPERTIES:
- :TRIGGER: if consider(any) match("nightly") then ids(12345)
todo!(DONE) endif
+ :TRIGGER: if consider(all) match("nightly") then ids(12345)
todo!(DONE) endif
:END:
- The second is a lot easier to understand: just switch the then and
-else clauses:
+ The second is to switch the then and else clauses:
* TODO Prepare Tomorrow's Lunch
:nightly:
:PROPERTIES:
@@ -1388,6 +1558,12 @@ predicates with ’?’.
Thus, one can have an action that files a target, and a finder that
finds a file.
+ We recommend that you don’t name a finder with a special character at
+the end of its name. As we devise new ideas, we consider using special
+characters for additional categories of keywords. Thus, to avoid
+complications in the future, it’s best if everyone avoids using
+characters that may become reserved in the future.
+
File: org-edna.info, Node: Finders 1, Next: Actions 1, Prev: Naming
Conventions, Up: Extending Edna
@@ -1637,6 +1813,7 @@ Changelog
* Menu:
+* 1.0: 10.
* 1.0beta8: 10beta8.
* 1.0beta7: 10beta7.
* 1.0beta6: 10beta6.
@@ -1646,7 +1823,23 @@ Changelog
* 1.0beta2: 10beta2.
-File: org-edna.info, Node: 10beta8, Next: 10beta7, Up: Changelog
+File: org-edna.info, Node: 10, Next: 10beta8, Up: Changelog
+
+1.0
+===
+
+ • Various bugs fixes
+ • Fixed parsing of consideration
+ • Limited cache to just the finders that don’t depend on current
+ position
+ • Added “buffer” option for match finder
+ • Added timestamp sorting to relatives finder
+ • Inverted meaning of consideration to avoid confusion
+ • Added *note has-tags?: Checking Tags. and *note matches?: Matching
+ Headings. conditions
+
+
+File: org-edna.info, Node: 10beta8, Next: 10beta7, Prev: 10, Up: Changelog
1.0beta8
========
@@ -1760,79 +1953,83 @@ Big release here, with three new features.
Tag Table:
Node: Top225
-Node: Copying4114
-Node: Introduction4936
-Node: Installation and Setup5884
-Node: Basic Operation6608
-Node: Blockers8459
-Node: Triggers8745
-Node: Syntax9007
-Node: Basic Features9697
-Node: Finders10000
-Node: ancestors11765
-Node: children12359
-Node: descendants12769
-Node: file13291
-Node: first-child14040
-Node: ids14300
-Node: match14961
-Node: next-sibling15599
-Node: next-sibling-wrap15856
-Node: olp16170
-Node: org-file16582
-Node: parent17227
-Node: previous-sibling17425
-Node: previous-sibling-wrap17686
-Node: relatives17965
-Node: rest-of-siblings21586
-Node: rest-of-siblings-wrap21871
-Node: self22220
-Node: siblings22381
-Node: siblings-wrap22618
-Node: Actions22922
-Node: Scheduled/Deadline23664
-Node: TODO State27239
-Node: Archive27607
-Node: Chain Property27927
-Node: Clocking28210
-Node: Property28622
-Node: Priority30809
-Node: Tag31378
-Node: Effort31595
-Node: Advanced Features31984
-Node: Finder Cache32432
-Node: Conditions33471
-Node: done34107
-Node: headings34271
-Node: todo-state34647
-Node: variable-set34903
-Node: has-property35332
-Node: re-search35601
-Node: Negating Conditions35961
-Node: Consideration36348
-Node: Conditional Forms37917
-Node: Setting the Properties40573
-Node: Extending Edna41657
-Node: Naming Conventions42147
-Node: Finders 142608
-Node: Actions 142970
-Node: Conditions 143429
-Node: Contributing44315
-Node: Bugs45181
-Node: Working with EDE45538
-Node: Compiling Edna46622
-Node: Testing Edna47491
-Node: Before Sending Changes48472
-Node: Developing with Bazaar49159
-Node: Documentation49900
-Node: Changelog50356
-Node: 10beta850606
-Node: 10beta750718
-Node: 10beta651012
-Node: 10beta551288
-Node: 10beta451675
-Node: 10beta351928
-Node: 10beta252367
+Node: Copying4346
+Node: Introduction5168
+Node: Installation and Setup6116
+Node: Basic Operation6840
+Node: Blockers8691
+Node: Triggers8977
+Node: Syntax9239
+Node: Basic Features9929
+Node: Finders10283
+Node: ancestors12048
+Node: children12642
+Node: descendants13052
+Node: file13574
+Node: first-child14323
+Node: ids14583
+Node: match15244
+Node: next-sibling15882
+Node: next-sibling-wrap16139
+Node: olp16453
+Node: org-file16865
+Node: parent17510
+Node: previous-sibling17708
+Node: previous-sibling-wrap17969
+Node: relatives18248
+Node: rest-of-siblings21974
+Node: rest-of-siblings-wrap22259
+Node: self22608
+Node: siblings22769
+Node: siblings-wrap23006
+Node: Actions23310
+Node: Scheduled/Deadline24073
+Node: TODO State27587
+Node: Archive28312
+Node: Chain Property28632
+Node: Clocking29385
+Node: Property29797
+Node: Priority31970
+Node: Tag32539
+Node: Effort32756
+Node: Getting Help33140
+Node: Advanced Features33585
+Node: Finder Cache34033
+Node: Conditions35072
+Node: Heading is DONE35878
+Node: File Has Headings36084
+Node: Heading TODO State36506
+Node: Lisp Variable Set36800
+Node: Heading Has Property37468
+Node: Regexp Search38214
+Node: Checking Tags38657
+Node: Matching Headings39559
+Node: Negating Conditions40156
+Node: Consideration40551
+Node: Conditional Forms42735
+Node: Setting the Properties45423
+Node: Extending Edna46507
+Node: Naming Conventions46997
+Node: Finders 147789
+Node: Actions 148151
+Node: Conditions 148610
+Node: Contributing49496
+Node: Bugs50362
+Node: Working with EDE50719
+Node: Compiling Edna51803
+Node: Testing Edna52672
+Node: Before Sending Changes53653
+Node: Developing with Bazaar54340
+Node: Documentation55081
+Node: Changelog55537
+Node: 1055798
+Node: 10beta856300
+Node: 10beta756423
+Node: 10beta656717
+Node: 10beta556993
+Node: 10beta457380
+Node: 10beta357633
+Node: 10beta258072
End Tag Table
diff --git a/packages/org-edna/org-edna.org b/packages/org-edna/org-edna.org
index 1f01709..89c32aa 100644
--- a/packages/org-edna/org-edna.org
+++ b/packages/org-edna/org-edna.org
@@ -526,6 +526,8 @@ All arguments are symbols, unless noted otherwise.
- scheduled-down: Scheduled time, closest first
- deadline-up: Deadline time, farthest first
- deadline-down: Deadline time, closest first
+- timestamp-up: Timestamp time, farthest first
+- timestamp-down: Timestamp time, closest first
Many of the other finders are shorthand for argument combinations of relative:
@@ -692,14 +694,15 @@ PLANNING is either scheduled or deadline.
Examples:
-- scheduled!("Mon 09:00") -> Set SCHEDULED to the following Monday at 9:00
-- deadline!("++2h") -> Set DEADLINE to two hours from now.
-- deadline!(copy) deadline!("+1h") -> Copy the source deadline to the target,
then increment it by an hour.
-- scheduled!("+1wkdy") -> Set SCHEDULED to the next weekday
-- scheduled!("+1d +wkdy") -> Same as above
-- deadline!("+1m -wkdy") -> Set SCHEDULED up one month, but move backward to
find a weekend
-- scheduled!("float 2 Tue Feb") -> Set SCHEDULED to the second Tuesday in the
following February
-- scheduled!("float 3 Thu") -> Set SCHEDULED to the third Thursday in the
following month
+- scheduled!("Mon 09:00") :: Set SCHEDULED to the following Monday at 9:00
+- deadline!("++2h") :: Set DEADLINE to two hours from now.
+- deadline!(copy) deadline!("+1h") :: Copy the source deadline to the target,
then increment it by an hour.
+- scheduled!("+1wkdy") :: Set SCHEDULED to the next weekday
+- scheduled!("+1d +wkdy") :: Same as above
+- deadline!("+1m -wkdy") :: Set DEADLINE up one month, but move backward to
find a weekend
+- scheduled!("float 2 Tue Feb") :: Set SCHEDULED to the second Tuesday in the
following February
+- scheduled!("float 3 Thu") :: Set SCHEDULED to the third Thursday in the
following month
+
*** TODO State
:PROPERTIES:
:CUSTOM_ID: todo!
@@ -713,6 +716,27 @@ Sets the TODO state of the target heading to NEW-STATE.
NEW-STATE may either be a string or a symbol denoting the new TODO state. It
can also be the empty string, in which case the TODO state is removed.
+Example:
+
+#+BEGIN_SRC org
+,* TODO Heading 1
+ :PROPERTIES:
+ :TRIGGER: next-sibling todo!(DONE)
+ :END:
+,* TODO Heading 2
+#+END_SRC
+
+In this example, when "Heading 1" is marked as DONE, it will also mark "Heading
+2" as DONE:
+
+#+BEGIN_SRC org
+,* DONE Heading 1
+ :PROPERTIES:
+ :TRIGGER: next-sibling todo!(DONE)
+ :END:
+,* DONE Heading 2
+#+END_SRC
+
*** Archive
:PROPERTIES:
:CUSTOM_ID: archive!
@@ -737,6 +761,32 @@ nil, Edna will not ask before archiving targets.
Copies PROPERTY from the source entry to all targets. Does nothing if the
source heading has no property PROPERTY.
+Example:
+
+#+BEGIN_SRC org
+,* TODO Heading 1
+ :PROPERTIES:
+ :COUNTER: 2
+ :TRIGGER: next-sibling chain!("COUNTER")
+ :END:
+,* TODO Heading 2
+#+END_SRC
+
+In this example, when "Heading 1" is marked as DONE, it will copy its COUNTER
+property to "Heading 2":
+
+#+BEGIN_SRC org
+,* DONE Heading 1
+ :PROPERTIES:
+ :COUNTER: 2
+ :TRIGGER: next-sibling chain!("COUNTER")
+ :END:
+,* TODO Heading 2
+ :PROPERTIES:
+ :COUNTER: 2
+ :END:
+#+END_SRC
+
*** Clocking
:PROPERTIES:
:CUSTOM_ID: clocking
@@ -804,7 +854,7 @@ Additionally, all special forms will fail if the property
is not already set:
#+begin_src org
,* TODO Test
:PROPERTIES:
- :TRIGGER: self set-property("TEST" inc)
+ :TRIGGER: self set-property!("TEST" inc)
:END:
#+end_src
@@ -817,8 +867,8 @@ Deletes the property PROPERTY from all targets.
Examples:
-- set-property!("COUNTER" "1") -> Sets the property COUNTER to 1 on all targets
-- set-property!("COUNTER" inc) -> Increments the property COUNTER by 1.
Following the previous example, it would be 2.
+- set-property!("COUNTER" "1") :: Sets the property COUNTER to 1 on all targets
+- set-property!("COUNTER" inc) :: Increments the property COUNTER by 1.
Following the previous example, it would be 2.
*** Priority
:PROPERTIES:
@@ -861,13 +911,32 @@ e.g. tag1:tag2
:DESCRIPTION: So much effort!
:END:
-- Syntax: set-effort!(VALUE)
+Modifies the effort of all targets.
+
+- Syntax: set-effort!("VALUE")
+
+ Set the effort of all targets to "VALUE".
+
+- Syntax: set-effort!(NUMBER)
+
+ Sets the effort to the NUMBER'th allowed effort property.
+
+- Syntax: set-effort!(increment)
+
+ Increment the effort value.
-Sets the effort of all targets according to VALUE:
+** Getting Help
+:PROPERTIES:
+:CUSTOM_ID: help
+:DESCRIPTION: Getting some help
+:END:
+
+Edna provides help for any keyword with ~M-x org-edna-describe-keyword~. When
+invoked, a list of keywords (finders, actions, etc.) known to Edna will be
+provided. Select any one to get its description.
-- If VALUE is a string, then the effort is set to VALUE
-- If VALUE is an integer, then set the value to the VALUE'th allowed effort
property
-- If VALUE is the symbol 'increment, increment effort
+This description includes the syntax and an explanation of what the keyword
+does. Some descriptions also contain examples.
* Advanced Features
:PROPERTIES:
@@ -910,7 +979,7 @@ that target, then the source heading is blocked.
If no condition is specified, ~!done?~ is used by default, which means block if
any target heading isn't done.
-*** done
+*** Heading is DONE
:PROPERTIES:
:CUSTOM_ID: done
:END:
@@ -919,7 +988,7 @@ any target heading isn't done.
Blocks the source heading if any target heading is DONE.
-*** headings
+*** File Has Headings
:PROPERTIES:
:CUSTOM_ID: headings
:END:
@@ -935,7 +1004,7 @@ org-file("refile.org") headings?
The above example blocks if refile.org has any headings.
-*** todo-state
+*** Heading TODO State
:PROPERTIES:
:CUSTOM_ID: todo-state
:END:
@@ -946,7 +1015,7 @@ Blocks if any target heading has TODO state set to STATE.
STATE may be a string or a symbol.
-*** variable-set
+*** Lisp Variable Set
:PROPERTIES:
:CUSTOM_ID: variable-set
:END:
@@ -958,11 +1027,12 @@ against VALUE. Block the source heading if VARIABLE =
VALUE.
VARIABLE should be a symbol, and VALUE is any valid lisp expression.
-#+BEGIN_EXAMPLE
-self variable-set?(test-variable 12)
-#+END_EXAMPLE
+Examples:
-*** has-property
+- self variable-set?(test-variable 12) :: Blocks if the variable
~test-variable~ is set to 12.
+- self variable-set?(buffer-file-name "org-edna.org") :: Blocks if the
variable ~buffer-file-name~ is set to "org-edna.org".
+
+*** Heading Has Property
:PROPERTIES:
:CUSTOM_ID: has-property
:END:
@@ -971,7 +1041,25 @@ self variable-set?(test-variable 12)
Tests each target for the property PROPERTY, and blocks if it's set to VALUE.
-*** re-search
+Example:
+
+#+begin_src org
+,* TODO Take Shower
+ :PROPERTIES:
+ :COUNT: 1
+ :TRIGGER: self set-property!("COUNT" inc) todo!("TODO")
+ :END:
+,* TODO Wash Towels
+ :PROPERTIES:
+ :BLOCKER: previous-sibling !has-property?("COUNT" "3")
+ :TRIGGER: previous-sibling set-property!("COUNT" "0")
+ :END:
+#+end_src
+
+In this example, "Wash Towels" can't be completed until the user has showered
at
+least three times.
+
+*** Regexp Search
:PROPERTIES:
:CUSTOM_ID: re-search
:DESCRIPTION: Search for a regular expression
@@ -983,7 +1071,57 @@ Blocks the source heading if the regular expression
REGEXP is present in any
of the targets.
The targets are expected to be files, although this will work with other
targets
-as well.
+as well. When given a target heading, the heading's file will be searched.
+*** Checking Tags
+:PROPERTIES:
+:CUSTOM_ID: has-tags
+:DESCRIPTION: Matching against a set of tags
+:END:
+
+- Syntax: has-tags?("TAG1" "TAG2" ...)
+
+Blocks the source heading if any of the target headings have one or more of the
+given tags.
+
+#+begin_src org
+,* TODO Task 1 :tag1:
+,* TODO Task 2 :tag3:tag2:
+,* TODO Task 3
+ :PROPERTIES:
+ :BLOCKER: rest-of-siblings-wrap has-tags?("tag1" "tag2")
+ :END:
+#+end_src
+
+In the above example, Tasks 1 and 2 will block Task 3. Task 1 will block it
+because it contains "tag1" as one of its tags, and likewise for Task 2 and
+"tag2".
+
+Note that marking "Task 1" or "Task 2" as DONE will not unblock "Task 3". If
+you want to set up such a system, use the [[#match][match]] finder.
+*** Matching Headings
+:PROPERTIES:
+:CUSTOM_ID: matches
+:DESCRIPTION: Matching against a match string
+:END:
+
+- Syntax: matches?("MATCH-STRING")
+
+Blocks the source heading if any of the target headings match against
+MATCH-STRING.
+
+MATCH-STRING is a string passed to ~org-map-entries~.
+
+#+begin_src org
+,* TODO Task 1
+,* TODO Task 2
+,* TODO Task 3
+ :PROPERTIES:
+ :BLOCKER: rest-of-siblings-wrap !matches?("TODO==\"DONE\"")
+ :END:
+#+end_src
+
+In the above example, Tasks 1 and 2 will block Task 3 until they're marked as
+DONE.
*** Negating Conditions
:PROPERTIES:
@@ -1002,45 +1140,76 @@ tagged "test" does *not* have the property PROP set to
"1".
:DESCRIPTION: Only some of them
:END:
-"Consideration" is a special keyword that's only valid for blockers.
+"Consideration" and "consider" are special keywords that are only valid for
+blockers.
-This says "Allow a task to complete if CONSIDERATION of its targets pass the
-given condition".
+A blocker says "If ANY heading in TARGETS meets CONDITION, block this task".
-This keyword can allow specifying only a portion of tasks to consider:
+In order to modify the ANY part of that statement, the ~consider~ keyword may
be
+used:
-1. consider(PERCENT)
-2. consider(NUMBER)
-3. consider(all) (Default)
-4. consider(any)
+1. consider(any)
+2. consider(all)
+3. consider(FRACTION)
+4. consider(NUMBER)
-(1) tells the blocker to only consider some portion of the targets. If at
least
-PERCENT of them are in a DONE state, allow the task to be set to DONE. PERCENT
-must be a decimal, and doesn't need to include a %-sign.
+(1) blocks the current task if any target meets the blocking condition. This
is
+the default case.
-(2) tells the blocker to only consider NUMBER of the targets.
+(2) blocks the current task only if all targets meet the blocking condition.
-(3) tells the blocker to consider all following targets.
+#+begin_src org
+,* Shovel Snow
+,** TODO Shovel on Monday
+,** TODO Shovel on Tuesday
+,** TODO Shovel on Wednesday
+,** TODO Put shovel away
+ :PROPERTIES:
+ :BLOCKER: consider(all) rest-of-siblings-wrap
+ :END:
+#+end_src
-(4) tells the blocker to allow passage if any of the targets pass.
+The above example blocks "Put shovel away" so long as all of the siblings are
+still marked TODO.
-A consideration must be specified before the conditions to which it applies:
+(3) blocks the current task if at least FRACTION of the targets meet the
+blocking condition.
-#+BEGIN_EXAMPLE
-consider(0.5) siblings match("find_me") consider(all) !done?
-#+END_EXAMPLE
+#+begin_src org
+,* Work
+,** TODO Shovel Snow
+,** TODO Clean room
+,** TODO Vacuum
+,** TODO Eat lunch
+,** TODO Work on Edna
+ :PROPERTIES:
+ :BLOCKER: consider(0.5) rest-of-siblings-wrap
+ :END:
+#+end_src
-The above code will allow task completion if at least half the siblings are
-complete, and all tasks tagged "find_me" are complete.
+The above example blocks "Work on Edna" so long as at least half of the
siblings
+are marked TODO. This means that three of them must be completed before
+development can begin on Edna.
-#+BEGIN_SRC emacs-lisp
-consider(1) ids(ID1 ID2 ID3) consider(2) ids(ID3 ID4 ID5 ID6)
-#+END_SRC
+(4) blocks the current task if at least NUMBER of the targets meet the blocking
+condition.
+
+#+begin_src org
+,* Work
+,** TODO Shovel Snow
+,** TODO Clean room
+,** TODO Vacuum
+,** TODO Eat lunch
+,** TODO Work on Edna
+ :PROPERTIES:
+ :BLOCKER: consider(2) rest-of-siblings-wrap
+ :END:
+#+end_src
-The above code will allow task completion if at least one of ID1, ID2, and ID3
-are complete, and at least two of ID3, ID4, ID5, and ID6 are complete.
+The above example blocks "Work on Edna" so long as two of the siblings are
+marked TODO. This means that NUMBER=1 is the same as specifying ~any~.
-If no consideration is given, ALL is assumed.
+A consideration must be specified before the conditions to which it applies.
Both "consider" and "consideration" are valid keywords; they both mean the same
thing.
@@ -1099,18 +1268,19 @@ trigger the original until the last one is marked DONE.
Occasionally, you may find that you'd rather execute a form if the condition
*would* block. There are two options.
-The first is confusing: use ~consider(any)~. This will tell Edna to pass so
-long as one of the targets meets the condition. This is the opposite of Edna's
-standard operation, which only allows passage if all targets meet the
condition.
+The first is to use ~consider(all)~. This will tell Edna to block only if all
+of the targets meets the condition, and thus not block if at least one of them
+does not meet the condition. This is the opposite of Edna's standard
operation,
+which only allows passage if all targets meet the condition.
#+begin_src org
,* TODO Prepare Tomorrow's Lunch :nightly:
:PROPERTIES:
- :TRIGGER: if consider(any) match("nightly") then ids(12345) todo!(DONE)
endif
+ :TRIGGER: if consider(all) match("nightly") then ids(12345) todo!(DONE)
endif
:END:
#+end_src
-The second is a lot easier to understand: just switch the then and else
clauses:
+The second is to switch the then and else clauses:
#+begin_src org
,* TODO Prepare Tomorrow's Lunch :nightly:
@@ -1174,6 +1344,12 @@ Scheme to suffix destructive functions with '!' and
predicates with '?'.
Thus, one can have an action that files a target, and a finder that finds a
file.
+We recommend that you don't name a finder with a special character at the end
of
+its name. As we devise new ideas, we consider using special characters for
+additional categories of keywords. Thus, to avoid complications in the future,
+it's best if everyone avoids using characters that may become reserved in the
+future.
+
** Finders
:PROPERTIES:
:DESCRIPTION: Making a new finder
@@ -1410,6 +1586,16 @@ making any changes:
:PROPERTIES:
:DESCRIPTION: List of changes by version
:END:
+** 1.0
+
+- Various bugs fixes
+ - Fixed parsing of consideration
+ - Limited cache to just the finders that don't depend on current position
+- Added "buffer" option for match finder
+- Added timestamp sorting to relatives finder
+- Inverted meaning of consideration to avoid confusion
+- Added [[#has-tags][has-tags?]] and [[#matches][matches?]] conditions
+
** 1.0beta8
Quick fix for beta7.
** 1.0beta7
diff --git a/packages/org-edna/test.mk b/packages/org-edna/test.mk
index 29e31e5..4b6590c 100644
--- a/packages/org-edna/test.mk
+++ b/packages/org-edna/test.mk
@@ -23,6 +23,7 @@ test: compile
-L "." \
-l "ert" \
-l "org-edna-tests.el" \
+ --eval "(setq org-edna-test-inhibit-messages t)" \
-f ert-run-tests-batch-and-exit
include Makefile
- [elpa] master 9dea42d 02/13: Added note about naming conventions., (continued)
- [elpa] master 9dea42d 02/13: Added note about naming conventions., Ian Dunn, 2018/11/25
- [elpa] master 0194996 01/13: Added org-edna-describe-keyword function, Ian Dunn, 2018/11/25
- [elpa] master 1bb1fed 08/13: Added timestamp sorting to relatives finder, Ian Dunn, 2018/11/25
- [elpa] master 2c5ac0c 12/13: Bumped version, Ian Dunn, 2018/11/25
- [elpa] master 58be309 06/13: Fixed up description of set-effort!., Ian Dunn, 2018/11/25
- [elpa] master c6b3234 07/13: Additional tests and minor cleanup, Ian Dunn, 2018/11/25
- [elpa] master cb98c0f 10/13: Removed remnants of old build system, Ian Dunn, 2018/11/25
- [elpa] master 98651a0 09/13: Inverted definition of consideration, Ian Dunn, 2018/11/25
- [elpa] master 45310da 05/13: Various fixes from testing, Ian Dunn, 2018/11/25
- [elpa] master 26d2a0d 11/13: Added has-tags? and matches? conditions, Ian Dunn, 2018/11/25
- [elpa] master 76ef9bf 13/13: Merge commit '2c5ac0cb808ae6953fbc74cc497245dafb51051f',
Ian Dunn <=