My emacs godmode indicators and Elisp equivalent of defonce

Table of Contents

Godmode

Godmode1 is a handy mode that aims to ease the stress on otherwise stretching fingers by basically holding C- or M-C for you. A common difficulty is knowing if you are currently in Godmode or not, especially since I no longer use doom-modeline2. The first solution that is recommended is to change your cursor depending on whether you are in the mode or not. For years, back when I used godmode more rarely, this was enough. Now that I am making heavier use, though, this runs in to problems with places that make similar cursor changes and are hard to change, like Magit and my Org Agenda. So my new goal was to make a toggle that would change both my cursor and my mode-line, but allow me to return to my previous settings again on toggle, without hard-coding any values (since at times I make ready changes to my themes, and don’t want to be changing the code whenever I do this).

No Defonce

In Clojure there is a useful function “defonce” that only sets a value to a variable if it has not been set. My current elisp effort is to toggle an emacs face value between a new value and back again to its original value, whatever it is. I get lost with all the setq, defcustom, set-face*, and other variations of setting things that elisp has… it definitely seems like one of those things where the language has some lack of design thought.3

I came up a sloppy workaround using the dubiously named bound-and-true-p and with or, below, but there is actually a built-in way, farther below.

(defun tsa/god-cursor ()    
  (setq tsa/cursor-bg
        (or (bound-and-true-p tsa/cursor-bg)
            (face-attribute 'cursor :background)))
  (if (or god-local-mode buffer-read-only)
      (progn 
        (setq cursor-type 'hbar)
        (set-face-attribute 'cursor nil
                            :background "#11ff33"))
    (progn 
        (setq cursor-type 'box)
        (set-face-attribute 'cursor nil
                            :background tsa/cursor-bg))))

Answer: defvar

Ah! The solution we want is defvar, which does exactly what we want. Credit to redditor with a mythically great 4handle for this tip.

(defun tsa/god-cursor ()    
  (defvar tsa/cursor-bg (face-attribute 'cursor :background))

  (if (or god-local-mode buffer-read-only)
      (progn 
        (setq cursor-type 'hbar)
        (set-face-attribute 'cursor nil
                            :background "#11ff33"))
    (progn 
        (setq cursor-type 'box)
        (set-face-attribute 'cursor nil
                            :background tsa/cursor-bg))))

(defun tsa/god-update-mode-line ()
  (defvar tsa/fg  (face-attribute 'mode-line :foreground))
  (defvar tsa/bg  (face-attribute 'mode-line :background))
  (defvar tsa/fgi (face-attribute 'mode-line-inactive :foreground))
  (defvar tsa/bgi (face-attribute 'mode-line-inactive :background))
  (cond
   (god-local-mode
    (set-face-attribute 'mode-line nil
                        :foreground "#604000"
                        :background "#fff29a")
    (set-face-attribute 'mode-line-inactive nil
                        :foreground "#3f3000"
                        :background "#fff3da"))
   (t
    (set-face-attribute 'mode-line nil
                        :foreground tsa/fg
                        :background tsa/bg)
    (set-face-attribute 'mode-line-inactive nil
                        :foreground tsa/fgi
                        :background tsa/bgi))))

(defun tsa/reflect-god-mode ()
  "Make visual changes representing whether God is enabled"
  (interactive "P")
  (tsa/god-update-mode-line)
  (tsa/god-cursor))

  (add-hook 'post-command-hook 'tsa/reflect-god-mode)

Footnotes

1 Godmode can be found on github: https://github.com/emacsorphanage/god-mode. It is a less involved solution to the RSI problem than the highly acclaimed total modal-instead-of-chords VIM rewrites based on EVIL, such as Spacemacs and Doom.

2 Doom-modeline, https://github.com/seagle0128/doom-modeline, is a very fully-featured, very attractive modeline solution. It does, however, include a whole lot of magic to make it work, and I quit it when I had emacs-crashing changes due to one of its hundreds of features misbehaving. I still consult it, however, for good ideas.

3 original solicitation for help here: https://www.reddit.com/r/emacs/comments/peklms/elisp_equivalent_to_defonce_for_toggling_variables/

4 TheDrownedKraken, at https://www.reddit.com/r/emacs/comments/peklms/elisp_equivalent_to_defonce_for_toggling_variables/hay0zrk?utm_source=share&utm_medium=web2x&context=3

Tory Anderson avatar
Tory Anderson
Full-time Web App Engineer, Digital Humanist, Researcher, Computer Psychologist