I'm using the Dvorak keyboard layout and have swapped C-x and C-t, because I find it easier to type on Dvorak.

(define-key key-translation-map [?\C-x] [?\C-t])
(define-key key-translation-map [?\C-t] [?\C-x])

I'm also using god-mode, though, so I'd like x to be swapped with t when in god-local-mode, such that typing x results in C-t and typing t results in C-x. I have a very crude solution using keyboard-translate:

(defun my-god-swap-on ()
  (keyboard-translate ?x ?t)
  (keyboard-translate ?t ?x))

(defun my-god-swap-off ()
  (aset keyboard-translate-table ?t nil)
  (aset keyboard-translate-table ?x nil))

(add-hook 'god-mode-enabled-hook 'my-god-swap-on)
(add-hook 'god-mode-disabled-hook 'my-god-swap-off)

Although this works it is extremely inelegant as it modifies global state. If I switched to a buffer where god-mode is not enabled, the translation would still be active. I could add yet another hook for buffer switching to make sure that the translation is disabled when god-local-mode is inactive in the current buffer, but this seems like a very ugly mitigation technique.

Is it possible to restrict the effects to a keyboard translation to only a single minor mode?

If this is not possible, is there another more elegant way I could swap keys in god-mode? I see that god-mode rewires all keys bound to self-insert-command to god-mode-self-insert, but this uses this-command-keys-vector to determine with what key it was called and I cannot seem to shadow the definition of this-command-keys-vector with a let binding to swap x and t in a wrapper before passing control to god-mode-self-insert.

3 Answers 3


FWIW, here's your solution in 'before' advice form:

n.b. this is untested (I don't use god-mode).

(defadvice god-mode-lookup-key-sequence (before my-swap-x-t)
  "Swap ?x and ?t KEY arguments."
  (case (ad-get-arg 0)
    (?x (ad-set-arg 0 ?t))
    (?t (ad-set-arg 0 ?x))))
(ad-activate 'god-mode-lookup-key-sequence)
  • This is exactly what I was looking for. I knew there had to be a way to achieve this by advising the function, but I didn't know exactly how to do it. Thanks!
    – user2005
    Nov 1, 2014 at 4:01

The general solution is to modify god-mode so that it takes the key translation mechanism into account. This way, it will work with any key translation you've set or will set (C-h causing DEL is another common one).

I had the same problem so I filed a bug and wrote a patch that fixes it, at least for C- combinations. It changes the last sexp of key-string-after-consuming-key from:

(if key-string-so-far
    (concat key-string-so-far " " next-modifier next-key)
  (concat next-modifier next-key))


(let* ((next-literal-key-string (concat next-modifier next-key))
       (translation (lookup-key key-translation-map
                                (read-kbd-macro next-literal-key-string t)))
        (if translation
            (format-kbd-macro translation)

  (if key-string-so-far
      (concat key-string-so-far " " next-interpreted-key-string)

I now use this:

(defun original-god-mode-lookup-key-sequence (&optional key key-string-so-far) ())
(fset 'original-god-mode-lookup-key-sequence (symbol-function 'god-mode-lookup-key-sequence))

(defun god-mode-lookup-key-sequence (&optional key key-string-so-far)
  "Wrap original-god-mode-lookup-key-sequence.  Swap x and t."
  (case key
    (?x (original-god-mode-lookup-key-sequence ?t key-string-so-far))
    (?t (original-god-mode-lookup-key-sequence ?x key-string-so-far))
    (t (original-god-mode-lookup-key-sequence key key-string-so-far))))

It makes a copy of god-mode-lookup-key-sequence as original-god-mode-lookup-key-sequence and then overrides it with a wrapper that performs the swap for x and t. It's not elegant, but at least it won't leak.

  • Maybe (defalias 'bar (symbol-function 'foo)) would be a simpler start?
    – phils
    Oct 31, 2014 at 23:55
  • 1
    It seems like a use-case for some 'before' advice, though. It could just change the argument if necessary, before the original function executes.
    – phils
    Oct 31, 2014 at 23:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.