Org Speed Keys! BOOM! Great org-mode's feature! And a good OPPORTUNITY to talk about self-insert-command
Hey Emacsers,
How are you doing?
This post is "pretty" special to me because I can still remember and feel:
the first time I discovered Org Speed Keys and,
the first time I took a look at its implementation.
It was two AHA moments, one in my Emacs journey and the other in my Elisp journey.
I hope you'll enjoy it :)
Org Speed Keys
Org speed keys is not a minor mode but an org-mode feature that can be
turned on by setting the variable org-use-speed-commands to t
.
It allows when the cursor is at a the beginning of a heading to call a command by pressing a single "printing" key (keys that would normally insert a character in the buffer).
For instance, if we are at the beginning of a heading, and we press
n
, the cursor moves to the next visible heading (or stays at the
current heading if it's the last one in the buffer) if we use the
default bindings provided by org-mode in the variable
org-speed-commands.
It is "almost" the same that pressing C-c C-n
but in SHORTER. Isn't it
super cool???
If you've never tried it, go for it.
Just run:
M-x eval-expression RET (setq org-use-speed-commands t)
Open one of your org documents then with the cursor at at the very
beginning of a heading press n
or p
repeatedly.
We can see org speed command bindings, by typing ?
(which call
lisp/org-keys.el) with the cursor at the very beginning of a
heading. This pops up the following help buffer:
Speed commands
==============
Outline Navigation
------------------
n (org-speed-move-safe 'org-next-visible-heading)
p (org-speed-move-safe 'org-previous-visible-heading)
f (org-speed-move-safe 'org-forward-heading-same-level)
b (org-speed-move-safe 'org-backward-heading-same-level)
F org-next-block
B org-previous-block
u (org-speed-move-safe 'outline-up-heading)
j org-goto
g (org-refile '(4))
Outline Visibility
------------------
c org-cycle
C org-shifttab
org-display-outline-path
s org-toggle-narrow-to-subtree
k org-cut-subtree
= org-columns
... THERE ARE MORE ...
? org-speed-command-help
If we want to use our own bindings, we can modify:
the variable org-speed-commands since org-mode 9.5,
the variable
org-speed-commands-user
for previous versions.
Note: the variable org-speed-commands-user
has been made obsolete
since org-mode 9.5 but it is still supported and should be removed
from org-mode 9.6 as we can read in the source code lisp/org-keys.el:
;; FIXME: don't check `org-speed-commands-user' past 9.6
How does org-mode implement Org Speed keys?
Remapping self-insert-command
When org-mode is turned on, among other initialization tasks it remaps the command self-insert-command to the command org-self-insert-command in the keymap org-mode-map as we can see in the file lisp/org-keys.el:
(org-remap org-mode-map
'self-insert-command 'org-self-insert-command
'delete-char 'org-delete-char
'delete-backward-char 'org-delete-backward-char
'kill-line 'org-kill-line
'open-line 'org-open-line
'yank 'org-yank
'comment-dwim 'org-comment-dwim
'move-beginning-of-line 'org-beginning-of-line
'move-end-of-line 'org-end-of-line
'forward-paragraph 'org-forward-paragraph
'backward-paragraph 'org-backward-paragraph
'backward-sentence 'org-backward-sentence
'forward-sentence 'org-forward-sentence
'fill-paragraph 'org-fill-paragraph
'delete-indentation 'org-delete-indentation
'transpose-words 'org-transpose-words)
In the case of self-insert-command, the preceding remapping can be written like this (this is what org-remap does):
(define-key org-mode-map [remap self-insert-command] 'org-self-insert-command)
As a consequence, in org-mode, when we press a printing key (that
would by default insert a character in the buffer), let say we press n
,
the "command loop editor" does several things:
it performs a key lookup for
n
in the current active maps and find thatn
is bound to self-insert-command (due to the default binding in the keymap global-map which is the global map by default), then,it checks if the command self-insert-command has a remapping in the current active maps (with the function command-remapping),
it finds that self-insert-command is remapped to org-self-insert-command (due to the remapping in the keymap org-mode-map) and instead of calling self-insert-command, it calls org-self-insert-command.
Now, the command org-self-insert-command has the complete power over
the actions to perform. Nothing forces it to insert the character n
in the buffer.
If it is magic, this is org-self-insert-command
Before talking about how org-self-insert-command performs speed keys, let me ask you some questions:
Have you ever noticed that when you modify the title of a headline the tags are automatically re-aligned? Guess what? This is org-self-insert-command in action (calling org-fix-tags-on-the-fly just after inserting a character).
Have you ever noticed that inserting less characters in a table field than its width doesn't move the right bar of the field? This is org-self-insert-command in action.
Did you know that in a table, if you type printing keys just after moving with
TAB
,S-TAB
,RET
, the table field is made blank before inserting the characters (with org-table-auto-blank-field set tot
)? This is also org-self-insert-command (see Inside Emacs #6 (part 5) Why is it so fast to edit tables with org-table?).
org-self-insert-command implementation
Let's get back to our example. We've pressed the "printing" key n
and
"the command loop editor" has called org-self-insert-command.
The command org-self-insert-command does the following (assuming we
have set org-use-speed-commands to t
):
locally set the variable
kv
to the vector[?n]
corresponding to the key sequence that invoked the command org-self-insert-command (the "printing" keyn
we've pressed),run the hook org-speed-command-hook "until success" with the argument
"n"
(a string, corresponding to the evaluation of(make-string 1 (aref [?n] 0))
knowing thatkv
is[?n]
). Considering that the hook org-speed-command-hook is equal to the list(org-speed-command-activate org-babel-speed-command-activate)
by default, the function run-hook-with-args-until-success evaluate successively the forms:(org-speed-command-activate "n")
,(org-babel-speed-command-activate "n")
,
stopping at the first one that returns non-nil, and return that value, or return
nil
if both evaluate tonil
.Then, this returned value becomes the value of the variable
org-speed-command
,if
org-speed-command
is either a command, a function or a non empty list,org-speed-command
is called or evaluated (and org-self-insert-command returns "maybe" witout insertingn
in the buffer). Iforg-speed-command
is neither a command, a function nor a non empty list, the command org-self-insert-command is called with the variable org-use-speed-commands locally set tonil
which leads to skip the first clause of the special formcond
in the body of org-self-insert-command, and org-self-insert-command among other "actions" will insert the charactern
in the buffer.
Below you can see the parts of the command org-self-insert-command related to the speed keys feature that we have just discussed:
(defun org-self-insert-command (N)
;; ...
(interactive "p")
(cond
((and org-use-speed-commands
(let ((kv (this-command-keys-vector)))
(setq org-speed-command
(run-hook-with-args-until-success
'org-speed-command-hook
(make-string 1 (aref kv (1- (length kv))))))))
(cond
((commandp org-speed-command)
(setq this-command org-speed-command)
(call-interactively org-speed-command))
((functionp org-speed-command)
(funcall org-speed-command))
((and org-speed-command (listp org-speed-command))
(eval org-speed-command))
(t (let (org-use-speed-commands)
(call-interactively 'org-self-insert-command)))))
((and
;; ...
(org-at-table-p)
;; ....
)
(self-insert-command N)
;; ...
)
(t
;; ...
(self-insert-command N)
(org-fix-tags-on-the-fly)
;; ...
)))
org-speed-command-activate
We still have one function to cover: org-speed-command-activate (knowing that org-babel-speed-command-activate does "almost the same thing" but for the cursor at the beginning of source blocks).
Indeed, continuing with our example, if evaluating
(org-speed-command-activate "n")
returns a command (a function or a
non empty list) this command will be called instead of inserting the
character "n"
.
In the simple case, when org-use-speed-commands is set to t
(it could
also be a function), org-speed-command-activate checks:
if the point (the cursor) is at the beginning of line
(bolp)
and,if this line is a heading
(looking-at org-outline-regexp)
.
If true, org-speed-command-activate looks for the key "n"
in the alist
org-speed-commands defined by default like this:
(defcustom org-speed-commands
'(("Outline Navigation")
("n" . (org-speed-move-safe 'org-next-visible-heading))
("p" . (org-speed-move-safe 'org-previous-visible-heading))
("f" . (org-speed-move-safe 'org-forward-heading-same-level))
("b" . (org-speed-move-safe 'org-backward-heading-same-level))
("F" . org-next-block)
("B" . org-previous-block)
;; ...
))
and returns the s-exp associated with the key "n"
which is by default:
(org-speed-move-safe 'org-next-visible-heading)
As we left aside the case where org-use-speed-commands is a function
and we anticipate the org-mode version 9.6, we can write a simplified
version of org-speed-command-activate, that we call
org-speed-command-activate-SIMPLE
, like this:
(defun org-speed-command-activate-SIMPLE (keys)
(when (and (bolp) (looking-at org-outline-regexp))
(cdr (assoc keys org-speed-commands))))
So, when we presse the key n
(in org-mode):
org-babel-next-src-block
if we are at the beginning of a heading, the cursor is moved "safely" to the next visible heading,
if we are at the beginning of a source block (due to org-babel-speed-command-activate in the hook org-speed-command-hook), the cursor is moved to the next source block,
and if we were anywhere else, the character
n
is inserted in the buffer.
We are done with the mechanism of Org Speed Keys.
Let's talk a bit about the command self-insert-command.
self-insert-command
Bindings of printing characters to self-insert-command in the keymap global-map
In this section, our goal is to see what are the printing characters that are bound to the command self-insert-command.
In the info node elisp#Controlling Active Maps, we can read the following:
-- Variable: global-map
This variable contains the default global keymap that maps Emacs
keyboard input to commands. The global keymap is normally this
keymap. The default global keymap is a full keymap that binds
‘self-insert-command’ to all of the printing characters.
Indeed, the variable global-map is defined in the file lisp/subr.el and is set to be the global keymap (using use-global-map) as we can see:
(defvar global-map
(let ((map (make-keymap)))
;; ...
(define-key map "\C-i" #'self-insert-command)
(let* ((vec1 (make-vector 1 nil))
(f (lambda (from to)
(while (< from to)
(aset vec1 0 from)
(define-key map vec1 #'self-insert-command)
(setq from (1+ from))))))
(funcall f #o040 #o0177)
(when (eq system-type 'ms-dos) ;FIXME: Why?
(funcall f #o0200 #o0240))
(funcall f #o0240 #o0400))
(define-key map "\C-a" #'beginning-of-line)
(define-key map "\C-b" #'backward-char)
(define-key map "\C-e" #'end-of-line)
(define-key map "\C-f" #'forward-char)
;; ...
map)
"..."
)
(use-global-map global-map)
Many bindings in the keymap global-map are added in other files. For
instance the key sequences C-g
, C-u
, C-k
, C-w
and C-y
are added to
global-map in the file bindings.el like this:
(define-key global-map "\C-g" 'keyboard-quit)
(define-key global-map "\C-u" 'universal-argument)
(define-key global-map "\C-k" 'kill-line)
(define-key global-map "\C-w" 'kill-region)
(define-key global-map "\C-y" 'yank)
Now, let's focus in the let*
binding in the definition of the keymap
global-map. In this let*
binding, we define a function f
that binds in
the local keymap map
all the characters (represented as integer)
between from
to to
(excluded), and we apply it to the following
limits (excluding the ms-dos case):
from
#o040
to#o0177
,from
#o0240
to#o0400
.
If it is not clear what are the printing characters that are bound to self-insert-command in the keymap global-map we can "list" them like this:
(with-temp-buffer
(dotimes (i (- #o0177 #o040))
(insert (+ #o040 i)))
(buffer-string))
;; " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
(with-temp-buffer
(dotimes (i (- #o0400 #o0240))
(insert (+ #o0240 i)))
(buffer-string))
;; " ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
Remapping self-insert-command to undefined in Info-mode, help-mode and special-mode
In some cases (like in Info-mode and help-mode) we want to override all printing keys to be undefined before remapping some of them to specific commands.
This is done by remapping in the local keymap the command self-insert-command to the function undefined.
Note that remapping self-insert-command to nil
in the local keymaps
won't override self-insert-command in the global map as we can read in
the info node elisp#Key Lookup:
The symbol ‘undefined’ is worth special mention: it means to treat
the key as undefined. Strictly speaking, the key is defined, and
its binding is the command ‘undefined’; but that command does the
same thing that is done automatically for an undefined key: it
rings the bell (by calling ‘ding’) but does not signal an error.
‘undefined’ is used in local keymaps to override a global key
binding and make the key undefined locally. A local binding of
‘nil’ would fail to do this because it would not override the
global binding.
In the case of Info-mode this is done by calling the function
suppress-keymap (which overrides all printing keys to be undefined,
maps -
to the command negative-argument and the numbers 0,1,..., 9 to
the command digit-argument), when defining the keymap Info-mode-map as
follow:
(defvar Info-mode-map
(let ((map (make-keymap)))
(suppress-keymap map)
;; ...
(define-key map "1" 'Info-nth-menu-item)
(define-key map "2" 'Info-nth-menu-item)
;;...
(define-key map "n" 'Info-next)
(define-key map "p" 'Info-prev)
(define-key map "q" 'quit-window)
;;...
map)
"...")
In the case of help-mode this is done by setting the parent keymap of
help-mode-map to be a composed keymap of button-buffer-map and the
parent map special-mode-map (which overrides all printing keys to be
undefined, maps -
to the command negative-argument and the numbers
0,1,..., 9 to the command digit-argument), as we can see below:
(defvar help-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map (make-composed-keymap button-buffer-map
special-mode-map))
(define-key map "n" 'help-goto-next-page)
(define-key map "p" 'help-goto-previous-page)
;; ...
map)
"..."
)
Best function name ever: bitch_at_user
What a beautiful name for a function!
This is a C
function we can find in Emacs source code. If I'm not
wrong it is called in the body of 3 functions. Mainly, this function
when called will ring the bell. This function is defined in the file
src/dispnew.c as follow:
void
bitch_at_user (void)
{
if (noninteractive)
putchar (07);
else if (!INTERACTIVE) /* Stop executing a keyboard macro. */
{
const char *msg
= "Keyboard macro terminated by a command ringing the bell";
Fsignal (Quser_error, list1 (build_string (msg)));
}
else
ring_bell (XFRAME (selected_frame));
}
Why talking about this function?
Because its name is AMAZING and because I encounter it when I was reading the source code of self-insert-command.
Indeed, the function bitch_at_user is called in the body of self-insert-command when the command self-insert-command is invoked by a key sequence that is not a printing key.
For instance, we can trigger it by binding for the key sequence C-<f1>
(pick any key sequence you are not using) to self-insert-command like
this:
(global-set-key (kbd "C-<f1>") 'self-insert-command)
And now, if we press C-<f1>
, we trigger the function bitch_at_user and:
we see
Wrong type argument: characterp, C-f1
in the echo area and,depending on the "setting of the bell" (see elisp#Beeping) a) nothing more happens, b) we hear the bell ringing, or c) we see the screen flashing.
AS NO SENTENCE CAN BEAT THE SOURCE CODE, here is the source code of self-insert-command defined in the file src/cmds.c:
DEFUN ("self-insert-command", Fself_insert_command, Sself_insert_command, 1, 2,
"(list (prefix-numeric-value current-prefix-arg) last-command-event)",
doc: /* Insert the character you type.
Whichever character C you type to run this command is inserted.
The numeric prefix argument N says how many times to repeat the insertion.
Before insertion, `expand-abbrev' is executed if the inserted character does
not have word syntax and the previous character in the buffer does.
After insertion, `internal-auto-fill' is called if
`auto-fill-function' is non-nil and if the `auto-fill-chars' table has
a non-nil value for the inserted character. At the end, it runs
`post-self-insert-hook'. */)
(Lisp_Object n, Lisp_Object c)
{
CHECK_FIXNUM (n);
/* Backward compatibility. */
if (NILP (c))
c = last_command_event;
if (XFIXNUM (n) < 0)
error ("Negative repetition argument %"pI"d", XFIXNUM (n));
if (XFIXNAT (n) < 2)
call0 (Qundo_auto_amalgamate);
/* Barf if the key that invoked this was not a character. */
if (!CHARACTERP (c))
bitch_at_user ();
else {
int character = translate_char (Vtranslation_table_for_input,
XFIXNUM (c));
int val = internal_self_insert (character, XFIXNAT (n));
if (val == 2)
Fset (Qundo_auto__this_command_amalgamating, Qnil);
frame_make_pointer_invisible (SELECTED_FRAME ());
}
return Qnil;
}
WE ARE DONE!!!
Q&A
Check Q&A.