emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master bd64a1d 14/14: Merge commit 'ea0f9fb914cccc1d127eea94bc4c6


From: Ian Dunn
Subject: [elpa] master bd64a1d 14/14: Merge commit 'ea0f9fb914cccc1d127eea94bc4c607dbcd4dc7d'
Date: Sun, 17 Dec 2017 17:39:59 -0500 (EST)

branch: master
commit bd64a1dff8c5eca8b288b1d1532eff4cc14f47a5
Merge: 7f7981b ea0f9fb
Author: Ian Dunn <address@hidden>
Commit: Ian Dunn <address@hidden>

    Merge commit 'ea0f9fb914cccc1d127eea94bc4c607dbcd4dc7d'
---
 packages/org-edna/.bzrignore         |    1 -
 packages/org-edna/dir                |   19 +
 packages/org-edna/org-edna-tests.el  |  599 +++++++++++++-
 packages/org-edna/org-edna-tests.org |   56 ++
 packages/org-edna/org-edna.el        | 1426 ++++++++++++++++++++++++++++------
 packages/org-edna/org-edna.info      | 1411 +++++++++++++++++++++++++++++++++
 packages/org-edna/org-edna.org       |  509 +++++++++---
 7 files changed, 3667 insertions(+), 354 deletions(-)

diff --git a/packages/org-edna/.bzrignore b/packages/org-edna/.bzrignore
index ec97837..5228a6a 100644
--- a/packages/org-edna/.bzrignore
+++ b/packages/org-edna/.bzrignore
@@ -1,6 +1,5 @@
 *.elc
 local.mk
 org-edna-autoloads.el
-org-edna.info
 org-edna.texi
 org-edna.html
\ No newline at end of file
diff --git a/packages/org-edna/dir b/packages/org-edna/dir
new file mode 100644
index 0000000..cf49cac
--- /dev/null
+++ b/packages/org-edna/dir
@@ -0,0 +1,19 @@
+This is the file .../info/dir, which contains the
+topmost node of the Info hierarchy, called (dir)Top.
+The first time you invoke Info you start off looking at this node.
+
+File: dir,     Node: Top       This is the top of the INFO tree
+
+  This (the Directory node) gives a menu of major topics.
+  Typing "q" exits, "H" lists all Info commands, "d" returns here,
+  "h" gives a primer for first-timers,
+  "mEmacs<Return>" visits the Emacs manual, etc.
+
+  In Emacs, you can click mouse button 2 on a menu item or cross reference
+  to select it.
+
+* Menu:
+
+Emacs
+* Org Edna: (org-edna).         Extensible Dependencies ’N’ Actions for 
+                                  Org Mode tasks.
diff --git a/packages/org-edna/org-edna-tests.el 
b/packages/org-edna/org-edna-tests.el
index bdbd49f..d3911db 100644
--- a/packages/org-edna/org-edna-tests.el
+++ b/packages/org-edna/org-edna-tests.el
@@ -52,6 +52,18 @@
 (defconst org-edna-test-id-heading-four  
"7d4d564b-18b2-445c-a0c8-b1b3fb9ad29e")
 (defconst org-edna-test-archive-heading  
"d7668277-f959-43ba-8e85-8a3c76996862")
 
+(defconst org-edna-test-relative-grandparent 
"c07cf4c1-3693-443a-9d79-b581f7cbd62c")
+(defconst org-edna-test-relative-parent-one  
"5a35daf7-4957-4588-9a68-21d8763a9e0d")
+(defconst org-edna-test-relative-parent-two  
"4fe67f03-2b35-4708-8c38-54d2c4dfab81")
+(defconst org-edna-test-relative-standard-child 
"7c542695-8165-4c8b-b44d-4c12fa009548")
+(defconst org-edna-test-relative-child-with-children 
"c7a986df-8d89-4509-b086-6db429b5607b")
+(defconst org-edna-test-relative-grandchild-one 
"588bbd29-2e07-437f-b74d-f72459b545a1")
+(defconst org-edna-test-relative-grandchild-two 
"a7047c81-21ec-46cd-8289-60ad515900ff")
+(defconst org-edna-test-relative-child-with-todo 
"8c0b31a1-af49-473c-92ea-a5c1c3bace33")
+(defconst org-edna-test-relative-commented-child 
"0a1b9508-17ce-49c5-8ff3-28a0076374f5")
+(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-find-test-heading (id)
   "Find the test heading with id ID."
   (org-id-find-id-in-file id org-edna-test-file t))
@@ -210,14 +222,14 @@
 
 (ert-deftest org-edna-finder/siblings ()
   (let* ((org-agenda-files `(,org-edna-test-file))
-         (current (org-id-find "82a4ac3d-9565-4f94-bc84-2bbfd8d7d96c" t))
+         (current (org-id-find org-edna-test-sibling-one-id t))
          (siblings (mapcar
                     (lambda (uuid) (org-id-find uuid t))
-                    '("72534efa-e932-460b-ae2d-f044a0074815"
-                      "06aca55e-ce09-46df-80d7-5b52e55d6505")))
+                    `(,org-edna-test-sibling-one-id
+                      ,org-edna-test-sibling-two-id
+                      ,org-edna-test-sibling-three-id)))
          (targets (org-with-point-at current
                     (org-edna-finder/siblings))))
-    (should (= (length targets) 2))
     (should (equal siblings targets))))
 
 (ert-deftest org-edna-finder/siblings-wrap ()
@@ -254,6 +266,28 @@
     (should (= (length targets) 1))
     (should (equal siblings targets))))
 
+(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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    `(,org-edna-test-sibling-three-id)))
+         (targets (org-with-point-at current
+                    (org-edna-finder/next-sibling-wrap))))
+    (should (= (length targets) 1))
+    (should (equal siblings targets))))
+
+(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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    `(,org-edna-test-sibling-one-id)))
+         (targets (org-with-point-at current
+                    (org-edna-finder/next-sibling-wrap))))
+    (should (= (length targets) 1))
+    (should (equal siblings targets))))
+
 (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))
@@ -296,6 +330,465 @@
     (should (= (length targets) 1))
     (should (equal parent targets))))
 
