From 63789e96b54d8e8acb91792216cc564f16d2cd07 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 26 May 2018 14:01:03 -0600
Subject: [PATCH 01/47] Fix handling of newline + carriage return in async pty
 (#333)

---
 spec/async_spec.rb      | 39 +++++++++++++++++++++++++++++++++++++++
 src/async.zsh           |  5 ++++-
 zsh-autosuggestions.zsh |  5 ++++-
 3 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/spec/async_spec.rb b/spec/async_spec.rb
index 0bcbaa1..10f1a74 100644
--- a/spec/async_spec.rb
+++ b/spec/async_spec.rb
@@ -27,6 +27,45 @@ context 'with asynchronous suggestions enabled' do
     end
   end
 
+  it 'should not add extra carriage returns before newlines' do
+    session.
+      send_string('echo "').
+      send_keys('escape').
+      send_keys('enter').
+      send_string('"').
+      send_keys('enter')
+
+    session.clear_screen
+
+    session.send_string('echo')
+    wait_for { session.content }.to eq("echo \"\n\"")
+  end
+
+  it 'should treat carriage returns and newlines as separate characters' do
+    session.
+      send_string('echo "').
+      send_keys('C-v').
+      send_keys('enter').
+      send_string('foo"').
+      send_keys('enter')
+
+    session.
+      send_string('echo "').
+      send_keys('control').
+      send_keys('enter').
+      send_string('bar"').
+      send_keys('enter')
+
+    session.clear_screen
+
+    session.
+      send_string('echo "').
+      send_keys('C-v').
+      send_keys('enter')
+
+    wait_for { session.content }.to eq('echo "^Mfoo"')
+  end
+
   describe 'exiting a subshell' do
     it 'should not cause error messages to be printed' do
       session.run_command('$(exit)')
diff --git a/src/async.zsh b/src/async.zsh
index 9a0cfaa..62e3329 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -17,9 +17,12 @@ _zsh_autosuggest_async_server() {
 		sleep 1 # Block for long enough for the signal to come through
 	}
 
-	# Output only newlines (not carriage return + newline)
+	# Don't add any extra carriage returns
 	stty -onlcr
 
+	# Don't translate carriage returns to newlines
+	stty -icrnl
+
 	# Silence any error messages
 	exec 2>/dev/null
 
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 1c3eab5..e2e06be 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -595,9 +595,12 @@ _zsh_autosuggest_async_server() {
 		sleep 1 # Block for long enough for the signal to come through
 	}
 
-	# Output only newlines (not carriage return + newline)
+	# Don't add any extra carriage returns
 	stty -onlcr
 
+	# Don't translate carriage returns to newlines
+	stty -icrnl
+
 	# Silence any error messages
 	exec 2>/dev/null
 

From 5549b68e6edd285a50173c7360a935d91879f33a Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 26 May 2018 15:33:32 -0600
Subject: [PATCH 02/47] Async is less reliable in zsh versions < 5.0.8

`stty` occasionally hangs (always in CircleCI) inside the async pty.

Disable the tests for now until we can figure out and fix/workaround
this issue.
---
 spec/async_spec.rb       | 4 ++++
 spec/terminal_session.rb | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/spec/async_spec.rb b/spec/async_spec.rb
index 10f1a74..152adde 100644
--- a/spec/async_spec.rb
+++ b/spec/async_spec.rb
@@ -1,4 +1,8 @@
 context 'with asynchronous suggestions enabled' do
+  before do
+    skip 'Async mode not supported below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
+  end
+
   let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] }
 
   describe '`up-line-or-beginning-search`' do
diff --git a/spec/terminal_session.rb b/spec/terminal_session.rb
index 2d9468f..f91ee6c 100644
--- a/spec/terminal_session.rb
+++ b/spec/terminal_session.rb
@@ -18,6 +18,10 @@ class TerminalSession
     tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'")
   end
 
+  def zsh_version
+    @zsh_version ||= Gem::Version.new(`#{ZSH_BIN} -c 'echo -n $ZSH_VERSION'`)
+  end
+
   def tmux_socket_name
     @tmux_socket_name ||= SecureRandom.hex(6)
   end

