Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complex keybinding in bash

Tags:

bash

readline

Is there a way to combine two operations into one keybinding (dont think would work with function).

This is what I'd like to do:

I'd like a keybinding (say Ctrl-X) to -

  1. insert some text, then
  2. invoke the complete or menu-complete, using the inserted text as the basis for the completion

I know that I can (in ~/.inputrc) specify

  • Insertion of text with (C-X: "ls")
  • Execute readline commands (C-SPACE: menu-complete)

But I am not sure how to put these together

like image 400
nhed Avatar asked Dec 03 '11 08:12

nhed


2 Answers

The trick to this is to call functions which rebinds your keys. In my example I'll use C-b to insert text and to call menu-complete, instead of C-x. You'll have to sacrifice a key, in my example C-t

In .bashrc, or a bash file to be sourced

set_Cb_to_insert_text() {
  bind '"\C-m": accept-line'
  bind '"\C-b":"ls \C-t1"'
  bind -x '"\C-t1":set_Cb_to_complete'
}
set_Cb_to_complete() {
  bind '"\C-m":"\C-t2\C-t3"'
  bind '"\C-b": menu-complete'
  bind '"\C-t2": accept-line'
  bind -x '"\C-t3":set_Cb_to_insert_text'
}
set_Cb_to_insert_text

How this works:

With bind, you can bind keys to do one of three things, but no combination of them:

  • Execute a readline command: bind '"key": command'
  • Execute a series of keystrokes: bind '"key":"keystrokes"'
  • Execute a shell command: bind -x '"key": shell-command'

So if you want to combine these three things, you'll need to bind them each to a separate combination of keystrokes (in my example C-t{1,2,3}) and bind a key to execute all these keystrokes.

In the example:

C-b first inserts ls and 'presses' C-t1, which executes set_Cb_to_complete, which in turn rebinds C-b to menu-complete. It also rebinds C-m, carriage return, or Enter, because it now needs to do two things: Accept the line, and reset C-b to insert ls, by calling the set_Cb_to_insert_text function, which also resets Enter to it's normal use.

The reason I said that C-t had to be "sacrificed", is that if you press C-t, readline will wait to see if you are going to press 1, or 2, or any of the bound key sequences, before it takes any action. But when you first have put C-t to this use, you can use it as an initial key for a huge amount of keystrokes to cover all your readline trickery.

Piece of advice: While you are writing and testing these, bind an alternate key to accept-line, because suddenly something breaks the chain at the wrong place, and you are stuck in a terminal without a way to execute commands :)

like image 89
aktivb Avatar answered Oct 10 '22 14:10

aktivb


This might work for you:

"\ex": menu-complete
"\ez": "ls \ex"

Include these lines in your ~/.inputrc file.

These lines set Alt-x to menu-complete and Alt-z to ls space menu-complete. This will give you the first file in the directory and use Alt-x to cycle through the remainder one at a time.

See here for more examples of macros.

Checkout the readline commands by invoking bind -p or bind -P and bind -s will show the macros you already have. See here for the bind command also you can make one off macros too, see here. Lastly check that the .inputrc file is being read, I had trouble because the environmental variable was set to /etc/Inputrc and my personal version was never being invoked.

BTW steer clear of Control-x as it is already in use for many readline commands.

like image 32
potong Avatar answered Oct 10 '22 16:10

potong