Intro:
Based on the existing code for Emacs' diary-sunrise-sunset, I attempted to create two new functions diary-sunrise and diary-sunset.
My reasons for this are included below under the heading "XY-description".
I have code below which seems to work, except when I restart with a new Emacs. I can fix this by momentarily using the original built-in diary-sunrise-sunset. From then on, my functions perform beautifully.
In other words, I have to use the built-in %%(diary-sunrise-sunset) just a one time before my %%(diary-sunrise) and %%(diary-sunset) will work.
Question:
Can you help me fix my use of these functions so that I do not have to take the awkward step of first getting the built-in function called?
The lines of code that seem suspicious to me are the ones that go
;;;###diary-autoload
While I have some idea of the necessity of loading programs, I am not sure what's going on here, or if this is where the issue lies. (I've never seen that particular syntax.)
I have tried M-: (require 'solar) and M-: (require 'diary), but neither have worked (and just now calendar). I have tried putting my code both in my .emacs and in the built-in .../lisp/calendar/solar.el (and byte-recompiling), but neither have worked.
My functions:
(They are each slight modifications of solar-sunrise-sunset-string and diary-sunrise-sunset, which are both defined in .../lisp/calendar/solar.el).
Sunrise:
(defun solar-sunrise-string (date &optional nolocation)
"String of *local* time of sunrise and daylight on Gregorian DATE."
(let ((l (solar-sunrise-sunset date)))
(format
"%s (%s hours daylight)"
(if (car l)
(concat "Sunset " (apply 'solar-time-string (car l)))
"no sunset")
(nth 2 l)
)))
;; To be called from diary-list-sexp-entries, where DATE is bound.
;;;###diary-autoload
(defun diary-sunrise ()
"Local time of sunrise as a diary entry.
Accurate to a few seconds."
(or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(solar-sunrise-string date))
Sunset:
(defun solar-sunset-string (date &optional nolocation)
"String of *local* time of sunset and daylight on Gregorian DATE."
(let ((l (solar-sunrise-sunset date)))
(format
"%s (%s hours daylight)"
(if (cadr l)
(concat "Sunset " (apply 'solar-time-string (cadr l)))
"no sunset")
(nth 2 l)
)))
;; To be called from diary-list-sexp-entries, where DATE is bound.
;;;###diary-autoload
(defun diary-sunset ()
"Local time of sunset as a diary entry.
Accurate to a few seconds."
(or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(solar-sunset-string date))
XY description:
I am using Emacs' Org-mode, and just starting to use agenda views. I like the builtin diary-sunrise-sunset function, but wanted to make some minor tweaks to make it more to my liking.
Basically, Org-mode's agenda view will extract the first time it sees from the diary sexp %%(diary-sunrise-sunset), for instance
Sat, Apr 5, 2014
Sunrise 6:43am (PDT), sunset 7:42pm (PDT) at Springfield, OH (12:59 hours daylight)
and thus make an entry of
6:43am........ Sunrise (PDT), sunset 7:42pm (PDT) at Springfield, OH (12:59 hours daylight)
in the agenda view.
What I would like it to do is something more like,
6:43am........ Sunrise (PDT) (12:59 hours daylight)
8:00am........ ----------------
10:00am........ ----------------
12:00pm........ ----------------
2:00pm........ ----------------
4:00pm........ ----------------
5:51pm........ now - - - - - - - - - - - - - - - - - - - - - - - - -
6:00pm........ ----------------
7:42pm........ Sunset (PDT) (12:59 hours daylight)
Where the data is split into the two times, rather than all written only at the sunrise time.
Bonus:
a snippet so that C-c a d will give you a nice day agenda view:
(setq org-agenda-custom-commands
'(("d" "day's agenda"
agenda ""
(
(org-agenda-files '("/e/org/agendatest.org"))
(org-agenda-prefix-format "%t %s")
(org-agenda-span 'day)
(org-agenda-timegrid-use-ampm t)
)
)
))
In the last paragraph of my question (eighth paragraph?), I lied, (require 'solar) was the answer... I did not try it hard enough as it didn't respond to auto-completion.
The scenic route to the answer took me to these distractions:
Maintaining Emacs autoload files for user-installed elisp?
elisp - Where should I add autoload cookies in my Emacs Lisp package? Is there a definitive guide?
C-h i g (elisp) Autoload
a search through the lisp directory for any update-file-autoloads
I did some Edebugging wondering if anything I was using had an autoload cookie (not completely understanding it yet), but then noticed there was a (provide 'solar) at the end of solar.el, so then I figured (require 'solar) had to work. It did and that was that.
From the code in my question, I remove the autoload cookies (not sure if that's necessary), and add (require 'solar), and it works! Enjoy!
Related
When I am in calendar and want to jump to the entry in org-agenda, I press 'c'.
Would it be possible to have the DAY view as the standard so when I jump I only see one day, and not the whole week which is more cluttered? Thanks!
You can do it in various ways. In the spirit of "teaching someone to fish", the first thing that you have to do is figure out what c in the calendar does. So open the calendar, place the cursor on a date and say C-h c c which will tell you that c in the calendar runs the command
org-calendar-goto-agenda. So the next step is to do C-h f org-calendar-goto-agenda and read its documentation. Then click on the link in that documentation to go to the code and read it.
So here are three alternatives:
The code of the function is pretty simple:
(defun org-calendar-goto-agenda ()
"Compute the Org agenda for the calendar date displayed at the cursor.
This is a command t (org-agenda-span 1))hat has to be installed in `calendar-mode-map'."
(interactive)
;; Temporarily disable sticky agenda since user clearly wants to
;; refresh view anyway.
(let ((org-agenda-buffer-tmp-name "*Org Agenda(a)*")
(org-agenda-sticky nil))
(org-agenda-list nil (calendar-absolute-from-gregorian
(calendar-cursor-to-date))
nil)))
You can modify the function to make the agenda view to be just one day, by setting the variable org-agenda-span to 1 locally:
(defun org-calendar-goto-agenda ()
"Compute the Org agenda for the calendar date displayed at the cursor.
This is a command t (org-agenda-span 1))hat has to be installed in `calendar-mode-map'."
(interactive)
;; Temporarily disable sticky agenda since user clearly wants to
;; refresh view anyway.
(let ((org-agenda-buffer-tmp-name "*Org Agenda(a)*")
(org-agenda-sticky nil)
(org-agenda-span 1))
(org-agenda-list nil (calendar-absolute-from-gregorian
(calendar-cursor-to-date))
nil)))
The trouble with this is that you have now forked the Org mode code and you will need to make this change to the code every time you update. That might be appropriate in some cases, but you should avoid it if at all possible.
You can define your own command and bind it to c in the calendar:
(defun my-org-calendar-goto-agenda ()
(interactive)
(let ((org-agenda-span 1))
(org-calendar-goto-agenda)))
(define-key calendar-mode-map (kbd "c") #'my-org-calendar-goto-agenda)
That works pretty well. You have to remember to undo the keymap modification to restore the original state if you want to try the third method below:
(define-key calendar-mode-map (kbd "c") #'org-calendar-goto-agenda)
You can add an advice to the org-calendar-goto-agenda command. This is a formalization of the second option above: it is slightly more "advanced" but it is a powerful way of dealing with questions of this sort: you are given a function that does almost what you want and you want to customize it so that it does exactly what you want. This method cannot always be used, but it can be used in this case and it provides a simple example:
(defun org-calendar-goto-agenda-around (orig-fun &rest args)
(let ((org-agenda-span 1))
(apply orig-fun args)))
(advice-add 'org-calendar-goto-agenda :around #'org-calendar-goto-agenda-around)
The idea is that you define an around function that does something around the call of the original function (in this case, it let-binds org-agenda-span to 1, just like the other methods and then calls the original function in this new let-bound environment). Then we modify the original function by installing the advice. If you do C-h f org-calendar-goto-agenda now, you will see that there is some added information:
This function has :around advice: ‘org-calendar-goto-agenda-around’.
This is probably the cleanest way to do what you want. You can also easily remove the advice with
(advice-remove 'org-calendar-goto-agenda #'org-calendar-goto-agenda-around)
so this method allows you to experiment fairly easily. This is my recommended solution.
I am using emacs 25.3.2 and org-mode 8.2.10. For some reason whenever I try to add a new diary entry in the org-Agenda I get this error message
org-agenda-diary-entry: Wrong type argument: commandp, insert-diary-entryInvalid face reference: nil
I have tried all types: day weekly, monthly and none worked. If I try to add the entry from emacs calendar it works fine (I end up in the diary file).
Relevant entries from my .emacs file are
;; Calendar/diary controls
;; This code has to be at the beggining to ensure an european date-style format
(add-hook 'calendar-load-hook
(lambda ()
(calendar-set-date-style 'european)))
(set-default 'truncate-lines t)
(add-hook 'diary-list-entries-hook 'diary-include-other-diary-files)
(add-hook 'diary-mark-entries-hook 'diary-mark-included-diary-files)
and
(setq org-agenda-include-diary t) ; agenda stuff
(setq org-agenda-files (list "~/org/NOTES.org")) ; agenda stuff
I have searched online, and the best answer I found was how to describe the functions commandp and org-agenda-diary-entry, but I couldn't find anyone with the same type of issue and I cannot understand what the problem is. Thank you in advance for any help.
This bug was caused by a renaming of diary functions. It was fixed in org-mode by the following commit:
commit 685d3ba4af8cbb45447bb0b6c215a7135e7ed77e
Author: Florian Beck <fb#miszellen.de>
Date: Sat Nov 15 19:14:27 2014 +0100
Replace obsolete aliases of calendar functions
* lisp/org-agenda.el (org-agenda-diary-entry): Replace obsolete functions.
diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el
index 7632e1b17..b4b0a24de 100644
--- a/lisp/org-agenda.el
+++ b/lisp/org-agenda.el
## -9473,13 +9473,13 ## entries in that Org-mode file."
(message "Diary entry: [d]ay [w]eekly [m]onthly [y]early [a]nniversary [b]lock [c]yclic")
(read-char-exclusive)))
(cmd (cdr (assoc char
- '((?d . insert-diary-entry)
...
+ '((?d . diary-insert-entry)
...
8.2.10 dates from October 2014, just before this fix went in. I think it's time to upgrade.
I use the Org-Mode diary to keep a record of my upcoming appointments.
In my diary.org file I could have an entry that looks something like the following:
*** 2014-10-31 Friday
**** 9:30 Take dog to vet
<2014-10-31 Fri>
Now imagine I need to reschedule my vet appointment. Is there a quick way (i.e. some Org-Mode command) to refile the appropriate heading within the same file but under a different date?
I don't see a builtin function that does this, but it sounds pretty useful. This function is really just a simplification of the code in org-archive.el that archives to datetrees. If you instead want to refile based on SCHEDULED, DEADLINE or some other property, just change "TIMESTAMP" to the property you want.
(defun org-refile-to-datetree ()
"Refile a subtree to a datetree corresponding to it's timestamp."
(interactive)
(let* ((datetree-date (org-entry-get nil "TIMESTAMP" t))
(date (org-date-to-gregorian datetree-date)))
(when date
(save-excursion
(org-cut-subtree)
(org-datetree-find-date-create date)
(org-narrow-to-subtree)
(show-subtree)
(org-end-of-subtree t)
(newline)
(goto-char (point-max))
(org-paste-subtree 4)
(widen)
)
)
))
Use org-refile with C-c C-w. This will allow you to refile it to the rescheduled date. Also be sure to check out the variables org-refile-use-outline-path (nil by default) and org-outline-path-complete-in-steps.
I would also recommend using the full power of org-mode dates so they show up in the agenda (C-a) appropriately. For example:
* Take Dog to Vet
<2014-10-31 Fri 09:30>--<2014-10-31 Fri 10:30>
I regularly mark habits as DONE the day after the activity was done. The habits module automatically updates a bunch of dates in the metadata when I do this, but the date is always today. So then, I have to manually edit the SCHEDULED, LOGBOOK, and LAST_REPEAT states.
Is there a way to mark a habit as DONE for a day in the past? So instead of doing C-c C-t d (I have "d" set up as "DONE") I could get a prompt which asked me for a date.
I've wanted to do this for some time, too, and your question inspired me to finally figure it out.
There is a function called "org-todo-yesterday." By default, it's not mapped to any keys, but you could always call it with M-x org-todo-yesterday (or map it if you're using it a lot). For me, it breaks because it calls "third" which isn't a defined function in my install.
For a more generic function that prompts us for the date and marks things as done at that time, we can add this function (inspired by org-todo-yesterday) to our emacs init file. It will act as if you finished things at 23:59 on the target date, which is hopefully good enough.
(defun dk/org-todo-custom-date (&optional arg)
"Like org-todo-yesterday, but prompt the user for a date. The time
of change will be 23:59 on that day"
(interactive "P")
(let* ((hour (nth 2 (decode-time
(org-current-time))))
(daysback (- (date-to-day (current-time-string)) (org-time-string-to-absolute (org-read-date))))
(org-extend-today-until (+ 1 (* 24 (- daysback 1)) hour))
(org-use-effective-time t)) ; use the adjusted timestamp for logging
(if (eq major-mode 'org-agenda-mode)
(org-agenda-todo arg)
(org-todo arg))))
Will this work? This simply completes the habit and reschedule it for today
(defun org-todo-yesterday2 (&optional arg)
(interactive "P")
(org-todo-yesterday arg)
(org-schedule arg (format-time-string "%Y-%m-%d")
)
)
I have the following function definition:
(defun nth (n)
(format
(concat
"%d"
(if (memq n '(11 12 13)) "th"
(let ((last-digit (% n 10)))
(case last-digit
(1 "st")
(2 "nd")
(3 "rd")
(otherwise "th"))))) n))
and I'd like to be able to use it in format-time-string.
Normally, I would look at the source of the function, but this one is defined in the C source code. (I would presume this precludes hooking something onto it, but I stand to be corrected.)
How can I add another format specifier (say, %o) that will apply nth to the appropriate argument?
Desired usage:
(format-time-string "%A, %B %o, %T (%z)" (seconds-to-time 1250553600))
=> "Monday, August 17th, 20:00:00 (-0400)"
Here is what you want to do. Stefan and Drew already gave some important remarks (don't overwrite nth and look at the info-files of emacs-lisp/advising functions).
(defun ordinal (n)
"Special day of month format."
(format
(concat
"%d"
(if (memq n '(11 12 13)) "th"
(let ((last-digit (% n 10)))
(case last-digit
(1 "st")
(2 "nd")
(3 "rd")
(otherwise "th"))))) n))
(defadvice format-time-string (before ordinal activate)
"Add ordinal to %d."
(let ((day (nth 3 (decode-time (or time (current-time))))))
(setq format-string
(replace-regexp-in-string "%o"
(ordinal day)
format-string))))
Notes:
I did not handle the UNIVERSAL argument
The hack does not work when format-time-string is called from C (as you can read in the manual).
AFAIK you're out of luck: format-time-string does not offer any way to do that.
You might be able to work around this by using something like:
(let ((ti (seconds-to-time 1250553600)))
(format-time-string (concat "%A, %B " (my-nth (format-time-string "%d" ti)) ", %T (%z)") ti))
This said, I've always been told that "August 17th" is wrong: you're supposed to write "August 17" which is pronounced "August the seventheenth".
One more thing: nth is a predefined core function. Better not overwrite it with your own completely different definition.
To add to what Stefan said ("You're out of luck") --
format-time-string is a built-in, but you can advise built-ins too. However, since the kind of surgery you want to do would dig into the bowels of the definition (which you cannot do), you would need to in fact replace the definition of format-time-string in the defadvice, i.e., not use ad-do-it at all.
In other words, one way or the other (defun or defadvice) you would need to completely redefine the function, in Lisp. Which is about the same as saying "You're out of luck."