2016-09-12 Running Emacs commands not too often

If you are like me, you probably check your email too often. (Maybe nobody does anything stupid like that and it’s only me who has such unproductive habits, but I doubt it;-)…) As many Emacs users, I try to boost my productivity, and email notifications or checking email too often is a great way to actually worsen it.

What I do is hit Update way too often in my mu4e. To the point that I even wished it was possible that my email client said to me: /tsk tsk, you’ve just checked your email, you’d better wait another 30 minutes before you do that again!/

Ah, wait a moment.

So, here is my first (actually, second, but don’t tell anyone) stab at it.

(defun not-too-often-add-guard (fun interval)
  "Add a not-too-often guard to FUN with INTERVAL.
This means that if FUN gets called less than INTERVAL minutes
after last call, the use is asked whether s?he really wants to
run the command."
  (let* ((fun-name (symbol-name fun))
	  (intern (concat "not-too-often-interval-" fun-name)))
	  (intern (concat "not-too-often-last-time-" fun-name)))
	  (intern (concat "not-too-often-guard-" fun-name))))
    (set nto-int-sym interval)
    (set nto-last-time-sym 0)
    (fset nto-guard-sym (lambda (orig-fun &rest args)
			  (let ((elapsed (time-to-seconds
					   (symbol-value nto-last-time-sym)))))
			    (if (< elapsed
				   (* 60 (symbol-value nto-int-sym)))
				(cond ((y-or-n-p
					(format "You called %s %s minutes ago.  Are you sure you want to proceed? "
						fun-name (/ elapsed 60.0)))
				       (set nto-last-time-sym (current-time))
				       (apply orig-fun args))
			      (set nto-last-time-sym (current-time))
			      (apply orig-fun args)))))
    (put nto-guard-sym 'function-documentation
	  "Issue a warning if function `%s' is called less than %s minutes from last call."
	  fun-name interval))
    (advice-add fun :around nto-guard-sym)))

It works, and this is pretty much everything I can say about it. In fact, when I posted this to the Emacs ML, Michael Heerdegen suggested a nice improvement: a way to create generic functions (closures, in fact) which measure time since their last invocation. (If it were Java, it would be called GenericStopWatchMeasuringTimeFromLastInvocationFactory, I guess. I’d take the liberty of calling it create-stopwatch, though.) After some consideration, I decided that I won’t use that solution (even though it is nice). While I know that closures can serve to hide (“encapsulate”) the “private” variables (like not-too-often-interval-mu4e-update-mail-and-index) so that it is impossible (well, not really impossible, but difficult) to access them from outside code, this is not exactly what I want: I guess it is actually desirable for the user to be able to access those variables. And now that I think about it, it would even be better to use defcustom for them, so that the user could inspect them using the “Customize” interface.

It seems that my above code will need some refactoring… And while at that, I could (also per Michael’s suggestion) get rid of that set and fset stuff, and make my not-too-often-guard function into a macro. Then, I could utilize the backquoting mechanism to achieve a similar effect in a nicer way.

So, expect a follow-up to this post in some time!

CategoryBlog, CategoryEnglish, CategoryEmacs