6

I can only find examples of setting Readline key bindings using Control (\C-) or Escape (\e) as prefixes.

In my case, on macOS, the \C- space is completely filled up by default key bindings, and the \e space is not practical on a Macbook with a touchbar.

Entering "\M-f": kill-word in ~/.inputrc results in:

bind -P | grep -F "\M-"
kill-word can be found on "\e[3;5~" "\ed".

But kill-word cannot be executed using either of Option, Command, or fn as prefixes - Readline ignores it.

Is this issue specific to macOS, and how can I solve it?

Furthermore, how can I control the "timeout" that should occur before a key binding, that is a prefix of a longer key binding, is executed?

@added (@laktak): bind '"^[f": kill-word' doesn't work, but instead results in ƒ on the terminal: enter image description here

Kevin E
  • 468
Shuzheng
  • 4,411
  • Does using something other than f work? Since (at least on my Linux) \M-f is already bound to forward-word, perhaps that's causing a conflict. – terdon Sep 08 '19 at 11:35
  • Not in my case, bind -q forward-word results in forward-word can be invoked via "\e[1;3C", "\e[1;5C", "\ef". – Shuzheng Sep 08 '19 at 11:38
  • Do you know if Readline only allows \e and \C as special prefixes? – Shuzheng Sep 08 '19 at 11:39
  • Well yes, \ef is M-f. Does it work if you use another letter? – terdon Sep 08 '19 at 11:42
  • What do you mean by another letter? The \ef is only invoked, if I press the Escape key on the touchbar. Do you know if it's possible to map Option to Escape? And do you know how to control the "timeout", I mention :) ? – Shuzheng Sep 08 '19 at 11:44
  • I mean not f. Use Meta and another letter, one that isn't already used anywhere. One of those returned by for i in {a..z} {A..Z}; do bind -P | grep -q "\"\\\e$i" || echo $i; done. – terdon Sep 08 '19 at 11:49
  • You should really configure your terminal emulator to do the equivalent of xtem's *VT100.metaSendsEscape: true, and then bind your keys as "\ef" instead of "\M-f". See my comments here –  Sep 08 '19 at 12:09
  • @mosvy, I would love to use \e, but it's a pain to press Escape on a Macbook with touchbar. – Shuzheng Sep 08 '19 at 13:02
  • 1
    Your terminal emulator should send an Escape, f to the program inside the terminal when you press Option + F or whatever. Or it can be configured to do that. –  Sep 08 '19 at 13:10
  • @mosvy - thanks. I will try that. Do you also know how to control the "timeout" before Readline executes a key binding that's a prefix of another key binding? – Shuzheng Sep 08 '19 at 13:17
  • set keyseq-timeout 10 –  Sep 08 '19 at 13:18
  • ... in your ~/.inputrc. or directly in bash with bind 'set keyseq-timeout 10' –  Sep 08 '19 at 13:25
  • @mosvy, thanks, but your comments doesn't seem to work for macOS. The terminal still prints "ƒ", when pressing Option+f. – Shuzheng Sep 08 '19 at 13:27
  • I don't have a MacOS, but your terminal certainly has an option to send escape, f when pressing Option+F. Or if it doesn't, use another terminal or just bind '"ƒ": ... to whatever your want. –  Sep 08 '19 at 13:32
  • Thanks @mosvy! - it works by setting bind '"ƒ": kill-word'. But bind -q kill-word lists it as "?\222". How can I see that ƒ corresponds to "?\222"? – Shuzheng Sep 08 '19 at 13:47
  • you use bind -q kill-word | od -c, see that the '?' before \222 is octal 306, then printf "\306\222\n" will tell you that it really is ƒ (yes, this is another bash/readline bug). –  Sep 08 '19 at 22:55
  • if you remove the XXX "experimental code" which still assumes latin1 instead of multibyte/utf-8 and recompile bash, the binding will be printed as "ƒ" instead of "<FROG>\222". You should submit a bug report. –  Sep 08 '19 at 23:07
  • @mosvy, is it also possible to go get \306\222 from the ƒ (the other direction)? – Shuzheng Sep 09 '19 at 05:15
  • I'm not sure I get what you mean, but you can try echo ƒ | perl -pe 's/(.)/sprintf"\\%03o",ord$1/ge' –  Sep 09 '19 at 06:26

4 Answers4

7

I don't think you're doing anything wrong, you just need to tell Terminal.app to use the "Option" key as "Meta" instead. Just go to the preferences for Terminal (+,), then Profiles, then the Keyboard tab for your default profile and check "Use Option as Meta key."

Screenshot of Terminal.app "Profiles" → "Keyboard" settings

Otherwise, you're getting the default macOS behavior (in most keymaps) where Option + key produces special characters, just like Option+f yields the small letter "f" with hook from your screenshot (complete reference here).

In iTerm 2, I believe the left Option key already does the sensible thing by default, but just in case, here's how to change that:

iTerm 2 Option key behavior

As far as remapping Option to Escape, if that's a thing you still wanted to do, that can be accomplished from the Keyboard prefpane in System Preferences, by clicking the "Modifier Keys…" button near the bottom of the window.

macOS System Settings Keyboard prefpane

Then remap it to whatever you please (from the available choices):

Remapping modifier keys in the macOS Keyboard prefpane

I hit up this preference pane in about the first five minutes of using a new Mac, just so I can remap Caps Lock to Control, since the Control key on a Mac keyboard is in an awkward spot.

