branch: master commit fd1de2f180c97fd52ae4843ca3e2d04f26c91f97 Author: Ian Dunn <du...@gnu.org> Commit: Ian Dunn <du...@gnu.org>
Fixed stacking multiple conditions * org-edna.el (org-edna--normalize-sexp-form): Only set state if not breaking. (org-edna--expand-sexp-form): Properly handle blocking-var. * org-edna-tests.el (org-edna-expand-sexp-form): (org-edna-expand-sexp-form-multiple): (org-edna-expand-sexp-form-if-else): (org-edna-expand-sexp-form-if-no-else): Removed buggy expansion tests. (org-edna-doc-test/multiple-blockers): Added test for multiple blockers. * org-edna.org (Multiple Blockers): Added section. --- org-edna-tests.el | 204 +++++++---------------------------------------------- org-edna-tests.org | 13 ++++ org-edna.el | 25 +++++-- org-edna.info | 198 ++++++++++++++++++++++++++++++--------------------- org-edna.org | 28 +++++++- 5 files changed, 202 insertions(+), 266 deletions(-) diff --git a/org-edna-tests.el b/org-edna-tests.el index b6adf15..331438f 100644 --- a/org-edna-tests.el +++ b/org-edna-tests.el @@ -336,183 +336,6 @@ This avoids org-id digging into its internal database." ;; Error should point to the start of the if-statement (should (eq (plist-get data :error-pos) 45)))) -(ert-deftest org-edna-expand-sexp-form () - ;; Override cl-gentemp so we have a repeatable test - (cl-letf* (((symbol-function 'cl-gentemp) (lambda (&optional prefix) (intern (format "%s1" prefix)))) - (input-sexp '((self) - (!done?))) - (output-form (org-edna--expand-sexp-form input-sexp))) - (should (equal - output-form - '(let ((targets1 nil) - (consideration1 nil) - (blocking-entry1 nil)) - (setq targets1 (org-edna--add-targets targets1 (org-edna--handle-finder 'org-edna-finder/self 'nil))) - (setq blocking-entry1 - (or blocking-entry1 - (org-edna--handle-condition 'org-edna-condition/done? - '! 'nil targets1 - consideration1)))))))) - -(ert-deftest org-edna-expand-sexp-form-multiple () - (cl-letf* ((target-ctr 0) - (consideration-ctr 0) - (blocking-entry-ctr 0) - ((symbol-function 'cl-gentemp) - (lambda (&optional prefix) - (let ((ctr (pcase prefix - ("targets" (cl-incf target-ctr)) - ("consideration" (cl-incf consideration-ctr)) - ("blocking-entry" (cl-incf blocking-entry-ctr)) - (_ 0)))) - (intern (format "%s%s" prefix ctr))))) - (input-sexp '(((match "checklist") - (todo! DONE)) - ((siblings) - (todo! TODO)))) - (expected-form - '(let ((targets1 nil) - (consideration1 nil) - (blocking-entry1 nil)) - ;; Don't need a new set of variables - (let ((targets2 targets1) - (consideration2 consideration1) - (blocking-entry2 blocking-entry1)) - (setq targets2 - (org-edna--add-targets targets2 - (org-edna--handle-finder 'org-edna-finder/match '("checklist")))) - (org-edna--handle-action 'org-edna-action/todo! - targets2 - (point-marker) - '(DONE))) - (let ((targets5 targets1) - (consideration5 consideration1) - (blocking-entry5 blocking-entry1)) - (setq targets5 - (org-edna--add-targets targets5 - (org-edna--handle-finder 'org-edna-finder/siblings 'nil))) - (org-edna--handle-action 'org-edna-action/todo! - targets5 - (point-marker) - '(TODO))))) - (output-form (org-edna--expand-sexp-form input-sexp))) - (should (equal output-form expected-form)))) - -(ert-deftest org-edna-expand-sexp-form-if-else () - (cl-letf* ((target-ctr 0) - (consideration-ctr 0) - (blocking-entry-ctr 0) - ((symbol-function 'cl-gentemp) - (lambda (&optional prefix) - (let ((ctr (pcase prefix - ("targets" (cl-incf target-ctr)) - ("consideration" (cl-incf consideration-ctr)) - ("blocking-entry" (cl-incf blocking-entry-ctr)) - (_ 0)))) - (intern (format "%s%s" prefix ctr))))) - (input-sexp '((if - ((match "checklist") - (done\?)) - ((self) - (todo! TODO)) - ((siblings) - (todo! DONE))))) - (expected-form - '(let - ((targets1 nil) - (consideration1 nil) - (blocking-entry1 nil)) - (if - ;; No inheritance in the conditional scope - (not - (let - ((targets3 nil) - (consideration3 nil) - (blocking-entry3 nil)) - ;; Add targets for checklist match - (setq targets3 - (org-edna--add-targets targets3 - (org-edna--handle-finder 'org-edna-finder/match '("checklist")))) - ;; Handle condition - (setq blocking-entry3 - (or blocking-entry3 - (org-edna--handle-condition 'org-edna-condition/done\? 'nil 'nil targets3 consideration3))))) - ;; Use the top-level scope for then case - (progn - ;; Add targets for self finder - (setq targets1 - (org-edna--add-targets targets1 - (org-edna--handle-finder 'org-edna-finder/self 'nil))) - ;; Mark as TODO - (org-edna--handle-action 'org-edna-action/todo! targets1 - (point-marker) - '(TODO))) - ;; Use the top-level scope for the else case - (progn - ;; Find siblings - (setq targets1 - (org-edna--add-targets targets1 - (org-edna--handle-finder 'org-edna-finder/siblings 'nil))) - ;; Mark as DONE - (org-edna--handle-action 'org-edna-action/todo! targets1 - (point-marker) - '(DONE)))))) - - (output-form (org-edna--expand-sexp-form input-sexp))) - (should (equal output-form expected-form)))) - -(ert-deftest org-edna-expand-sexp-form-if-no-else () - (cl-letf* ((target-ctr 0) - (consideration-ctr 0) - (blocking-entry-ctr 0) - ((symbol-function 'cl-gentemp) - (lambda (&optional prefix) - (let ((ctr (pcase prefix - ("targets" (cl-incf target-ctr)) - ("consideration" (cl-incf consideration-ctr)) - ("blocking-entry" (cl-incf blocking-entry-ctr)) - (_ 0)))) - (intern (format "%s%s" prefix ctr))))) - (input-sexp '((if - ((match "checklist") - (done\?)) - ((self) - (todo! TODO))))) - (expected-form - '(let - ((targets1 nil) - (consideration1 nil) - (blocking-entry1 nil)) - (if - ;; No inheritance in the conditional scope - (not - (let - ((targets3 nil) - (consideration3 nil) - (blocking-entry3 nil)) - ;; Add targets for checklist match - (setq targets3 - (org-edna--add-targets targets3 - (org-edna--handle-finder 'org-edna-finder/match '("checklist")))) - ;; Handle condition - (setq blocking-entry3 - (or blocking-entry3 - (org-edna--handle-condition 'org-edna-condition/done\? 'nil 'nil targets3 consideration3))))) - ;; Use the top-level scope for then case - (progn - ;; Add targets for self finder - (setq targets1 - (org-edna--add-targets targets1 - (org-edna--handle-finder 'org-edna-finder/self 'nil))) - ;; Mark as TODO - (org-edna--handle-action 'org-edna-action/todo! targets1 - (point-marker) - '(TODO))) - ;; End with a nil - nil))) - (output-form (org-edna--expand-sexp-form input-sexp))) - (should (equal output-form expected-form)))) - ;;; Finders @@ -2187,6 +2010,33 @@ the relative finders all still work while cache is enabled." (org-edna-test-mark-done first-pom) (should (string-equal (org-entry-get second-pom "COUNT") "2"))))) +(ert-deftest org-edna-doc-test/multiple-blockers () + (org-edna-doc-test-setup "61e754c2-f292-42b5-8166-e4298dc190d6" + (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")) + + ;; Reset 1 + (org-edna-test-mark-todo first-pom) + + ;; Set 2 to DONE + (org-edna-test-mark-done second-pom) + + ;; Verify that 3 is still blocked + (should (org-edna-test-check-block third-pom "Check after Second")) + + ;; Set 1 to DONE + (org-edna-test-mark-done first-pom) + + ;; Verify that 3 is no longer blocked. + (should (not (org-edna-test-check-block third-pom "Check after Both")))))) + (provide 'org-edna-tests) ;;; org-edna-tests.el ends here diff --git a/org-edna-tests.org b/org-edna-tests.org index 2819700..8553cc4 100644 --- a/org-edna-tests.org +++ b/org-edna-tests.org @@ -348,3 +348,16 @@ DEADLINE: <2000-01-15 Sat +1d> :TRIGGER: next-sibling chain!("COUNT") :END: *** TODO Heading 2 +** Multiple Blockers +:PROPERTIES: +:ID: 61e754c2-f292-42b5-8166-e4298dc190d6 +:END: +*** TODO Heading 1 +:PROPERTIES: +:ID: 1942caf2-caad-4757-b689-3c0029c1d8a5 +:END: +*** TODO Heading 2 +*** TODO Heading 3 +:PROPERTIES: +:BLOCKER: previous-sibling !done? ids(1942caf2-caad-4757-b689-3c0029c1d8a5) !done? +:END: diff --git a/org-edna.el b/org-edna.el index 486cd88..d9ebc2b 100644 --- a/org-edna.el +++ b/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.0 +;; Version: 1.0.1 ;; This file is part of GNU Emacs. @@ -286,8 +286,10 @@ the remainder of FORM after the current scope was parsed." (unless (eq action-or-condition 'condition) (org-edna--syntax-error "Conditions aren't allowed in this context" from-string error-pos)))) - ;; Update state - (setq state type) + ;; Only update state if we're not breaking. If we are, then the + ;; new state doesn't matter. + (unless need-break + (setq state type)) (if need-break ;; changing state ;; Keep current-form on remaining-form so we have it for the ;; next scope, since we didn't process it here. @@ -417,13 +419,22 @@ OLD-BLOCKING-VAR are used internally." (consideration-var (if use-old-scope old-consideration-var (cl-gentemp "consideration"))) - (blocking-var (if use-old-scope + ;; The only time we want a new blocking-var is when we are in a + ;; conditional scope. Otherwise, we want the same blocking-var + ;; passed through all scopes. The only time old-blocking-var won't + ;; be set is if we are starting a new global scope, or we are + ;; starting a conditional scope. + (blocking-var (if old-blocking-var old-blocking-var (cl-gentemp "blocking-entry"))) ;; These won't be used if use-old-scope is non-nil - (let-binds `((,target-var ,old-target-var) - (,consideration-var ,old-consideration-var) - (,blocking-var ,old-blocking-var))) + (tmp-let-binds `((,target-var ,old-target-var) + (,consideration-var ,old-consideration-var))) + ;; Append blocking-var separately to avoid it attempting to let-bind nil. + (let-binds (if old-blocking-var + tmp-let-binds + (append tmp-let-binds + (list (list blocking-var nil))))) (wrapper-form (if use-old-scope '(progn) `(let (,@let-binds))))) diff --git a/org-edna.info b/org-edna.info index abd1b88..78ba679 100644 --- a/org-edna.info +++ b/org-edna.info @@ -93,7 +93,8 @@ Conditions * Regexp Search:: Search for a regular expression * Checking Tags:: Matching against a set of tags * Matching Headings:: Matching against a match string -* Negating Conditions:: +* Negating Conditions:: Doing the opposite +* Multiple Conditions:: stacking blockers Extending Edna @@ -115,6 +116,7 @@ Contributing Changelog +* 1.0.1: 101. * 1.0: 10. * 1.0beta8: 10beta8. * 1.0beta7: 10beta7. @@ -1186,7 +1188,8 @@ means block if any target heading isn’t done. * Regexp Search:: Search for a regular expression * Checking Tags:: Matching against a set of tags * Matching Headings:: Matching against a match string -* Negating Conditions:: +* Negating Conditions:: Doing the opposite +* Multiple Conditions:: stacking blockers File: org-edna.info, Node: Heading is DONE, Next: File Has Headings, Up: Conditions @@ -1336,7 +1339,7 @@ MATCH-STRING. marked as DONE. -File: org-edna.info, Node: Negating Conditions, Prev: Matching Headings, Up: Conditions +File: org-edna.info, Node: Negating Conditions, Next: Multiple Conditions, Prev: Matching Headings, Up: Conditions Negating Conditions ------------------- @@ -1349,6 +1352,28 @@ Any condition can be negated by using ’!’ before the condition. heading tagged “test” does *not* have the property PROP set to “1”. +File: org-edna.info, Node: Multiple Conditions, Prev: Negating Conditions, Up: Conditions + +Multiple Conditions +------------------- + +Multiple blocking conditions can be used for a single entry. The +heading will block if any of the provided conditions evaluate to true. + + * TODO Heading 1 + :PROPERTIES: + :ID: 1942caf2-caad-4757-b689-3c0029c1d8a5 + :END: + * TODO Heading 2 + * TODO Heading 3 + :PROPERTIES: + :BLOCKER: previous-sibling !done? ids(1942caf2-caad-4757-b689-3c0029c1d8a5) !done? + :END: + + “Heading 3” will block if either “Heading 1” isn’t done (ids) or +“Heading 2” isn’t done (previous-sibling). + + File: org-edna.info, Node: Consideration, Next: Conditional Forms, Prev: Conditions, Up: Advanced Features Consideration @@ -1813,6 +1838,7 @@ Changelog * Menu: +* 1.0.1: 101. * 1.0: 10. * 1.0beta8: 10beta8. * 1.0beta7: 10beta7. @@ -1823,7 +1849,15 @@ Changelog * 1.0beta2: 10beta2. -File: org-edna.info, Node: 10, Next: 10beta8, Up: Changelog +File: org-edna.info, Node: 101, Next: 10, Up: Changelog + +1.0.1 +===== + + • Fixed bug in multiple blocking conditions + + +File: org-edna.info, Node: 10, Next: 10beta8, Prev: 101, Up: Changelog 1.0 === @@ -1953,83 +1987,85 @@ Big release here, with three new features. Tag Table: Node: Top225 -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 +Node: Copying4439 +Node: Introduction5261 +Node: Installation and Setup6209 +Node: Basic Operation6933 +Node: Blockers8784 +Node: Triggers9070 +Node: Syntax9332 +Node: Basic Features10022 +Node: Finders10376 +Node: ancestors12141 +Node: children12735 +Node: descendants13145 +Node: file13667 +Node: first-child14416 +Node: ids14676 +Node: match15337 +Node: next-sibling15975 +Node: next-sibling-wrap16232 +Node: olp16546 +Node: org-file16958 +Node: parent17603 +Node: previous-sibling17801 +Node: previous-sibling-wrap18062 +Node: relatives18341 +Node: rest-of-siblings22067 +Node: rest-of-siblings-wrap22352 +Node: self22701 +Node: siblings22862 +Node: siblings-wrap23099 +Node: Actions23403 +Node: Scheduled/Deadline24166 +Node: TODO State27680 +Node: Archive28405 +Node: Chain Property28725 +Node: Clocking29478 +Node: Property29890 +Node: Priority32063 +Node: Tag32632 +Node: Effort32849 +Node: Getting Help33233 +Node: Advanced Features33678 +Node: Finder Cache34126 +Node: Conditions35165 +Node: Heading is DONE36050 +Node: File Has Headings36256 +Node: Heading TODO State36678 +Node: Lisp Variable Set36972 +Node: Heading Has Property37640 +Node: Regexp Search38386 +Node: Checking Tags38829 +Node: Matching Headings39731 +Node: Negating Conditions40328 +Node: Multiple Conditions40751 +Node: Consideration41433 +Node: Conditional Forms43617 +Node: Setting the Properties46305 +Node: Extending Edna47389 +Node: Naming Conventions47879 +Node: Finders 148671 +Node: Actions 149033 +Node: Conditions 149492 +Node: Contributing50378 +Node: Bugs51244 +Node: Working with EDE51601 +Node: Compiling Edna52685 +Node: Testing Edna53554 +Node: Before Sending Changes54535 +Node: Developing with Bazaar55222 +Node: Documentation55963 +Node: Changelog56419 +Node: 10156694 +Node: 1056819 +Node: 10beta857333 +Node: 10beta757456 +Node: 10beta657750 +Node: 10beta558026 +Node: 10beta458413 +Node: 10beta358666 +Node: 10beta259105 End Tag Table diff --git a/org-edna.org b/org-edna.org index 89c32aa..b0c8b79 100644 --- a/org-edna.org +++ b/org-edna.org @@ -1126,6 +1126,7 @@ DONE. *** Negating Conditions :PROPERTIES: :CUSTOM_ID: negate +:DESCRIPTION: Doing the opposite :END: Any condition can be negated by using '!' before the condition. @@ -1135,6 +1136,30 @@ match("test") !has-property?("PROP" "1") The above example will cause the source heading to block if any heading tagged "test" does *not* have the property PROP set to "1". +*** Multiple Conditions +:PROPERTIES: +:CUSTOM_ID: multiple +:DESCRIPTION: stacking blockers +:END: + +Multiple blocking conditions can be used for a single entry. The heading will +block if any of the provided conditions evaluate to true. + +#+begin_src org +,* TODO Heading 1 + :PROPERTIES: + :ID: 1942caf2-caad-4757-b689-3c0029c1d8a5 + :END: +,* TODO Heading 2 +,* TODO Heading 3 + :PROPERTIES: + :BLOCKER: previous-sibling !done? ids(1942caf2-caad-4757-b689-3c0029c1d8a5) !done? + :END: +#+end_src + +"Heading 3" will block if either "Heading 1" isn't done (ids) or "Heading 2" +isn't done (previous-sibling). + ** Consideration :PROPERTIES: :DESCRIPTION: Only some of them @@ -1586,6 +1611,8 @@ making any changes: :PROPERTIES: :DESCRIPTION: List of changes by version :END: +** 1.0.1 +- Fixed bug in multiple blocking conditions ** 1.0 - Various bugs fixes @@ -1595,7 +1622,6 @@ making any changes: - 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