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:
 | |
| 
 |