Kevin E
  • 468
  • I should add that the option key in macOS is faaaaar too useful to be remapping to something else. As an experiment, switch to Finder and open any menu (even the Ctrl+click / right-click context menu) and watch what happens when you press and release the "Option" key. – Kevin E May 31 '20 at 13:42
2

The utility would be:

showkey (1)          - examine the codes sent by the keyboard

but this seems to be missing on mac-os...here it is "Key Codes"...?

Or you type ctrl-V (qouted insert) and then some special key. In bash, this prints ^[OP in xterm and ^[[[A in the console for the F1 key.

The readline variable:

keyseq-timeout (500)

is the timeout to use for a half-finished sequence.

Underneath this, there must be a sort of keymap file, where the keys get translated to symbols. This is from /usr/share/kbd/keymaps/mac/ on linux:

keycode 51 = Delete  Remove
        alt keycode 51 = Meta_Delete
        shift alt keycode 51 = Meta_Delete
        control keycode  51 = Remove
keycode 53 = Escape
        alt keycode 53 = Meta_Escape
        shift alt keycode 53 = Meta_Escape
keycode  54 = Control
keycode  55 = Alt         # Command/Apple key
keycode  56 = Shift
keycode  57 = Caps_Lock
keycode  58 = AltGr       # Alt/Option key

You see the flexibilty! The Meta-Delete symbol could become kill-word...


bind '"^[f": kill-word

This ^[ must be a \e, or a ctrl-V, then Escape: one byte, not circumflex plus bracket.


It is like saying: ^C means control-C, not a Shift-6 and then a Shift-c...in the shell (bash) or in vim when I type ctrl-v and then Esc I get ^[, but it is just one character when you step back over it. Together with these meta-flag options in readline it can get complicated...I just illustrate the whole chain:

keymap "default.map" (translates scancode and modifier to keysymbols)

keycode 105 = Left F150 F151
  string F150 = "\033[150"
  string F151 = "\033[151"
keycode 106 = Right F154 F155
  string F154 = "\033[154"
  string F155 = "\033[155"

The F150 and F151 after default "Left" mean shift- and control-left-arrow. In a second step you can define a string, an escape sequence in this case. Here, \e did not work, but the octal 033 is ascii 27 is control-[ is Escape...this keymap is linux specific, but X Windows has a similar logic.

And in .inputrc:

"\e[150": backward-word 
"\e[151": shell-backward-word 
"\e[154": forward-word 
"\e[155": shell-forward-word 

This works very nice in the linux console. In xterm under X Windows (Xorg): not at all: Xorg scans the keys itself.

backward-word can be invoked via "\e[150", "\e[1;2D", "\e[1;3D", "\eb".

The two in the middle with semicolon are VTxxx style modified left arrows. This was preconfigured, and also gets bound to backward-word automatically.

  • Thanks is keyseq-timeout in milliseconds? Do you see it in man bash, or where should I look? You're right, showkey is not available on macOS through brew. I cannot get you Ctrl-V trick to work in iTerm :( – Shuzheng Sep 08 '19 at 18:24
  • What do you mean by "console"? Isn't xterm the same as the console? – Shuzheng Sep 08 '19 at 18:26
  • I'm not sure what you mean by "This ^[ must be a \e, or a ctrl-V, then Escape: one byte, not circumflex plus bracket."? Could I use ^[ instead of \e? – Shuzheng Sep 08 '19 at 18:34
  • xterm is a (VT220) terminal emulator in a GUI. The linux console is the built-in virtual tty. The example (F1 key) in my answer shows one difference. xterm uses XKB from X Windows for key mapping, modifiers, kbd layout. I see now I have kill-word on \e[3;5~ like you --- this is Control-Delete for X. On the console the Delete and arrow keys did not have any Shift- or Control mofifiers defined. –  Sep 08 '19 at 20:03
  • thanks. Off-topic: how do I identify whether I'm talking to the Linux console built-in virtual tty, or some other terminal emulator? – Shuzheng Sep 09 '19 at 05:10
  • The $TERM variable is used for that. Vim has an option -T to override it. After "vim -T linux" in xterm (or "TERM=linux vim") the modified arrow doesn't work. In a xterm shell $TERM is "xterm", on a linux vt it is "linux" . I understand xterm uses the terminfo "database" to map the XKB escape sequences to certain well-known functions. All this seems totally screwed up, but it just grew like that historically. Wikipedia has good articles on how things evolved. –  Sep 09 '19 at 05:58
0

If I understood your question correctly:

bind '"^[f": kill-word'

Will define Option+F to invoke kill-word

laktak
  • 5,946
  • Thanks, @laktak. How do I see that ^[f means Option? Are there also symbols for e.g. Command? Also, do you know the answer to, how to control the "timeout" before Readline uses a key binding that's a prefix of another key binding? – Shuzheng Sep 08 '19 at 13:04
  • your solution doesn't seem to work. Option+f results in ƒ in the terminal. – Shuzheng Sep 08 '19 at 13:15
  • @Shuzheng press Control+v (at the prompt) and then type the key combination you want to use - this should show you the keycode for the bind. – laktak Sep 08 '19 at 14:57
0

The other option is to use Esc and then hit the d button, with that you can simulate the same keybinding.

Same as pasting the last argument of the previous command (M-.) could be replaced by hitting Esc first and then ..

To help myself on my MacBooks starting with the Touch Bar versions I've remapped the Caps Lock ⇪ with ⎋ Escape

Remapping Caps Lock