Merge pull request #330 from zsh-users/features/completion-suggestions
Features/completion suggestions
This commit is contained in:
		
						commit
						8593624996
					
				
							
								
								
									
										1
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										1
									
								
								Makefile
								
								
								
								
							|  | @ -9,6 +9,7 @@ SRC_FILES := \ | ||||||
| 	$(SRC_DIR)/highlight.zsh \
 | 	$(SRC_DIR)/highlight.zsh \
 | ||||||
| 	$(SRC_DIR)/widgets.zsh \
 | 	$(SRC_DIR)/widgets.zsh \
 | ||||||
| 	$(SRC_DIR)/strategies/*.zsh \
 | 	$(SRC_DIR)/strategies/*.zsh \
 | ||||||
|  | 	$(SRC_DIR)/fetch.zsh \
 | ||||||
| 	$(SRC_DIR)/async.zsh \
 | 	$(SRC_DIR)/async.zsh \
 | ||||||
| 	$(SRC_DIR)/start.zsh | 	$(SRC_DIR)/start.zsh | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								README.md
								
								
								
								
							
							
						
						
									
										11
									
								
								README.md
								
								
								
								
							|  | @ -2,7 +2,7 @@ | ||||||
| 
 | 
 | ||||||
| _[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._ | _[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._ | ||||||
| 
 | 
 | ||||||
| It suggests commands as you type, based on command history. | It suggests commands as you type. | ||||||
| 
 | 
 | ||||||
| Requirements: Zsh v4.3.11 or later | Requirements: Zsh v4.3.11 or later | ||||||
| 
 | 
 | ||||||
|  | @ -39,10 +39,13 @@ Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion | ||||||
| 
 | 
 | ||||||
| ### Suggestion Strategy | ### Suggestion Strategy | ||||||
| 
 | 
 | ||||||
| Set `ZSH_AUTOSUGGEST_STRATEGY` to choose the strategy for generating suggestions. There are currently two to choose from: | `ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. The strategies in the array are tried successively until a suggestion is found. There are currently three built-in strategies to choose from: | ||||||
| 
 | 
 | ||||||
| - `default`: Chooses the most recent match. | - `history`: Chooses the most recent match from history. | ||||||
| - `match_prev_cmd`: Chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`. | - `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`. | ||||||
|  | - `completion`: (experimental) Chooses a suggestion based on what tab-completion would suggest. | ||||||
|  | 
 | ||||||
|  | For example, setting `ZSH_AUTOSUGGEST_STRATEGY=(history completion)` will first try to find a suggestion from your history, but, if it can't find a match, will find a suggestion from the completion engine. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ### Widget Mapping | ### Widget Mapping | ||||||
|  |  | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | describe 'a running zpty command' do | ||||||
|  |   let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } } | ||||||
|  | 
 | ||||||
|  |   context 'when using `completion` strategy' do | ||||||
|  |     let(:options) { ["ZSH_AUTOSUGGEST_STRATEGY=completion"] } | ||||||
|  | 
 | ||||||
|  |     it 'is not affected' do | ||||||
|  |       session.send_keys('a').send_keys('C-h') | ||||||
|  |       session.run_command('zpty -t kitty; echo $?') | ||||||
|  | 
 | ||||||
|  |       wait_for { session.content }.to end_with("\n0") | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -2,7 +2,7 @@ describe 'using `zle -U`' do | ||||||
|   let(:before_sourcing) do |   let(:before_sourcing) do | ||||||
|     -> do |     -> do | ||||||
|       session. |       session. | ||||||
|         run_command('_zsh_autosuggest_strategy_test() { sleep 1; _zsh_autosuggest_strategy_default "$1" }'). |         run_command('_zsh_autosuggest_strategy_test() { sleep 1; _zsh_autosuggest_strategy_history "$1" }'). | ||||||
|         run_command('foo() { zle -U - "echo hello" }; zle -N foo; bindkey ^B foo') |         run_command('foo() { zle -U - "echo hello" }; zle -N foo; bindkey ^B foo') | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -1,20 +1,45 @@ | ||||||
| describe 'a suggestion for a given prefix' do | describe 'a suggestion for a given prefix' do | ||||||
|   let(:options) { ['_zsh_autosuggest_strategy_default() { suggestion="echo foo" }'] } |   let(:history_strategy) { '_zsh_autosuggest_strategy_history() { suggestion="history" }' } | ||||||
|  |   let(:foobar_strategy) { '_zsh_autosuggest_strategy_foobar() { [[ "foobar baz" = $1* ]] && suggestion="foobar baz" }' } | ||||||
|  |   let(:foobaz_strategy) { '_zsh_autosuggest_strategy_foobaz() { [[ "foobaz bar" = $1* ]] && suggestion="foobaz bar" }' } | ||||||
| 
 | 
 | ||||||
|   it 'is determined by calling the default strategy function' do |   let(:options) { [ history_strategy ] } | ||||||
|     session.send_string('e') | 
 | ||||||
|     wait_for { session.content }.to eq('echo foo') |   it 'by default is determined by calling the `history` strategy function' do | ||||||
|  |     session.send_string('h') | ||||||
|  |     wait_for { session.content }.to eq('history') | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   context 'when ZSH_AUTOSUGGEST_STRATEGY is set' do |   context 'when ZSH_AUTOSUGGEST_STRATEGY is set to an array' do | ||||||
|     let(:options) { [ |     let(:options) { [ | ||||||
|       '_zsh_autosuggest_strategy_custom() { suggestion="echo foo" }', |       foobar_strategy, | ||||||
|       'ZSH_AUTOSUGGEST_STRATEGY=custom' |       foobaz_strategy, | ||||||
|  |       'ZSH_AUTOSUGGEST_STRATEGY=(foobar foobaz)' | ||||||
|     ] } |     ] } | ||||||
| 
 | 
 | ||||||
|     it 'is determined by calling the specified strategy function' do |     it 'is determined by the first strategy function to return a suggestion' do | ||||||
|       session.send_string('e') |       session.send_string('foo') | ||||||
|       wait_for { session.content }.to eq('echo foo') |       wait_for { session.content }.to eq('foobar baz') | ||||||
|  | 
 | ||||||
|  |       session.send_string('baz') | ||||||
|  |       wait_for { session.content }.to eq('foobaz bar') | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   context 'when ZSH_AUTOSUGGEST_STRATEGY is set to a string' do | ||||||
|  |     let(:options) { [ | ||||||
|  |       foobar_strategy, | ||||||
|  |       foobaz_strategy, | ||||||
|  |       'ZSH_AUTOSUGGEST_STRATEGY="foobar foobaz"' | ||||||
|  |     ] } | ||||||
|  | 
 | ||||||
|  |     it 'is determined by the first strategy function to return a suggestion' do | ||||||
|  |       session.send_string('foo') | ||||||
|  |       wait_for { session.content }.to eq('foobar baz') | ||||||
|  | 
 | ||||||
|  |       session.send_string('baz') | ||||||
|  |       wait_for { session.content }.to eq('foobaz bar') | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | describe 'the `completion` suggestion strategy' do | ||||||
|  |   let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=completion'] } | ||||||
|  |   let(:before_sourcing) do | ||||||
|  |     -> do | ||||||
|  |       session. | ||||||
|  |         run_command('autoload compinit && compinit'). | ||||||
|  |         run_command('_foo() { compadd bar }'). | ||||||
|  |         run_command('compdef _foo baz') | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   it 'suggests the first completion result' do | ||||||
|  |     session.send_string('baz ') | ||||||
|  |     wait_for { session.content }.to eq('baz bar') | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   context 'when async mode is enabled' do | ||||||
|  |     let(:options) { ['ZSH_AUTOSUGGEST_USE_ASYNC=true', 'ZSH_AUTOSUGGEST_STRATEGY=completion'] } | ||||||
|  | 
 | ||||||
|  |     it 'suggests the first completion result' do | ||||||
|  |       session.send_string('baz ') | ||||||
|  |       wait_for { session.content }.to eq('baz bar') | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| require 'strategies/special_characters_helper' | require 'strategies/special_characters_helper' | ||||||
| 
 | 
 | ||||||
| describe 'the default suggestion strategy' do | describe 'the `history` suggestion strategy' do | ||||||
|   it 'suggests the last matching history entry' do |   it 'suggests the last matching history entry' do | ||||||
|     with_history('ls foo', 'ls bar', 'echo baz') do |     with_history('ls foo', 'ls bar', 'echo baz') do | ||||||
|       session.send_string('ls') |       session.send_string('ls') | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| require 'strategies/special_characters_helper' | require 'strategies/special_characters_helper' | ||||||
| 
 | 
 | ||||||
| describe 'the match_prev_cmd strategy' do | describe 'the `match_prev_cmd` strategy' do | ||||||
|   let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd'] } |   let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd'] } | ||||||
| 
 | 
 | ||||||
|   it 'suggests the last matching history entry after the previous command' do |   it 'suggests the last matching history entry after the previous command' do | ||||||
|  |  | ||||||
|  | @ -11,7 +11,9 @@ ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' | ||||||
| # Prefix to use when saving original versions of bound widgets | # Prefix to use when saving original versions of bound widgets | ||||||
| ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- | ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- | ||||||
| 
 | 
 | ||||||
| ZSH_AUTOSUGGEST_STRATEGY=default | # Strategies to use to fetch a suggestion | ||||||
|  | # Will try each strategy in order until a suggestion is returned | ||||||
|  | ZSH_AUTOSUGGEST_STRATEGY=(history) | ||||||
| 
 | 
 | ||||||
| # Widgets that clear the suggestion | # Widgets that clear the suggestion | ||||||
| ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( | ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( | ||||||
|  | @ -68,4 +70,7 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( | ||||||
| ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= | ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= | ||||||
| 
 | 
 | ||||||
| # Pty name for calculating autosuggestions asynchronously | # Pty name for calculating autosuggestions asynchronously | ||||||
| ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty | ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_async_pty | ||||||
|  | 
 | ||||||
|  | # Pty name for capturing completions for completion suggestion strategy | ||||||
|  | ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty | ||||||
|  |  | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Fetch Suggestion                                                   # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Loops through all specified strategies and returns a suggestion | ||||||
|  | # from the first strategy to provide one. | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_fetch_suggestion() { | ||||||
|  | 	typeset -g suggestion | ||||||
|  | 	local -a strategies | ||||||
|  | 	local strategy | ||||||
|  | 
 | ||||||
|  | 	# Ensure we are working with an array | ||||||
|  | 	strategies=(${=ZSH_AUTOSUGGEST_STRATEGY}) | ||||||
|  | 
 | ||||||
|  | 	for strategy in $strategies; do | ||||||
|  | 		# Try to get a suggestion from this strategy | ||||||
|  | 		_zsh_autosuggest_strategy_$strategy "$1" | ||||||
|  | 
 | ||||||
|  | 		# Break once we've found a suggestion | ||||||
|  | 		[[ -n "$suggestion" ]] && break | ||||||
|  | 	done | ||||||
|  | } | ||||||
|  | @ -0,0 +1,124 @@ | ||||||
|  | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Completion Suggestion Strategy                                     # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Fetches suggestions from zsh's completion engine | ||||||
|  | # Based on https://github.com/Valodim/zsh-capture-completion | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_capture_setup() { | ||||||
|  | 	zmodload zsh/zutil # For `zparseopts` | ||||||
|  | 
 | ||||||
|  | 	# Ensure completions have been initialized | ||||||
|  | 	if ! whence compdef >/dev/null; then | ||||||
|  | 		autoload -Uz compinit && compinit | ||||||
|  | 	fi | ||||||
|  | 
 | ||||||
|  | 	# There is a bug in zpty module (fixed in zsh/master) by which a | ||||||
|  | 	# zpty that exits will kill all zpty processes that were forked | ||||||
|  | 	# before it. Here we set up a zsh exit hook to SIGKILL the zpty | ||||||
|  | 	# process immediately, before it has a chance to kill any other | ||||||
|  | 	# zpty processes. | ||||||
|  | 	zshexit() { | ||||||
|  | 		kill -KILL $$ | ||||||
|  | 		sleep 1 # Block for long enough for the signal to come through | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	# Never group stuff! | ||||||
|  | 	zstyle ':completion:*' list-grouped false | ||||||
|  | 
 | ||||||
|  | 	# No list separator, this saves some stripping later on | ||||||
|  | 	zstyle ':completion:*' list-separator '' | ||||||
|  | 
 | ||||||
|  | 	# Override compadd (this is our hook) | ||||||
|  | 	compadd () { | ||||||
|  | 		setopt localoptions norcexpandparam | ||||||
|  | 
 | ||||||
|  | 		# Just delegate and leave if any of -O, -A or -D are given | ||||||
|  | 		if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then | ||||||
|  | 			builtin compadd "$@" | ||||||
|  | 			return $? | ||||||
|  | 		fi | ||||||
|  | 
 | ||||||
|  | 		# Capture completions by injecting -A parameter into the compadd call. | ||||||
|  | 		# This takes care of matching for us. | ||||||
|  | 		typeset -a __hits | ||||||
|  | 		builtin compadd -A __hits "$@" | ||||||
|  | 
 | ||||||
|  | 		# Exit if no completion results | ||||||
|  | 		[[ -n $__hits ]] || return | ||||||
|  | 
 | ||||||
|  | 		# Extract prefixes and suffixes from compadd call. we can't do zsh's cool | ||||||
|  | 		# -r remove-func magic, but it's better than nothing. | ||||||
|  | 		typeset -A apre hpre hsuf asuf | ||||||
|  | 		zparseopts -E P:=apre p:=hpre S:=asuf s:=hsuf | ||||||
|  | 
 | ||||||
|  | 		# Print the first match | ||||||
|  | 		echo -nE - $'\0'$IPREFIX$apre$hpre$__hits[1]$dsuf$hsuf$asuf$'\0' | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_capture_widget() { | ||||||
|  | 	_zsh_autosuggest_capture_setup | ||||||
|  | 
 | ||||||
|  | 	zle complete-word | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | zle -N autosuggest-capture-completion _zsh_autosuggest_capture_widget | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_capture_buffer() { | ||||||
|  | 	local BUFFERCONTENT="$1" | ||||||
|  | 
 | ||||||
|  | 	_zsh_autosuggest_capture_setup | ||||||
|  | 
 | ||||||
|  | 	zmodload zsh/parameter # For `$functions` | ||||||
|  | 
 | ||||||
|  | 	# Make vared completion work as if for a normal command line | ||||||
|  | 	# https://stackoverflow.com/a/7057118/154703 | ||||||
|  | 	autoload +X _complete | ||||||
|  | 	functions[_original_complete]=$functions[_complete] | ||||||
|  | 	_complete () { | ||||||
|  | 		unset 'compstate[vared]' | ||||||
|  | 		_original_complete "$@" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	# Open zle with buffer set so we can capture completions for it | ||||||
|  | 	vared BUFFERCONTENT | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_capture_completion() { | ||||||
|  | 	typeset -g completion | ||||||
|  | 	local line REPLY | ||||||
|  | 
 | ||||||
|  | 	# Zle will be inactive if we are in async mode | ||||||
|  | 	if zle; then | ||||||
|  | 		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zle autosuggest-capture-completion | ||||||
|  | 	else | ||||||
|  | 		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_buffer "\$1" | ||||||
|  | 		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t' | ||||||
|  | 	fi | ||||||
|  | 
 | ||||||
|  | 	# The completion result is surrounded by null bytes, so read the | ||||||
|  | 	# content between the first two null bytes. | ||||||
|  | 	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0' | ||||||
|  | 
 | ||||||
|  | 	# On older versions of zsh, we sometimes get extra bytes after the | ||||||
|  | 	# second null byte, so trim those off the end | ||||||
|  | 	completion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}" | ||||||
|  | 
 | ||||||
|  | 	# Destroy the pty | ||||||
|  | 	zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_strategy_completion() { | ||||||
|  | 	typeset -g suggestion | ||||||
|  | 	local completion | ||||||
|  | 
 | ||||||
|  | 	# Fetch the first completion result | ||||||
|  | 	_zsh_autosuggest_capture_completion "$1" | ||||||
|  | 
 | ||||||
|  | 	# Add the completion string to the buffer to build the full suggestion | ||||||
|  | 	local -i i=1 | ||||||
|  | 	while [[ "$completion" != "${1[$i,-1]}"* ]]; do ((i++)); done | ||||||
|  | 	suggestion="${1[1,$i-1]}$completion" | ||||||
|  | } | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| 
 | 
 | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Default Suggestion Strategy                                        # | # History Suggestion Strategy                                        # | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Suggests the most recent history item that matches the given | # Suggests the most recent history item that matches the given | ||||||
| # prefix. | # prefix. | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
| _zsh_autosuggest_strategy_default() { | _zsh_autosuggest_strategy_history() { | ||||||
| 	# Reset options to defaults and enable LOCAL_OPTIONS | 	# Reset options to defaults and enable LOCAL_OPTIONS | ||||||
| 	emulate -L zsh | 	emulate -L zsh | ||||||
| 
 | 
 | ||||||
|  | @ -97,7 +97,7 @@ _zsh_autosuggest_fetch() { | ||||||
| 		_zsh_autosuggest_async_request "$BUFFER" | 		_zsh_autosuggest_async_request "$BUFFER" | ||||||
| 	else | 	else | ||||||
| 		local suggestion | 		local suggestion | ||||||
| 		_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER" | 		_zsh_autosuggest_fetch_suggestion "$BUFFER" | ||||||
| 		_zsh_autosuggest_suggest "$suggestion" | 		_zsh_autosuggest_suggest "$suggestion" | ||||||
| 	fi | 	fi | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -47,7 +47,9 @@ ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' | ||||||
| # Prefix to use when saving original versions of bound widgets | # Prefix to use when saving original versions of bound widgets | ||||||
| ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- | ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- | ||||||
| 
 | 
 | ||||||
| ZSH_AUTOSUGGEST_STRATEGY=default | # Strategies to use to fetch a suggestion | ||||||
|  | # Will try each strategy in order until a suggestion is returned | ||||||
|  | ZSH_AUTOSUGGEST_STRATEGY=(history) | ||||||
| 
 | 
 | ||||||
| # Widgets that clear the suggestion | # Widgets that clear the suggestion | ||||||
| ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( | ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( | ||||||
|  | @ -104,7 +106,10 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( | ||||||
| ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= | ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= | ||||||
| 
 | 
 | ||||||
| # Pty name for calculating autosuggestions asynchronously | # Pty name for calculating autosuggestions asynchronously | ||||||
| ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty | ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_async_pty | ||||||
|  | 
 | ||||||
|  | # Pty name for capturing completions for completion suggestion strategy | ||||||
|  | ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty | ||||||
| 
 | 
 | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Utility Functions                                                  # | # Utility Functions                                                  # | ||||||
|  | @ -378,7 +383,7 @@ _zsh_autosuggest_fetch() { | ||||||
| 		_zsh_autosuggest_async_request "$BUFFER" | 		_zsh_autosuggest_async_request "$BUFFER" | ||||||
| 	else | 	else | ||||||
| 		local suggestion | 		local suggestion | ||||||
| 		_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER" | 		_zsh_autosuggest_fetch_suggestion "$BUFFER" | ||||||
| 		_zsh_autosuggest_suggest "$suggestion" | 		_zsh_autosuggest_suggest "$suggestion" | ||||||
| 	fi | 	fi | ||||||
| } | } | ||||||
|  | @ -494,13 +499,137 @@ zle -N autosuggest-disable _zsh_autosuggest_widget_disable | ||||||
| zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle | zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle | ||||||
| 
 | 
 | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Default Suggestion Strategy                                        # | # Completion Suggestion Strategy                                     # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Fetches suggestions from zsh's completion engine | ||||||
|  | # Based on https://github.com/Valodim/zsh-capture-completion | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_capture_setup() { | ||||||
|  | 	zmodload zsh/zutil # For `zparseopts` | ||||||
|  | 
 | ||||||
|  | 	# Ensure completions have been initialized | ||||||
|  | 	if ! whence compdef >/dev/null; then | ||||||
|  | 		autoload -Uz compinit && compinit | ||||||
|  | 	fi | ||||||
|  | 
 | ||||||
|  | 	# There is a bug in zpty module (fixed in zsh/master) by which a | ||||||
|  | 	# zpty that exits will kill all zpty processes that were forked | ||||||
|  | 	# before it. Here we set up a zsh exit hook to SIGKILL the zpty | ||||||
|  | 	# process immediately, before it has a chance to kill any other | ||||||
|  | 	# zpty processes. | ||||||
|  | 	zshexit() { | ||||||
|  | 		kill -KILL $$ | ||||||
|  | 		sleep 1 # Block for long enough for the signal to come through | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	# Never group stuff! | ||||||
|  | 	zstyle ':completion:*' list-grouped false | ||||||
|  | 
 | ||||||
|  | 	# No list separator, this saves some stripping later on | ||||||
|  | 	zstyle ':completion:*' list-separator '' | ||||||
|  | 
 | ||||||
|  | 	# Override compadd (this is our hook) | ||||||
|  | 	compadd () { | ||||||
|  | 		setopt localoptions norcexpandparam | ||||||
|  | 
 | ||||||
|  | 		# Just delegate and leave if any of -O, -A or -D are given | ||||||
|  | 		if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then | ||||||
|  | 			builtin compadd "$@" | ||||||
|  | 			return $? | ||||||
|  | 		fi | ||||||
|  | 
 | ||||||
|  | 		# Capture completions by injecting -A parameter into the compadd call. | ||||||
|  | 		# This takes care of matching for us. | ||||||
|  | 		typeset -a __hits | ||||||
|  | 		builtin compadd -A __hits "$@" | ||||||
|  | 
 | ||||||
|  | 		# Exit if no completion results | ||||||
|  | 		[[ -n $__hits ]] || return | ||||||
|  | 
 | ||||||
|  | 		# Extract prefixes and suffixes from compadd call. we can't do zsh's cool | ||||||
|  | 		# -r remove-func magic, but it's better than nothing. | ||||||
|  | 		typeset -A apre hpre hsuf asuf | ||||||
|  | 		zparseopts -E P:=apre p:=hpre S:=asuf s:=hsuf | ||||||
|  | 
 | ||||||
|  | 		# Print the first match | ||||||
|  | 		echo -nE - $'\0'$IPREFIX$apre$hpre$__hits[1]$dsuf$hsuf$asuf$'\0' | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_capture_widget() { | ||||||
|  | 	_zsh_autosuggest_capture_setup | ||||||
|  | 
 | ||||||
|  | 	zle complete-word | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | zle -N autosuggest-capture-completion _zsh_autosuggest_capture_widget | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_capture_buffer() { | ||||||
|  | 	local BUFFERCONTENT="$1" | ||||||
|  | 
 | ||||||
|  | 	_zsh_autosuggest_capture_setup | ||||||
|  | 
 | ||||||
|  | 	zmodload zsh/parameter # For `$functions` | ||||||
|  | 
 | ||||||
|  | 	# Make vared completion work as if for a normal command line | ||||||
|  | 	# https://stackoverflow.com/a/7057118/154703 | ||||||
|  | 	autoload +X _complete | ||||||
|  | 	functions[_original_complete]=$functions[_complete] | ||||||
|  | 	_complete () { | ||||||
|  | 		unset 'compstate[vared]' | ||||||
|  | 		_original_complete "$@" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	# Open zle with buffer set so we can capture completions for it | ||||||
|  | 	vared BUFFERCONTENT | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_capture_completion() { | ||||||
|  | 	typeset -g completion | ||||||
|  | 	local line REPLY | ||||||
|  | 
 | ||||||
|  | 	# Zle will be inactive if we are in async mode | ||||||
|  | 	if zle; then | ||||||
|  | 		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zle autosuggest-capture-completion | ||||||
|  | 	else | ||||||
|  | 		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_buffer "\$1" | ||||||
|  | 		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t' | ||||||
|  | 	fi | ||||||
|  | 
 | ||||||
|  | 	# The completion result is surrounded by null bytes, so read the | ||||||
|  | 	# content between the first two null bytes. | ||||||
|  | 	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0' | ||||||
|  | 
 | ||||||
|  | 	# On older versions of zsh, we sometimes get extra bytes after the | ||||||
|  | 	# second null byte, so trim those off the end | ||||||
|  | 	completion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}" | ||||||
|  | 
 | ||||||
|  | 	# Destroy the pty | ||||||
|  | 	zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_strategy_completion() { | ||||||
|  | 	typeset -g suggestion | ||||||
|  | 	local completion | ||||||
|  | 
 | ||||||
|  | 	# Fetch the first completion result | ||||||
|  | 	_zsh_autosuggest_capture_completion "$1" | ||||||
|  | 
 | ||||||
|  | 	# Add the completion string to the buffer to build the full suggestion | ||||||
|  | 	local -i i=1 | ||||||
|  | 	while [[ "$completion" != "${1[$i,-1]}"* ]]; do ((i++)); done | ||||||
|  | 	suggestion="${1[1,$i-1]}$completion" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # History Suggestion Strategy                                        # | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Suggests the most recent history item that matches the given | # Suggests the most recent history item that matches the given | ||||||
| # prefix. | # prefix. | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
| _zsh_autosuggest_strategy_default() { | _zsh_autosuggest_strategy_history() { | ||||||
| 	# Reset options to defaults and enable LOCAL_OPTIONS | 	# Reset options to defaults and enable LOCAL_OPTIONS | ||||||
| 	emulate -L zsh | 	emulate -L zsh | ||||||
| 
 | 
 | ||||||
|  | @ -577,6 +706,30 @@ _zsh_autosuggest_strategy_match_prev_cmd() { | ||||||
| 	typeset -g suggestion="$history[$histkey]" | 	typeset -g suggestion="$history[$histkey]" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Fetch Suggestion                                                   # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Loops through all specified strategies and returns a suggestion | ||||||
|  | # from the first strategy to provide one. | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_fetch_suggestion() { | ||||||
|  | 	typeset -g suggestion | ||||||
|  | 	local -a strategies | ||||||
|  | 	local strategy | ||||||
|  | 
 | ||||||
|  | 	# Ensure we are working with an array | ||||||
|  | 	strategies=(${=ZSH_AUTOSUGGEST_STRATEGY}) | ||||||
|  | 
 | ||||||
|  | 	for strategy in $strategies; do | ||||||
|  | 		# Try to get a suggestion from this strategy | ||||||
|  | 		_zsh_autosuggest_strategy_$strategy "$1" | ||||||
|  | 
 | ||||||
|  | 		# Break once we've found a suggestion | ||||||
|  | 		[[ -n "$suggestion" ]] && break | ||||||
|  | 	done | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Async                                                              # | # Async                                                              # | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue