On Emacs Theme Precedence

I’ve written before about themes in Emacs, and in particular about using them for something other than just colors. And naturally, you can have more than one theme active at the same time. So what happens when you have a variable set in two active themes?

The list of enabled themes is the custom-enabled-themes variable. And rather intuitively, earlier ones override later ones. Normally, (load-theme 'mytheme) prepends mytheme to custom-enabled-themes, so the settings you just loaded take effect, and last until you load some other settings on top of them, which makes sense.

There’s one twist, though: the settings you change through the “Customize Emacs” system also count as a theme, at least in some ways. If you look at the property list for a symbol defined in two themes, you might see something like

(symbol-plist 'my-var)

(theme-value
 ((user 'customized-value)
  (second-theme 'second-value)
  (first-theme 'first-value))
 saved-value 'customized-value
 ...)

This tells us that two themes, first-theme and second-theme, have both defined my-var, and set it to first-value and second-value respectively. The symbol’s value is resolved in the order in which it appears here, so second-value overrides first-value because it was loaded later.

But there’s that third user entry, which comes first, and thus overrides the other values: that comes from (customize-variable 'my-var). I find this counterintuitive, since I think of the customization variables as the defaults, to be overridden by themes.

The solution I came up with this was to define my own theme, arensb, to hold all of the settings that might be overridden by other themes, and to load it first. Actually, I already had this theme lying around to store my favorite colors and faces, but themes can hold other values, so it made sense to reuse it.

Monthly Reports with Org-Mode

Like a lot of people, I have to submit a monthly “bullet” report, listing the things I’ve done in the previous month.

Since I use Org-Mode for planning, scheduling, and organizing tool (or rather: I tend to throw a bunch of notes into a file and tell this love child of a day planner and a wiki to tell me what I should do next), I figured I should use that.

I could use the timeline feature (C-c a L), but that only works for the current buffer, and I want a report that covers all buffers, just like the agenda.

What I’ve done in the past is to use C-c a a to get the agenda view, go back a month, toggle displaying completed/archived/whatever items, and go through that to make my bullet list.

But I finally got around to encapsulating that into a single M-x bullet command:

; Make it easier to generate bullets for $BOSS
(defvar bullet-entry-types
  '(:closed)
  "Org-mode agenda types that we want to see in the monthly bullet report
See `org-agenda-entry-types'."
  )

(defun bullets ()
  "Show a list of achievements for the past month, for monthly reports.
Uses `org-agenda'.
"
  (interactive)
  (require 'org-agenda)
  ; All we're doing here, really, is calling `org-agenda' with
  ; arguments giving a start date and a number of days. But to do
  ; that, we need to figure out
  ; - the date of the first of last month
  ; - the number of days in last month
  (let* ((now (current-time))
	 ; Figure out when last month was. Assuming that I run this
	 ; close to the beginning of a month, then `now' minus two
	 ; weeks was some time in the previous month. We can use that
	 ; to extract the year and month that we're interested in.
	 (2weeks-ago
	  (time-subtract now
			 (days-to-time 14)))
	 ; We'll also need to know when the first of this month was,
	 ; to find out how long last month was. If today is the 12th
	 ; of the month, then the first of the month was `now' minus
	 ; 11 days.
	 (1st-of-this-month
	  (time-subtract now
			 (days-to-time
			  (- (nth 3 (decode-time now))
			     1))))
	 ; Ditto to find the first of last month.
	 (1st-of-last-month
	  (time-subtract 2weeks-ago
			 (days-to-time
			  (- (nth 3 (decode-time 2weeks-ago))
			     1))))
	 ; The length of last month is the difference (in days)
	 ; between the first of last month, and the first of this
	 ; month.
	 (len-last-month
	  (time-to-number-of-days
	   (time-subtract 1st-of-this-month
			  1st-of-last-month)))
	 (start-date (decode-time 1st-of-last-month))
	 (start-year (nth 5 start-date))	; Year number
	 (start-mon (nth 4 start-date))		; Month number
	 ; Restrict the agenda to only those types of entries we're
	 ; interested in. I think this takes advantage of dynamic
	 ; scoping, which is normally an abomination unto the lord,
	 ; but is useful here.
	 (org-agenda-entry-types bullet-entry-types)
	 )
    ; Create an agenda with the stuff we've prepared above
    (org-agenda-list nil
		     (format "%04d-%02d-01"
			     start-year
			     start-mon)
		     len-last-month)
    ))

I hope this proves useful to someone.