Files
dotfiles/dot_zsh-vi-mode/zsh-vi-mode.zsh

717 lines
20 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# zsh-vi-mode.zsh -- A better and friendly vi(vim) mode for Zsh
# https://github.com/jeffreytse/zsh-vi-mode
#
# Copyright (c) 2020 Jeffrey Tse
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# All Settings
# Some of these variables should be set before sourcing this file.
#
# ZVM_CONFIG_FUNC
# the config function (default is `zvm_config`), if this config function
# exists, it will be called automatically, you can do some configurations
# in this aspect before you source this plugin.
#
# For example:
#
# ```zsh
# function zvm_config() {
# ZVM_LINE_INIT_MODE=$ZVM_MODE_INSERT
# ZVM_VI_INSERT_ESCAPE_BINDKEY=jk
# }
#
# source ~/zsh-vi-mode.zsh
# ```
#
# ZVM_INIT_MODE
# the plugin initial mode (default is doing the initialization when the first
# new command line is starting. For doing the initialization instantly, you
# can set it to `sourcing`.
#
# ZVM_VI_ESCAPE_BINDKEY
# the vi escape key for all modes (default is ^[ => <ESC>), you can set it
# to whatever you like, such as `jj`, `jk` and so on.
#
# ZVM_VI_INSERT_ESCAPE_BINDKEY
# the vi escape key of insert mode (default is $ZVM_VI_ESCAPE_BINDKEY), you
# can set it to whatever, such as `jj`, `jk` and so on.
#
# ZVM_VI_VISUAL_ESCAPE_BINDKEY
# the vi escape key of visual mode (default is $ZVM_VI_ESCAPE_BINDKEY), you
# can set it to whatever, such as `jj`, `jk` and so on.
#
# ZVM_VI_OPPEND_ESCAPE_BINDKEY
# the vi escape key of operator pendding mode (default is
# $ZVM_VI_ESCAPE_BINDKEY), you can set it to whatever, such as `jj`, `jk`
# and so on.
#
# ZVM_VI_INSERT_MODE_LEGACY_UNDO:
# using legacy undo behavior in vi insert mode (default is false)
#
# ZVM_VI_HIGHLIGHT_FOREGROUND:
# the behavior of highlight foreground (surrounds, visual-line, etc) in vi mode
#
# ZVM_VI_HIGHLIGHT_BACKGROUND:
# the behavior of highlight background (surrounds, visual-line, etc) in vi mode
#
# ZVM_VI_HIGHLIGHT_EXTRASTYLE:
# the behavior of highlight extra style (i.e. bold, underline) in vi mode
#
# For example:
# ZVM_VI_HIGHLIGHT_FOREGROUND=green # Color name
# ZVM_VI_HIGHLIGHT_FOREGROUND=#008800 # Hex value
# ZVM_VI_HIGHLIGHT_BACKGROUND=red # Color name
# ZVM_VI_HIGHLIGHT_BACKGROUND=#ff0000 # Hex value
# ZVM_VI_HIGHLIGHT_EXTRASTYLE=bold,underline # bold and underline
#
# ZVM_VI_SURROUND_BINDKEY
# the key binding mode for surround operating (default is 'classic')
#
# 1. 'classic' mode (verb->s->surround):
# S" Add " for visual selection
# ys" Add " for visual selection
# cs"' Change " to '
# ds" Delete "
#
# 2. 's-prefix' mode (s->verb->surround):
# sa" Add " for visual selection
# sd" Delete "
# sr"' Change " to '
#
# How to select surround text object?
# vi" Select the text object inside the quotes
# va( Select the text object including the brackets
#
# Then you can do any operation for the selection:
#
# 1. Add surrounds for text object
# vi" -> S[ or sa[ => "object" -> "[object]"
#
# 2. Delete/Yank/Change text object
# di( or vi( -> d
# ca( or va( -> c
# yi( or vi( -> y
#
# ZVM_READKEY_ENGINE
# the readkey engine for reading and processing the key events, and the
# below engines are supported:
# ZVM_READKEY_ENGINE_NEX (Default)
# ZVM_READKEY_ENGINE_ZLE
#
# the NEX is a better engine for reading and handling the key events than
# the Zsh's ZLE engine, currently the NEX engine is at beta stage, and
# you can change to Zsh's ZLE engine if you want.
#
# ZVM_KEYTIMEOUT:
# the key input timeout for waiting for next key (default is 0.4 seconds)
#
# ZVM_ESCAPE_KEYTIMEOUT:
# the key input timeout for waiting for next key if it is beginning with
# an escape character (default is 0.03 seconds), and this option is just
# available for the NEX readkey engine
#
# ZVM_LINE_INIT_MODE
# the setting for init mode of command line (default is empty), empty will
# keep the last command mode, for the first command line it will be insert
# mode, you can also set it to a specific vi mode to alway keep the mode
# for each command line
#
# For example:
# ZVM_LINE_INIT_MODE=$ZVM_MODE_INSERT
# ZVM_LINE_INIT_MODE=$ZVM_MODE_NORMAL
#
# ZVM_LAZY_KEYBINDINGS:
# the setting for lazy keybindings (default is true), and lazy keybindings
# will postpone the keybindings of vicmd and visual keymaps to the first
# time entering normal mode
#
# ZVM_NORMAL_MODE_CURSOR:
# the prompt cursor in normal mode
#
# ZVM_INSERT_MODE_CURSOR:
# the prompt cursor in insert mode
#
# ZVM_VISUAL_MODE_CURSOR:
# the prompt cursor in visual mode
#
# ZVM_VISUAL_LINE_MODE_CURSOR:
# the prompt cursor in visual line mode
#
# ZVM_OPPEND_MODE_CURSOR:
# the prompt cursor in operator pending mode
#
# You can change the cursor style by below:
# ZVM_INSERT_MODE_CURSOR=$ZVM_CURSOR_BLOCK
#
# and the below cursor style are supported:
# ZVM_CURSOR_USER_DEFAULT
# ZVM_CURSOR_BLOCK
# ZVM_CURSOR_UNDERLINE
# ZVM_CURSOR_BEAM
# ZVM_CURSOR_BLINKING_BLOCK
# ZVM_CURSOR_BLINKING_UNDERLINE
# ZVM_CURSOR_BLINKING_BEAM
#
# ZVM_VI_EDITOR
# the editor to edit your command line (default is $EDITOR)
#
# ZVM_TMPDIR
# the temporary directory (default is $TMPDIR, otherwise it's /tmp)
#
# ZVM_TERM
# the term for handling terminal sequences, it's important for some
# terminal emulators to show cursor properly (default is $TERM)
#
# ZVM_CURSOR_STYLE_ENABLED
# enable the cursor style feature (default is true)
#
# ZVM_SYSTEM_CLIPBOARD_ENABLED
# enable the system clipboard feature (default is false), if you want to enable
# it, you should also set the copy and paste commands below:
#
# ZVM_CLIPBOARD_COPY_CMD
# the command for copying text to system clipboard
#
# ZVM_CLIPBOARD_PASTE_CMD
# the command for pasting text from system clipboard
#
# For example:
# - For macOS, you can set it to `pbcopy` and `pbpaste`
# - For Linux, you can set it to `xclip -selection clipboard` and
# `xclip -selection clipboard -o`)
# - For Wayland, you can also use `wl-copy` and `wl-paste`
# - For WSL, you can also use `clip.exe`
#
# If you don't set these two commands, the plugin will try to detect them
# automatically for you.
#
# ZVM_OPEN_CMD
# the command for opening URL or file path (e.g. `xdg-open`, `open`, `start`
# and so on)
#
# ZVM_OPEN_URL_CMD
# the command for opening URL (default is $ZVM_OPEN_CMD)
#
# ZVM_OPEN_FILE_CMD
# the command for opening file path (default is $ZVM_OPEN_CMD)
#
# Avoid sourcing plugin multiple times
command -v 'zvm_version' >/dev/null && return
# Plugin information
typeset -gr ZVM_NAME='zsh-vi-mode'
typeset -gr ZVM_DESCRIPTION='💻 A better and friendly vi(vim) mode plugin for ZSH.'
typeset -gr ZVM_REPOSITORY='https://github.com/jeffreytse/zsh-vi-mode'
typeset -gr ZVM_VERSION='0.12.0'
# Plugin initial status
ZVM_INIT_DONE=false
# Postpone reset prompt (i.e. postpone the widget `reset-prompt`)
# -1 (No postponing)
# >=0 (Postponing, the decimal value stands for calling times of `reset-prompt`)
ZVM_POSTPONE_RESET_PROMPT=-1
# Disable reset prompt (i.e. postpone the widget `reset-prompt`)
ZVM_RESET_PROMPT_DISABLED=false
# Operator pending mode
ZVM_OPPEND_MODE=false
# Insert mode could be
# `i` (insert)
# `a` (append)
# `I` (insert at the non-blank beginning of current line)
# `A` (append at the end of current line)
ZVM_INSERT_MODE='i'
# The mode could be the below value:
# `n` (normal)
# `i` (insert)
# `v` (visual)
# `vl` (visual-line)
ZVM_MODE=''
# The keys typed to invoke this widget, as a literal string
ZVM_KEYS=''
# The region hilight information
ZVM_REGION_HIGHLIGHT=()
# Default zvm readkey engines
ZVM_READKEY_ENGINE_NEX='nex'
ZVM_READKEY_ENGINE_ZLE='zle'
ZVM_READKEY_ENGINE_DEFAULT=$ZVM_READKEY_ENGINE_NEX
# Default alternative character for escape characters
ZVM_ESCAPE_SPACE='\s'
ZVM_ESCAPE_NEWLINE='^J'
# Default vi modes
ZVM_MODE_LAST=''
ZVM_MODE_NORMAL='n'
ZVM_MODE_INSERT='i'
ZVM_MODE_VISUAL='v'
ZVM_MODE_VISUAL_LINE='vl'
ZVM_MODE_REPLACE='r'
# Default cursor styles
ZVM_CURSOR_USER_DEFAULT='ud'
ZVM_CURSOR_BLOCK='bl'
ZVM_CURSOR_UNDERLINE='ul'
ZVM_CURSOR_BEAM='be'
ZVM_CURSOR_BLINKING_BLOCK='bbl'
ZVM_CURSOR_BLINKING_UNDERLINE='bul'
ZVM_CURSOR_BLINKING_BEAM='bbe'
# The commands need to be repeated
ZVM_REPEAT_MODE=false
ZVM_REPEAT_RESET=false
ZVM_REPEAT_COMMANDS=($ZVM_MODE_NORMAL i)
# Range handling return values
ZVM_RANGE_HANDLER_RET_OK=0
ZVM_RANGE_HANDLER_RET_CONTINUE=1
ZVM_RANGE_HANDLER_RET_PUSHBACK=2
ZVM_RANGE_HANDLER_RET_CANCEL=3
# URL regex pattern
ZVM_URL_SCHEME='^(http(s)?:\/\/.)?(ftp(s)?:\/\/.)?(file:\/\/.)?(www\.)?'
ZVM_URL_HOST='[-a-zA-Z0-9@:%._\+~#=]{0,255}\.[a-z]{2,6}'
ZVM_URL_PATH='([-a-zA-Z0-9@:%_\+.~#?&\/=]*)$'
ZVM_URL_REGEX="${ZVM_URL_SCHEME}${ZVM_URL_HOST}${ZVM_URL_PATH}"
##########################################
# Initial all default settings
# Default config function
: ${ZVM_CONFIG_FUNC:='zvm_config'}
# Set the readkey engine (default is NEX engine)
: ${ZVM_READKEY_ENGINE:=$ZVM_READKEY_ENGINE_DEFAULT}
# Set key input timeout (default is 0.4 seconds)
: ${ZVM_KEYTIMEOUT:=0.4}
# Set the escape key timeout (default is 0.03 seconds)
: ${ZVM_ESCAPE_KEYTIMEOUT:=0.03}
# Set keybindings mode (default is true)
# The lazy keybindings will post the keybindings of vicmd and visual
# keymaps to the first time entering the normal mode
: ${ZVM_LAZY_KEYBINDINGS:=true}
# All keybindings for lazy loading
if $ZVM_LAZY_KEYBINDINGS; then
ZVM_LAZY_KEYBINDINGS_LIST=()
fi
# Set the cursor style in defferent vi modes, the value you could use
# the predefined value, such as $ZVM_CURSOR_BLOCK, $ZVM_CURSOR_BEAM,
# $ZVM_CURSOR_BLINKING_BLOCK and so on.
: ${ZVM_INSERT_MODE_CURSOR:=$ZVM_CURSOR_BEAM}
: ${ZVM_NORMAL_MODE_CURSOR:=$ZVM_CURSOR_BLOCK}
: ${ZVM_VISUAL_MODE_CURSOR:=$ZVM_CURSOR_BLOCK}
: ${ZVM_VISUAL_LINE_MODE_CURSOR:=$ZVM_CURSOR_BLOCK}
# Operator pending mode cursor style (default is underscore)
: ${ZVM_OPPEND_MODE_CURSOR:=$ZVM_CURSOR_UNDERLINE}
# Set the vi escape key (default is ^[ => <ESC>)
: ${ZVM_VI_ESCAPE_BINDKEY:=^[}
: ${ZVM_VI_INSERT_ESCAPE_BINDKEY:=$ZVM_VI_ESCAPE_BINDKEY}
: ${ZVM_VI_VISUAL_ESCAPE_BINDKEY:=$ZVM_VI_ESCAPE_BINDKEY}
: ${ZVM_VI_OPPEND_ESCAPE_BINDKEY:=$ZVM_VI_ESCAPE_BINDKEY}
# Set the line init mode (empty will keep the last mode)
# you can also set it to others, such as $ZVM_MODE_INSERT.
: ${ZVM_LINE_INIT_MODE:=$ZVM_MODE_LAST}
: ${ZVM_VI_INSERT_MODE_LEGACY_UNDO:=false}
: ${ZVM_VI_SURROUND_BINDKEY:=classic}
: ${ZVM_VI_HIGHLIGHT_BACKGROUND:=#cc0000}
: ${ZVM_VI_HIGHLIGHT_FOREGROUND:=#eeeeee}
: ${ZVM_VI_HIGHLIGHT_EXTRASTYLE:=default}
: ${ZVM_VI_EDITOR:=${EDITOR:-vim}}
: ${ZVM_TMPDIR:=${TMPDIR:-/tmp}}
# Set the term for handling terminal sequences, it's important for some
# terminal emulators to show cursor properly (default is $TERM)
: ${ZVM_TERM:=${TERM:-xterm-256color}}
# Enable the cursor style feature
: ${ZVM_CURSOR_STYLE_ENABLED:=true}
# Enable system clipboard feature
: ${ZVM_SYSTEM_CLIPBOARD_ENABLED:=false}
: ${ZVM_CLIPBOARD_COPY_CMD:=}
: ${ZVM_CLIPBOARD_PASTE_CMD:=}
# Open URL or file path feature
: ${ZVM_OPEN_CMD:=}
: ${ZVM_OPEN_URL_CMD:=${ZVM_OPEN_CMD:-}}
: ${ZVM_OPEN_FILE_CMD:=${ZVM_OPEN_CMD:-}}
# All the extra commands
commands_array_names=(
zvm_before_init_commands
zvm_after_init_commands
zvm_before_select_vi_mode_commands
zvm_after_select_vi_mode_commands
zvm_before_lazy_keybindings_commands
zvm_after_lazy_keybindings_commands
)
for commands_array_name in $commands_array_names; do
# Ensure commands set to an empty array, if not already set.
if [[ -z "${(P)commands_array_name}" ]]; then
typeset -g -a $commands_array_name
fi
done
# All the handlers for switching keyword
zvm_switch_keyword_handlers=(
zvm_switch_number
zvm_switch_boolean
zvm_switch_operator
zvm_switch_weekday
zvm_switch_month
)
# History for switching keyword
zvm_switch_keyword_history=()
# Display version information
function zvm_version() {
local git_info=$(git show -s --format="(%h, %ci)" 2>/dev/null)
echo -e "$ZVM_NAME $ZVM_VERSION $git_info"
echo -e "\e[4m$ZVM_REPOSITORY\e[0m"
echo -e "$ZVM_DESCRIPTION"
}
# The widget wrapper
function zvm_widget_wrapper() {
local rawfunc=$1;
local func=$2;
local called=$3;
local -i retval=0
if ! $called; then
$rawfunc "${@:4}"
retval=$?
fi
$func "${@:4}"
[[ $retval -eq 0 ]] && retval=$?
return $retval
}
# Define widget function
function zvm_define_widget() {
local widget=$1
local func=$2 || $1
local result=($(zle -l -L "${widget}"))
# Check if existing the same name
if [[ ${#result[@]} == 4 ]]; then
local rawfunc=${result[4]}
local wrapper="zvm_${widget}-wrapper"
# To avoid double calling, we need to check if the raw function
# has been called already in the custom widget function
local rawcode=$(declare -f $func 2>/dev/null)
local called=false
[[ "$rawcode" == *"\$rawfunc"* ]] && { called=true }
eval "$wrapper() { zvm_widget_wrapper $rawfunc $func $called \"\$@\" }"
func=$wrapper
fi
zle -N $widget $func
}
# Get the keys typed to invoke this widget, as a literal string
function zvm_keys() {
local keys=${ZVM_KEYS:-$KEYS}
# Append the prefix of keys if it is visual or visual-line mode
case "${ZVM_MODE}" in
$ZVM_MODE_VISUAL)
if [[ "$keys" != v* ]]; then
keys="v${keys}"
fi
;;
$ZVM_MODE_VISUAL_LINE)
if [[ "$keys" != V* ]]; then
keys="V${keys}"
fi
;;
esac
# Escape the newline and space characters, otherwise, we can't
# get the output from subshell correctly.
keys=${keys//$'\n'/$ZVM_ESCAPE_NEWLINE}
keys=${keys// /$ZVM_ESCAPE_SPACE}
echo $keys
}
# Find the widget on a specified bindkey
function zvm_find_bindkey_widget() {
local keymap=$1
local keys=$2
local prefix_mode=${3:-false}
retval=()
if $prefix_mode; then
local pos=0
local spos=3
local prefix_keys=$keys
# Get the prefix keys
if [[ $prefix_keys ]]; then
prefix_keys=${prefix_keys:0:-1}
# If the last key is an escape key (e.g. \", \`, \\) we still
# need to remove the escape backslash `\`
if [[ ${prefix_keys: -1} == '\' ]]; then
prefix_keys=${prefix_keys:0:-1}
fi
fi
local result=$(bindkey -M ${keymap} -p "$prefix_keys")$'\n'
# Split string to array by newline
local i=
for ((i=$spos;i<$#result;i++)); do
# Save the last whitespace character of the line
# and continue continue handling while meeting `\n`
case "${result:$i:1}" in
' ') spos=$i; i=$i+1; continue;;
[$'\n']);;
*) continue;;
esac
# Check if it has the same prefix keys and retrieve the widgets
if [[ "${result:$((pos+1)):$#keys}" == "$keys" ]]; then
# Get the binding keys
local k=${result:$((pos+1)):$((spos-pos-2))}
# Escape spaces in key bindings (space -> $ZVM_ESCAPE_SPACE)
k=${k// /$ZVM_ESCAPE_SPACE}
retval+=($k ${result:$((spos+1)):$((i-spos-1))})
fi
# Save as new position
pos=$i+1
# Skip 3 characters
# One key and quotes at least (i.e \n"_" )
i=$i+3
done
else
local result=$(bindkey -M ${keymap} "$keys")
if [[ "${result: -14}" == ' undefined-key' ]]; then
return
fi
# Escape spaces in key bindings (space -> $ZVM_ESCAPE_SPACE)
local i=
for ((i=$#result;i>=0;i--)); do
# Backward find the first whitespace character
[[ "${result:$i:1}" == ' ' ]] || continue
# Retrieve the keys and widget
local k=${result:1:$i-2}
# Escape spaces in key bindings (space -> $ZVM_ESCAPE_SPACE)
k=${k// /$ZVM_ESCAPE_SPACE}
retval+=($k ${result:$i+1})
break
done
fi
}
# Read keys for retrieving widget
function zvm_readkeys() {
local keymap=$1
local key=${2:-$(zvm_keys)}
local keys=
local widget=
local result=
local pattern=
local timeout=
while :; do
# Keep reading key for escape character
if [[ "$key" == $'\e' ]]; then
while :; do
local k=
read -t $ZVM_ESCAPE_KEYTIMEOUT -k 1 k || break
key="${key}${k}"
done
fi
keys="${keys}${key}"
# Handle the pattern
if [[ -n "$key" ]]; then
# Transform the non-printed characters
local k=$(zvm_escape_non_printed_characters "${key}")
# Escape keys
# " -> \" It's a special character in bash syntax
# ` -> \` It's a special character in bash syntax
# <space> -> ` ` It's a special character in bash syntax
k=${k//\"/\\\"}
k=${k//\`/\\\`}
k=${k//$ZVM_ESCAPE_SPACE/ }
pattern="${pattern}${k}"
fi
# Find out widgets that match this key pattern
zvm_find_bindkey_widget $keymap "$pattern" true
result=(${retval[@]})
# Exit key input if there is only one widget matched
# or no more widget matched.
case ${#result[@]} in
2) key=; widget=${result[2]}; break;;
0) break;;
esac
# Evaluate the readkey timeout
# Special timeout for the escape sequence
if [[ "${keys}" == $'\e' ]]; then
timeout=$ZVM_ESCAPE_KEYTIMEOUT
# Check if there is any one custom escape sequence
local i=
for ((i=1; i<=${#result[@]}; i=i+2)); do
if [[ "${result[$i]}" =~ '^\^\[\[?[A-Z0-9]*~?\^\[' ]]; then
timeout=$ZVM_KEYTIMEOUT
break
fi
done
else
timeout=$ZVM_KEYTIMEOUT
fi
# Wait for reading next key, and we should save the widget
# as the final widget if it is full matching
key=
if [[ "${result[1]}" == "${pattern}" ]]; then
widget=${result[2]}
# Get current widget as final widget when reading key timeout
read -t $timeout -k 1 key || break
else
zvm_enter_oppend_mode
read -k 1 key
fi
done
# Exit operator pending mode
if $ZVM_OPPEND_MODE; then
zvm_exit_oppend_mode
fi
if [[ -z "$key" ]]; then
retval=(${keys} $widget)
else
retval=(${keys:0:-$#key} $widget $key)
fi
}
# Add key bindings
function zvm_bindkey() {
local keymap=$1
local keys=$2
local widget=$3
local params=$4
local key=
# We should bind keys with an existing widget
[[ -z $widget ]] && return
# If lazy keybindings is enabled, we need to add to the lazy list
if [[ ${ZVM_LAZY_KEYBINDINGS_LIST+x} && ${keymap} != viins ]]; then
keys=${keys//\"/\\\"}
keys=${keys//\`/\\\`}
ZVM_LAZY_KEYBINDINGS_LIST+=(
"${keymap} \"${keys}\" ${widget} \"${params}\""
)
return
fi
# Hanle the keybinding of NEX readkey engine
if [[ $ZVM_READKEY_ENGINE == $ZVM_READKEY_ENGINE_NEX ]]; then
# Get the first key (especially check if ctrl characters)
if [[ $#keys -gt 1 && "${keys:0:1}" == '^' ]]; then
key=${keys:0:2}
else
key=${keys:0:1}
# As any character that is not bound to one of the history control
# related functions, or self-insert or self-insert-unmeta, will
# cause the mode to be exited To prevent history search, so that
# we need to bind keys explicitly.
if [[ "$keymap" == "viins" ]]; then
bindkey -M isearch "${key}" self-insert
fi
fi
bindkey -M $keymap "${key}" zvm_readkeys_handler
fi
# Wrap params to a new widget
if [[ -n $params ]]; then
local suffix=$(zvm_string_to_hex $params)
eval "$widget:$suffix() { $widget $params }"
widget="$widget:$suffix"
zvm_define_widget $widget
fi
# Bind keys with with a widget
bindkey -M $keymap "${keys}" $widget
}
# Convert string to hexadecimal
function zvm_string_to_hex() {
local str= i=
for ((i=1;i<=$#1;i++)); do
str+=$(printf '%x' "'${1[$i]}")
done
echo "$str"
}
# Escape non-printed characters
function zvm_escape_non_printed_characters() {
local str= i=
for ((i=0;i<$#1;i++)); do
local c=${1:$i:1}
if [[ "$c" < ' ' ]]; then
local ord=$(($(printf '%d' "'$c")+64))
c=$(printf \\$(printf '%03o' $ord))
str="${str}^${c}"
elif [[ "$c" == '' ]]; then
str="${str}^?"
elif [[ "$c" == '