history-substring-search.zsh 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. #!/usr/bin/env zsh
  2. #
  3. # This is a clean-room implementation of the Fish[1] shell's history search
  4. # feature, where you can type in any part of any previously entered command
  5. # and press the UP and DOWN arrow keys to cycle through the matching commands.
  6. #
  7. #-----------------------------------------------------------------------------
  8. # Usage
  9. #-----------------------------------------------------------------------------
  10. #
  11. # 1. Load this script into your interactive ZSH session:
  12. #
  13. # % source history-substring-search.zsh
  14. #
  15. # If you want to use the zsh-syntax-highlighting[6] script along with this
  16. # script, then make sure that you load it *before* you load this script:
  17. #
  18. # % source zsh-syntax-highlighting.zsh
  19. # % source history-substring-search.zsh
  20. #
  21. # 2. Type any part of any previous command and then:
  22. #
  23. # * Press the UP arrow key to select the nearest command that (1) contains
  24. # your query and (2) is older than the current command in the command
  25. # history.
  26. #
  27. # * Press the DOWN arrow key to select the nearest command that (1)
  28. # contains your query and (2) is newer than the current command in the
  29. # command history.
  30. #
  31. # * Press ^U (the Control and U keys simultaneously) to abort the search.
  32. #
  33. # 3. If a matching command spans more than one line of text, press the LEFT
  34. # arrow key to move the cursor away from the end of the command, and then:
  35. #
  36. # * Press the UP arrow key to move the cursor to the line above. When the
  37. # cursor reaches the first line of the command, pressing the UP arrow
  38. # key again will cause this script to perform another search.
  39. #
  40. # * Press the DOWN arrow key to move the cursor to the line below. When
  41. # the cursor reaches the last line of the command, pressing the DOWN
  42. # arrow key again will cause this script to perform another search.
  43. #
  44. #-----------------------------------------------------------------------------
  45. # Configuration
  46. #-----------------------------------------------------------------------------
  47. #
  48. # This script defines the following global variables. You may override their
  49. # default values only after having loaded this script into your ZSH session.
  50. #
  51. # * HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND is a global variable that defines
  52. # how the query should be highlighted inside a matching command. Its default
  53. # value causes this script to highlight using bold, white text on a magenta
  54. # background. See the "Character Highlighting" section in the zshzle(1) man
  55. # page to learn about the kinds of values you may assign to this variable.
  56. #
  57. # * HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND is a global variable that
  58. # defines how the query should be highlighted when no commands in the
  59. # history match it. Its default value causes this script to highlight using
  60. # bold, white text on a red background. See the "Character Highlighting"
  61. # section in the zshzle(1) man page to learn about the kinds of values you
  62. # may assign to this variable.
  63. #
  64. # * HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS is a global variable that defines
  65. # how the command history will be searched for your query. Its default value
  66. # causes this script to perform a case-insensitive search. See the "Globbing
  67. # Flags" section in the zshexpn(1) man page to learn about the kinds of
  68. # values you may assign to this variable.
  69. #
  70. #-----------------------------------------------------------------------------
  71. # History
  72. #-----------------------------------------------------------------------------
  73. #
  74. # This script was originally written by Peter Stephenson[2], who published it
  75. # to the ZSH users mailing list (thereby making it public domain) in September
  76. # 2009. It was later revised by Guido van Steen and released under the BSD
  77. # license (see below) as part of the fizsh[3] project in January 2011.
  78. #
  79. # It was later extracted from fizsh[3] release 1.0.1, refactored heavily, and
  80. # repackaged as both an oh-my-zsh plugin[4] and as an independently loadable
  81. # ZSH script[5] by Suraj N. Kurapati in 2011.
  82. #
  83. # It was further developed[4] by Guido van Steen, Suraj N. Kurapati, Sorin
  84. # Ionescu, and Vincent Guerci in 2011.
  85. #
  86. # [1]: http://fishshell.com
  87. # [2]: http://www.zsh.org/mla/users/2009/msg00818.html
  88. # [3]: http://sourceforge.net/projects/fizsh/
  89. # [4]: https://github.com/robbyrussell/oh-my-zsh/pull/215
  90. # [5]: https://github.com/sunaku/zsh-history-substring-search
  91. # [6]: https://github.com/nicoulaj/zsh-syntax-highlighting
  92. #
  93. ##############################################################################
  94. #
  95. # Copyright (c) 2009 Peter Stephenson
  96. # Copyright (c) 2011 Guido van Steen
  97. # Copyright (c) 2011 Suraj N. Kurapati
  98. # Copyright (c) 2011 Sorin Ionescu
  99. # Copyright (c) 2011 Vincent Guerci
  100. # All rights reserved.
  101. #
  102. # Redistribution and use in source and binary forms, with or without
  103. # modification, are permitted provided that the following conditions are met:
  104. #
  105. # * Redistributions of source code must retain the above copyright
  106. # notice, this list of conditions and the following disclaimer.
  107. #
  108. # * Redistributions in binary form must reproduce the above
  109. # copyright notice, this list of conditions and the following
  110. # disclaimer in the documentation and/or other materials provided
  111. # with the distribution.
  112. #
  113. # * Neither the name of the FIZSH nor the names of its contributors
  114. # may be used to endorse or promote products derived from this
  115. # software without specific prior written permission.
  116. #
  117. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  118. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  119. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  120. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  121. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  122. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  123. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  124. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  125. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  126. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  127. # POSSIBILITY OF SUCH DAMAGE.
  128. #
  129. ##############################################################################
  130. #-----------------------------------------------------------------------------
  131. # configuration variables
  132. #-----------------------------------------------------------------------------
  133. HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND='bg=magenta,fg=white,bold'
  134. HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND='bg=red,fg=white,bold'
  135. HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS='i'
  136. #-----------------------------------------------------------------------------
  137. # the main ZLE widgets
  138. #-----------------------------------------------------------------------------
  139. function history-substring-search-up() {
  140. _history-substring-search-begin
  141. _history-substring-search-up-history ||
  142. _history-substring-search-up-buffer ||
  143. _history-substring-search-up-search
  144. _history-substring-search-end
  145. }
  146. function history-substring-search-down() {
  147. _history-substring-search-begin
  148. _history-substring-search-down-history ||
  149. _history-substring-search-down-buffer ||
  150. _history-substring-search-down-search
  151. _history-substring-search-end
  152. }
  153. zle -N history-substring-search-up
  154. zle -N history-substring-search-down
  155. bindkey '\e[A' history-substring-search-up
  156. bindkey '\e[B' history-substring-search-down
  157. #-----------------------------------------------------------------------------
  158. # implementation details
  159. #-----------------------------------------------------------------------------
  160. setopt extendedglob
  161. zmodload -F zsh/parameter
  162. #
  163. # We have to "override" some keys and widgets if the
  164. # zsh-syntax-highlighting plugin has not been loaded:
  165. #
  166. # https://github.com/nicoulaj/zsh-syntax-highlighting
  167. #
  168. if [[ $+functions[_zsh_highlight] -eq 0 ]]; then
  169. #
  170. # Dummy implementation of _zsh_highlight()
  171. # that simply removes existing highlights
  172. #
  173. function _zsh_highlight() {
  174. region_highlight=()
  175. }
  176. #
  177. # Remove existing highlights when the user
  178. # inserts printable characters into $BUFFER
  179. #
  180. function ordinary-key-press() {
  181. if [[ $KEYS == [[:print:]] ]]; then
  182. region_highlight=()
  183. fi
  184. zle .self-insert
  185. }
  186. zle -N self-insert ordinary-key-press
  187. #
  188. # Override ZLE widgets to invoke _zsh_highlight()
  189. #
  190. # https://github.com/nicoulaj/zsh-syntax-highlighting/blob/
  191. # bb7fcb79fad797a40077bebaf6f4e4a93c9d8163/zsh-syntax-highlighting.zsh#L121
  192. #
  193. #--------------8<-------------------8<-------------------8<-----------------
  194. #
  195. # Copyright (c) 2010-2011 zsh-syntax-highlighting contributors
  196. # All rights reserved.
  197. #
  198. # Redistribution and use in source and binary forms, with or without
  199. # modification, are permitted provided that the following conditions are
  200. # met:
  201. #
  202. # * Redistributions of source code must retain the above copyright
  203. # notice, this list of conditions and the following disclaimer.
  204. #
  205. # * Redistributions in binary form must reproduce the above copyright
  206. # notice, this list of conditions and the following disclaimer in the
  207. # documentation and/or other materials provided with the distribution.
  208. #
  209. # * Neither the name of the zsh-syntax-highlighting contributors nor the
  210. # names of its contributors may be used to endorse or promote products
  211. # derived from this software without specific prior written permission.
  212. #
  213. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  214. # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  215. # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  216. # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  217. # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  218. # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  219. # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  220. # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  221. # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  222. # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  223. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  224. # Load ZSH module zsh/zleparameter, needed to override user defined widgets.
  225. zmodload zsh/zleparameter 2>/dev/null || {
  226. echo 'zsh-syntax-highlighting: failed loading zsh/zleparameter, exiting.' >&2
  227. return -1
  228. }
  229. # Override ZLE widgets to make them invoke _zsh_highlight.
  230. for event in ${${(f)"$(zle -la)"}:#(_*|orig-*|.run-help|.which-command)}; do
  231. if [[ "$widgets[$event]" == completion:* ]]; then
  232. eval "zle -C orig-$event ${${${widgets[$event]}#*:}/:/ } ; $event() { builtin zle orig-$event && _zsh_highlight } ; zle -N $event"
  233. else
  234. case $event in
  235. accept-and-menu-complete)
  236. eval "$event() { builtin zle .$event && _zsh_highlight } ; zle -N $event"
  237. ;;
  238. # The following widgets should NOT remove any previously
  239. # applied highlighting. Therefore we do not remap them.
  240. .forward-char|.backward-char|.up-line-or-history|.down-line-or-history)
  241. ;;
  242. .*)
  243. clean_event=$event[2,${#event}] # Remove the leading dot in the event name
  244. case ${widgets[$clean_event]-} in
  245. (completion|user):*)
  246. ;;
  247. *)
  248. eval "$clean_event() { builtin zle $event && _zsh_highlight } ; zle -N $clean_event"
  249. ;;
  250. esac
  251. ;;
  252. *)
  253. ;;
  254. esac
  255. fi
  256. done
  257. unset event clean_event
  258. #-------------->8------------------->8------------------->8-----------------
  259. fi
  260. function _history-substring-search-begin() {
  261. _history_substring_search_move_cursor_eol=false
  262. _history_substring_search_query_highlight=
  263. #
  264. # Continue using the previous $_history_substring_search_result by default,
  265. # unless the current query was cleared or a new/different query was entered.
  266. #
  267. if [[ -z $BUFFER || $BUFFER != $_history_substring_search_result ]]; then
  268. #
  269. # For the purpose of highlighting we will also keep
  270. # a version without doubly-escaped meta characters.
  271. #
  272. _history_substring_search_query=$BUFFER
  273. #
  274. # $BUFFER contains the text that is in the command-line currently.
  275. # we put an extra "\\" before meta characters such as "\(" and "\)",
  276. # so that they become "\\\(" and "\\\)".
  277. #
  278. _history_substring_search_query_escaped=${BUFFER//(#m)[\][()|\\*?#<>~^]/\\$MATCH}
  279. #
  280. # Find all occurrences of the search query in the history file.
  281. #
  282. # (k) turns it an array of line numbers.
  283. #
  284. # (on) seems to remove duplicates, which are default
  285. # options. They can be turned off by (ON).
  286. #
  287. _history_substring_search_matches=(${(kon)history[(R)(#$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)*${_history_substring_search_query_escaped}*]})
  288. #
  289. # Define the range of values that $_history_substring_search_match_index
  290. # can take: [0, $_history_substring_search_matches_count_plus].
  291. #
  292. _history_substring_search_matches_count=$#_history_substring_search_matches
  293. _history_substring_search_matches_count_plus=$(( _history_substring_search_matches_count + 1 ))
  294. _history_substring_search_matches_count_sans=$(( _history_substring_search_matches_count - 1 ))
  295. #
  296. # If $_history_substring_search_match_index is equal to
  297. # $_history_substring_search_matches_count_plus, this indicates that we
  298. # are beyond the beginning of $_history_substring_search_matches.
  299. #
  300. # If $_history_substring_search_match_index is equal to 0, this indicates
  301. # that we are beyond the end of $_history_substring_search_matches.
  302. #
  303. # If we have initially pressed "up" we have to initialize
  304. # $_history_substring_search_match_index to
  305. # $_history_substring_search_matches_count_plus so that it will be
  306. # decreased to $_history_substring_search_matches_count.
  307. #
  308. # If we have initially pressed "down" we have to initialize
  309. # $_history_substring_search_match_index to
  310. # $_history_substring_search_matches_count so that it will be increased to
  311. # $_history_substring_search_matches_count_plus.
  312. #
  313. if [[ $WIDGET == history-substring-search-down ]]; then
  314. _history_substring_search_match_index=$_history_substring_search_matches_count
  315. else
  316. _history_substring_search_match_index=$_history_substring_search_matches_count_plus
  317. fi
  318. fi
  319. }
  320. function _history-substring-search-end() {
  321. _history_substring_search_result=$BUFFER
  322. # move the cursor to the end of the command line
  323. if [[ $_history_substring_search_move_cursor_eol == true ]]; then
  324. CURSOR=${#BUFFER}
  325. fi
  326. # highlight command line using zsh-syntax-highlighting
  327. _zsh_highlight
  328. # highlight the search query inside the command line
  329. if [[ -n $_history_substring_search_query_highlight && -n $_history_substring_search_query ]]; then
  330. #
  331. # The following expression yields a variable $MBEGIN, which
  332. # indicates the begin position + 1 of the first occurrence
  333. # of _history_substring_search_query_escaped in $BUFFER.
  334. #
  335. : ${(S)BUFFER##(#m$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)($_history_substring_search_query##)}
  336. local begin=$(( MBEGIN - 1 ))
  337. local end=$(( begin + $#_history_substring_search_query ))
  338. region_highlight+=("$begin $end $_history_substring_search_query_highlight")
  339. fi
  340. # For debugging purposes:
  341. # zle -R "mn: "$_history_substring_search_match_index" m#: "${#_history_substring_search_matches}
  342. # read -k -t 200 && zle -U $REPLY
  343. # Exit successfully from the history-substring-search-* widgets.
  344. true
  345. }
  346. function _history-substring-search-up-buffer() {
  347. #
  348. # Check if the UP arrow was pressed to move the cursor within a multi-line
  349. # buffer. This amounts to three tests:
  350. #
  351. # 1. $#buflines -gt 1.
  352. #
  353. # 2. $CURSOR -ne $#BUFFER.
  354. #
  355. # 3. Check if we are on the first line of the current multi-line buffer.
  356. # If so, pressing UP would amount to leaving the multi-line buffer.
  357. #
  358. # We check this by adding an extra "x" to $LBUFFER, which makes
  359. # sure that xlbuflines is always equal to the number of lines
  360. # until $CURSOR (including the line with the cursor on it).
  361. #
  362. local buflines XLBUFFER xlbuflines
  363. buflines=(${(f)BUFFER})
  364. XLBUFFER=$LBUFFER"x"
  365. xlbuflines=(${(f)XLBUFFER})
  366. if [[ $#buflines -gt 1 && $CURSOR -ne $#BUFFER && $#xlbuflines -ne 1 ]]; then
  367. zle up-line-or-history
  368. return true
  369. fi
  370. false
  371. }
  372. function _history-substring-search-down-buffer() {
  373. #
  374. # Check if the DOWN arrow was pressed to move the cursor within a multi-line
  375. # buffer. This amounts to three tests:
  376. #
  377. # 1. $#buflines -gt 1.
  378. #
  379. # 2. $CURSOR -ne $#BUFFER.
  380. #
  381. # 3. Check if we are on the last line of the current multi-line buffer.
  382. # If so, pressing DOWN would amount to leaving the multi-line buffer.
  383. #
  384. # We check this by adding an extra "x" to $RBUFFER, which makes
  385. # sure that xrbuflines is always equal to the number of lines
  386. # from $CURSOR (including the line with the cursor on it).
  387. #
  388. local buflines XRBUFFER xrbuflines
  389. buflines=(${(f)BUFFER})
  390. XRBUFFER="x"$RBUFFER
  391. xrbuflines=(${(f)XRBUFFER})
  392. if [[ $#buflines -gt 1 && $CURSOR -ne $#BUFFER && $#xrbuflines -ne 1 ]]; then
  393. zle down-line-or-history
  394. return true
  395. fi
  396. false
  397. }
  398. function _history-substring-search-up-history() {
  399. #
  400. # Behave like up in ZSH, except clear the $BUFFER
  401. # when beginning of history is reached like in Fish.
  402. #
  403. if [[ -z $_history_substring_search_query ]]; then
  404. # we have reached the absolute top of history
  405. if [[ $HISTNO -eq 1 ]]; then
  406. BUFFER=
  407. # going up from somewhere below the top of history
  408. else
  409. zle up-history
  410. fi
  411. return true
  412. fi
  413. false
  414. }
  415. function _history-substring-search-down-history() {
  416. #
  417. # Behave like down-history in ZSH, except clear the
  418. # $BUFFER when end of history is reached like in Fish.
  419. #
  420. if [[ -z $_history_substring_search_query ]]; then
  421. # going down from the absolute top of history
  422. if [[ $HISTNO -eq 1 && -z $BUFFER ]]; then
  423. BUFFER=${history[1]}
  424. _history_substring_search_move_cursor_eol=true
  425. # going down from somewhere above the bottom of history
  426. else
  427. zle down-history
  428. fi
  429. return true
  430. fi
  431. false
  432. }
  433. function _history-substring-search-up-search() {
  434. _history_substring_search_move_cursor_eol=true
  435. #
  436. # Highlight matches during history-substring-up-search:
  437. #
  438. # The following constants have been initialized in
  439. # _history-substring-search-up/down-search():
  440. #
  441. # $_history_substring_search_matches is the current list of matches
  442. # $_history_substring_search_matches_count is the current number of matches
  443. # $_history_substring_search_matches_count_plus is the current number of matches + 1
  444. # $_history_substring_search_matches_count_sans is the current number of matches - 1
  445. # $_history_substring_search_match_index is the index of the current match
  446. #
  447. # The range of values that $_history_substring_search_match_index can take
  448. # is: [0, $_history_substring_search_matches_count_plus]. A value of 0
  449. # indicates that we are beyond the end of
  450. # $_history_substring_search_matches. A value of
  451. # $_history_substring_search_matches_count_plus indicates that we are beyond
  452. # the beginning of $_history_substring_search_matches.
  453. #
  454. # In _history-substring-search-up-search() the initial value of
  455. # $_history_substring_search_match_index is
  456. # $_history_substring_search_matches_count_plus. This value is set in
  457. # _history-substring-search-begin(). _history-substring-search-up-search()
  458. # will initially decrease it to $_history_substring_search_matches_count.
  459. #
  460. if [[ $_history_substring_search_match_index -ge 2 ]]; then
  461. #
  462. # Highlight the next match:
  463. #
  464. # 1. Decrease the value of $_history_substring_search_match_index.
  465. #
  466. # 2. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  467. # to highlight the current buffer.
  468. #
  469. (( _history_substring_search_match_index-- ))
  470. BUFFER=$history[$_history_substring_search_matches[$_history_substring_search_match_index]]
  471. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  472. elif [[ $_history_substring_search_match_index -eq 1 ]]; then
  473. #
  474. # We will move beyond the end of $_history_substring_search_matches:
  475. #
  476. # 1. Decrease the value of $_history_substring_search_match_index.
  477. #
  478. # 2. Save the current buffer in $_history_substring_search_old_buffer,
  479. # so that it can be retrieved by
  480. # _history-substring-search-down-search() later.
  481. #
  482. # 3. Make $BUFFER equal to $_history_substring_search_query.
  483. #
  484. # 4. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  485. # to highlight the current buffer.
  486. #
  487. (( _history_substring_search_match_index-- ))
  488. _history_substring_search_old_buffer=$BUFFER
  489. BUFFER=$_history_substring_search_query
  490. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  491. elif [[ $_history_substring_search_match_index -eq $_history_substring_search_matches_count_plus ]]; then
  492. #
  493. # We were beyond the beginning of $_history_substring_search_matches but
  494. # UP makes us move back to $_history_substring_search_matches:
  495. #
  496. # 1. Decrease the value of $_history_substring_search_match_index.
  497. #
  498. # 2. Restore $BUFFER from $_history_substring_search_old_buffer.
  499. #
  500. # 3. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  501. # to highlight the current buffer.
  502. #
  503. (( _history_substring_search_match_index-- ))
  504. BUFFER=$_history_substring_search_old_buffer
  505. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  506. fi
  507. }
  508. function _history-substring-search-down-search() {
  509. _history_substring_search_move_cursor_eol=true
  510. #
  511. # Highlight matches during history-substring-up-search:
  512. #
  513. # The following constants have been initialized in
  514. # _history-substring-search-up/down-search():
  515. #
  516. # $_history_substring_search_matches is the current list of matches
  517. # $_history_substring_search_matches_count is the current number of matches
  518. # $_history_substring_search_matches_count_plus is the current number of matches + 1
  519. # $_history_substring_search_matches_count_sans is the current number of matches - 1
  520. # $_history_substring_search_match_index is the index of the current match
  521. #
  522. # The range of values that $_history_substring_search_match_index can take
  523. # is: [0, $_history_substring_search_matches_count_plus]. A value of 0
  524. # indicates that we are beyond the end of
  525. # $_history_substring_search_matches. A value of
  526. # $_history_substring_search_matches_count_plus indicates that we are beyond
  527. # the beginning of $_history_substring_search_matches.
  528. #
  529. # In _history-substring-search-down-search() the initial value of
  530. # $_history_substring_search_match_index is
  531. # $_history_substring_search_matches_count. This value is set in
  532. # _history-substring-search-begin().
  533. # _history-substring-search-down-search() will initially increase it to
  534. # $_history_substring_search_matches_count_plus.
  535. #
  536. if [[ $_history_substring_search_match_index -le $_history_substring_search_matches_count_sans ]]; then
  537. #
  538. # Highlight the next match:
  539. #
  540. # 1. Increase $_history_substring_search_match_index by 1.
  541. #
  542. # 2. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  543. # to highlight the current buffer.
  544. #
  545. (( _history_substring_search_match_index++ ))
  546. BUFFER=$history[$_history_substring_search_matches[$_history_substring_search_match_index]]
  547. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  548. elif [[ $_history_substring_search_match_index -eq $_history_substring_search_matches_count ]]; then
  549. #
  550. # We will move beyond the beginning of $_history_substring_search_matches:
  551. #
  552. # 1. Increase $_history_substring_search_match_index by 1.
  553. #
  554. # 2. Save the current buffer in $_history_substring_search_old_buffer, so
  555. # that it can be retrieved by _history-substring-search-up-search()
  556. # later.
  557. #
  558. # 3. Make $BUFFER equal to $_history_substring_search_query.
  559. #
  560. # 4. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  561. # to highlight the current buffer.
  562. #
  563. (( _history_substring_search_match_index++ ))
  564. _history_substring_search_old_buffer=$BUFFER
  565. BUFFER=$_history_substring_search_query
  566. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  567. elif [[ $_history_substring_search_match_index -eq 0 ]]; then
  568. #
  569. # We were beyond the end of $_history_substring_search_matches but DOWN
  570. # makes us move back to the $_history_substring_search_matches:
  571. #
  572. # 1. Increase $_history_substring_search_match_index by 1.
  573. #
  574. # 2. Restore $BUFFER from $_history_substring_search_old_buffer.
  575. #
  576. # 3. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  577. # to highlight the current buffer.
  578. #
  579. (( _history_substring_search_match_index++ ))
  580. BUFFER=$_history_substring_search_old_buffer
  581. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  582. fi
  583. }
  584. # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
  585. # vim: ft=zsh sw=2 ts=2 et