history-substring-search.zsh 25 KB

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