Compare commits

...

67 Commits

Author SHA1 Message Date
Eric Freese
a7f0106b31 Merge pull request #393 from zsh-users/releases/v0.5.0
Releases/v0.5.0
2018-11-24 10:00:59 -07:00
Eric Freese
286656635c v0.5.0 2018-11-24 09:56:52 -07:00
Eric Freese
0d994d4732 Update changelog for v0.5.0 release 2018-11-24 09:56:52 -07:00
Eric Nielsen
9e4d3c337f Don't overwrite config with default values
otherwise users are obliged to set the config values *after* sourcing
the plugin. They're not able to do it before. Also, re-sourcing the
plugin will reset the values to the defaults again.

See zimfw/zimfw#301

Fixes #335
2018-11-24 09:56:52 -07:00
Eric Freese
f0f1332e6b Add after_sourcing hook for tests
Is executed immediately after sourcing the plugin
2018-11-24 09:56:52 -07:00
Eric Freese
a78ea16c50 Support fallback strategies by setting array in config 2018-11-24 09:56:30 -07:00
Eric Freese
62f5f14f2f default => history 2018-11-24 09:56:00 -07:00
Eric Freese
4540304fa0 Reset opts in some functions affected by GLOB_SUBST
Should fix GitHub #334
2018-11-19 15:15:48 -07:00
Eric Freese
df6f6f9ff4 Add install directions for Antigen 2018-11-19 15:15:48 -07:00
Eric Freese
4097d6e0ae Support widgets starting with dashes (ex: -a-widget)
Fixes #337
2018-11-19 15:15:48 -07:00
Eric Freese
822a1f30e0 Async is less reliable in zsh versions < 5.0.8
`stty` occasionally hangs (always in CircleCI) inside the async pty.

Disable the tests for now until we can figure out and fix/workaround
this issue.
2018-11-19 15:15:48 -07:00
Eric Freese
0f80f9511d Fix handling of newline + carriage return in async pty (#333) 2018-11-19 15:15:48 -07:00
Eric Freese
be6b22b864 Add info to the readme about docker testing image 2018-11-18 21:35:52 -07:00
Eric Freese
69d7fa14d3 Build docker image for running tests
Published at `ericfreese/zsh-autosuggestions-test`.

Contains:
- Ruby 2.5.3
- Supported zsh versions 4.3.11 through 5.6.2
- Test ruby dependencies

Also updates Circle CI config to use built docker image.
2018-11-18 21:11:35 -07:00
Eric Freese
affe7c687e Run circle-ci upgrade script
From: https://raw.githubusercontent.com/CircleCI-Public/circleci-config-generator/9979b712033a072c3135aafb3c06e252c4e791f1/cci-config-generator.sh
2018-11-10 13:11:25 -07:00
Eric Freese
ebaf409002 Merge pull request #355 from amehmeto/patch-1
Update INSTALL.md
2018-07-02 20:59:14 -06:00
amehmeto
2d0ae6044c Update INSTALL.md
I lost myself in the internet abyss because of this imprecision. It might be helpful for the new noob trying to install that great tweak. :)
2018-07-03 04:25:20 +02:00
Eric Freese
d7c796719e Merge pull request #332 from zsh-users/develop
v0.4.3
2018-05-21 11:40:10 -06:00
Eric Freese
aa0b10db44 v0.4.3 2018-05-21 11:38:41 -06:00
Eric Freese
72ccee33b4 Pull out separate doc for installation instructions 2018-05-21 11:35:26 -06:00
Eric Freese
c113e49fe2 Update license copyright year 2018-05-21 10:52:41 -06:00
Eric Freese
3b39b9561c Merge branch 'master' into develop 2018-05-21 10:47:20 -06:00
Eric Freese
b003b2238a Update changelog for v0.4.3 release 2018-05-21 10:46:03 -06:00
Eric Freese
df5fb858aa Destroy old pty even if it's no longer running (#249)
For unknown reasons, the pty will occasionally quit running. In these
cases, we still want to remove it so that a fresh one can be created. We
don't actually need this check because error messages from `zle` and
`zpty` are redirected to /dev/null.

One sure way to kill all currently running pty's is to run `exit` in a
subshell. Even without zsh-autosuggestions loaded, the following works:

    % zmodload zsh/zpty
    % zpty -b foo cat
    % zpty -b bar cat
    % zpty
    (31689) bar: cat
    (31666) foo: cat
    % $(exit)
    % zpty
    (finished) bar: cat
    (finished) foo: cat
2018-05-15 13:55:37 -06:00
Eric Freese
726bc4eb5c Create general spec for async behavior 2018-05-15 13:42:18 -06:00
Harm te Hennepe
59c72c6805 Don't break kill ring rotation 2018-05-15 12:54:09 -06:00
Eric Freese
393f7b8bb9 Fix vi-mode partial-accept
Issue #188. PR #324.

Thanks to @toadjaune and @IngoHeimbach.
2018-05-15 11:44:42 -06:00
Eric Freese
42f5a06f7f Need to reset the POSTDISPLAY if exiting early
Specific case where this matters is following:

Be in vi insert mode with some text in the buffer and the cursor at the
end of the buffer. Press `esc` to trigger `vi-cmd-mode widget`, then
before the cursor moves (KEYTIMEOUT), press `h` to trigger
`vi-backward-char` widget. When `vi-cmd-mode` original widget exits,
KEYS_QUEUED_COUNT will be non-zero and the suggestion will be lost.
2018-05-15 11:44:42 -06:00
Eric Freese
51fef255da Add method to connect terminal to tmux session during tests
Useful with `binding.pry` to inspect behavior of tests.
2018-05-15 11:44:18 -06:00
Eric Freese
19ad3ba7cd Add new 5.5.1 version of zsh to CI 2018-05-15 11:44:18 -06:00
Benjamin Denhartog
b2b9bf3b8c update arch linux installation instructions (now available via pacman)
closes #328
2018-05-14 10:43:45 -06:00
Eric Freese
67a364bc17 Merge pull request #321 from babaorum/fix/documentation/oh-my-zsh-install
make Oh my zsh install works without ZSH_CUSTOM defined
2018-04-17 15:00:46 -06:00
babaorum
afc14f79cc make Oh my zsh install works without ZSH_CUSTOM defined 2018-04-16 22:37:54 +02:00
Eric Freese
60aff2d944 Remove unused local $suggestion variable 2018-03-27 14:51:37 -06:00
Eric Freese
6dfe9c8cd8 Merge pull request #319 from zsh-users/fixes/async_history
Don't fetch suggestions after [up,down]-line-or-beginning-search
2018-03-23 16:15:56 -06:00
Eric Freese
3136700ccf Don't fetch suggestions after [up,down]-line-or-beginning-search
These widgets rely on `$LASTWIDGET` being set to restore the cursor
position. When asynchronous suggestions are enabled, and the widget
triggers a suggestion to be fetched, `autosuggest-suggest` will be
called and $LASTWIDGET will be set to it.
2018-03-23 16:08:11 -06:00
Eric Freese
2202ed7bac Merge pull request #304 from okdana/dana/no-beep
Avoid ringing bell when accepting suggestions
2018-01-16 14:11:06 -07:00
dana
c978004c0e ..._invoke_original_widget: Return 0 when given no arguments
`_zsh_autosuggest_widget_accept()` (&al.) passes this function's return status
on, and ZLE rings the bell if it's >0. Not having an original widget isn't an
error condition, so always returning 0 here should be OK to avoid the bell

