Emacs: Xah Fly Keys Customization

By Xah Lee. Date: . Last updated: .

This page shows how to customize Emacs: Xah Fly Keys.

Add/Change Command Mode Activation Key

Here's a example of how to set the command mode activation key.

(require 'xah-fly-keys)
(xah-fly-keys-set-layout "qwerty")

;; make F4 activate command mode
(global-set-key (kbd "<f4>") 'xah-fly-command-mode-activate)

The (kbd "<f4>") is the F4 key. For other key's syntax, see Emacs: How to Define Keys

On a typical PC keyboard, i recommend you make the CapsLock key to send Home key signal for activating command mode.

(Home key by default will activate command mode.)

How to Make the CapsLock Key do Home Key

How to Make the CapsLock Key do Home Key

Add/Change Insert Mode Activation Key

If you have a nice keyboard with extra thumb keys such as Kinesis Advantage2 Keyboard or a Foot Pedal [see Kinesis Savant Elite2 Foot Pedal] , you can set a dedicated key to switch to insert mode.

For example:

(require 'xah-fly-keys)
(xah-fly-keys-set-layout "qwerty")

;; make End key to activate insertion mode
(global-set-key (kbd "<end>") 'xah-fly-insert-mode-activate)

Add Key to Toggle Command/Insert

(require 'xah-fly-keys)
(xah-fly-keys-set-layout "qwerty")

;; make F4 toggle command/insert mode
(global-set-key (kbd "<f4>") 'xah-fly-mode-toggle)

Disable Changes to Control Key Shortcuts

xah-fly-keys supports standard Control key shortcuts such as Ctrl+o open, Ctrl+w close Ctrl+v paste Ctrl+z undo, etc.

You can disable them by:

;; must come before loading xah-fly-keys
(setq xah-fly-use-control-key nil)

(require 'xah-fly-keys)

when disabled, no control binding will be touched by xah-fly-keys.

Disable Changes to Meta Key Shortcuts

The only meta keybinding xah-fly-keys bind is Alt+Space as a extra key for xah-fly-command-mode-activate (so, out of the box, it's convenient for people who haven't bothered setting CapsLock.)

You can disable this by:

(setq xah-fly-use-meta-key nil) ; must come before loading xah-fly-keys

Make Escape Key Do C-g

You can make the Escape key do emacs's Ctrl+g.

(define-key key-translation-map (kbd "ESC") (kbd "C-g"))

Note: this will work 99% of time. When it doesn't work, just press Ctrl+g. (the only case i know it doesn't work is when you quit emacs, and emacs says there are unsaved file and if you still want to quit, and pressing Escape to cancel quit doesn't work, but Ctrl+g works.)

Note: for text terminal users, escape key is critical, especially if you do not have Meta key setup. Because Meta+x can be typed by Escape x. So, if you remap Escape, you lose that.

Change Global Leader Key

You can set a global leader key, so you don't have to switch to command mode first. This is especially useful if you have a Foot Pedal [see Kinesis Savant Elite2 Foot Pedal] or extra thumb keys.

(global-set-key (kbd "<f9>") xah-fly-leader-key-map)
; must come after loading xah-fly-keys

Add or Change Leader Key Sequence

Here's a example of add/change a 3-key sequence.

Put this in your init, after loading xah-fly-keys:

;; make xah-fly-keys qwerty 【leader d f】 do command calendar
(define-key xah-fly-e-keymap (kbd "u") 'calendar)
;; qwerty 【leader d f】 is dvorak 【leader e u】
;; this overwrites existing command or keymap of 【leader e u】

Note, xah-fly-keys's key syntax is with dvorak keys. So, if you are using qwerty, you need to first find out what's the dvorak key.

For example, let's say you are a qwerty user, and you want to add/change the qwerty key sequence 【leader d f】 to call command calendar.

The steps are:

Find out what's qwerty d in dvorak. (qwerty d is dvorak e) [see QWERTY Dvorak Layout Conversion Table]

in xah-fly-keys.el file, find the keymap name that corresponds to the leader key map for the dvorak key, by finding it in the definition of xah-fly-leader-key-map.

Here's the short version of xah-fly-leader-key-map definition:

(xah-fly--define-keys
 (define-prefix-command 'xah-fly-leader-key-map)
 '(
   ;; ...

   ("1" . nil)
   ("2" . nil)
   ("3" . delete-window)
   ("4" . split-window-right)
   ("5" . balance-windows)
   ("6" . nil)
   ("7" . nil)
   ("8" . nil)
   ("9" . ispell-word)
   ("0" . nil)

   ("a" . mark-whole-buffer)
   ("b" . end-of-buffer)
   ("c" . xah-fly-c-keymap)
   ("d" . beginning-of-buffer)
   ("e" . xah-fly-e-keymap)
   ("f" . xah-search-current-word)
   ("g" . isearch-forward)
   ("h" . xah-fly-h-keymap)
   ("i" . xah-copy-file-path)
   ("j" . xah-copy-all-or-region)
   ("k" . xah-paste-or-paste-previous)
   ("l" . recenter-top-bottom)
   ("m" . dired-jump)
   ("n" . xah-fly-n-keymap)
   ("o" . exchange-point-and-mark)
   ("p" . query-replace)
   ("q" . xah-cut-all-or-region)
   ("r" . xah-fly-r-keymap)
   ("s" . save-buffer)
   ("t" . xah-fly-t-keymap)
   ("u" . switch-to-buffer)
   ("v" . nil)
   ("w" . xah-fly-w-keymap)
   ("x" . nil)
   ("y" . xah-show-kill-ring)
   ("z" . nil)
   ;;
   ))

Since you are looking for qwerty 【leader d】, and that's dvorak e, so you look at the entry for e, and the result is xah-fly-e-keymap

now, since you want add/change qwerty 【leader d f】, now find out what's qwerty f in dvorak. (qwerty f is dvorak u)

Now you know, the key you want to change is u, in keymap xah-fly-e-keymap.

So, the code you want to put in init file is:

(xah-fly-keys 1)

;; make xah-fly-keys qwerty 【leader d f】 do calendar
(define-key xah-fly-e-keymap (kbd "u") 'calendar)
;; this is dvorak 【leader e u】

Creating a Whole Keymap

Here's a example of creating a whole keymap.

Let's say you want

and anything after 【leader 8】 would be your personal keys.

Here's the code:

(define-prefix-command 'my-keymap)

(define-key my-keymap (kbd "SPC") 'cmd1)
(define-key my-keymap (kbd "3") 'cmd2)
(define-key my-keymap (kbd "a") 'cmd3)

;; make xah-fly-keys 【leader 8】 as prefix for my-keymap
(define-key xah-fly-leader-key-map (kbd "8") my-keymap)

;; so now,
;; 【leader 8 space】 is cmd1
;; 【leader 8 3】 is cmd2
;; 【leader 8 a】 is cmd3
;; etc

If you want your letters to be translated according to xah-fly-keys-set-layout, then do it this way:

(xah-fly--define-keys
 ;; create a keymap my-keymap
 (define-prefix-command 'my-keymap)
 '(
   ("SPC" . cmd1)
   ("3" . cmd2)
   ("a" . cmd3)
   ;;
   ))

;; make xah-fly-keys 【leader 8】 as prefix for my-keymap
(xah-fly--define-keys
 (define-prefix-command 'xah-fly-leader-key-map)
 '(
   ("8" . my-keymap)
   ;;
   ))

;; all letters are dvorak. They get translated to whatever your xah-fly-keys-set-layout is set to

Change execute-extended-command (M-x) of Your Choice

Add one of the following, before loading xah fly keys:

(setq xah-fly-M-x-command 'execute-extended-command)
(setq xah-fly-M-x-command 'smex)
(setq xah-fly-M-x-command 'helm-M-x)
(setq xah-fly-M-x-command 'counsel-M-x)

Add Hook

There are these hooks you can use:

xah-fly-command-mode-activate-hook
Hook variable. Value should be a list of function symbols. When command mode is activated, these functions are called after activation.
xah-fly-insert-mode-activate-hook
Hook variable. Value should be a list of function symbols. When insert mode is activated, these functions are called after activation.

Add/Change Keys in Xah Fly Keys Command Mode

(defun my-config-xah-fly-key ()
  "Modify keys for xah fly key command mode keys
To be added to `xah-fly-command-mode-activate-hook'"
  (interactive)
  (define-key xah-fly-key-map (kbd "1") 'my-command-abc)
  ;; more here
  )

(add-hook 'xah-fly-command-mode-activate-hook 'my-config-xah-fly-key)

Add/Change Keys in Xah Fly Keys Insert Mode

(defun my-xfk-addon-insert ()
  "Modify keys for xah fly key command mode keys
To be added to `xah-fly-insert-mode-activate-hook'"
  (interactive)
  (define-key xah-fly-key-map (kbd "2") 'my-command-xyz)
  ;; more here
  )

(add-hook 'xah-fly-insert-mode-activate-hook 'my-xfk-addon-insert)

Alternatively, just modify xah-fly-keys.el directly. Modify the function xah-fly-command-mode-init. (or, just copy the whole function out, modify it, put in your init after the code for loading xah-fly-keys.)

But i recommend that you do not change it. Just stick to it. Because, one thing about creating a keybinding system is that there's a lot habit to overcome. I've been changing keys every month for over 10 years. Each time, it's extremely painful, and with strong desire to go back. And, once you start to explore keys, you often become biased towards your CURRENT workflow or work. So, you get into a situation that you change every few months, when project changes, or your choice of major mode changes, and in particular, when your keyboard changes. Also, if you don't have a year or two dedicated study about keybinding system, you create a key system that you think must be logically flawless, but actually just to your habit, your hands, your current keyboard, and your workflow, not to someone else.

xah-fly-keys's keybinding choices is not scientifically the best, because that is basically an illusive concept, but is near optimal.

if you are interested in keybinding efficiency research, be sure to read:

Setup Major Mode Custom Keys

Here's the best way to create leader key set for any major mode, so you don't have to press Ctrl+c.

First, get a list of the major mode's commands you want. Then, decide for a leader key to call them. For example, you might have F4 a and F4 b and F4 c.

Find out what's the keymap name in the major mode. (look at its source code), then do as follows. You can do this for org mode, magit mode. Here's a example with go-mode.el.

;; example of adding a leader key map to golang mode
(when (fboundp 'go-mode)

  (defun xah-config-go-mode ()
    "config go-mode. Version 2021-01-15"
    (interactive)
    (progn
      ;; create a keymap
      (define-prefix-command 'xah-golang-leader-map)
      ;; add keys to it
      (define-key xah-golang-leader-map (kbd "c") 'xah-gofmt)
      (define-key xah-golang-leader-map (kbd "j") 'godef-jump)
      ;; add more of the major mode key/command here
      )
    ;; modify the major mode's key map, so that a key becomes your leader key
    (define-key go-mode-map (kbd "<delete>") xah-golang-leader-map)
    ;;
    )

  (add-hook 'go-mode-hook 'xah-config-go-mode))

those asking about how to setup key in magit, the above is a good solution

Make a Key do x Depending on Major Mode

You can have a key do different things depending on what's the current major mode.

The most simple way, is to write a wrapper command. The wrapper command will check current mode and call different commands. Then, bind the wrapper command to a key in command mode.

Here's a example of a wrapper command.

When in command mode, suppose you want key 5 to call:

(defun my-wrapper ()
  "call different commands depending on what's current major mode."
  (interactive)
  (cond
   ((string-equal major-mode "x1-mode") (x1-cmd))
   ((string-equal major-mode "x2-mode") (x2-cmd))
   ;; more major-mode checking here

   ;; if nothing match, do nothing
   (t nil)))

Then, just follow the keybinding instruction mentioned on this page to set this command to a key.

Change Color for Mode State

You can make the mode state more obvious, by changing current line highlight. e.g.

(defun my-highlight-line-on () (global-hl-line-mode 1))
(defun my-highlight-line-off () (global-hl-line-mode 0))

(add-hook 'xah-fly-command-mode-activate-hook 'my-highlight-line-on)
(add-hook 'xah-fly-insert-mode-activate-hook  'my-highlight-line-off)

You can also change background color.

(defun my-xfk-command-color () (set-background-color "honeydew"))
(defun my-xfk-insert-color () (set-background-color "white"))

(add-hook 'xah-fly-command-mode-activate-hook 'my-xfk-command-color)
(add-hook 'xah-fly-insert-mode-activate-hook  'my-xfk-insert-color)

Russian Layout Addon

Emacs: Xah Fly Keys for Russian Layout

Start in Command Mode When Emacs Daemon Starts

;; 2021-03-10 this fix the problem of: when emacs start as daemon, xah fly keys is not in command mode. thx to David Wilson (daviwil)

(defun my/server-fix-up()
  "Make sure 'xah-fly-keys' is starting in command-mode.

https://github.com/xahlee/xah-fly-keys/issues/103
https://github.com/daviwil/emacs-from-scratch/blob/master/show-notes/Emacs-Tips-08.org#configuring-the-ui-for-new-frames"
  (xah-fly-keys-set-layout "dvorak")
  (xah-fly-keys t))

(if (daemonp)
    (add-hook 'server-after-make-frame-hook 'my/server-fix-up))

Advanced Customization, How Does xah-fly-keys Work

If you look at the file, in 10 minutes you should have a basic idea. The code is simple.

There's these 2 important variables:

xah-fly-key-map
Variable. Value is a elisp keymap. This keymap is used for command mode and insertion mode.
xah-fly-leader-key-map
Variable. Value is a elisp keymap. It's the leader keys sequence.

(note: in elisp, keymap is just a list of key/command pairs. The command part can be another keymap, in which case, it is a key sequence.)

There's these 2 important commands:

xah-fly-command-mode-init
set keybinding for command mode. Basically, it just modify xah-fly-key-map.
xah-fly-insert-mode-init
set keybinding for insert mode. Basically, it just modify xah-fly-key-map, by setting all keys to nil (which means, do emacs default, usually means they do self-insert-command).

xah-fly-command-mode-init looks like this:

(defun xah-fly-command-mode-init ()
  (xah-fly--define-keys
   xah-fly-key-map
   '(

     ("a" . execute-extended-command)
     ("b" . isearch-forward)
     ("c" . previous-line)
     ;; more here

     )))

The "a" "b" "c" there are Dvorak keys.

The function xah-fly--define-keys takes care of translating Dvorak to QWERTY, if user has qwery set (xah-fly-keys-set-layout 'qwerty) .

xah-fly-insert-mode-init looks like this:

(defun xah-fly-insert-mode-init ()
  (xah-fly--define-keys
   xah-fly-key-map
   '(

     ("a" . nil)
     ("b" . nil)
     ("c" . nil)

     ;; more
     )))

xah-fly-leader-key-map looks like this:

(xah-fly--define-keys
 (define-prefix-command 'xah-fly-leader-key-map)
 '(

   ("a" . mark-whole-buffer)
   ("b" . end-of-buffer)
   ("c" . xah-fly-c-keymap)
   ("d" . beginning-of-buffer)
   ("e" . xah-fly-e-keymap)
   ("f" . xah-search-current-word)

   ;; more
   ))

For example, the line ("b" . end-of-buffer) means press leader b will call end-of-buffer.

Note: All keys in the file are Dvorak keys. For example, if you see ("b" . isearch-forward), that's Dvorak b, and when user has QWERTY set, that b is automatically translated to n.

back to Emacs: Xah Fly Keys

Xah Fly Keys