+(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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    `(,org-edna-test-sibling-one-id)))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives 'from-top 1))))
+    (should (equal siblings targets))))
+
+(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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    `(,org-edna-test-sibling-three-id)))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives 'from-bottom 1))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/forward-wrap-no-wrap ()
+  (let* ((start-marker org-edna-test-sibling-one-id)
+         (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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 1))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/forward-wrap-wrap ()
+  (let* ((start-marker org-edna-test-sibling-three-id)
+         (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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 1))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/forward-no-wrap-no-wrap ()
+  (let* ((start-marker org-edna-test-sibling-one-id)
+         (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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 1))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/forward-no-wrap-wrap ()
+  (let* ((start-marker org-edna-test-sibling-three-id)
+         (target-list nil)
+         (arg 'forward-no-wrap)
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/backward-wrap-no-wrap ()
+  (let* ((start-marker org-edna-test-sibling-three-id)
+         (target-list `(,org-edna-test-sibling-two-id))
+         (arg 'backward-wrap)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/backward-wrap-wrap ()
+  (let* ((start-marker org-edna-test-sibling-one-id)
+         (target-list `(,org-edna-test-sibling-three-id))
+         (arg 'backward-wrap)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/backward-no-wrap-no-wrap ()
+  (let* ((start-marker org-edna-test-sibling-three-id)
+         (target-list `(,org-edna-test-sibling-two-id))
+         (arg 'backward-no-wrap)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/backward-no-wrap-wrap ()
+  (let* ((start-marker org-edna-test-sibling-one-id)
+         (target-list nil)
+         (arg 'backward-no-wrap)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/walk-up ()
+  (let* ((start-marker org-edna-test-sibling-one-id)
+         (target-list `(,org-edna-test-parent-id))
+         (arg 'walk-up)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/walk-up-with-self ()
+  (let* ((start-marker org-edna-test-sibling-one-id)
+         (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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 1))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/walk-down ()
+  (let* ((start-marker org-edna-test-parent-id)
+         (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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 1))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/walk-down-with-self ()
+  (let* ((start-marker org-edna-test-parent-id)
+         (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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 1))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/walk-down ()
+  (let* ((start-marker org-edna-test-parent-id)
+         (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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 1))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/walk-down-full ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-standard-child
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-grandchild-one
+                        ,org-edna-test-relative-grandchild-two
+                        ,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-commented-child
+                        ,org-edna-test-relative-archived-child
+                        ,org-edna-test-relative-child-with-done))
+         (arg 'walk-down)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/step-down-full ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-standard-child
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-commented-child
+                        ,org-edna-test-relative-archived-child
+                        ,org-edna-test-relative-child-with-done))
+         (arg 'step-down)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/filter-todo-only ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (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))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 'todo-only))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/filter-todo-and-done-only ()
+  (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))
+         (arg 'step-down)
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg 'todo-and-done-only))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/filter-no-comments ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-standard-child
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-archived-child
+                        ,org-edna-test-relative-child-with-done))
+         (arg 'step-down)
+         (filter 'no-comment)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg filter size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/filter-no-archive ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-standard-child
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-commented-child
+                        ,org-edna-test-relative-child-with-done))
+         (arg 'step-down)
+         (filter 'no-archive)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg filter size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/filter-has-tag ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-archived-child))
+         (arg 'step-down)
+         (filter "+ARCHIVE")
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg filter size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/filter-no-tag ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-standard-child
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-commented-child
+                        ,org-edna-test-relative-child-with-done))
+         (arg 'step-down)
+         (filter "-ARCHIVE")
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg filter size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/filter-matches-regexp ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-child-with-done))
+         (arg 'step-down)
+         (filter "Child Heading With .*")
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg filter size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/sort-reverse ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-child-with-done
+                        ,org-edna-test-relative-archived-child
+                        ,org-edna-test-relative-commented-child
+                        ,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-standard-child))
+         (arg 'step-down)
+         (sort 'reverse-sort)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets (org-with-point-at current
+                    (org-edna-finder/relatives arg sort size))))
+    (should (equal siblings targets))))
+
+(ert-deftest org-edna-relatives/sort-priority ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-archived-child
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-commented-child
+                        ,org-edna-test-relative-standard-child
+                        ,org-edna-test-relative-child-with-done))
+         (arg 'step-down)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list))
+         (targets ))
+    (should (equal siblings
+                   (org-with-point-at current
+                     (org-edna-finder/relatives arg 'priority-up size))))
+    (should (equal (nreverse siblings)
+                   (org-with-point-at current
+                     (org-edna-finder/relatives arg 'priority-down size))))))
+
+(ert-deftest org-edna-relatives/sort-effort ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-child-with-done
+                        ,org-edna-test-relative-commented-child
+                        ,org-edna-test-relative-archived-child
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-child-with-todo
+                        ,org-edna-test-relative-standard-child))
+         (arg 'step-down)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list)))
+    (should (equal siblings
+                   (org-with-point-at current
+                     (org-edna-finder/relatives arg 'effort-up size))))
+    (should (equal (nreverse siblings)
+                   (org-with-point-at current
+                     (org-edna-finder/relatives arg 'effort-down size))))))
+
+(ert-deftest org-edna-relatives/sort-scheduled ()
+  (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-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list)))
+    (should (equal siblings
+                   (org-with-point-at current
+                     (org-edna-finder/relatives arg 'scheduled-up size))))
+    (should (equal (nreverse siblings)
+                   (org-with-point-at current
+                     (org-edna-finder/relatives arg 'scheduled-down size))))))
+
+(ert-deftest org-edna-relatives/sort-deadline ()
+  (let* ((start-marker org-edna-test-relative-parent-one)
+         (target-list `(,org-edna-test-relative-commented-child
+                        ,org-edna-test-relative-standard-child
+                        ,org-edna-test-relative-child-with-done
+                        ,org-edna-test-relative-child-with-children
+                        ,org-edna-test-relative-archived-child
+                        ,org-edna-test-relative-child-with-todo))
+         (arg 'step-down)
+         (size (length target-list))
+         (org-agenda-files `(,org-edna-test-file))
+         (current (org-id-find start-marker t))
+         (siblings (mapcar
+                    (lambda (uuid) (org-id-find uuid t))
+                    target-list)))
+    (should (equal siblings
+                   (org-with-point-at current
+                     (org-edna-finder/relatives arg 'deadline-up size))))
+    (should (equal (nreverse siblings)
+                   (org-with-point-at current
+                     (org-edna-finder/relatives arg 'deadline-down size))))))
+
 
 ;; Actions
 
@@ -330,7 +823,6 @@
       (should (not (org-entry-get nil "SCHEDULED"))))))
 
 (ert-deftest org-edna-action-scheduled/cp ()
-  ;; Override `current-time' so we can get a deterministic value
   (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))
@@ -350,20 +842,113 @@
              (org-agenda-files `(,org-edna-test-file))
              (target (org-id-find "97e6b0f0-40c4-464f-b760-6e5ca9744eb5" t)))
     (org-with-point-at target
-      ;; Time started at Jan 15, 2000
+      ;; 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>")))))
+
+(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>")))))
@@ -374,7 +959,7 @@
       (org-edna-action/tag! nil "tag")
       (should (equal (org-get-tags) '("tag")))
       (org-edna-action/tag! nil "")
-      (should-not (org-get-tags)))))
+      (should (equal (org-get-tags) '(""))))))
 
 (ert-deftest org-edna-action-property ()
   (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one)))
diff --git a/packages/org-edna/org-edna-tests.org 
b/packages/org-edna/org-edna-tests.org
index 0c2c24e..01c0dcc 100644
--- a/packages/org-edna/org-edna-tests.org
+++ b/packages/org-edna/org-edna-tests.org
@@ -80,3 +80,59 @@ SCHEDULED: <2017-01-01 Sun>
 :ID:       caccd0a6-d400-410a-9018-b0635b07a37e
 :LOGGING:  nil
 :END:
+* Relatives Tests
+:PROPERTIES:
+:ID:       c07cf4c1-3693-443a-9d79-b581f7cbd62c
+:END:
+** Parent Heading #1
+:PROPERTIES:
+:ID:       5a35daf7-4957-4588-9a68-21d8763a9e0d
+:END:
+*** [#C] Standard Child Heading
+DEADLINE: <2017-01-07 Sat> SCHEDULED: <2017-01-02 Mon>
+:PROPERTIES:
+:ID:       7c542695-8165-4c8b-b44d-4c12fa009548
+:Effort:   0:01
+:END:
+*** [#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:
+**** Child Heading One
+:PROPERTIES:
+:ID:       588bbd29-2e07-437f-b74d-f72459b545a1
+:END:
+**** Child Heading Two
+:PROPERTIES:
+:ID:       a7047c81-21ec-46cd-8289-60ad515900ff
+:END:
+*** TODO [#A] Child Heading with TODO
+DEADLINE: <2017-01-01 Sun> SCHEDULED: <2017-01-06 Fri>
+:PROPERTIES:
+:ID:       8c0b31a1-af49-473c-92ea-a5c1c3bace33
+:Effort:   0:02
+:END:
+*** [#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:
+*** [#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:
+*** 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:
+** Parent Sub Heading #2
+:PROPERTIES:
+:ID:       4fe67f03-2b35-4708-8c38-54d2c4dfab81
+:END:
diff --git a/packages/org-edna/org-edna.el b/packages/org-edna/org-edna.el
index d11f5de..d460543 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.0alpha1
+;; Version: 1.0beta1
 
 ;; This file is part of GNU Emacs.
 
@@ -29,16 +29,22 @@
 ;; fulfilled before a task can be completed and actions to take once it is.
 
 ;; Org Edna runs when either the BLOCKER or TRIGGER properties are set on a
-;; headline, and when it is changing from a TODO state to a DONE state.
+;; heading, and when it is changing from a TODO state to a DONE state.
 
 ;;; History:
 
 ;;; Code:
 
 (require 'org)
-(require 'subr-x)
+(eval-when-compile (require 'subr-x))
 (require 'seq)
 
+;; Compatibility for Emacs < 26.1
+(unless (fboundp 'if-let*)
+  (defalias 'if-let* 'if-let))
+(unless (fboundp 'when-let*)
+  (defalias 'when-let* 'when-let))
+
 (defgroup org-edna nil
   "Extensible Dependencies 'N' Actions"
   :group 'org)
@@ -145,12 +151,12 @@ function."
 (defun org-edna--handle-condition (func mod args targets consideration)
   "Handle a condition."
   ;; Check the condition at each target
-  (when-let ((blocks
-              (mapcar
-               (lambda (entry-marker)
-                 (org-with-point-at entry-marker
-                   (apply func mod args)))
-               targets)))
+  (when-let* ((blocks
+               (mapcar
+                (lambda (entry-marker)
+                  (org-with-point-at entry-marker
+                    (apply func mod args)))
+                targets)))
     ;; Apply consideration
     (org-edna-handle-consideration consideration blocks)))
 
@@ -163,7 +169,7 @@ indicating whether FORM accepts actions or conditions."
         (blocking-entry)
         (consideration 'all)
         (state nil) ;; Type of operation
-        ;; Keep track of the current headline
+        ;; Keep track of the current heading
         (last-entry (point-marker))
         (pos 0))
     (while (< pos (length form))
@@ -241,7 +247,7 @@ is changing from a TODO state to a DONE state, run BODY."
            (error
             (if (eq (car err) 'invalid-read-syntax)
                 (org-edna--print-syntax-error (cdr err))
-              (message "Edna Error: %s" (error-message-string err)))
+              (message "Edna Error at heading %s: %s" (org-get-heading t t t) 
(error-message-string err)))
             (setq org-block-entry-blocking (org-get-heading))
             ;; Block
             nil))
@@ -256,7 +262,7 @@ See `org-edna-run' for CHANGE-PLIST explanation.
 
 This shouldn't be run from outside of `org-trigger-hook'."
   (org-edna-run change-plist
-    (when-let ((form (org-entry-get pos "TRIGGER" org-edna-use-inheritance)))
+    (when-let* ((form (org-entry-get pos "TRIGGER" org-edna-use-inheritance)))
       (org-edna-process-form form 'action))))
 
 (defun org-edna-blocker-function (change-plist)
@@ -266,7 +272,7 @@ See `org-edna-run' for CHANGE-PLIST explanation.
 
 This shouldn't be run from outside of `org-blocker-hook'."
   (org-edna-run change-plist
-    (if-let ((form (org-entry-get pos "BLOCKER" org-edna-use-inheritance)))
+    (if-let* ((form (org-entry-get pos "BLOCKER" org-edna-use-inheritance)))
         (org-edna-process-form form 'condition)
       t)))
 
@@ -295,6 +301,8 @@ Remove Edna's workers from `org-trigger-hook' and
 (defun org-edna-finder/match (match-spec &optional scope skip)
   "Find entries using Org matching.
 
+Edna Syntax: match(\"MATCH-SPEC\" SCOPE SKIP)
+
 MATCH-SPEC may be any valid match string; it is passed straight
 into `org-map-entries'.
 
@@ -316,92 +324,467 @@ SCOPE defaults to agenda, and SKIP defaults to nil.
 
 ;; ID finder
 (defun org-edna-finder/ids (&rest ids)
-  "Find a list of headlines with given IDs.
+  "Find a list of headings with given IDs.
+
+Edna Syntax: ids(ID1 ID2 ...)
 
-IDS are all UUIDs as understood by `org-id-find'."
+Each ID is a UUID as understood by `org-id-find'.
+
+Note that in the edna syntax, the IDs don't need to be quoted."
   (mapcar (lambda (id) (org-id-find id 'marker)) ids))
 
 (defun org-edna-finder/self ()
+  "Finder for the current heading.
+
+Edna Syntax: self"
   (list (point-marker)))
 
-(defun org-edna-finder/siblings ()
+(defun org-edna-first-sibling ()
+  "Return a marker to the first child of the current level."
   (org-with-wide-buffer
-   (let ((self (and (ignore-errors (org-back-to-heading t)) (point)))
-         (markers))
-     (org-up-heading-safe)
-     (org-goto-first-child)
-     (unless (equal (point) self)
-       (push (point-marker) markers))
-     (while (org-get-next-sibling)
-       (unless (equal (point) self)
-         (push (point-marker) markers)))
-     (nreverse markers))))
-
-(defun org-edna-finder/siblings-wrap ()
+   (org-up-heading-safe)
+   (org-goto-first-child)
+   (point-marker)))
+
+(defun org-edna-last-sibling ()
+  "Return a marker to the first child of the current level."
+  ;; Unfortunately, we have to iterate through every heading on this level to
+  ;; find the first one.
   (org-with-wide-buffer
-   (let ((self (and (ignore-errors (org-back-to-heading t)) (point)))
-         (markers))
-     ;; Go from this heading to the end
-     (save-excursion
-       (while (org-get-next-sibling)
-         (unless (equal (point) self)
-           (push (point-marker) markers))))
-     ;; Go to the first heading
-     (org-up-heading-safe)
-     (org-goto-first-child)
-     (save-excursion
-       (while (not (equal (point) self))
-         (push (point-marker) markers)
-         (org-get-next-sibling)))
-     (nreverse markers))))
-
-(defun org-edna-finder/rest-of-siblings ()
+   (while (org-goto-sibling)
+     ;; Do nothing, just keep going down
+     )
+   (point-marker)))
+
+(defun org-edna-goto-sibling (&optional previous wrap)
+  "Move to the next sibling on the same level as the current heading."
+  (let ((next (save-excursion
+                (if previous (org-get-last-sibling) (org-get-next-sibling)))))
+    (cond
+     ;; We have a sibling, so go to it and return non-nil
+     (next (goto-char next))
+     ;; We have no sibling, and we're not wrapping, so return nil
+     ((not wrap) nil)
+     (t
+      ;; Go to the first child if going forward, or the last if going backward,
+      ;; and return non-nil.
+      (goto-char
+       (if previous
+           (org-edna-last-sibling)
+         (org-edna-first-sibling)))
+      t))))
+
+(defun org-edna-self-marker ()
+  "Return a marker to the current heading."
   (org-with-wide-buffer
-   (let ((self (and (ignore-errors (org-back-to-heading t)) (point)))
-         (markers))
-     ;; Go from this heading to the end
-     (while (org-get-next-sibling)
-       (unless (equal (point) self)
-         (push (point-marker) markers)))
-     (nreverse markers))))
+   (and (ignore-errors (org-back-to-heading t) (point-marker)))))
 
-(defun org-edna-finder/next-sibling ()
-  (org-with-wide-buffer
-   (and (org-get-next-sibling)
-        (list (point-marker)))))
+(defun org-edna-collect-current-level (start backward wrap include-point)
+  "Collect the headings on the current level.
 
-(defun org-edna-finder/previous-sibling ()
-  (org-with-wide-buffer
-   (and (org-get-last-sibling)
-        (list (point-marker)))))
+START is a point or marker from which to start collection.
 
-(defun org-edna-finder/first-child ()
-  (org-with-wide-buffer
-   (and (org-goto-first-child)
-        (list (point-marker)))))
+BACKWARDS means go backward through the level instead of forward.
+
+If WRAP is non-nil, wrap around when the end of the current level
+is reached.
 
-(defun org-edna-finder/children ()
+If INCLUDE-START is non-nil, include the current point."
   (org-with-wide-buffer
    (let ((markers))
-     (org-goto-first-child)
-     (push (point-marker) markers)
-     (while (org-get-next-sibling)
+     (goto-char start)
+     ;; Handle including point
+     (when include-point
+       (push (point-marker) markers))
+     (while (and (org-edna-goto-sibling backward wrap)
+                 (not (equal (point-marker) start)))
        (push (point-marker) markers))
      (nreverse markers))))
 
-(defun org-edna-finder/parent ()
-  (org-with-wide-buffer
-   (and (org-up-heading-safe)
-        (list (point-marker)))))
+(defun org-edna-collect-ancestors (&optional with-self)
+  (let ((markers))
+    (when with-self
+      (push (point-marker) markers))
+    (org-with-wide-buffer
+     (while (org-up-heading-safe)
+       (push (point-marker) markers)))
+    (nreverse markers)))
+
+(defun org-edna-collect-descendants (&optional with-self)
+  (let ((targets
+         (org-with-wide-buffer
+          (org-map-entries
+           (lambda nil (point-marker))
+           nil 'tree))))
+    ;; Remove the first one (self) if we didn't want self
+    (unless with-self
+      (pop targets))
+    targets))
+
+(defun org-edna-entry-has-tags-p (&rest tags)
+  "Returns non-nil if the current entry has any tags in TAGS."
+  (when-let* ((entry-tags (org-get-tags-at)))
+    (seq-intersection tags entry-tags)))
+
+(defun org-edna-finder/relatives (&rest options)
+  "Find some relative of the current heading.
+
+Edna Syntax: relatives(OPTION OPTION...)
+Edna Syntax: chain-find(OPTION OPTION...)
+
+Identical to the chain argument in org-depend, relatives selects
+its single target using the following method:
 
-(defun org-edna-finder/descendants ()
-  (org-with-wide-buffer
-   (org-map-entries
-    (lambda nil (point-marker))
-    nil 'tree)))
+1. Creates a list of possible targets
+2. Filters the targets from Step 1
+3. Sorts the targets from Step 2
+
+One option from each of the following three categories may be
+used; if more than one is specified, the last will be used.
+Filtering is the exception to this; each filter argument adds to
+the current filter.  Apart from that, argument order is
+irrelevant.
+
+The chain-find finder is also provided for backwards
+compatibility, and for similarity to org-depend.
+
+All arguments are symbols, unless noted otherwise.
+
+*Selection*
+
+- from-top:             Select siblings of the current heading, starting at 
the top
+- from-bottom:          As above, but from the bottom
+- from-current:         Selects siblings, starting from the heading (wraps)
+- no-wrap:              As above, but without wrapping
+- forward-no-wrap:      Find entries on the same level, going forward
+- forward-wrap:         As above, but wrap when the end is reached
+- backward-no-wrap:     Find entries on the same level, going backward
+- backward-wrap:        As above, but wrap when the start is reached
+- walk-up:              Walk up the tree, excluding self
+- walk-up-with-self:    As above, but including self
+- walk-down:            Recursively walk down the tree, excluding self
+- walk-down-with-self:  As above, but including self
+- step-down:            Collect headings from one level down
+
+*Filtering*
+
+- todo-only:          Select only targets with TODO state set that isn't a 
DONE state
+- todo-and-done-only: Select all targets with a TODO state set
+- no-comments:        Skip commented headings
+- no-archive:         Skip archived headings
+- NUMBER:             Only use that many headings, starting from the first one
+                      If passed 0, use all headings
+                      If <0, omit that many headings from the end
+- \"+tag\":           Only select headings with given tag
+- \"-tag\":           Only select headings without tag
+- \"REGEX\":          select headings whose titles match REGEX
+
+*Sorting*
+
+- no-sort:         Remove other sorting in affect
+- reverse-sort:    Reverse other sorts (stacks with other sort methods)
+- random-sort:     Sort in a random order
+- priority-up:     Sort by priority, highest first
+- priority-down:   Same, but lowest first
+- effort-up:       Sort by effort, highest first
+- effort-down:     Sort by effort, lowest first
+- scheduled-up:    Scheduled time, farthest first
+- scheduled-down:  Scheduled time, closest first
+- deadline-up:     Deadline time, farthest first
+- deadline-down:   Deadline time, closest first
+"
+  (let (targets
+        sortfun
+        reverse-sort
+        (idx 0) ;; By default, use all entries
+        filterfuns ;; No filtering by default
+        ;; From org-depend.el:
+        ;; (and (not todo-and-done-only)
+        ;;      (member (second item) org-done-keywords))
+        )
+    (dolist (opt options)
+      (pcase opt
+        ('from-top
+         (setq targets (org-edna-collect-current-level 
(org-edna-first-sibling) nil nil t)))
+        ('from-bottom
+         (setq targets (org-edna-collect-current-level (org-edna-last-sibling) 
t nil t)))
+        ((or 'from-current 'forward-wrap)
+         (setq targets (org-edna-collect-current-level (org-edna-self-marker) 
nil t nil)))
+        ((or 'no-wrap 'forward-no-wrap)
+         (setq targets (org-edna-collect-current-level (org-edna-self-marker) 
nil nil nil)))
+        ('backward-no-wrap
+         (setq targets (org-edna-collect-current-level (org-edna-self-marker) 
t nil nil)))
+        ('backward-wrap
+         (setq targets (org-edna-collect-current-level (org-edna-self-marker) 
t t nil)))
+        ('walk-up
+         (setq targets (org-edna-collect-ancestors nil)))
+        ('walk-up-with-self
+         (setq targets (org-edna-collect-ancestors t)))
+        ('walk-down
+         (setq targets (org-edna-collect-descendants nil)))
+        ('walk-down-with-self
+         (setq targets (org-edna-collect-descendants t)))
+        ('step-down
+         (setq targets
+               (org-with-wide-buffer
+                (org-goto-first-child)
+                (org-edna-collect-current-level (org-edna-self-marker) nil nil 
t))))
+        ('todo-only
+         ;; Remove any entry without a TODO keyword, or with a DONE keyword
+         (cl-pushnew
+          (lambda (target)
+            (let ((kwd (org-entry-get target "TODO")))
+              (or (not kwd)
+                  (member kwd org-done-keywords))))
+          filterfuns
+          :test 'equal))
+        ('todo-and-done-only
+         ;; Remove any entry without a TODO keyword
+         (cl-pushnew
+          (lambda (target)
+            (not (org-entry-get target "TODO")))
+          filterfuns :test 'equal))
+        ((pred numberp)
+         (setq idx opt))
+        ((and (pred stringp)
+              (pred (lambda (opt) (string-match-p "^\\+" opt))))
+         (cl-pushnew
+          (lambda (target)
+            ;; This is a function that will return non-nil if the entry should
+            ;; be removed, so remove those entries that don't have the tag
+            (org-with-point-at target
+              (not (org-edna-entry-has-tags-p (string-remove-prefix "+" 
opt)))))
+          filterfuns :test 'equal))
+        ((and (pred stringp)
+              (pred (lambda (opt) (string-match-p "^\\-" opt))))
+         (cl-pushnew
+          (lambda (target)
+            ;; This is a function that will return non-nil if the entry should
+            ;; be removed, so remove those entries that DO have the tag
+            (org-with-point-at target
+              (org-edna-entry-has-tags-p (string-remove-prefix "-" opt))))
+          filterfuns :test 'equal))
+        ((pred stringp)
+         (cl-pushnew
+          (lambda (target)
+            ;; Return non-nil if entry doesn't match the regular expression, so
+            ;; it will be removed.
+            (not (string-match-p opt
+                               (org-with-point-at target
+                                 (org-get-heading t t t t)))))
+          filterfuns :test 'equal))
+        ('no-comment
+         (cl-pushnew
+          (lambda (target)
+            (org-with-point-at target
+              (org-in-commented-heading-p)))
+          filterfuns :test 'equal))
+        ('no-archive
+         (cl-pushnew
+          (lambda (target)
+            (org-with-point-at target
+              (org-edna-entry-has-tags-p org-archive-tag)))
+          filterfuns :test 'equal))
+        ('no-sort
+         (setq sortfun nil
+               reverse-sort nil))
+        ('random-sort
+         (setq sortfun
+               (lambda (_rhs _lhs)
+                 (let ((l (random 100))
+                       (r (random 100)))
+                   (< l r)))))
+        ('reverse-sort
+         (setq reverse-sort t))
+        ('priority-up
+         ;; A is highest priority, but assigned the lowest value, so we need to
+         ;; reverse the sort here.
+         (setq sortfun
+               (lambda (lhs rhs)
+                 (let ((priority-lhs (org-entry-get lhs "PRIORITY"))
+                       (priority-rhs (org-entry-get rhs "PRIORITY")))
+                   (string-lessp priority-lhs priority-rhs)))))
+        ('priority-down
+         (setq sortfun
+               (lambda (lhs rhs)
+                 (let ((priority-lhs (org-entry-get lhs "PRIORITY"))
+                       (priority-rhs (org-entry-get rhs "PRIORITY")))
+                   (not (string-lessp priority-lhs priority-rhs))))))
+        ('effort-up
+         (setq sortfun
+               (lambda (lhs rhs)
+                 (let ((effort-lhs (org-duration-to-minutes (org-entry-get lhs 
"EFFORT")))
+                       (effort-rhs (org-duration-to-minutes (org-entry-get rhs 
"EFFORT"))))
+                   (not (< effort-lhs effort-rhs))))))
+        ('effort-down
+         (setq sortfun
+               (lambda (lhs rhs)
+                 (let ((effort-lhs (org-duration-to-minutes (org-entry-get lhs 
"EFFORT")))
+                       (effort-rhs (org-duration-to-minutes (org-entry-get rhs 
"EFFORT"))))
+                   (< effort-lhs effort-rhs)))))
+        ('scheduled-up
+         (setq sortfun
+               (lambda (lhs rhs)
+                 (let ((time-lhs (org-get-scheduled-time lhs))
+                       (time-rhs (org-get-scheduled-time rhs)))
+                   (not (time-less-p time-lhs time-rhs))))))
+        ('scheduled-down
+         (setq sortfun
+               (lambda (lhs rhs)
+                 (let ((time-lhs (org-get-scheduled-time lhs))
+                       (time-rhs (org-get-scheduled-time rhs)))
+                   (time-less-p time-lhs time-rhs)))))
+        ('deadline-up
+         (setq sortfun
+               (lambda (lhs rhs)
+                 (let ((time-lhs (org-get-deadline-time lhs))
+                       (time-rhs (org-get-deadline-time rhs)))
+                   (not (time-less-p time-lhs time-rhs))))))
+        ('deadline-down
+         (setq sortfun
+               (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)))))))
+    (setq filterfuns (nreverse filterfuns))
+    (when (and targets sortfun)
+      (setq targets (seq-sort sortfun targets)))
+    (dolist (filterfun filterfuns)
+      (setq targets (seq-remove filterfun targets)))
+    (when reverse-sort
+      (setq targets (nreverse targets)))
+    (when (and targets (/= idx 0))
+      (if (> idx (seq-length targets))
+          (message "Edna relatives finder got index %s out of bounds of target 
size; ignoring" idx)
+        (setq targets (seq-subseq targets 0 idx))))
+    targets))
+
+(defalias 'org-edna-finder/chain-find 'org-edna-finder/relatives)
+
+(defun org-edna-finder/siblings (&rest options)
+  "Finder for all siblings of the source heading.
+
+Edna Syntax: siblings(OPTIONS...)
+
+Siblings are returned in order, starting from the first heading.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 'from-top options))
+
+(defun org-edna-finder/rest-of-siblings (&rest options)
+  "Finder for the siblings after the source heading.
+
+Edna Syntax: rest-of-siblings(OPTIONS...)
+
+Siblings are returned in order, starting from the first heading
+after the source heading.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 'forward-no-wrap options))
+
+(defun org-edna-finder/rest-of-siblings-wrap (&rest options)
+  "Finder for all siblings of the source heading.
+
+Edna Syntax: rest-of-siblings-wrap(OPTIONS...)
+
+Siblings are returned in order, starting from the first heading
+after the source heading and wrapping when it reaches the end.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 'forward-wrap options))
+
+(defalias 'org-edna-finder/siblings-wrap 
'org-edna-finder/rest-of-siblings-wrap)
+
+(defun org-edna-finder/next-sibling (&rest options)
+  "Finder for the next sibling after the source heading.
+
+Edna Syntax: next-sibling(OPTIONS...)
+
+If the source heading is the last of its siblings, no target is
+returned.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 1 'forward-no-wrap options))
+
+(defun org-edna-finder/next-sibling-wrap (&rest options)
+  "Finder for the next sibling after the source heading.
+
+Edna Syntax: next-sibling-wrap(OPTIONS...)
+
+If the source heading is the last of its siblings, its first
+sibling is returned.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 1 'forward-wrap options))
+
+(defun org-edna-finder/previous-sibling (&rest options)
+  "Finder for the first sibling before the source heading.
+
+Edna Syntax: previous-sibling(OPTIONS...)
+
+If the source heading is the first of its siblings, no target is
+returned.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 1 'backward-no-wrap options))
+
+(defun org-edna-finder/previous-sibling-wrap (&rest options)
+  "Finder for the first sibling before the source heading.
+
+Edna Syntax: previous-sibling-wrap(OPTIONS...)
+
+If the source heading is the first of its siblings, no target is
+returned.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 1 'backward-wrap options))
+
+(defun org-edna-finder/first-child (&rest options)
+  "Return the first child of the source heading.
+
+Edna Syntax: first-child(OPTIONS...)
+
+If the source heading has no children, no target is returned.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 1 'step-down options))
+
+(defun org-edna-finder/children (&rest options)
+  "Finder for the immediate children of the source heading.
+
+Edna Syntax: children(OPTIONS...)
+
+If the source has no children, no target is returned.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 'step-down options))
+
+(defun org-edna-finder/parent (&rest options)
+  "Finder for the parent of the source heading.
+
+Edna Syntax: parent(OPTIONS...)
+
+If the source heading is a top-level heading, no target is
+returned.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 1 'walk-up options))
+
+(defun org-edna-finder/descendants (&rest options)
+  "Finder for all descendants of the source heading.
+
+Edna Syntax: descendants(OPTIONS...)
+
+This is ALL descendants of the source heading, across all
+levels.  This also includes the source heading.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 'walk-down options))
 
-(defun org-edna-finder/ancestors ()
-  "Find a list of ancestors.
+(defun org-edna-finder/ancestors (&rest options)
+  "Finder for the ancestors of the source heading.
+
+Edna Syntax: ancestors(OPTIONS...)
 
 Example:
 
@@ -416,17 +799,18 @@ Example:
 
 In the above example, Heading 5 will be blocked until Heading 1,
 Heading 3, and Heading 4 are marked DONE, while Heading 2 is
-ignored."
-  (org-with-wide-buffer
-   (let ((markers))
-     (while (org-up-heading-safe)
-       (push (point-marker) markers))
-     (nreverse markers))))
+ignored.
+
+See `org-edna-finder/relatives' for the OPTIONS argument."
+  (apply 'org-edna-finder/relatives 'walk-up options))
 
 (defun org-edna-finder/olp (file olp)
-  "Find a headline by its outline path.
+  "Finder for heading by its outline path.
 
-Finds the heading given by OLP in FILE.  Both arguments are strings.
+Edna Syntax: olp(\"FILE\" \"OLP\")
+
+Finds the heading given by OLP in FILE.  Both arguments are
+strings.  OLP is an outline path.  Example:
 
 * TODO Test
   :PROPERTIES:
@@ -442,146 +826,75 @@ Test will block if the heading \"path/to/heading\" in
 ;; TODO: Clean up the buffer when it's finished
 
 (defun org-edna-finder/file (file)
-  "Find a file by name.
+  "Finder for a file by its name.
+
+Edna Syntax: file(\"FILE\")
 
-The `file' finder finds a single file, specified as a string.
-The returned target will be the minimum point in the file.
+FILE is the full path to the desired file.  The returned target
+will be the minimum point in the file.
 
 * TODO Test
   :PROPERTIES:
   :BLOCKER:  file(\"~/myfile.org\") headings?
   :END:
 
-Here, \"Test\" will block until myfile.org is clear of headlines.
+Here, \"Test\" will block until myfile.org is clear of headings.
 
-Note that with the default condition, `file' won't work."
+Note that this does not give a valid heading, so any conditions
+or actions that require will throw an error.  Consult the
+documentation for individual actions or conditions to determine
+which ones will and won't work."
   ;; If there isn't a buffer visiting file, then there's no point in having a
   ;; marker to the start of the file, so use `find-file-noselect'.
   (with-current-buffer (find-file-noselect file)
     (list (point-min-marker))))
 
 (defun org-edna-finder/org-file (file)
-  "Find a file in `org-directory'.
+  "Finder for FILE in `org-directory'.
 
-A special form of `file', `org-file' will find FILE (a string) in
-`org-directory'.
+Edna Syntax: org-file(\"FILE\")
+
+FILE is the relative path of a file in `org-directory'.  Nested
+files are allowed, such as \"my-directory/my-file.org\".  The
+returned target is the minimum point of FILE.
 
 * TODO Test
   :PROPERTIES:
   :BLOCKER:  org-file(\"test.org\")
   :END:
 
-Note that the file still requires an extension."
+Note that the file still requires an extension; the \"org\" here
+just means to look in `org-directory', not necessarily an
+`org-mode' file.
+
+Note that this does not give a valid heading, so any conditions
+or actions that require will throw an error.  Consult the
+documentation for individual actions or conditions to determine
+which ones will and won't work."
   (with-current-buffer (find-file-noselect (expand-file-name file 
org-directory))
     (list (point-min-marker))))
 
-(defun org-edna-finder/chain-find (&rest options)
-  "Find a target as org-depend does.
-
-Identical to the chain argument in org-depend, chain-find selects its single
-target using the following method:
-
-1. Creates a list of possible targets
-2. Filters the targets from Step 1
-3. Sorts the targets from Step 2
-
-After this is finished, chain-find selects the first target in the list and
-returns it.
-
-One option from each of the following three categories may be used; if more 
than
-one is specified, the last will be used.
-
-*Selection*
-
-- from-top:     Select siblings of the current headline, starting at the top
-- from-bottom:  As above, but from the bottom
-- from-current: Selects siblings, starting from the headline (wraps)
-- no-wrap:      As above, but without wrapping
-
-*Filtering*
-
-- todo-only:          Select only targets with TODO state set that isn't a 
DONE state
-- todo-and-done-only: Select all targets with a TODO state set
-
-*Sorting*
-
-- priority-up:   Sort by priority, highest first
-- priority-down: Same, but lowest first
-- effort-up:     Sort by effort, highest first
-- effort-down:   Sort by effort, lowest first"
-  ;; sortfun - function to use to sort elements
-  ;; filterfun - Function to use to filter elements
-  ;; Both should handle positioning point
-  (let (targets
-        sortfun
-        ;; From org-depend.el:
-        ;; (and (not todo-and-done-only)
-        ;;      (member (second item) org-done-keywords))
-        (filterfun (lambda (target)
-                     (member (org-entry-get target "TODO") 
org-done-keywords))))
-    (dolist (opt options)
-      (pcase opt
-        ('from-top
-         (setq targets (org-edna-finder/siblings)))
-        ('from-bottom
-         (setq targets (seq-reverse (org-edna-finder/siblings))))
-        ('from-current
-         (setq targets (org-edna-finder/siblings-wrap)))
-        ('no-wrap
-         (setq targets (org-edna-finder/rest-of-siblings)))
-        ('todo-only
-         ;; Remove any entry without a TODO keyword, or with a DONE keyword
-         (setq filterfun
-               (lambda (target)
-                 (let ((kwd (org-entry-get target "TODO")))
-                   (or (not kwd)
-                       (member kwd org-done-keywords))))))
-        ('todo-and-done-only
-         ;; Remove any entry without a TODO keyword
-         (setq filterfun
-               (lambda (target)
-                 (not (org-entry-get target "TODO")))))
-        ('priority-up
-         (setq sortfun
-               (lambda (lhs rhs)
-                 (let ((priority-lhs (org-entry-get lhs "PRIORITY"))
-                       (priority-rhs (org-entry-get rhs "PRIORITY")))
-                   (not (string-lessp priority-lhs priority-rhs))))))
-        ('priority-down
-         (setq sortfun
-               (lambda (lhs rhs)
-                 (let ((priority-lhs (org-entry-get lhs "PRIORITY"))
-                       (priority-rhs (org-entry-get rhs "PRIORITY")))
-                   (string-lessp priority-lhs priority-rhs)))))
-        ('effort-up
-         (setq sortfun
-               (lambda (lhs rhs)
-                 (let ((effort-lhs (org-duration-to-minutes (org-entry-get lhs 
"EFFORT")))
-                       (effort-rhs (org-duration-to-minutes (org-entry-get rhs 
"EFFORT"))))
-                   (not (< effort-lhs effort-rhs))))))
-        ('effort-down
-         (setq sortfun
-               (lambda (lhs rhs)
-                 (let ((effort-lhs (org-duration-to-minutes (org-entry-get lhs 
"EFFORT")))
-                       (effort-rhs (org-duration-to-minutes (org-entry-get rhs 
"EFFORT"))))
-                   (< effort-lhs effort-rhs)))))))
-    (when (and targets sortfun)
-      (setq targets (seq-sort sortfun targets)))
-    (when (and targets filterfun)
-      (setq targets (seq-remove filterfun targets)))
-    (when targets
-      (list (seq-elt 0 targets)))))
-
 
 
 ;; Set TODO state
-(defun org-edna-action/todo! (last-entry new-state)
-  (ignore last-entry)
+(defun org-edna-action/todo! (_last-entry new-state)
+  "Action to set a target heading's TODO state to NEW-STATE.
+
+Edna Syntax: todo!(NEW-STATE)
+Edna Syntax: todo!(\"NEW-STATE\")
+
+NEW-STATE may either be a symbol or a string.  If it is a symbol,
+the symbol name is used for the new state.  Otherwise, it is a
+string for the new state, or \"\" to remove the state."
   (org-todo (if (stringp new-state) new-state (symbol-name new-state))))
 
 ;; Set planning info
 
 (defun org-edna--mod-timestamp (time-stamp n what)
+  "Modify the timestamp TIME-STAMP by N WHATs.
+
+N is an integer.  WHAT can be `day', `month', `year', `minute',
+`second'."
   (with-temp-buffer
     (insert time-stamp)
     (goto-char (point-min))
@@ -589,15 +902,272 @@ one is specified, the last will be used.
     (buffer-string)))
 
 (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")))
 
-(defun org-edna--handle-planning (type last-entry args)
-  ;; Need case-fold-search enabled so org-read-date-get-relative will 
recognize "M"
+;; Silence the byte-compiler
+(defvar parse-time-weekdays)
+(defvar parse-time-months)
+
+(defun org-edna--read-date-get-relative (s today default)
+  "Like `org-read-date-get-relative' but with a few additions.
+
+S is a string with the form [+|-|++|--][N]THING.
+
+THING may be any of the following:
+
+- A weekday (WEEKDAY), in which case the number of days from
+  either TODAY or DEFAULT to the next WEEKDAY will be computed.
+  If N is given, jump forward that many occurrences of WEEKDAY
+
+- The string \"weekday\" or \"wkdy\", in which jump forward X
+  days to land on a weekday.  If a weekend is found instead, move
+  in the direction given (+/-) until a weekday is found.
+
+S may also end with [+|-][DAY].  DAY may be either a weekday
+string, such as Monday, Tue, or Friday, or the strings
+\"weekday\", \"wkdy\", \"weekend\", or \"wknd\".  The former
+indicates that the time should land on the given day of the week,
+while the latter group indicates that the time should land on
+that type, either a weekday or a weekend.  The [+|-] in this
+string indicates that the time should be incremented or
+decremented to find the target day.
+
+Return shift list (N what def-flag) to get to the desired date
+WHAT       is \"M\", \"h\", \"d\", \"w\", \"m\", or \"y\" for minute, hour, 
day, week, month, year.
+N          is the number of WHATs to shift.
+DEF-FLAG   is t when a double ++ or -- indicates shift relative to
+           the DEFAULT date rather than TODAY.
+
+Examples:
+
+\"+1d +wkdy\" finds the number of days to move ahead in order to
+find a weekday.  This is the same as \"+1wkdy\", and returns (N \"d\" nil).
+
+\"+5d -wkdy\" means move forward 5 days, then backward until a
+weekday is found.  Returns (N \"d\" nil).
+
+\"+1m +wknd\" means move forward one month, then forward until a
+weekend is found.  Returns (N \"d\" nil), since day precision is
+required."
+  (require 'parse-time)
+  (let* ((case-fold-search t) ;; ignore case when matching, so we get any
+         ;; capitalization of weekday names
+         (weekdays (mapcar 'car parse-time-weekdays))
+         ;; type-strings maps the type of thing to the index in decoded time
+         ;; (see `decode-time')
+         (type-strings '(("M" . 1)
+                         ("h" . 2)
+                         ("d" . 3)
+                         ("w" . 3)
+                         ("m" . 4)
+                         ("y" . 5)))
+         (regexp (rx-to-string
+                  `(and string-start
+                        (submatch (repeat 0 2 (in ?+ ?-)))
+                        (submatch (zero-or-more digit))
+                        (submatch (or (any ,@(mapcar 'car type-strings))
+                                      "weekday" "wkdy"
+                                      ,@weekdays)
+                                  word-end)
+                        (zero-or-one
+                         (submatch (and (one-or-more " ")
+                                        (submatch (zero-or-one (in ?+ ?-)))
+                                        (submatch (or "weekday" "wkdy"
+                                                      "weekend" "wknd"
+                                                      ,@weekdays)
+                                                  word-end))))
+                        string-end))))
+    (when (string-match regexp s)
+      (let* ((dir (if (> (match-end 1) (match-beginning 1))
+                     (string-to-char (substring (match-string 1 s) -1))
+                   ?+))
+            (rel (and (match-end 1) (= 2 (- (match-end 1) (match-beginning 
1)))))
+            (n (if (match-end 2) (string-to-number (match-string 2 s)) 1))
+            (what (if (match-end 3) (match-string 3 s) "d"))
+            (wday1 (cdr (assoc (downcase what) parse-time-weekdays)))
+            (date (if rel default today))
+            (wday (nth 6 (decode-time date)))
+             ;; Are we worrying about where we land?
+             (have-landing (match-end 4))
+             (landing-direction (string-to-char
+                                 (if (and have-landing (match-end 5))
+                                     (match-string 5 s)
+                                   "+")))
+             (landing-type (when have-landing (match-string 6 s)))
+            delta ret)
+        (setq
+         ret
+         (pcase what
+           ;; Shorthand for +Nd +wkdy or -Nd -wkdy
+           ((or "weekday" "wkdy")
+            ;; Determine where we land after N days
+            (let* ((del (* n (if (= dir ?-) -1 1)))
+                   (end-day (mod (+ del wday) 7)))
+              (while (member end-day calendar-weekend-days)
+                (let ((d (if (= dir ?-) -1 1)))
+                  (cl-incf del d)
+                  (setq end-day (mod (+ end-day d) 7))))
+              (list del "d" rel)))
+           ((pred (lambda (arg) (member arg (mapcar 'car type-strings))))
+            (list (* n (if (= dir ?-) -1 1)) what rel))
+           ((pred (lambda (arg) (member arg weekdays)))
+            (setq delta (mod (+ 7 (- wday1 wday)) 7))
+           (when (= delta 0) (setq delta 7))
+           (when (= dir ?-)
+             (setq delta (- delta 7))
+             (when (= delta 0) (setq delta -7)))
+           (when (> n 1) (setq delta (+ delta (* (1- n) (if (= dir ?-) -7 
7)))))
+           (list delta "d" rel))))
+         (if (or (not have-landing)
+                 (member what '("M" "h"))) ;; Don't change landing for minutes 
or hours
+            ret ;; Don't worry about landing, just return
+          (pcase-let* ((`(,del ,what _) ret)
+                       (mod-index (cdr (assoc what type-strings)))
+                       ;; Increment the appropriate entry in the original 
decoded time
+                       (raw-landing-time
+                        (let ((tmp (copy-sequence (decode-time date))))
+                          (cl-incf (seq-elt tmp mod-index)
+                                   ;; We increment the days by 7 when we have 
weeks
+                                   (if (string-equal what "w") (* 7 del) del))
+                          tmp))
+                       (encoded-landing-time (apply 'encode-time 
raw-landing-time))
+                       ;; Get the initial time difference in days, rounding 
down
+                       ;; (it should be something like 3.0, so it won't matter)
+                       (time-diff (truncate
+                                   (/ (float-time (time-subtract 
encoded-landing-time
+                                                                 date))
+                                      86400))) ;; seconds in a day
+                       ;; Decoded landing time
+                       (landing-time (decode-time encoded-landing-time))
+                       ;; Numeric Landing direction
+                       (l-dir (if (= landing-direction ?-) -1 1))
+                       ;; Current numeric day of the week on which we end
+                       (end-day (nth 6 landing-time))
+                       ;; Numeric days of the week on which we are allowed to 
land
+                       (allowed-targets
+                        (pcase landing-type
+                          ((or "weekday" "wkdy")
+                           (seq-difference (number-sequence 0 6) 
calendar-weekend-days))
+                          ((or "weekend" "wknd")
+                           calendar-weekend-days)
+                          ((pred (lambda (arg) (member arg weekdays)))
+                           (list (cdr (assoc (downcase landing-type) 
parse-time-weekdays)))))))
+            ;; While we aren't looking at a valid day, move one day in the 
l-dir
+            ;; direction.
+            (while (not (member end-day allowed-targets))
+              (cl-incf time-diff l-dir)
+              (setq end-day (mod (+ end-day l-dir) 7)))
+            (list time-diff "d" rel)))))))
+
+(defun org-edna--float-time (arg this-time default)
+  "Read a float time string from ARG.
+
+A float time argument string is as follows:
+
+float [+|-|++|--]?N DAYNAME[ MONTH[ DAY]]
+
+N is an integer
+DAYNAME is either an integer day of the week, or a weekday string
+
+MONTH may be a month string or an integer.  Use 0 for the
+following or previous month.
+
+DAY is an optional integer.  If not given, it will be 1 (for
+forward) or the last day of MONTH (backward)."
+  (require 'parse-time)
   (let* ((case-fold-search t)
-         (arg (nth 0 args))
+         (weekdays (mapcar 'car parse-time-weekdays))
+         (month-names (mapcar 'car parse-time-months))
+         (regexp (rx-to-string
+                  `(and string-start
+                        "float "
+                        ;; First argument, N
+                        (submatch (repeat 0 2 (in ?+ ?-)))
+                        (submatch word-start (one-or-more digit) word-end)
+                        " "
+                        ;; Second argument, weekday digit or string
+                        (submatch word-start
+                                  (or (in (?0 . ?6)) ;; Weekday digit
+                                      ,@weekdays)
+                                  word-end)
+                        ;; Third argument, month digit or string
+                        (zero-or-one
+                         " " (submatch word-start
+                                       (or (repeat 1 2 digit)
+                                           ,@month-names)
+                                       word-end)
+                         ;; Fourth argument, day in month
+                         (zero-or-one
+                          " "
+                          (submatch word-start
+                                    (repeat 1 2 digit)
+                                    word-end)))))))
+    (when (string-match regexp arg)
+      (pcase-let* ((inc (match-string 1 arg))
+                   (dir (if (not (string-empty-p inc)) ;; non-empty string
+                           (string-to-char (substring inc -1))
+                         ?+))
+                  (rel (= (length inc) 2))
+                   (numeric-dir (if (= dir ?+) 1 -1))
+                   (nth (* (string-to-number (match-string 2 arg)) 
numeric-dir))
+                   (dayname (let* ((tmp (match-string 3 arg))
+                                   (day (cdr (assoc (downcase tmp) 
parse-time-weekdays))))
+                              (or day (string-to-number tmp))))
+                   (month (if-let* ((tmp (match-string 4 arg)))
+                              (or (cdr (assoc (downcase tmp) 
parse-time-months))
+                                  (string-to-number tmp))
+                            0))
+                   (day (if (match-end 5) (string-to-number (match-string 5 
arg)) 0))
+                   (ts (if rel default this-time))
+                   (`(_ _ _ ,dec-day ,dec-month ,dec-year _ _ _) (decode-time 
ts))
+                   ;; If month isn't given, use the 1st of the following (or 
previous) month
+                   ;; If month is given, use the 1st (or day, if given) of that
+                   ;; following month
+                   (month-given (not (= month 0)))
+                   ;; If day isn't provided, pass nil to
+                   ;; `calendar-nth-named-absday' so it can handle it.
+                   (act-day (if (not (= day 0)) day nil))
+                   (`(,act-month ,act-year)
+                    (if (not month-given)
+                        ;; Month wasn't given, so start at the following or 
previous month.
+                        (list (+ dec-month (if (= dir ?+) 1 -1)) dec-year)
+                      ;; Month was given, so adjust the year accordingly
+                      (cond
+                       ;; If month is after dec-month and we're incrementing,
+                       ;; keep year
+                       ((and (> month dec-month) (= dir ?+))
+                        (list month dec-year))
+                       ;; If month is before or the same as dec-month, and 
we're
+                       ;; incrementing, increment year.
+                       ((and (<= month dec-month) (= dir ?+))
+                        (list month (1+ dec-year)))
+                       ;; We're moving backwards, but month is after, so
+                       ;; decrement year.
+                       ((and (>= month dec-month) (= dir ?-))
+                        (list month (1- dec-year)))
+                       ;; We're moving backwards, and month is backward, so
+                       ;; leave it.
+                       ((and (< month dec-month) (= dir ?-))
+                        (list month dec-year)))))
+                   (abs-days-now (calendar-absolute-from-gregorian `(,dec-month
+                                                                     ,dec-day
+                                                                     
,dec-year)))
+                   (abs-days-then (calendar-nth-named-absday nth dayname
+                                                             act-month
+                                                             act-year
+                                                             act-day)))
+        ;; Return the same arguments as `org-edna--read-date-get-relative' 
above.
+        (list (- abs-days-then abs-days-now) "d" rel)))))
+
+(defun org-edna--handle-planning (type last-entry args)
+  "Handle planning of type TYPE."
+  (let* ((arg (nth 0 args))
          (last-ts (org-with-point-at last-entry (org-edna--get-planning-info 
type)))
          (this-ts (org-edna--get-planning-info type))
-         (this-time (and this-ts (org-parse-time-string this-ts)))
+         (this-time (and this-ts (org-time-string-to-time this-ts)))
          (current (org-current-time))
          (current-ts (format-time-string (org-time-stamp-format t) current))
          (type-map '(("y" . year)
@@ -613,11 +1183,16 @@ one is specified, the last will be used.
         (error "Tried to copy but last entry doesn't have a timestamp"))
       ;; Copy old time verbatim
       (org-add-planning-info type last-ts))
+     ((string-match-p "\\`float " arg)
+      (pcase-let* ((`(,n ,what-string ,def) (org-edna--float-time arg 
this-time current))
+                   (ts (if def current-ts this-ts))
+                   (what (cdr (assoc-string what-string type-map))))
+        (org--deadline-or-schedule nil type (org-edna--mod-timestamp ts n 
what))))
      ((string-match-p "\\`[+-]" arg)
       ;; Starts with a + or -, so assume we're incrementing a timestamp
       ;; We support hours and minutes, so this must be supported separately,
       ;; since org-read-date-analyze doesn't
-      (pcase-let* ((`(,n ,what-string ,def) (org-read-date-get-relative arg 
this-time current))
+      (pcase-let* ((`(,n ,what-string ,def) (org-edna--read-date-get-relative 
arg this-time current))
                    (ts (if def current-ts this-ts))
                    (what (cdr (assoc-string what-string type-map))))
         (org--deadline-or-schedule nil type (org-edna--mod-timestamp ts n 
what))))
@@ -635,52 +1210,190 @@ one is specified, the last will be used.
         (org--deadline-or-schedule nil type new-ts))))))
 
 (defun org-edna-action/scheduled! (last-entry &rest args)
+  "Action to set the scheduled time of a target heading based on ARGS.
+
+Edna Syntax: scheduled!(\"DATE[ TIME]\")                                [1]
+Edna Syntax: scheduled!(rm|remove)                                      [2]
+Edna Syntax: scheduled!(cp|copy)                                        [3]
+Edna Syntax: scheduled!(\"[+|-|++|--]NTHING[ [+|-]LANDING]\")           [4]
+Edna Syntax: scheduled!(\"float [+|-|++|--]?N DAYNAME [ DAY[ MONTH]]\") [5]
+
+In form 1, schedule the target for the given date and time.  If
+DATE is a weekday instead of a date, schedule the target for the
+following weekday.  If it is a date, schedule it for that date
+exactly.  TIME is a time string, such as HH:MM.  If it isn't
+specified, only a date will be applied to the target.  Any string
+recognized by `org-read-date' may be used.
+
+Form 2 will remove the scheduled time from the target.
+
+Form 3 will copy the scheduled time from LAST-ENTRY (the current
+heading) to the target.
+
+Form 4 increments(+) or decrements(-) the target's scheduled time
+by N THINGS relative to either itself (+/-) or the current
+time (++/--).  THING is one of y (years), m (months), d (days),
+h (hours), or M (minutes), and N is an integer.
+
+Form 4 may also include a \"landing\" specification.  This is
+either (a) a day of the week (\"Sun\", \"friday\", etc.), (b)
+\"weekday\" or \"wkdy\", or (c) \"weekend\" or \"wknd\".
+
+If (a), then the target date will be adjusted forward (+) or
+backward (-) to find the closest target day of the week.
+Form (b) will adjust the target time to find a weekday, and (c)
+does the same, but for weekends.
+
+Form 5 handles \"float\" time, named for `diary-float'.  This
+form will set the target's scheduled time to the date of the Nth
+DAYNAME after/before MONTH DAY.  MONTH may be a month string or
+an integer.  Use 0 or leave blank for the following or previous
+month.  DAY is an optional integer.  If not given, it will be
+1 (for forward) or the last day of MONTH (backward)."
   (org-edna--handle-planning 'scheduled last-entry args))
 
 (defun org-edna-action/deadline! (last-entry &rest args)
+  "Action to set the deadline time of a target heading based on ARGS.
+
+Edna Syntax: deadline!(\"DATE[ TIME]\")                                [1]
+Edna Syntax: deadline!(rm|remove)                                      [2]
+Edna Syntax: deadline!(cp|copy)                                        [3]
+Edna Syntax: deadline!(\"[+|-|++|--]NTHING[ [+|-]LANDING]\")           [4]
+Edna Syntax: deadline!(\"float [+|-|++|--]?N DAYNAME [ DAY[ MONTH]]\") [5]
+
+In form 1, set the deadline the target for the given date and
+time.  If DATE is a weekday instead of a date, set the deadline
+the target for the following weekday.  If it is a date, set the
+deadline it for that date exactly.  TIME is a time string, such
+as HH:MM.  If it isn't specified, only a date will be applied to
+the target.  Any string recognized by `org-read-date' may be
+used.
+
+Form 2 will remove the deadline time from the target.
+
+Form 3 will copy the deadline time from LAST-ENTRY (the current
+heading) to the target.
+
+Form 4 increments(+) or decrements(-) the target's deadline time
+by N THINGS relative to either itself (+/-) or the current
+time (++/--).  THING is one of y (years), m (months), d (days),
+h (hours), or M (minutes), and N is an integer.
+
+Form 4 may also include a \"landing\" specification.  This is
+either (a) a day of the week (\"Sun\", \"friday\", etc.), (b)
+\"weekday\" or \"wkdy\", or (c) \"weekend\" or \"wknd\".
+
+If (a), then the target date will be adjusted forward (+) or
+backward (-) to find the closest target day of the week.
+Form (b) will adjust the target time to find a weekday, and (c)
+does the same, but for weekends.
+
+Form 5 handles \"float\" time, named for `diary-float'.  This
+form will set the target's scheduled time to the date of the Nth
+DAYNAME after/before MONTH DAY.  MONTH may be a month string or
+an integer.  Use 0 or leave blank for the following or previous
+month.  DAY is an optional integer.  If not given, it will be
+1 (for forward) or the last day of MONTH (backward)."
   (org-edna--handle-planning 'deadline last-entry args))
 
-(defun org-edna-action/tag! (last-entry tags)
-  (ignore last-entry)
+(defun org-edna-action/tag! (_last-entry tags)
+  "Action to set the tags of a target heading to TAGS.
+
+Edna Syntax: tag!(\"TAGS\")
+
+TAGS is a valid tag specification, such as \":aa:bb:cc:\"."
   (org-set-tags-to tags))
 
-(defun org-edna-action/set-property! (last-entry property value)
-  (ignore last-entry)
+(defun org-edna-action/set-property! (_last-entry property value)
+  "Action to set the property PROPERTY of a target heading to VALUE.
+
+Edna Syntax: set-property!(\"PROPERTY\" \"VALUE\")
+
+PROPERTY and VALUE are both strings.  PROPERTY must be a valid
+org mode property."
   (org-entry-put nil property value))
 
-(defun org-edna-action/delete-property! (last-entry property)
-  (ignore last-entry)
+(defun org-edna-action/delete-property! (_last-entry property)
+  "Action to delete a property from a target heading.
+
+Edna Syntax: delete-property!(\"PROPERTY\")
+
+PROPERTY must be a valid org mode property."
   (org-entry-delete nil property))
 
-(defun org-edna-action/clock-in! (last-entry)
-  (ignore last-entry)
+(defun org-edna-action/clock-in! (_last-entry)
+  "Action to clock into a target heading.
+
+Edna Syntax: clock-in!"
   (org-clock-in))
 
-(defun org-edna-action/clock-out! (last-entry)
-  (ignore last-entry)
+(defun org-edna-action/clock-out! (_last-entry)
+  "Action to clock out from the current clocked heading.
+
+Edna Syntax: clock-out!
+
+Note that this will not necessarily clock out of the target, but
+the actual running clock."
   (org-clock-out))
 
-(defun org-edna-action/set-priority! (last-entry priority-action)
-  "PRIORITY-ACTION is passed straight to `org-priority'."
-  (ignore last-entry)
+(defun org-edna-action/set-priority! (_last-entry priority-action)
+  "Action to set the priority of a target heading.
+
+Edna Syntax: set-priority!(\"PRIORITY-STRING\") [1]
+Edna Syntax: set-priority!(up)                  [2]
+Edna Syntax: set-priority!(down)                [3]
+Edna Syntax: set-priority!(P)                   [4]
+
+Form 1 sets the priority to PRIORITY-STRING, so PRIORITY-STRING
+must be a valid priority string, such as \"A\" or \"E\".  It may
+also be the string \" \", which removes the priority from the
+target.
+
+Form 2 cycles the target's priority up through the list of
+allowed priorities.
+
+Form 3 cycles the target's priority down through the list of
+allowed priorities.
+
+Form 4: Set the target's priority to the character P."
   (org-priority (if (stringp priority-action)
                     (string-to-char priority-action)
                   priority-action)))
 
-(defun org-edna-action/set-effort! (last-entry value)
-  (ignore last-entry)
+(defun org-edna-action/set-effort! (_last-entry value)
+  "Action to set the effort of a target heading.
+
+Edna Syntax: set-effort!(VALUE)     [1]
+Edna Syntax: set-effort!(increment) [2]
+
+For form 1, set the effort based on VALUE.  If VALUE is a string,
+it's converted to an integer.  Otherwise, the integer is used as
+the raw value for the effort.
+
+For form 2, increment the effort to the next allowed value."
   (if (eq value 'increment)
       (org-set-effort nil value)
     (org-set-effort value nil)))
 
-(defun org-edna-action/archive! (last-entry)
-  (ignore last-entry)
+(defun org-edna-action/archive! (_last-entry)
+  "Action to archive a target heading.
+
+Edna Syntax: archive!
+
+If `org-edna-prompt-for-archive', prompt before archiving the
+entry."
   (if org-edna-prompt-for-archive
       (org-archive-subtree-default-with-confirmation)
     (org-archive-subtree-default)))
 
 (defun org-edna-action/chain! (last-entry property)
-  (when-let ((old-prop (org-entry-get last-entry property)))
+  "Action to copy a property to a target heading.
+
+Edna Syntax: chain!(\"PROPERTY\")
+
+Copy PROPERTY from the source heading to the target heading.
+Does nothing if the source heading has no property PROPERTY."
+  (when-let* ((old-prop (org-entry-get last-entry property)))
     (org-entry-put nil property old-prop)))
 
 
@@ -698,34 +1411,90 @@ one is specified, the last will be used.
 ;; This means that we want to take the exclusive-or of condition and neg.
 
 (defun org-edna-condition/done? (neg)
-  (when-let ((condition
-              (if neg
-                  (member (org-entry-get nil "TODO") org-not-done-keywords)
-                (member (org-entry-get nil "TODO") org-done-keywords))))
+  "Condition to check if all target headings are in the DONE state.
+
+Edna Syntax: done?
+
+DONE state is determined by the local value of
+`org-done-keywords'.
+
+Example:
+
+* TODO Heading
+  :PROPERTIES:
+  :BLOCKER: match(\"target\") done?
+  :END:
+
+Here, \"Heading\" will block if all targets tagged \"target\" are
+in a DONE state.
+
+* TODO Heading 2
+  :PROPERTIES:
+  :BLOCKER: match(\"target\") !done?
+  :END:
+
+Here, \"Heading 2\" will block if all targets tagged \"target\"
+are not in a DONE state."
+  (when-let* ((condition
+               (if neg
+                   (member (org-entry-get nil "TODO") org-not-done-keywords)
+                 (member (org-entry-get nil "TODO") org-done-keywords))))
     (org-get-heading)))
 
 (defun org-edna-condition/todo-state? (neg state)
+  "Condition to check if all target headings have the TODO state STATE.
+
+Edna Syntax: todo-state?(\"STATE\")
+
+Block the source heading if all target headings have TODO state
+STATE.  STATE must be a valid TODO state string."
   (let ((condition (string-equal (org-entry-get nil "TODO") state)))
     (when (org-xor condition neg)
       (org-get-heading))))
 
 ;; Block if there are headings
 (defun org-edna-condition/headings? (neg)
+  "Condition to check if a target has headings in its file.
+
+Edna Syntax: headings?
+
+Block the source heading if any headings can be found in its
+file.  This means that target does not have to be a heading."
   (let ((condition (not (seq-empty-p (org-map-entries (lambda nil t))))))
     (when (org-xor condition neg)
       (buffer-name))))
 
 (defun org-edna-condition/variable-set? (neg var val)
+  "Condition to check if a variable is set in a target.
+
+Edna Syntax: variable-set?(VAR VAL)
+
+Evaluate VAR when visiting a target, and compare it with `equal'
+against VAL.  Block the source heading if VAR = VAL.
+
+Target does not have to be a heading."
   (let ((condition (equal (symbol-value var) val)))
     (when (org-xor condition neg)
       (format "%s %s= %s" var (if neg "!" "=") val))))
 
 (defun org-edna-condition/has-property? (neg prop val)
+  "Condition to check if a target heading has property PROP = VAL.
+
+Edna Syntax: has-property?(\"PROP\" \"VAL\")
+
+Block if the target heading has the property PROP set to VAL,
+both of which must be strings."
   (let ((condition (string-equal (org-entry-get nil prop) val)))
     (when (org-xor condition neg)
       (org-get-heading))))
 
 (defun org-edna-condition/re-search? (neg match)
+  "Condition to check for a regular expression in a target's file.
+
+Edna Syntax: re-search?(\"MATCH\")
+
+Block if regular expression MATCH can be found in target's file,
+starting from target's position."
   (let ((condition (re-search-forward match nil t)))
     (when (org-xor condition neg)
       (format "%s %s in %s" (if neg "Did Not Find" "Found") match 
(buffer-name)))))
@@ -733,6 +1502,15 @@ one is specified, the last will be used.
 
 
 (defun org-edna-handle-consideration (consideration blocks)
+  "Handle consideration CONSIDERATION.
+
+Edna Syntax: consideration(all) [1]
+Edna Syntax: consideration(N)   [2]
+Edna Syntax: consideration(P)   [3]
+
+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."
   (let ((first-block (seq-find #'identity blocks))
         (total-blocks (seq-length blocks)))
     (pcase consideration
@@ -754,6 +1532,210 @@ one is specified, the last will be used.
 
 
 
+;;; Popout editing
+
+(defvar org-edna-edit-original-marker nil)
+(defvar org-edna-blocker-section-marker nil)
+(defvar org-edna-trigger-section-marker nil)
+
+(defcustom org-edna-edit-buffer-name "*Org Edna Edit Blocker/Trigger*"
+  "Name of the popout buffer for editing blockers/triggers."
+  :type 'string
+  :group 'org-edna)
+
+(defun org-edna-in-edit-buffer-p ()
+  (string-equal (buffer-name) org-edna-edit-buffer-name))
+
+(defun org-edna-replace-newlines (string)
+  "Replace newlines with spaces in STRING."
+  (string-join (split-string string "\n" t) " "))
+
+(defun org-edna-edit-text-between-markers (first-marker second-marker)
+  "Collect the text between FIRST-MARKER and SECOND-MARKER."
+  (buffer-substring (marker-position first-marker)
+                    (marker-position second-marker)))
+
+(defun org-edna-edit-blocker-section-text ()
+  (when (org-edna-in-edit-buffer-p)
+    (let ((original-text (org-edna-edit-text-between-markers
+                          org-edna-blocker-section-marker
+                          org-edna-trigger-section-marker)))
+      ;; Strip the BLOCKER key
+      (when (string-match "^BLOCKER\n\\(\\(?:.*\n\\)+\\)" original-text)
+        (org-edna-replace-newlines (match-string 1 original-text))))))
+
+(defun org-edna-edit-trigger-section-text ()
+  (when (org-edna-in-edit-buffer-p)
+    (let ((original-text (org-edna-edit-text-between-markers
+                          org-edna-trigger-section-marker
+                          (point-max-marker))))
+      ;; Strip the TRIGGER key
+      (when (string-match "^TRIGGER\n\\(\\(?:.*\n\\)+\\)" original-text)
+        (org-edna-replace-newlines (match-string 1 original-text))))))
+
+(defvar org-edna-edit-map
+  (let ((map (make-sparse-keymap)))
+    (org-defkey map "\C-x\C-s"      'org-edna-edit-finish)
+    (org-defkey map "\C-c\C-s"      'org-edna-edit-finish)
+    (org-defkey map "\C-c\C-c"      'org-edna-edit-finish)
+    (org-defkey map "\C-c'"         'org-edna-edit-finish)
+    (org-defkey map "\C-c\C-q"      'org-edna-edit-abort)
+    (org-defkey map "\C-c\C-k"      'org-edna-edit-abort)
+    map))
+
+(defun org-edna-edit ()
+  "Edit the blockers and triggers for current heading in a separate buffer."
+  (interactive)
+  ;; Move to the start of the current heading
+  (let* ((heading-point (save-excursion
+                          (org-back-to-heading)
+                          (point-marker)))
+         (blocker (or (org-entry-get heading-point "BLOCKER") ""))
+         (trigger (or (org-entry-get heading-point "TRIGGER") ""))
+         (wc (current-window-configuration))
+         (sel-win (selected-window)))
+    (org-switch-to-buffer-other-window org-edna-edit-buffer-name)
+    (erase-buffer)
+    ;; Keep global-font-lock-mode from turning on font-lock-mode
+    (let ((font-lock-global-modes '(not fundamental-mode)))
+      (fundamental-mode))
+    (use-local-map org-edna-edit-map)
+    (setq-local font-lock-global-modes (list 'not major-mode))
+    (setq-local org-edna-edit-original-marker heading-point)
+    (setq-local org-window-configuration wc)
+    (setq-local org-selected-window sel-win)
+    (setq-local org-finish-function 'org-edna-edit-finish)
+    (insert "Edit blockers and triggers in this buffer under their respective 
sections below.
+All lines under a given section will be merged into one when saving back to
+the source buffer.  Finish with `C-c C-c' or abort with `C-c C-k'\n\n")
+    (setq-local org-edna-blocker-section-marker (point-marker))
+    (insert (format "BLOCKER\n%s\n\n" blocker))
+    (setq-local org-edna-trigger-section-marker (point-marker))
+    (insert (format "TRIGGER\n%s\n\n" trigger))
+
+    ;; Change syntax table to make ! and ? symbol constituents
+    (modify-syntax-entry ?! "_")
+    (modify-syntax-entry ?? "_")
+
+    ;; Set up completion
+    (add-hook 'completion-at-point-functions 'org-edna-completion-at-point nil 
t)))
+
+(defun org-edna-edit-finish ()
+  (interactive)
+  (let ((blocker (org-edna-edit-blocker-section-text))
+        (trigger (org-edna-edit-trigger-section-text))
+        (pos-marker org-edna-edit-original-marker)
+        (wc org-window-configuration)
+        (sel-win org-selected-window))
+    (set-window-configuration wc)
+    (select-window sel-win)
+    (goto-char pos-marker)
+    (unless (string-empty-p blocker)
+      (org-entry-put nil "BLOCKER" blocker))
+    (unless (string-empty-p trigger)
+      (org-entry-put nil "TRIGGER" trigger))
+    (kill-buffer org-edna-edit-buffer-name)))
+
+(defun org-edna-edit-abort ()
+  (interactive)
+  (let ((pos-marker org-edna-edit-original-marker)
+        (wc org-window-configuration)
+        (sel-win org-selected-window))
+    (set-window-configuration wc)
+    (select-window sel-win)
+    (goto-char pos-marker)
+    (kill-buffer org-edna-edit-buffer-name)))
+
+;;; Completion
+
+(defun org-edna-between-markers-p (point first-marker second-marker)
+  "Return non-nil if POINT is between FIRST-MARKER and SECOND-MARKER in the 
current buffer."
+  (and (markerp first-marker)
+       (markerp second-marker)
+       (eq (marker-buffer first-marker)
+           (marker-buffer second-marker))
+       (eq (current-buffer) (marker-buffer first-marker))
+       (<= (marker-position first-marker) point)
+       (>= (marker-position second-marker) point)))
+
+(defun org-edna-edit-in-blocker-section-p ()
+  "Return non-nil if `point' is in an edna blocker edit section."
+  (org-edna-between-markers-p (point)
+                              org-edna-blocker-section-marker
+                              org-edna-trigger-section-marker))
+
+(defun org-edna-edit-in-trigger-section-p ()
+  "Return non-nil if `point' is in an edna trigger edit section."
+  (org-edna-between-markers-p (point)
+                              org-edna-trigger-section-marker
+                              (point-max-marker)))
+
+(defun org-edna--collect-keywords (keyword-type &optional suffix)
+  (let* ((suffix (or suffix ""))
+         (edna-sym-list)
+         (edna-rx (rx-to-string `(and
+                                  string-start
+                                  "org-edna-"
+                                  ,keyword-type
+                                  "/"
+                                  (submatch (one-or-more ascii))
+                                  ,suffix
+                                  string-end))))
+    (mapatoms
+     (lambda (s)
+       (when (and (string-match edna-rx (symbol-name s)) (fboundp s))
+         (cl-pushnew (concat (match-string-no-properties 1 (symbol-name s)) 
suffix)
+                     edna-sym-list))))
+    edna-sym-list))
+
+(defun org-edna--collect-finders ()
+  (org-edna--collect-keywords "finder"))
+
+(defun org-edna--collect-actions ()
+  (org-edna--collect-keywords "action" "!"))
+
+(defun org-edna--collect-conditions ()
+  (org-edna--collect-keywords "condition" "?"))
+
+(defun org-edna-completions-for-blocker ()
+  "Return a list of all allowed Edna keywords for a blocker."
+  `(,@(org-edna--collect-finders)
+    ,@(org-edna--collect-conditions)
+    "consideration"))
+
+(defun org-edna-completions-for-trigger ()
+  "Return a list of all allowed Edna keywords for a trigger."
+  `(,@(org-edna--collect-finders)
+    ,@(org-edna--collect-actions)))
+
+(defun org-edna-completion-table-function (string pred action)
+  (let ((completions (cond
+                      ;; Don't offer completion inside of arguments
+                      ((> (syntax-ppss-depth (syntax-ppss)) 0) nil)
+                      ((org-edna-edit-in-blocker-section-p)
+                       (org-edna-completions-for-blocker))
+                      ((org-edna-edit-in-trigger-section-p)
+                       (org-edna-completions-for-trigger)))))
+    (pcase action
+      (`nil
+       (try-completion string completions pred))
+      (`t
+       (all-completions string completions pred))
+      (`lambda
+        (test-completion string completions pred))
+      (`(boundaries . _) nil)
+      (`metadata
+       `(metadata . ((category . org-edna)
+                     (annotation-function . nil)
+                     (display-sort-function . identity)
+                     (cycle-sort-function . identity)))))))
+
+(defun org-edna-completion-at-point ()
+  (when-let* ((bounds (bounds-of-thing-at-point 'symbol)))
+    (list (car bounds) (cdr bounds) 'org-edna-completion-table-function)))
+
+
+
 (declare-function lm-report-bug "lisp-mnt" (topic))
 
 (defun org-edna-submit-bug-report (topic)
diff --git a/packages/org-edna/org-edna.info b/packages/org-edna/org-edna.info
new file mode 100644
index 0000000..00bfa6e
--- /dev/null
+++ b/packages/org-edna/org-edna.info
@@ -0,0 +1,1411 @@
+This is org-edna.info, produced by makeinfo version 6.5 from
+org-edna.texi.
+
+INFO-DIR-SECTION Emacs
+START-INFO-DIR-ENTRY
+* Org Edna: (org-edna). Extensible Dependencies ’N’ Actions for Org Mode tasks.
+END-INFO-DIR-ENTRY
+
+
+File: org-edna.info,  Node: Top,  Next: Copying,  Up: (dir)
+
+Org Edna
+********
+
+* Menu:
+
+* Copying::
+* Introduction::                 A Brief Introduction to Edna
+* Basic Features::               Finders and Actions
+* Advanced Features::
+* Extending Edna::               What else can it do?
+* Contributing::                 I wanna help!
+
+— The Detailed Node Listing —
+
+Introduction
+
+* Installation and Setup::       How to install Edna
+* Basic Operation::              How to use Edna
+* Blockers::                     Blocking a TODO Item
+* Triggers::                     Triggering actions after completing a task
+* Syntax::                       Basic explanation of Edna’s syntax
+
+Basic Features
+
+* Finders::                      How to find targets
+* Actions::                      Next steps
+
+Finders
+
+* ancestors::                    Find a list of ancestors
+* children::                     Find all immediate children
+* descendants::                  Find all descendants
+* file::                         Find a file by name
+* first-child::                  Find the first child of a heading
+* ids::                          Find a list of headings with given IDs
+* match::                        Good old tag matching
+* next-sibling::                 Find the next sibling
+* next-sibling-wrap::            Find the next sibling, wrapping around
+* olp::                          Find a heading by its outline path
+* org-file::                     Find a file in org-directory
+* parent::                       Find a parent
+* previous-sibling::             Find the previous sibling
+* previous-sibling-wrap::        Find the previous sibling, with wrapping
+* relatives::                    Generic relative finder
+* rest-of-siblings::             Find the remaining siblings
+* rest-of-siblings-wrap::        Find the remaining siblings, with wrapping
+* self::
+* siblings::                     Find all the siblings
+* siblings-wrap::                Find the siblings, but wrap around
+
+
+Actions
+
+* Scheduled/Deadline::           Operate on planning information
+* TODO State::                   Set todo state
+* Archive::                      Archive targets
+* Chain Property::               Copy properties from source to targets
+* Clocking::                     Clock in or out of a target
+* Property::                     Set properties of targets
+* Priority::                     Set priorities of targets
+* Tag::                          Tags of a target
+* Effort::                       So much effort!
+
+
+Advanced Features
+
+* Conditions::                   More than just DONE headings
+* Consideration::                Only some of them
+* Setting the properties::       The easy way to set BLOCKER and TRIGGER
+
+Conditions
+
+* done::
+* headings::
+* todo-state::
+* variable-set::
+* has-property::
+* re-search::                    Search for a regular expression
+* Negating Conditions::
+
+
+Extending Edna
+
+* Naming Conventions::           How to name new functions
+* Finders: Finders (1).          Making a new finder
+* Actions: Actions (1).          Making a new action
+* Conditions: Conditions (1).    Making a new condition
+
+Contributing
+
+* Bugs::
+* Development::
+
+
+
+File: org-edna.info,  Node: Copying,  Next: Introduction,  Prev: Top,  Up: Top
+
+Copying
+*******
+
+Copyright (C) 2017 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/>.
+
+
+File: org-edna.info,  Node: Introduction,  Next: Basic Features,  Prev: 
Copying,  Up: Top
+
+Introduction
+************
+
+Extensible Dependencies ’N’ Actions (EDNA) for Org Mode tasks
+
+   Edna provides an extensible means of specifying conditions which must
+be fulfilled before a task can be completed and actions to take once it
+is.
+
+   Org Edna runs when either the BLOCKER or TRIGGER properties are set
+on a heading, and when it is changing from a TODO state to a DONE state.
+
+   For brevity, we use TODO state to indicate any state in
+‘org-not-done-keywords’, and DONE state to indicate any state in
+‘org-done-keywords’.
+
+* Menu:
+
+* Installation and Setup::       How to install Edna
+* Basic Operation::              How to use Edna
+* Blockers::                     Blocking a TODO Item
+* Triggers::                     Triggering actions after completing a task
+* Syntax::                       Basic explanation of Edna’s syntax
+
+
+File: org-edna.info,  Node: Installation and Setup,  Next: Basic Operation,  
Up: Introduction
+
+Installation and Setup
+======================
+
+*Requirements*
+
+Emacs   25.1
+seq     2.19
+org     9.0.5
+
+   There are two ways to install Edna: From GNU ELPA, or from source.
+
+   From ELPA:
+
+     M-x package-install org-edna
+
+   From Source:
+
+     bzr branch https://bzr.savannah.gnu.org/r/org-edna-el/ org-edna
+     make -C org-edna compile autoloads
+
+   After that, add the following to your init file (typically .emacs):
+
+     ;; Only necessary if installing from source
+     (add-to-list 'load-path "/full/path/to/org-edna/")
+     (load "/path/to/org-edna/org-edna-autoloads.el")
+
+     ;; Always necessary
+     (org-edna-load)
+
+   If you ever want to disable Edna, run ‘org-edna-unload’.
+
+
+File: org-edna.info,  Node: Basic Operation,  Next: Blockers,  Prev: 
Installation and Setup,  Up: Introduction
+
+Basic Operation
+===============
+
+Let’s start with an example: Say you want to do laundry, but once you’ve
+put your clothes in the washer, you forget about it.  Even with a tool
+like org-notify or appt, Org won’t know when to remind you.  If you’ve
+got them scheduled for an hour after the other, maybe you forgot one
+time, or ran a little late.  Now Org will remind you too early.
+
+   Edna can handle this for you like so:
+
+     * TODO Put clothes in washer
+       SCHEDULED: <2017-04-08 Sat 09:00>
+       :PROPERTIES:
+       :TRIGGER: next-sibling scheduled!("++1h")
+       :END:
+     * TODO Put clothes in dryer
+       :PROPERTIES:
+       :TRIGGER: next-sibling scheduled!("++1h")
+       :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:
+
+   After you’ve put your clothes in the washer and mark the task DONE,
+Edna will schedule the following task for one hour after you set the
+first heading as done.
+
+   Another example might be a checklist that you’ve done so many times
+that you do part of it on autopilot:
+
+     * TODO Address all TODOs in code
+     * TODO Commit Code to Repository
+
+   The last thing anyone wants is to find out that some part of the code
+on which they’ve been working for days has a surprise waiting for them.
+Once again, Edna can help:
+
+     * TODO Address all TODOs in code
+       :PROPERTIES:
+       :BLOCKER: file("main.cpp") file("code.cpp") re-search?("TODO")
+       :END:
+     * TODO Commit Code to Repository
+
+
+File: org-edna.info,  Node: Blockers,  Next: Triggers,  Prev: Basic Operation, 
 Up: Introduction
+
+Blockers
+========
+
+A blocker indicates conditions which must be met in order for a heading
+to be marked as DONE. Typically, this will be a list of headings that
+must be marked as DONE.
+
+
+File: org-edna.info,  Node: Triggers,  Next: Syntax,  Prev: Blockers,  Up: 
Introduction
+
+Triggers
+========
+
+A trigger is an action to take when a heading is set to done.  For
+example, scheduling another task, marking another task as TODO, or
+renaming a file.
+
+
+File: org-edna.info,  Node: Syntax,  Prev: Triggers,  Up: Introduction
+
+Syntax
+======
+
+Edna has its own language for commands, the basic form of which is
+KEYWORD(ARG1 ARG2 ...)
+
+   KEYWORD can be any valid lisp symbol, such as key-word, KEY_WORD!, or
+keyword?.
+
+   Each argument can be one of the following:
+
+   • A symbol, such as arg or org-mode
+   • A quoted string, such as “hello” or “My name is Edna”
+   • A number, such as 0.5, +1e3, or -5
+   • A UUID, such as c5e30c76-879a-494d-9281-3a4b559c1a3c
+
+   Each argument takes specific datatypes as input, so be sure to read
+the entry before using it.
+
+   The parentheses can be omitted for commands with no arguments.
+
+
+File: org-edna.info,  Node: Basic Features,  Next: Advanced Features,  Prev: 
Introduction,  Up: Top
+
+Basic Features
+**************
+
+The most basic features of Edna are *finders* and *actions*.
+
+* Menu:
+
+* Finders::                      How to find targets
+* Actions::                      Next steps
+
+
+File: org-edna.info,  Node: Finders,  Next: Actions,  Up: Basic Features
+
+Finders
+=======
+
+A finder specifies locations from which to test conditions or perform
+actions.  These locations are referred to as “targets”.  The current
+heading, i.e.  the one that is being blocked or triggered, is referred
+to as the “source” heading.
+
+   More than one finder may be used.  In this case, the targets are
+merged together, removing any duplicates.
+
+   Many finders take additional options, marked “OPTIONS”.  See *note
+relatives:: for information on these options.
+
+* Menu:
+
+* ancestors::                    Find a list of ancestors
+* children::                     Find all immediate children
+* descendants::                  Find all descendants
+* file::                         Find a file by name
+* first-child::                  Find the first child of a heading
+* ids::                          Find a list of headings with given IDs
+* match::                        Good old tag matching
+* next-sibling::                 Find the next sibling
+* next-sibling-wrap::            Find the next sibling, wrapping around
+* olp::                          Find a heading by its outline path
+* org-file::                     Find a file in org-directory
+* parent::                       Find a parent
+* previous-sibling::             Find the previous sibling
+* previous-sibling-wrap::        Find the previous sibling, with wrapping
+* relatives::                    Generic relative finder
+* rest-of-siblings::             Find the remaining siblings
+* rest-of-siblings-wrap::        Find the remaining siblings, with wrapping
+* self::
+* siblings::                     Find all the siblings
+* siblings-wrap::                Find the siblings, but wrap around
+
+
+File: org-edna.info,  Node: ancestors,  Next: children,  Up: Finders
+
+ancestors
+---------
+
+   • Syntax: ancestors(OPTIONS...)
+
+   The ‘ancestors’ finder returns a list of the source heading’s
+ancestors.
+
+   For example:
+
+     * TODO Heading 1
+     ** TODO Heading 2
+     ** TODO Heading 3
+     *** TODO Heading 4
+     **** TODO Heading 5
+          :PROPERTIES:
+          :BLOCKER:  ancestors
+          :END:
+
+   In the above example, “Heading 5” will be blocked until “Heading 1”,
+“Heading 3”, and “Heading 4” are marked “DONE”, while “Heading 2” is
+ignored.
+
+
+File: org-edna.info,  Node: children,  Next: descendants,  Prev: ancestors,  
Up: Finders
+
+children
+--------
+
+   • Syntax: children(OPTIONS...)
+
+   The ‘children’ finder returns a list of the *immediate* children of
+the source heading.  If the source has no children, no target is
+returned.
+
+   In order to get all levels of children of the source heading, use the
+*note descendants:: keyword instead.
+
+
+File: org-edna.info,  Node: descendants,  Next: file,  Prev: children,  Up: 
Finders
+
+descendants
+-----------
+
+   • Syntax: descendants(OPTIONS...)
+
+   The ‘descendants’ finder returns a list of all descendants of the
+source heading.
+
+     * TODO Heading 1
+        :PROPERTIES:
+        :BLOCKER:  descendants
+        :END:
+     ** TODO Heading 2
+     *** TODO Heading 3
+     **** TODO Heading 4
+     ***** TODO Heading 5
+
+   In the above example, “Heading 1” will block until Headings 2, 3, 4,
+and 5 are DONE.
+
+
+File: org-edna.info,  Node: file,  Next: first-child,  Prev: descendants,  Up: 
Finders
+
+file
+----
+
+   • Syntax: file(“FILE”)
+
+   The ‘file’ finder finds a single file, specified as a string.  The
+returned target will be the minimum point in the file.
+
+   Note that this does not give a valid heading, so any conditions or
+actions that require will throw an error.  Consult the documentation for
+individual actions or conditions to determine which ones will and won’t
+work.
+
+   See *note conditions: Conditions. for how to set a different
+condition.  For example:
+
+     * TODO Test
+       :PROPERTIES:
+       :BLOCKER:  file("~/myfile.org") headings?
+       :END:
+
+   Here, “Test” will block until myfile.org is clear of headings.
+
+
+File: org-edna.info,  Node: first-child,  Next: ids,  Prev: file,  Up: Finders
+
+first-child
+-----------
+
+   • Syntax: first-child(OPTIONS...)
+
+   Return the first child of the source heading.  If the source heading
+has no children, no target is returned.
+
+
+File: org-edna.info,  Node: ids,  Next: match,  Prev: first-child,  Up: Finders
+
+ids
+---
+
+   • Syntax: id(ID1 ID2 ...)
+
+   The ‘ids’ finder will search for headings with given IDs, using
+‘org-id’.  Any number of UUIDs may be specified.  For example:
+
+     * TODO Test
+       :PROPERTIES:
+       :BLOCKER:  ids(62209a9a-c63b-45ef-b8a8-12e47a9ceed9 
6dbd7921-a25c-4e20-b035-365677e00f30)
+       :END:
+
+   Here, “Test” will block until the heading with ID
+62209a9a-c63b-45ef-b8a8-12e47a9ceed9 and the heading with ID
+6dbd7921-a25c-4e20-b035-365677e00f30 are set to “DONE”.
+
+   Note that UUIDs need not be quoted; Edna will handle that for you.
+
+
+File: org-edna.info,  Node: match,  Next: next-sibling,  Prev: ids,  Up: 
Finders
+
+match
+-----
+
+   • Syntax: match(“MATCH-STRING” SCOPE SKIP)
+
+   The ‘match’ keyword will take any arguments that ‘org-map-entries’
+usually takes.  In fact, the arguments to ‘match’ are passed straight
+into ‘org-map-entries’.
+
+     * TODO Test
+       :PROPERTIES:
+       :BLOCKER:  match("test&mine" agenda)
+       :END:
+
+   “Test” will block until all entries tagged “test” and “mine” in the
+agenda files are marked DONE.
+
+   See the documentation for ‘org-map-entries’ for a full explanation of
+the first argument.
+
+
+File: org-edna.info,  Node: next-sibling,  Next: next-sibling-wrap,  Prev: 
match,  Up: Finders
+
+next-sibling
+------------
+
+   • Syntax: next-sibling(OPTIONS...)
+
+   The ‘next-sibling’ keyword returns the next sibling of the source
+heading, if any.
+
+
+File: org-edna.info,  Node: next-sibling-wrap,  Next: olp,  Prev: 
next-sibling,  Up: Finders
+
+next-sibling-wrap
+-----------------
+
+   • Syntax: next-sibling-wrap(OPTIONS...)
+
+   Find the next sibling of the source heading, if any.  If there isn’t,
+wrap back around to the first heading in the same subtree.
+
+
+File: org-edna.info,  Node: olp,  Next: org-file,  Prev: next-sibling-wrap,  
Up: Finders
+
+olp
+---
+
+   • Syntax: olp(“FILE” “OLP”)
+
+   Finds the heading given by OLP in FILE. Both arguments are strings.
+
+     * TODO Test
+       :PROPERTIES:
+       :BLOCKER:  olp("test.org" "path/to/heading")
+       :END:
+
+   “Test” will block if the heading “path/to/heading” in “test.org” is
+not DONE.
+
+
+File: org-edna.info,  Node: org-file,  Next: parent,  Prev: olp,  Up: Finders
+
+org-file
+--------
+
+   • Syntax: org-file(“FILE”)
+
+   A special form of ‘file’, ‘org-file’ will find FILE in
+‘org-directory’.
+
+   FILE is the relative path of a file in ‘org-directory’.  Nested files
+are allowed, such as “my-directory/my-file.org”.  The returned target is
+the minimum point of FILE.
+
+     * TODO Test
+       :PROPERTIES:
+       :BLOCKER:  org-file("test.org")
+       :END:
+
+   Note that the file still requires an extension; the “org” here just
+means to look in ‘org-directory’, not necessarily an Org mode file.
+
+
+File: org-edna.info,  Node: parent,  Next: previous-sibling,  Prev: org-file,  
Up: Finders
+
+parent
+------
+
+   • Syntax: parent(OPTIONS...)
+
+   Returns the parent of the source heading, if any.
+
+
+File: org-edna.info,  Node: previous-sibling,  Next: previous-sibling-wrap,  
Prev: parent,  Up: Finders
+
+previous-sibling
+----------------
+
+   • Syntax: previous-sibling(OPTIONS...)
+
+   Returns the previous sibling of the source heading on the same level.
+
+
+File: org-edna.info,  Node: previous-sibling-wrap,  Next: relatives,  Prev: 
previous-sibling,  Up: Finders
+
+previous-sibling-wrap
+---------------------
+
+   • Syntax: previous-sibling-wrap(OPTIONS...)
+
+   Returns the previous sibling of the source heading on the same level.
+
+
+File: org-edna.info,  Node: relatives,  Next: rest-of-siblings,  Prev: 
previous-sibling-wrap,  Up: Finders
+
+relatives
+---------
+
+Find some relative of the current heading.
+
+   • Syntax: relatives(OPTION OPTION...)
+   • Syntax: chain-find(OPTION OPTION...)
+
+   Identical to the chain argument in org-depend, relatives selects its
+single target using the following method:
+
+  1. Creates a list of possible targets
+  2. Filters the targets from Step 1
+  3. Sorts the targets from Step 2
+
+   One option from each of the following three categories may be used;
+if more than one is specified, the last will be used.  Filtering is the
+exception to this; each filter argument adds to the current filter.
+Apart from that, argument order is irrelevant.
+
+   The chain-find finder is also provided for backwards compatibility,
+and for similarity to org-depend.
+
+   All arguments are symbols, unless noted otherwise.
+
+   *Selection*
+
+   • from-top: Select siblings of the current heading, starting at the
+     top
+   • from-bottom: As above, but from the bottom
+   • from-current: Selects siblings, starting from the heading (wraps)
+   • no-wrap: As above, but without wrapping
+   • forward-no-wrap: Find entries on the same level, going forward
+   • forward-wrap: As above, but wrap when the end is reached
+   • backward-no-wrap: Find entries on the same level, going backward
+   • backward-wrap: As above, but wrap when the start is reached
+   • walk-up: Walk up the tree, excluding self
+   • walk-up-with-self: As above, but including self
+   • walk-down: Recursively walk down the tree, excluding self
+   • walk-down-with-self: As above, but including self
+   • step-down: Collect headings from one level down
+
+   *Filtering*
+
+   • todo-only: Select only targets with TODO state set that isn’t a
+     DONE state
+   • todo-and-done-only: Select all targets with a TODO state set
+   • no-comments: Skip commented headings
+   • no-archive: Skip archived headings
+   • NUMBER: Only use that many headings, starting from the first one If
+     passed 0, use all headings If <0, omit that many headings from the
+     end
+   • “+tag”: Only select headings with given tag
+   • “-tag”: Only select headings without tag
+   • “REGEX”: select headings whose titles match REGEX
+
+   *Sorting*
+
+   • no-sort: Remove other sorting in affect
+   • reverse-sort: Reverse other sorts (stacks with other sort methods)
+   • random-sort: Sort in a random order
+   • priority-up: Sort by priority, highest first
+   • priority-down: Same, but lowest first
+   • effort-up: Sort by effort, highest first
+   • effort-down: Sort by effort, lowest first
+   • scheduled-up: Scheduled time, farthest first
+   • scheduled-down: Scheduled time, closest first
+   • deadline-up: Deadline time, farthest first
+   • deadline-down: Deadline time, closest first
+
+   Many of the other finders are shorthand for argument combinations of
+relative:
+
+*note ancestors::
+     walk-up
+*note children::
+     step-down
+*note descendants::
+     walk-down
+*note first-child::
+     step-down 1
+*note next-sibling::
+     forward-no-wrap 1
+*note next-sibling-wrap::
+     forward-wrap 1
+*note parent::
+     walk-up 1
+*note previous-sibling::
+     backward-no-wrap 1
+*note previous-sibling-wrap::
+     backward-wrap 1
+*note rest-of-siblings::
+     forward-no-wrap
+*note rest-of-siblings-wrap::
+     forward-wrap
+*note siblings::
+     from-top
+*note siblings-wrap::
+     forward-wrap
+
+   Because these are implemented as shorthand, any arguments for
+relatives may also be passed to one of these finders.
+
+
+File: org-edna.info,  Node: rest-of-siblings,  Next: rest-of-siblings-wrap,  
Prev: relatives,  Up: Finders
+
+rest-of-siblings
+----------------
+
+   • Syntax: rest-of-siblings(OPTIONS...)
+
+   Starting from the heading following the current one, all same-level
+siblings are returned.
+
+
+File: org-edna.info,  Node: rest-of-siblings-wrap,  Next: self,  Prev: 
rest-of-siblings,  Up: Finders
+
+rest-of-siblings-wrap
+---------------------
+
+   • Syntax: rest-of-siblings-wrap(OPTIONS...)
+
+   Starting from the heading following the current one, all same-level
+siblings are returned.  When the end is reached, wrap back to the
+beginning.
+
+
+File: org-edna.info,  Node: self,  Next: siblings,  Prev: 
rest-of-siblings-wrap,  Up: Finders
+
+self
+----
+
+   • Syntax: self
+
+   Returns the source heading.
+
+
+File: org-edna.info,  Node: siblings,  Next: siblings-wrap,  Prev: self,  Up: 
Finders
+
+siblings
+--------
+
+   • Syntax: siblings(OPTIONS...)
+
+   Returns all siblings of the source heading as targets, starting from
+the first sibling.
+
+
+File: org-edna.info,  Node: siblings-wrap,  Prev: siblings,  Up: Finders
+
+siblings-wrap
+-------------
+
+   • Syntax: siblings-wrap(OPTIONS...)
+
+   Finds the siblings on the same level as the source heading, wrapping
+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
+
+Actions
+=======
+
+Once Edna has collected its targets for a trigger, it will perform
+actions on them.
+
+   Actions must always end with ’!’.
+
+* Menu:
+
+* Scheduled/Deadline::           Operate on planning information
+* TODO State::                   Set todo state
+* Archive::                      Archive targets
+* Chain Property::               Copy properties from source to targets
+* Clocking::                     Clock in or out of a target
+* Property::                     Set properties of targets
+* Priority::                     Set priorities of targets
+* Tag::                          Tags of a target
+* Effort::                       So much effort!
+
+
+File: org-edna.info,  Node: Scheduled/Deadline,  Next: TODO State,  Up: Actions
+
+Scheduled/Deadline
+------------------
+
+   • Syntax: scheduled!(OPTIONS)
+   • Syntax: deadline!(OPTIONS)
+
+   Set the scheduled or deadline time of any target headings.
+
+   There are several forms that the planning keywords can take.  In the
+following, PLANNING is either scheduled or deadline.
+
+   • PLANNING!(“DATE[ TIME]”)
+
+     Sets PLANNING to DATE at TIME. If DATE is a weekday instead of a
+     date, then set PLANNING to the following weekday.  If TIME is not
+     specified, only a date will be added to the target.
+
+     Any string recognized by ‘org-read-date’ may be used for DATE.
+
+     TIME is a time string, such as HH:MM.
+
+   • PLANNING!(rm|remove)
+
+     Remove PLANNING from all targets.  The argument to this form may be
+     either a string or a symbol.
+
+   • PLANNING!(copy|cp)
+
+     Copy PLANNING info verbatim from the source heading to all targets.
+     The argument to this form may be either a string or a symbol.
+
+   • PLANNING!(“[+|-|++|–]NTHING[ [+|-]LANDING]”)
+
+     Increment(+) or decrement(-) target’s PLANNING by N THINGs relative
+     to either itself (+/-) or the current time (++/–).
+
+     N is an integer
+
+     THING is one of y (years), m (months), d (days), h (hours), M
+     (minutes), a (case-insensitive) day of the week or its
+     abbreviation, or the strings “weekday” or “wkdy”.
+
+     If a day of the week is given as THING, move forward or backward N
+     weeks to find that day of the week.
+
+     If one of “weekday” or “wkdy” is given as THING, move forward or
+     backward N days, moving forward or backward to the next weekday.
+
+     This form may also include a “landing” specifier to control where
+     in the week the final date lands.  LANDING may be one of the
+     following:
+
+        • A day of the week, which means adjust the final date forward
+          (+) or backward (-) to land on that day of the week.
+
+        • One of “weekday” or “wkdy”, which means adjust the target date
+          to the closest weekday.
+
+        • One of “weekend” or “wknd”, which means adjust the target date
+          to the closest weekend.
+
+   • PLANNING!(“float [+|-|++|–]N DAYNAME[ MONTH[ DAY]]”)
+
+     Set time to the date of the Nth DAYNAME before/after MONTH DAY, as
+     per ‘diary-float’.
+
+     N is an integer.
+
+     DAYNAME may be either an integer, where 0=Sunday, 1=Monday, etc.,
+     or a string for that day.
+
+     MONTH may be an integer, 1-12, or a month’s string.  If MONTH is
+     empty, the following (+) or previous (-) month relative to the
+     target’s time (+/-) or the current time (++/–).
+
+     DAY is an integer, or empty or 0 to use the first of the month (+)
+     or the last of the month (-).
+
+   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
+
+
+File: org-edna.info,  Node: TODO State,  Next: Archive,  Prev: 
Scheduled/Deadline,  Up: Actions
+
+TODO State
+----------
+
+   • Syntax: todo!(NEW-STATE)
+
+   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.
+
+
+File: org-edna.info,  Node: Archive,  Next: Chain Property,  Prev: TODO State, 
 Up: Actions
+
+Archive
+-------
+
+   • Syntax: archive!
+
+   Archives all targets with confirmation.
+
+   Confirmation is controlled with ‘org-edna-prompt-for-archive’.  If
+this option is nil, Edna will not ask before archiving targets.
+
+
+File: org-edna.info,  Node: Chain Property,  Next: Clocking,  Prev: Archive,  
Up: Actions
+
+Chain Property
+--------------
+
+   • Syntax: chain!(“PROPERTY”)
+
+   Copies PROPERTY from the source entry to all targets.  Does nothing
+if the source heading has no property PROPERTY.
+
+
+File: org-edna.info,  Node: Clocking,  Next: Property,  Prev: Chain Property,  
Up: Actions
+
+Clocking
+--------
+
+   • Syntax: clock-in!
+   • Syntax: clock-out!
+
+   Clocks into or out of all targets.
+
+   ‘clock-in!’ has no special handling of targets, so be careful when
+specifying multiple targets.
+
+   In contrast, ‘clock-out!’ ignores its targets and only clocks out of
+the current clock, if any.
+
+
+File: org-edna.info,  Node: Property,  Next: Priority,  Prev: Clocking,  Up: 
Actions
+
+Property
+--------
+
+   • Syntax: set-property!(“PROPERTY” “VALUE”)
+
+   Sets the property PROPERTY on all targets to VALUE.
+
+   • Syntax: delete-property!(“PROPERTY”)
+
+   Deletes the property PROPERTY from all targets.
+
+
+File: org-edna.info,  Node: Priority,  Next: Tag,  Prev: Property,  Up: Actions
+
+Priority
+--------
+
+Sets the priority of all targets.
+
+   • Syntax: set-priority!(“PRIORITY”)
+
+     Set the priority to the first character of PRIORITY.
+
+   • Syntax: set-priority!(up)
+
+     Cycle the target’s priority up through the list of allowed
+     priorities.
+
+   • Syntax: set-priority!(down)
+
+     Cycle the target’s priority down through the list of allowed
+     priorities.
+
+   • Syntax: set-priority!(P)
+
+     Set the target’s priority to the character P.
+
+
+File: org-edna.info,  Node: Tag,  Next: Effort,  Prev: Priority,  Up: Actions
+
+Tag
+---
+
+   • Syntax: tag!(“TAG-SPEC”)
+
+   Tags all targets with TAG-SPEC, which is any valid tag specification,
+e.g.  tag1:tag2
+
+
+File: org-edna.info,  Node: Effort,  Prev: Tag,  Up: Actions
+
+Effort
+------
+
+   • Syntax: set-effort!(VALUE)
+
+   Sets the effort of all targets according to 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
+
+
+File: org-edna.info,  Node: Advanced Features,  Next: Extending Edna,  Prev: 
Basic Features,  Up: Top
+
+Advanced Features
+*****************
+
+* Menu:
+
+* Conditions::                   More than just DONE headings
+* Consideration::                Only some of them
+* Setting the properties::       The easy way to set BLOCKER and TRIGGER
+
+
+File: org-edna.info,  Node: Conditions,  Next: Consideration,  Up: Advanced 
Features
+
+Conditions
+==========
+
+Edna gives you he option to specify *blocking conditions*.  Each
+condition is checked for each of the specified targets; if one of the
+conditions returns true for 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.
+
+* Menu:
+
+* done::
+* headings::
+* todo-state::
+* variable-set::
+* has-property::
+* re-search::                    Search for a regular expression
+* Negating Conditions::
+
+
+File: org-edna.info,  Node: done,  Next: headings,  Up: Conditions
+
+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
+
+headings
+--------
+
+   • Syntax: headings?
+
+   Blocks the source heading if any target belongs to a file that has an
+Org heading.  This means that target does not have to be a heading.
+
+     org-file("refile.org") headings?
+
+   The above example blocks if refile.org has any headings.
+
+
+File: org-edna.info,  Node: todo-state,  Next: variable-set,  Prev: headings,  
Up: Conditions
+
+todo-state
+----------
+
+   • Syntax: todo-state?(STATE)
+
+   Blocks if any target heading has TODO state set to STATE.
+
+   STATE may be a string or a symbol.
+
+
+File: org-edna.info,  Node: variable-set,  Next: has-property,  Prev: 
todo-state,  Up: Conditions
+
+variable-set
+------------
+
+   • Syntax: variable-set?(VARIABLE VALUE)
+
+   Evaluate VARIABLE when visiting a target, and compare it with ‘equal’
+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)
+
+
+File: org-edna.info,  Node: has-property,  Next: re-search,  Prev: 
variable-set,  Up: Conditions
+
+has-property
+------------
+
+   • Syntax: has-property?(“PROPERTY” “VALUE”)
+
+   Tests each target for the property PROPERTY, and blocks if it’s set
+to VALUE.
+
+
+File: org-edna.info,  Node: re-search,  Next: Negating Conditions,  Prev: 
has-property,  Up: Conditions
+
+re-search
+---------
+
+   • Syntax: re-search?(“REGEXP”)
+
+   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.
+
+
+File: org-edna.info,  Node: Negating Conditions,  Prev: re-search,  Up: 
Conditions
+
+Negating Conditions
+-------------------
+
+Any condition can be negated by using ’!’ before the condition.
+
+     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”.
+
+
+File: org-edna.info,  Node: Consideration,  Next: Setting the properties,  
Prev: Conditions,  Up: Advanced Features
+
+Consideration
+=============
+
+“Consideration” is a special keyword that’s only valid for blockers.
+
+   This keyword can allow specifying only a portion of tasks to
+consider:
+
+  1. consider(PERCENT)
+  2. consider(NUMBER)
+  3. consider(all) (Default)
+
+   (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.
+
+   (2) tells the blocker to only consider NUMBER of the targets.
+
+   (3) tells the blocker to consider all following targets.
+
+   A consideration must be specified before the targets to which it
+applies:
+
+     consider(0.5) siblings consider(all) match("find_me")
+
+   The above code will allow task completion if at least half the
+siblings are complete, and all tasks tagged “find_me” are complete.
+
+     consider(1) ids(ID1 ID2 ID3) consider(2) ids(ID3 ID4 ID5 ID6)
+
+   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.
+
+   If no consideration is given, ALL is assumed.
+
+
+File: org-edna.info,  Node: Setting the properties,  Prev: Consideration,  Up: 
Advanced Features
+
+Setting the properties
+======================
+
+There are two ways to set the BLOCKER and TRIGGER properties: by hand,
+or the easy way.  You can probably guess which way we prefer.
+
+   With point within the heading you want to edit, type ‘M-x
+org-edna-edit’.  You end up in a buffer that looks like this:
+
+     Edit blockers and triggers in this buffer under their respective sections 
below.
+     All lines under a given section will be merged into one when saving back 
to
+     the source buffer.  Finish with `C-c C-c' or abort with `C-c C-k'.
+
+     BLOCKER
+     BLOCKER STUFF HERE
+
+     TRIGGER
+     TIRGGER STUFF HERE
+
+   In here, you can edit the blocker and trigger properties for the
+original heading in a cleaner environment.  More importantly, you can
+complete the names of any valid keyword within the BLOCKER or TRIGGER
+sections using ‘completion-at-point’.
+
+   When finished, type ‘C-c C-c’ to apply the changes, or ‘C-c C-k’ to
+throw out your changes.
+
+
+File: org-edna.info,  Node: Extending Edna,  Next: Contributing,  Prev: 
Advanced Features,  Up: Top
+
+Extending Edna
+**************
+
+Extending Edna is (relatively) simple.
+
+   During operation, Edna searches for functions of the form
+org-edna-TYPE/KEYWORD.
+
+* Menu:
+
+* Naming Conventions::           How to name new functions
+* Finders: Finders (1).          Making a new finder
+* Actions: Actions (1).          Making a new action
+* Conditions: Conditions (1).    Making a new condition
+
+
+File: org-edna.info,  Node: Naming Conventions,  Next: Finders (1),  Up: 
Extending Edna
+
+Naming Conventions
+==================
+
+In order to distinguish between actions, finders, and conditions, we add
+’?’ to conditions and ’!’ to actions.  This is taken from the practice
+in Guile and 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.
+
+
+File: org-edna.info,  Node: Finders (1),  Next: Actions (1),  Prev: Naming 
Conventions,  Up: Extending Edna
+
+Finders
+=======
+
+Finders have the form org-edna-finder/KEYWORD, like so:
+
+     (defun org-edna-finder/test-finder ()
+       (list (point-marker)))
+
+   All finders must return a list of markers, one for each target found,
+or nil if no targets were found.
+
+
+File: org-edna.info,  Node: Actions (1),  Next: Conditions (1),  Prev: Finders 
(1),  Up: Extending Edna
+
+Actions
+=======
+
+Actions have the form org-edna-action/KEYWORD!:
+
+     (defun org-edna-action/test-action! (last-entry arg1 arg2)
+       )
+
+   Each action has at least one argument: ‘last-entry’.  This is a
+marker for the current entry (not to be confused with the current
+target).
+
+   The rest of the arguments are the arguments specified in the form.
+
+
+File: org-edna.info,  Node: Conditions (1),  Prev: Actions (1),  Up: Extending 
Edna
+
+Conditions
+==========
+
+     (defun org-edna-condition/test-cond? (neg))
+
+   All conditions have at least one argument, “NEG”.  If NEG is non-nil,
+the condition should be negated.
+
+   Most conditions have the following form:
+
+     (defun org-edna-condition/test-condition? (neg)
+       (let ((condition (my-test-for-condition)))
+         (when (org-xor condition neg)
+           (string-for-blocking-entry-here))))
+
+   For conditions, we return true if condition is true and neg is false,
+or if condition is false and neg is true:
+
+cond   neg   res
+-------------------
+t      t     f
+t      f     t
+f      t     t
+f      f     f
+
+   This is an XOR table, so we pass CONDITION and NEG into ‘org-xor’ to
+get our result.
+
+   A condition must return a string if the current entry should be
+blocked.
+
+
+File: org-edna.info,  Node: Contributing,  Prev: Extending Edna,  Up: Top
+
+Contributing
+************
+
+We are all happy for any help you may provide.
+
+   First, check out the source code on Savannah:
+<https://savannah.nongnu.org/projects/org-edna-el/>
+
+     bzr branch https://bzr.savannah.gnu.org/r/org-edna-el/ org-edna
+
+   You’ll also want a copy of the most recent Org Mode source:
+
+     git clone git://orgmode.org/org-mode.git
+
+* Menu:
+
+* Bugs::
+* Development::
+
+
+File: org-edna.info,  Node: Bugs,  Next: Development,  Up: Contributing
+
+Bugs
+====
+
+There are two ways to submit bug reports:
+
+  1. Using the bug tracker at Savannah
+  2. Sending an email using ‘org-edna-submit-bug-report’
+
+   When submitting a bug report, be sure to include the Edna form that
+caused the bug, with as much context as possible.
+
+
+File: org-edna.info,  Node: Development,  Prev: Bugs,  Up: Contributing
+
+Development
+===========
+
+If you’re new to bazaar, we recommend using Emacs’s built-in VC package.
+It eases the overhead of dealing with a brand new VCS with a few
+standard commands.  For more information, see the info page on it (In
+Emacs, this is C-h r m Introduction to VC RET).
+
+   To contribute with bazaar, you can do the following:
+
+     # Hack away and make your changes
+     $ bzr commit -m "Changes I've made"
+     $ bzr send -o file-name.txt
+
+   Then, use ‘org-edna-submit-bug-report’ and attach “file-name.txt”.
+We can then merge that into the main development branch.
+
+   There are a few rules to follow:
+
+   • Verify that any new Edna keywords follow the appropriate naming
+     conventions
+   • Any new keywords should be documented
+   • We operate on headings, not headlines
+        • Use one word to avoid confusion
+   • Run ’make check’ to verify that your mods don’t break anything
+   • Avoid additional or altered dependencies if at all possible
+        • Exception: New versions of Org mode are allowed
+
+
+
+Tag Table:
+Node: Top225
+Node: Copying3409
+Node: Introduction4226
+Node: Installation and Setup5174
+Node: Basic Operation5967
+Node: Blockers7818
+Node: Triggers8104
+Node: Syntax8366
+Node: Basic Features9056
+Node: Finders9359
+Node: ancestors11124
+Node: children11718
+Node: descendants12128
+Node: file12650
+Node: first-child13399
+Node: ids13659
+Node: match14320
+Node: next-sibling14958
+Node: next-sibling-wrap15215
+Node: olp15529
+Node: org-file15941
+Node: parent16586
+Node: previous-sibling16784
+Node: previous-sibling-wrap17045
+Node: relatives17324
+Node: rest-of-siblings20945
+Node: rest-of-siblings-wrap21230
+Node: self21579
+Node: siblings21740
+Node: siblings-wrap21977
+Node: Actions22281
+Node: Scheduled/Deadline23023
+Node: TODO State26598
+Node: Archive26966
+Node: Chain Property27286
+Node: Clocking27569
+Node: Property27981
+Node: Priority28303
+Node: Tag28872
+Node: Effort29089
+Node: Advanced Features29478
+Node: Conditions29816
+Node: done30431
+Node: headings30595
+Node: todo-state30971
+Node: variable-set31227
+Node: has-property31656
+Node: re-search31925
+Node: Negating Conditions32285
+Node: Consideration32672
+Node: Setting the properties33904
+Node: Extending Edna34984
+Node: Naming Conventions35474
+Node: Finders (1)35937
+Node: Actions (1)36303
+Node: Conditions (1)36768
+Node: Contributing37658
+Node: Bugs38130
+Node: Development38482
+
+End Tag Table
+
+
+Local Variables:
+coding: utf-8
+End:
diff --git a/packages/org-edna/org-edna.org b/packages/org-edna/org-edna.org
index bb31c85..f42e445 100644
--- a/packages/org-edna/org-edna.org
+++ b/packages/org-edna/org-edna.org
@@ -10,11 +10,11 @@
 #+OPTIONS: *:t <:t d:nil todo:nil pri:nil tags:not-in-toc
 
 #+TEXINFO_DIR_CATEGORY: Emacs
-#+TEXINFO_DIR_TITLE: Org Edna: (edna)
+#+TEXINFO_DIR_TITLE: Org Edna: (org-edna)
 #+TEXINFO_DIR_DESC: Extensible Dependencies 'N' Actions for Org Mode tasks
 
 * Copying
-Copyright (C) 2017 Ian Dunn
+Copyright (C) 2017 Free Software Foundation, Inc.
 
 #+BEGIN_QUOTE
 This program is free software: you can redistribute it and/or modify
@@ -42,7 +42,7 @@ Edna provides an extensible means of specifying conditions 
which must be
 fulfilled before a task can be completed and actions to take once it is.
 
 Org Edna runs when either the BLOCKER or TRIGGER properties are set on a
-headline, and when it is changing from a TODO state to a DONE state.
+heading, and when it is changing from a TODO state to a DONE state.
 
 For brevity, we use TODO state to indicate any state in 
~org-not-done-keywords~,
 and DONE state to indicate any state in ~org-done-keywords~.
@@ -58,7 +58,15 @@ and DONE state to indicate any state in ~org-done-keywords~.
 | seq   |  2.19 |
 | org   | 9.0.5 |
 
-Right now, the only way to install Edna is by cloning the bazaar repo:
+There are two ways to install Edna: From GNU ELPA, or from source.
+
+From ELPA:
+
+#+BEGIN_EXAMPLE
+M-x package-install org-edna
+#+END_EXAMPLE
+
+From Source:
 
 #+BEGIN_SRC shell
 bzr branch https://bzr.savannah.gnu.org/r/org-edna-el/ org-edna
@@ -68,8 +76,11 @@ make -C org-edna compile autoloads
 After that, add the following to your init file (typically .emacs):
 
 #+BEGIN_SRC emacs-lisp
+;; Only necessary if installing from source
 (add-to-list 'load-path "/full/path/to/org-edna/")
 (load "/path/to/org-edna/org-edna-autoloads.el")
+
+;; Always necessary
 (org-edna-load)
 #+END_SRC
 
@@ -113,7 +124,7 @@ Edna can handle this for you like so:
 #+END_SRC
 
 After you've put your clothes in the washer and mark the task DONE, Edna will
-schedule the following task for one hour after you set the first headline as
+schedule the following task for one hour after you set the first heading as
 done.
 
 Another example might be a checklist that you've done so many times that you do
@@ -142,8 +153,8 @@ can help:
 :DESCRIPTION: Blocking a TODO Item
 :END:
 
-A blocker indicates conditions which must be met in order for a headline to be
-marked as DONE.  Typically, this will be a list of headlines that must be 
marked
+A blocker indicates conditions which must be met in order for a heading to be
+marked as DONE.  Typically, this will be a list of headings that must be marked
 as DONE.
 
 ** Triggers
@@ -152,7 +163,7 @@ as DONE.
 :DESCRIPTION: Triggering actions after completing a task
 :END:
 
-A trigger is an action to take when a headline is set to done.  For example,
+A trigger is an action to take when a heading is set to done.  For example,
 scheduling another task, marking another task as TODO, or renaming a file.
 
 ** Syntax
@@ -191,20 +202,24 @@ The most basic features of Edna are *finders* and 
*actions*.
 :CUSTOM_ID: finders
 :END:
 A finder specifies locations from which to test conditions or perform actions.
-These locations are referred to as "targets".
+These locations are referred to as "targets".  The current heading, i.e. the 
one
+that is being blocked or triggered, is referred to as the "source" heading.
 
 More than one finder may be used.  In this case, the targets are merged
 together, removing any duplicates.
 
+Many finders take additional options, marked "OPTIONS".  See 
[[#relatives][relatives]] for
+information on these options.
+
 *** ancestors
 :PROPERTIES:
 :DESCRIPTION: Find a list of ancestors
 :CUSTOM_ID: ancestors
 :END:
 
-Syntax: ancestors
+- Syntax: ancestors(OPTIONS...)
 
-The ~ancestors~ finder returns a list of the current headline's ancestors.
+The ~ancestors~ finder returns a list of the source heading's ancestors.
 
 For example:
 
@@ -222,58 +237,18 @@ For example:
 In the above example, "Heading 5" will be blocked until "Heading 1", "Heading
 3", and "Heading 4" are marked "DONE", while "Heading 2" is ignored.
 
-*** chain-find
-:PROPERTIES:
-:CUSTOM_ID: chain-find
-:DESCRIPTION: Find a target as org-depend does
-:END:
-
-Syntax: chain-find(OPTION OPTION...)
-
-Identical to the chain argument in org-depend, chain-find selects its single
-target using the following method:
-
-1. Creates a list of possible targets
-2. Filters the targets from Step 1
-3. Sorts the targets from Step 2
-
-After this is finished, chain-find selects the first target in the list and
-returns it.
-
-One option from each of the following three categories may be used; if more 
than
-one is specified, the last will be used.
-
-*Selection*
-
-- from-top:     Select siblings of the current headline, starting at the top
-- from-bottom:  As above, but from the bottom
-- from-current: Selects siblings, starting from the headline (wraps)
-- no-wrap:      As above, but without wrapping
-
-*Filtering*
-
-- todo-only:          Select only targets with TODO state set that isn't a 
DONE keyword
-- todo-and-done-only: Select all targets with a TODO state set
-
-*Sorting*
-
-- priority-up:   Sort by priority, highest first
-- priority-down: Same, but lowest first
-- effort-up:     Sort by effort, highest first
-- effort-down:   Sort by effort, lowest first
-
 *** children
 :PROPERTIES:
 :CUSTOM_ID: children
 :DESCRIPTION: Find all immediate children
 :END:
 
-Syntax: children
+- Syntax: children(OPTIONS...)
 
-The ~children~ finder returns a list of the *immediate* children of the current
-headline.
+The ~children~ finder returns a list of the *immediate* children of the source
+heading.  If the source has no children, no target is returned.
 
-In order to get all levels of children of the current headline, use the
+In order to get all levels of children of the source heading, use the
 [[#descendants][descendants]] keyword instead.
 
 *** descendants
@@ -282,10 +257,9 @@ In order to get all levels of children of the current 
headline, use the
 :DESCRIPTION: Find all descendants
 :END:
 
-Syntax: descendants
+- Syntax: descendants(OPTIONS...)
 
-The ~descendants~ finder returns a list of all descendants of the current
-headline.
+The ~descendants~ finder returns a list of all descendants of the source 
heading.
 
 #+BEGIN_SRC org
 ,* TODO Heading 1
@@ -312,8 +286,12 @@ DONE.
 The ~file~ finder finds a single file, specified as a string.  The returned 
target
 will be the minimum point in the file.
 
-Note that with the default condition, ~file~ won't work.  See 
[[#conditions][conditions]] for how
-to set a different condition.  For example:
+Note that this does not give a valid heading, so any conditions
+or actions that require will throw an error.  Consult the
+documentation for individual actions or conditions to determine
+which ones will and won't work.
+
+See [[#conditions][conditions]] for how to set a different condition.  For 
example:
 
 #+BEGIN_SRC org
 ,* TODO Test
@@ -322,27 +300,28 @@ to set a different condition.  For example:
   :END:
 #+END_SRC
 
-Here, "Test" will block until myfile.org is clear of headlines.
+Here, "Test" will block until myfile.org is clear of headings.
 
 *** first-child
 :PROPERTIES:
 :CUSTOM_ID: first-child
-:DESCRIPTION: Find the first child of a headline
+:DESCRIPTION: Find the first child of a heading
 :END:
 
-- Syntax: first-child
+- Syntax: first-child(OPTIONS...)
 
-The ~first-child~ finder returns the first child of a headline, if any.
+Return the first child of the source heading.  If the source heading has no
+children, no target is returned.
 
 *** ids
 :PROPERTIES:
-:DESCRIPTION: Find a list of headlines with given IDs
+:DESCRIPTION: Find a list of headings with given IDs
 :CUSTOM_ID: ids
 :END:
 
 - Syntax: id(ID1 ID2 ...)
 
-The ~ids~ finder will search for headlines with given IDs, using ~org-id~.  Any
+The ~ids~ finder will search for headings with given IDs, using ~org-id~.  Any
 number of UUIDs may be specified.  For example:
 
 #+BEGIN_SRC org
@@ -352,8 +331,8 @@ number of UUIDs may be specified.  For example:
   :END:
 #+END_SRC
 
-Here, "Test" will block until the headline with ID
-62209a9a-c63b-45ef-b8a8-12e47a9ceed9 and the headline with ID
+Here, "Test" will block until the heading with ID
+62209a9a-c63b-45ef-b8a8-12e47a9ceed9 and the heading with ID
 6dbd7921-a25c-4e20-b035-365677e00f30 are set to "DONE".
 
 Note that UUIDs need not be quoted; Edna will handle that for you.
@@ -388,15 +367,25 @@ argument.
 :DESCRIPTION: Find the next sibling
 :END:
 
-- Syntax: next-sibling
+- Syntax: next-sibling(OPTIONS...)
 
-The ~next-sibling~ keyword returns the next sibling of the current heading, if
-any.
+The ~next-sibling~ keyword returns the next sibling of the source heading, if 
any.
+
+*** next-sibling-wrap
+:PROPERTIES:
+:CUSTOM_ID: next-sibling-wrap
+:DESCRIPTION: Find the next sibling, wrapping around
+:END:
+
+- Syntax: next-sibling-wrap(OPTIONS...)
+
+Find the next sibling of the source heading, if any.  If there isn't, wrap back
+around to the first heading in the same subtree.
 
 *** olp
 :PROPERTIES:
 :CUSTOM_ID: olp
-:DESCRIPTION: Find a headline by its outline path
+:DESCRIPTION: Find a heading by its outline path
 :END:
 
 - Syntax: olp("FILE" "OLP")
@@ -422,6 +411,10 @@ Finds the heading given by OLP in FILE.  Both arguments 
are strings.
 
 A special form of ~file~, ~org-file~ will find FILE in ~org-directory~.
 
+FILE is the relative path of a file in ~org-directory~.  Nested
+files are allowed, such as "my-directory/my-file.org".  The
+returned target is the minimum point of FILE.
+
 #+BEGIN_SRC org
 ,* TODO Test
   :PROPERTIES:
@@ -429,7 +422,9 @@ A special form of ~file~, ~org-file~ will find FILE in 
~org-directory~.
   :END:
 #+END_SRC
 
-Note that the file still requires an extension.
+Note that the file still requires an extension; the "org" here
+just means to look in ~org-directory~, not necessarily an
+Org mode file.
 
 *** parent
 :PROPERTIES:
@@ -437,9 +432,9 @@ Note that the file still requires an extension.
 :DESCRIPTION: Find a parent
 :END:
 
-- Syntax: parent
+- Syntax: parent(OPTIONS...)
 
-Returns the parent of the current headline, if any.
+Returns the parent of the source heading, if any.
 
 *** previous-sibling
 :PROPERTIES:
@@ -447,9 +442,110 @@ Returns the parent of the current headline, if any.
 :DESCRIPTION: Find the previous sibling
 :END:
 
-- Syntax: previous-sibling
+- Syntax: previous-sibling(OPTIONS...)
+
+Returns the previous sibling of the source heading on the same level.
+
+*** previous-sibling-wrap
+:PROPERTIES:
+:CUSTOM_ID: previous-sibling-wrap
+:DESCRIPTION: Find the previous sibling, with wrapping
+:END:
+
+- Syntax: previous-sibling-wrap(OPTIONS...)
+
+Returns the previous sibling of the source heading on the same level.
+
+*** relatives
+:PROPERTIES:
+:CUSTOM_ID: relatives
+:DESCRIPTION: Generic relative finder
+:END:
+
+Find some relative of the current heading.
+
+- Syntax: relatives(OPTION OPTION...)
+- Syntax: chain-find(OPTION OPTION...)
+
+Identical to the chain argument in org-depend, relatives selects its single
+target using the following method:
+
+1. Creates a list of possible targets
+2. Filters the targets from Step 1
+3. Sorts the targets from Step 2
+
+One option from each of the following three categories may be
+used; if more than one is specified, the last will be used.
+Filtering is the exception to this; each filter argument adds to
+the current filter.  Apart from that, argument order is
+irrelevant.
+
+The chain-find finder is also provided for backwards
+compatibility, and for similarity to org-depend.
+
+All arguments are symbols, unless noted otherwise.
+
+*Selection*
+
+- from-top:             Select siblings of the current heading, starting at 
the top
+- from-bottom:          As above, but from the bottom
+- from-current:         Selects siblings, starting from the heading (wraps)
+- no-wrap:              As above, but without wrapping
+- forward-no-wrap:      Find entries on the same level, going forward
+- forward-wrap:         As above, but wrap when the end is reached
+- backward-no-wrap:     Find entries on the same level, going backward
+- backward-wrap:        As above, but wrap when the start is reached
+- walk-up:              Walk up the tree, excluding self
+- walk-up-with-self:    As above, but including self
+- walk-down:            Recursively walk down the tree, excluding self
+- walk-down-with-self:  As above, but including self
+- step-down:            Collect headings from one level down
+
+*Filtering*
+
+- todo-only:          Select only targets with TODO state set that isn't a 
DONE state
+- todo-and-done-only: Select all targets with a TODO state set
+- no-comments:        Skip commented headings
+- no-archive:         Skip archived headings
+- NUMBER:             Only use that many headings, starting from the first one
+                      If passed 0, use all headings
+                      If <0, omit that many headings from the end
+- "+tag":             Only select headings with given tag
+- "-tag":             Only select headings without tag
+- "REGEX":            select headings whose titles match REGEX
+
+*Sorting*
 
-Returns the previous sibling of the current headline on the same level.
+- no-sort:         Remove other sorting in affect
+- reverse-sort:    Reverse other sorts (stacks with other sort methods)
+- random-sort:     Sort in a random order
+- priority-up:     Sort by priority, highest first
+- priority-down:   Same, but lowest first
+- effort-up:       Sort by effort, highest first
+- effort-down:     Sort by effort, lowest first
+- scheduled-up:    Scheduled time, farthest first
+- scheduled-down:  Scheduled time, closest first
+- deadline-up:     Deadline time, farthest first
+- deadline-down:   Deadline time, closest first
+
+Many of the other finders are shorthand for argument combinations of relative:
+
+- [[#ancestors][ancestors]] :: walk-up
+- [[#children][children]] :: step-down
+- [[#descendants][descendants]] :: walk-down
+- [[#first-child][first-child]] :: step-down 1
+- [[#next-sibling][next-sibling]] :: forward-no-wrap 1
+- [[#next-sibling-wrap][next-sibling-wrap]] :: forward-wrap 1
+- [[#parent][parent]] :: walk-up 1
+- [[#previous-sibling][previous-sibling]] :: backward-no-wrap 1
+- [[#previous-sibling-wrap][previous-sibling-wrap]] :: backward-wrap 1
+- [[#rest-of-siblings][rest-of-siblings]] :: forward-no-wrap
+- [[#rest-of-siblings-wrap][rest-of-siblings-wrap]] :: forward-wrap
+- [[#siblings][siblings]] :: from-top
+- [[#siblings-wrap][siblings-wrap]] :: forward-wrap
+
+Because these are implemented as shorthand, any arguments for relatives may 
also
+be passed to one of these finders.
 
 *** rest-of-siblings
 :PROPERTIES:
@@ -457,11 +553,22 @@ Returns the previous sibling of the current headline on 
the same level.
 :DESCRIPTION: Find the remaining siblings
 :END:
 
-- Syntax: rest-of-siblings
+- Syntax: rest-of-siblings(OPTIONS...)
 
-Starting from the headline following the current one, all same-level siblings
+Starting from the heading following the current one, all same-level siblings
 are returned.
 
+*** rest-of-siblings-wrap
+:PROPERTIES:
+:CUSTOM_ID: rest-of-siblings-wrap
+:DESCRIPTION: Find the remaining siblings, with wrapping
+:END:
+
+- Syntax: rest-of-siblings-wrap(OPTIONS...)
+
+Starting from the heading following the current one, all same-level siblings
+are returned.  When the end is reached, wrap back to the beginning.
+
 *** self
 :PROPERTIES:
 :CUSTOM_ID: self
@@ -469,7 +576,7 @@ are returned.
 
 - Syntax: self
 
-Returns the current headline.
+Returns the source heading.
 
 *** siblings
 :PROPERTIES:
@@ -477,9 +584,10 @@ Returns the current headline.
 :DESCRIPTION: Find all the siblings
 :END:
 
-- Syntax: siblings
+- Syntax: siblings(OPTIONS...)
 
-Returns all siblings of the source heading as targets.
+Returns all siblings of the source heading as targets, starting from the first
+sibling.
 
 *** siblings-wrap
 :PROPERTIES:
@@ -487,11 +595,13 @@ Returns all siblings of the source heading as targets.
 :DESCRIPTION: Find the siblings, but wrap around
 :END:
 
-- Syntax: siblings-wrap
+- Syntax: siblings-wrap(OPTIONS...)
 
-Finds the siblings on the same level as the current headline, wrapping when it
+Finds the siblings on the same level as the source heading, wrapping when it
 reaches the end.
 
+Identical to the [[#rest-of-siblings-wrap][rest-of-siblings-wrap]] finder.
+
 ** Actions
 :PROPERTIES:
 :DESCRIPTION: Next steps
@@ -504,55 +614,110 @@ Actions must always end with '!'.
 *** Scheduled/Deadline
 :PROPERTIES:
 :CUSTOM_ID: planning
+:DESCRIPTION: Operate on planning information
 :END:
 
 - Syntax: scheduled!(OPTIONS)
 - Syntax: deadline!(OPTIONS)
 
-There are several forms that the planning keywords can take:
+Set the scheduled or deadline time of any target headings.
 
-- PLANNING("WKDY[ TIME]")
+There are several forms that the planning keywords can take.  In the following,
+PLANNING is either scheduled or deadline.
 
-  Sets PLANNING to the following weekday WKDY at TIME.  If TIME is not
-  specified, only a date will be added to the target.
+- PLANNING!("DATE[ TIME]")
 
-  WKDY is a weekday or weekday abbreviation (see ~org-read-date~)
+  Sets PLANNING to DATE at TIME.  If DATE is a weekday instead of a date, then
+  set PLANNING to the following weekday.  If TIME is not specified, only a date
+  will be added to the target.
 
-  TIME is a time string HH:MM, etc.
+  Any string recognized by ~org-read-date~ may be used for DATE.
 
-- PLANNING(rm|remove)
+  TIME is a time string, such as HH:MM.
+
+- PLANNING!(rm|remove)
 
   Remove PLANNING from all targets.  The argument to this form may be either a
   string or a symbol.
 
-- PLANNING(copy|cp)
+- PLANNING!(copy|cp)
 
-  Copy PLANNING info verbatim from the current headline to all targets.  The
+  Copy PLANNING info verbatim from the source heading to all targets.  The
   argument to this form may be either a string or a symbol.
 
-- PLANNING("[+|-][+|-]NTHING")
+- PLANNING!("[+|-|++|--]NTHING[ [+|-]LANDING]")
 
-  Increment(+) or decrement(-) source (double) or current (single) PLANNING by 
N
-  THINGs
+  Increment(+) or decrement(-) target's PLANNING by N THINGs relative to either
+  itself (+/-) or the current time (++/--).
 
   N is an integer
 
-  THING is one of y (years), m (months), d (days), h (hours), or M (minutes)
+  THING is one of y (years), m (months), d (days), h (hours), M (minutes), a
+  (case-insensitive) day of the week or its abbreviation, or the strings
+  "weekday" or "wkdy".
+
+  If a day of the week is given as THING, move forward or backward N weeks to
+  find that day of the week.
+
+  If one of "weekday" or "wkdy" is given as THING, move forward or backward N
+  days, moving forward or backward to the next weekday.
+
+  This form may also include a "landing" specifier to control where in the week
+  the final date lands.  LANDING may be one of the following:
+
+  - A day of the week, which means adjust the final date forward (+) or 
backward
+    (-) to land on that day of the week.
+
+  - One of "weekday" or "wkdy", which means adjust the target date to the
+    closest weekday.
+
+  - One of "weekend" or "wknd", which means adjust the target date to the
+    closest weekend.
+
+- PLANNING!("float [+|-|++|--]N DAYNAME[ MONTH[ DAY]]")
+
+  Set time to the date of the Nth DAYNAME before/after MONTH DAY, as per
+  ~diary-float~.
+
+  N is an integer.
+
+  DAYNAME may be either an integer, where 0=Sunday, 1=Monday, etc., or a string
+  for that day.
+
+  MONTH may be an integer, 1-12, or a month's string.  If MONTH is empty, the
+  following (+) or previous (-) month relative to the target's time (+/-) or 
the
+  current time (++/--).
+
+  DAY is an integer, or empty or 0 to use the first of the month (+) or the 
last
+  of the month (-).
 
 Examples:
 
-scheduled!("Mon 09:00") -> Set SCHEDULED to the following Monday at 9:00
+- 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
 *** TODO State
+:PROPERTIES:
+:CUSTOM_ID: todo!
+:DESCRIPTION: Set todo state
+:END:
 
 - Syntax: todo!(NEW-STATE)
 
-Sets the TODO state of the target headline to NEW-STATE.
+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.
+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.
 
 *** Archive
 :PROPERTIES:
 :CUSTOM_ID: archive!
+:DESCRIPTION: Archive targets
 :END:
 
 - Syntax: archive!
@@ -560,15 +725,24 @@ NEW-STATE may either be a string or a symbol denoting the 
new TODO state.
 Archives all targets with confirmation.
 
 Confirmation is controlled with ~org-edna-prompt-for-archive~.  If this option 
is
-nil, Edna will not ask before archiving the target.
+nil, Edna will not ask before archiving targets.
 
 *** Chain Property
+:PROPERTIES:
+:CUSTOM_ID: chain!
+:DESCRIPTION: Copy properties from source to targets
+:END:
 
 - Syntax: chain!("PROPERTY")
 
-Copies PROPERTY from the source entry to all targets.
+Copies PROPERTY from the source entry to all targets.  Does nothing if the
+source heading has no property PROPERTY.
 
 *** Clocking
+:PROPERTIES:
+:CUSTOM_ID: clocking
+:DESCRIPTION: Clock in or out of a target
+:END:
 
 - Syntax: clock-in!
 - Syntax: clock-out!
@@ -578,9 +752,15 @@ Clocks into or out of all targets.
 ~clock-in!~ has no special handling of targets, so be careful when specifying
 multiple targets.
 
+In contrast, ~clock-out!~ ignores its targets and only clocks out of the 
current
+clock, if any.
 *** Property
+:PROPERTIES:
+:CUSTOM_ID: properties
+:DESCRIPTION: Set properties of targets
+:END:
 
-- Syntax: set-property!("PROPERTY","VALUE")
+- Syntax: set-property!("PROPERTY" "VALUE")
 
 Sets the property PROPERTY on all targets to VALUE.
 
@@ -589,15 +769,34 @@ Sets the property PROPERTY on all targets to VALUE.
 Deletes the property PROPERTY from all targets.
 
 *** Priority
+:PROPERTIES:
+:CUSTOM_ID: priorities
+:DESCRIPTION: Set priorities of targets
+:END:
+
+Sets the priority of all targets.
+
+- Syntax: set-priority!("PRIORITY")
+
+  Set the priority to the first character of PRIORITY.
+
+- Syntax: set-priority!(up)
+
+  Cycle the target's priority up through the list of allowed priorities.
+
+- Syntax: set-priority!(down)
 
-- Syntax: set-priority!(PRIORITY)
+  Cycle the target's priority down through the list of allowed priorities.
 
-Sets the priority of all targets to PRIORITY.  PRIORITY is processed as 
follows:
+- Syntax: set-priority!(P)
 
-- If PRIORITY is a string, the first character is used as the priority
-- Any other value is passed into ~org-priority~ verbatim, so it can be 'up, 
'down, or an integer
+  Set the target's priority to the character P.
 
 *** Tag
+:PROPERTIES:
+:CUSTOM_ID: tags
+:DESCRIPTION: Tags of a target
+:END:
 
 - Syntax: tag!("TAG-SPEC")
 
@@ -605,6 +804,10 @@ Tags all targets with TAG-SPEC, which is any valid tag 
specification,
 e.g. tag1:tag2
 
 *** Effort
+:PROPERTIES:
+:CUSTOM_ID: effort
+:DESCRIPTION: So much effort!
+:END:
 
 - Syntax: set-effort!(VALUE)
 
@@ -621,11 +824,15 @@ Sets the effort of all targets according to VALUE:
 ** Conditions
 :PROPERTIES:
 :CUSTOM_ID: conditions
+:DESCRIPTION: More than just DONE headings
 :END:
 
 Edna gives you he option to specify *blocking conditions*.  Each condition is 
checked
 for each of the specified targets; if one of the conditions returns true for
-that target, then that headline is blocked.
+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
 :PROPERTIES:
@@ -634,7 +841,7 @@ that target, then that headline is blocked.
 
 - Syntax: done?
 
-Blocks the current headline if any target is DONE.
+Blocks the source heading if any target heading is DONE.
 
 *** headings
 :PROPERTIES:
@@ -643,7 +850,8 @@ Blocks the current headline if any target is DONE.
 
 - Syntax: headings?
 
-Blocks the current headline if any target belongs to a file that has an Org 
heading.
+Blocks the source heading if any target belongs to a file that has an Org
+heading.  This means that target does not have to be a heading.
 
 #+BEGIN_EXAMPLE
 org-file("refile.org") headings?
@@ -658,7 +866,7 @@ The above example blocks if refile.org has any headings.
 
 - Syntax: todo-state?(STATE)
 
-Blocks if any target has a headline with TODO state set to STATE.
+Blocks if any target heading has TODO state set to STATE.
 
 STATE may be a string or a symbol.
 
@@ -667,14 +875,15 @@ STATE may be a string or a symbol.
 :CUSTOM_ID: variable-set
 :END:
 
-- Syntax: variable-set?(VARIABLE,VALUE)
+- Syntax: variable-set?(VARIABLE VALUE)
 
-Blocks the current headline if VARIABLE is set to VALUE.
+Evaluate VARIABLE when visiting a target, and compare it with ~equal~
+against VALUE.  Block the source heading if VARIABLE = VALUE.
 
-VARIABLE should be a symbol, and VALUE is any valid lisp expression
+VARIABLE should be a symbol, and VALUE is any valid lisp expression.
 
 #+BEGIN_EXAMPLE
-self variable-set?(test-variable,12)
+self variable-set?(test-variable 12)
 #+END_EXAMPLE
 
 *** has-property
@@ -682,7 +891,7 @@ self variable-set?(test-variable,12)
 :CUSTOM_ID: has-property
 :END:
 
-- Syntax: has-property?("PROPERTY","VALUE")
+- Syntax: has-property?("PROPERTY" "VALUE")
 
 Tests each target for the property PROPERTY, and blocks if it's set to VALUE.
 
@@ -694,7 +903,7 @@ Tests each target for the property PROPERTY, and blocks if 
it's set to VALUE.
 
 - Syntax: re-search?("REGEXP")
 
-Blocks the current headline if the regular expression REGEXP is present in any
+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
@@ -704,16 +913,20 @@ as well.
 :PROPERTIES:
 :CUSTOM_ID: negate
 :END:
-Any condition can be negated using '!'.
+Any condition can be negated by using '!' before the condition.
 
 #+BEGIN_EXAMPLE
-match("test") !has-property?("PROP","1")
+match("test") !has-property?("PROP" "1")
 #+END_EXAMPLE
 
-The above example will cause the current headline to block if any headline
+The above example will cause the source heading to block if any heading
 tagged "test" does *not* have the property PROP set to "1".
 ** Consideration
-Special keyword that's only valid for blockers.
+:PROPERTIES:
+:DESCRIPTION: Only some of them
+:END:
+
+"Consideration" is a special keyword that's only valid for blockers.
 
 This keyword can allow specifying only a portion of tasks to consider:
 
@@ -746,6 +959,38 @@ 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.
 
 If no consideration is given, ALL is assumed.
+
+** Setting the properties
+:PROPERTIES:
+:DESCRIPTION: The easy way to set BLOCKER and TRIGGER
+:END:
+
+There are two ways to set the BLOCKER and TRIGGER properties: by hand, or the
+easy way.  You can probably guess which way we prefer.
+
+With point within the heading you want to edit, type ~M-x org-edna-edit~.  You 
end
+up in a buffer that looks like this:
+
+#+begin_example
+Edit blockers and triggers in this buffer under their respective sections 
below.
+All lines under a given section will be merged into one when saving back to
+the source buffer.  Finish with `C-c C-c' or abort with `C-c C-k'.
+
+BLOCKER
+BLOCKER STUFF HERE
+
+TRIGGER
+TIRGGER STUFF HERE
+#+end_example
+
+In here, you can edit the blocker and trigger properties for the original
+heading in a cleaner environment.  More importantly, you can complete the names
+of any valid keyword within the BLOCKER or TRIGGER sections using
+~completion-at-point~.
+
+When finished, type ~C-c C-c~ to apply the changes, or ~C-c C-k~ to throw out 
your
+changes.
+
 * Extending Edna
 :PROPERTIES:
 :DESCRIPTION: What else can it do?
@@ -853,7 +1098,7 @@ You'll also want a copy of the most recent Org Mode source:
 git clone git://orgmode.org/org-mode.git
 #+END_SRC
 
-*Bugs*
+** Bugs
 
 There are two ways to submit bug reports:
 
@@ -863,14 +1108,30 @@ There are two ways to submit bug reports:
 When submitting a bug report, be sure to include the Edna form that caused the
 bug, with as much context as possible.
 
-*Development*
+** Development
+
+If you're new to bazaar, we recommend using Emacs's built-in VC package.  It
+eases the overhead of dealing with a brand new VCS with a few standard 
commands.
+For more information, see the info page on it (In Emacs, this is
+C-h r m Introduction to VC RET).
+
+To contribute with bazaar, you can do the following:
+
+#+begin_src shell
+# Hack away and make your changes
+$ bzr commit -m "Changes I've made"
+$ bzr send -o file-name.txt
+#+end_src
 
-Submitting patches along with a bug report is the easiest way.
+Then, use ~org-edna-submit-bug-report~ and attach "file-name.txt".  We can then
+merge that into the main development branch.
 
 There are a few rules to follow:
 
 - Verify that any new Edna keywords follow the appropriate naming conventions
 - Any new keywords should be documented
+- We operate on headings, not headlines
+  - Use one word to avoid confusion
 - Run 'make check' to verify that your mods don't break anything
 - Avoid additional or altered dependencies if at all possible
   - Exception: New versions of Org mode are allowed



reply via email to

[Prev in Thread] Current Thread [Next in Thread]