[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 40b0ee9 12/14: Added two new forms for setting planning in
From: |
Ian Dunn |
Subject: |
[elpa] master 40b0ee9 12/14: Added two new forms for setting planning information |
Date: |
Sun, 17 Dec 2017 17:39:59 -0500 (EST) |
branch: master
commit 40b0ee9cde47833ee49c47cc8d58e19b4b94aa35
Author: Ian Dunn <address@hidden>
Commit: Ian Dunn <address@hidden>
Added two new forms for setting planning information
* org-edna.el (org-edna--read-date-get-relative): New defun for landing
form.
(org-edna--float-time): New defun for float form.
(org-edna--handle-planning): Use the new functions.
* org-edna-tests.el: Add new tests for landing and float.
* org-edna.org (Scheduled/Deadline): Document the new forms.
* org-edna.info: Updated documentation.
---
org-edna-tests.el | 96 +++++++++++++++-
org-edna.el | 324 +++++++++++++++++++++++++++++++++++++++++++++++++++---
org-edna.info | 186 +++++++++++++++++++------------
org-edna.org | 52 ++++++++-
4 files changed, 566 insertions(+), 92 deletions(-)
diff --git a/org-edna-tests.el b/org-edna-tests.el
index 00a9a8d..52d1540 100644
--- a/org-edna-tests.el
+++ b/org-edna-tests.el
@@ -352,7 +352,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))
@@ -372,20 +371,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>")))))
diff --git a/org-edna.el b/org-edna.el
index c01d354..364aa8b 100644
--- a/org-edna.el
+++ b/org-edna.el
@@ -711,14 +711,269 @@ N is an integer. WHAT can be `day', `month', `year',
`minute',
WHAT is either 'scheduled or 'deadline."
(org-entry-get nil (if (eq what 'scheduled) "SCHEDULED" "DEADLINE")))
+;; 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)
+ (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)))
+ (message "act day = %s" 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."
- ;; Need case-fold-search enabled so org-read-date-get-relative will
recognize "M"
- (let* ((case-fold-search t)
- (arg (nth 0 args))
+ (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)
@@ -734,11 +989,16 @@ WHAT is either 'scheduled or 'deadline."
(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))))
@@ -758,10 +1018,11 @@ WHAT is either 'scheduled or 'deadline."
(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\") [4]
+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
@@ -778,16 +1039,33 @@ 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."
+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\") [4]
+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
@@ -805,7 +1083,23 @@ 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."
+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)
diff --git a/org-edna.info b/org-edna.info
index 999d669..4bc75e0 100644
--- a/org-edna.info
+++ b/org-edna.info
@@ -73,9 +73,9 @@ Actions
Advanced Features
-* Conditions::
-* Consideration::
-* Setting the properties::
+* Conditions:: More than just DONE headings
+* Consideration:: Only some of them
+* Setting the properties:: The easy way to set BLOCKER and TRIGGER
Conditions
@@ -705,22 +705,68 @@ following, PLANNING is either scheduled or deadline.
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(-) 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 deadline!(“++1h”) -> Set DEADLINE to one hour from now.
-deadline!(copy) deadline!(“+1h”) -> Copy the source deadline to the
-target, then increment it by an hour.
+ • 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
@@ -851,9 +897,9 @@ Advanced Features
* Menu:
-* Conditions::
-* Consideration::
-* Setting the properties::
+* 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
@@ -975,7 +1021,7 @@ File: org-edna.info, Node: Consideration, Next: Setting
the properties, Prev:
Consideration
=============
-Special keyword that’s only valid for blockers.
+“Consideration” is a special keyword that’s only valid for blockers.
This keyword can allow specifying only a portion of tasks to
consider:
@@ -1206,62 +1252,62 @@ We can then merge that into the main development branch.
Tag Table:
Node: Top225
-Node: Copying3142
-Node: Introduction3959
-Node: Installation and Setup4907
-Node: Basic Operation5700
-Node: Blockers7551
-Node: Triggers7837
-Node: Syntax8099
-Node: Basic Features8789
-Node: Finders9092
-Node: ancestors10595
-Node: chain-find11179
-Node: children12517
-Node: descendants12916
-Node: file13426
-Node: first-child14175
-Node: ids14423
-Node: match15084
-Node: next-sibling15722
-Node: next-sibling-wrap15967
-Node: olp16269
-Node: org-file16681
-Node: parent17326
-Node: previous-sibling17512
-Node: rest-of-siblings17756
-Node: self18019
-Node: siblings18175
-Node: siblings-wrap18433
-Node: Actions18666
-Node: Scheduled/Deadline19408
-Node: TODO State20998
-Node: Archive21366
-Node: Chain Property21686
-Node: Clocking21969
-Node: Property22381
-Node: Priority22703
-Node: Tag23272
-Node: Effort23489
-Node: Advanced Features23878
-Node: Conditions24090
-Node: done24705
-Node: headings24869
-Node: todo-state25245
-Node: variable-set25501
-Node: has-property25930
-Node: re-search26199
-Node: Negating Conditions26559
-Node: Consideration26946
-Node: Setting the properties28153
-Node: Extending Edna29233
-Node: Naming Conventions29723
-Node: Finders (1)30186
-Node: Actions (1)30552
-Node: Conditions (1)31017
-Node: Contributing31907
-Node: Bugs32379
-Node: Development32731
+Node: Copying3268
+Node: Introduction4085
+Node: Installation and Setup5033
+Node: Basic Operation5826
+Node: Blockers7677
+Node: Triggers7963
+Node: Syntax8225
+Node: Basic Features8915
+Node: Finders9218
+Node: ancestors10721
+Node: chain-find11305
+Node: children12643
+Node: descendants13042
+Node: file13552
+Node: first-child14301
+Node: ids14549
+Node: match15210
+Node: next-sibling15848
+Node: next-sibling-wrap16093
+Node: olp16395
+Node: org-file16807
+Node: parent17452
+Node: previous-sibling17638
+Node: rest-of-siblings17882
+Node: self18145
+Node: siblings18301
+Node: siblings-wrap18559
+Node: Actions18792
+Node: Scheduled/Deadline19534
+Node: TODO State23109
+Node: Archive23477
+Node: Chain Property23797
+Node: Clocking24080
+Node: Property24492
+Node: Priority24814
+Node: Tag25383
+Node: Effort25600
+Node: Advanced Features25989
+Node: Conditions26327
+Node: done26942
+Node: headings27106
+Node: todo-state27482
+Node: variable-set27738
+Node: has-property28167
+Node: re-search28436
+Node: Negating Conditions28796
+Node: Consideration29183
+Node: Setting the properties30415
+Node: Extending Edna31495
+Node: Naming Conventions31985
+Node: Finders (1)32448
+Node: Actions (1)32814
+Node: Conditions (1)33279
+Node: Contributing34169
+Node: Bugs34641
+Node: Development34993
End Tag Table
diff --git a/org-edna.org b/org-edna.org
index cd34193..804ee5e 100644
--- a/org-edna.org
+++ b/org-edna.org
@@ -571,20 +571,62 @@ PLANNING is either scheduled or deadline.
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(-) 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
-deadline!("++1h") -> Set DEADLINE to one hour from now.
-deadline!(copy) deadline!("+1h") -> Copy the source deadline to the target,
then increment it by an hour.
+- 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!
- [elpa] master 5979517 03/14: Only require subr-x during compilation, (continued)
- [elpa] master 5979517 03/14: Only require subr-x during compilation, Ian Dunn, 2017/12/17
- [elpa] master 63ef489 01/14: Updated documentation for ELPA release, Ian Dunn, 2017/12/17
- [elpa] master 2cfb021 02/14: Added next-sibling-wrap finder, Ian Dunn, 2017/12/17
- [elpa] master ec145d9 07/14: Bumped version to beta1, Ian Dunn, 2017/12/17
- [elpa] master 848f046 08/14: Update if-let and when-let to their -let* counterparts, Ian Dunn, 2017/12/17
- [elpa] master 35a9a7e 04/14: Added support for interactive editing of blockers and triggers, Ian Dunn, 2017/12/17
- [elpa] master 7aa71e7 06/14: Added documentation for popout editing, Ian Dunn, 2017/12/17
- [elpa] master 1eabcf9 09/14: Don't present variables for keyword completion, Ian Dunn, 2017/12/17
- [elpa] master 32dada2 10/14: Add space between edit message and BLOCKER section, Ian Dunn, 2017/12/17
- [elpa] master 23b5ae9 11/14: Fix bug in keyword completion, Ian Dunn, 2017/12/17
- [elpa] master 40b0ee9 12/14: Added two new forms for setting planning information,
Ian Dunn <=
- [elpa] master 6826e92 05/14: Updated documentation, Ian Dunn, 2017/12/17
- [elpa] master ea0f9fb 13/14: Added relatives finder, Ian Dunn, 2017/12/17
- [elpa] master bd64a1d 14/14: Merge commit 'ea0f9fb914cccc1d127eea94bc4c607dbcd4dc7d', Ian Dunn, 2017/12/17