dotfiles/lib/widgets.zsh

80 lines
2.9 KiB
Bash
Raw Normal View History

2018-07-04 20:56:09 +00:00
#!/usr/bin/env zsh
2018-07-05 17:03:07 +00:00
# define an associative array for widgets with their corresponding keybindings
2018-07-05 15:50:12 +00:00
typeset -A widgets_list
2018-07-05 17:03:07 +00:00
# get all widgets ('widgets' -> http://zsh.sourceforge.net/Doc/Release/Zsh-Modules.html#The-zsh_002fzleparameter-Module)
2018-07-04 20:56:09 +00:00
for widget_name widget_info in ${(kv)widgets}; do
2018-07-05 17:03:07 +00:00
# ignore built-in widgets starting with a dot because ZSH defines them both
# with and without the dot
2018-07-04 20:56:09 +00:00
[[ "$widget_name" == .* ]] && continue
2018-07-05 17:03:07 +00:00
# ignore completion widgets
2018-07-04 20:56:09 +00:00
[[ "$widget_info" == completion:* ]] && continue
2018-07-05 17:03:07 +00:00
# by default, widgets don't have keybindings
2018-07-05 15:50:12 +00:00
widgets_list[$widget_name]="none"
done
2018-07-05 17:03:07 +00:00
# get keybindings for widgets (one widget can have multiple keybindings)
# iterate over existing keybindings (the 'f' flag splits output of the command
# by the '\n' char)
2018-07-05 15:50:12 +00:00
for line in "${(@f)$(bindkey)}"; do
2018-07-05 17:03:07 +00:00
# parse line a string of command-line arguments (eval is required here!)
2018-07-05 15:50:12 +00:00
eval "line_parts=($line)"
2018-07-05 17:03:07 +00:00
# array indexes in ZSH start from 1 (o_O)
2018-07-05 15:50:12 +00:00
widget_key="$line_parts[1]"
widget_name="$line_parts[2]"
2018-07-05 17:03:07 +00:00
widget_keys="$widgets_list[$widget_name]"
2018-07-05 15:50:12 +00:00
if [[ -z "$widget_keys" ]]; then
continue
else
case "$widget_keys" in
none) widget_keys="keys:" ;;
keys:*) widget_keys+=" " ;;
esac
widgets_list[$widget_name]="$widget_keys{$widget_key}"
fi
2018-07-04 20:56:09 +00:00
done
2018-07-05 17:03:07 +00:00
# convert list of widgets into a string
2018-07-05 15:50:12 +00:00
widgets_str=""
for widget_name widget_keys in ${(kv)widgets_list}; do
widgets_str+="$widget_name"
if [[ "$widget_keys" == keys:* ]]; then
2018-07-05 17:03:07 +00:00
# remove the 'keys:' prefix
2018-07-05 15:50:12 +00:00
widgets_str+=" ${widget_keys#keys:}"
fi
widgets_str+=$'\n'
done
2018-07-05 17:03:07 +00:00
# remove the trailing newline from the string
2018-07-05 15:50:12 +00:00
widgets_str="${widgets_str%$'\n'}"
unset widget_{name,info,key,keys}
2018-07-05 17:03:07 +00:00
# command palette allows you to search for widgets
2018-07-04 20:56:09 +00:00
_command-palette() {
2018-07-05 17:03:07 +00:00
# widget is selected with 'peco', a 'Simplistic interactive filtering tool'
2018-07-05 15:50:12 +00:00
local widget="$(echo "$widgets_str" | peco)"
if [[ -n "$widget" ]]; then
2018-07-05 17:03:07 +00:00
# parse widget name by cutting the selected string to the first space (which
# may contain keybindings)
2018-07-05 15:50:12 +00:00
widget="${widget%%$' '*}"
2018-07-05 17:03:07 +00:00
# HACK: This small Python script is used to send simluated keystrokes to the
# currentl TTY. It first executes the 'execute-named-cmd' widget, then
# enters the widget name and finally types the 'Enter' key. (Python
# was chosen because it supports required functionallity out of the box).
# NOTE! This script may not work on all platforms (especially, on Windows)!!!
2018-07-04 20:56:09 +00:00
python -c "
import fcntl, termios
with open('$TTY') as tty:
2018-07-05 17:03:07 +00:00
# ('\x1b' is the 'escape' char)
2018-07-05 15:50:12 +00:00
for char in '\x1bx${widget}\n':
2018-07-05 17:03:07 +00:00
# 'ioctl' is a syscall that can send special commands to file descriptors.
# 'TIOCSTI' is one of these commands and can be used to simulate keypresses.
2018-07-04 20:56:09 +00:00
fcntl.ioctl(tty, termios.TIOCSTI, char)
"
fi
}
zle -N command-palette _command-palette
2018-07-05 17:03:07 +00:00
bindkey "^[P" command-palette # Esc-Shift-P or Alt-Shift-P