You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
371 lines
12 KiB
371 lines
12 KiB
# Copy this file into /usr/share/zsh/site-functions/ |
|
# and add 'autoload n-history` to .zshrc |
|
# |
|
# This function allows to browse Z shell's history and use the |
|
# entries |
|
# |
|
# Uses n-list |
|
|
|
emulate -L zsh |
|
|
|
setopt extendedglob |
|
zmodload zsh/curses |
|
zmodload zsh/parameter |
|
|
|
local IFS=" |
|
" |
|
|
|
# Variables to save list's state when switching views |
|
# The views are: history and "most frequent history words" |
|
local one_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
local one_NLIST_CURRENT_IDX |
|
local one_NLIST_IS_SEARCH_MODE |
|
local one_NLIST_SEARCH_BUFFER |
|
local one_NLIST_TEXT_OFFSET |
|
local one_NLIST_IS_UNIQ_MODE |
|
local one_NLIST_IS_F_MODE |
|
local one_NLIST_GREP_STRING |
|
local one_NLIST_NONSELECTABLE_ELEMENTS |
|
local one_NLIST_REMEMBER_STATE |
|
local one_NLIST_ENABLED_EVENTS |
|
|
|
local two_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
local two_NLIST_CURRENT_IDX |
|
local two_NLIST_IS_SEARCH_MODE |
|
local two_NLIST_SEARCH_BUFFER |
|
local two_NLIST_TEXT_OFFSET |
|
local two_NLIST_IS_UNIQ_MODE |
|
local two_NLIST_IS_F_MODE |
|
local two_NLIST_GREP_STRING |
|
local two_NLIST_NONSELECTABLE_ELEMENTS |
|
local two_NLIST_REMEMBER_STATE |
|
local two_NLIST_ENABLED_EVENTS |
|
|
|
local three_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
local three_NLIST_CURRENT_IDX |
|
local three_NLIST_IS_SEARCH_MODE |
|
local three_NLIST_SEARCH_BUFFER |
|
local three_NLIST_TEXT_OFFSET |
|
local three_NLIST_IS_UNIQ_MODE |
|
local three_NLIST_IS_F_MODE |
|
local three_NLIST_GREP_STRING |
|
local three_NLIST_NONSELECTABLE_ELEMENTS |
|
local three_NLIST_REMEMBER_STATE |
|
local three_NLIST_ENABLED_EVENTS |
|
|
|
# history view |
|
integer active_view=0 |
|
|
|
# Lists are "0", "1", "2" - 1st, 2nd, 3rd |
|
# Switching is done in cyclic manner |
|
# i.e. 0 -> 1, 1 -> 2, 2 -> 0 |
|
_nhistory_switch_lists_states() { |
|
# First argument is current, newly selected list, i.e. $active_view |
|
# This implies that we are switching from previous view |
|
|
|
if [ "$1" = "0" ]; then |
|
# Switched to 1st list, save 3rd list's state |
|
three_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
three_NLIST_CURRENT_IDX=$NLIST_CURRENT_IDX |
|
three_NLIST_IS_SEARCH_MODE=$NLIST_IS_SEARCH_MODE |
|
three_NLIST_SEARCH_BUFFER=$NLIST_SEARCH_BUFFER |
|
three_NLIST_TEXT_OFFSET=$NLIST_TEXT_OFFSET |
|
three_NLIST_IS_UNIQ_MODE=$NLIST_IS_UNIQ_MODE |
|
three_NLIST_IS_F_MODE=$NLIST_IS_F_MODE |
|
three_NLIST_GREP_STRING=$NLIST_GREP_STRING |
|
three_NLIST_NONSELECTABLE_ELEMENTS=( ${NLIST_NONSELECTABLE_ELEMENTS[@]} ) |
|
three_NLIST_REMEMBER_STATE=$NLIST_REMEMBER_STATE |
|
three_NLIST_ENABLED_EVENTS=( ${NLIST_ENABLED_EVENTS[@]} ) |
|
|
|
# ..and restore 1st list's state |
|
NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$one_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
NLIST_CURRENT_IDX=$one_NLIST_CURRENT_IDX |
|
NLIST_IS_SEARCH_MODE=$one_NLIST_IS_SEARCH_MODE |
|
NLIST_SEARCH_BUFFER=$one_NLIST_SEARCH_BUFFER |
|
NLIST_TEXT_OFFSET=$one_NLIST_TEXT_OFFSET |
|
NLIST_IS_UNIQ_MODE=$one_NLIST_IS_UNIQ_MODE |
|
NLIST_IS_F_MODE=$one_NLIST_IS_F_MODE |
|
NLIST_GREP_STRING=$one_NLIST_GREP_STRING |
|
NLIST_NONSELECTABLE_ELEMENTS=( ${one_NLIST_NONSELECTABLE_ELEMENTS[@]} ) |
|
NLIST_REMEMBER_STATE=$one_NLIST_REMEMBER_STATE |
|
NLIST_ENABLED_EVENTS=( ${one_NLIST_ENABLED_EVENTS[@]} ) |
|
elif [ "$1" = "1" ]; then |
|
# Switched to 2nd list, save 1st list's state |
|
one_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
one_NLIST_CURRENT_IDX=$NLIST_CURRENT_IDX |
|
one_NLIST_IS_SEARCH_MODE=$NLIST_IS_SEARCH_MODE |
|
one_NLIST_SEARCH_BUFFER=$NLIST_SEARCH_BUFFER |
|
one_NLIST_TEXT_OFFSET=$NLIST_TEXT_OFFSET |
|
one_NLIST_IS_UNIQ_MODE=$NLIST_IS_UNIQ_MODE |
|
one_NLIST_IS_F_MODE=$NLIST_IS_F_MODE |
|
one_NLIST_GREP_STRING=$NLIST_GREP_STRING |
|
one_NLIST_NONSELECTABLE_ELEMENTS=( ${NLIST_NONSELECTABLE_ELEMENTS[@]} ) |
|
one_NLIST_REMEMBER_STATE=$NLIST_REMEMBER_STATE |
|
one_NLIST_ENABLED_EVENTS=( ${NLIST_ENABLED_EVENTS[@]} ) |
|
|
|
# ..and restore 2nd list's state |
|
NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$two_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
NLIST_CURRENT_IDX=$two_NLIST_CURRENT_IDX |
|
NLIST_IS_SEARCH_MODE=$two_NLIST_IS_SEARCH_MODE |
|
NLIST_SEARCH_BUFFER=$two_NLIST_SEARCH_BUFFER |
|
NLIST_TEXT_OFFSET=$two_NLIST_TEXT_OFFSET |
|
NLIST_IS_UNIQ_MODE=$two_NLIST_IS_UNIQ_MODE |
|
NLIST_IS_F_MODE=$two_NLIST_IS_F_MODE |
|
NLIST_GREP_STRING=$two_NLIST_GREP_STRING |
|
NLIST_NONSELECTABLE_ELEMENTS=( ${two_NLIST_NONSELECTABLE_ELEMENTS[@]} ) |
|
NLIST_REMEMBER_STATE=$two_NLIST_REMEMBER_STATE |
|
NLIST_ENABLED_EVENTS=( ${two_NLIST_ENABLED_EVENTS[@]} ) |
|
elif [ "$1" = "2" ]; then |
|
# Switched to 3rd list, save 2nd list's state |
|
two_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
two_NLIST_CURRENT_IDX=$NLIST_CURRENT_IDX |
|
two_NLIST_IS_SEARCH_MODE=$NLIST_IS_SEARCH_MODE |
|
two_NLIST_SEARCH_BUFFER=$NLIST_SEARCH_BUFFER |
|
two_NLIST_TEXT_OFFSET=$NLIST_TEXT_OFFSET |
|
two_NLIST_IS_UNIQ_MODE=$NLIST_IS_UNIQ_MODE |
|
two_NLIST_IS_F_MODE=$NLIST_IS_F_MODE |
|
two_NLIST_GREP_STRING=$NLIST_GREP_STRING |
|
two_NLIST_NONSELECTABLE_ELEMENTS=( ${NLIST_NONSELECTABLE_ELEMENTS[@]} ) |
|
two_NLIST_REMEMBER_STATE=$NLIST_REMEMBER_STATE |
|
two_NLIST_ENABLED_EVENTS=( ${NLIST_ENABLED_EVENTS[@]} ) |
|
|
|
# ..and restore 3rd list's state |
|
NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$three_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN |
|
NLIST_CURRENT_IDX=$three_NLIST_CURRENT_IDX |
|
NLIST_IS_SEARCH_MODE=$three_NLIST_IS_SEARCH_MODE |
|
NLIST_SEARCH_BUFFER=$three_NLIST_SEARCH_BUFFER |
|
NLIST_TEXT_OFFSET=$three_NLIST_TEXT_OFFSET |
|
NLIST_IS_UNIQ_MODE=$three_NLIST_IS_UNIQ_MODE |
|
NLIST_IS_F_MODE=$three_NLIST_IS_F_MODE |
|
NLIST_GREP_STRING=$three_NLIST_GREP_STRING |
|
NLIST_NONSELECTABLE_ELEMENTS=( ${three_NLIST_NONSELECTABLE_ELEMENTS[@]} ) |
|
NLIST_REMEMBER_STATE=$three_NLIST_REMEMBER_STATE |
|
NLIST_ENABLED_EVENTS=( ${three_NLIST_ENABLED_EVENTS[@]} ) |
|
fi |
|
} |
|
|
|
local most_frequent_db="$HOME/.config/znt/mostfrequent.db" |
|
_nhistory_generate_most_frequent() { |
|
local title=$'\x1b[00;31m'"Most frequent history words:"$'\x1b[00;00m\0' |
|
|
|
typeset -A uniq |
|
for k in "${historywords[@]}"; do |
|
uniq[$k]=$(( ${uniq[$k]:-0} + 1 )) |
|
done |
|
vk=() |
|
for k v in ${(kv)uniq}; do |
|
vk+="$v"$'\t'"$k" |
|
done |
|
|
|
print -rl -- "$title" "${(On)vk[@]}" > "$most_frequent_db" |
|
} |
|
|
|
# Load configuration |
|
unset NLIST_COLORING_PATTERN |
|
[ -f ~/.config/znt/n-list.conf ] && builtin source ~/.config/znt/n-list.conf |
|
[ -f ~/.config/znt/n-history.conf ] && builtin source ~/.config/znt/n-history.conf |
|
|
|
local list |
|
local selected |
|
local private_history_db="$HOME/.config/znt/privhist.db" |
|
|
|
local NLIST_GREP_STRING="$1" |
|
# 2 is: init once, then remember |
|
local NLIST_REMEMBER_STATE=2 |
|
two_NLIST_REMEMBER_STATE=2 |
|
three_NLIST_REMEMBER_STATE=2 |
|
|
|
# Only Private history has EDIT |
|
local -a NLIST_ENABLED_EVENTS |
|
NLIST_ENABLED_EVENTS=( "F1" "HELP" ) |
|
two_NLIST_ENABLED_EVENTS=( "F1" "EDIT" "HELP" ) |
|
three_NLIST_ENABLED_EVENTS=( "F1" "HELP" ) |
|
|
|
# All view should attempt to replace new lines with \n |
|
local NLIST_REPLACE_NEWLINES="1" |
|
two_NLIST_REPLACE_NEWLINES="1" |
|
three_NLIST_REPLACE_NEWLINES="1" |
|
|
|
# Only second and third view has non-selectable first entry |
|
local -a NLIST_NONSELECTABLE_ELEMENTS |
|
NLIST_NONSELECTABLE_ELEMENTS=( ) |
|
two_NLIST_NONSELECTABLE_ELEMENTS=( 1 ) |
|
three_NLIST_NONSELECTABLE_ELEMENTS=( 1 ) |
|
|
|
while (( 1 )); do |
|
|
|
# |
|
# View 1 - history |
|
# |
|
if [ "$active_view" = "0" ]; then |
|
list=( "$history[@]" ) |
|
list=( "${(@M)list:#(#i)*$NLIST_GREP_STRING*}" ) |
|
|
|
if [ "$#list" -eq 0 ]; then |
|
echo "No matching history entries" |
|
return 1 |
|
fi |
|
|
|
n-list "${list[@]}" |
|
|
|
# Selection or quit? |
|
if [[ "$REPLY" = -(#c0,1)[0-9]## && ("$REPLY" -lt 0 || "$REPLY" -gt 0) ]]; then |
|
break |
|
fi |
|
|
|
# View change? |
|
if [ "$REPLY" = "F1" ]; then |
|
# Target view: 2 |
|
active_view=1 |
|
_nhistory_switch_lists_states "1" |
|
elif [ "$REPLY" = "HELP" ]; then |
|
n-help |
|
fi |
|
|
|
# |
|
# View 3 - most frequent words in history |
|
# |
|
elif [ "$active_view" = "2" ]; then |
|
local -a dbfile |
|
dbfile=( $most_frequent_db(Nm+1) ) |
|
|
|
# Compute most frequent history words |
|
if [[ "${#NHISTORY_WORDS}" -eq "0" || "${#dbfile}" -ne "0" ]]; then |
|
# Read the list if it's there |
|
local -a list |
|
list=() |
|
[ -s "$most_frequent_db" ] && list=( ${(f)"$(<$most_frequent_db)"} ) |
|
|
|
# Will wait for the data? |
|
local message=0 |
|
if [[ "${#list}" -eq 0 ]]; then |
|
message=1 |
|
_nlist_alternate_screen 1 |
|
zcurses init |
|
zcurses delwin info 2>/dev/null |
|
zcurses addwin info "$term_height" "$term_width" 0 0 |
|
zcurses bg info white/black |
|
zcurses string info "Computing most frequent history words..."$'\n' |
|
zcurses string info "(This is done once per day, from now on transparently)"$'\n' |
|
zcurses refresh info |
|
sleep 3 |
|
fi |
|
|
|
# Start periodic list regeneration? |
|
if [[ "${#list}" -eq 0 || "${#dbfile}" -ne "0" ]]; then |
|
# Mark the file with current time, to prevent double |
|
# regeneration (on quick double change of view) |
|
print >> "$most_frequent_db" |
|
(_nhistory_generate_most_frequent &) &> /dev/null |
|
fi |
|
|
|
# Ensure we got the list, wait for it if needed |
|
while [[ "${#list}" -eq 0 ]]; do |
|
zcurses string info "." |
|
zcurses refresh info |
|
LANG=C sleep 0.5 |
|
[ -s "$most_frequent_db" ] && list=( ${(f)"$(<$most_frequent_db)"} ) |
|
done |
|
|
|
NHISTORY_WORDS=( "${list[@]}" ) |
|
|
|
if [ "$message" -eq "1" ]; then |
|
zcurses delwin info 2>/dev/null |
|
zcurses refresh |
|
zcurses end |
|
_nlist_alternate_screen 0 |
|
fi |
|
else |
|
# Reuse most frequent history words |
|
local -a list |
|
list=( "${NHISTORY_WORDS[@]}" ) |
|
fi |
|
|
|
n-list "${list[@]}" |
|
|
|
if [ "$REPLY" = "F1" ]; then |
|
# Target view: 1 |
|
active_view=0 |
|
_nhistory_switch_lists_states "0" |
|
elif [[ "$REPLY" = -(#c0,1)[0-9]## && "$REPLY" -lt 0 ]]; then |
|
break |
|
elif [[ "$REPLY" = -(#c0,1)[0-9]## && "$REPLY" -gt 0 ]]; then |
|
local word="${reply[REPLY]#(#s) #[0-9]##$'\t'}" |
|
one_NLIST_SEARCH_BUFFER="$word" |
|
one_NLIST_SEARCH_BUFFER="${one_NLIST_SEARCH_BUFFER## ##}" |
|
|
|
# Target view: 1 |
|
active_view=0 |
|
_nhistory_switch_lists_states "0" |
|
elif [ "$REPLY" = "HELP" ]; then |
|
n-help |
|
fi |
|
|
|
# |
|
# View 2 - private history |
|
# |
|
elif [ "$active_view" = "1" ]; then |
|
if [ -s "$private_history_db" ]; then |
|
local title=$'\x1b[00;32m'"Private history:"$'\x1b[00;00m\0' |
|
() { fc -Rap "$private_history_db" 20000 0; list=( "$title" ${history[@]} ) } |
|
else |
|
list=( "Private history - history entries selected via this tool will be put here" ) |
|
fi |
|
|
|
n-list "${list[@]}" |
|
|
|
# Selection or quit? |
|
if [[ "$REPLY" = -(#c0,1)[0-9]## && ("$REPLY" -lt 0 || "$REPLY" -gt 0) ]]; then |
|
break |
|
fi |
|
|
|
# View change? |
|
if [ "$REPLY" = "F1" ]; then |
|
# Target view: 3 |
|
active_view=2 |
|
_nhistory_switch_lists_states "2" |
|
# Edit of the history? |
|
elif [ "$REPLY" = "EDIT" ]; then |
|
"${EDITOR:-vim}" "$private_history_db" |
|
elif [ "$REPLY" = "HELP" ]; then |
|
n-help |
|
fi |
|
fi |
|
done |
|
|
|
if [ "$REPLY" -gt 0 ]; then |
|
selected="$reply[REPLY]" |
|
|
|
# Append to private history |
|
if [[ "$active_view" = "0" ]]; then |
|
local newline=$'\n' |
|
local selected_ph="${selected//$newline/\\$newline}" |
|
print -r -- "$selected_ph" >> "$private_history_db" |
|
fi |
|
|
|
# TMUX? |
|
if [[ "$ZNT_TMUX_MODE" = "1" ]]; then |
|
tmux send -t "$ZNT_TMUX_ORIGIN_SESSION:$ZNT_TMUX_ORIGIN_WINDOW.$ZNT_TMUX_ORIGIN_PANE" "$selected" |
|
tmux kill-window |
|
return 0 |
|
# ZLE? |
|
elif [ "${(t)CURSOR}" = "integer-local-special" ]; then |
|
zle .redisplay |
|
zle .kill-buffer |
|
LBUFFER+="$selected" |
|
else |
|
print -zr -- "$selected" |
|
fi |
|
else |
|
# TMUX? |
|
if [[ "$ZNT_TMUX_MODE" = "1" ]]; then |
|
tmux kill-window |
|
# ZLE? |
|
elif [[ "${(t)CURSOR}" = "integer-local-special" ]]; then |
|
zle redisplay |
|
fi |
|
fi |
|
|
|
return 0 |
|
|
|
# vim: set filetype=zsh:
|
|
|