Update autosuggestion plugin
authorJoerg Jaspert <joerg@debian.org>
Wed, 15 Nov 2017 08:40:15 +0000 (09:40 +0100)
committerJoerg Jaspert <joerg@debian.org>
Wed, 15 Nov 2017 08:40:15 +0000 (09:40 +0100)
60 files changed:
.zsh/external/zsh-autosuggestions/.editorconfig
.zsh/external/zsh-autosuggestions/.gitmodules [deleted file]
.zsh/external/zsh-autosuggestions/.rspec [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/.rubocop.yml [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/.ruby-version [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/CHANGELOG.md
.zsh/external/zsh-autosuggestions/Gemfile [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/Gemfile.lock [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/LICENSE
.zsh/external/zsh-autosuggestions/Makefile
.zsh/external/zsh-autosuggestions/README.md
.zsh/external/zsh-autosuggestions/VERSION
.zsh/external/zsh-autosuggestions/circle.yml [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/script/test_runner.zsh [deleted file]
.zsh/external/zsh-autosuggestions/spec/integrations/bracketed_paste_magic_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/integrations/client_zpty_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/integrations/wrapped_widget_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/integrations/zle_input_stack_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/multi_line_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/options/async_zpty_name_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/options/buffer_max_size_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/options/highlight_style_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/options/original_widget_prefix_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/options/strategy_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/options/use_async_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/options/widget_lists_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/spec_helper.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/special_characters_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/strategies/default_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/strategies/match_prev_cmd_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/terminal_session.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/widgets/disable_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/widgets/enable_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/widgets/fetch_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/spec/widgets/toggle_spec.rb [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/src/async.zsh [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/src/bind.zsh
.zsh/external/zsh-autosuggestions/src/config.zsh
.zsh/external/zsh-autosuggestions/src/deprecated.zsh [deleted file]
.zsh/external/zsh-autosuggestions/src/features.zsh [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/src/setup.zsh [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/src/start.zsh
.zsh/external/zsh-autosuggestions/src/strategies/default.zsh
.zsh/external/zsh-autosuggestions/src/strategies/match_prev_cmd.zsh
.zsh/external/zsh-autosuggestions/src/suggestion.zsh [deleted file]
.zsh/external/zsh-autosuggestions/src/util.zsh [new file with mode: 0644]
.zsh/external/zsh-autosuggestions/src/widgets.zsh
.zsh/external/zsh-autosuggestions/test/bind_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/highlight_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/strategies/default_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/strategies/match_prev_cmd_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/strategies_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/suggestion_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/test_helper.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/widgets/accept_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/widgets/clear_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/widgets/execute_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/widgets/modify_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/test/widgets/partial_accept_test.zsh [deleted file]
.zsh/external/zsh-autosuggestions/zsh-autosuggestions.zsh

index 51c4765..ddabb17 100644 (file)
@@ -8,3 +8,11 @@ indent_size = 4
 
 [*.md]
 indent_style = space
+
+[*.rb]
+indent_style = space
+indent_size = 2
+
+[*.yml]
+indent_style = space
+indent_size = 2
diff --git a/.zsh/external/zsh-autosuggestions/.gitmodules b/.zsh/external/zsh-autosuggestions/.gitmodules
deleted file mode 100644 (file)
index b45eb46..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[submodule "vendor/shunit2"]
-       path = vendor/shunit2
-       url = https://github.com/kward/shunit2
-[submodule "vendor/stub.sh"]
-       path = vendor/stub.sh
-       url = https://github.com/ericfreese/stub.sh
diff --git a/.zsh/external/zsh-autosuggestions/.rspec b/.zsh/external/zsh-autosuggestions/.rspec
new file mode 100644 (file)
index 0000000..43ae203
--- /dev/null
@@ -0,0 +1,3 @@
+--color
+--require spec_helper
+--format documentation
diff --git a/.zsh/external/zsh-autosuggestions/.rubocop.yml b/.zsh/external/zsh-autosuggestions/.rubocop.yml
new file mode 100644 (file)
index 0000000..9e0792f
--- /dev/null
@@ -0,0 +1,30 @@
+# Rails:
+#   Enabled: true
+
+AllCops:
+       TargetRubyVersion: 2.3
+       Include:
+               - '**/Rakefile'
+               - '**/config.ru'
+               - '**/Gemfile'
+
+Metrics/LineLength:
+       Max: 120
+
+Style/Documentation:
+       Enabled: false
+
+Style/DotPosition:
+       EnforcedStyle: trailing
+
+Style/FrozenStringLiteralComment:
+       Enabled: false
+
+Style/Lambda:
+       Enabled: false
+
+Style/MultilineMethodCallIndentation:
+       EnforcedStyle: indented
+
+Style/TrailingUnderscoreVariable:
+       Enabled: false
diff --git a/.zsh/external/zsh-autosuggestions/.ruby-version b/.zsh/external/zsh-autosuggestions/.ruby-version
new file mode 100644 (file)
index 0000000..2bf1c1c
--- /dev/null
@@ -0,0 +1 @@
+2.3.1
index 50a1e0a..ac0f8e6 100644 (file)
@@ -1,5 +1,18 @@
 # Changelog
 
+## v0.4.0
+- High-level integration tests using RSpec and tmux
+- Add continuous integration with Circle CI
+- Experimental support for asynchronous suggestions (#)
+- Fix problems with multi-line suggestions (#)
+- Optimize case where manually typing in suggestion
+- Avoid wrapping any zle-* widgets (#)
+- Remove support for deprecated options from v0.0.x
+- Handle history entries that begin with dashes (#)
+- Gracefully handle being sourced multiple times (#126)
+- Add enable/disable/toggle widgets to disable/enable suggestions (#219)
+
+
 ## v0.3.3
 - Switch from $history array to fc builtin for better performance with large HISTFILEs (#164)
 - Fix tilde handling when extended_glob is set (#168)
diff --git a/.zsh/external/zsh-autosuggestions/Gemfile b/.zsh/external/zsh-autosuggestions/Gemfile
new file mode 100644 (file)
index 0000000..8b5deec
--- /dev/null
@@ -0,0 +1,5 @@
+source 'https://rubygems.org'
+
+gem 'rspec'
+gem 'rspec-wait'
+gem 'pry-byebug'
diff --git a/.zsh/external/zsh-autosuggestions/Gemfile.lock b/.zsh/external/zsh-autosuggestions/Gemfile.lock
new file mode 100644 (file)
index 0000000..63ee778
--- /dev/null
@@ -0,0 +1,41 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    byebug (9.0.5)
+    coderay (1.1.1)
+    diff-lcs (1.3)
+    method_source (0.8.2)
+    pry (0.10.4)
+      coderay (~> 1.1.0)
+      method_source (~> 0.8.1)
+      slop (~> 3.4)
+    pry-byebug (3.4.0)
+      byebug (~> 9.0)
+      pry (~> 0.10)
+    rspec (3.5.0)
+      rspec-core (~> 3.5.0)
+      rspec-expectations (~> 3.5.0)
+      rspec-mocks (~> 3.5.0)
+    rspec-core (3.5.4)
+      rspec-support (~> 3.5.0)
+    rspec-expectations (3.5.0)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.5.0)
+    rspec-mocks (3.5.0)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.5.0)
+    rspec-support (3.5.0)
+    rspec-wait (0.0.9)
+      rspec (>= 3, < 4)
+    slop (3.6.0)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  pry-byebug
+  rspec
+  rspec-wait
+
+BUNDLED WITH
+   1.13.6
index ee52ee2..ad6594e 100644 (file)
@@ -1,5 +1,5 @@
 Copyright (c) 2013 Thiago de Arruda
-Copyright (c) 2016 Eric Freese
+Copyright (c) 2016-2017 Eric Freese
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
index fde3691..d5d162c 100644 (file)
@@ -1,14 +1,15 @@
 SRC_DIR    := ./src
-VENDOR_DIR := ./vendor
 
 SRC_FILES := \
+       $(SRC_DIR)/setup.zsh \
        $(SRC_DIR)/config.zsh \
-       $(SRC_DIR)/deprecated.zsh \
+       $(SRC_DIR)/util.zsh \
+       $(SRC_DIR)/features.zsh \
        $(SRC_DIR)/bind.zsh \
        $(SRC_DIR)/highlight.zsh \
        $(SRC_DIR)/widgets.zsh \
-       $(SRC_DIR)/suggestion.zsh \
        $(SRC_DIR)/strategies/*.zsh \
+       $(SRC_DIR)/async.zsh \
        $(SRC_DIR)/start.zsh
 
 HEADER_FILES := \
@@ -19,29 +20,17 @@ HEADER_FILES := \
 
 PLUGIN_TARGET := zsh-autosuggestions.zsh
 
-SHUNIT2 := $(VENDOR_DIR)/shunit2/2.1.6
-STUB_SH := $(VENDOR_DIR)/stub.sh/stub.sh
-
-TEST_PREREQS := \
-       $(SHUNIT2) \
-       $(STUB_SH)
-
 all: $(PLUGIN_TARGET)
 
 $(PLUGIN_TARGET): $(HEADER_FILES) $(SRC_FILES)
        cat $(HEADER_FILES) | sed -e 's/^/# /g' > $@
        cat $(SRC_FILES) >> $@
 
-$(SHUNIT2):
-       git submodule update --init vendor/shunit2
-
-$(STUB_SH):
-       git submodule update --init vendor/stub.sh
-
 .PHONY: clean
 clean:
        rm $(PLUGIN_TARGET)
 
 .PHONY: test
-test: all $(TEST_PREREQS)
-       script/test_runner.zsh $(TESTS)
+test: all
+       @test -n "$$TEST_ZSH_BIN" && echo "Testing zsh binary: $(TEST_ZSH_BIN)" || true
+       bundle exec rspec $(TESTS)
index 3a5c3f3..4df4e90 100644 (file)
@@ -4,6 +4,8 @@ _[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._
 
 It suggests commands as you type, based on command history.
 
+[![CircleCI](https://circleci.com/gh/zsh-users/zsh-autosuggestions.svg?style=svg)](https://circleci.com/gh/zsh-users/zsh-autosuggestions)
+
 <a href="https://asciinema.org/a/37390" target="_blank"><img src="https://asciinema.org/a/37390.png" width="400" /></a>
 
 
@@ -42,6 +44,39 @@ It suggests commands as you type, based on command history.
 
 3. Start a new terminal session.
 
+### Arch Linux via the AUR
+1. Install the [`zsh-autosuggestions`](https://aur.archlinux.org/packages/zsh-autosuggestions/) or the [`zsh-autosuggestions-git`](https://aur.archlinux.org/packages/zsh-autosuggestions-git/) packages from the [AUR](https://wiki.archlinux.org/index.php/Arch_User_Repository).
+
+    ```sh
+    pacaur -S zsh-autosuggestions
+    ```
+    or
+    ```
+    pacaur -S zsh-autosuggestions-git
+    ```
+
+2. Add the following to your `.zshrc`:
+
+    ```sh
+    source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
+    ```
+
+3. Start a new terminal session.
+
+### macOS via Homebrew
+1. Install the `zsh-autosuggestions` package using [Homebrew](https://brew.sh/).
+
+    ```sh
+    brew install zsh-autosuggestions
+    ```
+
+2. Add the following to your `.zshrc`:
+
+    ```sh
+    source /usr/local/share/zsh-autosuggestions/zsh-autosuggestions.zsh
+    ```
+
+3. Start a new terminal session.
 
 ## Usage
 
@@ -92,14 +127,22 @@ Widgets that modify the buffer and are not found in any of these arrays will fet
 Set `ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE` to an integer value to disable autosuggestion for large buffers. The default is unset, which means that autosuggestion will be tried for any buffer size. Recommended value is 20.
 This can be useful when pasting large amount of text in the terminal, to avoid triggering autosuggestion for too long strings.
 
+### 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).
+
 
 ### Key Bindings
 
-This plugin provides three widgets that you can use with `bindkey`:
+This plugin provides a few widgets that you can use with `bindkey`:
 
 1. `autosuggest-accept`: Accepts the current suggestion.
 2. `autosuggest-execute`: Accepts and executes the current suggestion.
 3. `autosuggest-clear`: Clears the current suggestion.
+4. `autosuggest-fetch`: Fetches a suggestion (works even when suggestions are disabled).
+5. `autosuggest-disable`: Disables suggestions.
+6. `autosuggest-enable`: Re-enables suggestions.
+7. `autosuggest-toggle`: Toggles between enabled/disabled suggestions.
 
 For example, this would bind <kbd>ctrl</kbd> + <kbd>space</kbd> to accept the current suggestion.
 
@@ -154,9 +197,9 @@ Pull requests are welcome! If you send a pull request, please:
 
 ### Testing
 
-Testing is performed with [`shunit2`](https://github.com/kward/shunit2) (v2.1.6). Documentation can be found [here](http://shunit2.googlecode.com/svn/trunk/source/2.1/doc/shunit2.html).
+Tests are written in ruby using the [`rspec`](http://rspec.info/) framework. They use [`tmux`](https://tmux.github.io/) to drive a pseudoterminal, sending simulated keystrokes and making assertions on the terminal content.
 
-The test script lives at `script/test_runner.zsh`. To run the tests, run `make test`.
+Test files live in `spec/`. To run the tests, run `make test`. To run a specific test, run `TESTS=spec/some_spec.rb make test`. You can also specify a `zsh` binary to use by setting the `TEST_ZSH_BIN` environment variable (ex: `TEST_ZSH_BIN=/bin/zsh make test`).
 
 
 ## License
diff --git a/.zsh/external/zsh-autosuggestions/circle.yml b/.zsh/external/zsh-autosuggestions/circle.yml
new file mode 100644 (file)
index 0000000..5e3a6f6
--- /dev/null
@@ -0,0 +1,12 @@
+machine:
+  environment:
+    ZSH_VERSIONS: 5.0.8 5.1.1 5.2 5.3.1
+
+dependencies:
+  pre:
+    - for v in $(echo $ZSH_VERSIONS | awk "{ for (i=$((1+CIRCLE_NODE_INDEX));i<=NF;i+=$CIRCLE_NODE_TOTAL) print \$i }"); do wget https://sourceforge.net/projects/zsh/files/zsh/$v/zsh-$v.tar.gz && tar xzf zsh-$v.tar.gz && cd zsh-$v && ./configure && sudo make install || exit 1; done
+
+test:
+  override:
+    - for v in $(echo $ZSH_VERSIONS | awk "{ for (i=$((1+CIRCLE_NODE_INDEX));i<=NF;i+=$CIRCLE_NODE_TOTAL) print \$i }"); do TEST_ZSH_BIN=/usr/local/bin/zsh-$v make test || exit 1; done:
+        parallel: true
diff --git a/.zsh/external/zsh-autosuggestions/script/test_runner.zsh b/.zsh/external/zsh-autosuggestions/script/test_runner.zsh
deleted file mode 100755 (executable)
index 0ff4173..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env zsh
-
-DIR="${0:a:h}"
-ROOT_DIR="$DIR/.."
-TEST_DIR="$ROOT_DIR/test"
-
-header() {
-       local message="$1"
-
-       cat <<-EOF
-
-#====================================================================#
-# $message
-#====================================================================#
-       EOF
-}
-
-# ZSH binary to use
-local zsh_bin="zsh"
-
-while getopts ":z:" opt; do
-       case $opt in
-               z)
-                       zsh_bin="$OPTARG"
-                       ;;
-               \?)
-                       echo "Invalid option: -$OPTARG" >&2
-                       exit 1
-                       ;;
-               :)
-                       echo "Option -$OPTARG requires an argument" >&2
-                       exit 1
-                       ;;
-       esac
-done
-
-shift $((OPTIND -1))
-
-# Test suites to run
-local -a tests
-if [ $#@ -gt 0 ]; then
-       tests=($@)
-else
-       tests=($TEST_DIR/**/*_test.zsh)
-fi
-
-local -i retval=0
-
-for suite in $tests; do
-       header "${suite#"$ROOT_DIR/"}"
-       "$zsh_bin" -f "$suite" || retval=$?
-done
-
-exit $retval
diff --git a/.zsh/external/zsh-autosuggestions/spec/integrations/bracketed_paste_magic_spec.rb b/.zsh/external/zsh-autosuggestions/spec/integrations/bracketed_paste_magic_spec.rb
new file mode 100644 (file)
index 0000000..f01b0e0
--- /dev/null
@@ -0,0 +1,36 @@
+describe 'pasting using bracketed-paste-magic' do
+  let(:before_sourcing) do
+    -> do
+      session.
+        run_command('autoload -Uz bracketed-paste-magic').
+        run_command('zle -N bracketed-paste bracketed-paste-magic')
+    end
+  end
+
+  context 'with suggestions disabled while pasting' do
+    before do
+      session.
+        run_command('bpm_init() { zle autosuggest-disable }').
+        run_command('bpm_finish() { zle autosuggest-enable }').
+        run_command('zstyle :bracketed-paste-magic paste-init bpm_init').
+        run_command('zstyle :bracketed-paste-magic paste-finish bpm_finish')
+    end
+
+    it 'does not show an incorrect suggestion' do
+      with_history('echo hello') do
+        session.paste_string("echo #{'a' * 60}")
+        sleep 1
+        expect(session.content).to eq("echo #{'a' * 60}")
+      end
+    end
+
+    it 'shows a suggestion after a non-modifying keystroke' do
+      with_history('echo hello') do
+        session.paste_string('echo')
+        sleep 1
+        session.send_keys('left')
+        wait_for { session.content }.to eq('echo hello')
+      end
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/integrations/client_zpty_spec.rb b/.zsh/external/zsh-autosuggestions/spec/integrations/client_zpty_spec.rb
new file mode 100644 (file)
index 0000000..8f1550e
--- /dev/null
@@ -0,0 +1,10 @@
+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/.zsh/external/zsh-autosuggestions/spec/integrations/wrapped_widget_spec.rb b/.zsh/external/zsh-autosuggestions/spec/integrations/wrapped_widget_spec.rb
new file mode 100644 (file)
index 0000000..61dfc2d
--- /dev/null
@@ -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/.zsh/external/zsh-autosuggestions/spec/integrations/zle_input_stack_spec.rb b/.zsh/external/zsh-autosuggestions/spec/integrations/zle_input_stack_spec.rb
new file mode 100644 (file)
index 0000000..8a2c990
--- /dev/null
@@ -0,0 +1,24 @@
+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('foo() { zle -U - "echo hello" }; zle -N foo; bindkey ^B foo')
+    end
+  end
+
+  let(:options) { ['unset ZSH_AUTOSUGGEST_USE_ASYNC', 'ZSH_AUTOSUGGEST_STRATEGY=test'] }
+
+  # TODO: This is only possible with the $KEYS_QUEUED_COUNT widget parameter, coming soon...
+  xit 'does not fetch a suggestion for every inserted character' do
+    session.send_keys('C-b')
+    wait_for { session.content }.to eq('echo hello')
+  end
+
+  it 'shows a suggestion when the widget completes' do
+    with_history('echo hello world') do
+      session.send_keys('C-b')
+      wait_for { session.content(esc_seqs: true) }.to match(/\Aecho hello\e\[[0-9]+m world/)
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/multi_line_spec.rb b/.zsh/external/zsh-autosuggestions/spec/multi_line_spec.rb
new file mode 100644 (file)
index 0000000..4ff2ae1
--- /dev/null
@@ -0,0 +1,13 @@
+describe 'a multi-line suggestion' do
+  it 'should be displayed on multiple lines' do
+    with_history(-> {
+      session.send_string('echo "')
+      session.send_keys('enter')
+      session.send_string('"')
+      session.send_keys('enter')
+    }) do
+      session.send_keys('e')
+      wait_for { session.content }.to eq("echo \"\n\"")
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/options/async_zpty_name_spec.rb b/.zsh/external/zsh-autosuggestions/spec/options/async_zpty_name_spec.rb
new file mode 100644 (file)
index 0000000..407ee70
--- /dev/null
@@ -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/.zsh/external/zsh-autosuggestions/spec/options/buffer_max_size_spec.rb b/.zsh/external/zsh-autosuggestions/spec/options/buffer_max_size_spec.rb
new file mode 100644 (file)
index 0000000..29ca8bc
--- /dev/null
@@ -0,0 +1,30 @@
+describe 'a suggestion' do
+  let(:term_opts) { { width: 200 } }
+  let(:long_command) { "echo #{'a' * 100}" }
+
+  around do |example|
+    with_history(long_command) { example.run }
+  end
+
+  it 'is provided for any buffer length' do
+    session.send_string(long_command[0...-1])
+    wait_for { session.content }.to eq(long_command)
+  end
+
+  context 'when ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE is specified' do
+    let(:buffer_max_size) { 10 }
+    let(:options) { ["ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=#{buffer_max_size}"] }
+
+    it 'is provided when the buffer is shorter than the specified length' do
+      session.send_string(long_command[0...(buffer_max_size - 1)])
+      wait_for { session.content }.to eq(long_command)
+    end
+
+    it 'is provided when the buffer is equal to the specified length' do
+      session.send_string(long_command[0...(buffer_max_size)])
+      wait_for { session.content }.to eq(long_command)
+    end
+
+    it 'is not provided when the buffer is longer than the specified length'
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/options/highlight_style_spec.rb b/.zsh/external/zsh-autosuggestions/spec/options/highlight_style_spec.rb
new file mode 100644 (file)
index 0000000..a7e39b3
--- /dev/null
@@ -0,0 +1,7 @@
+describe 'a displayed suggestion' do
+  it 'is shown in the default style'
+
+  describe 'when ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE is set to a zle_highlight string' do
+    it 'is shown in the specified style'
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/options/original_widget_prefix_spec.rb b/.zsh/external/zsh-autosuggestions/spec/options/original_widget_prefix_spec.rb
new file mode 100644 (file)
index 0000000..a4b6e98
--- /dev/null
@@ -0,0 +1,7 @@
+describe 'an original zle widget' do
+  context 'is accessible with the default prefix'
+
+  context 'when ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX is set' do
+    it 'is accessible with the specified prefix'
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/options/strategy_spec.rb b/.zsh/external/zsh-autosuggestions/spec/options/strategy_spec.rb
new file mode 100644 (file)
index 0000000..c9f01e1
--- /dev/null
@@ -0,0 +1,20 @@
+describe 'a suggestion for a given prefix' do
+  let(:options) { ['_zsh_autosuggest_strategy_default() { suggestion="echo foo" }'] }
+
+  it 'is determined by calling the default strategy function' do
+    session.send_string('e')
+    wait_for { session.content }.to eq('echo foo')
+  end
+
+  context 'when ZSH_AUTOSUGGEST_STRATEGY is set' do
+    let(:options) { [
+      '_zsh_autosuggest_strategy_custom() { suggestion="echo foo" }',
+      'ZSH_AUTOSUGGEST_STRATEGY=custom'
+    ] }
+
+    it 'is determined by calling the specified strategy function' do
+      session.send_string('e')
+      wait_for { session.content }.to eq('echo foo')
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/options/use_async_spec.rb b/.zsh/external/zsh-autosuggestions/spec/options/use_async_spec.rb
new file mode 100644 (file)
index 0000000..420dcc3
--- /dev/null
@@ -0,0 +1,7 @@
+describe 'suggestion fetching' do
+  it 'is performed synchronously'
+
+  context 'when ZSH_AUTOSUGGEST_USE_ASYNC is set' do
+    it 'is performed asynchronously'
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/options/widget_lists_spec.rb b/.zsh/external/zsh-autosuggestions/spec/options/widget_lists_spec.rb
new file mode 100644 (file)
index 0000000..c207c0c
--- /dev/null
@@ -0,0 +1,97 @@
+describe 'a zle widget' do
+  let(:widget) { 'my-widget' }
+  let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } }
+
+  context 'when added to ZSH_AUTOSUGGEST_ACCEPT_WIDGETS' do
+    let(:options) { ["ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})"] }
+
+    it 'accepts the suggestion when invoked' do
+      with_history('echo hello') do
+        session.send_string('e')
+        wait_for { session.content }.to eq('echo hello')
+        session.send_keys('C-b')
+        wait_for { session.content(esc_seqs: true) }.to eq('echo hello')
+      end
+    end
+  end
+
+  context 'when added to ZSH_AUTOSUGGEST_CLEAR_WIDGETS' do
+    let(:options) { ["ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(#{widget})"] }
+
+    it 'clears the suggestion when invoked' do
+      with_history('echo hello') do
+        session.send_string('e')
+        wait_for { session.content }.to eq('echo hello')
+        session.send_keys('C-b')
+        wait_for { session.content }.to eq('e')
+      end
+    end
+  end
+
+  context 'when added to ZSH_AUTOSUGGEST_EXECUTE_WIDGETS' do
+    let(:options) { ["ZSH_AUTOSUGGEST_EXECUTE_WIDGETS+=(#{widget})"] }
+
+    it 'executes the suggestion when invoked' do
+      with_history('echo hello') do
+        session.send_string('e')
+        wait_for { session.content }.to eq('echo hello')
+        session.send_keys('C-b')
+        wait_for { session.content }.to end_with("\nhello")
+      end
+    end
+  end
+
+  context 'when added to ZSH_AUTOSUGGEST_IGNORE_WIDGETS' do
+    let(:options) { ["ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(#{widget})"] }
+
+    it 'should not be wrapped with an autosuggest widget' do
+      session.run_command("echo $widgets[#{widget}]")
+      wait_for { session.content }.to end_with("\nuser:#{widget}")
+    end
+  end
+
+  context 'that moves the cursor forward' do
+    before { session.run_command("#{widget}() { zle forward-char }") }
+
+    context 'when added to ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS' do
+      let(:options) { ["ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(#{widget})"] }
+
+      it 'accepts the suggestion as far as the cursor is moved when invoked' do
+        with_history('echo hello') do
+          session.send_string('e')
+          wait_for { session.content }.to start_with('echo hello')
+          session.send_keys('C-b')
+          wait_for { session.content(esc_seqs: true) }.to match(/\Aec\e\[[0-9]+mho hello/)
+        end
+      end
+    end
+  end
+
+  context 'that modifies the buffer' do
+    before { session.run_command("#{widget}() { BUFFER=\"foo\" }") }
+
+    context 'when not added to any of the widget lists' do
+      it 'modifies the buffer and fetches a new suggestion' do
+        with_history('foobar') do
+          session.send_keys('C-b')
+          wait_for { session.content }.to eq('foobar')
+        end
+      end
+    end
+  end
+end
+
+describe 'a modification to the widget lists' do
+  let(:widget) { 'my-widget' }
+  let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } }
+  before { session.run_command("ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})") }
+
+  it 'takes effect on the next cmd line' do
+    with_history('echo hello') do
+      session.send_string('e')
+      wait_for { session.content }.to eq('echo hello')
+      session.send_keys('C-b')
+      wait_for { session.content(esc_seqs: true) }.to eq('echo hello')
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/spec_helper.rb b/.zsh/external/zsh-autosuggestions/spec/spec_helper.rb
new file mode 100644 (file)
index 0000000..64115d2
--- /dev/null
@@ -0,0 +1,50 @@
+require 'pry'
+require 'rspec/wait'
+require 'terminal_session'
+
+RSpec.shared_context 'terminal session' do
+  let(:term_opts) { {} }
+  let(:session) { TerminalSession.new(term_opts) }
+  let(:before_sourcing) { -> {} }
+  let(:options) { [] }
+
+  around do |example|
+    before_sourcing.call
+
+    session.run_command((['source zsh-autosuggestions.zsh'] + options).join('; '))
+    session.clear_screen
+
+    example.run
+
+    session.destroy
+  end
+
+  def with_history(*commands, &block)
+    session.run_command('fc -p')
+
+    commands.each do |c|
+      c.respond_to?(:call) ? c.call : session.run_command(c)
+    end
+
+    session.clear_screen
+
+    yield block
+
+    session.send_keys('C-c')
+    session.run_command('fc -P')
+  end
+end
+
+RSpec.configure do |config|
+  config.expect_with :rspec do |expectations|
+    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+  end
+
+  config.mock_with :rspec do |mocks|
+    mocks.verify_partial_doubles = true
+  end
+
+  config.wait_timeout = 2
+
+  config.include_context 'terminal session'
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/special_characters_spec.rb b/.zsh/external/zsh-autosuggestions/spec/special_characters_spec.rb
new file mode 100644 (file)
index 0000000..ce7810b
--- /dev/null
@@ -0,0 +1,60 @@
+describe 'a special character in the buffer' do
+  it 'should be treated like any other character' do
+    with_history('echo "hello*"', 'echo "hello."') do
+      session.send_string('echo "hello*')
+      wait_for { session.content }.to eq('echo "hello*"')
+    end
+
+    with_history('echo "hello?"', 'echo "hello."') do
+      session.send_string('echo "hello?')
+      wait_for { session.content }.to eq('echo "hello?"')
+    end
+
+    with_history('echo "hello\nworld"') do
+      session.send_string('echo "hello\\')
+      wait_for { session.content }.to eq('echo "hello\nworld"')
+    end
+
+    with_history('echo "\\\\"') do
+      session.send_string('echo "\\\\')
+      wait_for { session.content }.to eq('echo "\\\\"')
+    end
+
+    with_history('echo ~/foo') do
+      session.send_string('echo ~')
+      wait_for { session.content }.to eq('echo ~/foo')
+    end
+
+    with_history('echo "$(ls foo)"') do
+      session.send_string('echo "$(')
+      wait_for { session.content }.to eq('echo "$(ls foo)"')
+    end
+
+    with_history('echo "$history[123]"') do
+      session.send_string('echo "$history[')
+      wait_for { session.content }.to eq('echo "$history[123]"')
+      session.send_string('123]')
+      wait_for { session.content }.to eq('echo "$history[123]"')
+    end
+
+    with_history('echo "#yolo"') do
+      session.send_string('echo "#')
+      wait_for { session.content }.to eq('echo "#yolo"')
+    end
+
+    with_history('echo "#foo"', 'echo $#abc') do
+      session.send_string('echo "#')
+      wait_for { session.content }.to eq('echo "#foo"')
+    end
+
+    with_history('echo "^A"', 'echo "^B"') do
+      session.send_string('echo "^A')
+      wait_for { session.content }.to eq('echo "^A"')
+    end
+
+    with_history('-foo() {}') do
+      session.send_string('-')
+      wait_for { session.content }.to eq('-foo() {}')
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/strategies/default_spec.rb b/.zsh/external/zsh-autosuggestions/spec/strategies/default_spec.rb
new file mode 100644 (file)
index 0000000..94f3450
--- /dev/null
@@ -0,0 +1,8 @@
+describe 'the default suggestion strategy' do
+  it 'suggests the last matching history entry' do
+    with_history('ls foo', 'ls bar', 'echo baz') do
+      session.send_string('ls')
+      wait_for { session.content }.to eq('ls bar')
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/strategies/match_prev_cmd_spec.rb b/.zsh/external/zsh-autosuggestions/spec/strategies/match_prev_cmd_spec.rb
new file mode 100644 (file)
index 0000000..21be712
--- /dev/null
@@ -0,0 +1,17 @@
+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
+    with_history(
+      'echo what',
+      'ls foo',
+      'echo what',
+      'ls bar',
+      'ls baz',
+      'echo what'
+    ) do
+      session.send_string('ls')
+      wait_for { session.content }.to eq('ls bar')
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/terminal_session.rb b/.zsh/external/zsh-autosuggestions/spec/terminal_session.rb
new file mode 100644 (file)
index 0000000..82705d3
--- /dev/null
@@ -0,0 +1,91 @@
+require 'securerandom'
+
+class TerminalSession
+  ZSH_BIN = ENV['TEST_ZSH_BIN'] || 'zsh'
+
+  def initialize(opts = {})
+    opts = {
+      width: 80,
+      height: 24,
+      prompt: '',
+      term: 'xterm-256color',
+      zsh_bin: ZSH_BIN
+    }.merge(opts)
+
+    @opts = opts
+
+    cmd="PS1=\"#{opts[:prompt]}\" TERM=#{opts[:term]} #{ZSH_BIN} -f"
+    tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'")
+  end
+
+  def tmux_socket_name
+    @tmux_socket_name ||= SecureRandom.hex(6)
+  end
+
+  def run_command(command)
+    send_string(command)
+    send_keys('enter')
+
+    self
+  end
+
+  def send_string(str)
+    tmux_command("send-keys -t 0 -l -- '#{str.gsub("'", "\\'")}'")
+
+    self
+  end
+
+  def send_keys(*keys)
+    tmux_command("send-keys -t 0 #{keys.join(' ')}")
+
+    self
+  end
+
+  def paste_string(str)
+    tmux_command("set-buffer -- '#{str}'")
+    tmux_command("paste-buffer -dpr -t 0")
+
+    self
+  end
+
+  def content(esc_seqs: false)
+    cmd = 'capture-pane -p -t 0'
+    cmd += ' -e' if esc_seqs
+    tmux_command(cmd).strip
+  end
+
+  def clear_screen
+    send_keys('C-l')
+
+    i = 0
+    until content == opts[:prompt] || i > 20 do
+      sleep(0.1)
+      i = i + 1
+    end
+
+    self
+  end
+
+  def destroy
+    tmux_command('kill-session')
+  end
+
+  def cursor
+    tmux_command("display-message -t 0 -p '\#{cursor_x},\#{cursor_y}'").
+      strip.
+      split(',').
+      map(&:to_i)
+  end
+
+  private
+
+  attr_reader :opts
+
+  def tmux_command(cmd)
+    out = `tmux -u -L #{tmux_socket_name} #{cmd}`
+
+    raise("tmux error running: '#{cmd}'") unless $?.success?
+
+    out
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/widgets/disable_spec.rb b/.zsh/external/zsh-autosuggestions/spec/widgets/disable_spec.rb
new file mode 100644 (file)
index 0000000..b387a59
--- /dev/null
@@ -0,0 +1,19 @@
+describe 'the `autosuggest-disable` widget' do
+  before do
+    session.run_command('bindkey ^B autosuggest-disable')
+  end
+
+  it 'disables suggestions and clears the suggestion' do
+    with_history('echo hello') do
+      session.send_string('echo')
+      wait_for { session.content }.to eq('echo hello')
+
+      session.send_keys('C-b')
+      wait_for { session.content }.to eq('echo')
+
+      session.send_string(' h')
+      sleep 1
+      expect(session.content).to eq('echo h')
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/widgets/enable_spec.rb b/.zsh/external/zsh-autosuggestions/spec/widgets/enable_spec.rb
new file mode 100644 (file)
index 0000000..3ad35a8
--- /dev/null
@@ -0,0 +1,42 @@
+describe 'the `autosuggest-enable` widget' do
+  before do
+    session.
+      run_command('typeset -g _ZSH_AUTOSUGGEST_DISABLED').
+      run_command('bindkey ^B autosuggest-enable')
+  end
+
+  it 'enables suggestions and fetches a suggestion' do
+    with_history('echo hello') do
+      session.send_string('e')
+      sleep 1
+      expect(session.content).to eq('e')
+
+      session.send_keys('C-b')
+      session.send_string('c')
+      wait_for { session.content }.to eq('echo hello')
+    end
+  end
+
+  context 'invoked on an empty buffer' do
+    it 'does not fetch a suggestion' do
+      with_history('echo hello') do
+        session.send_keys('C-b')
+        sleep 1
+        expect(session.content).to eq('')
+      end
+    end
+  end
+
+  context 'invoked on a non-empty buffer' do
+    it 'fetches a suggestion' do
+      with_history('echo hello') do
+        session.send_string('e')
+        sleep 1
+        expect(session.content).to eq('e')
+
+        session.send_keys('C-b')
+        wait_for { session.content }.to eq('echo hello')
+      end
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/widgets/fetch_spec.rb b/.zsh/external/zsh-autosuggestions/spec/widgets/fetch_spec.rb
new file mode 100644 (file)
index 0000000..eb8f2ba
--- /dev/null
@@ -0,0 +1,24 @@
+describe 'the `autosuggest-fetch` widget' do
+  context 'when suggestions are disabled' do
+    before do
+      session.
+        run_command('bindkey ^B autosuggest-disable').
+        run_command('bindkey ^F autosuggest-fetch').
+        send_keys('C-b')
+    end
+
+    it 'will fetch and display a suggestion' do
+      with_history('echo hello') do
+        session.send_string('echo h')
+        sleep 1
+        expect(session.content).to eq('echo h')
+
+        session.send_keys('C-f')
+        wait_for { session.content }.to eq('echo hello')
+
+        session.send_string('e')
+        wait_for { session.content }.to eq('echo hello')
+      end
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/spec/widgets/toggle_spec.rb b/.zsh/external/zsh-autosuggestions/spec/widgets/toggle_spec.rb
new file mode 100644 (file)
index 0000000..8f9f3c3
--- /dev/null
@@ -0,0 +1,26 @@
+describe 'the `autosuggest-toggle` widget' do
+  before do
+    session.run_command('bindkey ^B autosuggest-toggle')
+  end
+
+  it 'toggles suggestions' do
+    with_history('echo world', 'echo hello') do
+      session.send_string('echo')
+      wait_for { session.content }.to eq('echo hello')
+
+      session.send_keys('C-b')
+      wait_for { session.content }.to eq('echo')
+
+      session.send_string(' h')
+      sleep 1
+      expect(session.content).to eq('echo h')
+
+      session.send_keys('C-b')
+      wait_for { session.content }.to eq('echo hello')
+
+      session.send_keys('C-h')
+      session.send_string('w')
+      wait_for { session.content }.to eq('echo world')
+    end
+  end
+end
diff --git a/.zsh/external/zsh-autosuggestions/src/async.zsh b/.zsh/external/zsh-autosuggestions/src/async.zsh
new file mode 100644 (file)
index 0000000..124c9ac
--- /dev/null
@@ -0,0 +1,109 @@
+
+#--------------------------------------------------------------------#
+# 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
+       }
+
+       # Output only newlines (not carriage return + newline)
+       stty -onlcr
+
+       # Silence any error messages
+       exec 2>/dev/null
+
+       local strategy=$1
+       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'
+}
+
+# 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() {
+       setopt LOCAL_OPTIONS EXTENDED_GLOB
+
+       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 -eq 0 ]; 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 _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY"
+
+       # 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() {
+       if zpty -t $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null; then
+               # Remove the input handler
+               zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
+
+               # Destroy the zpty
+               zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
+       fi
+}
+
+_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
+}
index 49763e8..d8629f1 100644 (file)
@@ -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,33 +38,38 @@ _zsh_autosuggest_bind_widget() {
 
                # User-defined widget
                user:*)
-                       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
@@ -53,7 +80,7 @@ _zsh_autosuggest_bind_widgets() {
        ignore_widgets=(
                .\*
                _\*
-               zle-line-\*
+               zle-\*
                autosuggest-\*
                $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\*
                $ZSH_AUTOSUGGEST_IGNORE_WIDGETS
index f519f6f..c7fc55a 100644 (file)
@@ -60,3 +60,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_pty
diff --git a/.zsh/external/zsh-autosuggestions/src/deprecated.zsh b/.zsh/external/zsh-autosuggestions/src/deprecated.zsh
deleted file mode 100644 (file)
index bcf0d74..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-
-#--------------------------------------------------------------------#
-# Handle Deprecated Variables/Widgets                                #
-#--------------------------------------------------------------------#
-
-_zsh_autosuggest_deprecated_warning() {
-       >&2 echo "zsh-autosuggestions: $@"
-}
-
-_zsh_autosuggest_check_deprecated_config() {
-       if [ -n "$AUTOSUGGESTION_HIGHLIGHT_COLOR" ]; then
-               _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_HIGHLIGHT_COLOR is deprecated. Use ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE instead."
-               [ -z "$ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" ] && ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=$AUTOSUGGESTION_HIGHLIGHT_STYLE
-               unset AUTOSUGGESTION_HIGHLIGHT_STYLE
-       fi
-
-       if [ -n "$AUTOSUGGESTION_HIGHLIGHT_CURSOR" ]; then
-               _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_HIGHLIGHT_CURSOR is deprecated."
-               unset AUTOSUGGESTION_HIGHLIGHT_CURSOR
-       fi
-
-       if [ -n "$AUTOSUGGESTION_ACCEPT_RIGHT_ARROW" ]; then
-               _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_ACCEPT_RIGHT_ARROW is deprecated. The right arrow now accepts the suggestion by default."
-               unset AUTOSUGGESTION_ACCEPT_RIGHT_ARROW
-       fi
-}
-
-_zsh_autosuggest_deprecated_start_widget() {
-       _zsh_autosuggest_deprecated_warning "The autosuggest-start widget is deprecated. For more info, see the README at https://github.com/zsh-users/zsh-autosuggestions."
-       zle -D autosuggest-start
-       eval "zle-line-init() {
-               $(echo $functions[${widgets[zle-line-init]#*:}] | sed -e 's/zle autosuggest-start//g')
-       }"
-}
-
-zle -N autosuggest-start _zsh_autosuggest_deprecated_start_widget
diff --git a/.zsh/external/zsh-autosuggestions/src/features.zsh b/.zsh/external/zsh-autosuggestions/src/features.zsh
new file mode 100644 (file)
index 0000000..7a5248f
--- /dev/null
@@ -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/.zsh/external/zsh-autosuggestions/src/setup.zsh b/.zsh/external/zsh-autosuggestions/src/setup.zsh
new file mode 100644 (file)
index 0000000..c74489f
--- /dev/null
@@ -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
index 54f5bb8..6fa8ce9 100644 (file)
@@ -5,9 +5,20 @@
 
 # Start the autosuggestion widgets
 _zsh_autosuggest_start() {
-       _zsh_autosuggest_check_deprecated_config
+       add-zsh-hook -d precmd _zsh_autosuggest_start
+
        _zsh_autosuggest_bind_widgets
+
+       # Re-bind widgets on every precmd to ensure we wrap other wrappers.
+       # Specifically, highlighting breaks if our widgets are wrapped by
+       # 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
 }
 
-autoload -Uz add-zsh-hook
+# Start the autosuggestion widgets on the next precmd
 add-zsh-hook precmd _zsh_autosuggest_start
index 29333f5..60c0494 100644 (file)
@@ -7,5 +7,19 @@
 #
 
 _zsh_autosuggest_strategy_default() {
-       fc -lnrm "$1*" 1 2>/dev/null | head -n 1
+       # 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
+       local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
+
+       # Get the history items that match
+       # - (r) subscript flag makes the pattern match on values
+       suggestion="${history[(r)$prefix*]}"
+
 }
index bf8bdd9..ee26346 100644 (file)
@@ -21,7 +21,7 @@
 # `HIST_EXPIRE_DUPS_FIRST`.
 
 _zsh_autosuggest_strategy_match_prev_cmd() {
-       local prefix="$1"
+       local prefix="${1//(#m)[\\()\[\]|*?~]/\\$MATCH}"
 
        # Get all history event numbers that correspond to history
        # entries that match pattern $prefix*
@@ -47,6 +47,6 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
                fi
        done
 
-       # Echo the matched history entry
-       echo -E "$history[$histkey]"
+       # Give back the matched history entry
+       suggestion="$history[$histkey]"
 }
diff --git a/.zsh/external/zsh-autosuggestions/src/suggestion.zsh b/.zsh/external/zsh-autosuggestions/src/suggestion.zsh
deleted file mode 100644 (file)
index 31a9f76..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-
-#--------------------------------------------------------------------#
-# Suggestion                                                         #
-#--------------------------------------------------------------------#
-
-# Delegate to the selected strategy to determine a suggestion
-_zsh_autosuggest_suggestion() {
-       local escaped_prefix="$(_zsh_autosuggest_escape_command "$1")"
-       local strategy_function="_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY"
-
-       if [ -n "$functions[$strategy_function]" ]; then
-               echo -E "$($strategy_function "$escaped_prefix")"
-       fi
-}
-
-_zsh_autosuggest_escape_command() {
-       setopt localoptions EXTENDED_GLOB
-
-       # Escape special chars in the string (requires EXTENDED_GLOB)
-       echo -E "${1//(#m)[\\()\[\]|*?~]/\\$MATCH}"
-}
diff --git a/.zsh/external/zsh-autosuggestions/src/util.zsh b/.zsh/external/zsh-autosuggestions/src/util.zsh
new file mode 100644 (file)
index 0000000..1f55d36
--- /dev/null
@@ -0,0 +1,11 @@
+
+#--------------------------------------------------------------------#
+# Utility Functions                                                  #
+#--------------------------------------------------------------------#
+
+_zsh_autosuggest_escape_command() {
+       setopt localoptions EXTENDED_GLOB
+
+       # Escape special chars in the string (requires EXTENDED_GLOB)
+       echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}"
+}
index 57f378e..aa3f248 100644 (file)
@@ -3,6 +3,30 @@
 # Autosuggest Widget Implementations                                 #
 #--------------------------------------------------------------------#
 
+# Disable suggestions
+_zsh_autosuggest_disable() {
+       typeset -g _ZSH_AUTOSUGGEST_DISABLED
+       _zsh_autosuggest_clear
+}
+
+# Enable suggestions
+_zsh_autosuggest_enable() {
+       unset _ZSH_AUTOSUGGEST_DISABLED
+
+       if [ $#BUFFER -gt 0 ]; then
+               _zsh_autosuggest_fetch
+       fi
+}
+
+# Toggle suggestions (enable/disable)
+_zsh_autosuggest_toggle() {
+       if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then
+               _zsh_autosuggest_enable
+       else
+               _zsh_autosuggest_disable
+       fi
+}
+
 # Clear the suggestion
 _zsh_autosuggest_clear() {
        # Remove the suggestion
@@ -15,37 +39,77 @@ _zsh_autosuggest_clear() {
 _zsh_autosuggest_modify() {
        local -i retval
 
+       # Only added to zsh very recently
+       local -i KEYS_QUEUED_COUNT
+
        # Save the contents of the buffer/postdisplay
        local orig_buffer="$BUFFER"
        local orig_postdisplay="$POSTDISPLAY"
 
-       # Clear suggestion while original widget runs
+       # Clear suggestion while waiting for next one
        unset POSTDISPLAY
 
        # Original widget may modify the buffer
        _zsh_autosuggest_invoke_original_widget $@
        retval=$?
 
+       # Don't fetch a new suggestion if there's more input to be read immediately
+       if [[ $PENDING > 0 ]] || [[ $KEYS_QUEUED_COUNT > 0 ]]; then
+               return $retval
+       fi
+
+       # Optimize if manually typing in the suggestion
+       if [ $#BUFFER -gt $#orig_buffer ]; then
+               local added=${BUFFER#$orig_buffer}
+
+               # If the string added matches the beginning of the postdisplay
+               if [ "$added" = "${orig_postdisplay:0:$#added}" ]; then
+                       POSTDISPLAY="${orig_postdisplay:$#added}"
+                       return $retval
+               fi
+       fi
+
        # Don't fetch a new suggestion if the buffer hasn't changed
        if [ "$BUFFER" = "$orig_buffer" ]; then
                POSTDISPLAY="$orig_postdisplay"
                return $retval
        fi
 
+       # Bail out if suggestions are disabled
+       if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then
+               return $?
+       fi
+
        # Get a new suggestion if the buffer is not empty after modification
-       local suggestion
        if [ $#BUFFER -gt 0 ]; then
-               if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -lt "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then
-                       suggestion="$(_zsh_autosuggest_suggestion "$BUFFER")"
+               if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -le "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then
+                       _zsh_autosuggest_fetch
                fi
        fi
 
-       # Add the suggestion to the POSTDISPLAY
-       if [ -n "$suggestion" ]; then
-               POSTDISPLAY="${suggestion#$BUFFER}"
+       return $retval
+}
+
+# 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
+               _zsh_autosuggest_async_request "$BUFFER"
+       else
+               local suggestion
+               _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER"
+               _zsh_autosuggest_suggest "$suggestion"
        fi
+}
 
-       return $retval
+# Offer a suggestion
+_zsh_autosuggest_suggest() {
+       local suggestion="$1"
+
+       if [ -n "$suggestion" ] && [ $#BUFFER -gt 0 ]; then
+               POSTDISPLAY="${suggestion#$BUFFER}"
+       else
+               unset POSTDISPLAY
+       fi
 }
 
 # Accept the entire suggestion
@@ -115,7 +179,7 @@ _zsh_autosuggest_partial_accept() {
        return $retval
 }
 
-for action in clear modify accept partial_accept execute; do
+for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
        eval "_zsh_autosuggest_widget_$action() {
                local -i retval
 
@@ -126,10 +190,17 @@ for action in clear modify accept partial_accept execute; do
 
                _zsh_autosuggest_highlight_apply
 
+               zle -R
+
                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
diff --git a/.zsh/external/zsh-autosuggestions/test/bind_test.zsh b/.zsh/external/zsh-autosuggestions/test/bind_test.zsh
deleted file mode 100644 (file)
index f73ea7f..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-testInvokeOriginalWidgetDefined() {
-       stub_and_eval \
-               zle \
-               'return 1'
-
-       _zsh_autosuggest_invoke_original_widget 'self-insert'
-
-       assertEquals \
-               '1' \
-               "$?"
-
-       assertTrue \
-               'zle was not invoked' \
-               'stub_called zle'
-
-       restore zle
-}
-
-testInvokeOriginalWidgetUndefined() {
-       stub_and_eval \
-               zle \
-               'return 1'
-
-       _zsh_autosuggest_invoke_original_widget 'some-undefined-widget'
-
-       assertEquals \
-               '0' \
-               "$?"
-
-       assertFalse \
-               'zle was invoked' \
-               'stub_called zle'
-
-       restore zle
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/highlight_test.zsh b/.zsh/external/zsh-autosuggestions/test/highlight_test.zsh
deleted file mode 100644 (file)
index 7268af8..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-testHighlightDefaultStyle() {
-       assertEquals \
-               'fg=8' \
-               "$ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE"
-}
-
-testHighlightApplyWithSuggestion() {
-       local orig_style=ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE
-       ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=4'
-
-       BUFFER='ec'
-       POSTDISPLAY='ho hello'
-       region_highlight=('0 2 fg=1')
-
-       _zsh_autosuggest_highlight_apply
-
-       assertEquals \
-               'highlight did not use correct style' \
-               "0 2 fg=1 2 10 $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" \
-               "$region_highlight"
-
-       assertEquals \
-               'higlight was not saved to be removed later' \
-               "2 10 $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" \
-               "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT"
-
-       ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=orig_style
-}
-
-testHighlightApplyWithoutSuggestion() {
-       BUFFER='echo hello'
-       POSTDISPLAY=''
-       region_highlight=('0 4 fg=1')
-
-       _zsh_autosuggest_highlight_apply
-
-       assertEquals \
-               'region_highlight was modified' \
-               '0 4 fg=1' \
-               "$region_highlight"
-
-       assertNull \
-               'last highlight region was not cleared' \
-               "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT"
-}
-
-testHighlightReset() {
-       BUFFER='ec'
-       POSTDISPLAY='ho hello'
-       region_highlight=('0 1 fg=1' '2 10 fg=8' '1 2 fg=1')
-       _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT='2 10 fg=8'
-
-       _zsh_autosuggest_highlight_reset
-
-       assertEquals \
-               'last highlight region was not removed' \
-               '0 1 fg=1 1 2 fg=1' \
-               "$region_highlight"
-
-       assertNull \
-               'last highlight variable was not cleared' \
-               "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT"
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/strategies/default_test.zsh b/.zsh/external/zsh-autosuggestions/test/strategies/default_test.zsh
deleted file mode 100755 (executable)
index f5200e6..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/../test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-testNoMatch() {
-       set_history <<-'EOF'
-               ls foo
-               ls bar
-       EOF
-
-       assertSuggestion \
-               'foo' \
-               ''
-
-       assertSuggestion \
-               'ls q' \
-               ''
-}
-
-testBasicMatches() {
-       set_history <<-'EOF'
-               ls foo
-               ls bar
-       EOF
-
-       assertSuggestion \
-               'ls f' \
-               'ls foo'
-
-       assertSuggestion \
-               'ls b' \
-               'ls bar'
-}
-
-testMostRecentMatch() {
-       set_history <<-'EOF'
-               ls foo
-               cd bar
-               ls baz
-               cd quux
-       EOF
-
-       assertSuggestion \
-               'ls' \
-               'ls baz'
-
-       assertSuggestion \
-               'cd' \
-               'cd quux'
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/strategies/match_prev_cmd_test.zsh b/.zsh/external/zsh-autosuggestions/test/strategies/match_prev_cmd_test.zsh
deleted file mode 100755 (executable)
index bf3fc64..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/../test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-
-       ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd
-}
-
-testNoMatch() {
-       set_history <<-'EOF'
-               ls foo
-               ls bar
-       EOF
-
-       assertSuggestion \
-               'foo' \
-               ''
-
-       assertSuggestion \
-               'ls q' \
-               ''
-}
-
-testBasicMatches() {
-       set_history <<-'EOF'
-               ls foo
-               ls bar
-       EOF
-
-       assertSuggestion \
-               'ls f' \
-               'ls foo'
-
-       assertSuggestion \
-               'ls b' \
-               'ls bar'
-}
-
-testMostRecentMatch() {
-       set_history <<-'EOF'
-               ls foo
-               cd bar
-               ls baz
-               cd quux
-       EOF
-
-       assertSuggestion \
-               'ls' \
-               'ls baz'
-
-       assertSuggestion \
-               'cd' \
-               'cd quux'
-}
-
-testMatchMostRecentAfterPreviousCmd() {
-       set_history <<-'EOF'
-               echo what
-               ls foo
-               ls bar
-               echo what
-               ls baz
-               ls quux
-               echo what
-       EOF
-
-       assertSuggestion \
-               'ls' \
-               'ls baz'
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/strategies_test.zsh b/.zsh/external/zsh-autosuggestions/test/strategies_test.zsh
deleted file mode 100644 (file)
index 0a937f4..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-assertBackslashSuggestion() {
-       set_history <<-'EOF'
-               echo "hello\nworld"
-       EOF
-
-       assertSuggestion \
-               'echo "hello\' \
-               'echo "hello\nworld"'
-}
-
-assertDoubleBackslashSuggestion() {
-       set_history <<-'EOF'
-               echo "\\"
-       EOF
-
-       assertSuggestion \
-               'echo "\\' \
-               'echo "\\"'
-}
-
-assertTildeSuggestion() {
-       set_history <<-'EOF'
-               cd ~/something
-       EOF
-
-       assertSuggestion \
-               'cd' \
-               'cd ~/something'
-
-       assertSuggestion \
-               'cd ~' \
-               'cd ~/something'
-
-       assertSuggestion \
-               'cd ~/s' \
-               'cd ~/something'
-}
-
-assertTildeSuggestionWithExtendedGlob() {
-       setopt local_options extended_glob
-
-       assertTildeSuggestion
-}
-
-assertParenthesesSuggestion() {
-       set_history <<-'EOF'
-               echo "$(ls foo)"
-       EOF
-
-       assertSuggestion \
-               'echo "$(' \
-               'echo "$(ls foo)"'
-}
-
-assertSquareBracketsSuggestion() {
-       set_history <<-'EOF'
-               echo "$history[123]"
-       EOF
-
-       assertSuggestion \
-               'echo "$history[' \
-               'echo "$history[123]"'
-}
-
-assertHashSuggestion() {
-       set_history <<-'EOF'
-               echo "#yolo"
-       EOF
-
-       assertSuggestion \
-               'echo "#' \
-               'echo "#yolo"'
-}
-
-testSpecialCharsForAllStrategies() {
-       local strategies
-       strategies=(
-               "default"
-               "match_prev_cmd"
-       )
-
-       for s in $strategies; do
-               ZSH_AUTOSUGGEST_STRATEGY="$s"
-
-               assertBackslashSuggestion
-               assertDoubleBackslashSuggestion
-               assertTildeSuggestion
-               assertTildeSuggestionWithExtendedGlob
-               assertParenthesesSuggestion
-               assertSquareBracketsSuggestion
-       done
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/suggestion_test.zsh b/.zsh/external/zsh-autosuggestions/test/suggestion_test.zsh
deleted file mode 100644 (file)
index fc6330d..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-testEscapeCommand() {
-       assertEquals \
-               'Did not escape single backslash' \
-               '\\' \
-               "$(_zsh_autosuggest_escape_command '\')"
-
-       assertEquals \
-               'Did not escape two backslashes' \
-               '\\\\' \
-               "$(_zsh_autosuggest_escape_command '\\')"
-
-       assertEquals \
-               'Did not escape parentheses' \
-               '\(\)' \
-               "$(_zsh_autosuggest_escape_command '()')"
-
-       assertEquals \
-               'Did not escape square brackets' \
-               '\[\]' \
-               "$(_zsh_autosuggest_escape_command '[]')"
-
-       assertEquals \
-               'Did not escape pipe' \
-               '\|' \
-               "$(_zsh_autosuggest_escape_command '|')"
-
-       assertEquals \
-               'Did not escape star' \
-               '\*' \
-               "$(_zsh_autosuggest_escape_command '*')"
-
-       assertEquals \
-               'Did not escape question mark' \
-               '\?' \
-               "$(_zsh_autosuggest_escape_command '?')"
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/test_helper.zsh b/.zsh/external/zsh-autosuggestions/test/test_helper.zsh
deleted file mode 100644 (file)
index 7e7dbc0..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-DIR="${0:a:h}"
-ROOT_DIR="$DIR/.."
-VENDOR_DIR="$ROOT_DIR/vendor"
-
-# Use stub.sh for stubbing/mocking
-source "$VENDOR_DIR/stub.sh/stub.sh"
-
-#--------------------------------------------------------------------#
-# Helper Functions                                                   #
-#--------------------------------------------------------------------#
-
-# Source the autosuggestions plugin file
-source_autosuggestions() {
-       source "$ROOT_DIR/zsh-autosuggestions.zsh"
-}
-
-# Set history list from stdin
-set_history() {
-       # Make a tmp file in shunit's tmp dir
-       local tmp=$(mktemp "$SHUNIT_TMPDIR/hist.XXX")
-
-       # Write from stdin to the tmp file
-       > "$tmp"
-
-       # Write an extra line to simulate history active mode
-       # See https://github.com/zsh-users/zsh/blob/ca3bc0d95d7deab4f5381f12b15047de748c0814/Src/hist.c#L69-L82
-       echo >> "$tmp"
-
-       # Clear history and re-read from the tmp file
-       fc -P; fc -p; fc -R "$tmp"
-
-       rm "$tmp"
-}
-
-# Should be called at the bottom of every test suite file
-# Pass in the name of the test script ($0) for shunit
-run_tests() {
-       local test_script="$1"
-       shift
-
-       # Required for shunit to work with zsh
-       setopt localoptions shwordsplit
-       SHUNIT_PARENT="$test_script"
-
-       source "$VENDOR_DIR/shunit2/2.1.6/src/shunit2"
-}
-
-#--------------------------------------------------------------------#
-# Custom Assertions                                                  #
-#--------------------------------------------------------------------#
-
-assertSuggestion() {
-       local prefix="$1"
-       local expected_suggestion="$2"
-
-       assertEquals \
-               "Did not get correct suggestion for prefix:<$prefix> using strategy <$ZSH_AUTOSUGGEST_STRATEGY>" \
-               "$expected_suggestion" \
-               "$(_zsh_autosuggest_suggestion "$prefix")"
-}
diff --git a/.zsh/external/zsh-autosuggestions/test/widgets/accept_test.zsh b/.zsh/external/zsh-autosuggestions/test/widgets/accept_test.zsh
deleted file mode 100644 (file)
index f126091..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/../test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-setUp() {
-       BUFFER=''
-       POSTDISPLAY=''
-       CURSOR=0
-       KEYMAP='main'
-}
-
-tearDown() {
-       restore _zsh_autosuggest_invoke_original_widget
-}
-
-testCursorAtEnd() {
-       BUFFER='echo'
-       POSTDISPLAY=' hello'
-       CURSOR=4
-
-       stub _zsh_autosuggest_invoke_original_widget
-
-       _zsh_autosuggest_accept 'original-widget'
-
-       assertTrue \
-               'original widget not invoked' \
-               'stub_called _zsh_autosuggest_invoke_original_widget'
-
-       assertEquals \
-               'BUFFER was not modified' \
-               'echo hello' \
-               "$BUFFER"
-
-       assertEquals \
-               'POSTDISPLAY was not cleared' \
-               '' \
-               "$POSTDISPLAY"
-}
-
-testCursorNotAtEnd() {
-       BUFFER='echo'
-       POSTDISPLAY=' hello'
-       CURSOR=2
-
-       stub _zsh_autosuggest_invoke_original_widget
-
-       _zsh_autosuggest_accept 'original-widget'
-
-       assertTrue \
-               'original widget not invoked' \
-               'stub_called _zsh_autosuggest_invoke_original_widget'
-
-       assertEquals \
-               'BUFFER was modified' \
-               'echo' \
-               "$BUFFER"
-
-       assertEquals \
-               'POSTDISPLAY was modified' \
-               ' hello' \
-               "$POSTDISPLAY"
-}
-
-testViCursorAtEnd() {
-       BUFFER='echo'
-       POSTDISPLAY=' hello'
-       CURSOR=3
-       KEYMAP='vicmd'
-
-       stub _zsh_autosuggest_invoke_original_widget
-
-       _zsh_autosuggest_accept 'original-widget'
-
-       assertTrue \
-               'original widget not invoked' \
-               'stub_called _zsh_autosuggest_invoke_original_widget'
-
-       assertEquals \
-               'BUFFER was not modified' \
-               'echo hello' \
-               "$BUFFER"
-
-       assertEquals \
-               'POSTDISPLAY was not cleared' \
-               '' \
-               "$POSTDISPLAY"
-}
-
-testViCursorNotAtEnd() {
-       BUFFER='echo'
-       POSTDISPLAY=' hello'
-       CURSOR=2
-       KEYMAP='vicmd'
-
-       stub _zsh_autosuggest_invoke_original_widget
-
-       _zsh_autosuggest_accept 'original-widget'
-
-       assertTrue \
-               'original widget not invoked' \
-               'stub_called _zsh_autosuggest_invoke_original_widget'
-
-       assertEquals \
-               'BUFFER was modified' \
-               'echo' \
-               "$BUFFER"
-
-       assertEquals \
-               'POSTDISPLAY was modified' \
-               ' hello' \
-               "$POSTDISPLAY"
-}
-
-testRetval() {
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'return 1'
-
-       _zsh_autosuggest_widget_accept 'original-widget'
-
-       assertEquals \
-               'Did not return correct value from original widget' \
-               '1' \
-               "$?"
-}
-
-testWidget() {
-       stub _zsh_autosuggest_highlight_reset
-       stub _zsh_autosuggest_accept
-       stub _zsh_autosuggest_highlight_apply
-
-       # Call the function pointed to by the widget since we can't call
-       # the widget itself when zle is not active
-       ${widgets[autosuggest-accept]#*:} 'original-widget'
-
-       assertTrue \
-               'autosuggest-accept widget does not exist' \
-               'zle -l autosuggest-accept'
-
-       assertTrue \
-               'highlight_reset was not called' \
-               'stub_called _zsh_autosuggest_highlight_reset'
-
-       assertTrue \
-               'widget function was not called' \
-               'stub_called _zsh_autosuggest_accept'
-
-       assertTrue \
-               'highlight_apply was not called' \
-               'stub_called _zsh_autosuggest_highlight_apply'
-
-       restore _zsh_autosuggest_highlight_reset
-       restore _zsh_autosuggest_accept
-       restore _zsh_autosuggest_highlight_apply
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/widgets/clear_test.zsh b/.zsh/external/zsh-autosuggestions/test/widgets/clear_test.zsh
deleted file mode 100644 (file)
index f0500c5..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/../test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-setUp() {
-       BUFFER=''
-       POSTDISPLAY=''
-}
-
-tearDown() {
-       restore _zsh_autosuggest_invoke_original_widget
-}
-
-testClear() {
-       BUFFER='ec'
-       POSTDISPLAY='ho hello'
-
-       _zsh_autosuggest_clear 'original-widget'
-
-       assertEquals \
-               'BUFFER was modified' \
-               'ec' \
-               "$BUFFER"
-
-       assertNull \
-               'POSTDISPLAY was not cleared' \
-               "$POSTDISPLAY"
-}
-
-testRetval() {
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'return 1'
-
-       _zsh_autosuggest_widget_clear 'original-widget'
-
-       assertEquals \
-               'Did not return correct value from original widget' \
-               '1' \
-               "$?"
-}
-
-testWidget() {
-       stub _zsh_autosuggest_highlight_reset
-       stub _zsh_autosuggest_clear
-       stub _zsh_autosuggest_highlight_apply
-
-       # Call the function pointed to by the widget since we can't call
-       # the widget itself when zle is not active
-       ${widgets[autosuggest-clear]#*:} 'original-widget'
-
-       assertTrue \
-               'autosuggest-clear widget does not exist' \
-               'zle -l autosuggest-clear'
-
-       assertTrue \
-               'highlight_reset was not called' \
-               'stub_called _zsh_autosuggest_highlight_reset'
-
-       assertTrue \
-               'widget function was not called' \
-               'stub_called _zsh_autosuggest_clear'
-
-       assertTrue \
-               'highlight_apply was not called' \
-               'stub_called _zsh_autosuggest_highlight_apply'
-
-       restore _zsh_autosuggest_highlight_reset
-       restore _zsh_autosuggest_clear
-       restore _zsh_autosuggest_highlight_apply
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/widgets/execute_test.zsh b/.zsh/external/zsh-autosuggestions/test/widgets/execute_test.zsh
deleted file mode 100644 (file)
index cb346db..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/../test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-tearDown() {
-       restore _zsh_autosuggest_invoke_original_widget
-}
-
-testRetval() {
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'return 1'
-
-       _zsh_autosuggest_widget_execute 'original-widget'
-
-       assertEquals \
-               'Did not return correct value from original widget' \
-               '1' \
-               "$?"
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/widgets/modify_test.zsh b/.zsh/external/zsh-autosuggestions/test/widgets/modify_test.zsh
deleted file mode 100644 (file)
index 7ed6346..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/../test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-setUp() {
-       BUFFER=''
-       POSTDISPLAY=''
-       ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=''
-}
-
-tearDown() {
-       restore _zsh_autosuggest_invoke_original_widget
-       restore _zsh_autosuggest_suggestion
-}
-
-testModify() {
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'BUFFER+="e"'
-
-       stub_and_echo \
-               _zsh_autosuggest_suggestion \
-               'echo hello'
-
-       _zsh_autosuggest_modify 'original-widget'
-
-       assertTrue \
-               'original widget not invoked' \
-               'stub_called _zsh_autosuggest_invoke_original_widget'
-
-       assertEquals \
-               'BUFFER was not modified' \
-               'e' \
-               "$BUFFER"
-
-       assertEquals \
-               'POSTDISPLAY does not contain suggestion' \
-               'cho hello' \
-               "$POSTDISPLAY"
-}
-
-testModifyBufferTooLarge() {
-
-       ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE='20'
-
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'BUFFER+="012345678901234567890"'
-
-       stub_and_echo \
-               _zsh_autosuggest_suggestion \
-               '012345678901234567890123456789'
-
-       _zsh_autosuggest_modify 'original-widget'
-
-       assertTrue \
-               'original widget not invoked' \
-               'stub_called _zsh_autosuggest_invoke_original_widget'
-
-       assertEquals \
-               'BUFFER was not modified' \
-               '012345678901234567890' \
-               "$BUFFER"
-
-       assertEquals \
-               'POSTDISPLAY does not contain suggestion' \
-               '' \
-               "$POSTDISPLAY"
-}
-
-testRetval() {
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'return 1'
-
-       _zsh_autosuggest_widget_modify 'original-widget'
-
-       assertEquals \
-               'Did not return correct value from original widget' \
-               '1' \
-               "$?"
-}
-
-run_tests "$0"
diff --git a/.zsh/external/zsh-autosuggestions/test/widgets/partial_accept_test.zsh b/.zsh/external/zsh-autosuggestions/test/widgets/partial_accept_test.zsh
deleted file mode 100644 (file)
index 60c78a6..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/env zsh
-
-source "${0:a:h}/../test_helper.zsh"
-
-oneTimeSetUp() {
-       source_autosuggestions
-}
-
-setUp() {
-       BUFFER=''
-       POSTDISPLAY=''
-       CURSOR=0
-}
-
-tearDown() {
-       restore _zsh_autosuggest_invoke_original_widget
-}
-
-testCursorMovesOutOfBuffer() {
-       BUFFER='ec'
-       POSTDISPLAY='ho hello'
-       CURSOR=1
-
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'CURSOR=5; LBUFFER="echo "; RBUFFER="hello"'
-
-       _zsh_autosuggest_partial_accept 'original-widget'
-
-       assertTrue \
-               'original widget not invoked' \
-               'stub_called _zsh_autosuggest_invoke_original_widget'
-
-       assertEquals \
-               'BUFFER was not modified correctly' \
-               'echo ' \
-               "$BUFFER"
-
-       assertEquals \
-               'POSTDISPLAY was not modified correctly' \
-               'hello' \
-               "$POSTDISPLAY"
-}
-
-testCursorStaysInBuffer() {
-       BUFFER='echo hello'
-       POSTDISPLAY=' world'
-       CURSOR=1
-
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'CURSOR=5; LBUFFER="echo "; RBUFFER="hello"'
-
-       _zsh_autosuggest_partial_accept 'original-widget'
-
-       assertTrue \
-               'original widget not invoked' \
-               'stub_called _zsh_autosuggest_invoke_original_widget'
-
-       assertEquals \
-               'BUFFER was modified' \
-               'echo hello' \
-               "$BUFFER"
-
-       assertEquals \
-               'POSTDISPLAY was modified' \
-               ' world' \
-               "$POSTDISPLAY"
-}
-
-testRetval() {
-       stub_and_eval \
-               _zsh_autosuggest_invoke_original_widget \
-               'return 1'
-
-       _zsh_autosuggest_widget_partial_accept 'original-widget'
-
-       assertEquals \
-               'Did not return correct value from original widget' \
-               '1' \
-               "$?"
-}
-
-run_tests "$0"
index 4905776..0d389ec 100644 (file)
@@ -1,8 +1,8 @@
 # Fish-like fast/unobtrusive autosuggestions for zsh.
 # https://github.com/zsh-users/zsh-autosuggestions
-# v0.3.3
+# v0.4.0
 # Copyright (c) 2013 Thiago de Arruda
-# Copyright (c) 2016 Eric Freese
+# Copyright (c) 2016-2017 Eric Freese
 # 
 # Permission is hereby granted, free of charge, to any person
 # obtaining a copy of this software and associated documentation
 # 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                                     #
 #--------------------------------------------------------------------#
@@ -68,7 +78,6 @@ ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
 # Widgets that accept the suggestion as far as the cursor moves
 ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
        forward-word
-       emacs-forward-word
        vi-forward-word
        vi-forward-word-end
        vi-forward-blank-word
@@ -88,52 +97,71 @@ 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_pty
+
 #--------------------------------------------------------------------#
-# Handle Deprecated Variables/Widgets                                #
+# Utility Functions                                                  #
 #--------------------------------------------------------------------#
 
-_zsh_autosuggest_deprecated_warning() {
-       >&2 echo "zsh-autosuggestions: $@"
+_zsh_autosuggest_escape_command() {
+       setopt localoptions EXTENDED_GLOB
+
+       # Escape special chars in the string (requires EXTENDED_GLOB)
+       echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}"
 }
 
-_zsh_autosuggest_check_deprecated_config() {
-       if [ -n "$AUTOSUGGESTION_HIGHLIGHT_COLOR" ]; then
-               _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_HIGHLIGHT_COLOR is deprecated. Use ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE instead."
-               [ -z "$ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" ] && ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=$AUTOSUGGESTION_HIGHLIGHT_STYLE
-               unset AUTOSUGGESTION_HIGHLIGHT_STYLE
-       fi
+#--------------------------------------------------------------------#
+# Feature Detection                                                  #
+#--------------------------------------------------------------------#
 
-       if [ -n "$AUTOSUGGESTION_HIGHLIGHT_CURSOR" ]; then
-               _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_HIGHLIGHT_CURSOR is deprecated."
-               unset AUTOSUGGESTION_HIGHLIGHT_CURSOR
-       fi
+_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 [ -n "$AUTOSUGGESTION_ACCEPT_RIGHT_ARROW" ]; then
-               _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_ACCEPT_RIGHT_ARROW is deprecated. The right arrow now accepts the suggestion by default."
-               unset AUTOSUGGESTION_ACCEPT_RIGHT_ARROW
+       if (( REPLY )); then
+               _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1
+       else
+               _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0
        fi
-}
 
-_zsh_autosuggest_deprecated_start_widget() {
-       _zsh_autosuggest_deprecated_warning "The autosuggest-start widget is deprecated. For more info, see the README at https://github.com/zsh-users/zsh-autosuggestions."
-       zle -D autosuggest-start
-       eval "zle-line-init() {
-               $(echo $functions[${widgets[zle-line-init]#*:}] | sed -e 's/zle autosuggest-start//g')
-       }"
+       zpty -d zsh_autosuggest_feature_detect
 }
 
-zle -N autosuggest-start _zsh_autosuggest_deprecated_start_widget
-
 #--------------------------------------------------------------------#
 # 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
@@ -141,33 +169,38 @@ _zsh_autosuggest_bind_widget() {
 
                # User-defined widget
                user:*)
-                       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
@@ -178,7 +211,7 @@ _zsh_autosuggest_bind_widgets() {
        ignore_widgets=(
                .\*
                _\*
-               zle-line-\*
+               zle-\*
                autosuggest-\*
                $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\*
                $ZSH_AUTOSUGGEST_IGNORE_WIDGETS
@@ -245,6 +278,30 @@ _zsh_autosuggest_highlight_apply() {
 # Autosuggest Widget Implementations                                 #
 #--------------------------------------------------------------------#
 
+# Disable suggestions
+_zsh_autosuggest_disable() {
+       typeset -g _ZSH_AUTOSUGGEST_DISABLED
+       _zsh_autosuggest_clear
+}
+
+# Enable suggestions
+_zsh_autosuggest_enable() {
+       unset _ZSH_AUTOSUGGEST_DISABLED
+
+       if [ $#BUFFER -gt 0 ]; then
+               _zsh_autosuggest_fetch
+       fi
+}
+
+# Toggle suggestions (enable/disable)
+_zsh_autosuggest_toggle() {
+       if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then
+               _zsh_autosuggest_enable
+       else
+               _zsh_autosuggest_disable
+       fi
+}
+
 # Clear the suggestion
 _zsh_autosuggest_clear() {
        # Remove the suggestion
@@ -257,37 +314,77 @@ _zsh_autosuggest_clear() {
 _zsh_autosuggest_modify() {
        local -i retval
 
+       # Only added to zsh very recently
+       local -i KEYS_QUEUED_COUNT
+
        # Save the contents of the buffer/postdisplay
        local orig_buffer="$BUFFER"
        local orig_postdisplay="$POSTDISPLAY"
 
-       # Clear suggestion while original widget runs
+       # Clear suggestion while waiting for next one
        unset POSTDISPLAY
 
        # Original widget may modify the buffer
        _zsh_autosuggest_invoke_original_widget $@
        retval=$?
 
+       # Don't fetch a new suggestion if there's more input to be read immediately
+       if [[ $PENDING > 0 ]] || [[ $KEYS_QUEUED_COUNT > 0 ]]; then
+               return $retval
+       fi
+
+       # Optimize if manually typing in the suggestion
+       if [ $#BUFFER -gt $#orig_buffer ]; then
+               local added=${BUFFER#$orig_buffer}
+
+               # If the string added matches the beginning of the postdisplay
+               if [ "$added" = "${orig_postdisplay:0:$#added}" ]; then
+                       POSTDISPLAY="${orig_postdisplay:$#added}"
+                       return $retval
+               fi
+       fi
+
        # Don't fetch a new suggestion if the buffer hasn't changed
        if [ "$BUFFER" = "$orig_buffer" ]; then
                POSTDISPLAY="$orig_postdisplay"
                return $retval
        fi
 
+       # Bail out if suggestions are disabled
+       if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then
+               return $?
+       fi
+
        # Get a new suggestion if the buffer is not empty after modification
-       local suggestion
        if [ $#BUFFER -gt 0 ]; then
-               if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -lt "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then
-                       suggestion="$(_zsh_autosuggest_suggestion "$BUFFER")"
+               if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -le "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then
+                       _zsh_autosuggest_fetch
                fi
        fi
 
-       # Add the suggestion to the POSTDISPLAY
-       if [ -n "$suggestion" ]; then
-               POSTDISPLAY="${suggestion#$BUFFER}"
+       return $retval
+}
+
+# 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
+               _zsh_autosuggest_async_request "$BUFFER"
+       else
+               local suggestion
+               _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER"
+               _zsh_autosuggest_suggest "$suggestion"
        fi
+}
 
-       return $retval
+# Offer a suggestion
+_zsh_autosuggest_suggest() {
+       local suggestion="$1"
+
+       if [ -n "$suggestion" ] && [ $#BUFFER -gt 0 ]; then
+               POSTDISPLAY="${suggestion#$BUFFER}"
+       else
+               unset POSTDISPLAY
+       fi
 }
 
 # Accept the entire suggestion
@@ -357,7 +454,7 @@ _zsh_autosuggest_partial_accept() {
        return $retval
 }
 
-for action in clear modify accept partial_accept execute; do
+for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
        eval "_zsh_autosuggest_widget_$action() {
                local -i retval
 
@@ -368,34 +465,20 @@ for action in clear modify accept partial_accept execute; do
 
                _zsh_autosuggest_highlight_apply
 
+               zle -R
+
                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
-
-#--------------------------------------------------------------------#
-# Suggestion                                                         #
-#--------------------------------------------------------------------#
-
-# Delegate to the selected strategy to determine a suggestion
-_zsh_autosuggest_suggestion() {
-       local escaped_prefix="$(_zsh_autosuggest_escape_command "$1")"
-       local strategy_function="_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY"
-
-       if [ -n "$functions[$strategy_function]" ]; then
-               echo -E "$($strategy_function "$escaped_prefix")"
-       fi
-}
-
-_zsh_autosuggest_escape_command() {
-       setopt localoptions EXTENDED_GLOB
-
-       # Escape special chars in the string (requires EXTENDED_GLOB)
-       echo -E "${1//(#m)[\\()\[\]|*?~]/\\$MATCH}"
-}
+zle -N autosuggest-enable _zsh_autosuggest_widget_enable
+zle -N autosuggest-disable _zsh_autosuggest_widget_disable
+zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
 
 #--------------------------------------------------------------------#
 # Default Suggestion Strategy                                        #
@@ -405,7 +488,21 @@ _zsh_autosuggest_escape_command() {
 #
 
 _zsh_autosuggest_strategy_default() {
-       fc -lnrm "$1*" 1 2>/dev/null | head -n 1
+       # 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
+       local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
+
+       # Get the history items that match
+       # - (r) subscript flag makes the pattern match on values
+       suggestion="${history[(r)$prefix*]}"
+
 }
 
 #--------------------------------------------------------------------#
@@ -430,7 +527,7 @@ _zsh_autosuggest_strategy_default() {
 # `HIST_EXPIRE_DUPS_FIRST`.
 
 _zsh_autosuggest_strategy_match_prev_cmd() {
-       local prefix="$1"
+       local prefix="${1//(#m)[\\()\[\]|*?~]/\\$MATCH}"
 
        # Get all history event numbers that correspond to history
        # entries that match pattern $prefix*
@@ -456,8 +553,117 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
                fi
        done
 
-       # Echo the matched history entry
-       echo -E "$history[$histkey]"
+       # Give back the matched history entry
+       suggestion="$history[$histkey]"
+}
+
+#--------------------------------------------------------------------#
+# 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
+       }
+
+       # Output only newlines (not carriage return + newline)
+       stty -onlcr
+
+       # Silence any error messages
+       exec 2>/dev/null
+
+       local strategy=$1
+       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'
+}
+
+# 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() {
+       setopt LOCAL_OPTIONS EXTENDED_GLOB
+
+       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 -eq 0 ]; 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 _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY"
+
+       # 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() {
+       if zpty -t $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null; then
+               # Remove the input handler
+               zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
+
+               # Destroy the zpty
+               zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
+       fi
+}
+
+_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
 }
 
 #--------------------------------------------------------------------#
@@ -466,9 +672,20 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
 
 # Start the autosuggestion widgets
 _zsh_autosuggest_start() {
-       _zsh_autosuggest_check_deprecated_config
+       add-zsh-hook -d precmd _zsh_autosuggest_start
+
        _zsh_autosuggest_bind_widgets
+
+       # Re-bind widgets on every precmd to ensure we wrap other wrappers.
+       # Specifically, highlighting breaks if our widgets are wrapped by
+       # 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
 }
 
-autoload -Uz add-zsh-hook
+# Start the autosuggestion widgets on the next precmd
 add-zsh-hook precmd _zsh_autosuggest_start