Fixes #228
2018-01-16 14:10:29 -06:00
Eric Freese
c7d4a85031 Merge pull request #299 from zsh-users/develop
v0.4.2
2017-12-06 08:31:52 -07:00
Eric Freese
15931f04ff v0.4.2 2017-12-06 08:30:12 -07:00
Eric Freese
9f1046727a Merge pull request #298 from zsh-users/fixes/support_older_versions
Fixes/support older versions
2017-12-06 08:27:18 -07:00
Eric Freese
f462410b3c Add zsh version requirements to readme 2017-12-06 08:21:27 -07:00
Eric Freese
4ea825faf8 Fix #247 and #248 without using (b) flag
To support older versions of zsh (< 5.0.8).

We were missing the EXTENDED_GLOB option that allows use of `(#m)` flag.
2017-12-06 08:09:14 -07:00
Eric Freese
a1babef972 Revert "Simplify escaping of pattern and fix match_prev_cmd strategy"
This reverts commit 7f8ff2867c.
2017-12-06 08:08:53 -07:00
Eric Freese
be8bba6f38 Run CI on prominent versions of zsh back to 4.3.11
RHEL6 bundles v4.3.11
Ubuntu 14.04 and Amazon Linux bundle v5.0.2
2017-12-06 07:45:45 -07:00
Eric Freese
dda220f140 Merge pull request #295 from zsh-users/develop
v0.4.1
2017-11-28 10:14:41 -07:00
Eric Freese
9f9237ab8a v0.4.1 2017-11-28 10:07:49 -07:00
Eric Freese
29257230fe Add missing issue/pr numbers from last version 2017-11-28 10:07:49 -07:00
Kaleb Elwert
940e10a691 Fix conditionals to use [[ and (( rather than [
This fixes a small issue in src/widgets.zsh which makes it so if you
alias [ to g[ (as is done in prezto if the gnu-utility module is loaded)
autosuggestions would fail.

The documentation for GNU test mentions that -o and -a should be avoided
if possible because it's not very clear. Also, with zsh and [[ -o
actually tests if an option is set, which makes this option even more
confusing.
2017-11-27 08:31:41 -07:00
Eric Freese
9f1f322979 Update comment about KEYS_QUEUED_COUNT support
Now that patch has released
2017-09-27 15:04:42 -06:00
Eric Freese
680ce21f26 Merge pull request #275 from zsh-users/fixes/warn_nested_var_opt
Use typeset -g to avoid warnnestedvar warnings
2017-09-27 15:02:15 -06:00
Eric Freese
256293cbb6 Use typeset -g to avoid warnnestedvar warnings
Fixes github issue #271
2017-09-27 13:24:06 -06:00
Eric Freese
977e70e21b Merge pull request #270 from ssiegel/fix-match_prev_cmd
Simplify escaping of pattern and fix match_prev_cmd strategy
2017-09-26 08:33:14 -06:00
Eric Freese
218acf2fbe Merge branch 'fixes/match_prev_cmd_special_chars' into develop 2017-09-26 08:30:43 -06:00
Eric Freese
0681a1c121 Remove flaky test that doesn't really matter that much
Seems like this would happen on some machines but not on others. Not
sure exactly what's going on, but this is such an edge case I'm just
going to remove the test.
2017-09-26 08:23:00 -06:00
Eric Freese
9e3f1bd359 Use https protocol instead of git in README instructions
https should work more broadly for folks
2017-09-26 07:56:29 -06:00
Eric Freese
ae4c344e82 Merge pull request #267 from isaacwhanson/fixes/match_prev_cmd_special_chars
escape pattern-matching chars on $prefix for match_prev_cmd strategy
2017-09-12 09:19:17 -06:00
Stefan Siegel
7f8ff2867c Simplify escaping of pattern and fix match_prev_cmd strategy
Maybe this is also a fix for #247, #248 and #258. Supersedes #267.

Testcase:
Using match_prev_cmd strategy and with these lines in history:
echo '1^'
echo '2^'
echo '1^'

type:
echo       (unexpected suggestion echo '1^' instead of echo '2^')
echo '1^1  (wrong suggestion echo '1^1echo '1^')
echo '1^#  (error "bad math expression")
2017-09-10 04:35:19 +02:00
Eric Freese
33b91a9dea Merge pull request #268 from thomas-mcdonald/patch-1
replace tabs in Rubocop config with spaces
2017-08-22 19:02:15 -06:00
Thomas McDonald
802f53a10a replace tabs in Rubocop config with spaces
YAML does not permit tabs and many parsers will error when loading a tab-indented file - http://www.yaml.org/faq.html
2017-08-20 23:36:18 +01:00
Isaac W Hanson
5e4487ed4a escape pattern-matching chars on $prefix for match_prev_cmd strategy 2017-08-18 16:00:42 -04:00
Eric Freese
9e110406fa Add test for special characters with match_prev_cmd strategy
Github #247 and #258
2017-08-18 11:18:07 -06:00
Alexander Neumann
1915e28882 Add 'emacs-forward-word'
This commit adds the 'emacs-forward-word' widget to the list of partial
accept widgets.
2017-08-18 11:14:21 -06:00
Eric Freese
d6d9a46981 Merge pull request #261 from alonhar/patch-1
Update README.md
2017-07-30 09:30:48 +02:00
Alon Harel
706499838c Update README.md 2017-07-24 21:19:07 +03:00
Eric Freese
e304365745 Merge pull request #256 from sumnerevans/patch-1
Added installation instructions for AUR, Homebrew
2017-07-08 09:58:35 +02:00
Sumner Evans
4e72f7a91e Added installation instructions for AUR, Homebrew 2017-07-07 09:17:46 -06:00
36 changed files with 768 additions and 275 deletions

16
.circleci/config.yml Normal file
View File

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

@@ -1 +1 @@
2.3.1 2.5.3

View File

@@ -1,14 +1,44 @@
# Changelog # 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 ## v0.4.0
- High-level integration tests using RSpec and tmux - High-level integration tests using RSpec and tmux
- Add continuous integration with Circle CI - Add continuous integration with Circle CI
- Experimental support for asynchronous suggestions (#) - Experimental support for asynchronous suggestions (#170)
- Fix problems with multi-line suggestions (#) - Fix problems with multi-line suggestions (#225)
- Optimize case where manually typing in suggestion - Optimize case where manually typing in suggestion
- Avoid wrapping any zle-* widgets (#) - Avoid wrapping any zle-* widgets (#206)
- Remove support for deprecated options from v0.0.x - Remove support for deprecated options from v0.0.x
- Handle history entries that begin with dashes (#) - Handle history entries that begin with dashes
- Gracefully handle being sourced multiple times (#126) - Gracefully handle being sourced multiple times (#126)
- Add enable/disable/toggle widgets to disable/enable suggestions (#219) - Add enable/disable/toggle widgets to disable/enable suggestions (#219)

20
Dockerfile Normal file
View File

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

77
INSTALL.md Normal file
View File

@@ -0,0 +1,77 @@
## 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) 2013 Thiago de Arruda
Copyright (c) 2016-2017 Eric Freese Copyright (c) 2016-2018 Eric Freese
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View File

@@ -9,6 +9,7 @@ SRC_FILES := \
$(SRC_DIR)/highlight.zsh \ $(SRC_DIR)/highlight.zsh \
$(SRC_DIR)/widgets.zsh \ $(SRC_DIR)/widgets.zsh \
$(SRC_DIR)/strategies/*.zsh \ $(SRC_DIR)/strategies/*.zsh \
$(SRC_DIR)/fetch.zsh \
$(SRC_DIR)/async.zsh \ $(SRC_DIR)/async.zsh \
$(SRC_DIR)/start.zsh $(SRC_DIR)/start.zsh

View File

@@ -4,6 +4,8 @@ _[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._
It suggests commands as you type, based on command history. 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) [![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> <a href="https://asciinema.org/a/37390" target="_blank"><img src="https://asciinema.org/a/37390.png" width="400" /></a>
@@ -11,38 +13,7 @@ It suggests commands as you type, based on command history.
## Installation ## Installation
### Manual See [INSTALL.md](INSTALL.md).
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 ## Usage
@@ -56,7 +27,7 @@ If you invoke the `forward-word` widget, it will partially accept the suggestion
## Configuration ## Configuration
You may want to override the default global config variables after sourcing the plugin. Default values of these variables can be found [here](src/config.zsh). You may want to override the default global config variables. Default values of these variables can be found [here](src/config.zsh).
**Note:** If you are using Oh My Zsh, you can put this configuration in a file in the `$ZSH_CUSTOM` directory. See their comments on [overriding internals](https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals). **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).
@@ -68,10 +39,10 @@ Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion
### Suggestion Strategy ### Suggestion Strategy
Set `ZSH_AUTOSUGGEST_STRATEGY` to choose the strategy for generating suggestions. There are currently two to choose from: `ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. The strategies in the array are tried successively until a suggestion is found. There are currently two built-in strategies to choose from:
- `default`: Chooses the most recent match. - `history`: Chooses the most recent match from history.
- `match_prev_cmd`: Chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`. - `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`.
### Widget Mapping ### Widget Mapping
@@ -168,6 +139,20 @@ Tests are written in ruby using the [`rspec`](http://rspec.info/) framework. The
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`). 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
```
## License ## License

View File

@@ -1 +1 @@
v0.4.0 v0.5.0

15
ZSH_VERSIONS Normal file
View File

@@ -0,0 +1,15 @@
# 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,12 +0,0 @@
machine:
environment:
ZSH_VERSIONS: 5.0.8 5.1.1 5.2 5.3.1
dependencies:
pre:
- for v in $(echo $ZSH_VERSIONS | awk "{ for (i=$((1+CIRCLE_NODE_INDEX));i<=NF;i+=$CIRCLE_NODE_TOTAL) print \$i }"); do wget https://sourceforge.net/projects/zsh/files/zsh/$v/zsh-$v.tar.gz && tar xzf zsh-$v.tar.gz && cd zsh-$v && ./configure && sudo make install || exit 1; done
test:
override:
- for v in $(echo $ZSH_VERSIONS | awk "{ for (i=$((1+CIRCLE_NODE_INDEX));i<=NF;i+=$CIRCLE_NODE_TOTAL) print \$i }"); do TEST_ZSH_BIN=/usr/local/bin/zsh-$v make test || exit 1; done:
parallel: true

26
install_test_zsh.sh Executable file
View File

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

84
spec/async_spec.rb Normal file
View File

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

@@ -23,14 +23,5 @@ describe 'pasting using bracketed-paste-magic' do
expect(session.content).to eq("echo #{'a' * 60}") expect(session.content).to eq("echo #{'a' * 60}")
end end
end end
it 'shows a suggestion after a non-modifying keystroke' do
with_history('echo hello') do
session.paste_string('echo')
sleep 1
session.send_keys('left')
wait_for { session.content }.to eq('echo hello')
end
end
end end
end end

View File

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

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

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

23
spec/kill_ring_spec.rb Normal file
View File

@@ -0,0 +1,23 @@
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,20 +1,55 @@
describe 'a suggestion for a given prefix' do describe 'a suggestion for a given prefix' do
let(:options) { ['_zsh_autosuggest_strategy_default() { suggestion="echo foo" }'] } let(:history_strategy) { '_zsh_autosuggest_strategy_history() { suggestion="history" }' }
let(:foobar_strategy) { '_zsh_autosuggest_strategy_foobar() { [[ "foobar baz" = $1* ]] && suggestion="foobar baz" }' }
let(:foobaz_strategy) { '_zsh_autosuggest_strategy_foobaz() { [[ "foobaz bar" = $1* ]] && suggestion="foobaz bar" }' }
it 'is determined by calling the default strategy function' do let(:after_sourcing) do
session.send_string('e') -> do
wait_for { session.content }.to eq('echo foo') session.run_command(history_strategy)
end
end end
context 'when ZSH_AUTOSUGGEST_STRATEGY is set' do it 'by default is determined by calling the `history` strategy function' do
let(:options) { [ session.send_string('h')
'_zsh_autosuggest_strategy_custom() { suggestion="echo foo" }', wait_for { session.content }.to eq('history')
'ZSH_AUTOSUGGEST_STRATEGY=custom' end
] }
it 'is determined by calling the specified strategy function' do context 'when ZSH_AUTOSUGGEST_STRATEGY is set to an array' do
session.send_string('e') let(:after_sourcing) do
wait_for { session.content }.to eq('echo foo') -> 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 end
end end

View File

@@ -6,12 +6,14 @@ RSpec.shared_context 'terminal session' do
let(:term_opts) { {} } let(:term_opts) { {} }
let(:session) { TerminalSession.new(term_opts) } let(:session) { TerminalSession.new(term_opts) }
let(:before_sourcing) { -> {} } let(:before_sourcing) { -> {} }
let(:after_sourcing) { -> {} }
let(:options) { [] } let(:options) { [] }
around do |example| around do |example|
before_sourcing.call before_sourcing.call
session.run_command(options.join('; '))
session.run_command((['source zsh-autosuggestions.zsh'] + options).join('; ')) session.run_command('source zsh-autosuggestions.zsh')
after_sourcing.call
session.clear_screen session.clear_screen
example.run example.run

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
require 'strategies/special_characters_helper'
describe 'the match_prev_cmd strategy' do describe 'the match_prev_cmd strategy' do
let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd'] } let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd'] }
@@ -14,4 +16,6 @@ describe 'the match_prev_cmd strategy' do
wait_for { session.content }.to eq('ls bar') wait_for { session.content }.to eq('ls bar')
end end
end end
include_examples 'special characters'
end end

View File

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

@@ -18,6 +18,10 @@ class TerminalSession
tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'") tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'")
end end
def zsh_version
@zsh_version ||= Gem::Version.new(`#{ZSH_BIN} -c 'echo -n $ZSH_VERSION'`)
end
def tmux_socket_name def tmux_socket_name
@tmux_socket_name ||= SecureRandom.hex(6) @tmux_socket_name ||= SecureRandom.hex(6)
end end
@@ -77,6 +81,10 @@ class TerminalSession
map(&:to_i) map(&:to_i)
end end
def attach!
tmux_command('attach-session')
end
private private
attr_reader :opts attr_reader :opts

View File

@@ -17,13 +17,15 @@ _zsh_autosuggest_async_server() {
sleep 1 # Block for long enough for the signal to come through sleep 1 # Block for long enough for the signal to come through
} }
# Output only newlines (not carriage return + newline) # Don't add any extra carriage returns
stty -onlcr stty -onlcr
# Don't translate carriage returns to newlines
stty -icrnl
# Silence any error messages # Silence any error messages
exec 2>/dev/null exec 2>/dev/null
local strategy=$1
local last_pid local last_pid
while IFS='' read -r -d $'\0' query; do while IFS='' read -r -d $'\0' query; do
@@ -33,7 +35,7 @@ _zsh_autosuggest_async_server() {
# Run suggestion search in the background # Run suggestion search in the background
( (
local suggestion local suggestion
_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query" _zsh_autosuggest_fetch_suggestion "$query"
echo -n -E "$suggestion"$'\0' echo -n -E "$suggestion"$'\0'
) & ) &
@@ -63,14 +65,14 @@ _zsh_autosuggest_async_pty_create() {
typeset -h REPLY typeset -h REPLY
# If we won't get a fd back from zpty, try to guess it # If we won't get a fd back from zpty, try to guess it
if [ $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD -eq 0 ]; then if (( ! $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD )); then
integer -l zptyfd integer -l zptyfd
exec {zptyfd}>&1 # Open a new file descriptor (above 10). exec {zptyfd}>&1 # Open a new file descriptor (above 10).
exec {zptyfd}>&- # Close it so it's free to be used by zpty. exec {zptyfd}>&- # Close it so it's free to be used by zpty.
fi fi
# Fork a zpty process running the server function # Fork a zpty process running the server function
zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "_zsh_autosuggest_async_server _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME _zsh_autosuggest_async_server
# Store the fd so we can remove the handler later # Store the fd so we can remove the handler later
if (( REPLY )); then if (( REPLY )); then
@@ -84,13 +86,11 @@ _zsh_autosuggest_async_pty_create() {
} }
_zsh_autosuggest_async_pty_destroy() { _zsh_autosuggest_async_pty_destroy() {
if zpty -t $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null; then
# Remove the input handler # Remove the input handler
zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
# Destroy the zpty # Destroy the zpty
zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
fi
} }
_zsh_autosuggest_async_pty_recreate() { _zsh_autosuggest_async_pty_recreate() {

View File

@@ -10,14 +10,14 @@ _zsh_autosuggest_incr_bind_count() {
_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=1 _ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=1
fi fi
bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1] typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
} }
_zsh_autosuggest_get_bind_count() { _zsh_autosuggest_get_bind_count() {
if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1] typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
else else
bind_count=0 typeset -gi bind_count=0
fi fi
} }
@@ -69,11 +69,13 @@ _zsh_autosuggest_bind_widget() {
}" }"
# Create the bound widget # Create the bound widget
zle -N $widget _zsh_autosuggest_bound_${bind_count}_$widget zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget
} }
# Map all configured widgets to the right autosuggest widgets # Map all configured widgets to the right autosuggest widgets
_zsh_autosuggest_bind_widgets() { _zsh_autosuggest_bind_widgets() {
emulate -L zsh
local widget local widget
local ignore_widgets local ignore_widgets
@@ -88,13 +90,13 @@ _zsh_autosuggest_bind_widgets() {
# Find every widget we might want to bind and bind it appropriately # Find every widget we might want to bind and bind it appropriately
for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do
if [ ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]; then if [[ -n ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget clear _zsh_autosuggest_bind_widget $widget clear
elif [ ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]; then elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget accept _zsh_autosuggest_bind_widget $widget accept
elif [ ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]; then elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget execute _zsh_autosuggest_bind_widget $widget execute
elif [ ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]; then elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget partial_accept _zsh_autosuggest_bind_widget $widget partial_accept
else else
# Assume any unspecified widget might modify the buffer # Assume any unspecified widget might modify the buffer
@@ -106,13 +108,13 @@ _zsh_autosuggest_bind_widgets() {
# Given the name of an original widget and args, invoke it, if it exists # Given the name of an original widget and args, invoke it, if it exists
_zsh_autosuggest_invoke_original_widget() { _zsh_autosuggest_invoke_original_widget() {
# Do nothing unless called with at least one arg # Do nothing unless called with at least one arg
[ $# -gt 0 ] || return (( $# )) || return 0
local original_widget_name="$1" local original_widget_name="$1"
shift shift
if [ $widgets[$original_widget_name] ]; then if (( ${+widgets[$original_widget_name]} )); then
zle $original_widget_name -- $@ zle $original_widget_name -- $@
fi fi
} }

View File

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

23
src/fetch.zsh Normal file
View File

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

View File

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

View File

@@ -15,7 +15,7 @@ _zsh_autosuggest_start() {
# to the widget list variables to take effect on the next precmd. # to the widget list variables to take effect on the next precmd.
add-zsh-hook precmd _zsh_autosuggest_bind_widgets add-zsh-hook precmd _zsh_autosuggest_bind_widgets
if [ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]; then if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
_zsh_autosuggest_async_start _zsh_autosuggest_async_start
fi fi
} }

View File

@@ -1,12 +1,12 @@
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
# Default Suggestion Strategy # # History Suggestion Strategy #
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
# Suggests the most recent history item that matches the given # Suggests the most recent history item that matches the given
# prefix. # prefix.
# #
_zsh_autosuggest_strategy_default() { _zsh_autosuggest_strategy_history() {
# Reset options to defaults and enable LOCAL_OPTIONS # Reset options to defaults and enable LOCAL_OPTIONS
emulate -L zsh emulate -L zsh
@@ -16,10 +16,10 @@ _zsh_autosuggest_strategy_default() {
# Escape backslashes and all of the glob operators so we can use # Escape backslashes and all of the glob operators so we can use
# this string as a pattern to search the $history associative array. # this string as a pattern to search the $history associative array.
# - (#m) globbing flag enables setting references for match data # - (#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}" local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
# Get the history items that match # Get the history items that match
# - (r) subscript flag makes the pattern match on values # - (r) subscript flag makes the pattern match on values
suggestion="${history[(r)$prefix*]}" typeset -g suggestion="${history[(r)${prefix}*]}"
} }

View File

@@ -21,7 +21,14 @@
# `HIST_EXPIRE_DUPS_FIRST`. # `HIST_EXPIRE_DUPS_FIRST`.
_zsh_autosuggest_strategy_match_prev_cmd() { _zsh_autosuggest_strategy_match_prev_cmd() {
local prefix="${1//(#m)[\\()\[\]|*?~]/\\$MATCH}" # 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}"
# Get all history event numbers that correspond to history # Get all history event numbers that correspond to history
# entries that match pattern $prefix* # entries that match pattern $prefix*
@@ -48,5 +55,5 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
done done
# Give back the matched history entry # Give back the matched history entry
suggestion="$history[$histkey]" typeset -g suggestion="$history[$histkey]"
} }

View File

@@ -13,14 +13,14 @@ _zsh_autosuggest_disable() {
_zsh_autosuggest_enable() { _zsh_autosuggest_enable() {
unset _ZSH_AUTOSUGGEST_DISABLED unset _ZSH_AUTOSUGGEST_DISABLED
if [ $#BUFFER -gt 0 ]; then if (( $#BUFFER )); then
_zsh_autosuggest_fetch _zsh_autosuggest_fetch
fi fi
} }
# Toggle suggestions (enable/disable) # Toggle suggestions (enable/disable)
_zsh_autosuggest_toggle() { _zsh_autosuggest_toggle() {
if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then if [[ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]]; then
_zsh_autosuggest_enable _zsh_autosuggest_enable
else else
_zsh_autosuggest_disable _zsh_autosuggest_disable
@@ -37,9 +37,11 @@ _zsh_autosuggest_clear() {
# Modify the buffer and get a new suggestion # Modify the buffer and get a new suggestion
_zsh_autosuggest_modify() { _zsh_autosuggest_modify() {
emulate -L zsh
local -i retval local -i retval
# Only added to zsh very recently # Only available in zsh >= 5.4
local -i KEYS_QUEUED_COUNT local -i KEYS_QUEUED_COUNT
# Save the contents of the buffer/postdisplay # Save the contents of the buffer/postdisplay
@@ -54,35 +56,36 @@ _zsh_autosuggest_modify() {
retval=$? retval=$?
# Don't fetch a new suggestion if there's more input to be read immediately # Don't fetch a new suggestion if there's more input to be read immediately
if [[ $PENDING > 0 ]] || [[ $KEYS_QUEUED_COUNT > 0 ]]; then if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
POSTDISPLAY="$orig_postdisplay"
return $retval return $retval
fi fi
# Optimize if manually typing in the suggestion # Optimize if manually typing in the suggestion
if [ $#BUFFER -gt $#orig_buffer ]; then if (( $#BUFFER > $#orig_buffer )); then
local added=${BUFFER#$orig_buffer} local added=${BUFFER#$orig_buffer}
# If the string added matches the beginning of the postdisplay # If the string added matches the beginning of the postdisplay
if [ "$added" = "${orig_postdisplay:0:$#added}" ]; then if [[ "$added" = "${orig_postdisplay:0:$#added}" ]]; then
POSTDISPLAY="${orig_postdisplay:$#added}" POSTDISPLAY="${orig_postdisplay:$#added}"
return $retval return $retval
fi fi
fi fi
# Don't fetch a new suggestion if the buffer hasn't changed # Don't fetch a new suggestion if the buffer hasn't changed
if [ "$BUFFER" = "$orig_buffer" ]; then if [[ "$BUFFER" = "$orig_buffer" ]]; then
POSTDISPLAY="$orig_postdisplay" POSTDISPLAY="$orig_postdisplay"
return $retval return $retval
fi fi
# Bail out if suggestions are disabled # Bail out if suggestions are disabled
if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then if [[ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]]; then
return $? return $?
fi fi
# Get a new suggestion if the buffer is not empty after modification # Get a new suggestion if the buffer is not empty after modification
if [ $#BUFFER -gt 0 ]; then if (( $#BUFFER > 0 )); then
if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -le "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then
_zsh_autosuggest_fetch _zsh_autosuggest_fetch
fi fi
fi fi
@@ -96,16 +99,18 @@ _zsh_autosuggest_fetch() {
_zsh_autosuggest_async_request "$BUFFER" _zsh_autosuggest_async_request "$BUFFER"
else else
local suggestion local suggestion
_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER" _zsh_autosuggest_fetch_suggestion "$BUFFER"
_zsh_autosuggest_suggest "$suggestion" _zsh_autosuggest_suggest "$suggestion"
fi fi
} }
# Offer a suggestion # Offer a suggestion
_zsh_autosuggest_suggest() { _zsh_autosuggest_suggest() {
emulate -L zsh
local suggestion="$1" local suggestion="$1"
if [ -n "$suggestion" ] && [ $#BUFFER -gt 0 ]; then if [[ -n "$suggestion" ]] && (( $#BUFFER )); then
POSTDISPLAY="${suggestion#$BUFFER}" POSTDISPLAY="${suggestion#$BUFFER}"
else else
unset POSTDISPLAY unset POSTDISPLAY
@@ -118,12 +123,12 @@ _zsh_autosuggest_accept() {
# When vicmd keymap is active, the cursor can't move all the way # When vicmd keymap is active, the cursor can't move all the way
# to the end of the buffer # to the end of the buffer
if [ "$KEYMAP" = "vicmd" ]; then if [[ "$KEYMAP" = "vicmd" ]]; then
max_cursor_pos=$((max_cursor_pos - 1)) max_cursor_pos=$((max_cursor_pos - 1))
fi fi
# Only accept if the cursor is at the end of the buffer # Only accept if the cursor is at the end of the buffer
if [ $CURSOR -eq $max_cursor_pos ]; then if [[ $CURSOR = $max_cursor_pos ]]; then
# Add the suggestion to the buffer # Add the suggestion to the buffer
BUFFER="$BUFFER$POSTDISPLAY" BUFFER="$BUFFER$POSTDISPLAY"
@@ -152,7 +157,7 @@ _zsh_autosuggest_execute() {
# Partially accept the suggestion # Partially accept the suggestion
_zsh_autosuggest_partial_accept() { _zsh_autosuggest_partial_accept() {
local -i retval local -i retval cursor_loc
# Save the contents of the buffer so we can restore later if needed # Save the contents of the buffer so we can restore later if needed
local original_buffer="$BUFFER" local original_buffer="$BUFFER"
@@ -164,13 +169,19 @@ _zsh_autosuggest_partial_accept() {
_zsh_autosuggest_invoke_original_widget $@ _zsh_autosuggest_invoke_original_widget $@
retval=$? 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 we've moved past the end of the original buffer
if [ $CURSOR -gt $#original_buffer ]; then if (( $cursor_loc > $#original_buffer )); then
# Set POSTDISPLAY to text right of the cursor # Set POSTDISPLAY to text right of the cursor
POSTDISPLAY="$RBUFFER" POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}"
# Clip the buffer at the cursor # Clip the buffer at the cursor
BUFFER="$LBUFFER" BUFFER="${BUFFER[1,$cursor_loc]}"
else else
# Restore the original buffer # Restore the original buffer
BUFFER="$original_buffer" BUFFER="$original_buffer"

View File

@@ -1,8 +1,8 @@
# Fish-like fast/unobtrusive autosuggestions for zsh. # Fish-like fast/unobtrusive autosuggestions for zsh.
# https://github.com/zsh-users/zsh-autosuggestions # https://github.com/zsh-users/zsh-autosuggestions
# v0.4.0 # v0.5.0
# Copyright (c) 2013 Thiago de Arruda # Copyright (c) 2013 Thiago de Arruda
# Copyright (c) 2016-2017 Eric Freese # Copyright (c) 2016-2018 Eric Freese
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation # obtaining a copy of this software and associated documentation
@@ -42,28 +42,32 @@ zmodload zsh/zpty
# Color to use when highlighting suggestion # Color to use when highlighting suggestion
# Uses format of `region_highlight` # Uses format of `region_highlight`
# More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets # More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' : ${ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'}
# Prefix to use when saving original versions of bound widgets # Prefix to use when saving original versions of bound widgets
ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- : ${ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-}
ZSH_AUTOSUGGEST_STRATEGY=default # Strategies to use to fetch a suggestion
# Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && ZSH_AUTOSUGGEST_STRATEGY=(history)
# Widgets that clear the suggestion # Widgets that clear the suggestion
ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( (( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward history-search-forward
history-search-backward history-search-backward
history-beginning-search-forward history-beginning-search-forward
history-beginning-search-backward history-beginning-search-backward
history-substring-search-up history-substring-search-up
history-substring-search-down history-substring-search-down
up-line-or-beginning-search
down-line-or-beginning-search
up-line-or-history up-line-or-history
down-line-or-history down-line-or-history
accept-line accept-line
) )
# Widgets that accept the entire suggestion # Widgets that accept the entire suggestion
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=( (( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char forward-char
end-of-line end-of-line
vi-forward-char vi-forward-char
@@ -72,33 +76,37 @@ ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
) )
# Widgets that accept the entire suggestion and execute it # Widgets that accept the entire suggestion and execute it
ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=( (( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
) )
# Widgets that accept the suggestion as far as the cursor moves # Widgets that accept the suggestion as far as the cursor moves
ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=( (( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word forward-word
emacs-forward-word
vi-forward-word vi-forward-word
vi-forward-word-end vi-forward-word-end
vi-forward-blank-word vi-forward-blank-word
vi-forward-blank-word-end 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) # Widgets that should be ignored (globbing supported but must be escaped)
ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( (( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
orig-\* orig-\*
beep beep
run-help run-help
set-local-history set-local-history
which-command which-command
yank yank
yank-pop
) )
# Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. # Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= : ${ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=}
# Pty name for calculating autosuggestions asynchronously # Pty name for calculating autosuggestions asynchronously
ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty : ${ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty}
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
# Utility Functions # # Utility Functions #
@@ -141,14 +149,14 @@ _zsh_autosuggest_incr_bind_count() {
_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=1 _ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=1
fi fi
bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1] typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
} }
_zsh_autosuggest_get_bind_count() { _zsh_autosuggest_get_bind_count() {
if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then
bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1] typeset -gi bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]
else else
bind_count=0 typeset -gi bind_count=0
fi fi
} }
@@ -200,11 +208,13 @@ _zsh_autosuggest_bind_widget() {
}" }"
# Create the bound widget # Create the bound widget
zle -N $widget _zsh_autosuggest_bound_${bind_count}_$widget zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget
} }
# Map all configured widgets to the right autosuggest widgets # Map all configured widgets to the right autosuggest widgets
_zsh_autosuggest_bind_widgets() { _zsh_autosuggest_bind_widgets() {
emulate -L zsh
local widget local widget
local ignore_widgets local ignore_widgets
@@ -219,13 +229,13 @@ _zsh_autosuggest_bind_widgets() {
# Find every widget we might want to bind and bind it appropriately # Find every widget we might want to bind and bind it appropriately
for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do
if [ ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]; then if [[ -n ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget clear _zsh_autosuggest_bind_widget $widget clear
elif [ ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]; then elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget accept _zsh_autosuggest_bind_widget $widget accept
elif [ ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]; then elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget execute _zsh_autosuggest_bind_widget $widget execute
elif [ ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]; then elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget partial_accept _zsh_autosuggest_bind_widget $widget partial_accept
else else
# Assume any unspecified widget might modify the buffer # Assume any unspecified widget might modify the buffer
@@ -237,13 +247,13 @@ _zsh_autosuggest_bind_widgets() {
# Given the name of an original widget and args, invoke it, if it exists # Given the name of an original widget and args, invoke it, if it exists
_zsh_autosuggest_invoke_original_widget() { _zsh_autosuggest_invoke_original_widget() {
# Do nothing unless called with at least one arg # Do nothing unless called with at least one arg
[ $# -gt 0 ] || return (( $# )) || return 0
local original_widget_name="$1" local original_widget_name="$1"
shift shift
if [ $widgets[$original_widget_name] ]; then if (( ${+widgets[$original_widget_name]} )); then
zle $original_widget_name -- $@ zle $original_widget_name -- $@
fi fi
} }
@@ -256,7 +266,7 @@ _zsh_autosuggest_invoke_original_widget() {
_zsh_autosuggest_highlight_reset() { _zsh_autosuggest_highlight_reset() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT 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}") region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}")
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi fi
@@ -266,8 +276,8 @@ _zsh_autosuggest_highlight_reset() {
_zsh_autosuggest_highlight_apply() { _zsh_autosuggest_highlight_apply() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if [ $#POSTDISPLAY -gt 0 ]; then if (( $#POSTDISPLAY )); then
_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE"
region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT") region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT")
else else
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
@@ -288,14 +298,14 @@ _zsh_autosuggest_disable() {
_zsh_autosuggest_enable() { _zsh_autosuggest_enable() {
unset _ZSH_AUTOSUGGEST_DISABLED unset _ZSH_AUTOSUGGEST_DISABLED
if [ $#BUFFER -gt 0 ]; then if (( $#BUFFER )); then
_zsh_autosuggest_fetch _zsh_autosuggest_fetch
fi fi
} }
# Toggle suggestions (enable/disable) # Toggle suggestions (enable/disable)
_zsh_autosuggest_toggle() { _zsh_autosuggest_toggle() {
if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then if [[ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]]; then
_zsh_autosuggest_enable _zsh_autosuggest_enable
else else
_zsh_autosuggest_disable _zsh_autosuggest_disable
@@ -312,9 +322,11 @@ _zsh_autosuggest_clear() {
# Modify the buffer and get a new suggestion # Modify the buffer and get a new suggestion
_zsh_autosuggest_modify() { _zsh_autosuggest_modify() {
emulate -L zsh
local -i retval local -i retval
# Only added to zsh very recently # Only available in zsh >= 5.4
local -i KEYS_QUEUED_COUNT local -i KEYS_QUEUED_COUNT
# Save the contents of the buffer/postdisplay # Save the contents of the buffer/postdisplay
@@ -329,35 +341,36 @@ _zsh_autosuggest_modify() {
retval=$? retval=$?
# Don't fetch a new suggestion if there's more input to be read immediately # Don't fetch a new suggestion if there's more input to be read immediately
if [[ $PENDING > 0 ]] || [[ $KEYS_QUEUED_COUNT > 0 ]]; then if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
POSTDISPLAY="$orig_postdisplay"
return $retval return $retval
fi fi
# Optimize if manually typing in the suggestion # Optimize if manually typing in the suggestion
if [ $#BUFFER -gt $#orig_buffer ]; then if (( $#BUFFER > $#orig_buffer )); then
local added=${BUFFER#$orig_buffer} local added=${BUFFER#$orig_buffer}
# If the string added matches the beginning of the postdisplay # If the string added matches the beginning of the postdisplay
if [ "$added" = "${orig_postdisplay:0:$#added}" ]; then if [[ "$added" = "${orig_postdisplay:0:$#added}" ]]; then
POSTDISPLAY="${orig_postdisplay:$#added}" POSTDISPLAY="${orig_postdisplay:$#added}"
return $retval return $retval
fi fi
fi fi
# Don't fetch a new suggestion if the buffer hasn't changed # Don't fetch a new suggestion if the buffer hasn't changed
if [ "$BUFFER" = "$orig_buffer" ]; then if [[ "$BUFFER" = "$orig_buffer" ]]; then
POSTDISPLAY="$orig_postdisplay" POSTDISPLAY="$orig_postdisplay"
return $retval return $retval
fi fi
# Bail out if suggestions are disabled # Bail out if suggestions are disabled
if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then if [[ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]]; then
return $? return $?
fi fi
# Get a new suggestion if the buffer is not empty after modification # Get a new suggestion if the buffer is not empty after modification
if [ $#BUFFER -gt 0 ]; then if (( $#BUFFER > 0 )); then
if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -le "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then
_zsh_autosuggest_fetch _zsh_autosuggest_fetch
fi fi
fi fi
@@ -371,16 +384,18 @@ _zsh_autosuggest_fetch() {
_zsh_autosuggest_async_request "$BUFFER" _zsh_autosuggest_async_request "$BUFFER"
else else
local suggestion local suggestion
_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER" _zsh_autosuggest_fetch_suggestion "$BUFFER"
_zsh_autosuggest_suggest "$suggestion" _zsh_autosuggest_suggest "$suggestion"
fi fi
} }
# Offer a suggestion # Offer a suggestion
_zsh_autosuggest_suggest() { _zsh_autosuggest_suggest() {
emulate -L zsh
local suggestion="$1" local suggestion="$1"
if [ -n "$suggestion" ] && [ $#BUFFER -gt 0 ]; then if [[ -n "$suggestion" ]] && (( $#BUFFER )); then
POSTDISPLAY="${suggestion#$BUFFER}" POSTDISPLAY="${suggestion#$BUFFER}"
else else
unset POSTDISPLAY unset POSTDISPLAY
@@ -393,12 +408,12 @@ _zsh_autosuggest_accept() {
# When vicmd keymap is active, the cursor can't move all the way # When vicmd keymap is active, the cursor can't move all the way
# to the end of the buffer # to the end of the buffer
if [ "$KEYMAP" = "vicmd" ]; then if [[ "$KEYMAP" = "vicmd" ]]; then
max_cursor_pos=$((max_cursor_pos - 1)) max_cursor_pos=$((max_cursor_pos - 1))
fi fi
# Only accept if the cursor is at the end of the buffer # Only accept if the cursor is at the end of the buffer
if [ $CURSOR -eq $max_cursor_pos ]; then if [[ $CURSOR = $max_cursor_pos ]]; then
# Add the suggestion to the buffer # Add the suggestion to the buffer
BUFFER="$BUFFER$POSTDISPLAY" BUFFER="$BUFFER$POSTDISPLAY"
@@ -427,7 +442,7 @@ _zsh_autosuggest_execute() {
# Partially accept the suggestion # Partially accept the suggestion
_zsh_autosuggest_partial_accept() { _zsh_autosuggest_partial_accept() {
local -i retval local -i retval cursor_loc
# Save the contents of the buffer so we can restore later if needed # Save the contents of the buffer so we can restore later if needed
local original_buffer="$BUFFER" local original_buffer="$BUFFER"
@@ -439,13 +454,19 @@ _zsh_autosuggest_partial_accept() {
_zsh_autosuggest_invoke_original_widget $@ _zsh_autosuggest_invoke_original_widget $@
retval=$? 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 we've moved past the end of the original buffer
if [ $CURSOR -gt $#original_buffer ]; then if (( $cursor_loc > $#original_buffer )); then
# Set POSTDISPLAY to text right of the cursor # Set POSTDISPLAY to text right of the cursor
POSTDISPLAY="$RBUFFER" POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}"
# Clip the buffer at the cursor # Clip the buffer at the cursor
BUFFER="$LBUFFER" BUFFER="${BUFFER[1,$cursor_loc]}"
else else
# Restore the original buffer # Restore the original buffer
BUFFER="$original_buffer" BUFFER="$original_buffer"
@@ -481,13 +502,13 @@ zle -N autosuggest-disable _zsh_autosuggest_widget_disable
zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
# Default Suggestion Strategy # # History Suggestion Strategy #
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
# Suggests the most recent history item that matches the given # Suggests the most recent history item that matches the given
# prefix. # prefix.
# #
_zsh_autosuggest_strategy_default() { _zsh_autosuggest_strategy_history() {
# Reset options to defaults and enable LOCAL_OPTIONS # Reset options to defaults and enable LOCAL_OPTIONS
emulate -L zsh emulate -L zsh
@@ -497,12 +518,12 @@ _zsh_autosuggest_strategy_default() {
# Escape backslashes and all of the glob operators so we can use # Escape backslashes and all of the glob operators so we can use
# this string as a pattern to search the $history associative array. # this string as a pattern to search the $history associative array.
# - (#m) globbing flag enables setting references for match data # - (#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}" local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}"
# Get the history items that match # Get the history items that match
# - (r) subscript flag makes the pattern match on values # - (r) subscript flag makes the pattern match on values
suggestion="${history[(r)$prefix*]}" typeset -g suggestion="${history[(r)${prefix}*]}"
} }
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
@@ -527,7 +548,14 @@ _zsh_autosuggest_strategy_default() {
# `HIST_EXPIRE_DUPS_FIRST`. # `HIST_EXPIRE_DUPS_FIRST`.
_zsh_autosuggest_strategy_match_prev_cmd() { _zsh_autosuggest_strategy_match_prev_cmd() {
local prefix="${1//(#m)[\\()\[\]|*?~]/\\$MATCH}" # 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}"
# Get all history event numbers that correspond to history # Get all history event numbers that correspond to history
# entries that match pattern $prefix* # entries that match pattern $prefix*
@@ -554,7 +582,30 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
done done
# Give back the matched history entry # Give back the matched history entry
suggestion="$history[$histkey]" typeset -g suggestion="$history[$histkey]"
}
#--------------------------------------------------------------------#
# Fetch Suggestion #
#--------------------------------------------------------------------#
# Loops through all specified strategies and returns a suggestion
# from the first strategy to provide one.
#
_zsh_autosuggest_fetch_suggestion() {
typeset -g suggestion
local -a strategies
# 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
} }
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
@@ -575,13 +626,15 @@ _zsh_autosuggest_async_server() {
sleep 1 # Block for long enough for the signal to come through sleep 1 # Block for long enough for the signal to come through
} }
# Output only newlines (not carriage return + newline) # Don't add any extra carriage returns
stty -onlcr stty -onlcr
# Don't translate carriage returns to newlines
stty -icrnl
# Silence any error messages # Silence any error messages
exec 2>/dev/null exec 2>/dev/null
local strategy=$1
local last_pid local last_pid
while IFS='' read -r -d $'\0' query; do while IFS='' read -r -d $'\0' query; do
@@ -591,7 +644,7 @@ _zsh_autosuggest_async_server() {
# Run suggestion search in the background # Run suggestion search in the background
( (
local suggestion local suggestion
_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query" _zsh_autosuggest_fetch_suggestion "$query"
echo -n -E "$suggestion"$'\0' echo -n -E "$suggestion"$'\0'
) & ) &
@@ -621,14 +674,14 @@ _zsh_autosuggest_async_pty_create() {
typeset -h REPLY typeset -h REPLY
# If we won't get a fd back from zpty, try to guess it # If we won't get a fd back from zpty, try to guess it
if [ $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD -eq 0 ]; then if (( ! $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD )); then
integer -l zptyfd integer -l zptyfd
exec {zptyfd}>&1 # Open a new file descriptor (above 10). exec {zptyfd}>&1 # Open a new file descriptor (above 10).
exec {zptyfd}>&- # Close it so it's free to be used by zpty. exec {zptyfd}>&- # Close it so it's free to be used by zpty.
fi fi
# Fork a zpty process running the server function # Fork a zpty process running the server function
zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "_zsh_autosuggest_async_server _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME _zsh_autosuggest_async_server
# Store the fd so we can remove the handler later # Store the fd so we can remove the handler later
if (( REPLY )); then if (( REPLY )); then
@@ -642,13 +695,11 @@ _zsh_autosuggest_async_pty_create() {
} }
_zsh_autosuggest_async_pty_destroy() { _zsh_autosuggest_async_pty_destroy() {
if zpty -t $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null; then
# Remove the input handler # Remove the input handler
zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null
# Destroy the zpty # Destroy the zpty
zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null
fi
} }
_zsh_autosuggest_async_pty_recreate() { _zsh_autosuggest_async_pty_recreate() {
@@ -682,7 +733,7 @@ _zsh_autosuggest_start() {
# to the widget list variables to take effect on the next precmd. # to the widget list variables to take effect on the next precmd.
add-zsh-hook precmd _zsh_autosuggest_bind_widgets add-zsh-hook precmd _zsh_autosuggest_bind_widgets
if [ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]; then if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
_zsh_autosuggest_async_start _zsh_autosuggest_async_start
fi fi
} }