From c52c428793fc4ea2326b9ad0860853959b0d2954 Mon Sep 17 00:00:00 2001
From: Eric Freese <ericdfreese@gmail.com>
Date: Tue, 28 Feb 2017 11:14:16 -0700
Subject: [PATCH] Fix issues with widgets wrapped by other plugins

Puts in a better fix for #126 and related issues.
---
 spec/integrations/wrapped_widget_spec.rb | 39 +++++++++++++++++++++++
 src/bind.zsh                             | 40 +++++++++++++++++++-----
 zsh-autosuggestions.zsh                  | 40 +++++++++++++++++++-----
 3 files changed, 105 insertions(+), 14 deletions(-)
 create mode 100644 spec/integrations/wrapped_widget_spec.rb

diff --git a/spec/integrations/wrapped_widget_spec.rb b/spec/integrations/wrapped_widget_spec.rb
new file mode 100644
index 0000000..61dfc2d
--- /dev/null
+++ b/spec/integrations/wrapped_widget_spec.rb
@@ -0,0 +1,39 @@
+describe 'a wrapped widget' do
+  let(:widget) { 'backward-delete-char' }
+
+  context 'initialized before sourcing the plugin' do
+    let(:before_sourcing) do
+      -> do
+        session.
+          run_command("_orig_#{widget}() { zle .#{widget} }").
+          run_command("zle -N orig-#{widget} _orig_#{widget}").
+          run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }").
+          run_command("zle -N #{widget} #{widget}-magic")
+      end
+    end
+
+    it 'executes the custom behavior and the built-in behavior' do
+      with_history('foobar', 'foodar') do
+        session.send_string('food').send_keys('C-h')
+        wait_for { session.content }.to eq('foobar')
+      end
+    end
+  end
+
+  context 'initialized after sourcing the plugin' do
+    before do
+      session.
+        run_command("zle -N orig-#{widget} ${widgets[#{widget}]#*:}").
+        run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }").
+        run_command("zle -N #{widget} #{widget}-magic").
+        clear_screen
+    end
+
+    it 'executes the custom behavior and the built-in behavior' do
+      with_history('foobar', 'foodar') do
+        session.send_string('food').send_keys('C-h')
+        wait_for { session.content }.to eq('foobar')
+      end
+    end
+  end
+end
diff --git a/src/bind.zsh b/src/bind.zsh
index e2ca5c9..d8629f1 100644
--- a/src/bind.zsh
+++ b/src/bind.zsh
@@ -3,12 +3,34 @@
 # Widget Helpers                                                     #
 #--------------------------------------------------------------------#
 
+_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
+
+	bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
+}
+
+_zsh_autosuggest_get_bind_count() {
+	if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
+		bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
+	else
+		bind_count=0
+	fi
+}
+
 # Bind a single widget to an autosuggest widget, saving a reference to the original widget
 _zsh_autosuggest_bind_widget() {
+	typeset -gA _ZSH_AUTOSUGGEST_BIND_COUNTS
+
 	local widget=$1
 	local autosuggest_action=$2
 	local prefix=$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX
 
+	local -i bind_count
+
 	# Save a reference to the original widget
 	case $widgets[$widget] in
 		# Already bound
@@ -16,34 +38,38 @@ _zsh_autosuggest_bind_widget() {
 
 		# User-defined widget
 		user:*)
-			zle -l "$prefix$widget" && zle -N "$widget" ${widgets[$prefix$widget]#*:}
-			zle -N $prefix$widget ${widgets[$widget]#*:}
+			_zsh_autosuggest_incr_bind_count $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$widget _zsh_autosuggest_orig_$widget
+			zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget
 			;;
 
 		# Completion widget
 		completion:*)
-			eval "zle -C $prefix${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
+			_zsh_autosuggest_incr_bind_count $widget
+			eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
 			;;
 	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
 	# correctly. $WIDGET cannot be trusted because other plugins call
 	# zle without the `-w` flag (e.g. `zle self-insert` instead of
 	# `zle self-insert -w`).
-	eval "_zsh_autosuggest_bound_${(q)widget}() {
-		_zsh_autosuggest_widget_$autosuggest_action $prefix${(q)widget} \$@
+	eval "_zsh_autosuggest_bound_${bind_count}_${(q)widget}() {
+		_zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@
 	}"
 
 	# Create the bound widget
-	zle -N $widget _zsh_autosuggest_bound_$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 34ca080..05e1bb4 100644
--- a/zsh-autosuggestions.zsh
+++ b/zsh-autosuggestions.zsh
@@ -137,12 +137,34 @@ _zsh_autosuggest_feature_detect_zpty_returns_fd() {
 # Widget Helpers                                                     #
 #--------------------------------------------------------------------#
 
+_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
+
+	bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
+}
+
+_zsh_autosuggest_get_bind_count() {
+	if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
+		bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
+	else
+		bind_count=0
+	fi
+}
+
 # Bind a single widget to an autosuggest widget, saving a reference to the original widget
 _zsh_autosuggest_bind_widget() {
+	typeset -gA _ZSH_AUTOSUGGEST_BIND_COUNTS
+
 	local widget=$1
 	local autosuggest_action=$2
 	local prefix=$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX
 
+	local -i bind_count
+
 	# Save a reference to the original widget
 	case $widgets[$widget] in
 		# Already bound
@@ -150,34 +172,38 @@ _zsh_autosuggest_bind_widget() {
 
 		# User-defined widget
 		user:*)
-			zle -l "$prefix$widget" && zle -N "$widget" ${widgets[$prefix$widget]#*:}
-			zle -N $prefix$widget ${widgets[$widget]#*:}
+			_zsh_autosuggest_incr_bind_count $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$widget _zsh_autosuggest_orig_$widget
+			zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget
 			;;
 
 		# Completion widget
 		completion:*)
-			eval "zle -C $prefix${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
+			_zsh_autosuggest_incr_bind_count $widget
+			eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
 			;;
 	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
 	# correctly. $WIDGET cannot be trusted because other plugins call
 	# zle without the `-w` flag (e.g. `zle self-insert` instead of
 	# `zle self-insert -w`).
-	eval "_zsh_autosuggest_bound_${(q)widget}() {
-		_zsh_autosuggest_widget_$autosuggest_action $prefix${(q)widget} \$@
+	eval "_zsh_autosuggest_bound_${bind_count}_${(q)widget}() {
+		_zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@
 	}"
 
 	# Create the bound widget
-	zle -N $widget _zsh_autosuggest_bound_$widget
+	zle -N $widget _zsh_autosuggest_bound_${bind_count}_$widget
 }
 
 # Map all configured widgets to the right autosuggest widgets