]> src.twobees.de Git - dotfiles.git/blob - stow/oh-my-zsh/.oh-my-zsh/plugins/zsh-navigation-tools/n-history
b425ecd10f4bb7040636e718df5f40b0a455fb13
[dotfiles.git] / stow / oh-my-zsh / .oh-my-zsh / plugins / zsh-navigation-tools / n-history
1 # Copy this file into /usr/share/zsh/site-functions/
2 # and add 'autoload n-history` to .zshrc
3 #
4 # This function allows to browse Z shell's history and use the
5 # entries
6 #
7 # Uses n-list
8
9 emulate -L zsh
10
11 setopt extendedglob
12 zmodload zsh/curses
13 zmodload zsh/parameter
14
15 local IFS="
16 "
17
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
31
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
43
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
55
56 # history view
57 integer active_view=0
58
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
65    
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[@]} )
79
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[@]} )
105
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[@]} )
131
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[@]} )
144     fi
145 }
146
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'
150
151     typeset -A uniq
152     for k in "${historywords[@]}"; do
153         uniq[$k]=$(( ${uniq[$k]:-0} + 1 ))
154     done
155     vk=()
156     for k v in ${(kv)uniq}; do
157         vk+="$v"$'\t'"$k"
158     done
159
160     print -rl -- "$title" "${(On)vk[@]}" > "$most_frequent_db"
161 }
162
163 # Load configuration
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
167
168 local list
169 local selected
170 local private_history_db="$HOME/.config/znt/privhist.db"
171
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
177
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" )
183
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"
188
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 )
194
195 while (( 1 )); do
196
197     #
198     # View 1 - history
199     #
200     if [ "$active_view" = "0" ]; then
201         list=( "$history[@]" )
202         list=( "${(@M)list:#(#i)*$NLIST_GREP_STRING*}" )
203
204         if [ "$#list" -eq 0 ]; then
205             echo "No matching history entries"
206             return 1
207         fi
208
209         n-list "${list[@]}"
210
211         # Selection or quit?
212         if [[ "$REPLY" = -(#c0,1)[0-9]## && ("$REPLY" -lt 0 || "$REPLY" -gt 0) ]]; then
213             break
214         fi
215
216         # View change?
217         if [ "$REPLY" = "F1" ]; then
218             # Target view: 2
219             active_view=1
220             _nhistory_switch_lists_states "1"
221         elif [ "$REPLY" = "HELP" ]; then
222             n-help
223         fi
224
225     #
226     # View 3 - most frequent words in history
227     #
228     elif [ "$active_view" = "2" ]; then
229         local -a dbfile
230         dbfile=( $most_frequent_db(Nm+1) )
231
232         # Compute most frequent history words
233         if [[ "${#NHISTORY_WORDS}" -eq "0" || "${#dbfile}" -ne "0" ]]; then
234             # Read the list if it's there
235             local -a list
236             list=()
237             [ -s "$most_frequent_db" ] && list=( ${(f)"$(<$most_frequent_db)"} )
238
239             # Will wait for the data?
240             local message=0
241             if [[ "${#list}" -eq 0 ]]; then
242                 message=1
243                 _nlist_alternate_screen 1
244                 zcurses init
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'
250                 zcurses refresh info
251                 sleep 3
252             fi
253
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
260             fi
261
262             # Ensure we got the list, wait for it if needed
263             while [[ "${#list}" -eq 0 ]]; do
264                 zcurses string info "."
265                 zcurses refresh info
266                 LANG=C sleep 0.5
267                 [ -s "$most_frequent_db" ] && list=( ${(f)"$(<$most_frequent_db)"} )
268             done
269
270             NHISTORY_WORDS=( "${list[@]}" )
271
272             if [ "$message" -eq "1" ]; then
273                 zcurses delwin info 2>/dev/null
274                 zcurses refresh
275                 zcurses end
276                 _nlist_alternate_screen 0
277             fi
278         else
279             # Reuse most frequent history words
280             local -a list
281             list=( "${NHISTORY_WORDS[@]}" )
282         fi
283
284         n-list "${list[@]}"
285
286         if [ "$REPLY" = "F1" ]; then
287             # Target view: 1
288             active_view=0
289             _nhistory_switch_lists_states "0"
290         elif [[ "$REPLY" = -(#c0,1)[0-9]## && "$REPLY" -lt 0 ]]; then
291             break
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## ##}"
296
297             # Target view: 1
298             active_view=0
299             _nhistory_switch_lists_states "0"
300         elif [ "$REPLY" = "HELP" ]; then
301             n-help
302         fi
303
304     #
305     # View 2 - private history
306     #
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[@]} ) }
311         else
312             list=( "Private history - history entries selected via this tool will be put here" )
313         fi
314
315         n-list "${list[@]}"
316
317         # Selection or quit?
318         if [[ "$REPLY" = -(#c0,1)[0-9]## && ("$REPLY" -lt 0 || "$REPLY" -gt 0) ]]; then
319             break
320         fi
321
322         # View change?
323         if [ "$REPLY" = "F1" ]; then
324             # Target view: 3
325             active_view=2
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
331             n-help
332         fi
333     fi
334 done
335
336 if [ "$REPLY" -gt 0 ]; then
337     selected="$reply[REPLY]"
338
339     # Append to private history
340     if [[ "$active_view" = "0" ]]; then
341         local newline=$'\n'
342         local selected_ph="${selected//$newline/\\$newline}"
343         print -r -- "$selected_ph" >> "$private_history_db"
344     fi
345
346     # TMUX?
347     if [[ "$ZNT_TMUX_MODE" = "1" ]]; then
348         tmux send -t "$ZNT_TMUX_ORIGIN_SESSION:$ZNT_TMUX_ORIGIN_WINDOW.$ZNT_TMUX_ORIGIN_PANE" "$selected"
349         tmux kill-window
350         return 0
351     # ZLE?
352     elif [ "${(t)CURSOR}" = "integer-local-special" ]; then
353         zle .redisplay
354         zle .kill-buffer
355         LBUFFER+="$selected"
356     else
357         print -zr -- "$selected"
358     fi
359 else
360     # TMUX?
361     if [[ "$ZNT_TMUX_MODE" = "1" ]]; then
362         tmux kill-window
363     # ZLE?
364     elif [[ "${(t)CURSOR}" = "integer-local-special" ]]; then
365         zle redisplay
366     fi
367 fi
368
369 return 0
370
371 # vim: set filetype=zsh: