Thomas
Thomas

Reputation: 17422

M-S-<left> not processed as expected

When I start a new emacs in a terminal like so

emacs -nw -Q

I can try to check what a certain keyboard shortcut is bound to by typing C-h k. When I do this for the shortcut M-S-<left> in the *scratch* buffer right after starting Emacs, I get:

<M-left> runs the command left-word (found in global-map), which is an interactive compiled Lisp function in ‘bindings.el’.

It is bound to <C-left>, <M-left>.

[...]

Note the absence of the "Shift" modifier in the recognized key sequence.

Now, the first instinct would be that the terminal (gnome-terminal in my case) does not produce the correct escape sequence, but it does: typing CTRL-V ALT-SHIFT-left in the terminal (i.e., not in Emacs) produces

^[[1;4D

and typing C-q C-S-<left> inside Emacs inserts the same into the current buffer. (Note that ^[ is a single character, namely ASCII 27 ("Escape").)

Checking input-decode-map reveals the following value (some parts left out (marked as ...) for brevity):

(keymap
 (27 keymap ... )
 keymap
 (keymap
  (27 keymap
      ...
      (91 keymap
          ...
          (51 keymap
              ...
              (59 keymap
                  (51 keymap
                      ...
                      (68 .
                          [M-left])
                  (52 keymap
              ...
                      (68 .
                          [M-S-left])
                      ...)
                  ...)
              ...)
          ...))))

Verifying the two key sequences with the following code gives the expected results:

(mapc '(lambda (c) (insert (format "%c" c))) '(27 91 49 59 51 68)) ; ^[[1;3D
(mapc '(lambda (c) (insert (format "%c" c))) '(27 91 49 59 52 68)) ; ^[[1;4D

However, I don't understand why these sequences are embedded in a second-level inner (keymap ...) list?!

It gets weirder.

Back in *scratch*, when I type

M-x local-set-key

and then M-S-<left>, it gets recognized correctly, as the next prompt in the minibuffer reveals:

Set key <M-S-left> locally to command:

If I proceed and provide a random function, say, beginning-of-line, a subsequent invocation of C-h k followed by M-S-<left> then indeed gives:

<M-S-left> runs the command beginning-of-line (found in lisp-interaction-mode-map), which is an interactive built-in function in ‘C source code’.

It is bound to <M-S-left>.

[...]

Locally setting M-S-<left> to nil will restore the initial behavior in which C-h k reports M-S-<left> as <M-left>.

What is going on here? Is this a configuration issue, or is it intended behavior, or are some other keymaps involved that transparently add another layer of remapping?

Upvotes: 3

Views: 608

Answers (1)

phils
phils

Reputation: 73246

It's a feature.

M-x elisp-index-search RET shift-translation says:

If an input character is upper-case (or has the shift modifier) and has no key binding, but its lower-case equivalent has one, then ‘read-key-sequence’ converts the character to lower case. Note that ‘lookup-key’ does not perform case conversion in this way.

When reading input results in such a “shift-translation”, Emacs sets the variable ‘this-command-keys-shift-translated’ to a non-‘nil’ value. Lisp programs can examine this variable if they need to modify their behavior when invoked by shift-translated keys. For example, the function ‘handle-shift-selection’ examines the value of this variable to determine how to activate or deactivate the region (*note handle-shift-selection: The Mark.).

Upvotes: 3

Related Questions