From 82b08e2dc8c59a40d363cb0c12f3516937cb1733 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 16 May 2018 15:27:06 -0600
Subject: [PATCH 03/47] First pass at getting suggestions from completion
 engine (#111)

Uses https://github.com/Valodim/zsh-capture-completion with some slight
modifications.
---
 src/config.zsh                |   5 +-
 src/strategies/completion.zsh | 132 ++++++++++++++++++++++++++++++++
 zsh-autosuggestions.zsh       | 137 +++++++++++++++++++++++++++++++++-
 3 files changed, 272 insertions(+), 2 deletions(-)
 create mode 100644 src/strategies/completion.zsh

diff --git a/src/config.zsh b/src/config.zsh
index 597307f..9b3dbae 100644
--- a/src/config.zsh
+++ b/src/config.zsh
@@ -68,4 +68,7 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
 ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
 
 # 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
diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
new file mode 100644
index 0000000..db948a0
--- /dev/null
+++ b/src/strategies/completion.zsh
@@ -0,0 +1,132 @@
+
+#--------------------------------------------------------------------#
+# Completion Suggestion Strategy                                     #
+#--------------------------------------------------------------------#
+# Fetches suggestions from zsh's completion engine
+#
+
+# Big thanks to https://github.com/Valodim/zsh-capture-completion
+_zsh_autosuggest_capture_completion() {
+	zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zsh -f -i
+
+	# line buffer for pty output
+	local line
+
+	setopt rcquotes
+	() {
+		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME source $1
+		repeat 4; do
+			zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line
+			[[ $line == ok* ]] && return
+		done
+		echo 'error initializing.' >&2
+		exit 2
+	} =( <<< '
+	# no prompt!
+	PROMPT=
+	# load completion system
+	autoload compinit
+	compinit -d ~/.zcompdump_autosuggestions
+	# never run a command
+	bindkey ''^M'' undefined
+	bindkey ''^J'' undefined
+	bindkey ''^I'' complete-word
+	# send a line with null-byte at the end before and after completions are output
+	null-line () {
+		echo -E - $''\0''
+	}
+	compprefuncs=( null-line )
+	comppostfuncs=( null-line exit )
+	# never group stuff!
+	zstyle '':completion:*'' list-grouped false
+	# don''t insert tab when attempting completion on empty line
+	zstyle '':completion:*'' insert-tab false
+	# no list separator, this saves some stripping later on
+	zstyle '':completion:*'' list-separator ''''
+	# we use zparseopts
+	zmodload zsh/zutil
+	# override compadd (this our hook)
+	compadd () {
+		# check if any of -O, -A or -D are given
+		if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then
+			# if that is the case, just delegate and leave
+			builtin compadd "$@"
+			return $?
+		fi
+		# ok, this concerns us!
+		# echo -E - got this: "$@"
+		# be careful with namespacing here, we don''t want to mess with stuff that
+		# should be passed to compadd!
+		typeset -a __hits __dscr __tmp
+		# do we have a description parameter?
+		# note we don''t use zparseopts here because of combined option parameters
+		# with arguments like -default- confuse it.
+		if (( $@[(I)-d] )); then # kind of a hack, $+@[(r)-d] doesn''t work because of line noise overload
+			# next param after -d
+			__tmp=${@[$[${@[(i)-d]}+1]]}
+			# description can be given as an array parameter name, or inline () array
+			if [[ $__tmp == \(* ]]; then
+				eval "__dscr=$__tmp"
+			else
+				__dscr=( "${(@P)__tmp}" )
+			fi
+		fi
+		# capture completions by injecting -A parameter into the compadd call.
+		# this takes care of matching for us.
+		builtin compadd -A __hits -D __dscr "$@"
+		# JESUS CHRIST IT TOOK ME FOREVER TO FIGURE OUT THIS OPTION WAS SET AND WAS MESSING WITH MY SHIT HERE
+		setopt localoptions norcexpandparam extendedglob
+		# 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
+		# append / to directories? we are only emulating -f in a half-assed way
+		# here, but it''s better than nothing.
+		integer dirsuf=0
+		# don''t be fooled by -default- >.>
+		if [[ -z $hsuf && "${${@//-default-/}% -# *}" == *-[[:alnum:]]#f* ]]; then
+			dirsuf=1
+		fi
+		# just drop
+		[[ -n $__hits ]] || return
+		# this is the point where we have all matches in $__hits and all
+		# descriptions in $__dscr!
+		# display all matches
+		local dsuf dscr
+		for i in {1..$#__hits}; do
+			# add a dir suffix?
+			(( dirsuf )) && [[ -d $__hits[$i] ]] && dsuf=/ || dsuf=
+			# description to be displayed afterwards
+			(( $#__dscr >= $i )) && dscr=" -- ${${__dscr[$i]}##$__hits[$i] #}" || dscr=
+			echo -E - $IPREFIX$apre$hpre$__hits[$i]$dsuf$hsuf$asuf
+		done
+	}
+	# signal success!
+	echo ok')
+
+	zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "$*"$'\t'
+
+	integer tog=0
+	# read from the pty, and parse linewise
+	while zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME; do :; done | while IFS= read -r line; do
+		if [[ $line == *$'\0\r' ]]; then
+			(( tog++ )) && return 0 || continue
+		fi
+		# display between toggles
+		(( tog )) && echo -E - $line
+	done
+
+	return 2
+}
+
+_zsh_autosuggest_strategy_completion() {
+	typeset -g suggestion=$(_zsh_autosuggest_capture_completion "$1" | head -n 1)
+
+	# Strip the trailing carriage return
+	suggestion="${suggestion%$'\r'}"
+
+	# Add the completion string to the buffer to build the full suggestion
+	local -i i=1
+	while [[ "$suggestion" != "${1[$i,-1]}"* ]]; do ((i++)); done
+	suggestion="${1[1,$i-1]}$suggestion"
+}
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index e2e06be..edd6d84 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -104,7 +104,10 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
 ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
 
 # 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                                                  #
@@ -493,6 +496,138 @@ zle -N autosuggest-enable _zsh_autosuggest_widget_enable
 zle -N autosuggest-disable _zsh_autosuggest_widget_disable
 zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 
+#--------------------------------------------------------------------#
+# Completion Suggestion Strategy                                     #
+#--------------------------------------------------------------------#
+# Fetches suggestions from zsh's completion engine
+#
+
+# Big thanks to https://github.com/Valodim/zsh-capture-completion
+_zsh_autosuggest_capture_completion() {
+	zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zsh -f -i
+
+	# line buffer for pty output
+	local line
+
+	setopt rcquotes
+	() {
+		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME source $1
+		repeat 4; do
+			zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line
+			[[ $line == ok* ]] && return
+		done
+		echo 'error initializing.' >&2
+		exit 2
+	} =( <<< '
+	# no prompt!
+	PROMPT=
+	# load completion system
+	autoload compinit
+	compinit -d ~/.zcompdump_autosuggestions
+	# never run a command
+	bindkey ''^M'' undefined
+	bindkey ''^J'' undefined
+	bindkey ''^I'' complete-word
+	# send a line with null-byte at the end before and after completions are output
+	null-line () {
+		echo -E - $''\0''
+	}
+	compprefuncs=( null-line )
+	comppostfuncs=( null-line exit )
+	# never group stuff!
+	zstyle '':completion:*'' list-grouped false
+	# don''t insert tab when attempting completion on empty line
+	zstyle '':completion:*'' insert-tab false
+	# no list separator, this saves some stripping later on
+	zstyle '':completion:*'' list-separator ''''
+	# we use zparseopts
+	zmodload zsh/zutil
+	# override compadd (this our hook)
+	compadd () {
+		# check if any of -O, -A or -D are given
+		if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then
+			# if that is the case, just delegate and leave
+			builtin compadd "$@"
+			return $?
+		fi
+		# ok, this concerns us!
+		# echo -E - got this: "$@"
+		# be careful with namespacing here, we don''t want to mess with stuff that
+		# should be passed to compadd!
+		typeset -a __hits __dscr __tmp
+		# do we have a description parameter?
+		# note we don''t use zparseopts here because of combined option parameters
+		# with arguments like -default- confuse it.
+		if (( $@[(I)-d] )); then # kind of a hack, $+@[(r)-d] doesn''t work because of line noise overload
+			# next param after -d
+			__tmp=${@[$[${@[(i)-d]}+1]]}
+			# description can be given as an array parameter name, or inline () array
+			if [[ $__tmp == \(* ]]; then
+				eval "__dscr=$__tmp"
+			else
+				__dscr=( "${(@P)__tmp}" )
+			fi
+		fi
+		# capture completions by injecting -A parameter into the compadd call.
+		# this takes care of matching for us.
+		builtin compadd -A __hits -D __dscr "$@"
+		# JESUS CHRIST IT TOOK ME FOREVER TO FIGURE OUT THIS OPTION WAS SET AND WAS MESSING WITH MY SHIT HERE
+		setopt localoptions norcexpandparam extendedglob
+		# 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
+		# append / to directories? we are only emulating -f in a half-assed way
+		# here, but it''s better than nothing.
+		integer dirsuf=0
+		# don''t be fooled by -default- >.>
+		if [[ -z $hsuf && "${${@//-default-/}% -# *}" == *-[[:alnum:]]#f* ]]; then
+			dirsuf=1
+		fi
+		# just drop
+		[[ -n $__hits ]] || return
+		# this is the point where we have all matches in $__hits and all
+		# descriptions in $__dscr!
+		# display all matches
+		local dsuf dscr
+		for i in {1..$#__hits}; do
+			# add a dir suffix?
+			(( dirsuf )) && [[ -d $__hits[$i] ]] && dsuf=/ || dsuf=
+			# description to be displayed afterwards
+			(( $#__dscr >= $i )) && dscr=" -- ${${__dscr[$i]}##$__hits[$i] #}" || dscr=
+			echo -E - $IPREFIX$apre$hpre$__hits[$i]$dsuf$hsuf$asuf
+		done
+	}
+	# signal success!
+	echo ok')
+
+	zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "$*"$'\t'
+
+	integer tog=0
+	# read from the pty, and parse linewise
+	while zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME; do :; done | while IFS= read -r line; do
+		if [[ $line == *$'\0\r' ]]; then
+			(( tog++ )) && return 0 || continue
+		fi
+		# display between toggles
+		(( tog )) && echo -E - $line
+	done
+
+	return 2
+}
+
+_zsh_autosuggest_strategy_completion() {
+	typeset -g suggestion=$(_zsh_autosuggest_capture_completion "$1" | head -n 1)
+
+	# Strip the trailing carriage return
+	suggestion="${suggestion%$'\r'}"
+
+	# Add the completion string to the buffer to build the full suggestion
+	local -i i=1
+	while [[ "$suggestion" != "${1[$i,-1]}"* ]]; do ((i++)); done
+	suggestion="${1[1,$i-1]}$suggestion"
+}
+
 #--------------------------------------------------------------------#
 # Default Suggestion Strategy                                        #
 #--------------------------------------------------------------------#

From c5551daabcea48d11028036df88a43a064934d18 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 16 May 2018 15:52:46 -0600
Subject: [PATCH 04/47] Default strategy now tries history first and falls back
 to completion

---
 src/strategies/default.zsh | 22 +++++++---------------
 src/strategies/history.zsh | 25 +++++++++++++++++++++++++
 zsh-autosuggestions.zsh    | 19 ++++++++++++++++++-
 3 files changed, 50 insertions(+), 16 deletions(-)
 create mode 100644 src/strategies/history.zsh

diff --git a/src/strategies/default.zsh b/src/strategies/default.zsh
index 0e85fb5..68617ff 100644
--- a/src/strategies/default.zsh
+++ b/src/strategies/default.zsh
@@ -2,24 +2,16 @@
 #--------------------------------------------------------------------#
 # Default Suggestion Strategy                                        #
 #--------------------------------------------------------------------#
-# Suggests the most recent history item that matches the given
-# prefix.
+# Will provide suggestions from your history. If no matches are found
+# in history, will provide a suggestion from the completion engine.
 #
 
 _zsh_autosuggest_strategy_default() {
-	# Reset options to defaults and enable LOCAL_OPTIONS
-	emulate -L zsh
+	typeset -g suggestion
 
-	# Enable globbing flags so that we can use (#m)
-	setopt EXTENDED_GLOB
+	_zsh_autosuggest_strategy_history "$1"
 
-	# Escape backslashes and all of the glob operators so we can use
-	# this string as a pattern to search the $history associative array.
-	# - (#m) globbing flag enables setting references for match data
-	# TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
-	local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
-
-	# Get the history items that match
-	# - (r) subscript flag makes the pattern match on values
-	typeset -g suggestion="${history[(r)${prefix}*]}"
+	if [[ -z "$suggestion" ]]; then
+		_zsh_autosuggest_strategy_completion "$1"
+	fi
 }
diff --git a/src/strategies/history.zsh b/src/strategies/history.zsh
new file mode 100644
index 0000000..a2755a5
--- /dev/null
+++ b/src/strategies/history.zsh
@@ -0,0 +1,25 @@
+
+#--------------------------------------------------------------------#
+# History Suggestion Strategy                                        #
+#--------------------------------------------------------------------#
+# Suggests the most recent history item that matches the given
+# prefix.
+#
+
+_zsh_autosuggest_strategy_history() {
+	# Reset options to defaults and enable LOCAL_OPTIONS
+	emulate -L zsh
+
+	# Enable globbing flags so that we can use (#m)
+	setopt EXTENDED_GLOB
+
+	# Escape backslashes and all of the glob operators so we can use
+	# this string as a pattern to search the $history associative array.
+	# - (#m) globbing flag enables setting references for match data
+	# TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
+	local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
+
+	# Get the history items that match
+	# - (r) subscript flag makes the pattern match on values
+	typeset -g suggestion="${history[(r)${prefix}*]}"
+}
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index edd6d84..41336a7 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -631,11 +631,28 @@ _zsh_autosuggest_strategy_completion() {
 #--------------------------------------------------------------------#
 # Default Suggestion Strategy                                        #
 #--------------------------------------------------------------------#
+# Will provide suggestions from your history. If no matches are found
+# in history, will provide a suggestion from the completion engine.
+#
+
+_zsh_autosuggest_strategy_default() {
+	typeset -g suggestion
+
+	_zsh_autosuggest_strategy_history "$1"
+
+	if [[ -z "$suggestion" ]]; then
+		_zsh_autosuggest_strategy_completion "$1"
+	fi
+}
+
+#--------------------------------------------------------------------#
+# History Suggestion Strategy                                        #
+#--------------------------------------------------------------------#
 # Suggests the most recent history item that matches the given
 # prefix.
 #
 
-_zsh_autosuggest_strategy_default() {
+_zsh_autosuggest_strategy_history() {
 	# Reset options to defaults and enable LOCAL_OPTIONS
 	emulate -L zsh
 

From f63afd5969a7c4fe098f686c005dee8d07b6b058 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 16 May 2018 16:29:01 -0600
Subject: [PATCH 05/47] Fix async pty name option spec

---
 spec/options/async_zpty_name_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/spec/options/async_zpty_name_spec.rb b/spec/options/async_zpty_name_spec.rb
index 407ee70..60be875 100644
--- a/spec/options/async_zpty_name_spec.rb
+++ b/spec/options/async_zpty_name_spec.rb
@@ -3,7 +3,7 @@ context 'when async suggestions are enabled' do
 
   describe 'the zpty for async suggestions' do
     it 'is created with the default name' do
-      session.run_command('zpty -t zsh_autosuggest_pty &>/dev/null; echo $?')
+      session.run_command('zpty -t zsh_autosuggest_async_pty &>/dev/null; echo $?')
       wait_for { session.content }.to end_with("\n0")
     end
 

From 4cca26ec84e764b077175ae20c54c8d673061fc2 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Fri, 18 May 2018 15:05:45 -0600
Subject: [PATCH 06/47] Modify completion code to better fit our needs

Only need the first completion result
---
 src/strategies/completion.zsh | 124 ++++++++++++---------------------
 zsh-autosuggestions.zsh       | 125 ++++++++++++----------------------
 2 files changed, 85 insertions(+), 164 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index db948a0..aa87673 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -9,114 +9,74 @@
 _zsh_autosuggest_capture_completion() {
 	zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zsh -f -i
 
-	# line buffer for pty output
 	local line
 
 	setopt rcquotes
 	() {
-		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME source $1
-		repeat 4; do
-			zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line
-			[[ $line == ok* ]] && return
-		done
-		echo 'error initializing.' >&2
-		exit 2
+		# Initialize the pty env, blocking until null byte is seen
+		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "source $1"
+		zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
 	} =( <<< '
-	# no prompt!
-	PROMPT=
-	# load completion system
+	exec 2>/dev/null # Silence any error messages
+
 	autoload compinit
 	compinit -d ~/.zcompdump_autosuggestions
-	# never run a command
-	bindkey ''^M'' undefined
-	bindkey ''^J'' undefined
-	bindkey ''^I'' complete-word
-	# send a line with null-byte at the end before and after completions are output
-	null-line () {
-		echo -E - $''\0''
-	}
-	compprefuncs=( null-line )
-	comppostfuncs=( null-line exit )
-	# never group stuff!
+
+	# Exit as soon as completion is finished
+	comppostfuncs=( exit )
+
+	# Never group stuff!
 	zstyle '':completion:*'' list-grouped false
-	# don''t insert tab when attempting completion on empty line
-	zstyle '':completion:*'' insert-tab false
+
 	# no list separator, this saves some stripping later on
 	zstyle '':completion:*'' list-separator ''''
+
 	# we use zparseopts
 	zmodload zsh/zutil
+
 	# override compadd (this our hook)
 	compadd () {
-		# check if any of -O, -A or -D are given
+		# Just delegate and leave if any of -O, -A or -D are given
 		if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then
-			# if that is the case, just delegate and leave
 			builtin compadd "$@"
 			return $?
 		fi
-		# ok, this concerns us!
-		# echo -E - got this: "$@"
-		# be careful with namespacing here, we don''t want to mess with stuff that
-		# should be passed to compadd!
-		typeset -a __hits __dscr __tmp
-		# do we have a description parameter?
-		# note we don''t use zparseopts here because of combined option parameters
-		# with arguments like -default- confuse it.
-		if (( $@[(I)-d] )); then # kind of a hack, $+@[(r)-d] doesn''t work because of line noise overload
-			# next param after -d
-			__tmp=${@[$[${@[(i)-d]}+1]]}
-			# description can be given as an array parameter name, or inline () array
-			if [[ $__tmp == \(* ]]; then
-				eval "__dscr=$__tmp"
-			else
-				__dscr=( "${(@P)__tmp}" )
-			fi
-		fi
-		# capture completions by injecting -A parameter into the compadd call.
-		# this takes care of matching for us.
-		builtin compadd -A __hits -D __dscr "$@"
-		# JESUS CHRIST IT TOOK ME FOREVER TO FIGURE OUT THIS OPTION WAS SET AND WAS MESSING WITH MY SHIT HERE
+
 		setopt localoptions norcexpandparam extendedglob
-		# extract prefixes and suffixes from compadd call. we can''t do zsh''s cool
+
+		typeset -a __hits
+
+		# Capture completions by injecting -A parameter into the compadd call.
+		# This takes care of matching for us.
+		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
-		# append / to directories? we are only emulating -f in a half-assed way
-		# here, but it''s better than nothing.
-		integer dirsuf=0
-		# don''t be fooled by -default- >.>
-		if [[ -z $hsuf && "${${@//-default-/}% -# *}" == *-[[:alnum:]]#f* ]]; then
-			dirsuf=1
-		fi
-		# just drop
-		[[ -n $__hits ]] || return
-		# this is the point where we have all matches in $__hits and all
-		# descriptions in $__dscr!
-		# display all matches
-		local dsuf dscr
-		for i in {1..$#__hits}; do
-			# add a dir suffix?
-			(( dirsuf )) && [[ -d $__hits[$i] ]] && dsuf=/ || dsuf=
-			# description to be displayed afterwards
-			(( $#__dscr >= $i )) && dscr=" -- ${${__dscr[$i]}##$__hits[$i] #}" || dscr=
-			echo -E - $IPREFIX$apre$hpre$__hits[$i]$dsuf$hsuf$asuf
-		done
-	}
-	# signal success!
-	echo ok')
 
+		# Print the first match
+		echo -nE - $''\0''$IPREFIX$apre$hpre$__hits[1]$dsuf$hsuf$asuf$''\0''
+	}
+
+	# Signal setup completion by sending null byte
+	echo $''\0''
+	')
+
+	# Send the string and a tab to trigger completion
 	zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "$*"$'\t'
 
-	integer tog=0
-	# read from the pty, and parse linewise
-	while zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME; do :; done | while IFS= read -r line; do
-		if [[ $line == *$'\0\r' ]]; then
-			(( tog++ )) && return 0 || continue
-		fi
-		# display between toggles
-		(( tog )) && echo -E - $line
-	done
+	# Read up to the start of the first result
+	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
 
-	return 2
+	# Read the first result
+	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
+
+	# Print it, removing the trailing null byte
+	echo -E - ${line%$'\0'}
 }
 
 _zsh_autosuggest_strategy_completion() {
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 41336a7..a2134da 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -506,114 +506,75 @@ zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 _zsh_autosuggest_capture_completion() {
 	zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zsh -f -i
 
-	# line buffer for pty output
 	local line
 
 	setopt rcquotes
 	() {
-		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME source $1
-		repeat 4; do
-			zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line
-			[[ $line == ok* ]] && return
-		done
-		echo 'error initializing.' >&2
-		exit 2
+		# Setup, blocking until null byte to signal completion
+		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "source $1"
+		zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
 	} =( <<< '
-	# no prompt!
-	PROMPT=
-	# load completion system
+	exec 2>/dev/null # Silence any error messages
+
 	autoload compinit
 	compinit -d ~/.zcompdump_autosuggestions
-	# never run a command
-	bindkey ''^M'' undefined
-	bindkey ''^J'' undefined
-	bindkey ''^I'' complete-word
-	# send a line with null-byte at the end before and after completions are output
-	null-line () {
-		echo -E - $''\0''
-	}
-	compprefuncs=( null-line )
-	comppostfuncs=( null-line exit )
-	# never group stuff!
+
+	# Exit as soon as completion is finished
+	comppostfuncs=( exit )
+
+	# Never group stuff!
 	zstyle '':completion:*'' list-grouped false
-	# don''t insert tab when attempting completion on empty line
-	zstyle '':completion:*'' insert-tab false
+
 	# no list separator, this saves some stripping later on
 	zstyle '':completion:*'' list-separator ''''
+
 	# we use zparseopts
 	zmodload zsh/zutil
+
 	# override compadd (this our hook)
 	compadd () {
-		# check if any of -O, -A or -D are given
+		# Just delegate and leave if any of -O, -A or -D are given
 		if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then
-			# if that is the case, just delegate and leave
 			builtin compadd "$@"
 			return $?
 		fi
-		# ok, this concerns us!
-		# echo -E - got this: "$@"
-		# be careful with namespacing here, we don''t want to mess with stuff that
-		# should be passed to compadd!
-		typeset -a __hits __dscr __tmp
-		# do we have a description parameter?
-		# note we don''t use zparseopts here because of combined option parameters
-		# with arguments like -default- confuse it.
-		if (( $@[(I)-d] )); then # kind of a hack, $+@[(r)-d] doesn''t work because of line noise overload
-			# next param after -d
-			__tmp=${@[$[${@[(i)-d]}+1]]}
-			# description can be given as an array parameter name, or inline () array
-			if [[ $__tmp == \(* ]]; then
-				eval "__dscr=$__tmp"
-			else
-				__dscr=( "${(@P)__tmp}" )
-			fi
-		fi
-		# capture completions by injecting -A parameter into the compadd call.
-		# this takes care of matching for us.
-		builtin compadd -A __hits -D __dscr "$@"
-		# JESUS CHRIST IT TOOK ME FOREVER TO FIGURE OUT THIS OPTION WAS SET AND WAS MESSING WITH MY SHIT HERE
+
 		setopt localoptions norcexpandparam extendedglob
-		# extract prefixes and suffixes from compadd call. we can''t do zsh''s cool
+
+		typeset -a __hits
+
+		# Capture completions by injecting -A parameter into the compadd call.
+		# This takes care of matching for us.
+		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
-		# append / to directories? we are only emulating -f in a half-assed way
-		# here, but it''s better than nothing.
-		integer dirsuf=0
-		# don''t be fooled by -default- >.>
-		if [[ -z $hsuf && "${${@//-default-/}% -# *}" == *-[[:alnum:]]#f* ]]; then
-			dirsuf=1
-		fi
-		# just drop
-		[[ -n $__hits ]] || return
-		# this is the point where we have all matches in $__hits and all
-		# descriptions in $__dscr!
-		# display all matches
-		local dsuf dscr
-		for i in {1..$#__hits}; do
-			# add a dir suffix?
-			(( dirsuf )) && [[ -d $__hits[$i] ]] && dsuf=/ || dsuf=
-			# description to be displayed afterwards
-			(( $#__dscr >= $i )) && dscr=" -- ${${__dscr[$i]}##$__hits[$i] #}" || dscr=
-			echo -E - $IPREFIX$apre$hpre$__hits[$i]$dsuf$hsuf$asuf
-		done
-	}
-	# signal success!
-	echo ok')
 
+		# Print the first match
+		echo -nE - $''\0''$IPREFIX$apre$hpre$__hits[1]$dsuf$hsuf$asuf$''\0''
+	}
+
+	# Signal setup completion by sending null byte
+	echo $''\0''
+	')
+
+
+	# Send the string and a tab to trigger completion
 	zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "$*"$'\t'
 
-	integer tog=0
-	# read from the pty, and parse linewise
-	while zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME; do :; done | while IFS= read -r line; do
-		if [[ $line == *$'\0\r' ]]; then
-			(( tog++ )) && return 0 || continue
-		fi
-		# display between toggles
-		(( tog )) && echo -E - $line
-	done
+	# Read up to the start of the first result
+	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
 
-	return 2
+	# Read the first result
+	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
+
+	# Print it, removing the trailing null byte
+	echo -E - ${line%$'\0'}
 }
 
 _zsh_autosuggest_strategy_completion() {

From 0a548c62f4f57bb68cb6c45ff75b4781bb39b451 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Fri, 18 May 2018 15:24:48 -0600
Subject: [PATCH 07/47] Forgot to make after small tweak

---
 zsh-autosuggestions.zsh | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index a2134da..c29eb9e 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -510,7 +510,7 @@ _zsh_autosuggest_capture_completion() {
 
 	setopt rcquotes
 	() {
-		# Setup, blocking until null byte to signal completion
+		# Initialize the pty env, blocking until null byte is seen
 		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "source $1"
 		zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
 	} =( <<< '
@@ -563,7 +563,6 @@ _zsh_autosuggest_capture_completion() {
 	echo $''\0''
 	')
 
-
 	# Send the string and a tab to trigger completion
 	zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "$*"$'\t'
 

From 6ffaec725a29ff2a199ceb173f72eadc42254582 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Tue, 22 May 2018 16:13:56 -0600
Subject: [PATCH 08/47] Allow completion suggestions from current shell

The `zsh -f` running in the PTY doesn't know about the non-exported
variables and functions defined in the original shell, thus can't make
suggestions for them. Run local functions in the PTY instead of a new
`zsh` process.

We have to handle things differently based on whether zle is active or
not (async vs. sync mode).
---
 src/strategies/completion.zsh | 110 +++++++++++++++++++---------------
 zsh-autosuggestions.zsh       | 110 +++++++++++++++++++---------------
 2 files changed, 124 insertions(+), 96 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index aa87673..e8aac6c 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -3,90 +3,104 @@
 # Completion Suggestion Strategy                                     #
 #--------------------------------------------------------------------#
 # Fetches suggestions from zsh's completion engine
+# Based on https://github.com/Valodim/zsh-capture-completion
 #
 
-# Big thanks to https://github.com/Valodim/zsh-capture-completion
-_zsh_autosuggest_capture_completion() {
-	zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zsh -f -i
-
-	local line
-
-	setopt rcquotes
-	() {
-		# Initialize the pty env, blocking until null byte is seen
-		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "source $1"
-		zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
-	} =( <<< '
-	exec 2>/dev/null # Silence any error messages
-
-	autoload compinit
-	compinit -d ~/.zcompdump_autosuggestions
-
-	# Exit as soon as completion is finished
-	comppostfuncs=( exit )
+_zsh_autosuggest_capture_setup() {
+	zmodload zsh/zutil # For `zparseopts`
 
 	# Never group stuff!
-	zstyle '':completion:*'' list-grouped false
+	zstyle ':completion:*' list-grouped false
 
-	# no list separator, this saves some stripping later on
-	zstyle '':completion:*'' list-separator ''''
+	# No list separator, this saves some stripping later on
+	zstyle ':completion:*' list-separator ''
 
-	# we use zparseopts
-	zmodload zsh/zutil
-
-	# override compadd (this our hook)
+	# 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
 
-		setopt localoptions norcexpandparam extendedglob
-
-		typeset -a __hits
-
 		# 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.
+		# 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''
+		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 "$@"
 	}
 
-	# Signal setup completion by sending null byte
-	echo $''\0''
-	')
+	# Open zle with buffer set so we can capture completions for it
+	vared BUFFERCONTENT
+}
 
-	# Send the string and a tab to trigger completion
-	zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "$*"$'\t'
+_zsh_autosuggest_capture_completion() {
+	typeset -g completion
+	local line
 
-	# Read up to the start of the first result
+	# 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'
-
-	# Read the first result
 	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
+	completion="${line%$'\0'}"
 
-	# Print it, removing the trailing null byte
-	echo -E - ${line%$'\0'}
+	# Destroy the pty
+	zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
 }
 
 _zsh_autosuggest_strategy_completion() {
-	typeset -g suggestion=$(_zsh_autosuggest_capture_completion "$1" | head -n 1)
+	typeset -g suggestion completion
 
-	# Strip the trailing carriage return
-	suggestion="${suggestion%$'\r'}"
+	# 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 [[ "$suggestion" != "${1[$i,-1]}"* ]]; do ((i++)); done
-	suggestion="${1[1,$i-1]}$suggestion"
+	while [[ "$completion" != "${1[$i,-1]}"* ]]; do ((i++)); done
+	suggestion="${1[1,$i-1]}$completion"
 }
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index c29eb9e..40f6f66 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -500,92 +500,106 @@ zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 # Completion Suggestion Strategy                                     #
 #--------------------------------------------------------------------#
 # Fetches suggestions from zsh's completion engine
+# Based on https://github.com/Valodim/zsh-capture-completion
 #
 
-# Big thanks to https://github.com/Valodim/zsh-capture-completion
-_zsh_autosuggest_capture_completion() {
-	zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zsh -f -i
-
-	local line
-
-	setopt rcquotes
-	() {
-		# Initialize the pty env, blocking until null byte is seen
-		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "source $1"
-		zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
-	} =( <<< '
-	exec 2>/dev/null # Silence any error messages
-
-	autoload compinit
-	compinit -d ~/.zcompdump_autosuggestions
-
-	# Exit as soon as completion is finished
-	comppostfuncs=( exit )
+_zsh_autosuggest_capture_setup() {
+	zmodload zsh/zutil # For `zparseopts`
 
 	# Never group stuff!
-	zstyle '':completion:*'' list-grouped false
+	zstyle ':completion:*' list-grouped false
 
-	# no list separator, this saves some stripping later on
-	zstyle '':completion:*'' list-separator ''''
+	# No list separator, this saves some stripping later on
+	zstyle ':completion:*' list-separator ''
 
-	# we use zparseopts
-	zmodload zsh/zutil
-
-	# override compadd (this our hook)
+	# 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
 
-		setopt localoptions norcexpandparam extendedglob
-
-		typeset -a __hits
-
 		# 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.
+		# 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''
+		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 "$@"
 	}
 
-	# Signal setup completion by sending null byte
-	echo $''\0''
-	')
+	# Open zle with buffer set so we can capture completions for it
+	vared BUFFERCONTENT
+}
 
-	# Send the string and a tab to trigger completion
-	zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME "$*"$'\t'
+_zsh_autosuggest_capture_completion() {
+	typeset -g completion
+	local line
 
-	# Read up to the start of the first result
+	# 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'
-
-	# Read the first result
 	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
+	completion="${line%$'\0'}"
 
-	# Print it, removing the trailing null byte
-	echo -E - ${line%$'\0'}
+	# Destroy the pty
+	zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
 }
 
 _zsh_autosuggest_strategy_completion() {
-	typeset -g suggestion=$(_zsh_autosuggest_capture_completion "$1" | head -n 1)
+	typeset -g suggestion completion
 
-	# Strip the trailing carriage return
-	suggestion="${suggestion%$'\r'}"
+	# 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 [[ "$suggestion" != "${1[$i,-1]}"* ]]; do ((i++)); done
-	suggestion="${1[1,$i-1]}$suggestion"
+	while [[ "$completion" != "${1[$i,-1]}"* ]]; do ((i++)); done
+	suggestion="${1[1,$i-1]}$completion"
 }
 
 #--------------------------------------------------------------------#

From 3dbd9afaec0a5f3c1c63779d665b1dcf93b60c7f Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Tue, 22 May 2018 17:16:45 -0600
Subject: [PATCH 09/47] Fix completion strategy killing other pty's

Only a problem in synchronous mode
---
 spec/integrations/client_zpty_spec.rb | 21 +++++++++++++++++----
 src/strategies/completion.zsh         | 10 ++++++++++
 zsh-autosuggestions.zsh               | 10 ++++++++++
 3 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/spec/integrations/client_zpty_spec.rb b/spec/integrations/client_zpty_spec.rb
index 8f1550e..e364759 100644
--- a/spec/integrations/client_zpty_spec.rb
+++ b/spec/integrations/client_zpty_spec.rb
@@ -1,10 +1,23 @@
 describe 'a running zpty command' do
   let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } }
 
-  it 'is not affected by running zsh-autosuggestions' do
-    sleep 1 # Give a little time for precmd hooks to run
-    session.run_command('zpty -t kitty; echo $?')
+  context 'when sourcing the plugin' do
+    it 'is not affected' do
+      sleep 1 # Give a little time for precmd hooks to run
+      session.run_command('zpty -t kitty; echo $?')
 
-    wait_for { session.content }.to end_with("\n0")
+      wait_for { session.content }.to end_with("\n0")
+    end
+  end
+
+  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
diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index e8aac6c..6517444 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -9,6 +9,16 @@
 _zsh_autosuggest_capture_setup() {
 	zmodload zsh/zutil # For `zparseopts`
 
+	# 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
 
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 40f6f66..644bacf 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -506,6 +506,16 @@ zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 _zsh_autosuggest_capture_setup() {
 	zmodload zsh/zutil # For `zparseopts`
 
+	# 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
 

From cf458d2a3bcc494a1ab63fdb542f528b42b71546 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 23 May 2018 14:50:56 -0600
Subject: [PATCH 10/47] Fix completion suggestions when compinit is not enabled

Need to make sure compinit is called in the pty or the shell hangs
---
 src/strategies/completion.zsh | 2 ++
 zsh-autosuggestions.zsh       | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 6517444..c8b176b 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -9,6 +9,8 @@
 _zsh_autosuggest_capture_setup() {
 	zmodload zsh/zutil # For `zparseopts`
 
+	autoload compinit && compinit
+
 	# 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
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 644bacf..756cfbc 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -506,6 +506,8 @@ zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 _zsh_autosuggest_capture_setup() {
 	zmodload zsh/zutil # For `zparseopts`
 
+	autoload compinit && compinit
+
 	# 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

From 7d19f8f9b2bf03b92372f95d37f5b55a6e941684 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 23 May 2018 22:04:16 -0600
Subject: [PATCH 11/47] Rename default spec to history spec

---
 spec/strategies/{default_spec.rb => history_spec.rb} | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
 rename spec/strategies/{default_spec.rb => history_spec.rb} (85%)

diff --git a/spec/strategies/default_spec.rb b/spec/strategies/history_spec.rb
similarity index 85%
rename from spec/strategies/default_spec.rb
rename to spec/strategies/history_spec.rb
index 89321f3..f8ae526 100644
--- a/spec/strategies/default_spec.rb
+++ b/spec/strategies/history_spec.rb
@@ -1,6 +1,6 @@
 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
     with_history('ls foo', 'ls bar', 'echo baz') do
       session.send_string('ls')

From 973205005cca59f0701e6379474bf24eef805ac1 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 23 May 2018 22:04:47 -0600
Subject: [PATCH 12/47] Add spec for `completion` strategy

---
 spec/strategies/completion_spec.rb     | 30 ++++++++++++++++++++++++++
 spec/strategies/match_prev_cmd_spec.rb |  2 +-
 src/strategies/completion.zsh          |  5 ++++-
 zsh-autosuggestions.zsh                |  5 ++++-
 4 files changed, 39 insertions(+), 3 deletions(-)
 create mode 100644 spec/strategies/completion_spec.rb

diff --git a/spec/strategies/completion_spec.rb b/spec/strategies/completion_spec.rb
new file mode 100644
index 0000000..62cf0e5
--- /dev/null
+++ b/spec/strategies/completion_spec.rb
@@ -0,0 +1,30 @@
+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
+    before do
+      skip 'Async mode not supported below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
+    end
+
+    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
+
diff --git a/spec/strategies/match_prev_cmd_spec.rb b/spec/strategies/match_prev_cmd_spec.rb
index f1596ba..5a143b8 100644
--- a/spec/strategies/match_prev_cmd_spec.rb
+++ b/spec/strategies/match_prev_cmd_spec.rb
@@ -1,6 +1,6 @@
 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'] }
 
   it 'suggests the last matching history entry after the previous command' do
diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index c8b176b..0808575 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -9,7 +9,10 @@
 _zsh_autosuggest_capture_setup() {
 	zmodload zsh/zutil # For `zparseopts`
 
-	autoload compinit && compinit
+	# 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
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 756cfbc..da10235 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -506,7 +506,10 @@ zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 _zsh_autosuggest_capture_setup() {
 	zmodload zsh/zutil # For `zparseopts`
 
-	autoload compinit && compinit
+	# 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

From 949c37419544e6fab313d5139723fa315d645cab Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Thu, 24 May 2018 16:45:20 -0600
Subject: [PATCH 13/47] Fix `completion` strategy on older versions of zsh

`zpty -r` with a pattern seems to have some funky behavior on older
versions, giving unpredictable results
---
 src/strategies/completion.zsh | 8 +++++---
 zsh-autosuggestions.zsh       | 8 +++++---
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 0808575..a23b630 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -100,9 +100,11 @@ _zsh_autosuggest_capture_completion() {
 
 	# 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'
-	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
-	completion="${line%$'\0'}"
+	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
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index da10235..6683262 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -597,9 +597,11 @@ _zsh_autosuggest_capture_completion() {
 
 	# 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'
-	zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0'
-	completion="${line%$'\0'}"
+	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

From bcbdad83e940917db9bbd0afd62057229446c00f Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 6 Jun 2018 21:49:03 -0600
Subject: [PATCH 14/47] Support fallback strategies by setting array in config

---
 Makefile                                  |  1 +
 README.md                                 | 11 ++++--
 spec/integrations/zle_input_stack_spec.rb |  2 +-
 spec/options/strategy_spec.rb             | 45 ++++++++++++++++-----
 src/async.zsh                             |  2 +-
 src/config.zsh                            |  4 +-
 src/fetch.zsh                             | 23 +++++++++++
 src/strategies/default.zsh                | 17 --------
 src/widgets.zsh                           |  2 +-
 zsh-autosuggestions.zsh                   | 48 +++++++++++++----------
 10 files changed, 100 insertions(+), 55 deletions(-)
 create mode 100644 src/fetch.zsh
 delete mode 100644 src/strategies/default.zsh

diff --git a/Makefile b/Makefile
index d5d162c..b89ff04 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@ SRC_FILES := \
 	$(SRC_DIR)/highlight.zsh \
 	$(SRC_DIR)/widgets.zsh \
 	$(SRC_DIR)/strategies/*.zsh \
+	$(SRC_DIR)/fetch.zsh \
 	$(SRC_DIR)/async.zsh \
 	$(SRC_DIR)/start.zsh
 
diff --git a/README.md b/README.md
index 4ad07d8..5039b53 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 _[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
 
@@ -39,10 +39,13 @@ Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion
 
 ### 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.
-- `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`.
+- `history`: Chooses the most recent match from history.
+- `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
diff --git a/spec/integrations/zle_input_stack_spec.rb b/spec/integrations/zle_input_stack_spec.rb
index 8a2c990..12cfbc7 100644
--- a/spec/integrations/zle_input_stack_spec.rb
+++ b/spec/integrations/zle_input_stack_spec.rb
@@ -2,7 +2,7 @@ describe 'using `zle -U`' do
   let(:before_sourcing) do
     -> do
       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')
     end
   end
diff --git a/spec/options/strategy_spec.rb b/spec/options/strategy_spec.rb
index c9f01e1..378d01e 100644
--- a/spec/options/strategy_spec.rb
+++ b/spec/options/strategy_spec.rb
@@ -1,20 +1,45 @@
 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
-    session.send_string('e')
-    wait_for { session.content }.to eq('echo foo')
+  let(:options) { [ history_strategy ] }
+
+  it 'by default is determined by calling the `history` strategy function' do
+    session.send_string('h')
+    wait_for { session.content }.to eq('history')
   end
 
-  context 'when ZSH_AUTOSUGGEST_STRATEGY is set' do
+  context 'when ZSH_AUTOSUGGEST_STRATEGY is set to an array' do
     let(:options) { [
-      '_zsh_autosuggest_strategy_custom() { suggestion="echo foo" }',
-      'ZSH_AUTOSUGGEST_STRATEGY=custom'
+      foobar_strategy,
+      foobaz_strategy,
+      'ZSH_AUTOSUGGEST_STRATEGY=(foobar foobaz)'
     ] }
 
-    it 'is determined by calling the specified strategy function' do
-      session.send_string('e')
-      wait_for { session.content }.to eq('echo foo')
+    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
+
+  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
+
diff --git a/src/async.zsh b/src/async.zsh
index 62e3329..dd54c24 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -35,7 +35,7 @@ _zsh_autosuggest_async_server() {
 		# Run suggestion search in the background
 		(
 			local suggestion
-			_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query"
+			_zsh_autosuggest_fetch_suggestion "$query"
 			echo -n -E "$suggestion"$'\0'
 		) &
 
diff --git a/src/config.zsh b/src/config.zsh
index 9b3dbae..4598191 100644
--- a/src/config.zsh
+++ b/src/config.zsh
@@ -11,7 +11,9 @@ ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
 # Prefix to use when saving original versions of bound widgets
 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
 ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
diff --git a/src/fetch.zsh b/src/fetch.zsh
new file mode 100644
index 0000000..f94e66d
--- /dev/null
+++ b/src/fetch.zsh
@@ -0,0 +1,23 @@
+
+#--------------------------------------------------------------------#
+# 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
+
+	# 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
+}
diff --git a/src/strategies/default.zsh b/src/strategies/default.zsh
deleted file mode 100644
index 68617ff..0000000
--- a/src/strategies/default.zsh
+++ /dev/null
@@ -1,17 +0,0 @@
-
-#--------------------------------------------------------------------#
-# Default Suggestion Strategy                                        #
-#--------------------------------------------------------------------#
-# Will provide suggestions from your history. If no matches are found
-# in history, will provide a suggestion from the completion engine.
-#
-
-_zsh_autosuggest_strategy_default() {
-	typeset -g suggestion
-
-	_zsh_autosuggest_strategy_history "$1"
-
-	if [[ -z "$suggestion" ]]; then
-		_zsh_autosuggest_strategy_completion "$1"
-	fi
-}
diff --git a/src/widgets.zsh b/src/widgets.zsh
index 87bb62e..89af395 100644
--- a/src/widgets.zsh
+++ b/src/widgets.zsh
@@ -97,7 +97,7 @@ _zsh_autosuggest_fetch() {
 		_zsh_autosuggest_async_request "$BUFFER"
 	else
 		local suggestion
-		_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER"
+		_zsh_autosuggest_fetch_suggestion "$BUFFER"
 		_zsh_autosuggest_suggest "$suggestion"
 	fi
 }
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 6683262..02d4b2f 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -47,7 +47,9 @@ ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
 # Prefix to use when saving original versions of bound widgets
 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
 ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
@@ -381,7 +383,7 @@ _zsh_autosuggest_fetch() {
 		_zsh_autosuggest_async_request "$BUFFER"
 	else
 		local suggestion
-		_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER"
+		_zsh_autosuggest_fetch_suggestion "$BUFFER"
 		_zsh_autosuggest_suggest "$suggestion"
 	fi
 }
@@ -619,23 +621,6 @@ _zsh_autosuggest_strategy_completion() {
 	suggestion="${1[1,$i-1]}$completion"
 }
 
-#--------------------------------------------------------------------#
-# Default Suggestion Strategy                                        #
-#--------------------------------------------------------------------#
-# Will provide suggestions from your history. If no matches are found
-# in history, will provide a suggestion from the completion engine.
-#
-
-_zsh_autosuggest_strategy_default() {
-	typeset -g suggestion
-
-	_zsh_autosuggest_strategy_history "$1"
-
-	if [[ -z "$suggestion" ]]; then
-		_zsh_autosuggest_strategy_completion "$1"
-	fi
-}
-
 #--------------------------------------------------------------------#
 # History Suggestion Strategy                                        #
 #--------------------------------------------------------------------#
@@ -720,6 +705,29 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
 	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
+
+	# 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                                                              #
 #--------------------------------------------------------------------#
@@ -756,7 +764,7 @@ _zsh_autosuggest_async_server() {
 		# Run suggestion search in the background
 		(
 			local suggestion
-			_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query"
+			_zsh_autosuggest_fetch_suggestion "$query"
 			echo -n -E "$suggestion"$'\0'
 		) &
 

From 4e466f0e4e412607d6354e2be716d4e4efdd4351 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sun, 10 Jun 2018 22:39:58 -0600
Subject: [PATCH 15/47] Support widgets starting with dashes (ex: `-a-widget`)

Fixes #337
---
 src/bind.zsh            | 2 +-
 zsh-autosuggestions.zsh | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/bind.zsh b/src/bind.zsh
index 42a0dd0..f538379 100644
--- a/src/bind.zsh
+++ b/src/bind.zsh
@@ -69,7 +69,7 @@ _zsh_autosuggest_bind_widget() {
 	}"
 
 	# Create the bound widget
-	zle -N $widget _zsh_autosuggest_bound_${bind_count}_$widget
+	zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget
 }
 
 # Map all configured widgets to the right autosuggest widgets
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index e2e06be..0a1f9c6 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -206,7 +206,7 @@ _zsh_autosuggest_bind_widget() {
 	}"
 
 	# Create the bound widget
-	zle -N $widget _zsh_autosuggest_bound_${bind_count}_$widget
+	zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget
 }
 
 # Map all configured widgets to the right autosuggest widgets

From b0ffc34fb83136b29e6ece5028f0fda6b1e00ee7 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sun, 10 Jun 2018 23:35:22 -0600
Subject: [PATCH 16/47] completion should be a local var

---
 src/strategies/completion.zsh | 3 ++-
 zsh-autosuggestions.zsh       | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index a23b630..847f22b 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -111,7 +111,8 @@ _zsh_autosuggest_capture_completion() {
 }
 
 _zsh_autosuggest_strategy_completion() {
-	typeset -g suggestion completion
+	typeset -g suggestion
+	local completion
 
 	# Fetch the first completion result
 	_zsh_autosuggest_capture_completion "$1"
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 02d4b2f..823f541 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -610,7 +610,8 @@ _zsh_autosuggest_capture_completion() {
 }
 
 _zsh_autosuggest_strategy_completion() {
-	typeset -g suggestion completion
+	typeset -g suggestion
+	local completion
 
 	# Fetch the first completion result
 	_zsh_autosuggest_capture_completion "$1"

From 9cb010151204926aee24e71d54879092d89c66b4 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Mon, 11 Jun 2018 02:06:18 -0600
Subject: [PATCH 17/47] Refactor async mode to no longer use zpty

See technique used in `fast-syntax-highlighting`:
- https://github.com/zdharma/fast-syntax-highlighting/commit/ca2e18bbc9e27b9264206c257d2ab68838162c70
- http://www.zsh.org/mla/users/2018/msg00424.html
---
 spec/async_spec.rb                    |  53 -----------
 spec/integrations/client_zpty_spec.rb |  10 ---
 spec/options/async_zpty_name_spec.rb  |  19 ----
 spec/terminal_session.rb              |   4 -
 src/async.zsh                         | 118 +++++-------------------
 src/start.zsh                         |   4 -
 src/widgets.zsh                       |   2 +-
 zsh-autosuggestions.zsh               | 124 +++++---------------------
 8 files changed, 46 insertions(+), 288 deletions(-)
 delete mode 100644 spec/integrations/client_zpty_spec.rb
 delete mode 100644 spec/options/async_zpty_name_spec.rb

diff --git a/spec/async_spec.rb b/spec/async_spec.rb
index 152adde..9405fb2 100644
--- a/spec/async_spec.rb
+++ b/spec/async_spec.rb
@@ -1,8 +1,4 @@
 context 'with asynchronous suggestions enabled' do
-  before do
-    skip 'Async mode not supported below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
-  end
-
   let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] }
 
   describe '`up-line-or-beginning-search`' do
@@ -30,55 +26,6 @@ context 'with asynchronous suggestions enabled' do
       end
     end
   end
-
-  it 'should not add extra carriage returns before newlines' do
-    session.
-      send_string('echo "').
-      send_keys('escape').
-      send_keys('enter').
-      send_string('"').
-      send_keys('enter')
-
-    session.clear_screen
-
-    session.send_string('echo')
-    wait_for { session.content }.to eq("echo \"\n\"")
-  end
-
-  it 'should treat carriage returns and newlines as separate characters' do
-    session.
-      send_string('echo "').
-      send_keys('C-v').
-      send_keys('enter').
-      send_string('foo"').
-      send_keys('enter')
-
-    session.
-      send_string('echo "').
-      send_keys('control').
-      send_keys('enter').
-      send_string('bar"').
-      send_keys('enter')
-
-    session.clear_screen
-
-    session.
-      send_string('echo "').
-      send_keys('C-v').
-      send_keys('enter')
-
-    wait_for { session.content }.to eq('echo "^Mfoo"')
-  end
-
-  describe 'exiting a subshell' do
-    it 'should not cause error messages to be printed' do
-      session.run_command('$(exit)')
-
-      sleep 1
-
-      expect(session.content).to eq('$(exit)')
-    end
-  end
 end
 
 
diff --git a/spec/integrations/client_zpty_spec.rb b/spec/integrations/client_zpty_spec.rb
deleted file mode 100644
index 8f1550e..0000000
--- a/spec/integrations/client_zpty_spec.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-describe 'a running zpty command' do
-  let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } }
-
-  it 'is not affected by running zsh-autosuggestions' do
-    sleep 1 # Give a little time for precmd hooks to run
-    session.run_command('zpty -t kitty; echo $?')
-
-    wait_for { session.content }.to end_with("\n0")
-  end
-end
diff --git a/spec/options/async_zpty_name_spec.rb b/spec/options/async_zpty_name_spec.rb
deleted file mode 100644
index 407ee70..0000000
--- a/spec/options/async_zpty_name_spec.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-context 'when async suggestions are enabled' do
-  let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] }
-
-  describe 'the zpty for async suggestions' do
-    it 'is created with the default name' do
-      session.run_command('zpty -t zsh_autosuggest_pty &>/dev/null; echo $?')
-      wait_for { session.content }.to end_with("\n0")
-    end
-
-    context 'when ZSH_AUTOSUGGEST_ASYNC_PTY_NAME is set' do
-      let(:options) { super() + ['ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=foo_pty'] }
-
-      it 'is created with the specified name' do
-        session.run_command('zpty -t foo_pty &>/dev/null; echo $?')
-        wait_for { session.content }.to end_with("\n0")
-      end
-    end
-  end
-end
diff --git a/spec/terminal_session.rb b/spec/terminal_session.rb
index f91ee6c..2d9468f 100644
--- a/spec/terminal_session.rb
+++ b/spec/terminal_session.rb
@@ -18,10 +18,6 @@ class TerminalSession
     tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'")
   end
 
-  def zsh_version
-    @zsh_version ||= Gem::Version.new(`#{ZSH_BIN} -c 'echo -n $ZSH_VERSION'`)
-  end
-
   def tmux_socket_name
     @tmux_socket_name ||= SecureRandom.hex(6)
   end
diff --git a/src/async.zsh b/src/async.zsh
index 62e3329..8111496 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -3,107 +3,33 @@
 # Async                                                              #
 #--------------------------------------------------------------------#
 
-# Zpty process is spawned running this function
-_zsh_autosuggest_async_server() {
-	emulate -R zsh
-
-	# 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
-	}
-
-	# Don't add any extra carriage returns
-	stty -onlcr
-
-	# Don't translate carriage returns to newlines
-	stty -icrnl
-
-	# Silence any error messages
-	exec 2>/dev/null
-
-	local last_pid
-
-	while IFS='' read -r -d $'\0' query; do
-		# Kill last bg process
-		kill -KILL $last_pid &>/dev/null
-
-		# Run suggestion search in the background
-		(
-			local suggestion
-			_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query"
-			echo -n -E "$suggestion"$'\0'
-		) &
-
-		last_pid=$!
-	done
-}
-
 _zsh_autosuggest_async_request() {
-	# Write the query to the zpty process to fetch a suggestion
-	zpty -w -n $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "${1}"$'\0'
+	typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD
+
+	# Close the last fd to invalidate old suggestions
+	if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
+		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
+	fi
+
+	# Fork a process to fetch a suggestion and open a pipe to read from it
+	exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
+		local suggestion
+		_zsh_autosuggest_fetch_suggestion "$1"
+		echo -nE "$suggestion"
+	)
+
+	# When the fd is readable, call the response handler
+	zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
 }
 
-# Called when new data is ready to be read from the pty
+# Called when new data is ready to be read from the pipe
 # First arg will be fd ready for reading
 # Second arg will be passed in case of error
 _zsh_autosuggest_async_response() {
-	setopt LOCAL_OPTIONS EXTENDED_GLOB
+	# Read everything from the fd and give it as a suggestion
+	zle autosuggest-suggest -- "$(<&$1)"
 
-	local suggestion
-
-	zpty -rt $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME suggestion '*'$'\0' 2>/dev/null
-	zle autosuggest-suggest -- "${suggestion%%$'\0'##}"
-}
-
-_zsh_autosuggest_async_pty_create() {
-	# With newer versions of zsh, REPLY stores the fd to read from
-	typeset -h REPLY
-
-	# If we won't get a fd back from zpty, try to guess it
-	if (( ! $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD )); then
-		integer -l zptyfd
-		exec {zptyfd}>&1  # Open a new file descriptor (above 10).
-		exec {zptyfd}>&-  # Close it so it's free to be used by zpty.
-	fi
-
-	# Fork a zpty process running the server function
-	zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME _zsh_autosuggest_async_server
-
-	# Store the fd so we can remove the handler later
-	if (( REPLY )); then
-		_ZSH_AUTOSUGGEST_PTY_FD=$REPLY
-	else
-		_ZSH_AUTOSUGGEST_PTY_FD=$zptyfd
-	fi
-
-	# Set up input handler from the zpty
-	zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response
-}
-
-_zsh_autosuggest_async_pty_destroy() {
-	# Remove the input handler
-	zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
-
-	# Destroy the zpty
-	zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
-}
-
-_zsh_autosuggest_async_pty_recreate() {
-	_zsh_autosuggest_async_pty_destroy
-	_zsh_autosuggest_async_pty_create
-}
-
-_zsh_autosuggest_async_start() {
-	typeset -g _ZSH_AUTOSUGGEST_PTY_FD
-
-	_zsh_autosuggest_feature_detect_zpty_returns_fd
-	_zsh_autosuggest_async_pty_recreate
-
-	# We recreate the pty to get a fresh list of history events
-	add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate
+	# Remove the handler and close the fd
+	zle -F "$1"
+	exec {1}<&-
 }
diff --git a/src/start.zsh b/src/start.zsh
index 6f48ab6..a73ee3b 100644
--- a/src/start.zsh
+++ b/src/start.zsh
@@ -14,10 +14,6 @@ _zsh_autosuggest_start() {
 	# zsh-syntax-highlighting widgets. This also allows modifications
 	# to the widget list variables to take effect on the next precmd.
 	add-zsh-hook precmd _zsh_autosuggest_bind_widgets
-
-	if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
-		_zsh_autosuggest_async_start
-	fi
 }
 
 # Start the autosuggestion widgets on the next precmd
diff --git a/src/widgets.zsh b/src/widgets.zsh
index 87bb62e..4cd8ca8 100644
--- a/src/widgets.zsh
+++ b/src/widgets.zsh
@@ -93,7 +93,7 @@ _zsh_autosuggest_modify() {
 
 # Fetch a new suggestion based on what's currently in the buffer
 _zsh_autosuggest_fetch() {
-	if zpty -t "$ZSH_AUTOSUGGEST_ASYNC_PTY_NAME" &>/dev/null; then
+	if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
 		_zsh_autosuggest_async_request "$BUFFER"
 	else
 		local suggestion
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 0a1f9c6..3106709 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -374,7 +374,7 @@ _zsh_autosuggest_modify() {
 
 # Fetch a new suggestion based on what's currently in the buffer
 _zsh_autosuggest_fetch() {
-	if zpty -t "$ZSH_AUTOSUGGEST_ASYNC_PTY_NAME" &>/dev/null; then
+	if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
 		_zsh_autosuggest_async_request "$BUFFER"
 	else
 		local suggestion
@@ -581,109 +581,35 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
 # Async                                                              #
 #--------------------------------------------------------------------#
 
-# Zpty process is spawned running this function
-_zsh_autosuggest_async_server() {
-	emulate -R zsh
-
-	# 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
-	}
-
-	# Don't add any extra carriage returns
-	stty -onlcr
-
-	# Don't translate carriage returns to newlines
-	stty -icrnl
-
-	# Silence any error messages
-	exec 2>/dev/null
-
-	local last_pid
-
-	while IFS='' read -r -d $'\0' query; do
-		# Kill last bg process
-		kill -KILL $last_pid &>/dev/null
-
-		# Run suggestion search in the background
-		(
-			local suggestion
-			_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query"
-			echo -n -E "$suggestion"$'\0'
-		) &
-
-		last_pid=$!
-	done
-}
-
 _zsh_autosuggest_async_request() {
-	# Write the query to the zpty process to fetch a suggestion
-	zpty -w -n $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "${1}"$'\0'
+	typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD
+
+	# Close the last fd to invalidate old suggestions
+	if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
+		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
+	fi
+
+	# Fork a process to fetch a suggestion and open a pipe to read from it
+	exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
+		local suggestion
+		_zsh_autosuggest_fetch_suggestion "$1"
+		echo -nE "$suggestion"
+	)
+
+	# When the fd is readable, call the response handler
+	zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
 }
 
-# Called when new data is ready to be read from the pty
+# Called when new data is ready to be read from the pipe
 # First arg will be fd ready for reading
 # Second arg will be passed in case of error
 _zsh_autosuggest_async_response() {
-	setopt LOCAL_OPTIONS EXTENDED_GLOB
+	# Read everything from the fd and give it as a suggestion
+	zle autosuggest-suggest -- "$(<&$1)"
 
-	local suggestion
-
-	zpty -rt $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME suggestion '*'$'\0' 2>/dev/null
-	zle autosuggest-suggest -- "${suggestion%%$'\0'##}"
-}
-
-_zsh_autosuggest_async_pty_create() {
-	# With newer versions of zsh, REPLY stores the fd to read from
-	typeset -h REPLY
-
-	# If we won't get a fd back from zpty, try to guess it
-	if (( ! $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD )); then
-		integer -l zptyfd
-		exec {zptyfd}>&1  # Open a new file descriptor (above 10).
-		exec {zptyfd}>&-  # Close it so it's free to be used by zpty.
-	fi
-
-	# Fork a zpty process running the server function
-	zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME _zsh_autosuggest_async_server
-
-	# Store the fd so we can remove the handler later
-	if (( REPLY )); then
-		_ZSH_AUTOSUGGEST_PTY_FD=$REPLY
-	else
-		_ZSH_AUTOSUGGEST_PTY_FD=$zptyfd
-	fi
-
-	# Set up input handler from the zpty
-	zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response
-}
-
-_zsh_autosuggest_async_pty_destroy() {
-	# Remove the input handler
-	zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
-
-	# Destroy the zpty
-	zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
-}
-
-_zsh_autosuggest_async_pty_recreate() {
-	_zsh_autosuggest_async_pty_destroy
-	_zsh_autosuggest_async_pty_create
-}
-
-_zsh_autosuggest_async_start() {
-	typeset -g _ZSH_AUTOSUGGEST_PTY_FD
-
-	_zsh_autosuggest_feature_detect_zpty_returns_fd
-	_zsh_autosuggest_async_pty_recreate
-
-	# We recreate the pty to get a fresh list of history events
-	add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate
+	# Remove the handler and close the fd
+	zle -F "$1"
+	exec {1}<&-
 }
 
 #--------------------------------------------------------------------#
@@ -701,10 +627,6 @@ _zsh_autosuggest_start() {
 	# zsh-syntax-highlighting widgets. This also allows modifications
 	# to the widget list variables to take effect on the next precmd.
 	add-zsh-hook precmd _zsh_autosuggest_bind_widgets
-
-	if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
-		_zsh_autosuggest_async_start
-	fi
 }
 
 # Start the autosuggestion widgets on the next precmd

From 4a268da1df268fd29f3ff8f6e9309499656b473b Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Mon, 11 Jun 2018 02:39:00 -0600
Subject: [PATCH 18/47] Fix readme- async no longer uses zpty

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 4ad07d8..2636936 100644
--- a/README.md
+++ b/README.md
@@ -67,7 +67,7 @@ This can be useful when pasting large amount of text in the terminal, to avoid t
 
 ### Enable Asynchronous Mode
 
-As of `v0.4.0`, suggestions can be fetched asynchronously using the `zsh/zpty` module. To enable this behavior, set the `ZSH_AUTOSUGGEST_USE_ASYNC` variable (it can be set to anything).
+As of `v0.4.0`, suggestions can be fetched asynchronously. To enable this behavior, set the `ZSH_AUTOSUGGEST_USE_ASYNC` variable (it can be set to anything).
 
 
 ### Key Bindings

From cd81522b30d394dda40cff7b4e38fa056c2a2ba9 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Tue, 12 Jun 2018 23:45:29 -0600
Subject: [PATCH 19/47] Attempt to kill async worker process when new request
 comes in

See http://www.zsh.org/mla/users/2018/msg00432.html
---
 src/async.zsh           | 20 +++++++++++++++++---
 zsh-autosuggestions.zsh | 20 +++++++++++++++++---
 2 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/src/async.zsh b/src/async.zsh
index 8111496..ee39332 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -3,21 +3,35 @@
 # Async                                                              #
 #--------------------------------------------------------------------#
 
-_zsh_autosuggest_async_request() {
-	typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD
+zmodload zsh/system
 
-	# Close the last fd to invalidate old suggestions
+_zsh_autosuggest_async_request() {
+	typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
+
+	# If we've got a pending request, cancel it
 	if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
+		# Close the file descriptor
 		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
+
+		# Assume the child process created a new process group and send
+		# TERM to the group to attempt to kill all descendent processes
+		kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
 	fi
 
 	# Fork a process to fetch a suggestion and open a pipe to read from it
 	exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
+		# Tell parent process our pid
+		echo $sysparams[pid]
+
+		# Fetch and print the suggestion
 		local suggestion
 		_zsh_autosuggest_fetch_suggestion "$1"
 		echo -nE "$suggestion"
 	)
 
+	# Read the pid from the child process
+	read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD
+
 	# When the fd is readable, call the response handler
 	zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
 }
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 3106709..4c14666 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -581,21 +581,35 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
 # Async                                                              #
 #--------------------------------------------------------------------#
 
-_zsh_autosuggest_async_request() {
-	typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD
+zmodload zsh/system
 
-	# Close the last fd to invalidate old suggestions
+_zsh_autosuggest_async_request() {
+	typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
+
+	# If we've got a pending request, cancel it
 	if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
+		# Close the file descriptor
 		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
+
+		# Assume the child process created a new process group and send
+		# TERM to the group to attempt to kill all descendent processes
+		kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
 	fi
 
 	# Fork a process to fetch a suggestion and open a pipe to read from it
 	exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
+		# Tell parent process our pid
+		echo $sysparams[pid]
+
+		# Fetch and print the suggestion
 		local suggestion
 		_zsh_autosuggest_fetch_suggestion "$1"
 		echo -nE "$suggestion"
 	)
 
+	# Read the pid from the child process
+	read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD
+
 	# When the fd is readable, call the response handler
 	zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
 }

From 43a011026ffd9c7e9dba4cf022922a4877f1d4e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20V=C3=A4th?= <martin@mvath.de>
Date: Mon, 18 Jun 2018 19:47:27 +0200
Subject: [PATCH 20/47] Do not leak global variables REPLY and strategy

https://github.com/zsh-users/zsh-autosuggestions/issues/341
---
 src/fetch.zsh                 | 1 +
 src/strategies/completion.zsh | 2 +-
 zsh-autosuggestions.zsh       | 3 ++-
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/fetch.zsh b/src/fetch.zsh
index f94e66d..1517018 100644
--- a/src/fetch.zsh
+++ b/src/fetch.zsh
@@ -9,6 +9,7 @@
 _zsh_autosuggest_fetch_suggestion() {
 	typeset -g suggestion
 	local -a strategies
+	local strategy
 
 	# Ensure we are working with an array
 	strategies=(${=ZSH_AUTOSUGGEST_STRATEGY})
diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 847f22b..30542ee 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -88,7 +88,7 @@ _zsh_autosuggest_capture_buffer() {
 
 _zsh_autosuggest_capture_completion() {
 	typeset -g completion
-	local line
+	local line REPLY
 
 	# Zle will be inactive if we are in async mode
 	if zle; then
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 8aad4c8..17e0d52 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -587,7 +587,7 @@ _zsh_autosuggest_capture_buffer() {
 
 _zsh_autosuggest_capture_completion() {
 	typeset -g completion
-	local line
+	local line REPLY
 
 	# Zle will be inactive if we are in async mode
 	if zle; then
@@ -716,6 +716,7 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
 _zsh_autosuggest_fetch_suggestion() {
 	typeset -g suggestion
 	local -a strategies
+	local strategy
 
 	# Ensure we are working with an array
 	strategies=(${=ZSH_AUTOSUGGEST_STRATEGY})

From 1ec43c7291db3327391360e466907329b36c6770 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Fri, 29 Jun 2018 22:08:33 -0600
Subject: [PATCH 21/47] Fix error when single quote entered into buffer

Error looked something like:

```
% echo 'f(zpty):8: unmatched '
_zsh_autosuggest_capture_completion:zpty:9: no such pty command: zsh_autosuggest_completion_pty
_zsh_autosuggest_capture_completion:zpty:14: no such pty command: zsh_autosuggest_completion_pty
_zsh_autosuggest_capture_completion:zpty:21: no such pty command: zsh_autosuggest_completion_pty
```

According to `man zshmodules`, the args to `zpty` are "concatenated with
spaces between, then executed as a command, as if passed to the eval
builtin." So we need to escape the `$` so that `$1` is passed to eval
instead of the value of `$1`.
---
 src/strategies/completion.zsh | 2 +-
 zsh-autosuggestions.zsh       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 30542ee..8bbd7c8 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -94,7 +94,7 @@ _zsh_autosuggest_capture_completion() {
 	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 $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_buffer "\$1"
 		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t'
 	fi
 
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 17e0d52..6f27260 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -593,7 +593,7 @@ _zsh_autosuggest_capture_completion() {
 	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 $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_buffer "\$1"
 		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t'
 	fi
 

From 7d968869e39df762a69b61510dc0b2207c3f9871 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 30 Jun 2018 15:03:14 -0600
Subject: [PATCH 22/47] Return if no completion found

---
 src/strategies/completion.zsh | 2 ++
 zsh-autosuggestions.zsh       | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 8bbd7c8..422f4cc 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -117,6 +117,8 @@ _zsh_autosuggest_strategy_completion() {
 	# Fetch the first completion result
 	_zsh_autosuggest_capture_completion "$1"
 
+	[[ -z "$completion" ]] && return
+
 	# Add the completion string to the buffer to build the full suggestion
 	local -i i=1
 	while [[ "$completion" != "${1[$i,-1]}"* ]]; do ((i++)); done
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index cad2847..4b617cc 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -616,6 +616,8 @@ _zsh_autosuggest_strategy_completion() {
 	# Fetch the first completion result
 	_zsh_autosuggest_capture_completion "$1"
 
+	[[ -z "$completion" ]] && return
+
 	# Add the completion string to the buffer to build the full suggestion
 	local -i i=1
 	while [[ "$completion" != "${1[$i,-1]}"* ]]; do ((i++)); done

From dad6be4d5ec5c5446b7d67ada8b81ccd7074a9d4 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 30 Jun 2018 15:04:56 -0600
Subject: [PATCH 23/47] Remove unused feature detection

Not needed after move away from zpty for async
---
 Makefile                |  1 -
 src/features.zsh        | 19 -------------------
 zsh-autosuggestions.zsh | 19 -------------------
 3 files changed, 39 deletions(-)
 delete mode 100644 src/features.zsh

diff --git a/Makefile b/Makefile
index b89ff04..93b8d94 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,6 @@ SRC_FILES := \
 	$(SRC_DIR)/setup.zsh \
 	$(SRC_DIR)/config.zsh \
 	$(SRC_DIR)/util.zsh \
-	$(SRC_DIR)/features.zsh \
 	$(SRC_DIR)/bind.zsh \
 	$(SRC_DIR)/highlight.zsh \
 	$(SRC_DIR)/widgets.zsh \
diff --git a/src/features.zsh b/src/features.zsh
deleted file mode 100644
index 7a5248f..0000000
--- a/src/features.zsh
+++ /dev/null
@@ -1,19 +0,0 @@
-
-#--------------------------------------------------------------------#
-# Feature Detection                                                  #
-#--------------------------------------------------------------------#
-
-_zsh_autosuggest_feature_detect_zpty_returns_fd() {
-	typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD
-	typeset -h REPLY
-
-	zpty zsh_autosuggest_feature_detect '{ zshexit() { kill -KILL $$; sleep 1 } }'
-
-	if (( REPLY )); then
-		_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1
-	else
-		_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0
-	fi
-
-	zpty -d zsh_autosuggest_feature_detect
-}
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 4b617cc..83b2f7d 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -122,25 +122,6 @@ _zsh_autosuggest_escape_command() {
 	echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}"
 }
 
-#--------------------------------------------------------------------#
-# Feature Detection                                                  #
-#--------------------------------------------------------------------#
-
-_zsh_autosuggest_feature_detect_zpty_returns_fd() {
-	typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD
-	typeset -h REPLY
-
-	zpty zsh_autosuggest_feature_detect '{ zshexit() { kill -KILL $$; sleep 1 } }'
-
-	if (( REPLY )); then
-		_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1
-	else
-		_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0
-	fi
-
-	zpty -d zsh_autosuggest_feature_detect
-}
-
 #--------------------------------------------------------------------#
 # Widget Helpers                                                     #
 #--------------------------------------------------------------------#

From 5529102afc618a3bff5fae75a969a1bb150fe270 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 30 Jun 2018 15:06:19 -0600
Subject: [PATCH 24/47] zpty module is only needed for `completion` strategy

---
 Makefile                      |  1 -
 README.md                     |  2 +-
 src/setup.zsh                 | 10 ----------
 src/start.zsh                 |  1 +
 src/strategies/completion.zsh |  2 ++
 zsh-autosuggestions.zsh       | 13 +++----------
 6 files changed, 7 insertions(+), 22 deletions(-)
 delete mode 100644 src/setup.zsh

diff --git a/Makefile b/Makefile
index 93b8d94..f6d13a7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,6 @@
 SRC_DIR    := ./src
 
 SRC_FILES := \
-	$(SRC_DIR)/setup.zsh \
 	$(SRC_DIR)/config.zsh \
 	$(SRC_DIR)/util.zsh \
 	$(SRC_DIR)/bind.zsh \
diff --git a/README.md b/README.md
index 4507c6b..dc7d21f 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,7 @@ Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion
 
 - `history`: Chooses the most recent match from history.
 - `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.
+- `completion`: (experimental) Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` and `zutil` modules)
 
 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.
 
diff --git a/src/setup.zsh b/src/setup.zsh
deleted file mode 100644
index c74489f..0000000
--- a/src/setup.zsh
+++ /dev/null
@@ -1,10 +0,0 @@
-
-#--------------------------------------------------------------------#
-# Setup                                                              #
-#--------------------------------------------------------------------#
-
-# Precmd hooks for initializing the library and starting pty's
-autoload -Uz add-zsh-hook
-
-# Asynchronous suggestions are generated in a pty
-zmodload zsh/zpty
diff --git a/src/start.zsh b/src/start.zsh
index a73ee3b..ff93fdf 100644
--- a/src/start.zsh
+++ b/src/start.zsh
@@ -17,4 +17,5 @@ _zsh_autosuggest_start() {
 }
 
 # Start the autosuggestion widgets on the next precmd
+autoload -Uz add-zsh-hook
 add-zsh-hook precmd _zsh_autosuggest_start
diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 422f4cc..ff13b81 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -87,6 +87,8 @@ _zsh_autosuggest_capture_buffer() {
 }
 
 _zsh_autosuggest_capture_completion() {
+	zmodload -s zsh/zpty || return
+
 	typeset -g completion
 	local line REPLY
 
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 83b2f7d..0052c69 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -25,16 +25,6 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 # OTHER DEALINGS IN THE SOFTWARE.
 
-#--------------------------------------------------------------------#
-# Setup                                                              #
-#--------------------------------------------------------------------#
-
-# Precmd hooks for initializing the library and starting pty's
-autoload -Uz add-zsh-hook
-
-# Asynchronous suggestions are generated in a pty
-zmodload zsh/zpty
-
 #--------------------------------------------------------------------#
 # Global Configuration Variables                                     #
 #--------------------------------------------------------------------#
@@ -567,6 +557,8 @@ _zsh_autosuggest_capture_buffer() {
 }
 
 _zsh_autosuggest_capture_completion() {
+	zmodload -s zsh/zpty || return
+
 	typeset -g completion
 	local line REPLY
 
@@ -780,4 +772,5 @@ _zsh_autosuggest_start() {
 }
 
 # Start the autosuggestion widgets on the next precmd
+autoload -Uz add-zsh-hook
 add-zsh-hook precmd _zsh_autosuggest_start

From c0315e96d84a9f992d236131783aaecc9117004f Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 30 Jun 2018 16:54:33 -0600
Subject: [PATCH 25/47] Don't use `-s` option to `zmodload`

It is not available in zsh versions older than 5.3
---
 src/strategies/completion.zsh | 2 +-
 zsh-autosuggestions.zsh       | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index ff13b81..5f71d98 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -87,7 +87,7 @@ _zsh_autosuggest_capture_buffer() {
 }
 
 _zsh_autosuggest_capture_completion() {
-	zmodload -s zsh/zpty || return
+	zmodload zsh/zpty 2>/dev/null || return
 
 	typeset -g completion
 	local line REPLY
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 0052c69..41c659f 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -557,7 +557,7 @@ _zsh_autosuggest_capture_buffer() {
 }
 
 _zsh_autosuggest_capture_completion() {
-	zmodload -s zsh/zpty || return
+	zmodload zsh/zpty 2>/dev/null || return
 
 	typeset -g completion
 	local line REPLY

From e97d132b3bef126b3f9c8f61b8a0bbefb9b29a9a Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 30 Jun 2018 15:38:05 -0600
Subject: [PATCH 26/47] Add install directions for Antigen

---
 INSTALL.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/INSTALL.md b/INSTALL.md
index 945cec7..4c54637 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -16,6 +16,16 @@
 
 3. Start a new terminal session.
 
+### Antigen
+
+1. Add the following to your `.zshrc`:
+
+    ```sh
+    antigen bundle zsh-users/zsh-autosuggestions
+    ```
+
+2. Start a new terminal session.
+
 ### Oh My Zsh
 
 1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`)

From 8ae0283c9034dbd3f479871c590d9b853b3bc84a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20V=C3=A4th?= <martin@mvath.de>
Date: Mon, 2 Jul 2018 19:26:06 +0200
Subject: [PATCH 27/47] Do not rely on implicit NULLCMD=cat

---
 src/async.zsh           | 2 +-
 zsh-autosuggestions.zsh | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/async.zsh b/src/async.zsh
index ee39332..eefdf4a 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -41,7 +41,7 @@ _zsh_autosuggest_async_request() {
 # Second arg will be passed in case of error
 _zsh_autosuggest_async_response() {
 	# Read everything from the fd and give it as a suggestion
-	zle autosuggest-suggest -- "$(<&$1)"
+	zle autosuggest-suggest -- "$(cat <&$1)"
 
 	# Remove the handler and close the fd
 	zle -F "$1"
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 41c659f..0190895 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -747,7 +747,7 @@ _zsh_autosuggest_async_request() {
 # Second arg will be passed in case of error
 _zsh_autosuggest_async_response() {
 	# Read everything from the fd and give it as a suggestion
-	zle autosuggest-suggest -- "$(<&$1)"
+	zle autosuggest-suggest -- "$(cat <&$1)"
 
 	# Remove the handler and close the fd
 	zle -F "$1"

From 245f5d2ba2a6da3305f6612e68828e24a36b7345 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 30 Jun 2018 18:15:38 -0600
Subject: [PATCH 28/47] Improve completion suggestions

Just insert the first completion directly into the buffer and read the
whole buffer from the zpty.
---
 src/strategies/completion.zsh | 95 +++++++++++------------------------
 zsh-autosuggestions.zsh       | 95 +++++++++++------------------------
 2 files changed, 56 insertions(+), 134 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 5f71d98..30139ba 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -2,18 +2,18 @@
 #--------------------------------------------------------------------#
 # Completion Suggestion Strategy                                     #
 #--------------------------------------------------------------------#
-# Fetches suggestions from zsh's completion engine
-# Based on https://github.com/Valodim/zsh-capture-completion
+# Fetches a suggestion from the completion engine
 #
 
-_zsh_autosuggest_capture_setup() {
-	zmodload zsh/zutil # For `zparseopts`
+_zsh_autosuggest_capture_postcompletion() {
+	# Always insert the first completion into the buffer
+	compstate[insert]=1
 
-	# Ensure completions have been initialized
-	if ! whence compdef >/dev/null; then
-		autoload -Uz compinit && compinit
-	fi
+	# Don't list completions
+	unset compstate[list]
+}
 
+_zsh_autosuggest_capture_completion_widget() {
 	# 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
@@ -24,54 +24,26 @@ _zsh_autosuggest_capture_setup() {
 		sleep 1 # Block for long enough for the signal to come through
 	}
 
-	# Never group stuff!
-	zstyle ':completion:*' list-grouped false
+	local -a +h comppostfuncs
+	comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
 
-	# No list separator, this saves some stripping later on
-	zstyle ':completion:*' list-separator ''
+	# Run the original widget wrapping `.complete-word` so we don't
+	# recursively try to fetch suggestions, since our pty is forked
+	# after autosuggestions is initialized.
+	zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]}
 
-	# 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'
-	}
+	# The completion has been added, print the buffer as the suggestion
+	echo -nE - $'\0'$BUFFER$'\0'
 }
 
-_zsh_autosuggest_capture_widget() {
-	_zsh_autosuggest_capture_setup
-
-	zle complete-word
-}
-
-zle -N autosuggest-capture-completion _zsh_autosuggest_capture_widget
+zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
 
 _zsh_autosuggest_capture_buffer() {
 	local BUFFERCONTENT="$1"
 
-	_zsh_autosuggest_capture_setup
+	zmodload zsh/parameter 2>/dev/null || return # For `$functions`
 
-	zmodload zsh/parameter # For `$functions`
+	bindkey '^I' autosuggest-capture-completion
 
 	# Make vared completion work as if for a normal command line
 	# https://stackoverflow.com/a/7057118/154703
@@ -86,12 +58,16 @@ _zsh_autosuggest_capture_buffer() {
 	vared BUFFERCONTENT
 }
 
-_zsh_autosuggest_capture_completion() {
-	zmodload zsh/zpty 2>/dev/null || return
-
-	typeset -g completion
+_zsh_autosuggest_strategy_completion() {
+	typeset -g suggestion
 	local line REPLY
 
+	# Exit if we don't have completions
+	whence compdef >/dev/null || return
+
+	# Exit if we don't have zpty
+	zmodload zsh/zpty 2>/dev/null || return
+
 	# Zle will be inactive if we are in async mode
 	if zle; then
 		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zle autosuggest-capture-completion
@@ -106,23 +82,8 @@ _zsh_autosuggest_capture_completion() {
 
 	# 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'*}"
+	suggestion="${${${(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"
-
-	[[ -z "$completion" ]] && return
-
-	# 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"
-}
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 0190895..81cb2dc 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -472,18 +472,18 @@ zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 #--------------------------------------------------------------------#
 # Completion Suggestion Strategy                                     #
 #--------------------------------------------------------------------#
-# Fetches suggestions from zsh's completion engine
-# Based on https://github.com/Valodim/zsh-capture-completion
+# Fetches a suggestion from the completion engine
 #
 
-_zsh_autosuggest_capture_setup() {
-	zmodload zsh/zutil # For `zparseopts`
+_zsh_autosuggest_capture_postcompletion() {
+	# Always insert the first completion into the buffer
+	compstate[insert]=1
 
-	# Ensure completions have been initialized
-	if ! whence compdef >/dev/null; then
-		autoload -Uz compinit && compinit
-	fi
+	# Don't list completions
+	unset compstate[list]
+}
 
+_zsh_autosuggest_capture_completion_widget() {
 	# 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
@@ -494,54 +494,26 @@ _zsh_autosuggest_capture_setup() {
 		sleep 1 # Block for long enough for the signal to come through
 	}
 
-	# Never group stuff!
-	zstyle ':completion:*' list-grouped false
+	local -a +h comppostfuncs
+	comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
 
-	# No list separator, this saves some stripping later on
-	zstyle ':completion:*' list-separator ''
+	# Run the original widget wrapping `.complete-word` so we don't
+	# recursively try to fetch suggestions, since our pty is forked
+	# after autosuggestions is initialized.
+	zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]}
 
-	# 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'
-	}
+	# The completion has been added, print the buffer as the suggestion
+	echo -nE - $'\0'$BUFFER$'\0'
 }
 
-_zsh_autosuggest_capture_widget() {
-	_zsh_autosuggest_capture_setup
-
-	zle complete-word
-}
-
-zle -N autosuggest-capture-completion _zsh_autosuggest_capture_widget
+zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
 
 _zsh_autosuggest_capture_buffer() {
 	local BUFFERCONTENT="$1"
 
-	_zsh_autosuggest_capture_setup
+	zmodload zsh/parameter 2>/dev/null || return # For `$functions`
 
-	zmodload zsh/parameter # For `$functions`
+	bindkey '^I' autosuggest-capture-completion
 
 	# Make vared completion work as if for a normal command line
 	# https://stackoverflow.com/a/7057118/154703
@@ -556,12 +528,16 @@ _zsh_autosuggest_capture_buffer() {
 	vared BUFFERCONTENT
 }
 
-_zsh_autosuggest_capture_completion() {
-	zmodload zsh/zpty 2>/dev/null || return
-
-	typeset -g completion
+_zsh_autosuggest_strategy_completion() {
+	typeset -g suggestion
 	local line REPLY
 
+	# Exit if we don't have completions
+	whence compdef >/dev/null || return
+
+	# Exit if we don't have zpty
+	zmodload zsh/zpty 2>/dev/null || return
+
 	# Zle will be inactive if we are in async mode
 	if zle; then
 		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zle autosuggest-capture-completion
@@ -576,27 +552,12 @@ _zsh_autosuggest_capture_completion() {
 
 	# 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'*}"
+	suggestion="${${${(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"
-
-	[[ -z "$completion" ]] && return
-
-	# 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                                        #
 #--------------------------------------------------------------------#

From 302bd7c059c2e657ea741d8d5074e2a0f23f1628 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sun, 1 Jul 2018 10:16:38 -0600
Subject: [PATCH 29/47] Setup zshexit hook immediately in both sync/async cases

---
 src/strategies/completion.zsh | 44 +++++++++++++++++++++--------------
 zsh-autosuggestions.zsh       | 44 +++++++++++++++++++++--------------
 2 files changed, 54 insertions(+), 34 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 30139ba..2f162dc 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -14,16 +14,6 @@ _zsh_autosuggest_capture_postcompletion() {
 }
 
 _zsh_autosuggest_capture_completion_widget() {
-	# 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
-	}
-
 	local -a +h comppostfuncs
 	comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
 
@@ -38,12 +28,32 @@ _zsh_autosuggest_capture_completion_widget() {
 
 zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
 
-_zsh_autosuggest_capture_buffer() {
-	local BUFFERCONTENT="$1"
-
-	zmodload zsh/parameter 2>/dev/null || return # For `$functions`
+_zsh_autosuggest_capture_setup() {
+	# There is a bug in zpty module in older zsh versions 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.
+	if ! is-at-least 5.4; then
+		zshexit() {
+			kill -KILL $$
+			sleep 1 # Block for long enough for the signal to come through
+		}
+	fi
 
 	bindkey '^I' autosuggest-capture-completion
+}
+
+_zsh_autosuggest_capture_completion_sync() {
+	_zsh_autosuggest_capture_setup
+
+	zle autosuggest-capture-completion
+}
+
+_zsh_autosuggest_capture_completion_async() {
+	_zsh_autosuggest_capture_setup
+
+	zmodload zsh/parameter 2>/dev/null || return # For `$functions`
 
 	# Make vared completion work as if for a normal command line
 	# https://stackoverflow.com/a/7057118/154703
@@ -55,7 +65,7 @@ _zsh_autosuggest_capture_buffer() {
 	}
 
 	# Open zle with buffer set so we can capture completions for it
-	vared BUFFERCONTENT
+	vared 1
 }
 
 _zsh_autosuggest_strategy_completion() {
@@ -70,9 +80,9 @@ _zsh_autosuggest_strategy_completion() {
 
 	# Zle will be inactive if we are in async mode
 	if zle; then
-		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zle autosuggest-capture-completion
+		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync
 	else
-		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_buffer "\$1"
+		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$1"
 		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t'
 	fi
 
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 81cb2dc..7a612f9 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -484,16 +484,6 @@ _zsh_autosuggest_capture_postcompletion() {
 }
 
 _zsh_autosuggest_capture_completion_widget() {
-	# 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
-	}
-
 	local -a +h comppostfuncs
 	comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
 
@@ -508,12 +498,32 @@ _zsh_autosuggest_capture_completion_widget() {
 
 zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
 
-_zsh_autosuggest_capture_buffer() {
-	local BUFFERCONTENT="$1"
-
-	zmodload zsh/parameter 2>/dev/null || return # For `$functions`
+_zsh_autosuggest_capture_setup() {
+	# There is a bug in zpty module in older zsh versions 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.
+	if ! is-at-least 5.4; then
+		zshexit() {
+			kill -KILL $$
+			sleep 1 # Block for long enough for the signal to come through
+		}
+	fi
 
 	bindkey '^I' autosuggest-capture-completion
+}
+
+_zsh_autosuggest_capture_completion_sync() {
+	_zsh_autosuggest_capture_setup
+
+	zle autosuggest-capture-completion
+}
+
+_zsh_autosuggest_capture_completion_async() {
+	_zsh_autosuggest_capture_setup
+
+	zmodload zsh/parameter 2>/dev/null || return # For `$functions`
 
 	# Make vared completion work as if for a normal command line
 	# https://stackoverflow.com/a/7057118/154703
@@ -525,7 +535,7 @@ _zsh_autosuggest_capture_buffer() {
 	}
 
 	# Open zle with buffer set so we can capture completions for it
-	vared BUFFERCONTENT
+	vared 1
 }
 
 _zsh_autosuggest_strategy_completion() {
@@ -540,9 +550,9 @@ _zsh_autosuggest_strategy_completion() {
 
 	# Zle will be inactive if we are in async mode
 	if zle; then
-		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME zle autosuggest-capture-completion
+		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync
 	else
-		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_buffer "\$1"
+		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$1"
 		zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t'
 	fi
 

From 4869a757c8b641007a405e6937db13e72eaca83d Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sun, 1 Jul 2018 20:55:53 -0600
Subject: [PATCH 30/47] Ensure we always destroy the zpty

If running in sync mode and a completion takes a long time, the user can
^C out of it. Without this patch, the pty will not be destroyed in this
case and the next time we go to create it, it will fail, making the
shell unusable.
---
 src/strategies/completion.zsh | 20 +++++++++++---------
 zsh-autosuggestions.zsh       | 20 +++++++++++---------
 2 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 2f162dc..9b6341c 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -86,14 +86,16 @@ _zsh_autosuggest_strategy_completion() {
 		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'
+	{
+		# 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
-	suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}"
-
-	# Destroy the pty
-	zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
+		# On older versions of zsh, we sometimes get extra bytes after the
+		# second null byte, so trim those off the end
+		suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}"
+	} always {
+		# Destroy the pty
+		zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
+	}
 }
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 7a612f9..68f1bb1 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -556,16 +556,18 @@ _zsh_autosuggest_strategy_completion() {
 		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'
+	{
+		# 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
-	suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}"
-
-	# Destroy the pty
-	zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
+		# On older versions of zsh, we sometimes get extra bytes after the
+		# second null byte, so trim those off the end
+		suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}"
+	} always {
+		# Destroy the pty
+		zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
+	}
 }
 
 #--------------------------------------------------------------------#

From f1c3b98774bb52667fe3303ace477898aedd3b9b Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Mon, 2 Jul 2018 09:31:48 -0600
Subject: [PATCH 31/47] Only capture completions at the end of the buffer.

To prevent the suggestion from not starting with the buffer string.

Example:

`ls / /[cursor left][cursor left]b`

Before the patch, suggests `ls /b /ls /bin/ /`

After the patch, suggests `ls /b /bin/`.

https://github.com/zsh-users/zsh-autosuggestions/issues/343#issuecomment-401675712
---
 src/strategies/completion.zsh | 3 +++
 zsh-autosuggestions.zsh       | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
index 9b6341c..7517822 100644
--- a/src/strategies/completion.zsh
+++ b/src/strategies/completion.zsh
@@ -17,6 +17,9 @@ _zsh_autosuggest_capture_completion_widget() {
 	local -a +h comppostfuncs
 	comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
 
+	# Only capture completions at the end of the buffer
+	CURSOR=$#BUFFER
+
 	# Run the original widget wrapping `.complete-word` so we don't
 	# recursively try to fetch suggestions, since our pty is forked
 	# after autosuggestions is initialized.
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 68f1bb1..be784b6 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -487,6 +487,9 @@ _zsh_autosuggest_capture_completion_widget() {
 	local -a +h comppostfuncs
 	comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
 
+	# Only capture completions at the end of the buffer
+	CURSOR=$#BUFFER
+
 	# Run the original widget wrapping `.complete-word` so we don't
 	# recursively try to fetch suggestions, since our pty is forked
 	# after autosuggestions is initialized.

From bd1fd9773838d28e565f9abdb31b1d367443fff3 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Mon, 2 Jul 2018 22:25:36 -0600
Subject: [PATCH 32/47] Cleanup unused async pty name

---
 src/config.zsh          | 3 ---
 zsh-autosuggestions.zsh | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/src/config.zsh b/src/config.zsh
index 4598191..4c489df 100644
--- a/src/config.zsh
+++ b/src/config.zsh
@@ -69,8 +69,5 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
 # Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound.
 ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
 
-# Pty name for calculating autosuggestions asynchronously
-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
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index be784b6..8169cc8 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -95,9 +95,6 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
 # Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound.
 ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
 
-# Pty name for calculating autosuggestions asynchronously
-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
 

From 0ee5b0a5c94d43cd41a3f4c23eaf31bcb8c96f4d Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Mon, 2 Jul 2018 22:28:16 -0600
Subject: [PATCH 33/47] Completion strategy no longer requires zutil module

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index dc7d21f..96bbcb4 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,7 @@ Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion
 
 - `history`: Chooses the most recent match from history.
 - `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. (requires `zpty` and `zutil` modules)
+- `completion`: (experimental) Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module)
 
 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.
 

From 93877f6b765fc25f52d4104a8049783cdc38b418 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Fri, 13 Jul 2018 11:25:59 -0600
Subject: [PATCH 34/47] We also need to remove the handler when cancelling
 async request

Should fix GitHub #353
---
 src/async.zsh           | 3 ++-
 zsh-autosuggestions.zsh | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/async.zsh b/src/async.zsh
index eefdf4a..296c1c2 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -10,8 +10,9 @@ _zsh_autosuggest_async_request() {
 
 	# If we've got a pending request, cancel it
 	if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
-		# Close the file descriptor
+		# Close the file descriptor and remove the handler
 		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
+		zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
 
 		# Assume the child process created a new process group and send
 		# TERM to the group to attempt to kill all descendent processes
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 8169cc8..3e7560a 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -689,8 +689,9 @@ _zsh_autosuggest_async_request() {
 
 	# If we've got a pending request, cancel it
 	if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
-		# Close the file descriptor
+		# Close the file descriptor and remove the handler
 		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
+		zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
 
 		# Assume the child process created a new process group and send
 		# TERM to the group to attempt to kill all descendent processes

From 88fe824ddfe1bb635f93e95098346725d864284a Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Fri, 13 Jul 2018 11:26:57 -0600
Subject: [PATCH 35/47] Add some error handling to async response handler

We only want to read data in case of POLLIN or POLLHUP. Not POLLNVAL or
select error.

We always want to remove the handler, so it doesn't get called in an
infinite loop when error is nval or err.

In zsh source, see main zle event loop in zle_main.c raw_getbyte
function.
---
 src/async.zsh           | 12 ++++++++----
 zsh-autosuggestions.zsh | 12 ++++++++----
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/src/async.zsh b/src/async.zsh
index 296c1c2..5c19a81 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -41,10 +41,14 @@ _zsh_autosuggest_async_request() {
 # First arg will be fd ready for reading
 # Second arg will be passed in case of error
 _zsh_autosuggest_async_response() {
-	# Read everything from the fd and give it as a suggestion
-	zle autosuggest-suggest -- "$(cat <&$1)"
+	if [[ -z "$2" || "$2" == "hup" ]]; then
+		# Read everything from the fd and give it as a suggestion
+		zle autosuggest-suggest -- "$(cat <&$1)"
 
-	# Remove the handler and close the fd
+		# Close the fd
+		exec {1}<&-
+	fi
+
+	# Always remove the handler
 	zle -F "$1"
-	exec {1}<&-
 }
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 3e7560a..27c42c6 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -720,12 +720,16 @@ _zsh_autosuggest_async_request() {
 # First arg will be fd ready for reading
 # Second arg will be passed in case of error
 _zsh_autosuggest_async_response() {
-	# Read everything from the fd and give it as a suggestion
-	zle autosuggest-suggest -- "$(cat <&$1)"
+	if [[ -z "$2" || "$2" == "hup" ]]; then
+		# Read everything from the fd and give it as a suggestion
+		zle autosuggest-suggest -- "$(cat <&$1)"
 
-	# Remove the handler and close the fd
+		# Close the fd
+		exec {1}<&-
+	fi
+
+	# Always remove the handler
 	zle -F "$1"
-	exec {1}<&-
 }
 
 #--------------------------------------------------------------------#

From 7ab212490470870bdf86074becc10d9954bfd5a0 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Fri, 13 Jul 2018 21:48:25 -0600
Subject: [PATCH 36/47] Kill async process by id when job control disabled

---
 src/async.zsh           | 15 ++++++++++++---
 zsh-autosuggestions.zsh | 15 ++++++++++++---
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/src/async.zsh b/src/async.zsh
index eefdf4a..814a185 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -13,9 +13,18 @@ _zsh_autosuggest_async_request() {
 		# Close the file descriptor
 		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
 
-		# Assume the child process created a new process group and send
-		# TERM to the group to attempt to kill all descendent processes
-		kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
+		# Zsh will make a new process group for the child process only if job
+		# control is enabled (MONITOR option)
+		if [[ -o MONITOR ]]; then
+			# Send the signal to the process group to kill any processes that may
+			# have been forked by the suggestion strategy
+			kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
+		else
+			# Kill just the child process since it wasn't placed in a new process
+			# group. If the suggestion strategy forked any child processes they may
+			# be orphaned and left behind.
+			kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
+		fi
 	fi
 
 	# Fork a process to fetch a suggestion and open a pipe to read from it
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 8169cc8..191cda1 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -692,9 +692,18 @@ _zsh_autosuggest_async_request() {
 		# Close the file descriptor
 		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
 
-		# Assume the child process created a new process group and send
-		# TERM to the group to attempt to kill all descendent processes
-		kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
+		# Zsh will make a new process group for the child process only if job
+		# control is enabled (MONITOR option)
+		if [[ -o MONITOR ]]; then
+			# Send the signal to the process group to kill any processes that may
+			# have been forked by the suggestion strategy
+			kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
+		else
+			# Kill just the child process since it wasn't placed in a new process
+			# group. If the suggestion strategy forked any child processes they may
+			# be orphaned and left behind.
+			kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
+		fi
 	fi
 
 	# Fork a process to fetch a suggestion and open a pipe to read from it

From 681ffc7b2891e97801a2104997ed8524fa2c4556 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Fri, 13 Jul 2018 22:16:53 -0600
Subject: [PATCH 37/47] Reset opts in some functions affected by GLOB_SUBST

Should fix GitHub #334
---
 src/bind.zsh            | 4 +++-
 src/widgets.zsh         | 4 ++++
 zsh-autosuggestions.zsh | 8 +++++++-
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/src/bind.zsh b/src/bind.zsh
index f538379..bb41ef8 100644
--- a/src/bind.zsh
+++ b/src/bind.zsh
@@ -74,7 +74,9 @@ _zsh_autosuggest_bind_widget() {
 
 # Map all configured widgets to the right autosuggest widgets
 _zsh_autosuggest_bind_widgets() {
-	local widget
+	emulate -L zsh
+
+ 	local widget
 	local ignore_widgets
 
 	ignore_widgets=(
diff --git a/src/widgets.zsh b/src/widgets.zsh
index 746944d..3312579 100644
--- a/src/widgets.zsh
+++ b/src/widgets.zsh
@@ -37,6 +37,8 @@ _zsh_autosuggest_clear() {
 
 # Modify the buffer and get a new suggestion
 _zsh_autosuggest_modify() {
+	emulate -L zsh
+
 	local -i retval
 
 	# Only available in zsh >= 5.4
@@ -104,6 +106,8 @@ _zsh_autosuggest_fetch() {
 
 # Offer a suggestion
 _zsh_autosuggest_suggest() {
+	emulate -L zsh
+
 	local suggestion="$1"
 
 	if [[ -n "$suggestion" ]] && (( $#BUFFER )); then
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index f94859e..4b39dda 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -184,7 +184,9 @@ _zsh_autosuggest_bind_widget() {
 
 # Map all configured widgets to the right autosuggest widgets
 _zsh_autosuggest_bind_widgets() {
-	local widget
+	emulate -L zsh
+
+ 	local widget
 	local ignore_widgets
 
 	ignore_widgets=(
@@ -291,6 +293,8 @@ _zsh_autosuggest_clear() {
 
 # Modify the buffer and get a new suggestion
 _zsh_autosuggest_modify() {
+	emulate -L zsh
+
 	local -i retval
 
 	# Only available in zsh >= 5.4
@@ -358,6 +362,8 @@ _zsh_autosuggest_fetch() {
 
 # Offer a suggestion
 _zsh_autosuggest_suggest() {
+	emulate -L zsh
+
 	local suggestion="$1"
 
 	if [[ -n "$suggestion" ]] && (( $#BUFFER )); then

From 4b28d92e01517ef944e93523594920c8c3752e29 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sat, 10 Nov 2018 13:56:31 -0700
Subject: [PATCH 38/47] Add `after_sourcing` hook for tests

Is executed immediately after sourcing the plugin
---
 spec/options/strategy_spec.rb | 32 +++++++++++++++++++++-----------
 spec/spec_helper.rb           |  2 ++
 2 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/spec/options/strategy_spec.rb b/spec/options/strategy_spec.rb
index 378d01e..58562d0 100644
--- a/spec/options/strategy_spec.rb
+++ b/spec/options/strategy_spec.rb
@@ -3,7 +3,11 @@ describe 'a suggestion for a given prefix' do
   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" }' }
 
-  let(:options) { [ history_strategy ] }
+  let(:after_sourcing) do
+    -> do
+      session.run_command(history_strategy)
+    end
+  end
 
   it 'by default is determined by calling the `history` strategy function' do
     session.send_string('h')
@@ -11,11 +15,14 @@ describe 'a suggestion for a given prefix' do
   end
 
   context 'when ZSH_AUTOSUGGEST_STRATEGY is set to an array' do
-    let(:options) { [
-      foobar_strategy,
-      foobaz_strategy,
-      'ZSH_AUTOSUGGEST_STRATEGY=(foobar foobaz)'
-    ] }
+    let(:after_sourcing) do
+      -> do
+        session.
+          run_command(foobar_strategy).
+          run_command(foobaz_strategy).
+          run_command('ZSH_AUTOSUGGEST_STRATEGY=(foobar foobaz)')
+      end
+    end
 
     it 'is determined by the first strategy function to return a suggestion' do
       session.send_string('foo')
@@ -27,11 +34,14 @@ describe 'a suggestion for a given prefix' do
   end
 
   context 'when ZSH_AUTOSUGGEST_STRATEGY is set to a string' do
-    let(:options) { [
-      foobar_strategy,
-      foobaz_strategy,
-      'ZSH_AUTOSUGGEST_STRATEGY="foobar foobaz"'
-    ] }
+    let(:after_sourcing) do
+      -> do
+        session.
+          run_command(foobar_strategy).
+          run_command(foobaz_strategy).
+          run_command('ZSH_AUTOSUGGEST_STRATEGY="foobar foobaz"')
+      end
+    end
 
     it 'is determined by the first strategy function to return a suggestion' do
       session.send_string('foo')
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 64115d2..bfcb706 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -6,12 +6,14 @@ RSpec.shared_context 'terminal session' do
   let(:term_opts) { {} }
   let(:session) { TerminalSession.new(term_opts) }
   let(:before_sourcing) { -> {} }
+  let(:after_sourcing) { -> {} }
   let(:options) { [] }
 
   around do |example|
     before_sourcing.call
 
     session.run_command((['source zsh-autosuggestions.zsh'] + options).join('; '))
+    after_sourcing.call
     session.clear_screen
 
     example.run

From e61442161e8978fa2ed18c57232ade819b5139b5 Mon Sep 17 00:00:00 2001
From: Eric Nielsen <eric@amalgamar.com.br>
Date: Mon, 15 Oct 2018 16:37:02 -0500
Subject: [PATCH 39/47] Don't overwrite config with default values

otherwise users are obliged to set the config values *after* sourcing
the plugin. They're not able to do it before. Also, re-sourcing the
plugin will reset the values to the defaults again.

See zimfw/zimfw#301

Fixes #335
---
 README.md               |  2 +-
 spec/spec_helper.rb     |  4 ++--
 src/config.zsh          | 22 +++++++++++-----------
 zsh-autosuggestions.zsh | 22 +++++++++++-----------
 4 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/README.md b/README.md
index 96bbcb4..65274e8 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ If you invoke the `forward-word` widget, it will partially accept the suggestion
 
 ## Configuration
 
-You may want to override the default global config variables after sourcing the plugin. Default values of these variables can be found [here](src/config.zsh).
+You may want to override the default global config variables. Default values of these variables can be found [here](src/config.zsh).
 
 **Note:** If you are using Oh My Zsh, you can put this configuration in a file in the `$ZSH_CUSTOM` directory. See their comments on [overriding internals](https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals).
 
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index bfcb706..abea917 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -11,8 +11,8 @@ RSpec.shared_context 'terminal session' do
 
   around do |example|
     before_sourcing.call
-
-    session.run_command((['source zsh-autosuggestions.zsh'] + options).join('; '))
+    session.run_command(options.join('; '))
+    session.run_command('source zsh-autosuggestions.zsh')
     after_sourcing.call
     session.clear_screen
 
diff --git a/src/config.zsh b/src/config.zsh
index 4c489df..dc01b7c 100644
--- a/src/config.zsh
+++ b/src/config.zsh
@@ -6,17 +6,17 @@
 # Color to use when highlighting suggestion
 # Uses format of `region_highlight`
 # More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
-ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
+: ${ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'}
 
 # Prefix to use when saving original versions of bound widgets
-ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
+: ${ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-}
 
 # Strategies to use to fetch a suggestion
 # Will try each strategy in order until a suggestion is returned
-ZSH_AUTOSUGGEST_STRATEGY=(history)
+(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && ZSH_AUTOSUGGEST_STRATEGY=(history)
 
 # Widgets that clear the suggestion
-ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
 	history-search-forward
 	history-search-backward
 	history-beginning-search-forward
@@ -31,7 +31,7 @@ ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
 )
 
 # Widgets that accept the entire suggestion
-ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
 	forward-char
 	end-of-line
 	vi-forward-char
@@ -40,11 +40,11 @@ ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
 )
 
 # Widgets that accept the entire suggestion and execute it
-ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
 )
 
 # Widgets that accept the suggestion as far as the cursor moves
-ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
 	forward-word
 	emacs-forward-word
 	vi-forward-word
@@ -56,7 +56,7 @@ ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
 )
 
 # Widgets that should be ignored (globbing supported but must be escaped)
-ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
 	orig-\*
 	beep
 	run-help
@@ -66,8 +66,8 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
 	yank-pop
 )
 
-# Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound.
-ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
+# Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
+: ${ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=}
 
 # Pty name for capturing completions for completion suggestion strategy
-ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty
+: ${ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty}
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 4b39dda..5127015 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -32,17 +32,17 @@
 # Color to use when highlighting suggestion
 # Uses format of `region_highlight`
 # More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
-ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
+: ${ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'}
 
 # Prefix to use when saving original versions of bound widgets
-ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
+: ${ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-}
 
 # Strategies to use to fetch a suggestion
 # Will try each strategy in order until a suggestion is returned
-ZSH_AUTOSUGGEST_STRATEGY=(history)
+(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && ZSH_AUTOSUGGEST_STRATEGY=(history)
 
 # Widgets that clear the suggestion
-ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
 	history-search-forward
 	history-search-backward
 	history-beginning-search-forward
@@ -57,7 +57,7 @@ ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
 )
 
 # Widgets that accept the entire suggestion
-ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
 	forward-char
 	end-of-line
 	vi-forward-char
@@ -66,11 +66,11 @@ ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
 )
 
 # Widgets that accept the entire suggestion and execute it
-ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
 )
 
 # Widgets that accept the suggestion as far as the cursor moves
-ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
 	forward-word
 	emacs-forward-word
 	vi-forward-word
@@ -82,7 +82,7 @@ ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
 )
 
 # Widgets that should be ignored (globbing supported but must be escaped)
-ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
+(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
 	orig-\*
 	beep
 	run-help
@@ -92,11 +92,11 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
 	yank-pop
 )
 
-# Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound.
-ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
+# Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
+: ${ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=}
 
 # Pty name for capturing completions for completion suggestion strategy
-ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty
+: ${ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty}
 
 #--------------------------------------------------------------------#
 # Utility Functions                                                  #

From 41657e35659864f21b24d3179e8890857d243e88 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sun, 16 Dec 2018 20:43:25 -0700
Subject: [PATCH 40/47] Revert async process substitution & completion strategy

They're not quite ready. Keep them on a feature branch for now.
---
 Makefile                              |   2 +
 README.md                             |   9 +-
 spec/async_spec.rb                    |  53 +++++
 spec/integrations/client_zpty_spec.rb |  12 +-
 spec/options/async_zpty_name_spec.rb  |  19 ++
 spec/strategies/completion_spec.rb    |  26 ---
 spec/terminal_session.rb              |   4 +
 src/async.zsh                         | 130 ++++++++----
 src/config.zsh                        |   4 +-
 src/features.zsh                      |  19 ++
 src/setup.zsh                         |  10 +
 src/start.zsh                         |   5 +-
 src/strategies/completion.zsh         | 104 ----------
 src/widgets.zsh                       |   2 +-
 zsh-autosuggestions.zsh               | 274 ++++++++++++--------------
 15 files changed, 333 insertions(+), 340 deletions(-)
 create mode 100644 spec/options/async_zpty_name_spec.rb
 delete mode 100644 spec/strategies/completion_spec.rb
 create mode 100644 src/features.zsh
 create mode 100644 src/setup.zsh
 delete mode 100644 src/strategies/completion.zsh

diff --git a/Makefile b/Makefile
index f6d13a7..b89ff04 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,10 @@
 SRC_DIR    := ./src
 
 SRC_FILES := \
+	$(SRC_DIR)/setup.zsh \
 	$(SRC_DIR)/config.zsh \
 	$(SRC_DIR)/util.zsh \
+	$(SRC_DIR)/features.zsh \
 	$(SRC_DIR)/bind.zsh \
 	$(SRC_DIR)/highlight.zsh \
 	$(SRC_DIR)/widgets.zsh \
diff --git a/README.md b/README.md
index 59ee831..7e3e674 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 _[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._
 
-It suggests commands as you type.
+It suggests commands as you type, based on command history.
 
 Requirements: Zsh v4.3.11 or later
 
@@ -39,13 +39,10 @@ Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion
 
 ### Suggestion Strategy
 
-`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:
+`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 two built-in strategies to choose from:
 
 - `history`: Chooses the most recent match from history.
 - `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. (requires `zpty` module)
-
-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
@@ -70,7 +67,7 @@ This can be useful when pasting large amount of text in the terminal, to avoid t
 
 ### Enable Asynchronous Mode
 
-As of `v0.4.0`, suggestions can be fetched asynchronously. To enable this behavior, set the `ZSH_AUTOSUGGEST_USE_ASYNC` variable (it can be set to anything).
+As of `v0.4.0`, suggestions can be fetched asynchronously using the `zsh/zpty` module. To enable this behavior, set the `ZSH_AUTOSUGGEST_USE_ASYNC` variable (it can be set to anything).
 
 
 ### Key Bindings
diff --git a/spec/async_spec.rb b/spec/async_spec.rb
index 9405fb2..152adde 100644
--- a/spec/async_spec.rb
+++ b/spec/async_spec.rb
@@ -1,4 +1,8 @@
 context 'with asynchronous suggestions enabled' do
+  before do
+    skip 'Async mode not supported below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
+  end
+
   let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] }
 
   describe '`up-line-or-beginning-search`' do
@@ -26,6 +30,55 @@ context 'with asynchronous suggestions enabled' do
       end
     end
   end
+
+  it 'should not add extra carriage returns before newlines' do
+    session.
+      send_string('echo "').
+      send_keys('escape').
+      send_keys('enter').
+      send_string('"').
+      send_keys('enter')
+
+    session.clear_screen
+
+    session.send_string('echo')
+    wait_for { session.content }.to eq("echo \"\n\"")
+  end
+
+  it 'should treat carriage returns and newlines as separate characters' do
+    session.
+      send_string('echo "').
+      send_keys('C-v').
+      send_keys('enter').
+      send_string('foo"').
+      send_keys('enter')
+
+    session.
+      send_string('echo "').
+      send_keys('control').
+      send_keys('enter').
+      send_string('bar"').
+      send_keys('enter')
+
+    session.clear_screen
+
+    session.
+      send_string('echo "').
+      send_keys('C-v').
+      send_keys('enter')
+
+    wait_for { session.content }.to eq('echo "^Mfoo"')
+  end
+
+  describe 'exiting a subshell' do
+    it 'should not cause error messages to be printed' do
+      session.run_command('$(exit)')
+
+      sleep 1
+
+      expect(session.content).to eq('$(exit)')
+    end
+  end
 end
 
 
diff --git a/spec/integrations/client_zpty_spec.rb b/spec/integrations/client_zpty_spec.rb
index b8abb37..8f1550e 100644
--- a/spec/integrations/client_zpty_spec.rb
+++ b/spec/integrations/client_zpty_spec.rb
@@ -1,14 +1,10 @@
 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 by running zsh-autosuggestions' do
+    sleep 1 # Give a little time for precmd hooks to run
+    session.run_command('zpty -t kitty; echo $?')
 
-    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
+    wait_for { session.content }.to end_with("\n0")
   end
 end
diff --git a/spec/options/async_zpty_name_spec.rb b/spec/options/async_zpty_name_spec.rb
new file mode 100644
index 0000000..407ee70
--- /dev/null
+++ b/spec/options/async_zpty_name_spec.rb
@@ -0,0 +1,19 @@
+context 'when async suggestions are enabled' do
+  let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] }
+
+  describe 'the zpty for async suggestions' do
+    it 'is created with the default name' do
+      session.run_command('zpty -t zsh_autosuggest_pty &>/dev/null; echo $?')
+      wait_for { session.content }.to end_with("\n0")
+    end
+
+    context 'when ZSH_AUTOSUGGEST_ASYNC_PTY_NAME is set' do
+      let(:options) { super() + ['ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=foo_pty'] }
+
+      it 'is created with the specified name' do
+        session.run_command('zpty -t foo_pty &>/dev/null; echo $?')
+        wait_for { session.content }.to end_with("\n0")
+      end
+    end
+  end
+end
diff --git a/spec/strategies/completion_spec.rb b/spec/strategies/completion_spec.rb
deleted file mode 100644
index bd2c72d..0000000
--- a/spec/strategies/completion_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-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
-
diff --git a/spec/terminal_session.rb b/spec/terminal_session.rb
index 2d9468f..f91ee6c 100644
--- a/spec/terminal_session.rb
+++ b/spec/terminal_session.rb
@@ -18,6 +18,10 @@ class TerminalSession
     tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'")
   end
 
+  def zsh_version
+    @zsh_version ||= Gem::Version.new(`#{ZSH_BIN} -c 'echo -n $ZSH_VERSION'`)
+  end
+
   def tmux_socket_name
     @tmux_socket_name ||= SecureRandom.hex(6)
   end
diff --git a/src/async.zsh b/src/async.zsh
index f1877d3..dd54c24 100644
--- a/src/async.zsh
+++ b/src/async.zsh
@@ -3,61 +3,107 @@
 # Async                                                              #
 #--------------------------------------------------------------------#
 
-zmodload zsh/system
+# Zpty process is spawned running this function
+_zsh_autosuggest_async_server() {
+	emulate -R zsh
 
-_zsh_autosuggest_async_request() {
-	typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
+	# 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
+	}
 
-	# If we've got a pending request, cancel it
-	if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
-		# Close the file descriptor and remove the handler
-		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
-		zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
+	# Don't add any extra carriage returns
+	stty -onlcr
 
-		# Zsh will make a new process group for the child process only if job
-		# control is enabled (MONITOR option)
-		if [[ -o MONITOR ]]; then
-			# Send the signal to the process group to kill any processes that may
-			# have been forked by the suggestion strategy
-			kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
-		else
-			# Kill just the child process since it wasn't placed in a new process
-			# group. If the suggestion strategy forked any child processes they may
-			# be orphaned and left behind.
-			kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
-		fi
-	fi
+	# Don't translate carriage returns to newlines
+	stty -icrnl
 
-	# Fork a process to fetch a suggestion and open a pipe to read from it
-	exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
-		# Tell parent process our pid
-		echo $sysparams[pid]
+	# Silence any error messages
+	exec 2>/dev/null
 
-		# Fetch and print the suggestion
-		local suggestion
-		_zsh_autosuggest_fetch_suggestion "$1"
-		echo -nE "$suggestion"
-	)
+	local last_pid
 
-	# Read the pid from the child process
-	read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD
+	while IFS='' read -r -d $'\0' query; do
+		# Kill last bg process
+		kill -KILL $last_pid &>/dev/null
 
-	# When the fd is readable, call the response handler
-	zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
+		# Run suggestion search in the background
+		(
+			local suggestion
+			_zsh_autosuggest_fetch_suggestion "$query"
+			echo -n -E "$suggestion"$'\0'
+		) &
+
+		last_pid=$!
+	done
 }
 
-# Called when new data is ready to be read from the pipe
+_zsh_autosuggest_async_request() {
+	# Write the query to the zpty process to fetch a suggestion
+	zpty -w -n $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "${1}"$'\0'
+}
+
+# Called when new data is ready to be read from the pty
 # First arg will be fd ready for reading
 # Second arg will be passed in case of error
 _zsh_autosuggest_async_response() {
-	if [[ -z "$2" || "$2" == "hup" ]]; then
-		# Read everything from the fd and give it as a suggestion
-		zle autosuggest-suggest -- "$(cat <&$1)"
+	setopt LOCAL_OPTIONS EXTENDED_GLOB
 
-		# Close the fd
-		exec {1}<&-
+	local suggestion
+
+	zpty -rt $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME suggestion '*'$'\0' 2>/dev/null
+	zle autosuggest-suggest -- "${suggestion%%$'\0'##}"
+}
+
+_zsh_autosuggest_async_pty_create() {
+	# With newer versions of zsh, REPLY stores the fd to read from
+	typeset -h REPLY
+
+	# If we won't get a fd back from zpty, try to guess it
+	if (( ! $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD )); then
+		integer -l zptyfd
+		exec {zptyfd}>&1  # Open a new file descriptor (above 10).
+		exec {zptyfd}>&-  # Close it so it's free to be used by zpty.
 	fi
 
-	# Always remove the handler
-	zle -F "$1"
+	# Fork a zpty process running the server function
+	zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME _zsh_autosuggest_async_server
+
+	# Store the fd so we can remove the handler later
+	if (( REPLY )); then
+		_ZSH_AUTOSUGGEST_PTY_FD=$REPLY
+	else
+		_ZSH_AUTOSUGGEST_PTY_FD=$zptyfd
+	fi
+
+	# Set up input handler from the zpty
+	zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response
+}
+
+_zsh_autosuggest_async_pty_destroy() {
+	# Remove the input handler
+	zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
+
+	# Destroy the zpty
+	zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
+}
+
+_zsh_autosuggest_async_pty_recreate() {
+	_zsh_autosuggest_async_pty_destroy
+	_zsh_autosuggest_async_pty_create
+}
+
+_zsh_autosuggest_async_start() {
+	typeset -g _ZSH_AUTOSUGGEST_PTY_FD
+
+	_zsh_autosuggest_feature_detect_zpty_returns_fd
+	_zsh_autosuggest_async_pty_recreate
+
+	# We recreate the pty to get a fresh list of history events
+	add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate
 }
diff --git a/src/config.zsh b/src/config.zsh
index dc01b7c..9ac1484 100644
--- a/src/config.zsh
+++ b/src/config.zsh
@@ -69,5 +69,5 @@
 # Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
 : ${ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=}
 
-# Pty name for capturing completions for completion suggestion strategy
-: ${ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty}
+# Pty name for calculating autosuggestions asynchronously
+: ${ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty}
diff --git a/src/features.zsh b/src/features.zsh
new file mode 100644
index 0000000..7a5248f
--- /dev/null
+++ b/src/features.zsh
@@ -0,0 +1,19 @@
+
+#--------------------------------------------------------------------#
+# Feature Detection                                                  #
+#--------------------------------------------------------------------#
+
+_zsh_autosuggest_feature_detect_zpty_returns_fd() {
+	typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD
+	typeset -h REPLY
+
+	zpty zsh_autosuggest_feature_detect '{ zshexit() { kill -KILL $$; sleep 1 } }'
+
+	if (( REPLY )); then
+		_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1
+	else
+		_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0
+	fi
+
+	zpty -d zsh_autosuggest_feature_detect
+}
diff --git a/src/setup.zsh b/src/setup.zsh
new file mode 100644
index 0000000..c74489f
--- /dev/null
+++ b/src/setup.zsh
@@ -0,0 +1,10 @@
+
+#--------------------------------------------------------------------#
+# Setup                                                              #
+#--------------------------------------------------------------------#
+
+# Precmd hooks for initializing the library and starting pty's
+autoload -Uz add-zsh-hook
+
+# Asynchronous suggestions are generated in a pty
+zmodload zsh/zpty
diff --git a/src/start.zsh b/src/start.zsh
index ff93fdf..6f48ab6 100644
--- a/src/start.zsh
+++ b/src/start.zsh
@@ -14,8 +14,11 @@ _zsh_autosuggest_start() {
 	# zsh-syntax-highlighting widgets. This also allows modifications
 	# to the widget list variables to take effect on the next precmd.
 	add-zsh-hook precmd _zsh_autosuggest_bind_widgets
+
+	if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
+		_zsh_autosuggest_async_start
+	fi
 }
 
 # Start the autosuggestion widgets on the next precmd
-autoload -Uz add-zsh-hook
 add-zsh-hook precmd _zsh_autosuggest_start
diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh
deleted file mode 100644
index 7517822..0000000
--- a/src/strategies/completion.zsh
+++ /dev/null
@@ -1,104 +0,0 @@
-
-#--------------------------------------------------------------------#
-# Completion Suggestion Strategy                                     #
-#--------------------------------------------------------------------#
-# Fetches a suggestion from the completion engine
-#
-
-_zsh_autosuggest_capture_postcompletion() {
-	# Always insert the first completion into the buffer
-	compstate[insert]=1
-
-	# Don't list completions
-	unset compstate[list]
-}
-
-_zsh_autosuggest_capture_completion_widget() {
-	local -a +h comppostfuncs
-	comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
-
-	# Only capture completions at the end of the buffer
-	CURSOR=$#BUFFER
-
-	# Run the original widget wrapping `.complete-word` so we don't
-	# recursively try to fetch suggestions, since our pty is forked
-	# after autosuggestions is initialized.
-	zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]}
-
-	# The completion has been added, print the buffer as the suggestion
-	echo -nE - $'\0'$BUFFER$'\0'
-}
-
-zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
-
-_zsh_autosuggest_capture_setup() {
-	# There is a bug in zpty module in older zsh versions 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.
-	if ! is-at-least 5.4; then
-		zshexit() {
-			kill -KILL $$
-			sleep 1 # Block for long enough for the signal to come through
-		}
-	fi
-
-	bindkey '^I' autosuggest-capture-completion
-}
-
-_zsh_autosuggest_capture_completion_sync() {
-	_zsh_autosuggest_capture_setup
-
-	zle autosuggest-capture-completion
-}
-
-_zsh_autosuggest_capture_completion_async() {
-	_zsh_autosuggest_capture_setup
-
-	zmodload zsh/parameter 2>/dev/null || return # 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 1
-}
-
-_zsh_autosuggest_strategy_completion() {
-	typeset -g suggestion
-	local line REPLY
-
-	# Exit if we don't have completions
-	whence compdef >/dev/null || return
-
-	# Exit if we don't have zpty
-	zmodload zsh/zpty 2>/dev/null || return
-
-	# Zle will be inactive if we are in async mode
-	if zle; then
-		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync
-	else
-		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$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
-		suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}"
-	} always {
-		# Destroy the pty
-		zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
-	}
-}
diff --git a/src/widgets.zsh b/src/widgets.zsh
index 3312579..6a2be4a 100644
--- a/src/widgets.zsh
+++ b/src/widgets.zsh
@@ -95,7 +95,7 @@ _zsh_autosuggest_modify() {
 
 # Fetch a new suggestion based on what's currently in the buffer
 _zsh_autosuggest_fetch() {
-	if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
+	if zpty -t "$ZSH_AUTOSUGGEST_ASYNC_PTY_NAME" &>/dev/null; then
 		_zsh_autosuggest_async_request "$BUFFER"
 	else
 		local suggestion
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 5127015..1407559 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -25,6 +25,16 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 # OTHER DEALINGS IN THE SOFTWARE.
 
+#--------------------------------------------------------------------#
+# Setup                                                              #
+#--------------------------------------------------------------------#
+
+# Precmd hooks for initializing the library and starting pty's
+autoload -Uz add-zsh-hook
+
+# Asynchronous suggestions are generated in a pty
+zmodload zsh/zpty
+
 #--------------------------------------------------------------------#
 # Global Configuration Variables                                     #
 #--------------------------------------------------------------------#
@@ -95,8 +105,8 @@
 # Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
 : ${ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=}
 
-# Pty name for capturing completions for completion suggestion strategy
-: ${ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty}
+# Pty name for calculating autosuggestions asynchronously
+: ${ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty}
 
 #--------------------------------------------------------------------#
 # Utility Functions                                                  #
@@ -109,6 +119,25 @@ _zsh_autosuggest_escape_command() {
 	echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}"
 }
 
+#--------------------------------------------------------------------#
+# Feature Detection                                                  #
+#--------------------------------------------------------------------#
+
+_zsh_autosuggest_feature_detect_zpty_returns_fd() {
+	typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD
+	typeset -h REPLY
+
+	zpty zsh_autosuggest_feature_detect '{ zshexit() { kill -KILL $$; sleep 1 } }'
+
+	if (( REPLY )); then
+		_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1
+	else
+		_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0
+	fi
+
+	zpty -d zsh_autosuggest_feature_detect
+}
+
 #--------------------------------------------------------------------#
 # Widget Helpers                                                     #
 #--------------------------------------------------------------------#
@@ -351,7 +380,7 @@ _zsh_autosuggest_modify() {
 
 # Fetch a new suggestion based on what's currently in the buffer
 _zsh_autosuggest_fetch() {
-	if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
+	if zpty -t "$ZSH_AUTOSUGGEST_ASYNC_PTY_NAME" &>/dev/null; then
 		_zsh_autosuggest_async_request "$BUFFER"
 	else
 		local suggestion
@@ -472,110 +501,6 @@ zle -N autosuggest-enable _zsh_autosuggest_widget_enable
 zle -N autosuggest-disable _zsh_autosuggest_widget_disable
 zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 
-#--------------------------------------------------------------------#
-# Completion Suggestion Strategy                                     #
-#--------------------------------------------------------------------#
-# Fetches a suggestion from the completion engine
-#
-
-_zsh_autosuggest_capture_postcompletion() {
-	# Always insert the first completion into the buffer
-	compstate[insert]=1
-
-	# Don't list completions
-	unset compstate[list]
-}
-
-_zsh_autosuggest_capture_completion_widget() {
-	local -a +h comppostfuncs
-	comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
-
-	# Only capture completions at the end of the buffer
-	CURSOR=$#BUFFER
-
-	# Run the original widget wrapping `.complete-word` so we don't
-	# recursively try to fetch suggestions, since our pty is forked
-	# after autosuggestions is initialized.
-	zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]}
-
-	# The completion has been added, print the buffer as the suggestion
-	echo -nE - $'\0'$BUFFER$'\0'
-}
-
-zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
-
-_zsh_autosuggest_capture_setup() {
-	# There is a bug in zpty module in older zsh versions 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.
-	if ! is-at-least 5.4; then
-		zshexit() {
-			kill -KILL $$
-			sleep 1 # Block for long enough for the signal to come through
-		}
-	fi
-
-	bindkey '^I' autosuggest-capture-completion
-}
-
-_zsh_autosuggest_capture_completion_sync() {
-	_zsh_autosuggest_capture_setup
-
-	zle autosuggest-capture-completion
-}
-
-_zsh_autosuggest_capture_completion_async() {
-	_zsh_autosuggest_capture_setup
-
-	zmodload zsh/parameter 2>/dev/null || return # 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 1
-}
-
-_zsh_autosuggest_strategy_completion() {
-	typeset -g suggestion
-	local line REPLY
-
-	# Exit if we don't have completions
-	whence compdef >/dev/null || return
-
-	# Exit if we don't have zpty
-	zmodload zsh/zpty 2>/dev/null || return
-
-	# Zle will be inactive if we are in async mode
-	if zle; then
-		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync
-	else
-		zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$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
-		suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}"
-	} always {
-		# Destroy the pty
-		zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
-	}
-}
-
 #--------------------------------------------------------------------#
 # History Suggestion Strategy                                        #
 #--------------------------------------------------------------------#
@@ -688,63 +613,109 @@ _zsh_autosuggest_fetch_suggestion() {
 # Async                                                              #
 #--------------------------------------------------------------------#
 
-zmodload zsh/system
+# Zpty process is spawned running this function
+_zsh_autosuggest_async_server() {
+	emulate -R zsh
 
-_zsh_autosuggest_async_request() {
-	typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
+	# 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
+	}
 
-	# If we've got a pending request, cancel it
-	if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
-		# Close the file descriptor and remove the handler
-		exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
-		zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
+	# Don't add any extra carriage returns
+	stty -onlcr
 
-		# Zsh will make a new process group for the child process only if job
-		# control is enabled (MONITOR option)
-		if [[ -o MONITOR ]]; then
-			# Send the signal to the process group to kill any processes that may
-			# have been forked by the suggestion strategy
-			kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
-		else
-			# Kill just the child process since it wasn't placed in a new process
-			# group. If the suggestion strategy forked any child processes they may
-			# be orphaned and left behind.
-			kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
-		fi
-	fi
+	# Don't translate carriage returns to newlines
+	stty -icrnl
 
-	# Fork a process to fetch a suggestion and open a pipe to read from it
-	exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
-		# Tell parent process our pid
-		echo $sysparams[pid]
+	# Silence any error messages
+	exec 2>/dev/null
 
-		# Fetch and print the suggestion
-		local suggestion
-		_zsh_autosuggest_fetch_suggestion "$1"
-		echo -nE "$suggestion"
-	)
+	local last_pid
 
-	# Read the pid from the child process
-	read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD
+	while IFS='' read -r -d $'\0' query; do
+		# Kill last bg process
+		kill -KILL $last_pid &>/dev/null
 
-	# When the fd is readable, call the response handler
-	zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
+		# Run suggestion search in the background
+		(
+			local suggestion
+			_zsh_autosuggest_fetch_suggestion "$query"
+			echo -n -E "$suggestion"$'\0'
+		) &
+
+		last_pid=$!
+	done
 }
 
-# Called when new data is ready to be read from the pipe
+_zsh_autosuggest_async_request() {
+	# Write the query to the zpty process to fetch a suggestion
+	zpty -w -n $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "${1}"$'\0'
+}
+
+# Called when new data is ready to be read from the pty
 # First arg will be fd ready for reading
 # Second arg will be passed in case of error
 _zsh_autosuggest_async_response() {
-	if [[ -z "$2" || "$2" == "hup" ]]; then
-		# Read everything from the fd and give it as a suggestion
-		zle autosuggest-suggest -- "$(cat <&$1)"
+	setopt LOCAL_OPTIONS EXTENDED_GLOB
 
-		# Close the fd
-		exec {1}<&-
+	local suggestion
+
+	zpty -rt $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME suggestion '*'$'\0' 2>/dev/null
+	zle autosuggest-suggest -- "${suggestion%%$'\0'##}"
+}
+
+_zsh_autosuggest_async_pty_create() {
+	# With newer versions of zsh, REPLY stores the fd to read from
+	typeset -h REPLY
+
+	# If we won't get a fd back from zpty, try to guess it
+	if (( ! $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD )); then
+		integer -l zptyfd
+		exec {zptyfd}>&1  # Open a new file descriptor (above 10).
+		exec {zptyfd}>&-  # Close it so it's free to be used by zpty.
 	fi
 
-	# Always remove the handler
-	zle -F "$1"
+	# Fork a zpty process running the server function
+	zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME _zsh_autosuggest_async_server
+
+	# Store the fd so we can remove the handler later
+	if (( REPLY )); then
+		_ZSH_AUTOSUGGEST_PTY_FD=$REPLY
+	else
+		_ZSH_AUTOSUGGEST_PTY_FD=$zptyfd
+	fi
+
+	# Set up input handler from the zpty
+	zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response
+}
+
+_zsh_autosuggest_async_pty_destroy() {
+	# Remove the input handler
+	zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
+
+	# Destroy the zpty
+	zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
+}
+
+_zsh_autosuggest_async_pty_recreate() {
+	_zsh_autosuggest_async_pty_destroy
+	_zsh_autosuggest_async_pty_create
+}
+
+_zsh_autosuggest_async_start() {
+	typeset -g _ZSH_AUTOSUGGEST_PTY_FD
+
+	_zsh_autosuggest_feature_detect_zpty_returns_fd
+	_zsh_autosuggest_async_pty_recreate
+
+	# We recreate the pty to get a fresh list of history events
+	add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate
 }
 
 #--------------------------------------------------------------------#
@@ -762,8 +733,11 @@ _zsh_autosuggest_start() {
 	# zsh-syntax-highlighting widgets. This also allows modifications
 	# to the widget list variables to take effect on the next precmd.
 	add-zsh-hook precmd _zsh_autosuggest_bind_widgets
+
+	if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
+		_zsh_autosuggest_async_start
+	fi
 }
 
 # Start the autosuggestion widgets on the next precmd
-autoload -Uz add-zsh-hook
 add-zsh-hook precmd _zsh_autosuggest_start

From e937e89267afa1110b99e2fab0a4c42c2ea6dda7 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Sun, 9 Dec 2018 10:14:19 -0700
Subject: [PATCH 41/47] Respect user's set options when running original widget

Fixes GitHub #379
---
 spec/integrations/auto_cd_spec.rb    | 14 ++++++++++++++
 spec/integrations/glob_subst_spec.rb | 12 ++++++++++++
 src/widgets.zsh                      |  4 ++--
 zsh-autosuggestions.zsh              |  4 ++--
 4 files changed, 30 insertions(+), 4 deletions(-)
 create mode 100644 spec/integrations/auto_cd_spec.rb
 create mode 100644 spec/integrations/glob_subst_spec.rb

diff --git a/spec/integrations/auto_cd_spec.rb b/spec/integrations/auto_cd_spec.rb
new file mode 100644
index 0000000..94bd24b
--- /dev/null
+++ b/spec/integrations/auto_cd_spec.rb
@@ -0,0 +1,14 @@
+describe 'with `AUTO_CD` option set' do
+  let(:after_sourcing) do
+    -> {
+      session.run_command('setopt AUTO_CD')
+      session.run_command('autoload compinit && compinit')
+    }
+  end
+
+  it 'directory names are still completed' do
+    session.send_string('sr')
+    session.send_keys('C-i')
+    wait_for { session.content }.to eq('src/')
+  end
+end
diff --git a/spec/integrations/glob_subst_spec.rb b/spec/integrations/glob_subst_spec.rb
new file mode 100644
index 0000000..c3dd671
--- /dev/null
+++ b/spec/integrations/glob_subst_spec.rb
@@ -0,0 +1,12 @@
+describe 'with `GLOB_SUBST` option set' do
+  let(:after_sourcing) do
+    -> {
+      session.run_command('setopt GLOB_SUBST')
+    }
+  end
+
+  it 'error messages are not printed' do
+    session.send_string('[[')
+    wait_for { session.content }.to eq('[[')
+  end
+end
diff --git a/src/widgets.zsh b/src/widgets.zsh
index 6a2be4a..874bf46 100644
--- a/src/widgets.zsh
+++ b/src/widgets.zsh
@@ -37,8 +37,6 @@ _zsh_autosuggest_clear() {
 
 # Modify the buffer and get a new suggestion
 _zsh_autosuggest_modify() {
-	emulate -L zsh
-
 	local -i retval
 
 	# Only available in zsh >= 5.4
@@ -55,6 +53,8 @@ _zsh_autosuggest_modify() {
 	_zsh_autosuggest_invoke_original_widget $@
 	retval=$?
 
+	emulate -L zsh
+
 	# Don't fetch a new suggestion if there's more input to be read immediately
 	if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
 		POSTDISPLAY="$orig_postdisplay"
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 514d957..c1cc14a 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -322,8 +322,6 @@ _zsh_autosuggest_clear() {
 
 # Modify the buffer and get a new suggestion
 _zsh_autosuggest_modify() {
-	emulate -L zsh
-
 	local -i retval
 
 	# Only available in zsh >= 5.4
@@ -340,6 +338,8 @@ _zsh_autosuggest_modify() {
 	_zsh_autosuggest_invoke_original_widget $@
 	retval=$?
 
+	emulate -L zsh
+
 	# Don't fetch a new suggestion if there's more input to be read immediately
 	if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
 		POSTDISPLAY="$orig_postdisplay"

From aee1b10db6ab6a3cdc592ad2d2a3982cec997af9 Mon Sep 17 00:00:00 2001
From: dana <dana@dana.is>
Date: Wed, 19 Dec 2018 01:20:57 -0600
Subject: [PATCH 42/47] Avoid warn_create_global warnings

---
 src/config.zsh          | 114 +++++++++++++++++++-------------
 src/widgets.zsh         |  25 ++++----
 zsh-autosuggestions.zsh | 139 ++++++++++++++++++++++++----------------
 3 files changed, 164 insertions(+), 114 deletions(-)

diff --git a/src/config.zsh b/src/config.zsh
index 9ac1484..3487230 100644
--- a/src/config.zsh
+++ b/src/config.zsh
@@ -6,68 +6,90 @@
 # Color to use when highlighting suggestion
 # Uses format of `region_highlight`
 # More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
-: ${ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'}
+(( ! ${+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE} )) &&
+typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
 
 # Prefix to use when saving original versions of bound widgets
-: ${ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-}
+(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) &&
+typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
 
 # Strategies to use to fetch a suggestion
 # Will try each strategy in order until a suggestion is returned
-(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && ZSH_AUTOSUGGEST_STRATEGY=(history)
+(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_STRATEGY
+	ZSH_AUTOSUGGEST_STRATEGY=(history)
+}
 
 # Widgets that clear the suggestion
-(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
-	history-search-forward
-	history-search-backward
-	history-beginning-search-forward
-	history-beginning-search-backward
-	history-substring-search-up
-	history-substring-search-down
-	up-line-or-beginning-search
-	down-line-or-beginning-search
-	up-line-or-history
-	down-line-or-history
-	accept-line
-)
+(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS
+	ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
+		history-search-forward
+		history-search-backward
+		history-beginning-search-forward
+		history-beginning-search-backward
+		history-substring-search-up
+		history-substring-search-down
+		up-line-or-beginning-search
+		down-line-or-beginning-search
+		up-line-or-history
+		down-line-or-history
+		accept-line
+	)
+}
 
 # Widgets that accept the entire suggestion
-(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
-	forward-char
-	end-of-line
-	vi-forward-char
-	vi-end-of-line
-	vi-add-eol
-)
+(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS
+	ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
+		forward-char
+		end-of-line
+		vi-forward-char
+		vi-end-of-line
+		vi-add-eol
+	)
+}
 
 # Widgets that accept the entire suggestion and execute it
-(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
-)
+(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS
+	ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
+	)
+}
 
 # Widgets that accept the suggestion as far as the cursor moves
-(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
-	forward-word
-	emacs-forward-word
-	vi-forward-word
-	vi-forward-word-end
-	vi-forward-blank-word
-	vi-forward-blank-word-end
-	vi-find-next-char
-	vi-find-next-char-skip
-)
+(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS
+	ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
+		forward-word
+		emacs-forward-word
+		vi-forward-word
+		vi-forward-word-end
+		vi-forward-blank-word
+		vi-forward-blank-word-end
+		vi-find-next-char
+		vi-find-next-char-skip
+	)
+}
 
 # Widgets that should be ignored (globbing supported but must be escaped)
-(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
-	orig-\*
-	beep
-	run-help
-	set-local-history
-	which-command
-	yank
-	yank-pop
-)
+(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS
+	ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
+		orig-\*
+		beep
+		run-help
+		set-local-history
+		which-command
+		yank
+		yank-pop
+	)
+}
 
 # Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
-: ${ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=}
+(( ! ${+ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE} )) &&
+typeset -g ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
 
 # Pty name for calculating autosuggestions asynchronously
-: ${ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty}
+(( ! ${+ZSH_AUTOSUGGEST_ASYNC_PTY_NAME} )) &&
+typeset -g ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty
diff --git a/src/widgets.zsh b/src/widgets.zsh
index 874bf46..4a4ce24 100644
--- a/src/widgets.zsh
+++ b/src/widgets.zsh
@@ -190,22 +190,25 @@ _zsh_autosuggest_partial_accept() {
 	return $retval
 }
 
-for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
-	eval "_zsh_autosuggest_widget_$action() {
-		local -i retval
+() {
+	local action
+	for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
+		eval "_zsh_autosuggest_widget_$action() {
+			local -i retval
 
-		_zsh_autosuggest_highlight_reset
+			_zsh_autosuggest_highlight_reset
 
-		_zsh_autosuggest_$action \$@
-		retval=\$?
+			_zsh_autosuggest_$action \$@
+			retval=\$?
 
-		_zsh_autosuggest_highlight_apply
+			_zsh_autosuggest_highlight_apply
 
-		zle -R
+			zle -R
 
-		return \$retval
-	}"
-done
+			return \$retval
+		}"
+	done
+}
 
 zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
 zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index c1cc14a..535c82e 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -42,71 +42,93 @@ zmodload zsh/zpty
 # Color to use when highlighting suggestion
 # Uses format of `region_highlight`
 # More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
-: ${ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'}
+(( ! ${+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE} )) &&
+typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
 
 # Prefix to use when saving original versions of bound widgets
-: ${ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-}
+(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) &&
+typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
 
 # Strategies to use to fetch a suggestion
 # Will try each strategy in order until a suggestion is returned
-(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && ZSH_AUTOSUGGEST_STRATEGY=(history)
+(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_STRATEGY
+	ZSH_AUTOSUGGEST_STRATEGY=(history)
+}
 
 # Widgets that clear the suggestion
-(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
-	history-search-forward
-	history-search-backward
-	history-beginning-search-forward
-	history-beginning-search-backward
-	history-substring-search-up
-	history-substring-search-down
-	up-line-or-beginning-search
-	down-line-or-beginning-search
-	up-line-or-history
-	down-line-or-history
-	accept-line
-)
+(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS
+	ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
+		history-search-forward
+		history-search-backward
+		history-beginning-search-forward
+		history-beginning-search-backward
+		history-substring-search-up
+		history-substring-search-down
+		up-line-or-beginning-search
+		down-line-or-beginning-search
+		up-line-or-history
+		down-line-or-history
+		accept-line
+	)
+}
 
 # Widgets that accept the entire suggestion
-(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
-	forward-char
-	end-of-line
-	vi-forward-char
-	vi-end-of-line
-	vi-add-eol
-)
+(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS
+	ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
+		forward-char
+		end-of-line
+		vi-forward-char
+		vi-end-of-line
+		vi-add-eol
+	)
+}
 
 # Widgets that accept the entire suggestion and execute it
-(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
-)
+(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS
+	ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
+	)
+}
 
 # Widgets that accept the suggestion as far as the cursor moves
-(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
-	forward-word
-	emacs-forward-word
-	vi-forward-word
-	vi-forward-word-end
-	vi-forward-blank-word
-	vi-forward-blank-word-end
-	vi-find-next-char
-	vi-find-next-char-skip
-)
+(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS
+	ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
+		forward-word
+		emacs-forward-word
+		vi-forward-word
+		vi-forward-word-end
+		vi-forward-blank-word
+		vi-forward-blank-word-end
+		vi-find-next-char
+		vi-find-next-char-skip
+	)
+}
 
 # Widgets that should be ignored (globbing supported but must be escaped)
-(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
-	orig-\*
-	beep
-	run-help
-	set-local-history
-	which-command
-	yank
-	yank-pop
-)
+(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && {
+	typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS
+	ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
+		orig-\*
+		beep
+		run-help
+		set-local-history
+		which-command
+		yank
+		yank-pop
+	)
+}
 
 # Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
-: ${ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=}
+(( ! ${+ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE} )) &&
+typeset -g ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
 
 # Pty name for calculating autosuggestions asynchronously
-: ${ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty}
+(( ! ${+ZSH_AUTOSUGGEST_ASYNC_PTY_NAME} )) &&
+typeset -g ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty
 
 #--------------------------------------------------------------------#
 # Utility Functions                                                  #
@@ -475,22 +497,25 @@ _zsh_autosuggest_partial_accept() {
 	return $retval
 }
 
-for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
-	eval "_zsh_autosuggest_widget_$action() {
-		local -i retval
+() {
+	local action
+	for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
+		eval "_zsh_autosuggest_widget_$action() {
+			local -i retval
 
-		_zsh_autosuggest_highlight_reset
+			_zsh_autosuggest_highlight_reset
 
-		_zsh_autosuggest_$action \$@
-		retval=\$?
+			_zsh_autosuggest_$action \$@
+			retval=\$?
 
-		_zsh_autosuggest_highlight_apply
+			_zsh_autosuggest_highlight_apply
 
-		zle -R
+			zle -R
 
-		return \$retval
-	}"
-done
+			return \$retval
+		}"
+	done
+}
 
 zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
 zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest

From 50579b33716f2b64251f6f192b2a89612c77caf8 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Fri, 21 Dec 2018 23:20:08 -0700
Subject: [PATCH 43/47] Move widget definitions inside anonymous function

---
 src/widgets.zsh         | 18 +++++++++---------
 zsh-autosuggestions.zsh | 18 +++++++++---------
 2 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/src/widgets.zsh b/src/widgets.zsh
index 4a4ce24..1912064 100644
--- a/src/widgets.zsh
+++ b/src/widgets.zsh
@@ -208,13 +208,13 @@ _zsh_autosuggest_partial_accept() {
 			return \$retval
 		}"
 	done
-}
 
-zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
-zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest
-zle -N autosuggest-accept _zsh_autosuggest_widget_accept
-zle -N autosuggest-clear _zsh_autosuggest_widget_clear
-zle -N autosuggest-execute _zsh_autosuggest_widget_execute
-zle -N autosuggest-enable _zsh_autosuggest_widget_enable
-zle -N autosuggest-disable _zsh_autosuggest_widget_disable
-zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
+	zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
+	zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest
+	zle -N autosuggest-accept _zsh_autosuggest_widget_accept
+	zle -N autosuggest-clear _zsh_autosuggest_widget_clear
+	zle -N autosuggest-execute _zsh_autosuggest_widget_execute
+	zle -N autosuggest-enable _zsh_autosuggest_widget_enable
+	zle -N autosuggest-disable _zsh_autosuggest_widget_disable
+	zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
+}
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 535c82e..5bb5cd7 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -515,16 +515,16 @@ _zsh_autosuggest_partial_accept() {
 			return \$retval
 		}"
 	done
-}
 
-zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
-zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest
-zle -N autosuggest-accept _zsh_autosuggest_widget_accept
-zle -N autosuggest-clear _zsh_autosuggest_widget_clear
-zle -N autosuggest-execute _zsh_autosuggest_widget_execute
-zle -N autosuggest-enable _zsh_autosuggest_widget_enable
-zle -N autosuggest-disable _zsh_autosuggest_widget_disable
-zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
+	zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
+	zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest
+	zle -N autosuggest-accept _zsh_autosuggest_widget_accept
+	zle -N autosuggest-clear _zsh_autosuggest_widget_clear
+	zle -N autosuggest-execute _zsh_autosuggest_widget_execute
+	zle -N autosuggest-enable _zsh_autosuggest_widget_enable
+	zle -N autosuggest-disable _zsh_autosuggest_widget_disable
+	zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
+}
 
 #--------------------------------------------------------------------#
 # History Suggestion Strategy                                        #

From f76472272e9a40a27a5b589f7ed3cf605c2993c4 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Mon, 1 Apr 2019 14:36:31 -0600
Subject: [PATCH 44/47] cleanup: Remove unnecessary braces

---
 src/bind.zsh            | 6 +++---
 zsh-autosuggestions.zsh | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/bind.zsh b/src/bind.zsh
index bb41ef8..ec762ef 100644
--- a/src/bind.zsh
+++ b/src/bind.zsh
@@ -39,20 +39,20 @@ _zsh_autosuggest_bind_widget() {
 		# User-defined widget
 		user:*)
 			_zsh_autosuggest_incr_bind_count $widget
-			zle -N $prefix${bind_count}-$widget ${widgets[$widget]#*:}
+			zle -N $prefix$bind_count-$widget ${widgets[$widget]#*:}
 			;;
 
 		# Built-in widget
 		builtin)
 			_zsh_autosuggest_incr_bind_count $widget
 			eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }"
-			zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget
+			zle -N $prefix$bind_count-$widget _zsh_autosuggest_orig_$widget
 			;;
 
 		# Completion widget
 		completion:*)
 			_zsh_autosuggest_incr_bind_count $widget
-			eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
+			eval "zle -C $prefix$bind_count-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
 			;;
 	esac
 
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 5bb5cd7..67cb5c9 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -200,20 +200,20 @@ _zsh_autosuggest_bind_widget() {
 		# User-defined widget
 		user:*)
 			_zsh_autosuggest_incr_bind_count $widget
-			zle -N $prefix${bind_count}-$widget ${widgets[$widget]#*:}
+			zle -N $prefix$bind_count-$widget ${widgets[$widget]#*:}
 			;;
 
 		# Built-in widget
 		builtin)
 			_zsh_autosuggest_incr_bind_count $widget
 			eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }"
-			zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget
+			zle -N $prefix$bind_count-$widget _zsh_autosuggest_orig_$widget
 			;;
 
 		# Completion widget
 		completion:*)
 			_zsh_autosuggest_incr_bind_count $widget
-			eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
+			eval "zle -C $prefix$bind_count-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
 			;;
 	esac
 

From 4a82ff1ead68db25645b2e920f17774a66e44e31 Mon Sep 17 00:00:00 2001
From: romkatv <roman.perepelitsa@gmail.com>
Date: Mon, 25 Feb 2019 12:59:31 +0100
Subject: [PATCH 45/47] speed up widget rebinding by removing redundant array
 subscripts

---
 src/bind.zsh            | 23 +++++------------------
 zsh-autosuggestions.zsh | 23 +++++------------------
 2 files changed, 10 insertions(+), 36 deletions(-)

diff --git a/src/bind.zsh b/src/bind.zsh
index ec762ef..a2e86e1 100644
--- a/src/bind.zsh
+++ b/src/bind.zsh
@@ -4,21 +4,8 @@
 #--------------------------------------------------------------------#
 
 _zsh_autosuggest_incr_bind_count() {
-	if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
-		((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]++))
-	else
-		_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=1
-	fi
-
-	typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
-}
-
-_zsh_autosuggest_get_bind_count() {
-	if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
-		typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
-	else
-		typeset -gi bind_count=0
-	fi
+	typeset -gi bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]+1))
+	_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=$bind_count
 }
 
 # Bind a single widget to an autosuggest widget, saving a reference to the original widget
@@ -34,7 +21,9 @@ _zsh_autosuggest_bind_widget() {
 	# Save a reference to the original widget
 	case $widgets[$widget] in
 		# Already bound
-		user:_zsh_autosuggest_(bound|orig)_*);;
+		user:_zsh_autosuggest_(bound|orig)_*)
+			bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$widget]))
+			;;
 
 		# User-defined widget
 		user:*)
@@ -56,8 +45,6 @@ _zsh_autosuggest_bind_widget() {
 			;;
 	esac
 
-	_zsh_autosuggest_get_bind_count $widget
-
 	# Pass the original widget's name explicitly into the autosuggest
 	# function. Use this passed in widget name to call the original
 	# widget instead of relying on the $WIDGET variable being set
diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh
index 67cb5c9..a2edce6 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -165,21 +165,8 @@ _zsh_autosuggest_feature_detect_zpty_returns_fd() {
 #--------------------------------------------------------------------#
 
 _zsh_autosuggest_incr_bind_count() {
-	if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
-		((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]++))
-	else
-		_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=1
-	fi
-
-	typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
-}
-
-_zsh_autosuggest_get_bind_count() {
-	if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
-		typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
-	else
-		typeset -gi bind_count=0
-	fi
+	typeset -gi bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]+1))
+	_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=$bind_count
 }
 
 # Bind a single widget to an autosuggest widget, saving a reference to the original widget
@@ -195,7 +182,9 @@ _zsh_autosuggest_bind_widget() {
 	# Save a reference to the original widget
 	case $widgets[$widget] in
 		# Already bound
-		user:_zsh_autosuggest_(bound|orig)_*);;
+		user:_zsh_autosuggest_(bound|orig)_*)
+			bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$widget]))
+			;;
 
 		# User-defined widget
 		user:*)
@@ -217,8 +206,6 @@ _zsh_autosuggest_bind_widget() {
 			;;
 	esac
 
-	_zsh_autosuggest_get_bind_count $widget
-
 	# Pass the original widget's name explicitly into the autosuggest
 	# function. Use this passed in widget name to call the original
 	# widget instead of relying on the $WIDGET variable being set

From 3ee91c731cfddf8203121cfc76af71d843a09294 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 3 Apr 2019 10:51:48 -0600
Subject: [PATCH 46/47] Update changelog for v0.5.1 release

---
 CHANGELOG.md | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 387071b..64a81f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## v0.5.1
+- Speed up widget rebinding (#413)
+- Clean up global variable creations (#403)
+- Respect user's set options when running original widget (#402)
+
 ## v0.5.0
 - Don't overwrite config with default values (#335)
 - Support fallback strategies by supplying array to suggestion config var

From f94e667f59b8d8a030b4b45477d5e8b57bac102c Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Wed, 3 Apr 2019 10:52:43 -0600
Subject: [PATCH 47/47] v0.5.1

---
 VERSION | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/VERSION b/VERSION
index b043aa6..992ac75 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.5.0
+v0.5.1