選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

805 行
24 KiB

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