history-substring-search.zsh 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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. zmodload zsh/terminfo
  156. if [[ -n "$terminfo[kcuu1]" ]]; then
  157. bindkey "$terminfo[kcuu1]" history-substring-search-up
  158. fi
  159. if [[ -n "$terminfo[kcud1]" ]]; then
  160. bindkey "$terminfo[kcud1]" history-substring-search-down
  161. fi
  162. #-----------------------------------------------------------------------------
  163. # implementation details
  164. #-----------------------------------------------------------------------------
  165. setopt extendedglob
  166. zmodload -F zsh/parameter
  167. #
  168. # We have to "override" some keys and widgets if the
  169. # zsh-syntax-highlighting plugin has not been loaded:
  170. #
  171. # https://github.com/nicoulaj/zsh-syntax-highlighting
  172. #
  173. if [[ $+functions[_zsh_highlight] -eq 0 ]]; then
  174. #
  175. # Dummy implementation of _zsh_highlight()
  176. # that simply removes existing highlights
  177. #
  178. function _zsh_highlight() {
  179. region_highlight=()
  180. }
  181. #
  182. # Remove existing highlights when the user
  183. # inserts printable characters into $BUFFER
  184. #
  185. function ordinary-key-press() {
  186. if [[ $KEYS == [[:print:]] ]]; then
  187. region_highlight=()
  188. fi
  189. zle .self-insert
  190. }
  191. zle -N self-insert ordinary-key-press
  192. #
  193. # Override ZLE widgets to invoke _zsh_highlight()
  194. #
  195. # https://github.com/nicoulaj/zsh-syntax-highlighting/blob/
  196. # bb7fcb79fad797a40077bebaf6f4e4a93c9d8163/zsh-syntax-highlighting.zsh#L121
  197. #
  198. #--------------8<-------------------8<-------------------8<-----------------
  199. #
  200. # Copyright (c) 2010-2011 zsh-syntax-highlighting contributors
  201. # All rights reserved.
  202. #
  203. # Redistribution and use in source and binary forms, with or without
  204. # modification, are permitted provided that the following conditions are
  205. # met:
  206. #
  207. # * Redistributions of source code must retain the above copyright
  208. # notice, this list of conditions and the following disclaimer.
  209. #
  210. # * Redistributions in binary form must reproduce the above copyright
  211. # notice, this list of conditions and the following disclaimer in the
  212. # documentation and/or other materials provided with the distribution.
  213. #
  214. # * Neither the name of the zsh-syntax-highlighting contributors nor the
  215. # names of its contributors may be used to endorse or promote products
  216. # derived from this software without specific prior written permission.
  217. #
  218. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  219. # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  220. # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  221. # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  222. # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  223. # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  224. # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  225. # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  226. # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  227. # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  228. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  229. # Load ZSH module zsh/zleparameter, needed to override user defined widgets.
  230. zmodload zsh/zleparameter 2>/dev/null || {
  231. echo 'zsh-syntax-highlighting: failed loading zsh/zleparameter, exiting.' >&2
  232. return -1
  233. }
  234. # Override ZLE widgets to make them invoke _zsh_highlight.
  235. for event in ${${(f)"$(zle -la)"}:#(_*|orig-*|.run-help|.which-command)}; do
  236. if [[ "$widgets[$event]" == completion:* ]]; then
  237. eval "zle -C orig-$event ${${${widgets[$event]}#*:}/:/ } ; $event() { builtin zle orig-$event && _zsh_highlight } ; zle -N $event"
  238. else
  239. case $event in
  240. accept-and-menu-complete)
  241. eval "$event() { builtin zle .$event && _zsh_highlight } ; zle -N $event"
  242. ;;
  243. # The following widgets should NOT remove any previously
  244. # applied highlighting. Therefore we do not remap them.
  245. .forward-char|.backward-char|.up-line-or-history|.down-line-or-history)
  246. ;;
  247. .*)
  248. clean_event=$event[2,${#event}] # Remove the leading dot in the event name
  249. case ${widgets[$clean_event]-} in
  250. (completion|user):*)
  251. ;;
  252. *)
  253. eval "$clean_event() { builtin zle $event && _zsh_highlight } ; zle -N $clean_event"
  254. ;;
  255. esac
  256. ;;
  257. *)
  258. ;;
  259. esac
  260. fi
  261. done
  262. unset event clean_event
  263. #-------------->8------------------->8------------------->8-----------------
  264. fi
  265. function _history-substring-search-begin() {
  266. _history_substring_search_move_cursor_eol=false
  267. _history_substring_search_query_highlight=
  268. #
  269. # Continue using the previous $_history_substring_search_result by default,
  270. # unless the current query was cleared or a new/different query was entered.
  271. #
  272. if [[ -z $BUFFER || $BUFFER != $_history_substring_search_result ]]; then
  273. #
  274. # For the purpose of highlighting we will also keep
  275. # a version without doubly-escaped meta characters.
  276. #
  277. _history_substring_search_query=$BUFFER
  278. #
  279. # $BUFFER contains the text that is in the command-line currently.
  280. # we put an extra "\\" before meta characters such as "\(" and "\)",
  281. # so that they become "\\\(" and "\\\)".
  282. #
  283. _history_substring_search_query_escaped=${BUFFER//(#m)[\][()|\\*?#<>~^]/\\$MATCH}
  284. #
  285. # Find all occurrences of the search query in the history file.
  286. #
  287. # (k) turns it an array of line numbers.
  288. #
  289. # (on) seems to remove duplicates, which are default
  290. # options. They can be turned off by (ON).
  291. #
  292. _history_substring_search_matches=(${(kon)history[(R)(#$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)*${_history_substring_search_query_escaped}*]})
  293. #
  294. # Define the range of values that $_history_substring_search_match_index
  295. # can take: [0, $_history_substring_search_matches_count_plus].
  296. #
  297. _history_substring_search_matches_count=$#_history_substring_search_matches
  298. _history_substring_search_matches_count_plus=$(( _history_substring_search_matches_count + 1 ))
  299. _history_substring_search_matches_count_sans=$(( _history_substring_search_matches_count - 1 ))
  300. #
  301. # If $_history_substring_search_match_index is equal to
  302. # $_history_substring_search_matches_count_plus, this indicates that we
  303. # are beyond the beginning of $_history_substring_search_matches.
  304. #
  305. # If $_history_substring_search_match_index is equal to 0, this indicates
  306. # that we are beyond the end of $_history_substring_search_matches.
  307. #
  308. # If we have initially pressed "up" we have to initialize
  309. # $_history_substring_search_match_index to
  310. # $_history_substring_search_matches_count_plus so that it will be
  311. # decreased to $_history_substring_search_matches_count.
  312. #
  313. # If we have initially pressed "down" we have to initialize
  314. # $_history_substring_search_match_index to
  315. # $_history_substring_search_matches_count so that it will be increased to
  316. # $_history_substring_search_matches_count_plus.
  317. #
  318. if [[ $WIDGET == history-substring-search-down ]]; then
  319. _history_substring_search_match_index=$_history_substring_search_matches_count
  320. else
  321. _history_substring_search_match_index=$_history_substring_search_matches_count_plus
  322. fi
  323. fi
  324. }
  325. function _history-substring-search-end() {
  326. _history_substring_search_result=$BUFFER
  327. # move the cursor to the end of the command line
  328. if [[ $_history_substring_search_move_cursor_eol == true ]]; then
  329. CURSOR=${#BUFFER}
  330. fi
  331. # highlight command line using zsh-syntax-highlighting
  332. _zsh_highlight
  333. # highlight the search query inside the command line
  334. if [[ -n $_history_substring_search_query_highlight && -n $_history_substring_search_query ]]; then
  335. #
  336. # The following expression yields a variable $MBEGIN, which
  337. # indicates the begin position + 1 of the first occurrence
  338. # of _history_substring_search_query_escaped in $BUFFER.
  339. #
  340. : ${(S)BUFFER##(#m$HISTORY_SUBSTRING_SEARCH_GLOBBING_FLAGS)($_history_substring_search_query##)}
  341. local begin=$(( MBEGIN - 1 ))
  342. local end=$(( begin + $#_history_substring_search_query ))
  343. region_highlight+=("$begin $end $_history_substring_search_query_highlight")
  344. fi
  345. # For debugging purposes:
  346. # zle -R "mn: "$_history_substring_search_match_index" m#: "${#_history_substring_search_matches}
  347. # read -k -t 200 && zle -U $REPLY
  348. # Exit successfully from the history-substring-search-* widgets.
  349. true
  350. }
  351. function _history-substring-search-up-buffer() {
  352. #
  353. # Check if the UP arrow was pressed to move the cursor within a multi-line
  354. # buffer. This amounts to three tests:
  355. #
  356. # 1. $#buflines -gt 1.
  357. #
  358. # 2. $CURSOR -ne $#BUFFER.
  359. #
  360. # 3. Check if we are on the first line of the current multi-line buffer.
  361. # If so, pressing UP would amount to leaving the multi-line buffer.
  362. #
  363. # We check this by adding an extra "x" to $LBUFFER, which makes
  364. # sure that xlbuflines is always equal to the number of lines
  365. # until $CURSOR (including the line with the cursor on it).
  366. #
  367. local buflines XLBUFFER xlbuflines
  368. buflines=(${(f)BUFFER})
  369. XLBUFFER=$LBUFFER"x"
  370. xlbuflines=(${(f)XLBUFFER})
  371. if [[ $#buflines -gt 1 && $CURSOR -ne $#BUFFER && $#xlbuflines -ne 1 ]]; then
  372. zle up-line-or-history
  373. return true
  374. fi
  375. false
  376. }
  377. function _history-substring-search-down-buffer() {
  378. #
  379. # Check if the DOWN arrow was pressed to move the cursor within a multi-line
  380. # buffer. This amounts to three tests:
  381. #
  382. # 1. $#buflines -gt 1.
  383. #
  384. # 2. $CURSOR -ne $#BUFFER.
  385. #
  386. # 3. Check if we are on the last line of the current multi-line buffer.
  387. # If so, pressing DOWN would amount to leaving the multi-line buffer.
  388. #
  389. # We check this by adding an extra "x" to $RBUFFER, which makes
  390. # sure that xrbuflines is always equal to the number of lines
  391. # from $CURSOR (including the line with the cursor on it).
  392. #
  393. local buflines XRBUFFER xrbuflines
  394. buflines=(${(f)BUFFER})
  395. XRBUFFER="x"$RBUFFER
  396. xrbuflines=(${(f)XRBUFFER})
  397. if [[ $#buflines -gt 1 && $CURSOR -ne $#BUFFER && $#xrbuflines -ne 1 ]]; then
  398. zle down-line-or-history
  399. return true
  400. fi
  401. false
  402. }
  403. function _history-substring-search-up-history() {
  404. #
  405. # Behave like up in ZSH, except clear the $BUFFER
  406. # when beginning of history is reached like in Fish.
  407. #
  408. if [[ -z $_history_substring_search_query ]]; then
  409. # we have reached the absolute top of history
  410. if [[ $HISTNO -eq 1 ]]; then
  411. BUFFER=
  412. # going up from somewhere below the top of history
  413. else
  414. zle up-history
  415. fi
  416. return true
  417. fi
  418. false
  419. }
  420. function _history-substring-search-down-history() {
  421. #
  422. # Behave like down-history in ZSH, except clear the
  423. # $BUFFER when end of history is reached like in Fish.
  424. #
  425. if [[ -z $_history_substring_search_query ]]; then
  426. # going down from the absolute top of history
  427. if [[ $HISTNO -eq 1 && -z $BUFFER ]]; then
  428. BUFFER=${history[1]}
  429. _history_substring_search_move_cursor_eol=true
  430. # going down from somewhere above the bottom of history
  431. else
  432. zle down-history
  433. fi
  434. return true
  435. fi
  436. false
  437. }
  438. function _history-substring-search-up-search() {
  439. _history_substring_search_move_cursor_eol=true
  440. #
  441. # Highlight matches during history-substring-up-search:
  442. #
  443. # The following constants have been initialized in
  444. # _history-substring-search-up/down-search():
  445. #
  446. # $_history_substring_search_matches is the current list of matches
  447. # $_history_substring_search_matches_count is the current number of matches
  448. # $_history_substring_search_matches_count_plus is the current number of matches + 1
  449. # $_history_substring_search_matches_count_sans is the current number of matches - 1
  450. # $_history_substring_search_match_index is the index of the current match
  451. #
  452. # The range of values that $_history_substring_search_match_index can take
  453. # is: [0, $_history_substring_search_matches_count_plus]. A value of 0
  454. # indicates that we are beyond the end of
  455. # $_history_substring_search_matches. A value of
  456. # $_history_substring_search_matches_count_plus indicates that we are beyond
  457. # the beginning of $_history_substring_search_matches.
  458. #
  459. # In _history-substring-search-up-search() the initial value of
  460. # $_history_substring_search_match_index is
  461. # $_history_substring_search_matches_count_plus. This value is set in
  462. # _history-substring-search-begin(). _history-substring-search-up-search()
  463. # will initially decrease it to $_history_substring_search_matches_count.
  464. #
  465. if [[ $_history_substring_search_match_index -ge 2 ]]; then
  466. #
  467. # Highlight the next match:
  468. #
  469. # 1. Decrease the value of $_history_substring_search_match_index.
  470. #
  471. # 2. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  472. # to highlight the current buffer.
  473. #
  474. (( _history_substring_search_match_index-- ))
  475. BUFFER=$history[$_history_substring_search_matches[$_history_substring_search_match_index]]
  476. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  477. elif [[ $_history_substring_search_match_index -eq 1 ]]; then
  478. #
  479. # We will move beyond the end of $_history_substring_search_matches:
  480. #
  481. # 1. Decrease the value of $_history_substring_search_match_index.
  482. #
  483. # 2. Save the current buffer in $_history_substring_search_old_buffer,
  484. # so that it can be retrieved by
  485. # _history-substring-search-down-search() later.
  486. #
  487. # 3. Make $BUFFER equal to $_history_substring_search_query.
  488. #
  489. # 4. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  490. # to highlight the current buffer.
  491. #
  492. (( _history_substring_search_match_index-- ))
  493. _history_substring_search_old_buffer=$BUFFER
  494. BUFFER=$_history_substring_search_query
  495. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  496. elif [[ $_history_substring_search_match_index -eq $_history_substring_search_matches_count_plus ]]; then
  497. #
  498. # We were beyond the beginning of $_history_substring_search_matches but
  499. # UP makes us move back to $_history_substring_search_matches:
  500. #
  501. # 1. Decrease the value of $_history_substring_search_match_index.
  502. #
  503. # 2. Restore $BUFFER from $_history_substring_search_old_buffer.
  504. #
  505. # 3. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  506. # to highlight the current buffer.
  507. #
  508. (( _history_substring_search_match_index-- ))
  509. BUFFER=$_history_substring_search_old_buffer
  510. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  511. fi
  512. }
  513. function _history-substring-search-down-search() {
  514. _history_substring_search_move_cursor_eol=true
  515. #
  516. # Highlight matches during history-substring-up-search:
  517. #
  518. # The following constants have been initialized in
  519. # _history-substring-search-up/down-search():
  520. #
  521. # $_history_substring_search_matches is the current list of matches
  522. # $_history_substring_search_matches_count is the current number of matches
  523. # $_history_substring_search_matches_count_plus is the current number of matches + 1
  524. # $_history_substring_search_matches_count_sans is the current number of matches - 1
  525. # $_history_substring_search_match_index is the index of the current match
  526. #
  527. # The range of values that $_history_substring_search_match_index can take
  528. # is: [0, $_history_substring_search_matches_count_plus]. A value of 0
  529. # indicates that we are beyond the end of
  530. # $_history_substring_search_matches. A value of
  531. # $_history_substring_search_matches_count_plus indicates that we are beyond
  532. # the beginning of $_history_substring_search_matches.
  533. #
  534. # In _history-substring-search-down-search() the initial value of
  535. # $_history_substring_search_match_index is
  536. # $_history_substring_search_matches_count. This value is set in
  537. # _history-substring-search-begin().
  538. # _history-substring-search-down-search() will initially increase it to
  539. # $_history_substring_search_matches_count_plus.
  540. #
  541. if [[ $_history_substring_search_match_index -le $_history_substring_search_matches_count_sans ]]; then
  542. #
  543. # Highlight the next match:
  544. #
  545. # 1. Increase $_history_substring_search_match_index by 1.
  546. #
  547. # 2. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  548. # to highlight the current buffer.
  549. #
  550. (( _history_substring_search_match_index++ ))
  551. BUFFER=$history[$_history_substring_search_matches[$_history_substring_search_match_index]]
  552. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  553. elif [[ $_history_substring_search_match_index -eq $_history_substring_search_matches_count ]]; then
  554. #
  555. # We will move beyond the beginning of $_history_substring_search_matches:
  556. #
  557. # 1. Increase $_history_substring_search_match_index by 1.
  558. #
  559. # 2. Save the current buffer in $_history_substring_search_old_buffer, so
  560. # that it can be retrieved by _history-substring-search-up-search()
  561. # later.
  562. #
  563. # 3. Make $BUFFER equal to $_history_substring_search_query.
  564. #
  565. # 4. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  566. # to highlight the current buffer.
  567. #
  568. (( _history_substring_search_match_index++ ))
  569. _history_substring_search_old_buffer=$BUFFER
  570. BUFFER=$_history_substring_search_query
  571. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND
  572. elif [[ $_history_substring_search_match_index -eq 0 ]]; then
  573. #
  574. # We were beyond the end of $_history_substring_search_matches but DOWN
  575. # makes us move back to the $_history_substring_search_matches:
  576. #
  577. # 1. Increase $_history_substring_search_match_index by 1.
  578. #
  579. # 2. Restore $BUFFER from $_history_substring_search_old_buffer.
  580. #
  581. # 3. Use $HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  582. # to highlight the current buffer.
  583. #
  584. (( _history_substring_search_match_index++ ))
  585. BUFFER=$_history_substring_search_old_buffer
  586. _history_substring_search_query_highlight=$HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND
  587. fi
  588. }
  589. # -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
  590. # vim: ft=zsh sw=2 ts=2 et