3

Q: How can I make org-read-date understand an abbreviation that calls a function that returns tomorrow's day of the week?

The super-short version: I'm trying to get the timestamp functionality of +1d when typing tom as a more finger-friendly way of saying "tomorrow."

This question follows up on this thread on dates and this thread on times.

I'm trying to train org-read-date to understand new, easier-to-type abbreviations when I'm entering timestamps, scheduled items, and deadlines. In particular, when entering a timestamp, I'd like to be able to enter tom and have org-read-date recognize it as "whatever day tomorrow is" (yes, I know I can type +1d, but it's an awfully awkward sequence of keys).

The parse-time functionality on which org-read-date relies expects an integer from 0 (Sunday) to 6 (Saturday). Fair enough, here's a little function that does it:

(defun tomorrow-day ()
  "Returns the day of the week for tomorrow."
  (let ((day (1+ (string-to-number (format-time-string "%w")))))
    (if (= day 7)
        0
      day)))

I'm stuck at the following point. Following this prior thread, I've pushed this function onto the parse-time-weekdays alist:

(push `("tom" . ,(tomorrow-day)) parse-time-weekdays)

Here's the problem: quasi-quoting pushes the literal value that tomorrow-day returns only at the time it was first evaluated (so, if today is Monday, tom refers to day number 2 from here to eternity). Instead, I need tomorrow-day evaluated each time I invoke tom when entering a timestamp. Why? Because I commonly leave an Emacs session running for days at a time, meaning that tom no longer refers to "tomorrow" after the day on which it was evaluated.

I've tried various forms of quoting and quasi-quoting, and eventually got quite silly with an intricate combination of funcalls and evals, all to no avail.

So: how do I get this function (tomorrow-day) evaluated every time I enter tom in a timestamp?

Dan
  • 32,584
  • 6
  • 98
  • 168
  • 2
    How about modifying `org-read-date-analyze` to transform your abreviation into whatever you want -- e.g., `(when (string-match . . .`? You'll probably be most interested in transforming the value of `ans` -- when `ans` is `tom`, make it `+1d` instead. You could also catch `ans` and modify it beforehand -- e.g., by modifying it inside `org-read-date` -- before `final` is set. – lawlist Nov 18 '14 at 02:38
  • @lawlist, thanks for the suggestion. `org-read-date-analyze` makes my head spin. Although I found an alternate way to get what I was after, I'll dig into that function for other modifications. – Dan Nov 18 '14 at 11:42

2 Answers2

2

Seems like we can build on @lawlist's idea and do something similar to this:

(advice-add 'org-read-date-analyze :around
            (lambda (orig ans org-def org-defdecode)
              (funcall orig (if (string-match "tom" ans) "+1d" ans) org-def org-defdecode)))
Constantine
  • 9,072
  • 1
  • 34
  • 49
  • Some whitespace is getting mixed in with the `ans` -- `(if (string-match "tom" ans) "+1d" ans)` should fix it. If you put in a message for `ans`, you'll see what I mean -- some stuff gets appended to the end that looks like whitespace. – lawlist Nov 18 '14 at 17:41
  • @lawlist: The unfortunate thing is that now "atom", "tom-tom" and so on also mean "tomorrow". – Constantine Nov 18 '14 at 17:51
  • Then we need a tighter regexp -- `"^tom +$"` or something like that. Or, we need to figure out exactly what type of whatspace is being inserted so a `string=` can work. – lawlist Nov 18 '14 at 18:00
  • Looks like `org-read-date-display` appends a space to the string typed in by the user until it gets something ending in four spaces. I wish I knew why it does that. Other callers of `org-read-date-analyze` don't do that (as far as I can tell), so I would use `"^tom *$"`. – Constantine Nov 21 '14 at 20:20
1

I figured out a somewhat convoluted way to get the functionality without modifying org-read-date or org-read-date-analyze. It uses the same approach in the question, but defining a timer that runs every 24-hours at midnight that resets the value of tom:

(defun tomorrow-day ()
  "Returns the day of the week for tomorrow."
  (let ((day (1+ (string-to-number (format-time-string "%w")))))
    (if (= day 7)
        0
      day)))

(eval-after-load 'parse-time
   '(progn
     (setq parse-time-weekdays (nconc parse-time-weekdays
                                      `(("tom" . ,(tomorrow-day))))
     (defvar parse-time-tomorrow-timer
       (run-at-time "12am"
                    (* 24 60 60)
                    (lambda ()
                      (setf (cdr (assoc "tom" parse-time-weekdays))
                            (tomorrow-day))))
       "Timer to reset the day to which \"tom\" refers in timestamps."))) 

What it lacks in elegance, it makes up in obscurity!

Dan
  • 32,584
  • 6
  • 98
  • 168