history-search-multi-word/history-search-multi-word
2016-10-22 11:55:05 +02:00

292 lines
11 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.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# To use (when not using as plugin) copy to site-functions and issue
# (add to ~/.zshrc):
#
# autoload history-search-multi-word
# zle -N history-search-multi-word
# zle -N history-search-multi-word-backwards history-search-multi-word
# zle -N history-search-multi-word-pbackwards history-search-multi-word
# zle -N history-search-multi-word-pforwards history-search-multi-word
# bindkey "^R" history-search-multi-word
#
# This will bind to Ctrl-R
#
# Zstyles:
# zstyle ":history-search-multi-word" page-size "8"
# zstyle ":history-search-multi-word" highlight-color "fg=yellow,bold"
# zstyle ":plugin:history-search-multi-word" synhl "yes"
emulate -LR zsh
setopt typesetsilent extendedglob noshortloops localtraps
# When an error, then no cursor keys bindings
zmodload zsh/terminfo 2>/dev/null
zmodload zsh/termcap 2>/dev/null
autoload is-at-least
typeset -g __hsmw_hcw_index
typeset -g __hsmw_hcw_widget_name __hsmw_hcw_restart __hsmw_hcw_call_count
typeset -g __hsmw_page_size __hsmw_hl_color __hsmw_synhl __hsmw_active
typeset -gaU __hsmw_hcw_found
(( __hsmw_hcw_call_count ++ ))
trap '(( __hsmw_hcw_call_count -- )); return 0;' INT
_hsmw_main() {
# First call or restart?
if [[ "$__hsmw_hcw_call_count" -le 1 || "$__hsmw_hcw_restart" = "1" ]]; then
if [[ "$__hsmw_hcw_call_count" -le 1 ]]; then
# Read configuration data
zstyle -s ':history-search-multi-word' page-size __hsmw_page_size || __hsmw_page_size=$(( LINES / 3 ))
zstyle -s ':history-search-multi-word' highlight-color __hsmw_hl_color || __hsmw_hl_color="fg=yellow,bold"
zstyle -s ":plugin:history-search-multi-word" synhl __hsmw_synhl || __hsmw_synhl="yes"
zstyle -s ":plugin:history-search-multi-word" active __hsmw_active || __hsmw_active="underline"
[[ "$__hsmw_synhl" != "1" && "$__hsmw_synhl" != "yes" && "$__hsmw_synhl" != "true" ]] && __hsmw_synhl="no" || __hsmw_synhl="yes"
fi
# '0' will get changed into $to_display limit
[[ "$WIDGET" = *-word || "$WIDGET" = *-pforwards ]] && __hsmw_hcw_index="1"
[[ "$WIDGET" = *-backwards || "$WIDGET" = *-pbackwards ]] && __hsmw_hcw_index="0"
__hsmw_hcw_widget_name="${${${WIDGET%-backwards}-pbackwards}-pforwards}"
__hsmw_hcw_found=( )
__hsmw_hcw_finished="0"
__hsmw_hcw_restart="0"
else
# Consecutive call
[[ "$WIDGET" = *-word ]] && (( __hsmw_hcw_index ++ ))
[[ "$WIDGET" = *-backwards ]] && (( __hsmw_hcw_index -- ))
[[ "$WIDGET" = *-pforwards ]] && (( __hsmw_hcw_index = __hsmw_hcw_index + __hsmw_page_size ))
[[ "$WIDGET" = *-pbackwards ]] && (( __hsmw_hcw_index = __hsmw_hcw_index - __hsmw_page_size ))
fi
# Find history entries matching pattern *word1*~^*word2*~^*word3* etc.
local search_buffer="${BUFFER%% ##}" search_pattern="" colsearch_pattern=""
search_buffer="${search_buffer## ##}"
search_buffer="${search_buffer//(#m)[][*?|#~^()><\\]/\\$MATCH}"
# Pattern will be *foo*~^*bar* (inventor: Mikael Magnusson)
# It's: foo and not what doesn't contain bar, etc.
search_pattern="${search_buffer// ##/*~^*}"
colsearch_pattern="${search_buffer// ##/|}"
if [ "$#__hsmw_hcw_found" -eq "0" ]; then
# The repeat will make the matching work on a fresh heap arena
repeat 1; do
# Tip: these are equal:
#__hsmw_hcw_found=( "${(@M)history:#(#i)*$~search_pattern*}" )
__hsmw_hcw_found=( "${(@)history[(R)(#i)*$~search_pattern*]}" )
done
fi
if [ "$#__hsmw_hcw_found" -le "0" ]; then
POSTDISPLAY=$'\n'"No matches found"
return 0
fi
#
# Pagination, index value guards
#
integer page_size="$__hsmw_page_size"
integer max_index="$#__hsmw_hcw_found"
[ "$page_size" -gt "$max_index" ] && page_size="$max_index"
[ "$__hsmw_hcw_index" -le 0 ] && __hsmw_hcw_index="$max_index"
[ "$__hsmw_hcw_index" -gt "$max_index" ] && __hsmw_hcw_index=1
integer page_start_idx=$(( ((__hsmw_hcw_index-1)/page_size)*page_size+1 ))
integer on_page_idx=$(( (__hsmw_hcw_index-1) % page_size + 1 ))
#
# Prepare display
#
typeset -a disp_list
disp_list=( "${(@)__hsmw_hcw_found[page_start_idx,page_start_idx+page_size-1]}" )
# All entries should have multilines replaced
disp_list=( "${(@)disp_list//$'\n'/\\n}" )
# ... and truncated to display width, and
# also preceeded by two spaces
disp_list=( "${(@)disp_list/(#m)*/ ${MATCH[1,COLUMNS-8]}}" )
#
# Detect where "> .." entry starts, add the ">" mark
#
local txt_before="${(F)${(@)disp_list[1,on_page_idx-1]}}"
local entry="${disp_list[on_page_idx]}"
local text="${(F)disp_list}"
integer replace_idx=${#txt_before}+2
(( replace_idx == 2 )) && replace_idx=1
text[replace_idx]=">"
#
# Colorify
#
local preamble=$'\n'"Searching for: $BUFFER"$'\n'"Element #$__hsmw_hcw_index of $max_index"$'\n'
integer offset=${#preamble}+${#BUFFER}
POSTDISPLAY="$preamble$text"
region_highlight=( )
if [[ "$__hsmw_synhl" = "yes" ]]; then
-hsmw-highlight-init
integer pre_index=0
local line
for line in "${disp_list[@]}"; do
reply=( )
-hsmw-highlight-process "$line"
region_highlight+=( "${reply[@]//(#b)([[:digit:]]##)/$(( ${match[1]} + pre_index + offset ))}" )
pre_index+=${#line}+1
done
fi
if [ -n "$colsearch_pattern" ]; then
local nl=$'\n'
# Following line is taken from Zaw (GH zsh-users/zaw)  license is named "Zaw", it is in LICENSE file
region_highlight+=( "${(f)${(S)text//*(#bi)(${~colsearch_pattern})/$(( offset + mbegin[1] - 1 )) $(( offset + mend[1] )) ${__hsmw_hl_color}${nl}}%$nl*}" )
fi
region_highlight+=( "$(( offset + ${#txt_before} )) $(( offset + ${#txt_before} + ${#entry} + 1 )) $__hsmw_active" )
}
[[ "$__HSMW_MH_SOURCED" != "1" ]] && source "$HSMW_REPO_DIR/hsmw-highlight"
_hsmw_main
_hsmw_simulate_widget() {
(( __hsmw_hcw_call_count ++ ))
_hsmw_main
}
_hsmw_self_insert() {
LBUFFER+="${KEYS[-1]}"
__hsmw_hcw_restart="1"
_hsmw_simulate_widget
}
_hsmw_backward_delete_char() {
LBUFFER="${LBUFFER%?}"
__hsmw_hcw_restart="1"
_hsmw_simulate_widget
}
_hsmw_delete_char() {
RBUFFER="${RBUFFER#?}"
__hsmw_hcw_restart="1"
_hsmw_simulate_widget
}
if [ "$__hsmw_hcw_call_count" -eq "1" ]; then
# Make the hsmw keymap a copy of the current main
bindkey -N hsmw emacs
local down_widget="${${${WIDGET%-backwards}%-pbackwards}-pforwards}"
local up_widget="${down_widget}-backwards"
local pdown_widget="${down_widget}-pforwards"
local pup_widget="${down_widget}-pbackwards"
# Manual, termcap, terminfo
bindkey -M hsmw '^[OA' "$up_widget"
bindkey -M hsmw '^[OB' "$down_widget"
bindkey -M hsmw '^[[A' "$up_widget"
bindkey -M hsmw '^[[B' "$down_widget"
[ -n "$termcap[ku]" ] && bindkey -M hsmw "$termcap[ku]" "$up_widget"
[ -n "$termcap[kd]" ] && bindkey -M hsmw "$termcap[kd]" "$down_widget"
[ -n "$termcap[kD]" ] && bindkey -M hsmw "$termcap[kD]" .delete-char
[ -n "$terminfo[kcuu1]" ] && bindkey -M hsmw "$terminfo[kcuu1]" "$up_widget"
[ -n "$terminfo[kcud1]" ] && bindkey -M hsmw "$terminfo[kcud1]" "$down_widget"
[ -n "$terminfo[kdch1]" ] && bindkey -M hsmw "$terminfo[kdch1]" .delete-char
# More bindkeys, to remove influence of plugins that overload things (z-sy-h, z-au-s)
bindkey -M hsmw '^[[D' .backward-char
bindkey -M hsmw '^[[C' .forward-char
[ -n "$termcap[kl]" ] && bindkey -M hsmw "$termcap[kl]" .backward-char
[ -n "$termcap[kr]" ] && bindkey -M hsmw "$termcap[kr]" .forward-char
[ -n "$terminfo[kcub1]" ] && bindkey -M hsmw "$terminfo[kcub1]" .backward-char
[ -n "$terminfo[kcuf1]" ] && bindkey -M hsmw "$terminfo[kcuf1]" .forward-char
# Now Home/End keys, first few recorded in my .zshrc during the years sequences
bindkey -M hsmw "\e[1~" .beginning-of-line
bindkey -M hsmw "\e[7~" .beginning-of-line
bindkey -M hsmw "\e[H" .beginning-of-line
bindkey -M hsmw "\e[4~" .end-of-line
bindkey -M hsmw "\e[F" .end-of-line
bindkey -M hsmw "\e[8~" .end-of-line
[ -n "$termcap[kh]" ] && bindkey -M hsmw "$termcap[kh]" .beginning-of-line
[ -n "$termcap[@7]" ] && bindkey -M hsmw "$termcap[@7]" .end-of-line
[ -n "$terminfo[khome]" ] && bindkey -M hsmw "$terminfo[khome]" .beginning-of-line
[ -n "$terminfo[kend]" ] && bindkey -M hsmw "$terminfo[kend]" .end-of-line
# The same for Ctrl-E, Ctrl-F
bindkey -M hsmw '^A' .beginning-of-line
bindkey -M hsmw '^E' .end-of-line
# Additional keys
bindkey -M hsmw '^P' "$up_widget"
bindkey -M hsmw '^N' "$down_widget"
# Page Up, Page Down keys
[ -n "$termcap[kP]" ] && bindkey -M hsmw "$termcap[kP]" "$pup_widget"
[ -n "$termcap[kN]" ] && bindkey -M hsmw "$termcap[kN]" "$pdown_widget"
[ -n "$terminfo[kpp]" ] && bindkey -M hsmw "$terminfo[kpp]" "$pup_widget"
[ -n "$terminfo[knp]" ] && bindkey -M hsmw "$terminfo[knp]" "$pdown_widget"
# Needed for Fedora 23, zsh-5.1.1
bindkey -M hsmw ' ' self-insert
# Removal of default Ctrl-R binding
bindkey -M hsmw '^R' "$down_widget"
# Substitute self-insert, backward-delete-char, delete-char
zle -A self-insert saved-self-insert
zle -A backward-delete-char saved-backward-delete-char
zle -A delete-char saved-delete-char
zle -N self-insert _hsmw_self_insert
zle -N backward-delete-char _hsmw_backward_delete_char
zle -N delete-char _hsmw_delete_char
# Override ourselves with what we actually are
# because zsh-autosuggestions change us
zle -A "$down_widget" saved-"$down_widget"
zle -A "$up_widget" saved-"$up_widget"
zle -N "$down_widget" _hsmw_simulate_widget
zle -N "$up_widget" _hsmw_simulate_widget
zle -A "$pdown_widget" saved-"$pdown_widget"
zle -A "$pup_widget" saved-"$pup_widget"
zle -N "$pdown_widget" _hsmw_simulate_widget
zle -N "$pup_widget" _hsmw_simulate_widget
# Trap INT to manually interrupt Zle to work around a bug
trap 'zle && zle .send-break' INT
if zle .recursive-edit -K hsmw; then
BUFFER="${__hsmw_hcw_found[__hsmw_hcw_index]}"
CURSOR="${#BUFFER}"
else
BUFFER=""
fi
POSTDISPLAY=""
# Restore self-insert, backward-delete-char, delete-char
zle -A saved-self-insert self-insert
zle -A saved-backward-delete-char backward-delete-char
zle -A saved-delete-char delete-char
zle -D saved-self-insert saved-backward-delete-char saved-delete-char
# Restore ourselves
zle -A saved-"$down_widget" "$down_widget"
zle -A saved-"$up_widget" "$up_widget"
zle -D saved-"$down_widget" saved-"$up_widget"
zle -A saved-"$pdown_widget" "$pdown_widget"
zle -A saved-"$pup_widget" "$pup_widget"
zle -D saved-"$pdown_widget" saved-"$pup_widget"
# Full reinitialisation at next call
__hsmw_hcw_call_count="0"
# Free memory
#__hsmw_hcw_found=( )
elif (( __hsmw_hcw_call_count > 0 )); then
(( __hsmw_hcw_call_count -- ))
fi
# vim:ft=zsh