mirror of
				https://github.com/ohmyzsh/ohmyzsh.git
				synced 2025-11-04 21:31:19 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			372 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			372 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
# 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:
 |