Compare commits

..

1 Commits

Author SHA1 Message Date
Eric Freese
9fb9675306 Fix #141: Disable suggestions for widgets called from widgets 2016-05-28 08:34:16 -06:00
76 changed files with 1280 additions and 2208 deletions

View File

@@ -1,16 +0,0 @@
version: 2
jobs:
build:
parallelism: 4
shell: /bin/bash --login
docker:
- image: ericfreese/zsh-autosuggestions-test:latest
command: /sbin/init
steps:
- checkout
- run:
name: Running tests
command: |
for v in $(grep "^[^#]" ZSH_VERSIONS | awk "(NR + $CIRCLE_NODE_INDEX) % $CIRCLE_NODE_TOTAL == 0"); do
TEST_ZSH_BIN=zsh-$v make test || exit 1
done

View File

@@ -8,11 +8,3 @@ indent_size = 4
[*.md]
indent_style = space
[*.rb]
indent_style = space
indent_size = 2
[*.yml]
indent_style = space
indent_size = 2

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[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

3
.rspec
View File

@@ -1,3 +0,0 @@
--color
--require spec_helper
--format documentation

View File

@@ -1,30 +0,0 @@
# 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

View File

@@ -1 +0,0 @@
2.5.3

View File

@@ -1,55 +1,5 @@
# Changelog
## v0.5.0
- Don't overwrite config with default values (#335)
- Support fallback strategies by supplying array to suggestion config var
- Rename "default" suggestion strategy to "history" to name it based on what it actually does
- Reset opts in some functions affected by `GLOB_SUBST` (#334)
- Support widgets starting with dashes (ex: `-a-widget`) (#337)
- Skip async tests in zsh versions less than 5.0.8 because of reliability issues
- Fix handling of newline + carriage return in async pty (#333)
## v0.4.3
- Avoid bell when accepting suggestions with `autosuggest-accept` (#228)
- Don't fetch suggestions after [up,down]-line-or-beginning-search (#227, #241)
- We are now running CI against new 5.5.1 version
- Fix partial-accept in vi mode (#188)
- Fix suggestion disappearing on fast movement after switching to `vicmd` mode (#290)
- Fix issue rotating through kill ring with `yank-pop` (#301)
- Fix issue creating new pty for async mode when previous pty is not properly cleaned up (#249)
## v0.4.2
- Fix bug in zsh versions older than 5.0.8 (#296)
- Officially support back to zsh v4.3.11
## v0.4.1
- Switch to [[ and (( conditionals instead of [ (#257)
- Avoid warnnestedvar warnings with `typeset -g` (#275)
- Replace tabs with spaces in yaml (#268)
- Clean up and fix escaping of special characters (#267)
- Add `emacs-forward-word` to default list of partial accept widgets (#246)
## v0.4.0
- High-level integration tests using RSpec and tmux
- Add continuous integration with Circle CI
- Experimental support for asynchronous suggestions (#170)
- Fix problems with multi-line suggestions (#225)
- Optimize case where manually typing in suggestion
- Avoid wrapping any zle-* widgets (#206)
- 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)
- Add config option for maximum buffer length to fetch suggestions for (#178)
- Add config option for list of widgets to ignore (#184)
- Don't fetch a new suggestion unless a modification widget actually modifies the buffer (#183)
## v0.3.2
- Test runner now supports running specific tests and choosing zsh binary
- Return code from original widget is now correctly passed through (#135)

View File

@@ -1,20 +0,0 @@
FROM ruby:2.5.3-alpine
RUN apk add --no-cache autoconf
RUN apk add --no-cache libtool
RUN apk add --no-cache libcap-dev
RUN apk add --no-cache pcre-dev
RUN apk add --no-cache curl
RUN apk add --no-cache build-base
RUN apk add --no-cache ncurses-dev
RUN apk add --no-cache tmux
WORKDIR /zsh-autosuggestions
ADD ZSH_VERSIONS /zsh-autosuggestions/ZSH_VERSIONS
ADD install_test_zsh.sh /zsh-autosuggestions/install_test_zsh.sh
RUN ./install_test_zsh.sh
ADD Gemfile /zsh-autosuggestions/Gemfile
ADD Gemfile.lock /zsh-autosuggestions/Gemfile.lock
RUN bundle install

View File

@@ -1,5 +0,0 @@
source 'https://rubygems.org'
gem 'rspec'
gem 'rspec-wait'
gem 'pry-byebug'

View File

@@ -1,41 +0,0 @@
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

View File

@@ -1,77 +0,0 @@
## Installation
### Manual (Git Clone)
1. Clone this repository somewhere on your machine. This guide will assume `~/.zsh/zsh-autosuggestions`.
```sh
git clone https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions
```
2. Add the following to your `.zshrc`:
```sh
source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh
```
3. Start a new terminal session.
### Antigen
1. Add the following to your `.zshrc`:
```sh
antigen bundle zsh-users/zsh-autosuggestions
```
2. Start a new terminal session.
### Oh My Zsh
1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`)
```sh
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
```
2. Add the plugin to the list of plugins for Oh My Zsh to load (inside `~/.zshrc`):
```sh
plugins=(zsh-autosuggestions)
```
3. Start a new terminal session.
### Arch Linux
1. Install [`zsh-autosuggestions`](https://www.archlinux.org/packages/community/any/zsh-autosuggestions/) from the `community` repository.
```sh
pacman -S zsh-autosuggestions
```
or, to use a package based on the `master` branch, install [`zsh-autosuggestions-git`](https://aur.archlinux.org/packages/zsh-autosuggestions-git/) from the [AUR](https://wiki.archlinux.org/index.php/Arch_User_Repository).
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.

View File

@@ -1,5 +1,5 @@
Copyright (c) 2013 Thiago de Arruda
Copyright (c) 2016-2018 Eric Freese
Copyright (c) 2016 Eric Freese
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

View File

@@ -1,16 +1,14 @@
SRC_DIR := ./src
VENDOR_DIR := ./vendor
SRC_FILES := \
$(SRC_DIR)/setup.zsh \
$(SRC_DIR)/config.zsh \
$(SRC_DIR)/util.zsh \
$(SRC_DIR)/features.zsh \
$(SRC_DIR)/deprecated.zsh \
$(SRC_DIR)/bind.zsh \
$(SRC_DIR)/highlight.zsh \
$(SRC_DIR)/widgets.zsh \
$(SRC_DIR)/suggestion.zsh \
$(SRC_DIR)/strategies/*.zsh \
$(SRC_DIR)/fetch.zsh \
$(SRC_DIR)/async.zsh \
$(SRC_DIR)/start.zsh
HEADER_FILES := \
@@ -21,17 +19,29 @@ 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 -n "$$TEST_ZSH_BIN" && echo "Testing zsh binary: $(TEST_ZSH_BIN)" || true
bundle exec rspec $(TESTS)
test: all $(TEST_PREREQS)
script/test_runner.zsh $(TESTS)

View File

@@ -4,16 +4,43 @@ _[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._
It suggests commands as you type, based on command history.
Requirements: Zsh v4.3.11 or later
[![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>
## Installation
See [INSTALL.md](INSTALL.md).
### Manual
1. Clone this repository somewhere on your machine. This guide will assume `~/.zsh/zsh-autosuggestions`.
```sh
git clone git://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions
```
2. Add the following to your `.zshrc`:
```sh
source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh
```
3. Start a new terminal session.
### Oh My Zsh
1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`)
```sh
git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions
```
2. Add the plugin to the list of plugins for Oh My Zsh to load:
```sh
plugins=(zsh-autosuggestions)
```
3. Start a new terminal session.
## Usage
@@ -27,7 +54,7 @@ If you invoke the `forward-word` widget, it will partially accept the suggestion
## Configuration
You may want to override the default global config variables. Default values of these variables can be found [here](src/config.zsh).
You may want to override the default global config variables after sourcing the plugin. Default values of these variables can be found [here](src/config.zsh).
**Note:** If you are using Oh My Zsh, you can put this configuration in a file in the `$ZSH_CUSTOM` directory. See their comments on [overriding internals](https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals).
@@ -39,10 +66,10 @@ Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion
### Suggestion Strategy
`ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. The strategies in the array are tried successively until a suggestion is found. There are currently two built-in strategies to choose from:
Set `ZSH_AUTOSUGGEST_STRATEGY` to choose the strategy for generating suggestions. There are currently two to choose from:
- `history`: Chooses the most recent match from history.
- `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`.
- `default`: Chooses the most recent match.
- `match_prev_cmd`: Chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)).
### Widget Mapping
@@ -53,34 +80,19 @@ This plugin works by triggering custom behavior when certain [zle widgets](http:
- `ZSH_AUTOSUGGEST_ACCEPT_WIDGETS`: Widgets in this array will accept the suggestion when invoked.
- `ZSH_AUTOSUGGEST_EXECUTE_WIDGETS`: Widgets in this array will execute the suggestion when invoked.
- `ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS`: Widgets in this array will partially accept the suggestion when invoked.
- `ZSH_AUTOSUGGEST_IGNORE_WIDGETS`: Widgets in this array will not trigger any custom behavior.
Widgets that modify the buffer and are not found in any of these arrays will fetch a new suggestion after they are invoked.
Widgets not in any of these lists will update the suggestion when invoked.
**Note:** A widget shouldn't belong to more than one of the above arrays.
### Disabling suggestion for large buffers
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 a few widgets that you can use with `bindkey`:
This plugin provides three 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.
@@ -135,23 +147,9 @@ Pull requests are welcome! If you send a pull request, please:
### Testing
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.
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).
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`).
A docker image for testing is available [on docker hub](https://hub.docker.com/r/ericfreese/zsh-autosuggestions-test). It comes with ruby, the bundler dependencies, and all supported versions of zsh installed.
Pull the docker image with:
```sh
docker pull ericfreese/zsh-autosuggestions-test
```
To run the tests for a specific version of zsh (where `<version>` below is substituted with the contents of a line from the [`ZSH_VERSIONS`](ZSH_VERSIONS) file):
```sh
docker run -it -e TEST_ZSH_BIN=zsh-<version> -v $PWD:/zsh-autosuggestions zsh-autosuggestions-test make test
```
The test script lives at `script/test_runner.zsh`. To run the tests, run `make test`.
## License

View File

@@ -1 +1 @@
v0.5.0
v0.3.2

View File

@@ -1,15 +0,0 @@
# Zsh releases to run tests against
# See https://github.com/zsh-users/zsh/releases
#
# When modifying this file, rebuild and push docker image:
# $ docker build -t ericfreese/zsh-autosuggestions-test .
# $ docker push ericfreese/zsh-autosuggestions-test
4.3.11
5.0.2
5.0.8
5.1.1
5.2
5.3.1
5.4.2
5.5.1
5.6.2

View File

@@ -1,26 +0,0 @@
#!/bin/sh
set -ex
for v in $(grep "^[^#]" ZSH_VERSIONS); do
mkdir zsh-$v
cd zsh-$v
curl -L https://api.github.com/repos/zsh-users/zsh/tarball/zsh-$v | tar xz --strip=1
./Util/preconfig
./configure --enable-pcre \
--enable-cap \
--enable-multibyte \
--with-term-lib='ncursesw tinfo' \
--without-tcsetpgrp \
--program-suffix="-$v"
make install.bin
make install.modules
make install.fns
cd ..
rm -rf zsh-$v
done

54
script/test_runner.zsh Executable file
View File

@@ -0,0 +1,54 @@
#!/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

View File

@@ -1,84 +0,0 @@
context 'with asynchronous suggestions enabled' do
before do
skip 'Async mode not supported below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
end
let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] }
describe '`up-line-or-beginning-search`' do
let(:before_sourcing) do
-> do
session.
run_command('autoload -U up-line-or-beginning-search').
run_command('zle -N up-line-or-beginning-search').
send_string('bindkey "').
send_keys('C-v').send_keys('up').
send_string('" up-line-or-beginning-search').
send_keys('enter')
end
end
it 'should show previous history entries' do
with_history(
'echo foo',
'echo bar',
'echo baz'
) do
session.clear_screen
3.times { session.send_keys('up') }
wait_for { session.content }.to eq("echo foo")
end
end
end
it 'should not add extra carriage returns before newlines' do
session.
send_string('echo "').
send_keys('escape').
send_keys('enter').
send_string('"').
send_keys('enter')
session.clear_screen
session.send_string('echo')
wait_for { session.content }.to eq("echo \"\n\"")
end
it 'should treat carriage returns and newlines as separate characters' do
session.
send_string('echo "').
send_keys('C-v').
send_keys('enter').
send_string('foo"').
send_keys('enter')
session.
send_string('echo "').
send_keys('control').
send_keys('enter').
send_string('bar"').
send_keys('enter')
session.clear_screen
session.
send_string('echo "').
send_keys('C-v').
send_keys('enter')
wait_for { session.content }.to eq('echo "^Mfoo"')
end
describe 'exiting a subshell' do
it 'should not cause error messages to be printed' do
session.run_command('$(exit)')
sleep 1
expect(session.content).to eq('$(exit)')
end
end
end

View File

@@ -1,14 +0,0 @@
describe 'with `AUTO_CD` option set' do
let(:after_sourcing) do
-> {
session.run_command('setopt AUTO_CD')
session.run_command('autoload compinit && compinit')
}
end
it 'directory names are still completed' do
session.send_string('sr')
session.send_keys('C-i')
wait_for { session.content }.to eq('src/')
end
end

View File

@@ -1,27 +0,0 @@
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
end
end

View File

@@ -1,10 +0,0 @@
describe 'a running zpty command' do
let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } }
it 'is not affected by running zsh-autosuggestions' do
sleep 1 # Give a little time for precmd hooks to run
session.run_command('zpty -t kitty; echo $?')
wait_for { session.content }.to end_with("\n0")
end
end

View File

@@ -1,12 +0,0 @@
describe 'with `GLOB_SUBST` option set' do
let(:after_sourcing) do
-> {
session.run_command('setopt GLOB_SUBST')
}
end
it 'error messages are not printed' do
session.send_string('[[')
wait_for { session.content }.to eq('[[')
end
end

View File

@@ -1,14 +0,0 @@
describe 'multiple words killed with `backward-kill-word`' do
before do
session.
send_string('echo first second').
send_keys('C-w').
send_keys('C-w')
wait_for { session.content }.to eq('echo')
end
it 'can be yanked back with `yank`' do
session.send_keys('C-y')
wait_for { session.content }.to eq('echo first second')
end
end

View File

@@ -1,13 +0,0 @@
describe 'rebinding [' do
context 'initialized before sourcing the plugin' do
before do
session.run_command("function [ { $commands[\\[] \"$@\" }")
session.clear_screen
end
it 'executes the custom behavior and the built-in behavior' do
session.send_string('asdf')
wait_for { session.content }.to eq('asdf')
end
end
end

View File

@@ -1,67 +0,0 @@
describe 'when using vi mode' do
let(:before_sourcing) do
-> do
session.run_command('bindkey -v')
end
end
describe 'moving the cursor after exiting insert mode' do
it 'should not clear the current suggestion' do
with_history('foobar foo') do
session.
send_string('foo').
send_keys('escape').
send_keys('h')
wait_for { session.content }.to eq('foobar foo')
end
end
end
describe '`vi-forward-word-end`' do
it 'should accept through the end of the current word' do
with_history('foobar foo') do
session.
send_string('foo').
send_keys('escape').
send_keys('e'). # vi-forward-word-end
send_keys('a'). # vi-add-next
send_string('baz')
wait_for { session.content }.to eq('foobarbaz')
end
end
end
describe '`vi-forward-word`' do
it 'should accept through the first character of the next word' do
with_history('foobar foo') do
session.
send_string('foo').
send_keys('escape').
send_keys('w'). # vi-forward-word
send_keys('a'). # vi-add-next
send_string('az')
wait_for { session.content }.to eq('foobar faz')
end
end
end
describe '`vi-find-next-char`' do
it 'should accept through the next occurrence of the character' do
with_history('foobar foo') do
session.
send_string('foo').
send_keys('escape').
send_keys('f'). # vi-find-next-char
send_keys('o').
send_keys('a'). # vi-add-next
send_string('b')
wait_for { session.content }.to eq('foobar fob')
end
end
end
end

View File

@@ -1,39 +0,0 @@
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

View File

@@ -1,24 +0,0 @@
describe 'using `zle -U`' do
let(:before_sourcing) do
-> do
session.
run_command('_zsh_autosuggest_strategy_test() { sleep 1; _zsh_autosuggest_strategy_history "$1" }').
run_command('foo() { zle -U - "echo hello" }; zle -N foo; bindkey ^B foo')
end
end
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

View File

@@ -1,23 +0,0 @@
context 'with some items in the kill ring' do
before do
session.
send_string('echo foo').
send_keys('C-u').
send_string('echo bar').
send_keys('C-u')
end
describe '`yank-pop`' do
it 'should cycle through all items in the kill ring' do
session.send_keys('C-y')
wait_for { session.content }.to eq('echo bar')
session.send_keys('escape').send_keys('y')
wait_for { session.content }.to eq('echo foo')
session.send_keys('escape').send_keys('y')
wait_for { session.content }.to eq('echo bar')
end
end
end

View File

@@ -1,13 +0,0 @@
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

View File

@@ -1,19 +0,0 @@
context 'when async suggestions are enabled' do
let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] }
describe 'the zpty for async suggestions' do
it 'is created with the default name' do
session.run_command('zpty -t zsh_autosuggest_pty &>/dev/null; echo $?')
wait_for { session.content }.to end_with("\n0")
end
context 'when ZSH_AUTOSUGGEST_ASYNC_PTY_NAME is set' do
let(:options) { super() + ['ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=foo_pty'] }
it 'is created with the specified name' do
session.run_command('zpty -t foo_pty &>/dev/null; echo $?')
wait_for { session.content }.to end_with("\n0")
end
end
end
end

View File

@@ -1,30 +0,0 @@
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

View File

@@ -1,7 +0,0 @@
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

View File

@@ -1,7 +0,0 @@
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

View File

@@ -1,55 +0,0 @@
describe 'a suggestion for a given prefix' do
let(:history_strategy) { '_zsh_autosuggest_strategy_history() { suggestion="history" }' }
let(:foobar_strategy) { '_zsh_autosuggest_strategy_foobar() { [[ "foobar baz" = $1* ]] && suggestion="foobar baz" }' }
let(:foobaz_strategy) { '_zsh_autosuggest_strategy_foobaz() { [[ "foobaz bar" = $1* ]] && suggestion="foobaz bar" }' }
let(:after_sourcing) do
-> do
session.run_command(history_strategy)
end
end
it 'by default is determined by calling the `history` strategy function' do
session.send_string('h')
wait_for { session.content }.to eq('history')
end
context 'when ZSH_AUTOSUGGEST_STRATEGY is set to an array' do
let(:after_sourcing) do
-> do
session.
run_command(foobar_strategy).
run_command(foobaz_strategy).
run_command('ZSH_AUTOSUGGEST_STRATEGY=(foobar foobaz)')
end
end
it 'is determined by the first strategy function to return a suggestion' do
session.send_string('foo')
wait_for { session.content }.to eq('foobar baz')
session.send_string('baz')
wait_for { session.content }.to eq('foobaz bar')
end
end
context 'when ZSH_AUTOSUGGEST_STRATEGY is set to a string' do
let(:after_sourcing) do
-> do
session.
run_command(foobar_strategy).
run_command(foobaz_strategy).
run_command('ZSH_AUTOSUGGEST_STRATEGY="foobar foobaz"')
end
end
it 'is determined by the first strategy function to return a suggestion' do
session.send_string('foo')
wait_for { session.content }.to eq('foobar baz')
session.send_string('baz')
wait_for { session.content }.to eq('foobaz bar')
end
end
end

View File

@@ -1,7 +0,0 @@
describe 'suggestion fetching' do
it 'is performed synchronously'
context 'when ZSH_AUTOSUGGEST_USE_ASYNC is set' do
it 'is performed asynchronously'
end
end

View File

@@ -1,97 +0,0 @@
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

View File

@@ -1,52 +0,0 @@
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(:after_sourcing) { -> {} }
let(:options) { [] }
around do |example|
before_sourcing.call
session.run_command(options.join('; '))
session.run_command('source zsh-autosuggestions.zsh')
after_sourcing.call
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

View File

@@ -1,12 +0,0 @@
require 'strategies/special_characters_helper'
describe 'the `history` suggestion strategy' do
it 'suggests the last matching history entry' do
with_history('ls foo', 'ls bar', 'echo baz') do
session.send_string('ls')
wait_for { session.content }.to eq('ls bar')
end
end
include_examples 'special characters'
end

View File

@@ -1,21 +0,0 @@
require 'strategies/special_characters_helper'
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
include_examples 'special characters'
end

View File

@@ -1,62 +0,0 @@
shared_examples 'special characters' do
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
end

View File

@@ -1,99 +0,0 @@
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 zsh_version
@zsh_version ||= Gem::Version.new(`#{ZSH_BIN} -c 'echo -n $ZSH_VERSION'`)
end
def tmux_socket_name
@tmux_socket_name ||= SecureRandom.hex(6)
end
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
def attach!
tmux_command('attach-session')
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

View File

@@ -1,19 +0,0 @@
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

View File

@@ -1,42 +0,0 @@
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

View File

@@ -1,24 +0,0 @@
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

View File

@@ -1,26 +0,0 @@
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

View File

@@ -1,109 +0,0 @@
#--------------------------------------------------------------------#
# Async #
#--------------------------------------------------------------------#
# Zpty process is spawned running this function
_zsh_autosuggest_async_server() {
emulate -R zsh
# There is a bug in zpty module (fixed in zsh/master) by which a
# zpty that exits will kill all zpty processes that were forked
# before it. Here we set up a zsh exit hook to SIGKILL the zpty
# process immediately, before it has a chance to kill any other
# zpty processes.
zshexit() {
kill -KILL $$
sleep 1 # Block for long enough for the signal to come through
}
# Don't add any extra carriage returns
stty -onlcr
# Don't translate carriage returns to newlines
stty -icrnl
# Silence any error messages
exec 2>/dev/null
local last_pid
while IFS='' read -r -d $'\0' query; do
# Kill last bg process
kill -KILL $last_pid &>/dev/null
# Run suggestion search in the background
(
local suggestion
_zsh_autosuggest_fetch_suggestion "$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 )); then
integer -l zptyfd
exec {zptyfd}>&1 # Open a new file descriptor (above 10).
exec {zptyfd}>&- # Close it so it's free to be used by zpty.
fi
# Fork a zpty process running the server function
zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME _zsh_autosuggest_async_server
# Store the fd so we can remove the handler later
if (( REPLY )); then
_ZSH_AUTOSUGGEST_PTY_FD=$REPLY
else
_ZSH_AUTOSUGGEST_PTY_FD=$zptyfd
fi
# Set up input handler from the zpty
zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response
}
_zsh_autosuggest_async_pty_destroy() {
# Remove the input handler
zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
# Destroy the zpty
zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
}
_zsh_autosuggest_async_pty_recreate() {
_zsh_autosuggest_async_pty_destroy
_zsh_autosuggest_async_pty_create
}
_zsh_autosuggest_async_start() {
typeset -g _ZSH_AUTOSUGGEST_PTY_FD
_zsh_autosuggest_feature_detect_zpty_returns_fd
_zsh_autosuggest_async_pty_recreate
# We recreate the pty to get a fresh list of history events
add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate
}

View File

@@ -3,34 +3,12 @@
# 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
typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
}
_zsh_autosuggest_get_bind_count() {
if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
else
typeset -gi bind_count=0
fi
}
# 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
@@ -38,65 +16,48 @@ _zsh_autosuggest_bind_widget() {
# User-defined widget
user:*)
_zsh_autosuggest_incr_bind_count $widget
zle -N $prefix${bind_count}-$widget ${widgets[$widget]#*:}
zle -N $prefix$widget ${widgets[$widget]#*:}
;;
# Built-in widget
builtin)
_zsh_autosuggest_incr_bind_count $widget
eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }"
zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget
zle -N $prefix$widget _zsh_autosuggest_orig_$widget
;;
# Completion widget
completion:*)
_zsh_autosuggest_incr_bind_count $widget
eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
eval "zle -C $prefix${(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_${bind_count}_${(q)widget}() {
_zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@
eval "_zsh_autosuggest_bound_${(q)widget}() {
_zsh_autosuggest_widget_$autosuggest_action $prefix${(q)widget} \$@
}"
# Create the bound widget
zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget
zle -N $widget _zsh_autosuggest_bound_$widget
}
# Map all configured widgets to the right autosuggest widgets
_zsh_autosuggest_bind_widgets() {
emulate -L zsh
local widget
local ignore_widgets
ignore_widgets=(
.\*
_\*
zle-\*
autosuggest-\*
$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\*
$ZSH_AUTOSUGGEST_IGNORE_WIDGETS
)
local widget;
# Find every widget we might want to bind and bind it appropriately
for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do
if [[ -n ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]]; then
for widget in ${${(f)"$(builtin zle -la)"}:#(.*|_*|orig-*|autosuggest-*|$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX*|zle-line-*|run-help|which-command|beep|set-local-history|yank)}; do
if [ ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]; then
_zsh_autosuggest_bind_widget $widget clear
elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then
elif [ ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]; then
_zsh_autosuggest_bind_widget $widget accept
elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then
elif [ ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]; then
_zsh_autosuggest_bind_widget $widget execute
elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then
elif [ ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]; then
_zsh_autosuggest_bind_widget $widget partial_accept
else
# Assume any unspecified widget might modify the buffer
@@ -108,13 +69,13 @@ _zsh_autosuggest_bind_widgets() {
# Given the name of an original widget and args, invoke it, if it exists
_zsh_autosuggest_invoke_original_widget() {
# Do nothing unless called with at least one arg
(( $# )) || return 0
[ $# -gt 0 ] || return
local original_widget_name="$1"
shift
if (( ${+widgets[$original_widget_name]} )); then
if [ $widgets[$original_widget_name] ]; then
zle $original_widget_name -- $@
fi
}

View File

@@ -6,90 +6,44 @@
# Color to use when highlighting suggestion
# Uses format of `region_highlight`
# More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
(( ! ${+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE} )) &&
typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
# Prefix to use when saving original versions of bound widgets
(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) &&
typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
# Strategies to use to fetch a suggestion
# Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
typeset -ga ZSH_AUTOSUGGEST_STRATEGY
ZSH_AUTOSUGGEST_STRATEGY=(history)
}
ZSH_AUTOSUGGEST_STRATEGY=default
# Widgets that clear the suggestion
(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS
ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward
history-search-backward
history-beginning-search-forward
history-beginning-search-backward
history-substring-search-up
history-substring-search-down
up-line-or-beginning-search
down-line-or-beginning-search
up-line-or-history
down-line-or-history
accept-line
)
}
ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward
history-search-backward
history-beginning-search-forward
history-beginning-search-backward
history-substring-search-up
history-substring-search-down
up-line-or-history
down-line-or-history
accept-line
)
# Widgets that accept the entire suggestion
(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char
end-of-line
vi-forward-char
vi-end-of-line
vi-add-eol
)
}
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char
end-of-line
vi-forward-char
vi-end-of-line
vi-add-eol
)
# Widgets that accept the entire suggestion and execute it
(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS
ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
)
}
ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
)
# Widgets that accept the suggestion as far as the cursor moves
(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word
emacs-forward-word
vi-forward-word
vi-forward-word-end
vi-forward-blank-word
vi-forward-blank-word-end
vi-find-next-char
vi-find-next-char-skip
)
}
# Widgets that should be ignored (globbing supported but must be escaped)
(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS
ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
orig-\*
beep
run-help
set-local-history
which-command
yank
yank-pop
)
}
# Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
(( ! ${+ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE} )) &&
typeset -g ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
# Pty name for calculating autosuggestions asynchronously
(( ! ${+ZSH_AUTOSUGGEST_ASYNC_PTY_NAME} )) &&
typeset -g ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty
ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word
vi-forward-word
vi-forward-word-end
vi-forward-blank-word
vi-forward-blank-word-end
)

36
src/deprecated.zsh Normal file
View File

@@ -0,0 +1,36 @@
#--------------------------------------------------------------------#
# 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

View File

@@ -1,19 +0,0 @@
#--------------------------------------------------------------------#
# Feature Detection #
#--------------------------------------------------------------------#
_zsh_autosuggest_feature_detect_zpty_returns_fd() {
typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD
typeset -h REPLY
zpty zsh_autosuggest_feature_detect '{ zshexit() { kill -KILL $$; sleep 1 } }'
if (( REPLY )); then
_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1
else
_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0
fi
zpty -d zsh_autosuggest_feature_detect
}

View File

@@ -1,24 +0,0 @@
#--------------------------------------------------------------------#
# Fetch Suggestion #
#--------------------------------------------------------------------#
# Loops through all specified strategies and returns a suggestion
# from the first strategy to provide one.
#
_zsh_autosuggest_fetch_suggestion() {
typeset -g suggestion
local -a strategies
local strategy
# Ensure we are working with an array
strategies=(${=ZSH_AUTOSUGGEST_STRATEGY})
for strategy in $strategies; do
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Break once we've found a suggestion
[[ -n "$suggestion" ]] && break
done
}

View File

@@ -7,7 +7,7 @@
_zsh_autosuggest_highlight_reset() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if [[ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]]; then
if [ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]; then
region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}")
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi
@@ -17,8 +17,8 @@ _zsh_autosuggest_highlight_reset() {
_zsh_autosuggest_highlight_apply() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if (( $#POSTDISPLAY )); then
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE"
if [ $#POSTDISPLAY -gt 0 ]; then
_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE"
region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT")
else
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT

View File

@@ -1,10 +0,0 @@
#--------------------------------------------------------------------#
# Setup #
#--------------------------------------------------------------------#
# Precmd hooks for initializing the library and starting pty's
autoload -Uz add-zsh-hook
# Asynchronous suggestions are generated in a pty
zmodload zsh/zpty

View File

@@ -5,20 +5,9 @@
# Start the autosuggestion widgets
_zsh_autosuggest_start() {
add-zsh-hook -d precmd _zsh_autosuggest_start
_zsh_autosuggest_check_deprecated_config
_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
}
# Start the autosuggestion widgets on the next precmd
autoload -Uz add-zsh-hook
add-zsh-hook precmd _zsh_autosuggest_start

View File

@@ -0,0 +1,18 @@
#--------------------------------------------------------------------#
# Default Suggestion Strategy #
#--------------------------------------------------------------------#
# Suggests the most recent history item that matches the given
# prefix.
#
_zsh_autosuggest_strategy_default() {
local prefix="$1"
# Get the keys of the history items that match
local -a histkeys
histkeys=(${(k)history[(r)$prefix*]})
# Echo the value of the first key
echo -E "${history[$histkeys[1]]}"
}

View File

@@ -1,25 +0,0 @@
#--------------------------------------------------------------------#
# History Suggestion Strategy #
#--------------------------------------------------------------------#
# Suggests the most recent history item that matches the given
# prefix.
#
_zsh_autosuggest_strategy_history() {
# Reset options to defaults and enable LOCAL_OPTIONS
emulate -L zsh
# Enable globbing flags so that we can use (#m)
setopt EXTENDED_GLOB
# Escape backslashes and all of the glob operators so we can use
# this string as a pattern to search the $history associative array.
# - (#m) globbing flag enables setting references for match data
# TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
# Get the history items that match
# - (r) subscript flag makes the pattern match on values
typeset -g suggestion="${history[(r)${prefix}*]}"
}

View File

@@ -16,19 +16,9 @@
# will be 'ls foo' rather than 'ls bar' because your most recently
# executed command (pwd) was previously followed by 'ls foo'.
#
# Note that this strategy won't work as expected with ZSH options that don't
# preserve the history order such as `HIST_IGNORE_ALL_DUPS` or
# `HIST_EXPIRE_DUPS_FIRST`.
_zsh_autosuggest_strategy_match_prev_cmd() {
# Reset options to defaults and enable LOCAL_OPTIONS
emulate -L zsh
# Enable globbing flags so that we can use (#m)
setopt EXTENDED_GLOB
# TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
local prefix="$1"
# Get all history event numbers that correspond to history
# entries that match pattern $prefix*
@@ -54,6 +44,6 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
fi
done
# Give back the matched history entry
typeset -g suggestion="$history[$histkey]"
# Echo the matched history entry
echo -E "$history[$histkey]"
}

21
src/suggestion.zsh Normal file
View File

@@ -0,0 +1,21 @@
#--------------------------------------------------------------------#
# 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}"
}

View File

@@ -1,11 +0,0 @@
#--------------------------------------------------------------------#
# Utility Functions #
#--------------------------------------------------------------------#
_zsh_autosuggest_escape_command() {
setopt localoptions EXTENDED_GLOB
# Escape special chars in the string (requires EXTENDED_GLOB)
echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}"
}

View File

@@ -3,30 +3,6 @@
# 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 )); 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
@@ -39,96 +15,44 @@ _zsh_autosuggest_clear() {
_zsh_autosuggest_modify() {
local -i retval
# Only available in zsh >= 5.4
local -i KEYS_QUEUED_COUNT
# Save the contents of the buffer/postdisplay
local orig_buffer="$BUFFER"
local orig_postdisplay="$POSTDISPLAY"
# Clear suggestion while waiting for next one
# Clear suggestion while original widget runs
unset POSTDISPLAY
# Original widget may modify the buffer
# Original widget modifies the buffer
_zsh_autosuggest_invoke_original_widget $@
retval=$?
emulate -L zsh
# Don't fetch a new suggestion if there's more input to be read immediately
if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
POSTDISPLAY="$orig_postdisplay"
return $retval
fi
# Optimize if manually typing in the suggestion
if (( $#BUFFER > $#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
# Only fetch suggestions at the first level of widget recursion
if [ -z "${funcstack[(rn:2:)_zsh_autosuggest_widget_*]}" ]; then
# Get a new suggestion if the buffer is not empty after modification
local suggestion
if [ $#BUFFER -gt 0 ]; then
suggestion="$(_zsh_autosuggest_suggestion "$BUFFER")"
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
if (( $#BUFFER > 0 )); then
if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then
_zsh_autosuggest_fetch
# Add the suggestion to the POSTDISPLAY
if [ -n "$suggestion" ]; then
POSTDISPLAY="${suggestion#$BUFFER}"
else
unset POSTDISPLAY
fi
fi
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_fetch_suggestion "$BUFFER"
_zsh_autosuggest_suggest "$suggestion"
fi
}
# Offer a suggestion
_zsh_autosuggest_suggest() {
emulate -L zsh
local suggestion="$1"
if [[ -n "$suggestion" ]] && (( $#BUFFER )); then
POSTDISPLAY="${suggestion#$BUFFER}"
else
unset POSTDISPLAY
fi
}
# Accept the entire suggestion
_zsh_autosuggest_accept() {
local -i max_cursor_pos=$#BUFFER
# When vicmd keymap is active, the cursor can't move all the way
# to the end of the buffer
if [[ "$KEYMAP" = "vicmd" ]]; then
if [ "$KEYMAP" = "vicmd" ]; then
max_cursor_pos=$((max_cursor_pos - 1))
fi
# Only accept if the cursor is at the end of the buffer
if [[ $CURSOR = $max_cursor_pos ]]; then
if [ $CURSOR -eq $max_cursor_pos ]; then
# Add the suggestion to the buffer
BUFFER="$BUFFER$POSTDISPLAY"
@@ -157,7 +81,7 @@ _zsh_autosuggest_execute() {
# Partially accept the suggestion
_zsh_autosuggest_partial_accept() {
local -i retval cursor_loc
local -i retval
# Save the contents of the buffer so we can restore later if needed
local original_buffer="$BUFFER"
@@ -169,19 +93,13 @@ _zsh_autosuggest_partial_accept() {
_zsh_autosuggest_invoke_original_widget $@
retval=$?
# Normalize cursor location across vi/emacs modes
cursor_loc=$CURSOR
if [[ "$KEYMAP" = "vicmd" ]]; then
cursor_loc=$((cursor_loc + 1))
fi
# If we've moved past the end of the original buffer
if (( $cursor_loc > $#original_buffer )); then
if [ $CURSOR -gt $#original_buffer ]; then
# Set POSTDISPLAY to text right of the cursor
POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}"
POSTDISPLAY="$RBUFFER"
# Clip the buffer at the cursor
BUFFER="${BUFFER[1,$cursor_loc]}"
BUFFER="$LBUFFER"
else
# Restore the original buffer
BUFFER="$original_buffer"
@@ -190,31 +108,21 @@ _zsh_autosuggest_partial_accept() {
return $retval
}
() {
local action
for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
eval "_zsh_autosuggest_widget_$action() {
local -i retval
for action in clear modify accept partial_accept execute; do
eval "_zsh_autosuggest_widget_$action() {
local -i retval
_zsh_autosuggest_highlight_reset
_zsh_autosuggest_highlight_reset
_zsh_autosuggest_$action \$@
retval=\$?
_zsh_autosuggest_$action \$@
retval=\$?
_zsh_autosuggest_highlight_apply
_zsh_autosuggest_highlight_apply
zle -R
return \$retval
}"
done
return \$retval
}"
done
zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest
zle -N autosuggest-accept _zsh_autosuggest_widget_accept
zle -N autosuggest-clear _zsh_autosuggest_widget_clear
zle -N autosuggest-execute _zsh_autosuggest_widget_execute
zle -N autosuggest-enable _zsh_autosuggest_widget_enable
zle -N autosuggest-disable _zsh_autosuggest_widget_disable
zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
}
zle -N autosuggest-accept _zsh_autosuggest_widget_accept
zle -N autosuggest-clear _zsh_autosuggest_widget_clear
zle -N autosuggest-execute _zsh_autosuggest_widget_execute

45
test/bind_test.zsh Normal file
View File

@@ -0,0 +1,45 @@
#!/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"

73
test/highlight_test.zsh Normal file
View File

@@ -0,0 +1,73 @@
#!/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"

View File

@@ -0,0 +1,56 @@
#!/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"

View File

@@ -0,0 +1,74 @@
#!/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"

95
test/strategies_test.zsh Normal file
View File

@@ -0,0 +1,95 @@
#!/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'
}
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
assertParenthesesSuggestion
assertSquareBracketsSuggestion
done
}
run_tests "$0"

46
test/suggestion_test.zsh Normal file
View File

@@ -0,0 +1,46 @@
#!/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"

60
test/test_helper.zsh Normal file
View File

@@ -0,0 +1,60 @@
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")"
}

View File

@@ -0,0 +1,161 @@
#!/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_widget_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_widget_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_widget_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_widget_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"

View File

@@ -0,0 +1,77 @@
#!/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_widget_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"

View File

@@ -0,0 +1,26 @@
#!/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"

View File

@@ -0,0 +1,58 @@
#!/usr/bin/env zsh
source "${0:a:h}/../test_helper.zsh"
oneTimeSetUp() {
source_autosuggestions
}
setUp() {
BUFFER=''
POSTDISPLAY=''
}
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_widget_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"
}
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"

View File

@@ -0,0 +1,84 @@
#!/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_widget_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_widget_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"

1
vendor/shunit2 vendored Submodule

Submodule vendor/shunit2 added at 46973db9df

1
vendor/stub.sh vendored Submodule

Submodule vendor/stub.sh added at bd6f3c4666

View File

@@ -1,8 +1,8 @@
# Fish-like fast/unobtrusive autosuggestions for zsh.
# https://github.com/zsh-users/zsh-autosuggestions
# v0.5.0
# v0.3.2
# Copyright (c) 2013 Thiago de Arruda
# Copyright (c) 2016-2018 Eric Freese
# Copyright (c) 2016 Eric Freese
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -25,16 +25,6 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#--------------------------------------------------------------------#
# Setup #
#--------------------------------------------------------------------#
# Precmd hooks for initializing the library and starting pty's
autoload -Uz add-zsh-hook
# Asynchronous suggestions are generated in a pty
zmodload zsh/zpty
#--------------------------------------------------------------------#
# Global Configuration Variables #
#--------------------------------------------------------------------#
@@ -42,156 +32,94 @@ zmodload zsh/zpty
# Color to use when highlighting suggestion
# Uses format of `region_highlight`
# More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
(( ! ${+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE} )) &&
typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
# Prefix to use when saving original versions of bound widgets
(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) &&
typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
# Strategies to use to fetch a suggestion
# Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
typeset -ga ZSH_AUTOSUGGEST_STRATEGY
ZSH_AUTOSUGGEST_STRATEGY=(history)
}
ZSH_AUTOSUGGEST_STRATEGY=default
# Widgets that clear the suggestion
(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS
ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward
history-search-backward
history-beginning-search-forward
history-beginning-search-backward
history-substring-search-up
history-substring-search-down
up-line-or-beginning-search
down-line-or-beginning-search
up-line-or-history
down-line-or-history
accept-line
)
}
ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward
history-search-backward
history-beginning-search-forward
history-beginning-search-backward
history-substring-search-up
history-substring-search-down
up-line-or-history
down-line-or-history
accept-line
)
# Widgets that accept the entire suggestion
(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char
end-of-line
vi-forward-char
vi-end-of-line
vi-add-eol
)
}
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char
end-of-line
vi-forward-char
vi-end-of-line
vi-add-eol
)
# Widgets that accept the entire suggestion and execute it
(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS
ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
)
}
ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
)
# Widgets that accept the suggestion as far as the cursor moves
(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word
emacs-forward-word
vi-forward-word
vi-forward-word-end
vi-forward-blank-word
vi-forward-blank-word-end
vi-find-next-char
vi-find-next-char-skip
)
ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word
vi-forward-word
vi-forward-word-end
vi-forward-blank-word
vi-forward-blank-word-end
)
#--------------------------------------------------------------------#
# Handle Deprecated Variables/Widgets #
#--------------------------------------------------------------------#
_zsh_autosuggest_deprecated_warning() {
>&2 echo "zsh-autosuggestions: $@"
}
# Widgets that should be ignored (globbing supported but must be escaped)
(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS
ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
orig-\*
beep
run-help
set-local-history
which-command
yank
yank-pop
)
}
# Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
(( ! ${+ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE} )) &&
typeset -g ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=
# Pty name for calculating autosuggestions asynchronously
(( ! ${+ZSH_AUTOSUGGEST_ASYNC_PTY_NAME} )) &&
typeset -g ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty
#--------------------------------------------------------------------#
# Utility Functions #
#--------------------------------------------------------------------#
_zsh_autosuggest_escape_command() {
setopt localoptions EXTENDED_GLOB
# Escape special chars in the string (requires EXTENDED_GLOB)
echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}"
}
#--------------------------------------------------------------------#
# Feature Detection #
#--------------------------------------------------------------------#
_zsh_autosuggest_feature_detect_zpty_returns_fd() {
typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD
typeset -h REPLY
zpty zsh_autosuggest_feature_detect '{ zshexit() { kill -KILL $$; sleep 1 } }'
if (( REPLY )); then
_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1
else
_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0
_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
zpty -d zsh_autosuggest_feature_detect
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
#--------------------------------------------------------------------#
# 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
typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
}
_zsh_autosuggest_get_bind_count() {
if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
else
typeset -gi bind_count=0
fi
}
# 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
@@ -199,65 +127,48 @@ _zsh_autosuggest_bind_widget() {
# User-defined widget
user:*)
_zsh_autosuggest_incr_bind_count $widget
zle -N $prefix${bind_count}-$widget ${widgets[$widget]#*:}
zle -N $prefix$widget ${widgets[$widget]#*:}
;;
# Built-in widget
builtin)
_zsh_autosuggest_incr_bind_count $widget
eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }"
zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget
zle -N $prefix$widget _zsh_autosuggest_orig_$widget
;;
# Completion widget
completion:*)
_zsh_autosuggest_incr_bind_count $widget
eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
eval "zle -C $prefix${(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_${bind_count}_${(q)widget}() {
_zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@
eval "_zsh_autosuggest_bound_${(q)widget}() {
_zsh_autosuggest_widget_$autosuggest_action $prefix${(q)widget} \$@
}"
# Create the bound widget
zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget
zle -N $widget _zsh_autosuggest_bound_$widget
}
# Map all configured widgets to the right autosuggest widgets
_zsh_autosuggest_bind_widgets() {
emulate -L zsh
local widget
local ignore_widgets
ignore_widgets=(
.\*
_\*
zle-\*
autosuggest-\*
$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\*
$ZSH_AUTOSUGGEST_IGNORE_WIDGETS
)
local widget;
# Find every widget we might want to bind and bind it appropriately
for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do
if [[ -n ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]]; then
for widget in ${${(f)"$(builtin zle -la)"}:#(.*|_*|orig-*|autosuggest-*|$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX*|zle-line-*|run-help|which-command|beep|set-local-history|yank)}; do
if [ ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]; then
_zsh_autosuggest_bind_widget $widget clear
elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then
elif [ ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]; then
_zsh_autosuggest_bind_widget $widget accept
elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then
elif [ ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]; then
_zsh_autosuggest_bind_widget $widget execute
elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then
elif [ ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]; then
_zsh_autosuggest_bind_widget $widget partial_accept
else
# Assume any unspecified widget might modify the buffer
@@ -269,13 +180,13 @@ _zsh_autosuggest_bind_widgets() {
# Given the name of an original widget and args, invoke it, if it exists
_zsh_autosuggest_invoke_original_widget() {
# Do nothing unless called with at least one arg
(( $# )) || return 0
[ $# -gt 0 ] || return
local original_widget_name="$1"
shift
if (( ${+widgets[$original_widget_name]} )); then
if [ $widgets[$original_widget_name] ]; then
zle $original_widget_name -- $@
fi
}
@@ -288,7 +199,7 @@ _zsh_autosuggest_invoke_original_widget() {
_zsh_autosuggest_highlight_reset() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if [[ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]]; then
if [ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]; then
region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}")
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi
@@ -298,8 +209,8 @@ _zsh_autosuggest_highlight_reset() {
_zsh_autosuggest_highlight_apply() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if (( $#POSTDISPLAY )); then
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE"
if [ $#POSTDISPLAY -gt 0 ]; then
_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE"
region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT")
else
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
@@ -310,30 +221,6 @@ _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 )); 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
@@ -346,96 +233,44 @@ _zsh_autosuggest_clear() {
_zsh_autosuggest_modify() {
local -i retval
# Only available in zsh >= 5.4
local -i KEYS_QUEUED_COUNT
# Save the contents of the buffer/postdisplay
local orig_buffer="$BUFFER"
local orig_postdisplay="$POSTDISPLAY"
# Clear suggestion while waiting for next one
# Clear suggestion while original widget runs
unset POSTDISPLAY
# Original widget may modify the buffer
# Original widget modifies the buffer
_zsh_autosuggest_invoke_original_widget $@
retval=$?
emulate -L zsh
# Don't fetch a new suggestion if there's more input to be read immediately
if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
POSTDISPLAY="$orig_postdisplay"
return $retval
fi
# Optimize if manually typing in the suggestion
if (( $#BUFFER > $#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
# Only fetch suggestions at the first level of widget recursion
if [ -z "${funcstack[(rn:2:)_zsh_autosuggest_widget_*]}" ]; then
# Get a new suggestion if the buffer is not empty after modification
local suggestion
if [ $#BUFFER -gt 0 ]; then
suggestion="$(_zsh_autosuggest_suggestion "$BUFFER")"
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
if (( $#BUFFER > 0 )); then
if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then
_zsh_autosuggest_fetch
# Add the suggestion to the POSTDISPLAY
if [ -n "$suggestion" ]; then
POSTDISPLAY="${suggestion#$BUFFER}"
else
unset POSTDISPLAY
fi
fi
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_fetch_suggestion "$BUFFER"
_zsh_autosuggest_suggest "$suggestion"
fi
}
# Offer a suggestion
_zsh_autosuggest_suggest() {
emulate -L zsh
local suggestion="$1"
if [[ -n "$suggestion" ]] && (( $#BUFFER )); then
POSTDISPLAY="${suggestion#$BUFFER}"
else
unset POSTDISPLAY
fi
}
# Accept the entire suggestion
_zsh_autosuggest_accept() {
local -i max_cursor_pos=$#BUFFER
# When vicmd keymap is active, the cursor can't move all the way
# to the end of the buffer
if [[ "$KEYMAP" = "vicmd" ]]; then
if [ "$KEYMAP" = "vicmd" ]; then
max_cursor_pos=$((max_cursor_pos - 1))
fi
# Only accept if the cursor is at the end of the buffer
if [[ $CURSOR = $max_cursor_pos ]]; then
if [ $CURSOR -eq $max_cursor_pos ]; then
# Add the suggestion to the buffer
BUFFER="$BUFFER$POSTDISPLAY"
@@ -464,7 +299,7 @@ _zsh_autosuggest_execute() {
# Partially accept the suggestion
_zsh_autosuggest_partial_accept() {
local -i retval cursor_loc
local -i retval
# Save the contents of the buffer so we can restore later if needed
local original_buffer="$BUFFER"
@@ -476,19 +311,13 @@ _zsh_autosuggest_partial_accept() {
_zsh_autosuggest_invoke_original_widget $@
retval=$?
# Normalize cursor location across vi/emacs modes
cursor_loc=$CURSOR
if [[ "$KEYMAP" = "vicmd" ]]; then
cursor_loc=$((cursor_loc + 1))
fi
# If we've moved past the end of the original buffer
if (( $cursor_loc > $#original_buffer )); then
if [ $CURSOR -gt $#original_buffer ]; then
# Set POSTDISPLAY to text right of the cursor
POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}"
POSTDISPLAY="$RBUFFER"
# Clip the buffer at the cursor
BUFFER="${BUFFER[1,$cursor_loc]}"
BUFFER="$LBUFFER"
else
# Restore the original buffer
BUFFER="$original_buffer"
@@ -497,58 +326,62 @@ _zsh_autosuggest_partial_accept() {
return $retval
}
() {
local action
for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
eval "_zsh_autosuggest_widget_$action() {
local -i retval
for action in clear modify accept partial_accept execute; do
eval "_zsh_autosuggest_widget_$action() {
local -i retval
_zsh_autosuggest_highlight_reset
_zsh_autosuggest_highlight_reset
_zsh_autosuggest_$action \$@
retval=\$?
_zsh_autosuggest_$action \$@
retval=\$?
_zsh_autosuggest_highlight_apply
_zsh_autosuggest_highlight_apply
zle -R
return \$retval
}"
done
return \$retval
}"
done
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-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
#--------------------------------------------------------------------#
# 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}"
}
#--------------------------------------------------------------------#
# History Suggestion Strategy #
# Default Suggestion Strategy #
#--------------------------------------------------------------------#
# Suggests the most recent history item that matches the given
# prefix.
#
_zsh_autosuggest_strategy_history() {
# Reset options to defaults and enable LOCAL_OPTIONS
emulate -L zsh
_zsh_autosuggest_strategy_default() {
local prefix="$1"
# Enable globbing flags so that we can use (#m)
setopt EXTENDED_GLOB
# Get the keys of the history items that match
local -a histkeys
histkeys=(${(k)history[(r)$prefix*]})
# Escape backslashes and all of the glob operators so we can use
# this string as a pattern to search the $history associative array.
# - (#m) globbing flag enables setting references for match data
# TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
# Get the history items that match
# - (r) subscript flag makes the pattern match on values
typeset -g suggestion="${history[(r)${prefix}*]}"
# Echo the value of the first key
echo -E "${history[$histkeys[1]]}"
}
#--------------------------------------------------------------------#
@@ -568,19 +401,9 @@ _zsh_autosuggest_strategy_history() {
# will be 'ls foo' rather than 'ls bar' because your most recently
# executed command (pwd) was previously followed by 'ls foo'.
#
# Note that this strategy won't work as expected with ZSH options that don't
# preserve the history order such as `HIST_IGNORE_ALL_DUPS` or
# `HIST_EXPIRE_DUPS_FIRST`.
_zsh_autosuggest_strategy_match_prev_cmd() {
# Reset options to defaults and enable LOCAL_OPTIONS
emulate -L zsh
# Enable globbing flags so that we can use (#m)
setopt EXTENDED_GLOB
# TODO: Use (b) flag when we can drop support for zsh older than v5.0.8
local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
local prefix="$1"
# Get all history event numbers that correspond to history
# entries that match pattern $prefix*
@@ -606,141 +429,8 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
fi
done
# Give back the matched history entry
typeset -g suggestion="$history[$histkey]"
}
#--------------------------------------------------------------------#
# Fetch Suggestion #
#--------------------------------------------------------------------#
# Loops through all specified strategies and returns a suggestion
# from the first strategy to provide one.
#
_zsh_autosuggest_fetch_suggestion() {
typeset -g suggestion
local -a strategies
local strategy
# Ensure we are working with an array
strategies=(${=ZSH_AUTOSUGGEST_STRATEGY})
for strategy in $strategies; do
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Break once we've found a suggestion
[[ -n "$suggestion" ]] && break
done
}
#--------------------------------------------------------------------#
# Async #
#--------------------------------------------------------------------#
# Zpty process is spawned running this function
_zsh_autosuggest_async_server() {
emulate -R zsh
# There is a bug in zpty module (fixed in zsh/master) by which a
# zpty that exits will kill all zpty processes that were forked
# before it. Here we set up a zsh exit hook to SIGKILL the zpty
# process immediately, before it has a chance to kill any other
# zpty processes.
zshexit() {
kill -KILL $$
sleep 1 # Block for long enough for the signal to come through
}
# Don't add any extra carriage returns
stty -onlcr
# Don't translate carriage returns to newlines
stty -icrnl
# Silence any error messages
exec 2>/dev/null
local last_pid
while IFS='' read -r -d $'\0' query; do
# Kill last bg process
kill -KILL $last_pid &>/dev/null
# Run suggestion search in the background
(
local suggestion
_zsh_autosuggest_fetch_suggestion "$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 )); then
integer -l zptyfd
exec {zptyfd}>&1 # Open a new file descriptor (above 10).
exec {zptyfd}>&- # Close it so it's free to be used by zpty.
fi
# Fork a zpty process running the server function
zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME _zsh_autosuggest_async_server
# Store the fd so we can remove the handler later
if (( REPLY )); then
_ZSH_AUTOSUGGEST_PTY_FD=$REPLY
else
_ZSH_AUTOSUGGEST_PTY_FD=$zptyfd
fi
# Set up input handler from the zpty
zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response
}
_zsh_autosuggest_async_pty_destroy() {
# Remove the input handler
zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
# Destroy the zpty
zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
}
_zsh_autosuggest_async_pty_recreate() {
_zsh_autosuggest_async_pty_destroy
_zsh_autosuggest_async_pty_create
}
_zsh_autosuggest_async_start() {
typeset -g _ZSH_AUTOSUGGEST_PTY_FD
_zsh_autosuggest_feature_detect_zpty_returns_fd
_zsh_autosuggest_async_pty_recreate
# We recreate the pty to get a fresh list of history events
add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate
# Echo the matched history entry
echo -E "$history[$histkey]"
}
#--------------------------------------------------------------------#
@@ -749,20 +439,9 @@ _zsh_autosuggest_async_start() {
# Start the autosuggestion widgets
_zsh_autosuggest_start() {
add-zsh-hook -d precmd _zsh_autosuggest_start
_zsh_autosuggest_check_deprecated_config
_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
}
# Start the autosuggestion widgets on the next precmd
autoload -Uz add-zsh-hook
add-zsh-hook precmd _zsh_autosuggest_start