From f78844cec4a23abe4ebdf4cab82f17b585f524b8 Mon Sep 17 00:00:00 2001 From: Philip Kaludercic Date: Wed, 10 May 2023 08:08:21 +0200 Subject: [PATCH 8/8] Extract most of the README into a separate manual --- MANUAL.org | 564 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 594 +---------------------------------------------------- 2 files changed, 565 insertions(+), 593 deletions(-) create mode 100644 MANUAL.org diff --git a/MANUAL.org b/MANUAL.org new file mode 100644 index 0000000..3cd3ca9 --- /dev/null +++ b/MANUAL.org @@ -0,0 +1,564 @@ +#+title: Devil Mode +#+author: Susam Pal +#+email: susam@susam.net +#+language: en +#+options: ':t toc:nil author:t email:t num:t +#+texinfo_dir_category: Emacs misc features +#+texinfo_dir_title: Devil: (devil-mode) +#+texinfo_dir_desc: Minor mode for Devil-like command entering + +#+texinfo: @insertcopying + +Devil mode intercepts our keystrokes and translates them to Emacs key +sequences according to a configurable set of translation rules. For +example, with the default translation rules, when we type =, x , f=, +Devil translates it to =C-x C-f=. + +The choice of the comma key (=,=) to mean the control modifier key +(=C-=) may seem outrageous. After all, the comma is a very important +punctuation both in prose as well as in code. Can we really get away +with using =,= to mean the =C-= modifier? It turns out, this terrible +idea can be made to work without too much of a hassle. At least it works +for me! It might work for you too. If it does not, Devil can be +configured to use another key instead of =,= to mean the =C-= modifier. +See the section [[#custom-devil-key][Custom Devil Key]] for an example. + +A sceptical reader may rightfully ask: If =,= is translated to =C-=, how +on earth are we going to insert a literal =,= into the text when we need +to? The section [[#typing-commas][Typing Commas]] answers this. But +before we get there, we have some fundamentals to cover. Take the plunge +and see what unfolds! Maybe you will like this! Maybe you will not! If +you do not like this, you can always retreat to God mode, Evil mode, the +vanilla key bindings, or whatever piques your fancy! + +* Notation +:PROPERTIES: +:CUSTOM_ID: notation +:END: +A quick note about the notation used in the document: The previous +example shows that =, x , f= is translated to =C-x C-f=. What this +really means is that the key sequence ,x,f is translated to ctrl+x +ctrl+f. We do not really type any space after the commas. The key , is +directly followed by the key x. However, the key sequence notation used +in this document contains spaces between each keystroke. This is +consistent with how key sequences are represented in Emacs in general +and how Emacs functions like =key-description=, =describe-key=, etc. +represent key sequences. When we really need to type a space, it is +represented as =SPC=. + +* Install +:PROPERTIES: +:CUSTOM_ID: install +:END: +** Install Interactively from MELPA +:PROPERTIES: +:CUSTOM_ID: install-interactively-from-melpa +:END: +Devil is available via [[https://melpa.org/][MELPA]]. You may already +have a preferred way of installing packages from MELPA. If so, install +the package named =devil= to get Devil. For the sake of completeness, +here is a very basic way of installing Devil from MELPA: + +1. Add the following to the Emacs initialization file (i.e., =~/.emacs= + or =~/.emacs.d/init.el= or =~/.config/emacs/init.el=): + + #+begin_src sh + (require 'package) + (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) + (package-initialize) + #+end_src + +2. Start Emacs with the updated initialization file and then type these + commands: + + #+begin_example + M-x package-refresh-contents RET + M-x package-install RET devil RET + #+end_example + +3. Confirm that Devil is installed successfully with this command: + + #+begin_example + M-x devil-show-version RET + #+end_example + +4. Enable Devil mode with this command: + + #+begin_example + M-x global-devil-mode RET + #+end_example + +5. Type =, x , f= and watch Devil translate it to =C-x C-f= and invoke + the corresponding command. + +** Install Automatically from MELPA +:PROPERTIES: +:CUSTOM_ID: install-automatically-from-melpa +:END: +Here is yet another basic way to install and enable Devil using Elisp: + +#+begin_example +(require 'package) +(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) +(package-initialize) +(unless package-archive-contents + (package-refresh-contents)) +(unless (package-installed-p 'devil) + (package-install 'devil)) +(global-devil-mode) +(global-set-key (kbd "C-,") 'global-devil-mode) +#+end_example + +Now type =, x , f= and watch Devil translate it to =C-x C-f= and invoke +the corresponding command. Type =C-,= to disable Devil mode. Type =C-,= +again to enable it. + +** Install from Git Source +:PROPERTIES: +:CUSTOM_ID: install-from-git-source +:END: +If you prefer obtaining Devil from its Git repository, follow these +steps: + +1. Clone Devil to your system: + + #+begin_src sh + git clone https://github.com/susam/devil.git + #+end_src + +2. Add the following to your Emacs initialization: + + #+begin_example + (add-to-list 'load-path "/path/to/devil/") + (require 'devil) + (global-devil-mode) + (global-set-key (kbd "C-,") 'global-devil-mode) + #+end_example + +3. Start the Emacs editor. Devil mode should now be enabled in all + buffers. The modeline of each buffer should show the =Devil= lighter. + +4. Type =, x , f= and watch Devil translate it to =C-x C-f= and invoke + the corresponding command. Type =C-,= to disable Devil mode. Type + =C-,= again to enable it. + +* Use Devil + :PROPERTIES: + :CUSTOM_ID: use-devil + :END: +Assuming vanilla Emacs key bindings have not been changed and Devil has +not been customised, here are some examples that demonstrate how Devil +may be used: + +1. Type =, x , f= and watch Devil translate it to =C-x C-f= and invoke + the find file functionality. + +2. Type =, p= to move up one line. + +3. To move up multiple lines, type =, p p p= and so on. Some Devil key + sequences are repeatable keys. The repeatable Devil key sequences can + be repeated by typing the last key of the Devil key sequence over and + over again. + +4. Another example of a repeatable Devil key sequence is =, f f f= which + moves the cursor word by multiple characters. A few other examples of + repeatable keys are =, k k k= to kill lines, =, / / /= to undo + changs, etc. Type =C-h v devil-repeatable-keys RET= to see the + complete list of repeatable keys. + +5. Type =, s= and watch Devil translate it to =C-s= and invoke + incremental search. + +6. Type =, m s= and watch Devil translate it to =C-M-s= and invoke + regular-expression-based incremental search. Yes, =m= is translated + to =M-=. + +7. Type =, m m x= and watch Devil translate it to =M-x= and invoke the + corresponding command. + +8. Type =, u , f= and watch Devil translate it to =C-u C-f= and move the + cursor forward by 4 characters. + +9. Type =, u u , f= and the cursor moves forward by 16 characters. Devil + uses its translation rules and an additional keymap to make the input + key sequence behave like =C-u C-u C-f= which moves the cursor forward + by 16 characters. + +10. Type =, SPC= to type a comma followed by space. This is a special + key sequence to make it convenient to type a comma in the text. Note + that this sacrifices the use of =, SPC= to mean =C-SPC= which could + have been a convenient way to set a mark. + +11. Type =, z SPC= and watch Devil translate it to =C-SPC= and set a + mark. Yes, =, z= is translated to =C-= too. + +12. Similarly, type =, RET= to type a comma followed by the return key. + This is another special key. + +13. Type =, ,= to type a single comma. This special key is useful for + cases when you really need to type a single literal comma. + +* Typing Commas + :PROPERTIES: + :CUSTOM_ID: typing-commas + :END: +Devil makes the questionable choice of using the comma as its activation +key. As illustrated in the previous section, typing =, x , f= produces +the same effect as typing =C-x C-f=. One might naturally wonder how then +we are supposed to type literal commas. + +Most often when we edit text, we do not really type a comma in +isolation. Often we immediately follow the comma with a space or a +newline. This assumption usually holds good while editing regular text. +However, this assumption may not hold in some situations, like while +working with code when we need to add a single comma at the end of an +existing line. + +In scenarios where the above assumption holds good, typing =, SPC= +inserts a comma and a space. Similarly, typing =, RET= inserts a comma +and a newline. + +In scenarios, when we do need to type a single comma, type =, ,= +instead. + +Also, it is worth mentioning here that if all this fiddling with the +comma key feels clumsy, we could always customise the Devil key to +something else that feels better. We could also disable Devil mode +temporarily and enable it again later with =C-,= as explained in section +[[#use-devil][Use Devil]]. + +* Devil Reader + :PROPERTIES: + :CUSTOM_ID: devil-reader + :END: +The following points briefly describe how Devil reads Devil key +sequences, translates them to Emacs key sequences, and runs commands +bound to the key sequences: + +1. As soon as the Devil key is typed (which is =,= by default), Devil + wakes up and starts reading Devil key sequences. Type + =C-h v devil-key RET= to see the current Devil key. + +2. After each keystroke is read, Devil checks if the key sequence + accumulated is a special key. If it is, then the special command + bound to the special key is executed immediately. Note that this step + is performed before any translation rules are applied to the input + key sequence. This is how the Devil special key sequence =, SPC= + inserts a comma and a space. Type =C-h v devil-special-keys RET= + to see the list of special keys and the commands bound to them. + +3. If the key sequence accumulated so far is not a special key, then + Devil translates the Devil key sequence to a regular Emacs key + sequence. If the regular Emacs key sequence turns out to be a + complete key sequence and some command is found to be bound to it, + then that command is executed immediately. This is how the Devil key + sequence =, x , f= is translated to =C-x C-f= and the corresponding + binding is executed. If the translated key sequence is a complete key + sequence but no command is bound to it, then Devil displays a message + that the key sequence is undefined. Type + =C-h v devil-translations RET= to see the list of translation rules. + +4. After successfully translating a Devil key sequence to an Emacs key + sequence and executing the command bound to it, Devil checks if the + key sequence is a repeatable key sequence. If it is found to be a + repeatable key sequence, then Devil sets a transient map so that the + command can be repeated merely by typing the last keystroke of the + input key sequence. This is how =, p p p= moves the cursor up by + three lines. Type =C-h v devil-repeatable-keys RET= to see the + list of repeatable Devil key sequences. + +The variables =devil-special-keys=, =devil-translations=, and +=devil-repeatable-keys= may contain keys or values with the string =%k= +in them. This is a placeholder for =devil-key=. While applying the +special keys, translation rules, or repeat rules, each =%k= is replaced +with the actual value of =devil-key= before applying the rules. + +* Translation Rules + :PROPERTIES: + :CUSTOM_ID: translation-rules + :END: +The following points provide an account of the translation rules that +Devil follows in order to convert a Devil key sequence entered by the +user to an Emacs key sequence: + +1. The input key vector read from the user is converted to a key + description (i.e., the string functions like =describe-key=, + =key-description=, produce). For example, if the user types ,x,f, it + is converted to =, x , f=. + +2. Now the resulting key description is translated with simple string + replacements. If any part of the string matches a key in + =devil-translations=, then it is replaced with the corresponding + value. For example, =, x , f= is translated to =C- x C- f=. Then + Devil normalises the result to =C-x C-f= by removing superfluous + spaces after the modifier keys. + +3. However, if the simple string based replacement leads to an invalid + Emacs key sequence, it skips the replacement that causes the + resulting Emacs key sequence to become invalid. For example + =, m ,= results in =C-M-C-= after the simple string replacement + because the default translation rules replace =,= with =C-= and =m= + with =M-=. However, =C-M-C-= is an invalid key sequence, so the + replacement of the second =,= to =C-= is skipped. Therefore, the + input =, m ,= is translated to =C-M-,= instead. + +* Translation Examples + :PROPERTIES: + :CUSTOM_ID: translation-examples + :END: +By default, Devil supports a small but peculiar set of translation rules +that can be used to avoid modifier keys while typing various types of +key sequences. See =C-h v devil-translations RET= for the translation +rules. Here are some examples that demonstrate the default translation +rules. The obvious ones are shown first first. The more peculiar +translations come later in the table. + +| Input | Translated | Remarks | +|-----------+------------+-----------------------------------| +| =, s= | =C-s= | =,= is replaced with =C-= | +| =, m s= | =C-M-s= | =m= is replaced with =M-= | +| =, z s= | =C-SPC= | =, z= is replaced with =C-= too | +| =, z z= | =C-z= | ditto | +| =, m m x= | =M-x= | =, m m= is replaced with =M-= too | +| =, c , ,= | =C-c ,= | =, ,= is replaced with =,= | + +Note how we cannot use =, SPC= to set a mark because that key sequence +is already reserved as a special key sequence in =devil-special-keys=, +so Devil translates =, z= to =C-= too, so that we can still type =C-SPC= +using =, z s= and set a mark. + +Also, note how the translation of =, m m= to =M-= allows us to enter a +key sequence that begins with the =M-= modifier key. + +* Bonus Key Bindings + :PROPERTIES: + :CUSTOM_ID: bonus-key-bindings + :END: +Devil adds the following additional key bindings only when Devil is +enabled globally with =global-devil-mode=: + +- Adds the Devil key to =isearch-mode-map=, so that Devil key sequences + work in incremental search too. + +- Adds =u= to =universal-argument-more= to allow repeating the universal + argument command =C-u= simply by repeating =u=. + +As mentioned before these features are available only when Devil is +enabled globally with =global-devil-mode=. If Devil is enabled locally +with =devil-mode=, then these features are not available. + +* Custom Configuration Examples + :PROPERTIES: + :CUSTOM_ID: custom-configuration-examples + :END: +In the examples presented below, the =(require 'devil)= calls may be +omitted if Devil has been installed from MELPA. There are appropriate +autoloads in place in the Devil package that would ensure that it is +loaded automatically on enabling Devil mode. However, the =require= +calls have been included in the examples below for the sake of +completeness. + +** Local Mode + :PROPERTIES: + :CUSTOM_ID: local-mode + :END: +While the section [[#use-devil][Use Devil]] shows how we enable Devil mode globally, +this section shows how we can enable it locally. Here is an example +initialization code that enables Devil locally only in text buffers. + +#+begin_example +(require 'devil) +(add-hook 'text-mode-hook 'devil-mode) +(global-set-key (kbd "C-,") 'devil-mode) +#+end_example + +This is not recommended though because this does not provide a seamless +Devil experience. For example, with Devil enabled locally in a text +buffer like this, although we can type =, x , f= to launch the find-file +minibuffer, we cannot use Devil key sequences in the minibuffer. Further +the special keymaps described in the previous section work only when +Devil is enabled globally. + +** Custom Appearance + :PROPERTIES: + :CUSTOM_ID: custom-appearance + :END: +The following initialization code shows how we can customise Devil to +show a Devil smiley (😈) in the modeline and the echo area. + +#+begin_example +(require 'devil) +(setq devil-lighter " \U0001F608") +(setq devil-prompt "\U0001F608 %t") +(global-devil-mode) +(global-set-key (kbd "C-,") 'global-devil-mode) +#+end_example + +This is how Emacs may look if emojis are rendered correctly: + +[[https://i.imgur.com/oYtwnGi.png][[[https://i.imgur.com/oYtwnGi.png]]]] + +** Custom Devil Key + :PROPERTIES: + :CUSTOM_ID: custom-devil-key + :END: +The following initialization code shows how we can customise Devil to +use a different Devil key. + +#+begin_example +(defvar devil-key "") +(defvar devil-special-keys '(("%k %k" . (lambda () (interactive) (devil-run-key "%k"))))) +(require 'devil) +(global-devil-mode) +(global-set-key (kbd "C-") 'global-devil-mode) +#+end_example + +The above example sets the Devil key to the left arrow key, perhaps +another dubious choice for the Devil key. With this configuration, we +can use = x f= and have Devil translate it to =C-x C-f=. + +Additionally, the above example defines the =devil-special-keys= +variable to have a single entry that allows typing = = to +produce the same effect as the original ==. It removes the other +entries, so that = SPC= is no longer reserved as a special key. +Thus = SPC= can now be used to set a mark like one would normally +expect. + +** Multiple Devil Keys + :PROPERTIES: + :CUSTOM_ID: multiple-devil-keys + :END: +While this package provides the comma (=,=) as the default and the only +Devil key, nothing stops you from extending the mode map to support +multiple Devil keys. Say, you decide that in addition to activating +Devil with =,= which also plays the role of =C-=, you also want to +activate Devil with =.= which must now play the role of =M-=. To achieve +such a result, you could use this initialization code as a starting +point and then customise it further based on your requirements: + +#+begin_example +(defvar devil-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd ",") #'devil) + (define-key map (kbd ".") #'devil) + map)) +(defvar devil-special-keys '((", ," . (lambda () (insert ","))) + (". ." . (lambda () (insert "."))))) +(defvar devil-translations '(("," . "C-") + ("." . "M-"))) +(require 'devil) +(global-devil-mode) +#+end_example + +With this configuration, we can type =, x , f= for =C-x C-f= like +before. But now we can also type =. x= for =M-x=. Similarly, we can type +=, . s= for =C-M-s= and so on. Further, =, ,= inserts a literal comma +and =. .= inserts a literal dot. + +Note that by default Devil configures only one activation key (=,=) +because the more activation keys we add, the more intrusive Devil +becomes during regular editing tasks. Every key that we reserve for +activating Devil loses its default function and then we need workarounds +to somehow invoke the default function associated with that key (like +repeating =.= twice to insert a single =.= in the above example). +Therefore, it is a good idea to keep the number of Devil keys as small +as possible. + +* Why? + :PROPERTIES: + :CUSTOM_ID: why + :END: +Why go to the trouble of creating and using something like this? Why not +just remap caps lock to ctrl like every other sane person does? Or if it +is so important to avoid modifier keys, why not use something like God +mode or Evil mode? + +Well, for one, both God mode and Evil mode are modal editing modes. +Devil, on the other hand, provides a modeless editing experience of +Emacs as possible. + +Devil mode began as a fun little tiny experiment. From the outset, it +was clear that using something as crucial as the comma for specifying +the modifier key is asking for trouble. However, I still wanted to see +how far I could go with it. It turned out that in a matter of days, I +was using it full-time for all of my Emacs usage. + +This experiment was partly motivated by Macbook keyboards which do not +have a right ctrl key. Being a touch-typist myself, I found it +inconvenient to type key combinations like =C-x=, =C-a=, =C-w=, =C-s=, +etc. where both the modifier key and the modified key need to be pressed +with the left hand fingers. I am not particularly fond of remapping caps +lock to behave like ctrl because that still suffers from the problem +that key combinations like =C-x=, =C-a= require pressing both the +modifier key and the modified key with the left hand fingers. I know +many people remap both their caps lock and enter to behave like ctrl. +While I think that is a fine solution, I was not willing to put up with +the work required to make that work seamlessly across all the various +operating systems I work on. + +What began as a tiny whimsical experiment a few years ago turned out to +be quite effective, at least to me. I like that this solution is +implemented purely as Elisp and therefore does not have any external +dependency. I am sharing this solution here in the form of a minor mode, +just in case, there is someone out there who might find this useful too. + +* Comparison with God Mode + :PROPERTIES: + :CUSTOM_ID: comparison-with-god-mode + :END: +God mode provides a modal editing experience but Devil does not. Devil +has the same underlying philosophy as that of God mode, i.e., the user +should not have to learn new key bindings. However, Devil does not have +a hard separation between insert mode and command mode like God mode +has. Instead, Devil waits for an activation key (=,= by default) and as +soon as it is activated, it intercepts and translates keys, runs the +corresponding command, and then gets out of the way. So Devil tries to +retain the modeless editing experience of vanilla Emacs as much as +possible. + +Now it is worth mentioning that some of this modeless editing experience +can be reproduced in god-mode too using its +=god-execute-with-current-bindings= function. Here is an example: + +#+begin_example +(global-set-key (kbd ",") #'god-execute-with-current-bindings) +#+end_example + +With this configuration, God mode translates =, x f= to =C-x C-f=. +Similarly =, g x= invokes =M-x= and =, G s= invokes =C-M-x=. This +provides a modeless editing experience in God mode too. However, this +experience does not extend seamlessly to minibuffers. Devil does extend +its Devil key translation to minibuffers. + +Further note that in God mode the ctrl modifier has sticky behaviour, +i.e., the modifier remains active automatically for the entire key +sequence. Therefore in the above example, we type =,= only once while +typing =, x f= to invoke =C-x C-f=. However, this sticky behaviour +implies that we need some way to disambiguate between key sequences like +=C-x C-o= (delete blank lines) and =C-x o= (other window). God mode +solves this by introducing =SPC= to deactivate the modifier, e.g., +=, x o= translates to =C-x C-o= but =, x SPC o= translates to =C-x o=. +Devil does not treat the modifier key as sticky which leads to simpler +key sequences at the cost of a little additional typing, i.e., =, x , o= +translates to =C-x C-o= and =, x o= translates to =C-x o=. + +To summarize, there are primarily three things that Devil does +differently: + +- Provide a modeless editing experience from the outset. +- Seamlessly extend the same editing experience to minibuffer, + incremental search, etc. +- Translate key sequences using string replacements. This allows for + arbitrary and sophisticated key translations for the adventurous. +- Choose non-sticky behaviour for the modifier keys. + +These differences could make Devil easier to use than God mode for some +people but clumsy for other people. It depends on one's tastes and +preferences. + +* Support + :PROPERTIES: + :CUSTOM_ID: support + :END: +To report bugs, suggest improvements, or ask questions, +[[https://github.com/susam/devil/issues][create issues]]. diff --git a/README.md b/README.md index 749508c..39ef57e 100644 --- a/README.md +++ b/README.md @@ -13,599 +13,7 @@ charm the Devil! But beware, for in this sinister domain, you must relinquish your comma key and embrace an editing experience that whispers wicked secrets into your fingertips! - -Contents --------- - -* [Introduction](#introduction) -* [Notation](#notation) -* [Install](#install) - * [Install Interactively from MELPA](#install-interactively-from-melpa) - * [Install Automatically from MELPA](#install-automatically-from-melpa) - * [Install from Git Source](#install-from-git-source) -* [Use Devil](#use-devil) -* [Typing Commas](#typing-commas) -* [Devil Reader](#devil-reader) -* [Translation Rules](#translation-rules) -* [Translation Examples](#translation-examples) -* [Bonus Key Bindings](#bonus-key-bindings) -* [Custom Configuration Examples](#custom-configuration-examples) - * [Local Mode](#local-mode) - * [Custom Appearance](#custom-appearance) - * [Custom Devil Key](#custom-devil-key) - * [Multiple Devil Keys](#multiple-devil-keys) -* [Why?](#why) -* [Comparison with God Mode](#comparison-with-god-mode) -* [Support](#support) -* [Channels](#channels) -* [More](#more) - - -Introduction ------------- - -Devil mode intercepts our keystrokes and translates them to Emacs key -sequences according to a configurable set of translation rules. For -example, with the default translation rules, when we type `, x , f`, -Devil translates it to `C-x C-f`. - -The choice of the comma key (`,`) to mean the control modifier key -(`C-`) may seem outrageous. After all, the comma is a very important -punctuation both in prose as well as in code. Can we really get away -with using `,` to mean the `C-` modifier? It turns out, this terrible -idea can be made to work without too much of a hassle. At least it -works for me! It might work for you too. If it does not, Devil can be -configured to use another key instead of `,` to mean the `C-` -modifier. See the section [Custom Devil Key](#custom-devil-key) for an -example. - -A sceptical reader may rightfully ask: If `,` is translated to `C-`, -how on earth are we going to insert a literal `,` into the text when -we need to? The section [Typing Commas](#typing-commas) answers this. -But before we get there, we have some fundamentals to cover. Take the -plunge and see what unfolds! Maybe you will like this! Maybe you will -not! If you do not like this, you can always retreat to God mode, Evil -mode, the vanilla key bindings, or whatever piques your fancy! - - -Notation --------- - -A quick note about the notation used in the document: The previous -example shows that `, x , f` is translated to `C-x C-f`. What this -really means is that the key sequence -,x,f is translated to -ctrl+x ctrl+f. We do not -really type any space after the commas. The key , is -directly followed by the key x. However, the key sequence -notation used in this document contains spaces between each keystroke. -This is consistent with how key sequences are represented in Emacs in -general and how Emacs functions like `key-description`, -`describe-key`, etc. represent key sequences. When we really need to -type a space, it is represented as `SPC`. - - -Install -------- - -### Install Interactively from MELPA - -Devil is available via [MELPA](https://melpa.org/). You may already -have a preferred way of installing packages from MELPA. If so, install -the package named `devil` to get Devil. For the sake of completeness, -here is a very basic way of installing Devil from MELPA: - - 1. Add the following to the Emacs initialization file (i.e., - `~/.emacs` or `~/.emacs.d/init.el` or `~/.config/emacs/init.el`): - - ```sh - (require 'package) - (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) - (package-initialize) - ``` - - 2. Start Emacs with the updated initialization file and then type - these commands: - - ``` - M-x package-refresh-contents RET - M-x package-install RET devil RET - ``` - - 3. Confirm that Devil is installed successfully with this command: - - ``` - M-x devil-show-version RET - ``` - - 4. Enable Devil mode with this command: - - ``` - M-x global-devil-mode RET - ``` - - 4. Type `, x , f` and watch Devil translate it to `C-x C-f` and - invoke the corresponding command. - - -### Install Automatically from MELPA - -Here is yet another basic way to install and enable Devil using Elisp: - -```elisp -(require 'package) -(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) -(package-initialize) -(unless package-archive-contents - (package-refresh-contents)) -(unless (package-installed-p 'devil) - (package-install 'devil)) -(global-devil-mode) -(global-set-key (kbd "C-,") 'global-devil-mode) -``` - -Now type `, x , f` and watch Devil translate it to `C-x C-f` and -invoke the corresponding command. Type `C-,` to disable Devil mode. -Type `C-,` again to enable it. - - -### Install from Git Source - -If you prefer obtaining Devil from its Git repository, follow these -steps: - - 1. Clone Devil to your system: - - ```sh - git clone https://github.com/susam/devil.git - ``` - - 2. Add the following to your Emacs initialization: - - ```elisp - (add-to-list 'load-path "/path/to/devil/") - (require 'devil) - (global-devil-mode) - (global-set-key (kbd "C-,") 'global-devil-mode) - ``` - - 3. Start the Emacs editor. Devil mode should now be enabled in all - buffers. The modeline of each buffer should show the `Devil` - lighter. - - 4. Type `, x , f` and watch Devil translate it to `C-x C-f` and - invoke the corresponding command. Type `C-,` to disable Devil - mode. Type `C-,` again to enable it. - - -Use Devil ---------- - -Assuming vanilla Emacs key bindings have not been changed and Devil -has not been customised, here are some examples that demonstrate how -Devil may be used: - - 1. Type `, x , f` and watch Devil translate it to `C-x C-f` and - invoke the find file functionality. - - 2. Type `, p` to move up one line. - - 3. To move up multiple lines, type `, p p p` and so on. Some Devil - key sequences are repeatable keys. The repeatable Devil key - sequences can be repeated by typing the last key of the Devil key - sequence over and over again. - - 4. Another example of a repeatable Devil key sequence is `, f f f` - which moves the cursor word by multiple characters. A few other - examples of repeatable keys are `, k k k` to kill lines, `, / / /` - to undo changs, etc. Type `C-h v devil-repeatable-keys RET` to see - the complete list of repeatable keys. - - 5. Type `, s` and watch Devil translate it to `C-s` and invoke - incremental search. - - 6. Type `, m s` and watch Devil translate it to `C-M-s` and invoke - regular-expression-based incremental search. Yes, `m` is - translated to `M-`. - - 7. Type `, m m x` and watch Devil translate it to `M-x` and invoke - the corresponding command. - - 8. Type `, u , f` and watch Devil translate it to `C-u C-f` and move - the cursor forward by 4 characters. - - 9. Type `, u u , f` and the cursor moves forward by 16 characters. - Devil uses its translation rules and an additional keymap to make - the input key sequence behave like `C-u C-u C-f` which moves the - cursor forward by 16 characters. - -10. Type `, SPC` to type a comma followed by space. This is a special - key sequence to make it convenient to type a comma in the text. - Note that this sacrifices the use of `, SPC` to mean `C-SPC` which - could have been a convenient way to set a mark. - -11. Type `, z SPC` and watch Devil translate it to `C-SPC` and set a - mark. Yes, `, z` is translated to `C-` too. - -12. Similarly, type `, RET` to type a comma followed by the return - key. This is another special key. - -13. Type `, ,` to type a single comma. This special key is useful for - cases when you really need to type a single literal comma. - - -Typing Commas -------------- - -Devil makes the questionable choice of using the comma as its -activation key. As illustrated in the previous section, typing -`, x , f` produces the same effect as typing `C-x C-f`. One might -naturally wonder how then we are supposed to type literal commas. - -Most often when we edit text, we do not really type a comma in -isolation. Often we immediately follow the comma with a space or a -newline. This assumption usually holds good while editing regular -text. However, this assumption may not hold in some situations, like -while working with code when we need to add a single comma at the end -of an existing line. - -In scenarios where the above assumption holds good, typing `, SPC` -inserts a comma and a space. Similarly, typing `, RET` inserts a comma -and a newline. - -In scenarios, when we do need to type a single comma, type `, ,` instead. - -Also, it is worth mentioning here that if all this fiddling with the -comma key feels clumsy, we could always customise the Devil key to -something else that feels better. We could also disable Devil mode -temporarily and enable it again later with `C-,` as explained in -section [Get Started](#get-started). - - -Devil Reader ------------- - -The following points briefly describe how Devil reads Devil key -sequences, translates them to Emacs key sequences, and runs commands -bound to the key sequences: - - 1. As soon as the Devil key is typed (which is `,` by default), Devil - wakes up and starts reading Devil key sequences. Type `C-h v - devil-key RET` to see the current Devil key. - - 2. After each keystroke is read, Devil checks if the key sequence - accumulated is a special key. If it is, then the special command - bound to the special key is executed immediately. Note that this - step is performed before any translation rules are applied to the - input key sequence. This is how the Devil special key sequence `, - SPC` inserts a comma and a space. Type `C-h v - devil-special-keys RET` to see the list of special keys and - the commands bound to them. - - 3. If the key sequence accumulated so far is not a special key, then - Devil translates the Devil key sequence to a regular Emacs key - sequence. If the regular Emacs key sequence turns out to be a - complete key sequence and some command is found to be bound to it, - then that command is executed immediately. This is how the Devil - key sequence `, x , f` is translated to `C-x C-f` and the - corresponding binding is executed. If the translated key sequence - is a complete key sequence but no command is bound to it, then - Devil displays a message that the key sequence is undefined. Type - `C-h v devil-translations RET` to see the list of translation - rules. - - 4. After successfully translating a Devil key sequence to an Emacs - key sequence and executing the command bound to it, Devil checks - if the key sequence is a repeatable key sequence. If it is found - to be a repeatable key sequence, then Devil sets a transient map - so that the command can be repeated merely by typing the last - keystroke of the input key sequence. This is how `, p p p` moves - the cursor up by three lines. Type `C-h v devil-repeatable-keys - RET` to see the list of repeatable Devil key sequences. - -The variables `devil-special-keys`, `devil-translations`, and -`devil-repeatable-keys` may contain keys or values with the string -`%k` in them. This is a placeholder for `devil-key`. While applying -the special keys, translation rules, or repeat rules, each `%k` is -replaced with the actual value of `devil-key` before applying the -rules. - - -Translation Rules ------------------ - -The following points provide an account of the translation rules that -Devil follows in order to convert a Devil key sequence entered by the -user to an Emacs key sequence: - - 1. The input key vector read from the user is converted to a key - description (i.e., the string functions like `describe-key`, - `key-description`, produce). For example, if the user types - ,x,f, it is converted - to `, x , f`. - - 2. Now the resulting key description is translated with simple string - replacements. If any part of the string matches a key in - `devil-translations`, then it is replaced with the corresponding - value. For example, `, x , f` is translated to `C- x C- f`. Then - Devil normalises the result to `C-x C-f` by removing superfluous - spaces after the modifier keys. - - 3. However, if the simple string based replacement leads to an - invalid Emacs key sequence, it skips the replacement that causes - the resulting Emacs key sequence to become invalid. For example `, - m ,` results in `C-M-C-` after the simple string replacement - because the default translation rules replace `,` with `C-` and - `m` with `M-`. However, `C-M-C-` is an invalid key sequence, so - the replacement of the second `,` to `C-` is skipped. Therefore, - the input `, m ,` is translated to `C-M-,` instead. - - -Translation Examples --------------------- - -By default, Devil supports a small but peculiar set of translation -rules that can be used to avoid modifier keys while typing various -types of key sequences. See `C-h v devil-translations RET` for the -translation rules. Here are some examples that demonstrate the default -translation rules. The obvious ones are shown first first. The more -peculiar translations come later in the table. - -| Input | Translated | Remarks | -|-----------|------------|------------------------------------| -| `, s` | `C-s` | `,` is replaced with `C-` | -| `, m s` | `C-M-s` | `m` is replaced with `M-` | -| `, z s` | `C-SPC` | `, z` is replaced with `C-` too | -| `, z z` | `C-z` | ditto | -| `, m m x` | `M-x` | `, m m` is replaced with `M-` too | -| `, c , ,` | `C-c ,` | `, ,` is replaced with `,` | - -Note how we cannot use `, SPC` to set a mark because that key sequence -is already reserved as a special key sequence in `devil-special-keys`, -so Devil translates `, z` to `C-` too, so that we can still type -`C-SPC` using `, z s` and set a mark. - -Also, note how the translation of `, m m` to `M-` allows us to enter a -key sequence that begins with the `M-` modifier key. - - -Bonus Key Bindings ------------------- - -Devil adds the following additional key bindings only when Devil is -enabled globally with `global-devil-mode`: - -- Adds the Devil key to `isearch-mode-map`, so that Devil key - sequences work in incremental search too. - -- Adds `u` to `universal-argument-more` to allow repeating the - universal argument command `C-u` simply by repeating `u`. - -As mentioned before these features are available only when Devil is -enabled globally with `global-devil-mode`. If Devil is enabled locally -with `devil-mode`, then these features are not available. - - -Custom Configuration Examples ------------------------------ - -In the examples presented below, the `(require 'devil)` calls may be -omitted if Devil has been installed from MELPA. There are appropriate -autoloads in place in the Devil package that would ensure that it is -loaded automatically on enabling Devil mode. However, the `require` -calls have been included in the examples below for the sake of -completeness. - - -### Local Mode - -While the section [Get Started](#get-started) shows how we enable -Devil mode globally, this section shows how we can enable it locally. -Here is an example initialization code that enables Devil locally only -in text buffers. - -```elisp -(require 'devil) -(add-hook 'text-mode-hook 'devil-mode) -(global-set-key (kbd "C-,") 'devil-mode) -``` - -This is not recommended though because this does not provide a -seamless Devil experience. For example, with Devil enabled locally in -a text buffer like this, although we can type `, x , f` to launch the -find-file minibuffer, we cannot use Devil key sequences in the -minibuffer. Further the special keymaps described in the previous -section work only when Devil is enabled globally. - - -### Custom Appearance - -The following initialization code shows how we can customise Devil to -show a Devil smiley (😈) in the modeline and the echo area. - -```elisp -(require 'devil) -(setq devil-lighter " \U0001F608") -(setq devil-prompt "\U0001F608 %t") -(global-devil-mode) -(global-set-key (kbd "C-,") 'global-devil-mode) -``` - -This is how Emacs may look if emojis are rendered correctly: - -[![Screenshot of Emacs with Devil smiley][smiley-screenshot]][smiley-screenshot] - -[smiley-screenshot]: https://i.imgur.com/oYtwnGi.png - - -### Custom Devil Key - -The following initialization code shows how we can customise Devil to -use a different Devil key. - -```elisp -(defvar devil-key "") -(defvar devil-special-keys '(("%k %k" . (lambda () (interactive) (devil-run-key "%k"))))) -(require 'devil) -(global-devil-mode) -(global-set-key (kbd "C-") 'global-devil-mode) -``` - -The above example sets the Devil key to the left arrow key, perhaps -another dubious choice for the Devil key. With this configuration, we -can use ` x f` and have Devil translate it to `C-x C-f`. - -Additionally, the above example defines the `devil-special-keys` -variable to have a single entry that allows typing ` ` to -produce the same effect as the original ``. It removes the other -entries, so that ` SPC` is no longer reserved as a special key. -Thus ` SPC` can now be used to set a mark like one would -normally expect. - - -### Multiple Devil Keys - -While this package provides the comma (`,`) as the default and the -only Devil key, nothing stops you from extending the mode map to -support multiple Devil keys. Say, you decide that in addition to -activating Devil with `,` which also plays the role of `C-`, you also -want to activate Devil with `.` which must now play the role of `M-`. -To achieve such a result, you could use this initialization code as a -starting point and then customise it further based on your -requirements: - -```elisp -(defvar devil-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd ",") #'devil) - (define-key map (kbd ".") #'devil) - map)) -(defvar devil-special-keys '((", ," . (lambda () (insert ","))) - (". ." . (lambda () (insert "."))))) -(defvar devil-translations '(("," . "C-") - ("." . "M-"))) -(require 'devil) -(global-devil-mode) -``` - -With this configuration, we can type `, x , f` for `C-x C-f` like -before. But now we can also type `. x` for `M-x`. Similarly, we can -type `, . s` for `C-M-s` and so on. Further, `, ,` inserts a literal -comma and `. .` inserts a literal dot. - -Note that by default Devil configures only one activation key (`,`) -because the more activation keys we add, the more intrusive Devil -becomes during regular editing tasks. Every key that we reserve for -activating Devil loses its default function and then we need -workarounds to somehow invoke the default function associated with -that key (like repeating `.` twice to insert a single `.` in the above -example). Therefore, it is a good idea to keep the number of Devil -keys as small as possible. - - -Why? ----- - -Why go to the trouble of creating and using something like this? Why -not just remap caps lock to ctrl like every -other sane person does? Or if it is so important to avoid modifier -keys, why not use something like God mode or Evil mode? - -Well, for one, both God mode and Evil mode are modal editing modes. -Devil, on the other hand, provides a modeless editing experience of -Emacs as possible. - -Devil mode began as a fun little tiny experiment. From the outset, it -was clear that using something as crucial as the comma for specifying -the modifier key is asking for trouble. However, I still wanted to see -how far I could go with it. It turned out that in a matter of days, I -was using it full-time for all of my Emacs usage. - -This experiment was partly motivated by Macbook keyboards which do not -have a right ctrl key. Being a touch-typist myself, I found -it inconvenient to type key combinations like `C-x`, `C-a`, `C-w`, -`C-s`, etc. where both the modifier key and the modified key need to -be pressed with the left hand fingers. I am not particularly fond of -remapping caps lock to behave like ctrl because -that still suffers from the problem that key combinations like `C-x`, -`C-a` require pressing both the modifier key and the modified key with -the left hand fingers. I know many people remap both their caps -lock and enter to behave like ctrl. While -I think that is a fine solution, I was not willing to put up with the -work required to make that work seamlessly across all the various -operating systems I work on. - -What began as a tiny whimsical experiment a few years ago turned out -to be quite effective, at least to me. I like that this solution is -implemented purely as Elisp and therefore does not have any external -dependency. I am sharing this solution here in the form of a minor -mode, just in case, there is someone out there who might find this -useful too. - - -Comparison with God Mode ------------------------- - -God mode provides a modal editing experience but Devil does not. Devil -has the same underlying philosophy as that of God mode, i.e., the user -should not have to learn new key bindings. However, Devil does not -have a hard separation between insert mode and command mode like God -mode has. Instead, Devil waits for an activation key (`,` by default) -and as soon as it is activated, it intercepts and translates keys, -runs the corresponding command, and then gets out of the way. So Devil -tries to retain the modeless editing experience of vanilla Emacs as -much as possible. - -Now it is worth mentioning that some of this modeless editing -experience can be reproduced in god-mode too using its -`god-execute-with-current-bindings` function. Here is an example: - -```elisp -(global-set-key (kbd ",") #'god-execute-with-current-bindings) -``` - -With this configuration, God mode translates `, x f` to `C-x C-f`. -Similarly `, g x` invokes `M-x` and `, G s` invokes `C-M-x`. This -provides a modeless editing experience in God mode too. However, this -experience does not extend seamlessly to minibuffers. Devil does -extend its Devil key translation to minibuffers. - -Further note that in God mode the ctrl modifier has sticky -behaviour, i.e., the modifier remains active automatically for the -entire key sequence. Therefore in the above example, we type `,` only -once while typing `, x f` to invoke `C-x C-f`. However, this sticky -behaviour implies that we need some way to disambiguate between key -sequences like `C-x C-o` (delete blank lines) and `C-x o` (other -window). God mode solves this by introducing `SPC` to deactivate the -modifier, e.g., `, x o` translates to `C-x C-o` but `, x SPC o` -translates to `C-x o`. Devil does not treat the modifier key as sticky -which leads to simpler key sequences at the cost of a little -additional typing, i.e., `, x , o` translates to `C-x C-o` and `, x o` -translates to `C-x o`. - -To summarize, there are primarily three things that Devil does -differently: - - - Provide a modeless editing experience from the outset. - - Seamlessly extend the same editing experience to minibuffer, - incremental search, etc. - - Translate key sequences using string replacements. This allows for - arbitrary and sophisticated key translations for the adventurous. - - Choose non-sticky behaviour for the modifier keys. - -These differences could make Devil easier to use than God mode for -some people but clumsy for other people. It depends on one's tastes -and preferences. - - -Support -------- - -To report bugs, suggest improvements, or ask questions, -[create issues][ISSUES]. - -[ISSUES]: https://github.com/susam/devil/issues - +Read the [manual](./MANUAL.org) for more details on how to use Devil. Channels -------- -- 2.39.2