1
0

zsh_autosuggestions.zsh 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867
  1. # Fish-like fast/unobtrusive autosuggestions for zsh.
  2. # https://github.com/zsh-users/zsh-autosuggestions
  3. # v0.7.1
  4. # Copyright (c) 2013 Thiago de Arruda
  5. # Copyright (c) 2016-2021 Eric Freese
  6. #
  7. # Permission is hereby granted, free of charge, to any person
  8. # obtaining a copy of this software and associated documentation
  9. # files (the "Software"), to deal in the Software without
  10. # restriction, including without limitation the rights to use,
  11. # copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. # copies of the Software, and to permit persons to whom the
  13. # Software is furnished to do so, subject to the following
  14. # conditions:
  15. #
  16. # The above copyright notice and this permission notice shall be
  17. # included in all copies or substantial portions of the Software.
  18. #
  19. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  21. # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  23. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  24. # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  25. # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  26. # OTHER DEALINGS IN THE SOFTWARE.
  27. #--------------------------------------------------------------------#
  28. # Global Configuration Variables #
  29. #--------------------------------------------------------------------#
  30. # Color to use when highlighting suggestion
  31. # Uses format of `region_highlight`
  32. # More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
  33. (( ! ${+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE} )) &&
  34. typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
  35. # Prefix to use when saving original versions of bound widgets
  36. (( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) &&
  37. typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
  38. # Strategies to use to fetch a suggestion
  39. # Will try each strategy in order until a suggestion is returned
  40. (( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
  41. typeset -ga ZSH_AUTOSUGGEST_STRATEGY
  42. ZSH_AUTOSUGGEST_STRATEGY=(history)
  43. }
  44. # Widgets that clear the suggestion
  45. (( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && {
  46. typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS
  47. ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
  48. history-search-forward
  49. history-search-backward
  50. history-beginning-search-forward
  51. history-beginning-search-backward
  52. history-beginning-search-forward-end
  53. history-beginning-search-backward-end
  54. history-substring-search-up
  55. history-substring-search-down
  56. up-line-or-beginning-search
  57. down-line-or-beginning-search
  58. up-line-or-history
  59. down-line-or-history
  60. accept-line
  61. copy-earlier-word
  62. )
  63. }
  64. # Widgets that accept the entire suggestion
  65. (( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && {
  66. typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS
  67. ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
  68. forward-char
  69. end-of-line
  70. vi-forward-char
  71. vi-end-of-line
  72. vi-add-eol
  73. )
  74. }
  75. # Widgets that accept the entire suggestion and execute it
  76. (( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && {
  77. typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS
  78. ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
  79. )
  80. }
  81. # Widgets that accept the suggestion as far as the cursor moves
  82. (( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && {
  83. typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS
  84. ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
  85. forward-word
  86. emacs-forward-word
  87. vi-forward-word
  88. vi-forward-word-end
  89. vi-forward-blank-word
  90. vi-forward-blank-word-end
  91. vi-find-next-char
  92. vi-find-next-char-skip
  93. )
  94. }
  95. # Widgets that should be ignored (globbing supported but must be escaped)
  96. (( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && {
  97. typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS
  98. ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
  99. orig-\*
  100. beep
  101. run-help
  102. set-local-history
  103. which-command
  104. yank
  105. yank-pop
  106. zle-\*
  107. )
  108. }
  109. # Pty name for capturing completions for completion suggestion strategy
  110. (( ! ${+ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME} )) &&
  111. typeset -g ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty
  112. #--------------------------------------------------------------------#
  113. # Utility Functions #
  114. #--------------------------------------------------------------------#
  115. _zsh_autosuggest_escape_command() {
  116. setopt localoptions EXTENDED_GLOB
  117. # Escape special chars in the string (requires EXTENDED_GLOB)
  118. echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}"
  119. }
  120. #--------------------------------------------------------------------#
  121. # Widget Helpers #
  122. #--------------------------------------------------------------------#
  123. _zsh_autosuggest_incr_bind_count() {
  124. typeset -gi bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]+1))
  125. _ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=$bind_count
  126. }
  127. # Bind a single widget to an autosuggest widget, saving a reference to the original widget
  128. _zsh_autosuggest_bind_widget() {
  129. typeset -gA _ZSH_AUTOSUGGEST_BIND_COUNTS
  130. local widget=$1
  131. local autosuggest_action=$2
  132. local prefix=$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX
  133. local -i bind_count
  134. # Save a reference to the original widget
  135. case $widgets[$widget] in
  136. # Already bound
  137. user:_zsh_autosuggest_(bound|orig)_*)
  138. bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$widget]))
  139. ;;
  140. # User-defined widget
  141. user:*)
  142. _zsh_autosuggest_incr_bind_count $widget
  143. zle -N $prefix$bind_count-$widget ${widgets[$widget]#*:}
  144. ;;
  145. # Built-in widget
  146. builtin)
  147. _zsh_autosuggest_incr_bind_count $widget
  148. eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }"
  149. zle -N $prefix$bind_count-$widget _zsh_autosuggest_orig_$widget
  150. ;;
  151. # Completion widget
  152. completion:*)
  153. _zsh_autosuggest_incr_bind_count $widget
  154. eval "zle -C $prefix$bind_count-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
  155. ;;
  156. esac
  157. # Pass the original widget's name explicitly into the autosuggest
  158. # function. Use this passed in widget name to call the original
  159. # widget instead of relying on the $WIDGET variable being set
  160. # correctly. $WIDGET cannot be trusted because other plugins call
  161. # zle without the `-w` flag (e.g. `zle self-insert` instead of
  162. # `zle self-insert -w`).
  163. eval "_zsh_autosuggest_bound_${bind_count}_${(q)widget}() {
  164. _zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@
  165. }"
  166. # Create the bound widget
  167. zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget
  168. }
  169. # Map all configured widgets to the right autosuggest widgets
  170. _zsh_autosuggest_bind_widgets() {
  171. emulate -L zsh
  172. local widget
  173. local ignore_widgets
  174. ignore_widgets=(
  175. .\*
  176. _\*
  177. ${_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS/#/autosuggest-}
  178. $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\*
  179. $ZSH_AUTOSUGGEST_IGNORE_WIDGETS
  180. )
  181. # Find every widget we might want to bind and bind it appropriately
  182. for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do
  183. if [[ -n ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]]; then
  184. _zsh_autosuggest_bind_widget $widget clear
  185. elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then
  186. _zsh_autosuggest_bind_widget $widget accept
  187. elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then
  188. _zsh_autosuggest_bind_widget $widget execute
  189. elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then
  190. _zsh_autosuggest_bind_widget $widget partial_accept
  191. else
  192. # Assume any unspecified widget might modify the buffer
  193. _zsh_autosuggest_bind_widget $widget modify
  194. fi
  195. done
  196. }
  197. # Given the name of an original widget and args, invoke it, if it exists
  198. _zsh_autosuggest_invoke_original_widget() {
  199. # Do nothing unless called with at least one arg
  200. (( $# )) || return 0
  201. local original_widget_name="$1"
  202. shift
  203. if (( ${+widgets[$original_widget_name]} )); then
  204. zle $original_widget_name -- $@
  205. fi
  206. }
  207. #--------------------------------------------------------------------#
  208. # Highlighting #
  209. #--------------------------------------------------------------------#
  210. # If there was a highlight, remove it
  211. _zsh_autosuggest_highlight_reset() {
  212. typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
  213. if [[ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]]; then
  214. region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}")
  215. unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
  216. fi
  217. }
  218. # If there's a suggestion, highlight it
  219. _zsh_autosuggest_highlight_apply() {
  220. typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
  221. if (( $#POSTDISPLAY )); then
  222. typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE"
  223. region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT")
  224. else
  225. unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
  226. fi
  227. }
  228. #--------------------------------------------------------------------#
  229. # Autosuggest Widget Implementations #
  230. #--------------------------------------------------------------------#
  231. # Disable suggestions
  232. _zsh_autosuggest_disable() {
  233. typeset -g _ZSH_AUTOSUGGEST_DISABLED
  234. _zsh_autosuggest_clear
  235. }
  236. # Enable suggestions
  237. _zsh_autosuggest_enable() {
  238. unset _ZSH_AUTOSUGGEST_DISABLED
  239. if (( $#BUFFER )); then
  240. _zsh_autosuggest_fetch
  241. fi
  242. }
  243. # Toggle suggestions (enable/disable)
  244. _zsh_autosuggest_toggle() {
  245. if (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); then
  246. _zsh_autosuggest_enable
  247. else
  248. _zsh_autosuggest_disable
  249. fi
  250. }
  251. # Clear the suggestion
  252. _zsh_autosuggest_clear() {
  253. # Remove the suggestion
  254. POSTDISPLAY=
  255. _zsh_autosuggest_invoke_original_widget $@
  256. }
  257. # Modify the buffer and get a new suggestion
  258. _zsh_autosuggest_modify() {
  259. local -i retval
  260. # Only available in zsh >= 5.4
  261. local -i KEYS_QUEUED_COUNT
  262. # Save the contents of the buffer/postdisplay
  263. local orig_buffer="$BUFFER"
  264. local orig_postdisplay="$POSTDISPLAY"
  265. # Clear suggestion while waiting for next one
  266. POSTDISPLAY=
  267. # Original widget may modify the buffer
  268. _zsh_autosuggest_invoke_original_widget $@
  269. retval=$?
  270. emulate -L zsh
  271. # Don't fetch a new suggestion if there's more input to be read immediately
  272. if (( $PENDING > 0 || $KEYS_QUEUED_COUNT > 0 )); then
  273. POSTDISPLAY="$orig_postdisplay"
  274. return $retval
  275. fi
  276. # Optimize if manually typing in the suggestion or if buffer hasn't changed
  277. if [[ "$BUFFER" = "$orig_buffer"* && "$orig_postdisplay" = "${BUFFER:$#orig_buffer}"* ]]; then
  278. POSTDISPLAY="${orig_postdisplay:$(($#BUFFER - $#orig_buffer))}"
  279. return $retval
  280. fi
  281. # Bail out if suggestions are disabled
  282. if (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); then
  283. return $?
  284. fi
  285. # Get a new suggestion if the buffer is not empty after modification
  286. if (( $#BUFFER > 0 )); then
  287. if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then
  288. _zsh_autosuggest_fetch
  289. fi
  290. fi
  291. return $retval
  292. }
  293. # Fetch a new suggestion based on what's currently in the buffer
  294. _zsh_autosuggest_fetch() {
  295. if (( ${+ZSH_AUTOSUGGEST_USE_ASYNC} )); then
  296. _zsh_autosuggest_async_request "$BUFFER"
  297. else
  298. local suggestion
  299. _zsh_autosuggest_fetch_suggestion "$BUFFER"
  300. _zsh_autosuggest_suggest "$suggestion"
  301. fi
  302. }
  303. # Offer a suggestion
  304. _zsh_autosuggest_suggest() {
  305. emulate -L zsh
  306. local suggestion="$1"
  307. if [[ -n "$suggestion" ]] && (( $#BUFFER )); then
  308. POSTDISPLAY="${suggestion#$BUFFER}"
  309. else
  310. POSTDISPLAY=
  311. fi
  312. }
  313. # Accept the entire suggestion
  314. _zsh_autosuggest_accept() {
  315. local -i retval max_cursor_pos=$#BUFFER
  316. # When vicmd keymap is active, the cursor can't move all the way
  317. # to the end of the buffer
  318. if [[ "$KEYMAP" = "vicmd" ]]; then
  319. max_cursor_pos=$((max_cursor_pos - 1))
  320. fi
  321. # If we're not in a valid state to accept a suggestion, just run the
  322. # original widget and bail out
  323. if (( $CURSOR != $max_cursor_pos || !$#POSTDISPLAY )); then
  324. _zsh_autosuggest_invoke_original_widget $@
  325. return
  326. fi
  327. # Only accept if the cursor is at the end of the buffer
  328. # Add the suggestion to the buffer
  329. BUFFER="$BUFFER$POSTDISPLAY"
  330. # Remove the suggestion
  331. POSTDISPLAY=
  332. # Run the original widget before manually moving the cursor so that the
  333. # cursor movement doesn't make the widget do something unexpected
  334. _zsh_autosuggest_invoke_original_widget $@
  335. retval=$?
  336. # Move the cursor to the end of the buffer
  337. if [[ "$KEYMAP" = "vicmd" ]]; then
  338. CURSOR=$(($#BUFFER - 1))
  339. else
  340. CURSOR=$#BUFFER
  341. fi
  342. return $retval
  343. }
  344. # Accept the entire suggestion and execute it
  345. _zsh_autosuggest_execute() {
  346. # Add the suggestion to the buffer
  347. BUFFER="$BUFFER$POSTDISPLAY"
  348. # Remove the suggestion
  349. POSTDISPLAY=
  350. # Call the original `accept-line` to handle syntax highlighting or
  351. # other potential custom behavior
  352. _zsh_autosuggest_invoke_original_widget "accept-line"
  353. }
  354. # Partially accept the suggestion
  355. _zsh_autosuggest_partial_accept() {
  356. local -i retval cursor_loc
  357. # Save the contents of the buffer so we can restore later if needed
  358. local original_buffer="$BUFFER"
  359. # Temporarily accept the suggestion.
  360. BUFFER="$BUFFER$POSTDISPLAY"
  361. # Original widget moves the cursor
  362. _zsh_autosuggest_invoke_original_widget $@
  363. retval=$?
  364. # Normalize cursor location across vi/emacs modes
  365. cursor_loc=$CURSOR
  366. if [[ "$KEYMAP" = "vicmd" ]]; then
  367. cursor_loc=$((cursor_loc + 1))
  368. fi
  369. # If we've moved past the end of the original buffer
  370. if (( $cursor_loc > $#original_buffer )); then
  371. # Set POSTDISPLAY to text right of the cursor
  372. POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}"
  373. # Clip the buffer at the cursor
  374. BUFFER="${BUFFER[1,$cursor_loc]}"
  375. else
  376. # Restore the original buffer
  377. BUFFER="$original_buffer"
  378. fi
  379. return $retval
  380. }
  381. () {
  382. typeset -ga _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS
  383. _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS=(
  384. clear
  385. fetch
  386. suggest
  387. accept
  388. execute
  389. enable
  390. disable
  391. toggle
  392. )
  393. local action
  394. for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS modify partial_accept; do
  395. eval "_zsh_autosuggest_widget_$action() {
  396. local -i retval
  397. _zsh_autosuggest_highlight_reset
  398. _zsh_autosuggest_$action \$@
  399. retval=\$?
  400. _zsh_autosuggest_highlight_apply
  401. zle -R
  402. return \$retval
  403. }"
  404. done
  405. for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS; do
  406. zle -N autosuggest-$action _zsh_autosuggest_widget_$action
  407. done
  408. }
  409. #--------------------------------------------------------------------#
  410. # Completion Suggestion Strategy #
  411. #--------------------------------------------------------------------#
  412. # Fetches a suggestion from the completion engine
  413. #
  414. _zsh_autosuggest_capture_postcompletion() {
  415. # Always insert the first completion into the buffer
  416. compstate[insert]=1
  417. # Don't list completions
  418. unset 'compstate[list]'
  419. }
  420. _zsh_autosuggest_capture_completion_widget() {
  421. # Add a post-completion hook to be called after all completions have been
  422. # gathered. The hook can modify compstate to affect what is done with the
  423. # gathered completions.
  424. local -a +h comppostfuncs
  425. comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
  426. # Only capture completions at the end of the buffer
  427. CURSOR=$#BUFFER
  428. # Run the original widget wrapping `.complete-word` so we don't
  429. # recursively try to fetch suggestions, since our pty is forked
  430. # after autosuggestions is initialized.
  431. zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]}
  432. if is-at-least 5.0.3; then
  433. # Don't do any cr/lf transformations. We need to do this immediately before
  434. # output because if we do it in setup, onlcr will be re-enabled when we enter
  435. # vared in the async code path. There is a bug in zpty module in older versions
  436. # where the tty is not properly attached to the pty slave, resulting in stty
  437. # getting stopped with a SIGTTOU. See zsh-workers thread 31660 and upstream
  438. # commit f75904a38
  439. stty -onlcr -ocrnl -F /dev/tty
  440. fi
  441. # The completion has been added, print the buffer as the suggestion
  442. echo -nE - $'\0'$BUFFER$'\0'
  443. }
  444. zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
  445. _zsh_autosuggest_capture_setup() {
  446. # There is a bug in zpty module in older zsh versions by which a
  447. # zpty that exits will kill all zpty processes that were forked
  448. # before it. Here we set up a zsh exit hook to SIGKILL the zpty
  449. # process immediately, before it has a chance to kill any other
  450. # zpty processes.
  451. if ! is-at-least 5.4; then
  452. zshexit() {
  453. # The zsh builtin `kill` fails sometimes in older versions
  454. # https://unix.stackexchange.com/a/477647/156673
  455. kill -KILL $$ 2>&- || command kill -KILL $$
  456. # Block for long enough for the signal to come through
  457. sleep 1
  458. }
  459. fi
  460. # Try to avoid any suggestions that wouldn't match the prefix
  461. zstyle ':completion:*' matcher-list ''
  462. zstyle ':completion:*' path-completion false
  463. zstyle ':completion:*' max-errors 0 not-numeric
  464. bindkey '^I' autosuggest-capture-completion
  465. }
  466. _zsh_autosuggest_capture_completion_sync() {
  467. _zsh_autosuggest_capture_setup
  468. zle autosuggest-capture-completion
  469. }
  470. _zsh_autosuggest_capture_completion_async() {
  471. _zsh_autosuggest_capture_setup
  472. zmodload zsh/parameter 2>/dev/null || return # For `$functions`
  473. # Make vared completion work as if for a normal command line
  474. # https://stackoverflow.com/a/7057118/154703
  475. autoload +X _complete
  476. functions[_original_complete]=$functions[_complete]
  477. function _complete() {
  478. unset 'compstate[vared]'
  479. _original_complete "$@"
  480. }
  481. # Open zle with buffer set so we can capture completions for it
  482. vared 1
  483. }
  484. _zsh_autosuggest_strategy_completion() {
  485. # Reset options to defaults and enable LOCAL_OPTIONS
  486. emulate -L zsh
  487. # Enable extended glob for completion ignore pattern
  488. setopt EXTENDED_GLOB
  489. typeset -g suggestion
  490. local line REPLY
  491. # Exit if we don't have completions
  492. whence compdef >/dev/null || return
  493. # Exit if we don't have zpty
  494. zmodload zsh/zpty 2>/dev/null || return
  495. # Exit if our search string matches the ignore pattern
  496. [[ -n "$ZSH_AUTOSUGGEST_COMPLETION_IGNORE" ]] && [[ "$1" == $~ZSH_AUTOSUGGEST_COMPLETION_IGNORE ]] && return
  497. # Zle will be inactive if we are in async mode
  498. if zle; then
  499. zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync
  500. else
  501. zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$1"
  502. zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t'
  503. fi
  504. {
  505. # The completion result is surrounded by null bytes, so read the
  506. # content between the first two null bytes.
  507. zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0'
  508. # Extract the suggestion from between the null bytes. On older
  509. # versions of zsh (older than 5.3), we sometimes get extra bytes after
  510. # the second null byte, so trim those off the end.
  511. # See http://www.zsh.org/mla/workers/2015/msg03290.html
  512. suggestion="${${(@0)line}[2]}"
  513. } always {
  514. # Destroy the pty
  515. zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
  516. }
  517. }
  518. #--------------------------------------------------------------------#
  519. # History Suggestion Strategy #
  520. #--------------------------------------------------------------------#
  521. # Suggests the most recent history item that matches the given
  522. # prefix.
  523. #
  524. _zsh_autosuggest_strategy_history() {
  525. # Reset options to defaults and enable LOCAL_OPTIONS
  526. emulate -L zsh
  527. # Enable globbing flags so that we can use (#m) and (x~y) glob operator
  528. setopt EXTENDED_GLOB
  529. # Escape backslashes and all of the glob operators so we can use
  530. # this string as a pattern to search the $history associative array.
  531. # - (#m) globbing flag enables setting references for match data
  532. # TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
  533. local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
  534. # Get the history items that match the prefix, excluding those that match
  535. # the ignore pattern
  536. local pattern="$prefix*"
  537. if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then
  538. pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)"
  539. fi
  540. # Give the first history item matching the pattern as the suggestion
  541. # - (r) subscript flag makes the pattern match on values
  542. typeset -g suggestion="${history[(r)$pattern]}"
  543. }
  544. #--------------------------------------------------------------------#
  545. # Match Previous Command Suggestion Strategy #
  546. #--------------------------------------------------------------------#
  547. # Suggests the most recent history item that matches the given
  548. # prefix and whose preceding history item also matches the most
  549. # recently executed command.
  550. #
  551. # For example, suppose your history has the following entries:
  552. # - pwd
  553. # - ls foo
  554. # - ls bar
  555. # - pwd
  556. #
  557. # Given the history list above, when you type 'ls', the suggestion
  558. # will be 'ls foo' rather than 'ls bar' because your most recently
  559. # executed command (pwd) was previously followed by 'ls foo'.
  560. #
  561. # Note that this strategy won't work as expected with ZSH options that don't
  562. # preserve the history order such as `HIST_IGNORE_ALL_DUPS` or
  563. # `HIST_EXPIRE_DUPS_FIRST`.
  564. _zsh_autosuggest_strategy_match_prev_cmd() {
  565. # Reset options to defaults and enable LOCAL_OPTIONS
  566. emulate -L zsh
  567. # Enable globbing flags so that we can use (#m) and (x~y) glob operator
  568. setopt EXTENDED_GLOB
  569. # TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
  570. local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
  571. # Get the history items that match the prefix, excluding those that match
  572. # the ignore pattern
  573. local pattern="$prefix*"
  574. if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then
  575. pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)"
  576. fi
  577. # Get all history event numbers that correspond to history
  578. # entries that match the pattern
  579. local history_match_keys
  580. history_match_keys=(${(k)history[(R)$~pattern]})
  581. # By default we use the first history number (most recent history entry)
  582. local histkey="${history_match_keys[1]}"
  583. # Get the previously executed command
  584. local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")"
  585. # Iterate up to the first 200 history event numbers that match $prefix
  586. for key in "${(@)history_match_keys[1,200]}"; do
  587. # Stop if we ran out of history
  588. [[ $key -gt 1 ]] || break
  589. # See if the history entry preceding the suggestion matches the
  590. # previous command, and use it if it does
  591. if [[ "${history[$((key - 1))]}" == "$prev_cmd" ]]; then
  592. histkey="$key"
  593. break
  594. fi
  595. done
  596. # Give back the matched history entry
  597. typeset -g suggestion="$history[$histkey]"
  598. }
  599. #--------------------------------------------------------------------#
  600. # Fetch Suggestion #
  601. #--------------------------------------------------------------------#
  602. # Loops through all specified strategies and returns a suggestion
  603. # from the first strategy to provide one.
  604. #
  605. _zsh_autosuggest_fetch_suggestion() {
  606. typeset -g suggestion
  607. local -a strategies
  608. local strategy
  609. # Ensure we are working with an array
  610. strategies=(${=ZSH_AUTOSUGGEST_STRATEGY})
  611. for strategy in $strategies; do
  612. # Try to get a suggestion from this strategy
  613. _zsh_autosuggest_strategy_$strategy "$1"
  614. # Ensure the suggestion matches the prefix
  615. [[ "$suggestion" != "$1"* ]] && unset suggestion
  616. # Break once we've found a valid suggestion
  617. [[ -n "$suggestion" ]] && break
  618. done
  619. }
  620. #--------------------------------------------------------------------#
  621. # Async #
  622. #--------------------------------------------------------------------#
  623. _zsh_autosuggest_async_request() {
  624. zmodload zsh/system 2>/dev/null # For `$sysparams`
  625. typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
  626. # If we've got a pending request, cancel it
  627. if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
  628. # Close the file descriptor and remove the handler
  629. builtin exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
  630. zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
  631. # We won't know the pid unless the user has zsh/system module installed
  632. if [[ -n "$_ZSH_AUTOSUGGEST_CHILD_PID" ]]; then
  633. # Zsh will make a new process group for the child process only if job
  634. # control is enabled (MONITOR option)
  635. if [[ -o MONITOR ]]; then
  636. # Send the signal to the process group to kill any processes that may
  637. # have been forked by the suggestion strategy
  638. kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
  639. else
  640. # Kill just the child process since it wasn't placed in a new process
  641. # group. If the suggestion strategy forked any child processes they may
  642. # be orphaned and left behind.
  643. kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
  644. fi
  645. fi
  646. fi
  647. # Fork a process to fetch a suggestion and open a pipe to read from it
  648. builtin exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
  649. # Tell parent process our pid
  650. echo $sysparams[pid]
  651. # Fetch and print the suggestion
  652. local suggestion
  653. _zsh_autosuggest_fetch_suggestion "$1"
  654. echo -nE "$suggestion"
  655. )
  656. # There's a weird bug here where ^C stops working unless we force a fork
  657. # See https://github.com/zsh-users/zsh-autosuggestions/issues/364
  658. autoload -Uz is-at-least
  659. is-at-least 5.8 || command true
  660. # Read the pid from the child process
  661. read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD
  662. # When the fd is readable, call the response handler
  663. zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
  664. }
  665. # Called when new data is ready to be read from the pipe
  666. # First arg will be fd ready for reading
  667. # Second arg will be passed in case of error
  668. _zsh_autosuggest_async_response() {
  669. emulate -L zsh
  670. local suggestion
  671. if [[ -z "$2" || "$2" == "hup" ]]; then
  672. # Read everything from the fd and give it as a suggestion
  673. IFS='' read -rd '' -u $1 suggestion
  674. zle autosuggest-suggest -- "$suggestion"
  675. # Close the fd
  676. builtin exec {1}<&-
  677. fi
  678. # Always remove the handler
  679. zle -F "$1"
  680. _ZSH_AUTOSUGGEST_ASYNC_FD=
  681. }
  682. #--------------------------------------------------------------------#
  683. # Start #
  684. #--------------------------------------------------------------------#
  685. # Start the autosuggestion widgets
  686. _zsh_autosuggest_start() {
  687. # By default we re-bind widgets on every precmd to ensure we wrap other
  688. # wrappers. Specifically, highlighting breaks if our widgets are wrapped by
  689. # zsh-syntax-highlighting widgets. This also allows modifications to the
  690. # widget list variables to take effect on the next precmd. However this has
  691. # a decent performance hit, so users can set ZSH_AUTOSUGGEST_MANUAL_REBIND
  692. # to disable the automatic re-binding.
  693. if (( ${+ZSH_AUTOSUGGEST_MANUAL_REBIND} )); then
  694. add-zsh-hook -d precmd _zsh_autosuggest_start
  695. fi
  696. _zsh_autosuggest_bind_widgets
  697. }
  698. # Mark for auto-loading the functions that we use
  699. autoload -Uz add-zsh-hook is-at-least
  700. # Automatically enable asynchronous mode in newer versions of zsh. Disable for
  701. # older versions because there is a bug when using async mode where ^C does not
  702. # work immediately after fetching a suggestion.
  703. # See https://github.com/zsh-users/zsh-autosuggestions/issues/364
  704. if is-at-least 5.0.8; then
  705. typeset -g ZSH_AUTOSUGGEST_USE_ASYNC=
  706. fi
  707. # Start the autosuggestion widgets on the next precmd
  708. add-zsh-hook precmd _zsh_autosuggest_start