Widgets for bindkey

Just to hint you at a few widgets, that are already there…

Tetris

Someone once accused zsh of not being as complete as Emacs, because it lacks Tetris and an adventure game.
I like to use ^T (imagine that!) so in my .zshrc file I have:

autoload -U tetris
zle -N tetris
bindkey ^T tetris

if you are entering the above into your zshrc with vim, you might have to hit ^V before the ^T (because ^T is already mapped to tab in vim) . In Emacs it's C-q C-t

vi keys - show mode

If you are using vi keys and want to know in what mode you currently are, you can use the following:

function zle-line-init zle-keymap-select {
    RPS1="${${KEYMAP/vicmd/-- NORMAL --}/(main|viins)/-- INSERT --}"
    RPS2=$RPS1
    zle reset-prompt
}
zle -N zle-line-init
zle -N zle-keymap-select

That adds the desired information to your right side prompt.

If you want colors, or you want the information to appear somewhere else, I'm sure you'll figure that one out on your own. :-)

Using your $EDITOR

…to modify your commandline:

autoload -U   edit-command-line
zle -N        edit-command-line
bindkey '\ee' edit-command-line

Multiselecting from menus

### menu selection: pick item but stay in the menu
### bind this to 'ESC RETURN'
    bindkey -M menuselect '\e^M' accept-and-menu-complete 

How to make a widget changing argument under cursor

This is an example of how to make a simple widget that modifies the argument the cursor is currently on (or the previous argument if the cursor is between arguments). To simplify, we'll say "current argument" to refer this.

This example will substitute the current argument (which should be a path) by its dirname and bind the function to Alt-q (which by default does the same thing as Ctrl-q, and so is a free shortcut).

Make the widget

dirname-previous-word () {
	autoload -U modify-current-argument
	modify-current-argument '${ARG:h}'
}

zle -N dirname-previous-word
bindkey '^[q' dirname-previous-word
  • modify-current-argument is a ZSH function that does a common task for ZLE widgets that is hard to do by hand.
  • modify-current-argument takes an expression, which will be eval 'ed (so you will likely be escaping it), and then substitutes the "current argument" by the result of the eval'ed expression.
  • The expression can use the variable $ARG, which contains the value of the so-called "current argument".

Put it in a separate file

Warning /!\ This part is not certain, and should be tested before relying on it.

This will use $fpath.

To put the function :

  1. Create an empty directory for putting useful functions (e.g. mkdir -p ~/.zsh/functions)
  2. Create a file named dirname-previous-word containing the body of the function previously created (lines "autoload …" and "modify-current-argument …") in that new directory.

To use it, put this into your configuration :

  1. Include that directory into you fpath (e.g. fpath=($fpath ~/.zsh/functions)
  2. Load it with "autoload -U dirname-previous-word", and include the lines "zle -N dirname-previous-word" and "bindkey '^[q' dirname-previous-word"

Some notes on fpath :

  • A file named X in your fpath will contain the body of the function X (not the declaration, hence no "X () {"), and you will have to do autoload X
  • A file named X in your fpath can contain multiple functions definition. Write autoload X; X, and all of them are loaded and usable then.

Another method (more generic)

This method still replaces current argument, but gives more liberty on the substitution, for example to do something that just could not fit into a small expression as before.

autoload -U split-shell-arguments
autoload -U modify-current-argument

local res
split-shell-arguments

## for positionning on previous argument if cursor is between arguments
## CopyPasta from /usr/share/zsh/functions/Zle/modify-current-argument (maybe BSD license)
# Get the index of the word we want.
if (( REPLY & 1 )); then
	# Odd position; need previous word.
	(( REPLY-- ))
	# Pretend position was just after the end of it.
	(( REPLY2 = ${#reply[REPLY]} + 1 ))
fi
## EOCopyPasta

res=${reply[$REPLY]}
res=${res:h}

modify-current-argument '$res'

In the above code, ${reply[$REPLY]} is the value of the "current argument". We take its dirname of it, set it to the variable $res and we then use modify-current-argument to do the final substitution.

Note : We escaped $res when passing it to modify-current-argument which seems unnecessary, but remember it will be eval'ed, so should $res contain something ressembling an expression (e.g. with a $), you would be in trouble if you forgot escaping.

 
examples/zlewidgets.txt · Last modified: 2010/01/05 09:20 (external edit)