1 # Copy this file into /usr/share/zsh/site-functions/
2 # and add 'autoload n-history` to .zshrc
4 # This function allows to browse Z shell's history and use the
13 zmodload zsh/parameter
18 # Variables to save list's state when switching views
19 # The views are: history and "most frequent history words"
20 local one_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
21 local one_NLIST_CURRENT_IDX
22 local one_NLIST_IS_SEARCH_MODE
23 local one_NLIST_SEARCH_BUFFER
24 local one_NLIST_TEXT_OFFSET
25 local one_NLIST_IS_UNIQ_MODE
26 local one_NLIST_IS_F_MODE
27 local one_NLIST_GREP_STRING
28 local one_NLIST_NONSELECTABLE_ELEMENTS
29 local one_NLIST_REMEMBER_STATE
30 local one_NLIST_ENABLED_EVENTS
32 local two_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
33 local two_NLIST_CURRENT_IDX
34 local two_NLIST_IS_SEARCH_MODE
35 local two_NLIST_SEARCH_BUFFER
36 local two_NLIST_TEXT_OFFSET
37 local two_NLIST_IS_UNIQ_MODE
38 local two_NLIST_IS_F_MODE
39 local two_NLIST_GREP_STRING
40 local two_NLIST_NONSELECTABLE_ELEMENTS
41 local two_NLIST_REMEMBER_STATE
42 local two_NLIST_ENABLED_EVENTS
44 local three_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
45 local three_NLIST_CURRENT_IDX
46 local three_NLIST_IS_SEARCH_MODE
47 local three_NLIST_SEARCH_BUFFER
48 local three_NLIST_TEXT_OFFSET
49 local three_NLIST_IS_UNIQ_MODE
50 local three_NLIST_IS_F_MODE
51 local three_NLIST_GREP_STRING
52 local three_NLIST_NONSELECTABLE_ELEMENTS
53 local three_NLIST_REMEMBER_STATE
54 local three_NLIST_ENABLED_EVENTS
59 # Lists are "0", "1", "2" - 1st, 2nd, 3rd
60 # Switching is done in cyclic manner
61 # i.e. 0 -> 1, 1 -> 2, 2 -> 0
62 _nhistory_switch_lists_states() {
63 # First argument is current, newly selected list, i.e. $active_view
64 # This implies that we are switching from previous view
66 if [ "$1" = "0" ]; then
67 # Switched to 1st list, save 3rd list's state
68 three_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
69 three_NLIST_CURRENT_IDX=$NLIST_CURRENT_IDX
70 three_NLIST_IS_SEARCH_MODE=$NLIST_IS_SEARCH_MODE
71 three_NLIST_SEARCH_BUFFER=$NLIST_SEARCH_BUFFER
72 three_NLIST_TEXT_OFFSET=$NLIST_TEXT_OFFSET
73 three_NLIST_IS_UNIQ_MODE=$NLIST_IS_UNIQ_MODE
74 three_NLIST_IS_F_MODE=$NLIST_IS_F_MODE
75 three_NLIST_GREP_STRING=$NLIST_GREP_STRING
76 three_NLIST_NONSELECTABLE_ELEMENTS=( ${NLIST_NONSELECTABLE_ELEMENTS[@]} )
77 three_NLIST_REMEMBER_STATE=$NLIST_REMEMBER_STATE
78 three_NLIST_ENABLED_EVENTS=( ${NLIST_ENABLED_EVENTS[@]} )
80 # ..and restore 1st list's state
81 NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$one_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
82 NLIST_CURRENT_IDX=$one_NLIST_CURRENT_IDX
83 NLIST_IS_SEARCH_MODE=$one_NLIST_IS_SEARCH_MODE
84 NLIST_SEARCH_BUFFER=$one_NLIST_SEARCH_BUFFER
85 NLIST_TEXT_OFFSET=$one_NLIST_TEXT_OFFSET
86 NLIST_IS_UNIQ_MODE=$one_NLIST_IS_UNIQ_MODE
87 NLIST_IS_F_MODE=$one_NLIST_IS_F_MODE
88 NLIST_GREP_STRING=$one_NLIST_GREP_STRING
89 NLIST_NONSELECTABLE_ELEMENTS=( ${one_NLIST_NONSELECTABLE_ELEMENTS[@]} )
90 NLIST_REMEMBER_STATE=$one_NLIST_REMEMBER_STATE
91 NLIST_ENABLED_EVENTS=( ${one_NLIST_ENABLED_EVENTS[@]} )
92 elif [ "$1" = "1" ]; then
93 # Switched to 2nd list, save 1st list's state
94 one_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
95 one_NLIST_CURRENT_IDX=$NLIST_CURRENT_IDX
96 one_NLIST_IS_SEARCH_MODE=$NLIST_IS_SEARCH_MODE
97 one_NLIST_SEARCH_BUFFER=$NLIST_SEARCH_BUFFER
98 one_NLIST_TEXT_OFFSET=$NLIST_TEXT_OFFSET
99 one_NLIST_IS_UNIQ_MODE=$NLIST_IS_UNIQ_MODE
100 one_NLIST_IS_F_MODE=$NLIST_IS_F_MODE
101 one_NLIST_GREP_STRING=$NLIST_GREP_STRING
102 one_NLIST_NONSELECTABLE_ELEMENTS=( ${NLIST_NONSELECTABLE_ELEMENTS[@]} )
103 one_NLIST_REMEMBER_STATE=$NLIST_REMEMBER_STATE
104 one_NLIST_ENABLED_EVENTS=( ${NLIST_ENABLED_EVENTS[@]} )
106 # ..and restore 2nd list's state
107 NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$two_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
108 NLIST_CURRENT_IDX=$two_NLIST_CURRENT_IDX
109 NLIST_IS_SEARCH_MODE=$two_NLIST_IS_SEARCH_MODE
110 NLIST_SEARCH_BUFFER=$two_NLIST_SEARCH_BUFFER
111 NLIST_TEXT_OFFSET=$two_NLIST_TEXT_OFFSET
112 NLIST_IS_UNIQ_MODE=$two_NLIST_IS_UNIQ_MODE
113 NLIST_IS_F_MODE=$two_NLIST_IS_F_MODE
114 NLIST_GREP_STRING=$two_NLIST_GREP_STRING
115 NLIST_NONSELECTABLE_ELEMENTS=( ${two_NLIST_NONSELECTABLE_ELEMENTS[@]} )
116 NLIST_REMEMBER_STATE=$two_NLIST_REMEMBER_STATE
117 NLIST_ENABLED_EVENTS=( ${two_NLIST_ENABLED_EVENTS[@]} )
118 elif [ "$1" = "2" ]; then
119 # Switched to 3rd list, save 2nd list's state
120 two_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
121 two_NLIST_CURRENT_IDX=$NLIST_CURRENT_IDX
122 two_NLIST_IS_SEARCH_MODE=$NLIST_IS_SEARCH_MODE
123 two_NLIST_SEARCH_BUFFER=$NLIST_SEARCH_BUFFER
124 two_NLIST_TEXT_OFFSET=$NLIST_TEXT_OFFSET
125 two_NLIST_IS_UNIQ_MODE=$NLIST_IS_UNIQ_MODE
126 two_NLIST_IS_F_MODE=$NLIST_IS_F_MODE
127 two_NLIST_GREP_STRING=$NLIST_GREP_STRING
128 two_NLIST_NONSELECTABLE_ELEMENTS=( ${NLIST_NONSELECTABLE_ELEMENTS[@]} )
129 two_NLIST_REMEMBER_STATE=$NLIST_REMEMBER_STATE
130 two_NLIST_ENABLED_EVENTS=( ${NLIST_ENABLED_EVENTS[@]} )
132 # ..and restore 3rd list's state
133 NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=$three_NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN
134 NLIST_CURRENT_IDX=$three_NLIST_CURRENT_IDX
135 NLIST_IS_SEARCH_MODE=$three_NLIST_IS_SEARCH_MODE
136 NLIST_SEARCH_BUFFER=$three_NLIST_SEARCH_BUFFER
137 NLIST_TEXT_OFFSET=$three_NLIST_TEXT_OFFSET
138 NLIST_IS_UNIQ_MODE=$three_NLIST_IS_UNIQ_MODE
139 NLIST_IS_F_MODE=$three_NLIST_IS_F_MODE
140 NLIST_GREP_STRING=$three_NLIST_GREP_STRING
141 NLIST_NONSELECTABLE_ELEMENTS=( ${three_NLIST_NONSELECTABLE_ELEMENTS[@]} )
142 NLIST_REMEMBER_STATE=$three_NLIST_REMEMBER_STATE
143 NLIST_ENABLED_EVENTS=( ${three_NLIST_ENABLED_EVENTS[@]} )
147 local most_frequent_db="$HOME/.config/znt/mostfrequent.db"
148 _nhistory_generate_most_frequent() {
149 local title=$'\x1b[00;31m'"Most frequent history words:"$'\x1b[00;00m\0'
152 for k in "${historywords[@]}"; do
153 uniq[$k]=$(( ${uniq[$k]:-0} + 1 ))
156 for k v in ${(kv)uniq}; do
160 print -rl -- "$title" "${(On)vk[@]}" > "$most_frequent_db"
164 unset NLIST_COLORING_PATTERN
165 [ -f ~/.config/znt/n-list.conf ] && builtin source ~/.config/znt/n-list.conf
166 [ -f ~/.config/znt/n-history.conf ] && builtin source ~/.config/znt/n-history.conf
170 local private_history_db="$HOME/.config/znt/privhist.db"
172 local NLIST_GREP_STRING="$1"
173 # 2 is: init once, then remember
174 local NLIST_REMEMBER_STATE=2
175 two_NLIST_REMEMBER_STATE=2
176 three_NLIST_REMEMBER_STATE=2
178 # Only Private history has EDIT
179 local -a NLIST_ENABLED_EVENTS
180 NLIST_ENABLED_EVENTS=( "F1" "HELP" )
181 two_NLIST_ENABLED_EVENTS=( "F1" "EDIT" "HELP" )
182 three_NLIST_ENABLED_EVENTS=( "F1" "HELP" )
184 # All view should attempt to replace new lines with \n
185 local NLIST_REPLACE_NEWLINES="1"
186 two_NLIST_REPLACE_NEWLINES="1"
187 three_NLIST_REPLACE_NEWLINES="1"
189 # Only second and third view has non-selectable first entry
190 local -a NLIST_NONSELECTABLE_ELEMENTS
191 NLIST_NONSELECTABLE_ELEMENTS=( )
192 two_NLIST_NONSELECTABLE_ELEMENTS=( 1 )
193 three_NLIST_NONSELECTABLE_ELEMENTS=( 1 )
200 if [ "$active_view" = "0" ]; then
201 list=( "$history[@]" )
202 list=( "${(@M)list:#(#i)*$NLIST_GREP_STRING*}" )
204 if [ "$#list" -eq 0 ]; then
205 echo "No matching history entries"
212 if [[ "$REPLY" = -(#c0,1)[0-9]## && ("$REPLY" -lt 0 || "$REPLY" -gt 0) ]]; then
217 if [ "$REPLY" = "F1" ]; then
220 _nhistory_switch_lists_states "1"
221 elif [ "$REPLY" = "HELP" ]; then
226 # View 3 - most frequent words in history
228 elif [ "$active_view" = "2" ]; then
230 dbfile=( $most_frequent_db(Nm+1) )
232 # Compute most frequent history words
233 if [[ "${#NHISTORY_WORDS}" -eq "0" || "${#dbfile}" -ne "0" ]]; then
234 # Read the list if it's there
237 [ -s "$most_frequent_db" ] && list=( ${(f)"$(<$most_frequent_db)"} )
239 # Will wait for the data?
241 if [[ "${#list}" -eq 0 ]]; then
243 _nlist_alternate_screen 1
245 zcurses delwin info 2>/dev/null
246 zcurses addwin info "$term_height" "$term_width" 0 0
247 zcurses bg info white/black
248 zcurses string info "Computing most frequent history words..."$'\n'
249 zcurses string info "(This is done once per day, from now on transparently)"$'\n'
254 # Start periodic list regeneration?
255 if [[ "${#list}" -eq 0 || "${#dbfile}" -ne "0" ]]; then
256 # Mark the file with current time, to prevent double
257 # regeneration (on quick double change of view)
258 print >> "$most_frequent_db"
259 (_nhistory_generate_most_frequent &) &> /dev/null
262 # Ensure we got the list, wait for it if needed
263 while [[ "${#list}" -eq 0 ]]; do
264 zcurses string info "."
267 [ -s "$most_frequent_db" ] && list=( ${(f)"$(<$most_frequent_db)"} )
270 NHISTORY_WORDS=( "${list[@]}" )
272 if [ "$message" -eq "1" ]; then
273 zcurses delwin info 2>/dev/null
276 _nlist_alternate_screen 0
279 # Reuse most frequent history words
281 list=( "${NHISTORY_WORDS[@]}" )
286 if [ "$REPLY" = "F1" ]; then
289 _nhistory_switch_lists_states "0"
290 elif [[ "$REPLY" = -(#c0,1)[0-9]## && "$REPLY" -lt 0 ]]; then
292 elif [[ "$REPLY" = -(#c0,1)[0-9]## && "$REPLY" -gt 0 ]]; then
293 local word="${reply[REPLY]#(#s) #[0-9]##$'\t'}"
294 one_NLIST_SEARCH_BUFFER="$word"
295 one_NLIST_SEARCH_BUFFER="${one_NLIST_SEARCH_BUFFER## ##}"
299 _nhistory_switch_lists_states "0"
300 elif [ "$REPLY" = "HELP" ]; then
305 # View 2 - private history
307 elif [ "$active_view" = "1" ]; then
308 if [ -s "$private_history_db" ]; then
309 local title=$'\x1b[00;32m'"Private history:"$'\x1b[00;00m\0'
310 () { fc -Rap "$private_history_db" 20000 0; list=( "$title" ${history[@]} ) }
312 list=( "Private history - history entries selected via this tool will be put here" )
318 if [[ "$REPLY" = -(#c0,1)[0-9]## && ("$REPLY" -lt 0 || "$REPLY" -gt 0) ]]; then
323 if [ "$REPLY" = "F1" ]; then
326 _nhistory_switch_lists_states "2"
327 # Edit of the history?
328 elif [ "$REPLY" = "EDIT" ]; then
329 "${EDITOR:-vim}" "$private_history_db"
330 elif [ "$REPLY" = "HELP" ]; then
336 if [ "$REPLY" -gt 0 ]; then
337 selected="$reply[REPLY]"
339 # Append to private history
340 if [[ "$active_view" = "0" ]]; then
342 local selected_ph="${selected//$newline/\\$newline}"
343 print -r -- "$selected_ph" >> "$private_history_db"
347 if [[ "$ZNT_TMUX_MODE" = "1" ]]; then
348 tmux send -t "$ZNT_TMUX_ORIGIN_SESSION:$ZNT_TMUX_ORIGIN_WINDOW.$ZNT_TMUX_ORIGIN_PANE" "$selected"
352 elif [ "${(t)CURSOR}" = "integer-local-special" ]; then
357 print -zr -- "$selected"
361 if [[ "$ZNT_TMUX_MODE" = "1" ]]; then
364 elif [[ "${(t)CURSOR}" = "integer-local-special" ]]; then
371 # vim: set filetype=zsh: