history-substring-search.zsh 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. #!/usr/bin/env zsh
  2. ##############################################################################
  3. #
  4. # Copyright (c) 2009 Peter Stephenson
  5. # Copyright (c) 2011 Guido van Steen
  6. # Copyright (c) 2011 Suraj N. Kurapati
  7. # Copyright (c) 2011 Sorin Ionescu
  8. # Copyright (c) 2011 Vincent Guerci
  9. # All rights reserved.
  10. #
  11. # Redistribution and use in source and binary forms, with or without
  12. # modification, are permitted provided that the following conditions are met:
  13. #
  14. # * Redistributions of source code must retain the above copyright
  15. # notice, this list of conditions and the following disclaimer.
  16. #
  17. # * Redistributions in binary form must reproduce the above
  18. # copyright notice, this list of conditions and the following
  19. # disclaimer in the documentation and/or other materials provided
  20. # with the distribution.
  21. #
  22. # * Neither the name of the FIZSH nor the names of its contributors
  23. # may be used to endorse or promote products derived from this
  24. # software without specific prior written permission.
  25. #
  26. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  30. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. # POSSIBILITY OF SUCH DAMAGE.
  37. #
  38. ##############################################################################
  39. #-----------------------------------------------------------------------------
  40. # configuration variables
  41. #-----------------------------------------------------------------------------
  42. HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND='bg=magenta,fg=white,bold'
  43. HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND='bg=red,fg=white,bold'
  44. HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS='i'
  45. #-----------------------------------------------------------------------------
  46. # the main ZLE widgets
  47. #-----------------------------------------------------------------------------
  48. history-substring-search-up() {
  49. _history-substring-search-begin
  50. _history-substring-search-up-history ||
  51. _history-substring-search-up-buffer ||
  52. _history-substring-search-up-search
  53. _history-substring-search-end
  54. }
  55. history-substring-search-down() {
  56. _history-substring-search-begin
  57. _history-substring-search-down-history ||
  58. _history-substring-search-down-buffer ||
  59. _history-substring-search-down-search
  60. _history-substring-search-end
  61. }
  62. zle -N history-substring-search-up
  63. zle -N history-substring-search-down
  64. #-----------------------------------------------------------------------------
  65. # implementation details
  66. #-----------------------------------------------------------------------------
  67. zmodload -F zsh/parameter
  68. #
  69. # We have to "override" some keys and widgets if the
  70. # zsh-syntax-highlighting plugin has not been loaded:
  71. #
  72. # https://github.com/nicoulaj/zsh-syntax-highlighting
  73. #
  74. if [[ $+functions[_zsh_highlight] -eq 0 ]]; then
  75. #
  76. # Dummy implementation of _zsh_highlight() that
  77. # simply removes any existing highlights when the
  78. # user inserts printable characters into $BUFFER.
  79. #
  80. _zsh_highlight() {
  81. if [[ $KEYS == [[:print:]] ]]; then
  82. region_highlight=()
  83. fi
  84. }
  85. #
  86. # The following snippet was taken from the zsh-syntax-highlighting project:
  87. #
  88. # https://github.com/zsh-users/zsh-syntax-highlighting/blob/56b134f5d62ae3d4e66c7f52bd0cc2595f9b305b/zsh-syntax-highlighting.zsh#L126-161
  89. #
  90. # Copyright (c) 2010-2011 zsh-syntax-highlighting contributors
  91. # All rights reserved.
  92. #
  93. # Redistribution and use in source and binary forms, with or without
  94. # modification, are permitted provided that the following conditions are
  95. # met:
  96. #
  97. # * Redistributions of source code must retain the above copyright
  98. # notice, this list of conditions and the following disclaimer.
  99. #
  100. # * Redistributions in binary form must reproduce the above copyright
  101. # notice, this list of conditions and the following disclaimer in the
  102. # documentation and/or other materials provided with the distribution.
  103. #
  104. # * Neither the name of the zsh-syntax-highlighting contributors nor the
  105. # names of its contributors may be used to endorse or promote products
  106. # derived from this software without specific prior written permission.
  107. #
  108. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  109. # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  110. # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  111. # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  112. # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  113. # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  114. # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  115. # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  116. # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  117. # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  118. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  119. #
  120. #--------------8<-------------------8<-------------------8<-----------------
  121. # Rebind all ZLE widgets to make them invoke _zsh_highlights.
  122. _zsh_highlight_bind_widgets()
  123. {
  124. # Load ZSH module zsh/zleparameter, needed to override user defined widgets.
  125. zmodload zsh/zleparameter 2>/dev/null || {
  126. echo 'zsh-syntax-highlighting: failed loading zsh/zleparameter.' >&2
  127. return 1
  128. }
  129. # Override ZLE widgets to make them invoke _zsh_highlight.
  130. local cur_widget
  131. for cur_widget in ${${(f)"$(builtin zle -la)"}:#(.*|_*|orig-*|run-help|which-command|beep|yank*)}; do
  132. case $widgets[$cur_widget] in
  133. # Already rebound event: do nothing.
  134. user:$cur_widget|user:_zsh_highlight_widget_*);;
  135. # User defined widget: override and rebind old one with prefix "orig-".
  136. user:*) eval "zle -N orig-$cur_widget ${widgets[$cur_widget]#*:}; \
  137. _zsh_highlight_widget_$cur_widget() { builtin zle orig-$cur_widget -- \"\$@\" && _zsh_highlight }; \
  138. zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
  139. # Completion widget: override and rebind old one with prefix "orig-".
  140. completion:*) eval "zle -C orig-$cur_widget ${${widgets[$cur_widget]#*:}/:/ }; \
  141. _zsh_highlight_widget_$cur_widget() { builtin zle orig-$cur_widget -- \"\$@\" && _zsh_highlight }; \
  142. zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
  143. # Builtin widget: override and make it call the builtin ".widget".
  144. builtin) eval "_zsh_highlight_widget_$cur_widget() { builtin zle .$cur_widget -- \"\$@\" && _zsh_highlight }; \
  145. zle -N $cur_widget _zsh_highlight_widget_$cur_widget";;
  146. # Default: unhandled case.
  147. *) echo "zsh-syntax-highlighting: unhandled ZLE widget '$cur_widget'" >&2 ;;
  148. esac
  149. done
  150. }
  151. #-------------->8------------------->8------------------->8-----------------
  152. _zsh_highlight_bind_widgets
  153. fi
  154. _history-substring-search-begin() {
  155. setopt localoptions extendedglob
  156. _history_substring_search_refresh_display=
  157. _history_substring_search_query_highlight=
  158. #
  159. # Continue using the previous $_history_substring_search_result by default,
  160. # unless the current query was cleared or a new/different query was entered.
  161. #
  162. if [[ -z $BUFFER || $BUFFER != $_history_substring_search_result ]]; then
  163. #
  164. # For the purpose of highlighting we will also keep
  165. # a version without doubly-escaped meta characters.
  166. #
  167. _history_substring_search_query=$BUFFER
  168. #
  169. # $BUFFER contains the text that is in the command-line currently.
  170. # we put an extra "\\" before meta characters such as "\(" and "\)",
  171. # so that they become "\\\(" and "\\\)".
  172. #
  173. _history_substring_search_query_escaped=${BUFFER//(#m)[\][()|\\*?#<>~^]/\\$MATCH}
  174. #
  175. # Find all occurrences of the search query in the history file.
  176. #
  177. # (k) returns the "keys" (history index numbers) instead of the values
  178. # (Oa) reverses the order, because (R) returns results reversed.
  179. #
  180. _history_substring_search_matches=(${(kOa)history[(R)(#$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)*${_history_substring_search_query_escaped}*]})
  181. #
  182. # Define the range of values that $_history_substring_search_match_index
  183. # can take: [0, $_history_substring_search_matches_count_plus].
  184. #
  185. _history_substring_search_matches_count=$#_history_substring_search_matches
  186. _history_substring_search_matches_count_plus=$(( _history_substring_search_matches_count + 1 ))
  187. _history_substring_search_matches_count_sans=$(( _history_substring_search_matches_count - 1 ))
  188. #
  189. # If $_history_substring_search_match_index is equal to
  190. # $_history_substring_search_matches_count_plus, this indicates that we
  191. # are beyond the beginning of $_history_substring_search_matches.
  192. #
  193. # If $_history_substring_search_match_index is equal to 0, this indicates
  194. # that we are beyond the end of $_history_substring_search_matches.
  195. #
  196. # If we have initially pressed "up" we have to initialize
  197. # $_history_substring_search_match_index to
  198. # $_history_substring_search_matches_count_plus so that it will be
  199. # decreased to $_history_substring_search_matches_count.
  200. #
  201. # If we have initially pressed "down" we have to initialize
  202. # $_history_substring_search_match_index to
  203. # $_history_substring_search_matches_count so that it will be increased to
  204. # $_history_substring_search_matches_count_plus.
  205. #
  206. if [[ $WIDGET == history-substring-search-down ]]; then
  207. _history_substring_search_match_index=$_history_substring_search_matches_count
  208. else
  209. _history_substring_search_match_index=$_history_substring_search_matches_count_plus
  210. fi
  211. fi
  212. }
  213. _history-substring-search-end() {
  214. setopt localoptions extendedglob
  215. _history_substring_search_result=$BUFFER
  216. # the search was successful so display the result properly by clearing away
  217. # existing highlights and moving the cursor to the end of the result buffer
  218. if [[ $_history_substring_search_refresh_display -eq 1 ]]; then
  219. region_highlight=()
  220. CURSOR=${#BUFFER}
  221. fi
  222. # highlight command line using zsh-syntax-highlighting
  223. _zsh_highlight
  224. # highlight the search query inside the command line
  225. if [[ -n $_history_substring_search_query_highlight && -n $_history_substring_search_query ]]; then
  226. #
  227. # The following expression yields a variable $MBEGIN, which
  228. # indicates the begin position + 1 of the first occurrence
  229. # of _history_substring_search_query_escaped in $BUFFER.
  230. #
  231. : ${(S)BUFFER##(#m$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)($_history_substring_search_query##)}
  232. local begin=$(( MBEGIN - 1 ))
  233. local end=$(( begin + $#_history_substring_search_query ))
  234. region_highlight+=("$begin $end $_history_substring_search_query_highlight")
  235. fi
  236. # For debugging purposes:
  237. # zle -R "mn: "$_history_substring_search_match_index" m#: "${#_history_substring_search_matches}
  238. # read -k -t 200 && zle -U $REPLY
  239. # Exit successfully from the history-substring-search-* widgets.
  240. return 0
  241. }
  242. _history-substring-search-up-buffer() {
  243. #
  244. # Check if the UP arrow was pressed to move the cursor within a multi-line
  245. # buffer. This amounts to three tests:
  246. #
  247. # 1. $#buflines -gt 1.
  248. #
  249. # 2. $CURSOR -ne $#BUFFER.
  250. #
  251. # 3. Check if we are on the first line of the current multi-line buffer.
  252. # If so, pressing UP would amount to leaving the multi-line buffer.
  253. #
  254. # We check this by adding an extra "x" to $LBUFFER, which makes
  255. # sure that xlbuflines is always equal to the number of lines
  256. # until $CURSOR (including the line with the cursor on it).
  257. #
  258. local buflines XLBUFFER xlbuflines
  259. buflines=(${(f)BUFFER})
  260. XLBUFFER=$LBUFFER"x"
  261. xlbuflines=(${(f)XLBUFFER})
  262. if [[ $#buflines -gt 1 && $CURSOR -ne $#BUFFER && $#xlbuflines -ne 1 ]]; then
  263. zle up-line-or-history
  264. return 0
  265. fi
  266. return 1
  267. }
  268. _history-substring-search-down-buffer() {
  269. #
  270. # Check if the DOWN arrow was pressed to move the cursor within a multi-line
  271. # buffer. This amounts to three tests:
  272. #
  273. # 1. $#buflines -gt 1.
  274. #
  275. # 2. $CURSOR -ne $#BUFFER.
  276. #
  277. # 3. Check if we are on the last line of the current multi-line buffer.
  278. # If so, pressing DOWN would amount to leaving the multi-line buffer.
  279. #
  280. # We check this by adding an extra "x" to $RBUFFER, which makes
  281. # sure that xrbuflines is always equal to the number of lines
  282. # from $CURSOR (including the line with the cursor on it).
  283. #
  284. local buflines XRBUFFER xrbuflines
  285. buflines=(${(f)BUFFER})
  286. XRBUFFER="x"$RBUFFER
  287. xrbuflines=(${(f)XRBUFFER})
  288. if [[ $#buflines -gt 1 && $CURSOR -ne $#BUFFER && $#xrbuflines -ne 1 ]]; then
  289. zle down-line-or-history
  290. return 0
  291. fi
  292. return 1
  293. }
  294. _history-substring-search-up-history() {
  295. #
  296. # Behave like up in ZSH, except clear the $BUFFER
  297. # when beginning of history is reached like in Fish.
  298. #
  299. if [[ -z $_history_substring_search_query ]]; then
  300. # we have reached the absolute top of history
  301. if [[ $HISTNO -eq 1 ]]; then
  302. BUFFER=
  303. # going up from somewhere below the top of history
  304. else
  305. zle up-line-or-history
  306. fi
  307. return 0
  308. fi
  309. return 1
  310. }
  311. _history-substring-search-down-history() {
  312. #
  313. # Behave like down-history in ZSH, except clear the
  314. # $BUFFER when end of history is reached like in Fish.
  315. #
  316. if [[ -z $_history_substring_search_query ]]; then
  317. # going down from the absolute top of history
  318. if [[ $HISTNO -eq 1 && -z $BUFFER ]]; then
  319. BUFFER=${history[1]}
  320. _history_substring_search_refresh_display=1
  321. # going down from somewhere above the bottom of history
  322. else
  323. zle down-line-or-history
  324. fi
  325. return 0
  326. fi
  327. return 1
  328. }
  329. _history-substring-search-not-found() {
  330. #
  331. # Nothing matched the search query, so put it back into the $BUFFER while
  332. # highlighting it accordingly so the user can revise it and search again.
  333. #
  334. _history_substring_search_old_buffer=$BUFFER
  335. BUFFER=$_history_substring_search_query
  336. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  337. }
  338. _history-substring-search-up-search() {
  339. _history_substring_search_refresh_display=1
  340. #
  341. # Highlight matches during history-substring-up-search:
  342. #
  343. # The following constants have been initialized in
  344. # _history-substring-search-up/down-search():
  345. #
  346. # $_history_substring_search_matches is the current list of matches
  347. # $_history_substring_search_matches_count is the current number of matches
  348. # $_history_substring_search_matches_count_plus is the current number of matches + 1
  349. # $_history_substring_search_matches_count_sans is the current number of matches - 1
  350. # $_history_substring_search_match_index is the index of the current match
  351. #
  352. # The range of values that $_history_substring_search_match_index can take
  353. # is: [0, $_history_substring_search_matches_count_plus]. A value of 0
  354. # indicates that we are beyond the end of
  355. # $_history_substring_search_matches. A value of
  356. # $_history_substring_search_matches_count_plus indicates that we are beyond
  357. # the beginning of $_history_substring_search_matches.
  358. #
  359. # In _history-substring-search-up-search() the initial value of
  360. # $_history_substring_search_match_index is
  361. # $_history_substring_search_matches_count_plus. This value is set in
  362. # _history-substring-search-begin(). _history-substring-search-up-search()
  363. # will initially decrease it to $_history_substring_search_matches_count.
  364. #
  365. if [[ $_history_substring_search_match_index -ge 2 ]]; then
  366. #
  367. # Highlight the next match:
  368. #
  369. # 1. Decrease the value of $_history_substring_search_match_index.
  370. #
  371. # 2. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  372. # to highlight the current buffer.
  373. #
  374. (( _history_substring_search_match_index-- ))
  375. BUFFER=$history[$_history_substring_search_matches[$_history_substring_search_match_index]]
  376. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  377. elif [[ $_history_substring_search_match_index -eq 1 ]]; then
  378. #
  379. # We will move beyond the end of $_history_substring_search_matches:
  380. #
  381. # 1. Decrease the value of $_history_substring_search_match_index.
  382. #
  383. # 2. Save the current buffer in $_history_substring_search_old_buffer,
  384. # so that it can be retrieved by
  385. # _history-substring-search-down-search() later.
  386. #
  387. # 3. Make $BUFFER equal to $_history_substring_search_query.
  388. #
  389. # 4. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  390. # to highlight the current buffer.
  391. #
  392. (( _history_substring_search_match_index-- ))
  393. _history-substring-search-not-found
  394. elif [[ $_history_substring_search_match_index -eq $_history_substring_search_matches_count_plus ]]; then
  395. #
  396. # We were beyond the beginning of $_history_substring_search_matches but
  397. # UP makes us move back to $_history_substring_search_matches:
  398. #
  399. # 1. Decrease the value of $_history_substring_search_match_index.
  400. #
  401. # 2. Restore $BUFFER from $_history_substring_search_old_buffer.
  402. #
  403. # 3. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  404. # to highlight the current buffer.
  405. #
  406. (( _history_substring_search_match_index-- ))
  407. BUFFER=$_history_substring_search_old_buffer
  408. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  409. else
  410. #
  411. # We are at the beginning of history and there are no further matches.
  412. #
  413. _history-substring-search-not-found
  414. return
  415. fi
  416. #
  417. # When HIST_FIND_NO_DUPS is set, meaning that only unique command lines from
  418. # history should be matched, make sure the new and old results are different.
  419. # But when HIST_IGNORE_ALL_DUPS is set, ZSH already ensures a unique history.
  420. #
  421. if [[ ! -o HIST_IGNORE_ALL_DUPS && -o HIST_FIND_NO_DUPS && $BUFFER == $_history_substring_search_result ]]; then
  422. #
  423. # Repeat the current search so that a different (unique) match is found.
  424. #
  425. _history-substring-search-up-search
  426. fi
  427. }
  428. _history-substring-search-down-search() {
  429. _history_substring_search_refresh_display=1
  430. #
  431. # Highlight matches during history-substring-up-search:
  432. #
  433. # The following constants have been initialized in
  434. # _history-substring-search-up/down-search():
  435. #
  436. # $_history_substring_search_matches is the current list of matches
  437. # $_history_substring_search_matches_count is the current number of matches
  438. # $_history_substring_search_matches_count_plus is the current number of matches + 1
  439. # $_history_substring_search_matches_count_sans is the current number of matches - 1
  440. # $_history_substring_search_match_index is the index of the current match
  441. #
  442. # The range of values that $_history_substring_search_match_index can take
  443. # is: [0, $_history_substring_search_matches_count_plus]. A value of 0
  444. # indicates that we are beyond the end of
  445. # $_history_substring_search_matches. A value of
  446. # $_history_substring_search_matches_count_plus indicates that we are beyond
  447. # the beginning of $_history_substring_search_matches.
  448. #
  449. # In _history-substring-search-down-search() the initial value of
  450. # $_history_substring_search_match_index is
  451. # $_history_substring_search_matches_count. This value is set in
  452. # _history-substring-search-begin().
  453. # _history-substring-search-down-search() will initially increase it to
  454. # $_history_substring_search_matches_count_plus.
  455. #
  456. if [[ $_history_substring_search_match_index -le $_history_substring_search_matches_count_sans ]]; then
  457. #
  458. # Highlight the next match:
  459. #
  460. # 1. Increase $_history_substring_search_match_index by 1.
  461. #
  462. # 2. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  463. # to highlight the current buffer.
  464. #
  465. (( _history_substring_search_match_index++ ))
  466. BUFFER=$history[$_history_substring_search_matches[$_history_substring_search_match_index]]
  467. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  468. elif [[ $_history_substring_search_match_index -eq $_history_substring_search_matches_count ]]; then
  469. #
  470. # We will move beyond the beginning of $_history_substring_search_matches:
  471. #
  472. # 1. Increase $_history_substring_search_match_index by 1.
  473. #
  474. # 2. Save the current buffer in $_history_substring_search_old_buffer, so
  475. # that it can be retrieved by _history-substring-search-up-search()
  476. # later.
  477. #
  478. # 3. Make $BUFFER equal to $_history_substring_search_query.
  479. #
  480. # 4. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  481. # to highlight the current buffer.
  482. #
  483. (( _history_substring_search_match_index++ ))
  484. _history-substring-search-not-found
  485. elif [[ $_history_substring_search_match_index -eq 0 ]]; then
  486. #
  487. # We were beyond the end of $_history_substring_search_matches but DOWN
  488. # makes us move back to the $_history_substring_search_matches:
  489. #
  490. # 1. Increase $_history_substring_search_match_index by 1.
  491. #
  492. # 2. Restore $BUFFER from $_history_substring_search_old_buffer.
  493. #
  494. # 3. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  495. # to highlight the current buffer.
  496. #
  497. (( _history_substring_search_match_index++ ))
  498. BUFFER=$_history_substring_search_old_buffer
  499. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  500. else
  501. #
  502. # We are at the end of history and there are no further matches.
  503. #
  504. _history-substring-search-not-found
  505. return
  506. fi
  507. #
  508. # When HIST_FIND_NO_DUPS is set, meaning that only unique command lines from
  509. # history should be matched, make sure the new and old results are different.
  510. # But when HIST_IGNORE_ALL_DUPS is set, ZSH already ensures a unique history.
  511. #
  512. if [[ ! -o HIST_IGNORE_ALL_DUPS && -o HIST_FIND_NO_DUPS && $BUFFER == $_history_substring_search_result ]]; then
  513. #
  514. # Repeat the current search so that a different (unique) match is found.
  515. #
  516. _history-substring-search-down-search
  517. fi
  518. }
  519. # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
  520. # vim: ft=zsh sw=2 ts=2 et