Skip to content
forked from csmclaren/loco

A library and minor mode for entering key sequences

License

Notifications You must be signed in to change notification settings

emacsmirror/loco

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Loco  🚋

Loco is a package for Emacs. Loco lets you type any key sequence, including those requiring the modifiers Alt, Control, Hyper, Meta, or Super, without using any physical modifier keys except Shift. This makes it easy to enter key sequences that are complicated or use hard-to-reach keys.

Loco works by translating key sequences from one form to another. It does not redefine the standard key bindings, nor does it prevent you from defining your own. Learn the few rules of Loco and be productive immediately, using the key bindings you already know.

Loco does not preclude the use of physical modifier keys; it integrates well with them. There may even be some key bindings for which you find using physical modifier keys preferable. And it works equally well in the GUI or the terminal, working around the limitations of terminals that prevent the entry of certain keys, allowing you to move between both with ease.

When enabled, and using the default configuration, pressing S‑<return> will activate Loco.

In Emacs, S‑<return> means hold Shift then press Return.

Once activated, you can type any key sequence, taking advantage of the following special keys to help you avoid pressing any physical modifier keys:

  • j to apply the modifier Control to the next non‑special key;
  • k to apply the modifier Meta to the next non‑special key; or
  • l to open a menu that includes options to:
    • apply other modifiers (for example, Alt, Hyper, or Super) to the next non‑special key; or
    • enter the special keys themselves as the literal characters j, k, or l.
Examples (using the default configuration)
Typed Key Sequence Translated Key Sequence Command
j d C‑d delete‑char
k d M‑d kill‑word
j h i C‑h i info
j x j s C‑x C‑s save‑buffer

This is only a brief overview; see Usage for a detailed explanation.

The default configuration is not the only way to use Loco. Loco can be extensively customized with just a few lines of code. Many options are possible, including:

  • Changing the key bindings used to enable, disable, or activate Loco;
  • Changing the keys used while Loco reads a key sequence;
  • Configuring activation keys that also function as modifiers; and
  • Configuring activation keys that avoid modifiers completely.

Installation

From MELPA

To install Loco from MELPA (the easiest method), follow these steps:

  1. Modify your Emacs configuration

    Open your Emacs init file and complete the following steps:

    • Add the MELPA package archives

      (require 'package)
      (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
      (package-initialize)
    • Require loco

      (require 'loco)
    • Set the default key bindings and enable Loco in all buffers automatically on startup

      (loco-set-default-key-bindings)
      (global-loco-mode 1) ; Enable in all buffers

    To apply these changes, either restart Emacs or evaluate the modified sections of your configuration file.

From source

To install Loco from source, follow these steps:

  1. Clone the official repository from GitHub

    git clone https://github.com/csmclaren/loco.git
  2. Modify your Emacs configuration

    Open your Emacs init file and complete the following steps:

    • Add the load path and make sure to replace /path/to/loco with the path to your cloned repository

      (add-to-list 'load-path "/path/to/loco")
    • Require loco

      (require 'loco)
    • Set the default key bindings and enable Loco in all buffers automatically on startup

      (loco-set-default-key-bindings)
      (global-loco-mode 1) ; Enable in all buffers

    To apply these changes, either restart Emacs or evaluate the modified sections of your configuration file.

Usage

This section assumes that you have set the default key bindings for Loco, as recommended in Installation.

Keys, key sequences, and commands

When discussing keys, it is helpful to distinguish between physical keys and logical keys.

A physical key is what you press with your fingers on the keyboard. Some physical keys are called modifier keys. Emacs recognizes six modifier keys: Alt, Control, Hyper, Meta, Super, and Shift. Your keyboard may not have all of these keys. Furthermore, your keyboard might have different names for the modifier keys that it does have. For example, Super is called Command on Apple keyboards, and Meta is called Alt or Option on most modern keyboards. Most physical keys, like letters, numbers, and symbols, are non-modifier keys.

A logical key (or simply a key) is zero or more modifier keys pressed simultaneously with a non-modifier key. For example, holding down the physical Control key (written as C‑) while pressing the physical x key produces the key C‑x.

Key sequences are composed of one or more keys. For example, the key sequence C‑x s is composed of two keys: C‑x and s. Key sequences can be bound to commands. For example, C‑x s is bound to the command save-all-files.

By composing key sequences from keys, a large number of key sequences can be created from a small number of keys. This is similar to how an alphabet can be used to compose the words of a language: a large number of words can be created from a small number of letters.

How Loco is different

On modern keyboards, which typically have small or hard-to-reach modifier keys, or for people with RSI or other forms of limited mobility, pressing one or more keys simultaneously can be difficult or even painful. This is especially true when two or more modifier keys are required to complete a single key, or if a certain modifier key is particularly hard to reach.

To help solve this problem, Loco extends the idea of composing key sequences from keys to the level of the key itself. In Loco, logical keys can be composed of one or more physical keys pressed one after the other, not simultaneously. Additionally, Loco uses certain easy-to-reach non-modifier keys (or special keys) to assist you with key entry.

Enabling, disabling, and activating Loco

Loco works by adding a minor mode to Emacs. This mode can be enabled or disabled in some or all buffers.

To enable or disable Loco in the current buffer, use the command loco-mode. The key sequence C‑c , is bound to this command; pressing it will toggle Loco in the current buffer.

You can also call this command using Emacs Lisp with:

  • No argument, nil, or a positive number to enable Loco in the current buffer;

    (loco-mode 1) ; Enable in the current buffer
  • A zero or negative number to disable Loco in the current buffer; or

    (loco-mode 0) ; Disable in the current buffer
  • The symbol toggle to toggle Loco in the current buffer.

    (loco-mode 'toggle) ; Toggle in the current buffer

To enable or disable Loco in all buffers, use the command global-loco-mode. The key sequence C‑c . is bound to this command; pressing it will toggle Loco in all buffers.

This command will override any local settings.

You can also call this command using Emacs Lisp with:

  • No argument, nil, or a positive number to enable Loco in all buffers;

    (global-loco-mode 1) ; Enable in all buffers
  • A zero or negative number to disable Loco in all buffers; or

    (global-loco-mode 0) ; Disable in all buffers
  • The symbol toggle to toggle Loco in all buffers.

    (global-loco-mode 'toggle) ; Toggle in all buffers

When Loco is disabled in the current buffer, all keys can be typed normally as if Loco were not installed, with the exception of C‑c , and C‑c . themselves, which are bound in the global keymap.

When Loco is enabled in the current buffer it will place an indicator, which Emacs calls a "lighter", in the mode line of that buffer. The lighter for Loco is simply the string "Loco". Loco will also bind two additional key sequences: S‑<return> and C‑h S‑<return>. Pressing either of these will activate Loco.

When Loco is activated, it will prompt you to enter a key sequence, then it will lookup whether or not that key sequence is bound to a command. If a command is found, it will either execute or describe that command, depending on which key sequence was used to activate it. S‑<return> tells Loco that it should execute the command. C‑h S‑<return> tells Loco it should describe the command.

Entering key sequences

When Loco reads a key sequence:

  • It tracks a set of modifiers (Alt, Control, Hyper, Meta, and Super) that it considers "pending".

  • j and k add Control and Meta, respectively, to the set of pending modifiers, or represent themselves, if their respective modifiers are already pending.

  • l opens the Assist Menu, a menu which includes commands to toggle pending modifiers (a c h m and s) or enter keys for which there is no other way to enter because they have been repurposed by Loco (j k and l itself).

  • When a key is pressed to which modifiers could be applied, any pending modifiers from that set are applied to the key, as if the equivalent physical modifier keys were held down at the time the key was pressed. The set (if not empty) is then cleared. The key (now potentially modified) is then added to the key sequence.

  • When a key is added to the key sequence, Loco will check if the key sequence is bound to a command. If it is a partial match to one (or more) commands, Loco will continue to read keys. If it is an exact match to a command, Loco will stop reading keys and execute or describe that command (depending on how Loco was activated). Otherwise, Loco will stop reading keys and tell the user that no match was found.

  • It will display its prompt in the minibuffer. The prompt will consist of:

    • A right-pointing triangle (▶);

    • The current key sequence, if any, displayed in the normal Emacs style;

    • The set of pending modifiers, if any, displayed in the normal Emacs style, i.e., A-, C-, H-, M-, and s-, corresponding to the modifiers Alt, Control, Hyper, Meta, and Super, respectively; and

    • The Assist Menu, if open, displayed as [;] (in its collapsed state) or [achms jkl q x ;] (in its expanded state).

The following tables explain exactly how key presses are handled while reading a key sequence.

Normal operation (i.e., when the Assist Menu is closed)
Key Rule
j There are two possibilities for how this key is handled:
  • If Control is already pending, apply all pending modifiers to the key (clearing the modifiers), then add the modified key to the key sequence.
  • Otherwise, add Control to the set of pending modifiers.
k There are two possibilities for how this key is handled:
  • If Meta is already pending, apply all pending modifiers to the key (clearing the modifiers), then add the modified key to the key sequence.
  • Otherwise, add Meta to the set of pending modifiers.
l Open the Assist Menu.
Other Apply any pending modifiers to the key (clearing the modifiers), then add the (potentially modified) key to the key sequence.
Assisted operation (i.e., when the Assist Menu is open)
Key Rule
a c h m or s Toggle Alt Control Hyper Meta or Super respectively, in the set of pending modifiers, and close the Assist Menu.
A C H M or S Toggle Alt Control Hyper Meta or Super respectively, in the set of pending modifiers, but do not close the Assist Menu (see Sticky keys).
j k or l Apply any pending modifiers to the key (clearing the modifiers), add the (potentially modified) key to the key sequence, and close the Assist Menu.
q Close the Assist Menu and cancel the key sequence (equivalent to C‑g).
x Close the Assist Menu.
; Toggle the Assist Menu between its collapsed and expanded states.
Other Discard the key, but do not close the Assist Menu. If the Assist Menu is currently collapsed, expand it to remind the user of all available options.

Rock & roll

The keys j, k, and l were chosen to represent Control, Meta, and the Assist Menu, respectively, because on a QWERTY keyboard these keys are adjacent. This allows you to roll from side to side or rock your fingers back and forth over them.

Furthermore, you can build up the set of pending modifiers for a key in any order; for example, both j k and k j translate to C‑M‑.

These design choices enable efficient key entry, enhancing your ability to enter these important keys swiftly and accurately.

Sticky keys

On the Assist Menu, the keys used to toggle modifiers (a, c, h, m, and s) can be pressed in conjunction with the physical Shift key (A, C, H, M, and S, respectively).

These Shift-modified keys perform the same operation as their unmodified counterparts but make the Assist Menu sticky: after use, the menu remains open to use again.

The advantage of these keys is that multiple modifiers can be added or removed quickly. The disadvantage is that another key (for example, x) is then required to close the Assist Menu.

These keys are optional and not shown on the Assist Menu.

Examples

Most key sequences do not involve Control or Meta in conjunction with j, k, or l, making them easy to enter.

Typical key sequences and their translations
Typed Key Sequence Translated Key Sequence Command
j e C‑e move-end-of-line
k e M‑e forward-sentence
j k e or k j e C‑M‑e end-of-defun

Nine key sequences involve Control or Meta in conjunction with j, k, or l, making them more difficult to enter.

More difficult key sequences and their translations
Typed Key Sequence Translated Key Sequence Command
j l j or j j C‑j eval-print-last-sexp
j l k C‑k kill-line
j l l C‑l recenter-top-bottom
k l j M‑j default-indent-new-line
k l k or k k M‑k kill-sentence
k l l M‑l downcase-word
j k j or k j j C‑M‑j default-indent-new-line
j k k or k j k C‑M‑k kill-sexp
j k l l or k j l l C‑M‑l reposition-window

Describing commands

The built-in command describe-key waits for a key sequence to be input. If that key sequence is bound to a command, it describes the command. This is an excellent way to discover (or remind yourself) to which command a key sequence is bound.

In the global keymap, C‑h k (j h k) is bound to describe-key.

This command reads key sequences directly, without leveraging Loco. To use Loco to enter a key sequence for the purpose of describing the command to which it is bound, Loco provides a similar command called loco-default-describe-kseq.

When Loco is enabled in the current buffer, C‑h S‑<return> (j h S‑<return>) is bound to loco-default-describe-kseq.

Repeating commands

One advantage of using physical modifier keys is that once held, the non-modifier key can be pressed multiple times in sequence to repeat the command.

Emacs provides a number of ways to assist with repeating a command, and Loco works well with all of them. In particular, using repeat-mode to setup groups of commands that can be repeated by pressing only their last letter is highly recommended.

Working with physical modifier keys

While Loco aims to replace the need for physical modifier keys, there might be some cases where you want to use key bindings that use them. Because Loco does not replace or disable any keymaps, you are able to use other key bindings as you see fit.

Furthermore, while not necessary, any physical modifier keys you use while entering key sequences in Loco are properly merged with the set of pending modifiers.

Considerations when using a terminal

In a GUI environment, applications receive key events directly from the windowing system (for example, X11, Wayland, or Quartz Compositor). These systems have comprehensive support for detecting various key combinations, including multiple modifiers pressed simultaneously with other keys. The only limitation is usually from the operating system itself, which reserves certain key combinations for its own functions.

Modern terminal applications are typically run within a terminal emulator, an application that itself reserves certain key combinations for its own functions. This creates a disparity between the total number of keys available to the GUI application and the terminal application.

Terminal emulators are designed to mimic the behaviour of older physical terminals (for example, the VT100). Most terminal emulators work by encoding input into characters or escape sequences that the terminal application interprets. This limits how key events are handled by terminal applications in several ways:

  • The Control key is typically limited to modifying these specific 32 characters: @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ?. These characters have ASCII codes of 64 to 95 inclusive. When pressed in conjunction with Control, 64 is subtracted from their ASCII code, resulting in a Control Character. This form of encoding inherently limits which combinations are possible with Control and does not work well in conjunction with any other modifier key.

  • Other modifier keys, and keys without ASCII equivalents (for example, the arrow and function keys), are either encoded as character sequences beginning with the ESC character or not sent to terminal applications at all. Interpreting these sequences can be less reliable, especially for complex combinations involving multiple modifiers.

Fortunately, Loco works consistently in both the GUI and terminal. By translating simple key sequences into complex ones, Loco avoids the above limitations.

Customization

Most of this section assumes the reader has basic experience with Emacs Lisp.

Using the "Easy Customization Interface"

The Easy Customization Interface can be used to customize certain properties of Loco, including its lighter and its prompts.

You can also customize properties related to logging, which is useful if you are planning on modifying the source code for Loco. Logging is not explained in this document; see the source code for details.

Some of the properties presented in this interface take S-expressions as values. These permit more possibilities for values; for example, strings styled with custom colours and fonts.

All of the properties presented in this interface can be set using Emacs Lisp as well.

Changing the key bindings used to enable, disable, or activate Loco

The command loco-set-default-key-bindings sets the default key bindings to enable, disable, or activate Loco. Typically, you should call this command automatically on startup, as recommended in Installation, but it is not required that you do so. This command sets the default key bindings as follows, and you can edit this code to bind your own key sequences as desired.

;; Bind "C-c ," to toggle Loco in the current buffer
(keymap-global-set "C-c ," #'loco-mode)

;; Bind "C-c ." to toggle Loco in all buffers
(keymap-global-set "C-c ." #'global-loco-mode)

;; When Loco is enabled in the current buffer, bind "C-h S-<return>"
;; to read a key sequence then describe the command to which it is bound
(keymap-set loco-mode-keymap "C-h S-<return>"
            #'loco-default-describe-kseq)

;; When Loco is enabled in the current buffer, bind "S-<return>"
;; to read a key sequence then execute the command to which it is bound
(keymap-set loco-mode-keymap "S-<return>"
            #'loco-default-execute-kseq)

The command loco-unset-default-key-bindings is the inverse of loco-set-default-key-bindings; the former will unset any keybindings set by the latter.

Changing the keys used while Loco reads a key sequence

By default, S‑<return> is bound to loco-default-execute-kseq. This command is defined as follows.

(defun loco-default-execute-kseq (&rest args)
  (interactive)
  (apply #'loco-read-kseq :key-mod-c ?j :key-mod-m ?k args))

This command calls loco-read-kseq with two arguments. These arguments tell loco-read-kseq that while reading a key sequence, j should set Control pending and k should set Meta pending, respectively.

We can define a similar command, called my-loco-execute-kseq, and rebind S‑<return> to it as follows.

(defun my-loco-execute-kseq (&rest args)
  (interactive)
  (apply #'loco-read-kseq :key-am-mod-c-qk nil :key-am-mod-m-qk nil
                          :key-am-s-collapse ?/ :key-am-s-expand ?/
                          :key-am-s-open ?m :key-mod-c ?, :key-mod-m ?.
                          :validate t args))

(keymap-set loco-mode-keymap "S-<return>" #'my-loco-execute-kseq)

Our command calls loco-read-kseq with eight arguments.

The first seven arguments tell loco-read-kseq that while reading a key sequence:

  • no key should toggle Control then close the Assist Menu (when it is open);
  • no key should toggle Meta then close the Assist Menu (when it is open);
  • / should collapse the Assist Menu (when it is open and expanded);
  • / should expand the Assist Menu (when it is open and collapsed);
  • m should open the Assist Menu;
  • , should set Control pending; and
  • . should set Meta pending, respectively.

On a QWERTY keyboard, m, ,, ., and / are adjacent, like the default keys j, k, l, and ;, but located one row lower on the keyboard and with the two modifier keys located in the centre of the group. While only an example, this may be a desirable configuration for those who find it easier to mentally associate the modifiers to punctuation (, and . instead of j and k, respectively), or remember m as "menu".

In this configuration, we've decided that when Loco is reading a key sequence, m should open the Assist Menu. By repurposing m for this, we need to rely on the Assist Menu to help us enter a literal m when necessary. By default, the Assist Menu already reserves a c h m and s to toggle pending modifiers then close the Assist Menu. This creates the potential for conflict.

Our configuration also defines , and . as the primary means to set Control and Meta pending, respectively. As such, having the means to toggle these modifiers from the Assist Menu is less useful. To avoid the potential conflict in setting m to mean both m and Meta when the Assist Menu is open, we remove the latter meaning using :key-am-mod-m-qk nil. And while not necessary to avoid a conflict, for clarity and consistency we also set :key-am-mod-c-qk nil.

Note that, by default, the Assist Menu also reserves A C H M and S as the sticky versions of a c h m and s, respectively. As none of the sticky versions of these keys were removed from our configuration, there does remain a means to toggle Control and Meta from the Assist Menu if desired.

Our command calls loco-read-kseq with one additional argument: :validate t. When non-nil, this argument tells loco-read-kseq to validate the set of keys it will use while reading a key sequence. For example, the validation process checks that no key is assigned to more than one function. Any problems found during the validation process are printed in the log. :validate t is recommended any time you are customizing Loco's keys.

The Assist Menu displays itself according to the keys as you've configured them. By default, it will display itself (when open and expanded) like this: [achms jkl q x ;]. For the configuration in this example, it will display itself (when open and expanded) like this: [ahs ,.m q x /].

Our example is not quite complete. We should also ensure that we have a describe command that works in the same manner as our execute command.

By default, C‑h S‑<return> is bound to loco-default-describe-kseq. This command is defined as follows.

(defun loco-default-describe-kseq (&rest args)
  (interactive)
  (apply #'loco-default-execute-kseq :d t args))

This command calls loco-default-execute-kseq with one additional argument: :d t. When non-nil, this argument tells loco-read-kseq that if it reads a key sequence that is bound to a command, that command should be described instead of executed.

We can define a similar command, called my-loco-describe-kseq, and rebind C‑h S‑<return> to it as follows.

(defun my-loco-describe-kseq (&rest args)
  (interactive)
  (apply #'my-loco-execute-kseq :d t args))

(keymap-set loco-mode-keymap "C-h S-<return>" #'my-loco-describe-kseq)

There are a total of 25 keyword arguments that can be passed to loco-read-kseq, all of which are explained in the documentation for that command.

Advanced topics

Mapping Caps Lock to Control

S‑<return> is easy to press, but it would be easier still to be able to activate Loco with a physical modifier key located right on the home row. If this physical modifier key were Control, you would also have quick access to some very common Emacs key bindings without use of Loco. For example, C‑n and C‑p.

Caps Lock, located to the left of a on a QWERTY keyboard, is a rarely-used key in a prime location. As such, it is common to remap this key to Control.

For example, on MacOS, where keys are not easily remapped system-wide, mapping Caps Lock to Control is possible without any third-party tools. In System Preferences > Keyboard > Keyboard Shortcuts... > Modifier Keys, change Caps Lock key to Control. You might also want to change Control key to Caps Lock, effectively swapping the behaviour of the two keys, to ensure you still have a means to toggle Caps Lock if desired. If you use multiple keyboards, make sure to change these settings for each keyboard in turn by selecting each keyboard at the top of this dialog box. Press Done when complete.

You can set key bindings for C‑<return> and C‑h C‑<return> as follows.

;; When Loco is enabled in the current buffer, bind "C-h C-<return>"
;; to read a key sequence then describe the command to which it is bound
(keymap-set loco-mode-keymap "C-h C-<return>"
            #'loco-default-describe-kseq)

;; When Loco is enabled in the current buffer, bind "C-<return>"
;; to read a key sequence then execute the command to which it is bound
(keymap-set loco-mode-keymap "C-<return>"
            #'loco-default-execute-kseq)

Activation keys that also function as modifiers

Activation keys can also function as modifiers, allowing you to both activate Loco and set a pending modifier with a single key.

This example will extend the example in Changing the keys used while Loco reads a key sequence.

Consider the following code:

(defun my-loco-describe-control-kseq (&rest args)
  (interactive)
  (apply #'my-loco-execute-control-kseq :d t args))

(defun my-loco-describe-meta-kseq (&rest args)
  (interactive)
  (apply #'my-loco-execute-meta-kseq :d t args))

(keymap-set loco-mode-keymap "C-h C-," #'my-loco-describe-control-kseq)
(keymap-set loco-mode-keymap "C-h C-." #'my-loco-describe-meta-kseq)

(defun my-loco-execute-control-kseq (&rest args)
  (interactive)
  (apply #'loco-read-kseq :key-am-mod-c-qk nil :key-am-mod-m-qk nil
                          :key-am-s-collapse ?/ :key-am-s-expand ?/
                          :key-am-s-open ?m :key-mod-c ?, :key-mod-m ?.
                          :kseq [?,] :strip t
                          :validate t args))

(defun my-loco-execute-meta-kseq (&rest args)
  (interactive)
  (apply #'loco-read-kseq :key-am-mod-c-qk nil :key-am-mod-m-qk nil
                          :key-am-s-collapse ?/ :key-am-s-expand ?/
                          :key-am-s-open ?m :key-mod-c ?, :key-mod-m ?.
                          :kseq [?.] :strip t
                          :validate t args))

(keymap-set loco-mode-keymap "C-," #'my-loco-execute-control-kseq)
(keymap-set loco-mode-keymap "C-." #'my-loco-execute-meta-kseq)

Here we create a total of four activation keys.

C‑, and C‑. both activate Loco to read a key sequence and execute the command to which it is bound, but C‑, activates Loco with Control pending and C‑. with Meta pending.

C‑h C‑, and C‑h C‑. both activate Loco to read a key sequence and describe the command to which it is bound, but C‑h C‑, activates Loco with Control pending and C‑h C‑. with Meta pending.

Note the use of the physical Control key here. This allows us to choose two adjacent activation keys, as there are almost no keys that use Shift that aren't extremely important, aside from a few keys like S‑<return>, which we've been using. To use this example effectively, consider Mapping Caps Lock to Control to ensure that Control is easy to press.

The argument :kseq tells loco-read-kseq to immediately process a key sequence before reading any input from the user.

In my-loco-execute-control-kseq, :kseq [?,] tells loco-read-kseq to immediately process a single , before reading any input from the user. Because we've also assigned , to be interpreted as Control, the combined effect of both arguments is to make Control immediately pending.

Similarly, in my-loco-execute-meta-kseq, :kseq [?.] tells loco-read-kseq to immediately process a single . before reading any input from the user. Because we've also assigned . to be interpreted as Meta, the combined effect of both arguments is to make Meta immediately pending.

The argument :strip t tells loco-read-kseq to strip all modifiers from any key that it reads. This is optional; its effect, though subtle, can be desirable. As per Working with physical modifier keys, Loco is happy to merge any modified keys with any pending modifiers. When activating Loco with a key that is similar to the keys required to toggle pending modifiers, accidentally holding any physical modifier key past its intended key may modify subsequent keys, possibly resulting in a key sequence bound to a different command that intended. Stripping modifiers from keys allows for some forgiveness when typing quickly, to ensure the modifiers aren't incorrectly applied to the wrong keys.

Note that when both :strip and :validate are non-nil, the validation process will correctly strip all keys before checking for duplicates.

Activation keys that avoid modifiers completely

Activation keys can avoid modifiers completely, allowing you use Loco without using any physical modifier keys.

This example will extend the example in Activation keys that also function as modifiers.

Consider the following code:

(defun my-loco-describe-control-kseq (&rest args)
  (interactive)
  (apply #'my-loco-execute-control-kseq :d t args))

(defun my-loco-describe-meta-kseq (&rest args)
  (interactive)
  (apply #'my-loco-execute-meta-kseq :d t args))

(keymap-set loco-mode-keymap "C-h ," #'my-loco-describe-control-kseq)
(keymap-set loco-mode-keymap "C-h ." #'my-loco-describe-meta-kseq)

(defun my-loco-execute-control-kseq (&rest args)
  (interactive)
  (apply #'loco-read-kseq :dt t
                          :key-am-mod-c-qk nil :key-am-mod-m-qk nil
                          :key-am-s-collapse ?/ :key-am-s-expand ?/
                          :key-am-s-open ?m :key-mod-c ?, :key-mod-m ?.
                          :kseq [?,] :strip t
                          :validate t args))

(defun my-loco-execute-meta-kseq (&rest args)
  (interactive)
  (apply #'loco-read-kseq :dt t
                          :key-am-mod-c-qk nil :key-am-mod-m-qk nil
                          :key-am-s-collapse ?/ :key-am-s-expand ?/
                          :key-am-s-open ?m :key-mod-c ?, :key-mod-m ?.
                          :kseq [?.] :strip t
                          :validate t args))

(keymap-set loco-mode-keymap "," #'my-loco-execute-control-kseq)
(keymap-set loco-mode-keymap "." #'my-loco-execute-meta-kseq)

Here we consider an extreme (but useful) configuration that reduces the activation keys to C‑h ,, C‑h ., ,, and ..

The effect of setting , and . as activation keys is significant. By rebinding them to activation keys, they no longer perform their original purpose: to enter a literal comma or period, respectively. Both are essential punctuation, second only to the letters and digits in terms of frequency of use.

Here we see a new argument: :dt t. When non-nil, this argument tells loco-read-kseq to apply an additional rule when processing keys: the double-tap rule.

The double-tap rule, for this configuration, states that if , is pressed when Control is the only pending modifier, or if . is pressed when Meta is the only pending modifier, clear the modifier and add the key unmodified to the key sequence.

This rule permits , and . to be entered as , , or . ., respectively. It is called double-tap because it takes effect when these keys are tapped twice in succession (provided no other modifiers are in effect).

Without this rule, entering a literal comma or period would be unduly onerous. For example, here are four alternate ways by which you could enter a literal comma or period, respectively:

  1. , m S‑c , or . m S‑m .

    This method uses the Assist Menu (once), but uses a sticky key to keep the menu open.

  2. , m c m , or . m m m .

    This method uses the Assist Menu (twice), but does not require a physical modifier key.

  3. C‑q , or C‑q .

    In Emacs, C‑q is bound to the command quoted-insert, which will read the next key and insert it. This method uses quoted-insert, but requires a physical modifier key.

  4. , q , or , q .

    C‑q itself can be entered as , q. This method uses quoted-insert, but does not require a physical modifier key.

While the double-tap rule makes entering a literal comma or period easy, it does come with a cost.

Without this rule, , , would be translated to C‑,. The first , would tell Loco to consider Control as pending, and the second , would tell Loco to apply Control to ,.

Similarly, . . would be translated to M‑.. The first . would tell Loco to consider Meta as pending, and the second . would tell Loco to apply Meta to ..

While C‑, and M‑. are less frequently used than literal commas and periods (C‑, is not bound to any command and M‑. is bound to xref-find-definition), we must still be able to enter them.

Fortunately, we can use the Assist Menu for this. C‑, and M‑. can be entered as , m , and . m ., respectively.

Author and copyright

Loco was written and copyright in 2024 by Chris McLaren (@csmclaren).

License

This file is part of Loco.

Loco is licensed under the GNU General Public License v3.0. See the COPYING file for details.

Colophon

The logo for Loco is a representation of a tram car from the Unicode chart Transport and Map Symbols. Loco was created in Lisbon, Portugal, a city with a history of using tram cars such as these.

About

A library and minor mode for entering key sequences

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Emacs Lisp 95.0%
  • Lua 2.4%
  • Shell 1.6%
  • HTML 1.0%