diff --git a/lib/functions.zsh b/lib/functions.zsh index f9d4a971..08d1514f 100644 --- a/lib/functions.zsh +++ b/lib/functions.zsh @@ -229,3 +229,13 @@ function omz_urldecode { echo -E "$decoded" } + +# Print input iff $_OMZ_DEBUG is enabled (>0) +# All arguments are passed verbatim to print() +function _omz_dbg_print() { + if ! (( $_OMZ_DEBUG )); then + return + fi + print "$@" +} + diff --git a/lib/key-binding-support.zsh b/lib/key-binding-support.zsh new file mode 100644 index 00000000..e477c2ea --- /dev/null +++ b/lib/key-binding-support.zsh @@ -0,0 +1,200 @@ +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Builtins +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Standard-Widgets + +zmodload -i zsh/zutil + +# OMZ takes over zle-line-init/finish, to: +# a) provide hooks for multiple init/finish functions, like $precmd_functions does +# b) disable [sr]mkx on the few systems whose system zsh configuration enables it +typeset -g -a zle_line_init_functions zle_line_finish_functions +function zle-line-init() { + local fcn + for fcn in "${zle_line_init_functions[@]}"; do + $fcn + done +} +function zle-line-finish() { + local fcn + for fcn in "${zle_line_finish_functions[@]}"; do + $fcn + done +} +zle -N zle-line-init +zle -N zle-line-finish + +# DEBUG: This is the old stuff we used to do to put the keypads in application mode +# during command line editing. It's included here to make it easy to test the +# new key binding logic under both normal and application modes. This should +# go away once the modeless key binding stuff is tested. +# Note: This breaks the zle-line-init/finish hooks from above. +if (( $_OMZ_DEBUG_SMKX )); then + if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then + function zle-line-init() { + echoti smkx + } + function zle-line-finish() { + echoti rmkx + } + zle -N zle-line-init + zle -N zle-line-finish + fi +fi +# end old keypad-mode stuff + + +# Extra portability cursor key mappings for local-mode keypad and terminals +# which deviate from typical terminfo entries. Supplements $terminfo. +# Maps capability names to |-delimited lists of character sequences. +# Reflects the capabilities of the $TERM defined at startup. +typeset -AHg _omz_terminfo_extra +_omz_terminfo_extra=( + # Start with the basic Xterm local-mode sequences. (Some people call + # these "ANSI" key sequences.) + kcuu1 "\e[A" # up + kcud1 "\e[B" # down + kcuf1 "\e[C" # right ("forward") + kcub1 "\e[D" # left ("backward") + khome "\e[H" # home + kend "\e[F" # end + ) +# Portability entries for terminals we can't recognize from $TERM +# Putty and others may send the VT220 editing keypad sequences for +# Home/End. Bind those too, since there's no collision risk AFAIK. +# TODO: Should we alias the whole 6-key VT220 editing keypad? +_omz_terminfo_extra[khome]="$_omz_terminfo_extra[khome]|\e[1~" +_omz_terminfo_extra[kend]="$_omz_terminfo_extra[kend]|\e[4~" +# Do *not* bind '\e[Ow' here, which Putty may also send for End, because +# it collides with application-mode numeric keypad sequences. + +# Terminal-specific portability entries for the current terminal, for +# terminals whose local-mode sequences are known to differ from Xterm's +case "$TERM" in + rxvt-unicode*) + # rxvt-unicde local-cursor sequences that differ from xterm + _omz_terminfo_extra[khome]="$_omz_terminfo_extra[khome]|\e[7~" + _omz_terminfo_extra[kend]="$_omz_terminfo_extra[kend]|\e[8~" +esac + +# omz_bindkey - an extension to bindkey +# +# omz_bindkey [options] -t +# omz_bindkey -c +# omz_bindkey [...normal bindkey arguments...] +# +# Adds the following features on top of bindkey: +# 1) Logging +# 2) -t: binds a key, using terminfo and OMZ portability mappings +# 3) -c: binds an Xterm-style modified cursor key sequence +# +# Returns: +# 0 if a binding was made +# 1 if no binding was made +# 2 if an error occurred +function omz_bindkey() { + emulate -L zsh + # Note: "seq" is short for "sequence" in these functions + # We must capture all of bindkey's options to get ordering right when calling it + local -a omz_opts bindkey_opts + omz_opts=() + bindkey_opts=() + local -A optvals + zparseopts -a bindkey_opts -A optvals -D -E t:=omz_opts c=omz_opts M: + if [[ -n ${omz_opts[(r)-t]} ]]; then + # OMZ terminfo-based binding + local cap=${optvals[-t]} + local retval=1 seq seqs + seqs=() + if [[ -n "${terminfo[$cap]}" ]]; then + seqs=("$seqs[@]" "${terminfo[$cap]}") + fi + if [[ -n "${_omz_terminfo_extra[$cap]}" ]]; then + seqs=("$seqs[@]" "${(s:|:)_omz_terminfo_extra[$cap]}") + fi + # Done with a while loop because for seems to break here + local ix=0 + while (( ++ix <= $#seqs )); do + seq="${seqs[$ix]}" + _omz_dbg_print -r "omz_bindkey: terminfo: $cap '${(q)seq}' $@" + bindkey "${bindkey_opts[@]}" $seq "$@" + retval=0 + done + return $retval + elif [[ -n ${omz_opts[(r)-c]} ]]; then + # "-c" for "composed modified-cursor" keys + _omz_dbg_print -r "omz_bindkey: modified-cursor: '$1' '$2' '$3'" + _omz_bindkey_modified_cursor_key_xterm "$1" "$2" "$3" + else + _omz_dbg_print -r "omz_bindkey: passthrough: ${(q)@}" + bindkey "${bindkey_opts[@]}" "$@" + fi +} + +# Xterm style function key modifier codes +typeset -Ag _omz_ti_modifiers +_omz_ti_modifiers=( + 'shift' 2 + 'alt' 3 + 'alt-shift' 4 + 'ctrl' 5 + 'ctrl-shift' 6 + 'ctrl-alt' 7 + 'ctrl-alt-shift' 8 + 'meta' 9 + 'meta-shift' 10 + 'meta-alt' 11 + 'meta-alt-shift' 12 + 'meta-ctrl' 13 + 'meta-ctrl-shift' 14 + 'meta-ctrl-alt' 15 + 'meta-ctrl-alt-shift' 16 + ) + +# Binds a modified cursor key sequence +# Uses xterm modifier mask codes, +# per http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys +# When used in combination, modifier keys must be supplied in the following canonical order: +# meta ctrl alt shift +function _omz_bindkey_modified_cursor_key_xterm() { + emulate -L zsh + local opts + opts=() + zparseopts -a opts -D -E M: + local modifier=$1 key=$2 widget=$3 + local CSI='\e[' # Control Sequence Introducer + local seq modified_seq + local -A modifiers keys + set -A modifiers ${(kv)_omz_ti_modifiers} + keys=( + up A + down B + right C + left D + home H + end F + ) + if [[ -z "$modifiers[$modifier]" ]] || [[ -z "$keys[$key]" ]]; then + _omz_dbg_print -r "Invalid modified-cursor-key: modifier=$modifier key=$key" + return 1 + fi + seq="${CSI}${keys[$key]}" + _omz_bindkey_modified_cursor_seq_xterm_step "$seq" "$modifier" + # Wokaround for iTerm2: it reports alt as meta, so bind alt-modified sequences + # to the meta-modified variant, too. + # See https://gitlab.com/gnachman/iterm2/issues/3753 + if [[ "$TERM_PROGRAM" == "iTerm.app" ]]; then + if [[ "$modifier" == "alt" ]]; then + _omz_bindkey_modified_cursor_seq_xterm_step "$seq" "meta" + fi + fi +} + +function _omz_bindkey_modified_cursor_seq_xterm_step() { + local seq="$1" modifier="$2" + local modified_seq + local -A modifiers keys + set -A modifiers ${(kv)_omz_ti_modifiers} + modified_seq="${seq[1,-2]}1;$modifiers[$modifier]${seq[-1,-1]}" + omz_bindkey "${opts[@]}" "$modified_seq" "$widget" +} + diff --git a/lib/key-bindings.zsh b/lib/key-bindings.zsh index 0e056dc7..44ef1f2c 100644 --- a/lib/key-bindings.zsh +++ b/lib/key-bindings.zsh @@ -1,93 +1,39 @@ -# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html -# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Builtins -# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Standard-Widgets - -# Make sure that the terminal is in application mode when zle is active, since -# only then values from $terminfo are valid -if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then - function zle-line-init() { - echoti smkx - } - function zle-line-finish() { - echoti rmkx - } - zle -N zle-line-init - zle -N zle-line-finish -fi +# Specific key bindings +# See also key-binding-support.zsh for helper functions used here bindkey -e # Use emacs key bindings -bindkey '\ew' kill-region # [Esc-w] - Kill from the cursor to the mark -bindkey -s '\el' 'ls\n' # [Esc-l] - run command: ls -bindkey '^r' history-incremental-search-backward # [Ctrl-r] - Search backward incrementally for a specified string. The string may begin with ^ to anchor the search to the beginning of the line. -if [[ "${terminfo[kpp]}" != "" ]]; then - bindkey "${terminfo[kpp]}" up-line-or-history # [PageUp] - Up a line of history -fi -if [[ "${terminfo[knp]}" != "" ]]; then - bindkey "${terminfo[knp]}" down-line-or-history # [PageDown] - Down a line of history -fi +omz_bindkey '\ew' kill-region # [Esc-w] - Kill from the cursor to the mark +omz_bindkey -s '\el' 'ls\n' # [Esc-l] - run command: ls +omz_bindkey '^r' history-incremental-search-backward # [Ctrl-r] - Search backward incrementally for a specified string. + # The string may begin with ^ to anchor the search to the beginning of the line. -# start typing + [Up-Arrow] - fuzzy find history forward -if [[ "${terminfo[kcuu1]}" != "" ]]; then - autoload -U up-line-or-beginning-search - zle -N up-line-or-beginning-search - bindkey "${terminfo[kcuu1]}" up-line-or-beginning-search -fi -# start typing + [Down-Arrow] - fuzzy find history backward -if [[ "${terminfo[kcud1]}" != "" ]]; then - autoload -U down-line-or-beginning-search - zle -N down-line-or-beginning-search - bindkey "${terminfo[kcud1]}" down-line-or-beginning-search -fi +omz_bindkey -t kpp up-line-or-history # [PageUp] - Up a line of history +omz_bindkey -t knp down-line-or-history # [PageDown] - Down a line of history +omz_bindkey -t kcuu1 up-line-or-search # start typing + [Up-Arrow] - fuzzy find history forward +omz_bindkey -t kcud1 down-line-or-search # start typing + [Down-Arrow] - fuzzy find history backward +omz_bindkey -t khome beginning-of-line # [Home] - Go to beginning of line +omz_bindkey -t kend end-of-line # [End] - Go to end of line +omz_bindkey -c ctrl right forward-word # [Ctrl-Right] - move forward one word +omz_bindkey -c ctrl left backward-word # [Ctrl-Left] - move backward one word + +omz_bindkey ' ' magic-space # [Space] - do history expansion -if [[ "${terminfo[khome]}" != "" ]]; then - bindkey "${terminfo[khome]}" beginning-of-line # [Home] - Go to beginning of line -fi -if [[ "${terminfo[kend]}" != "" ]]; then - bindkey "${terminfo[kend]}" end-of-line # [End] - Go to end of line -fi +omz_bindkey -t kcbt reverse-menu-complete # [Shift-Tab] - move through the completion menu backwards -bindkey ' ' magic-space # [Space] - do history expansion - -bindkey '^[[1;5C' forward-word # [Ctrl-RightArrow] - move forward one word -bindkey '^[[1;5D' backward-word # [Ctrl-LeftArrow] - move backward one word - -if [[ "${terminfo[kcbt]}" != "" ]]; then - bindkey "${terminfo[kcbt]}" reverse-menu-complete # [Shift-Tab] - move through the completion menu backwards -fi - -bindkey '^?' backward-delete-char # [Backspace] - delete backward -if [[ "${terminfo[kdch1]}" != "" ]]; then - bindkey "${terminfo[kdch1]}" delete-char # [Delete] - delete forward -else - bindkey "^[[3~" delete-char - bindkey "^[3;5~" delete-char - bindkey "\e[3~" delete-char +omz_bindkey '^?' backward-delete-char # [Backspace] - delete backward +omz_bindkey -t kdch1 delete-char # [Delete] - delete forward +if [[ $? != 0 ]]; then + # Alternate forward-delete sequences, if not in terminfo + omz_bindkey '\e[3~' delete-char # VT220 editing keypad Del + omz_bindkey '\e3;5~' delete-char # I don't know what this is, but it was here before -apjanke fi # Edit the current command line in $EDITOR autoload -U edit-command-line zle -N edit-command-line -bindkey '\C-x\C-e' edit-command-line +omz_bindkey '\C-x\C-e' edit-command-line -# file rename magick -bindkey "^[m" copy-prev-shell-word +# File rename magick +omz_bindkey '\em' copy-prev-shell-word -# consider emacs keybindings: - -#bindkey -e ## emacs key bindings -# -#bindkey '^[[A' up-line-or-search -#bindkey '^[[B' down-line-or-search -#bindkey '^[^[[C' emacs-forward-word -#bindkey '^[^[[D' emacs-backward-word -# -#bindkey -s '^X^Z' '%-^M' -#bindkey '^[e' expand-cmd-path -#bindkey '^[^I' reverse-menu-complete -#bindkey '^X^N' accept-and-infer-next-history -#bindkey '^W' kill-region -#bindkey '^I' complete-word -## Fix weird sequence that rxvt produces -#bindkey -s '^[[Z' '\t' -# diff --git a/plugins/dircycle/dircycle.plugin.zsh b/plugins/dircycle/dircycle.plugin.zsh index 8a406b54..96571108 100644 --- a/plugins/dircycle/dircycle.plugin.zsh +++ b/plugins/dircycle/dircycle.plugin.zsh @@ -27,11 +27,5 @@ insert-cycledright () { zle -N insert-cycledright -# These sequences work for xterm, Apple Terminal.app, and probably others. -# Not for rxvt-unicode, but it doesn't seem differentiate Ctrl-Shift-Arrow -# from plain Shift-Arrow, at least by default. -# iTerm2 does not have these key combinations defined by default; you will need -# to add them under "Keys" in your profile if you want to use this. You can do -# this conveniently by loading the "xterm with Numeric Keypad" preset. -bindkey "\e[1;6D" insert-cycledleft -bindkey "\e[1;6C" insert-cycledright +omz_bindkey -c ctrl-shift left insert-cycledleft +omz_bindkey -c ctrl-shift right insert-cycledright diff --git a/plugins/history-substring-search/history-substring-search.plugin.zsh b/plugins/history-substring-search/history-substring-search.plugin.zsh index 7883a65f..1398b5e6 100644 --- a/plugins/history-substring-search/history-substring-search.plugin.zsh +++ b/plugins/history-substring-search/history-substring-search.plugin.zsh @@ -11,16 +11,11 @@ if test "$DISABLE_COLOR" = true; then unset HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND fi - # Bind terminal-specific up and down keys # Bind in both emacs and vi modes so it works in both, and is not # sensitive to whether this is loaded before or after the vi-mode plugin -if [[ -n "$terminfo[kcuu1]" ]]; then - bindkey -M emacs "$terminfo[kcuu1]" history-substring-search-up - bindkey -M viins "$terminfo[kcuu1]" history-substring-search-up -fi -if [[ -n "$terminfo[kcud1]" ]]; then - bindkey -M emacs "$terminfo[kcud1]" history-substring-search-down - bindkey -M viins "$terminfo[kcud1]" history-substring-search-down -fi +omz_bindkey -M emacs -t kcuu1 history-substring-search-up +omz_bindkey -M viins -t kcuu1 history-substring-search-up +omz_bindkey -M emacs -t kcud1 history-substring-search-down +omz_bindkey -M viins -t kcud1 history-substring-search-down diff --git a/plugins/history-substring-search/update-from-upstream.zsh b/plugins/history-substring-search/update-from-upstream.zsh index 81e1942a..71141dbe 100755 --- a/plugins/history-substring-search/update-from-upstream.zsh +++ b/plugins/history-substring-search/update-from-upstream.zsh @@ -75,14 +75,10 @@ cat >> $plugin_basename.plugin.zsh <