mirror of
https://github.com/zdharma-continuum/history-search-multi-word
synced 2024-11-25 04:40:21 +00:00
292 lines
11 KiB
Bash
292 lines
11 KiB
Bash
# 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
|