Compare commits

...

254 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
Eric Freese
2cb6eb6e29 Merge pull request #218 from zsh-users/develop
v0.4.0
2017-05-10 15:18:07 -06:00
Eric Freese
14179d869d Bump version 2017-05-10 15:05:43 -06:00
Eric Freese
281ed9bbf7 v0.4.0 changelog updates 2017-05-10 15:05:43 -06:00
Eric Freese
83129dd796 Make asynchronous suggestions disabled by default
While they are still experimental
2017-04-14 08:48:54 -06:00
Eric Freese
40b96f6cfd Merge pull request #223 from zsh-users/fixes/bpm_async_fix
Fixes/bpm async fix
2017-03-05 12:59:19 -05:00
Eric Freese
a2f0ffb122 Enabling suggestions should not fetch a suggestion if buffer is empty 2017-03-05 12:53:13 -05:00
Eric Freese
7d4a1d9a4a Add enable/disable/toggle widgets to disable suggestion functionality
[GitHub #219]

Intended to be helpful for folks using bracketed-paste-magic and other
widgets that use `zle -U`.
2017-03-03 18:43:17 -05:00
Eric Freese
e1959d0f61 Put in a general fix for #219 - Handling input from zle -U
Depends on patch to ZSH from workers/40702:

  http://www.zsh.org/mla/workers/2017/msg00414.html
2017-03-03 18:43:17 -05:00
Eric Freese
c52c428793 Fix issues with widgets wrapped by other plugins
Puts in a better fix for #126 and related issues.
2017-03-03 18:43:10 -05:00
Eric Freese
ea505b01e5 Add a spec for unlisted widgets fetching a new suggestion 2017-03-03 18:43:10 -05:00
Eric Freese
502fb4a174 Make tmux_socket_name public so you can access easily from binding.pry
Can attach while tests are stopped with `tmux -L <socket_name> attach`
2017-03-03 18:43:10 -05:00
Eric Freese
ce362248fa Use pry-byebug instead of pry for more functionality 2017-03-03 18:43:10 -05:00
Eric Freese
cb93366d0e Merge pull request #221 from zsh-users/circle_ci_setup
Set up Circle CI
2017-02-26 14:36:15 -07:00
Eric Freese
39762ecd97 Set up circle ci 2017-02-26 14:26:41 -07:00
Eric Freese
468b7403e9 Test should be passing block to RSpec wait_for
Fixes flaky test
2017-02-26 14:18:22 -07:00
Eric Freese
c9a51e0c4c Handle dashes at the beginning of commands 2017-02-18 16:51:53 -07:00
Eric Freese
48a21bf79e [cleanup] Remove an extra newline 2017-02-18 11:27:55 -07:00
Eric Freese
4afbbbadda We only need to run the feature detection if starting async 2017-02-18 11:25:27 -07:00
Eric Freese
e3fa4e4904 Don't do anything but re-bind widgets on each precmd
There's no need to re-run feature detection or async_start on every
precmd. Just do those once.
2017-02-18 11:15:53 -07:00
Eric Freese
2cd99e64b7 Add a test for modifying widget list vars after sourcing plugin 2017-02-18 11:15:53 -07:00
Eric Freese
c70d685d15 Clean up widget list spec 2017-02-18 11:12:10 -07:00
Eric Freese
255359dbb8 Use += to be a bit more true to the spec language 2017-02-18 10:35:30 -07:00
Eric Freese
4321fc097c We need to bind on every precmd to ensure we wrap other wrappers
Specifically, highlighting breaks if our widgets are wrapped by z-syn-h
widgets.
2017-02-17 23:20:04 -07:00
Eric Freese
75e850577d Gracefully handle being sourced multiple times
Should fix #126
2017-02-17 23:19:55 -07:00
Eric Freese
a0fcd81ce1 Destroy zpty on load if it already exists 2017-02-17 22:47:28 -07:00
Eric Freese
39ca3dac45 Use a different name for feature detection zpty
So that it doesn't conflict when the file is sourced again
2017-02-17 22:07:48 -07:00
Eric Freese
dcce973287 Remove support for long-deprecated options
These options have been deprecated for over a year.
2017-02-17 18:45:46 -07:00
Christian Höltje
0c940e70f2 Don't bind any zle-* methods
It seems like all the zle-* methods are special and shouldn't be
monkeyed with.

Specifically `zle-isearch-update` and friends. Binding that widget
caused `history-incremental-pattern-search` to stop working.

Fixes zsh-users/zsh-syntax-highlighting#387
2017-02-17 18:32:52 -07:00
Eric Freese
a109f52fe4 Merge pull request #180 from zsh-users/features/async
Asynchronous suggestions
2017-02-17 18:28:54 -07:00
Eric Freese
23ef16c297 Do not show suggestions if the buffer is empty 2017-02-17 18:26:34 -07:00
Eric Freese
938144530c Fix tests 2017-02-17 16:01:07 -07:00
Eric Freese
c4bfd8e2c6 Need to prevent zpty feature detection from HUPing existing zptys 2017-02-17 15:51:50 -07:00
Eric Freese
c959408305 Only wait a max of 2 seconds for content to match after clearing screen 2017-02-17 15:33:09 -07:00
Eric Freese
06fca77ffb Readme updates for v0.4.0 2017-02-16 20:12:04 -07:00
Eric Freese
9feac573c9 Do not show any error output from async zpty server process 2017-02-16 19:27:32 -07:00
Eric Freese
ed8056c5e8 Lots of async changes 2017-02-16 19:19:30 -07:00
Eric Freese
38eb7cdafd Update license date 2017-02-16 19:07:41 -07:00
Eric Freese
64e7ec5bf8 Rename internal term session method 2017-01-29 10:43:20 -07:00
Eric Freese
98f926d53d Clean up TerminalSession constructor a bit 2017-01-29 10:43:00 -07:00
Eric Freese
51e8755634 TerminalSession methods return self to support chaining 2017-01-29 10:42:28 -07:00
Eric Freese
5151adfe40 Make TerminalSession#clear block until the screen is cleared 2017-01-29 10:40:05 -07:00
Eric Freese
2c465a932a Rename async pty name config var 2017-01-29 10:39:07 -07:00
Eric Freese
e3eb286ea2 Lots of little async cleanups 2017-01-27 15:18:26 -07:00
Eric Freese
c3425870f1 Wait for the terminal.clear to go through before continuing
Prevents some flakiness in tests
2017-01-27 14:07:06 -07:00
Eric Freese
89dd69d517 Add pry gem for debugging support 2017-01-27 14:06:37 -07:00
Eric Freese
40bb2e7804 little cleanup 2017-01-26 17:00:56 -07:00
Eric Freese
16666da488 Handle versions of zsh where zpty does not set REPLY to fd of opened pty
Based on e702ec4697/async.zsh (L400-L406)
2017-01-26 16:50:19 -07:00
Eric Freese
f33b605a63 Move async initialization into start function to keep in one place 2017-01-26 16:40:34 -07:00
Eric Freese
78ba07179a Add feature detection
Checks whether `zpty` gives a file descriptor, which was not the case in
older versions of zsh.

Based on a4b2f81c96/async.zsh (L395-L401)
2017-01-26 16:40:34 -07:00
Eric Freese
3f57198d07 Only bind widgets once, on initial sourcing 2017-01-26 16:23:27 -07:00
Eric Freese
2dbd261989 Allow configuring of zsh binary to run integration tests against 2017-01-26 16:04:46 -07:00
Eric Freese
6c5cd42331 Go back to tracking last pid because kill %1 didn't seem to be working 2017-01-25 00:00:53 -07:00
Eric Freese
54e1eee924 Optimize case where manually typing in a suggestion 2017-01-25 00:00:13 -07:00
Eric Freese
21d9eda5dd Wrap suggestion fetch command in parens to actually run in background 2017-01-24 23:59:38 -07:00
Eric Freese
50e6832b8c Escape the prefix passed into the match_prev_cmd strategy 2017-01-24 23:06:41 -07:00
Eric Freese
0305908adf Revert fc usage in calculating suggestion
As far as I know, `fc` makes it impossible to tell whether history items
used an actual newline character or the string "\n". Pulling from the
`$history` array gives a more accurate representation of the actual
command that was run.
2017-01-24 23:04:07 -07:00
Eric Freese
8e06a54b1c Add test for string with "\n" in it 2017-01-24 22:49:21 -07:00
Eric Freese
b3208b08af Pass the chosen strategy into the suggestion server pty 2017-01-24 22:48:30 -07:00
Eric Freese
ab2742537f Quote the suggestion to support sh_split_word option 2017-01-24 22:27:47 -07:00
Eric Freese
e5a5b0c1e0 Output only newlines in the pty 2017-01-24 22:27:09 -07:00
Eric Freese
0337005eb0 Disable word splitting while reading to preserve whitespace 2017-01-24 21:59:22 -07:00
Eric Freese
b530b0c996 Use zpty -r with pattern matching to fetch suggestion 2017-01-24 20:01:30 -07:00
Eric Freese
5c891afd48 Reset zsh options inside pty (from zsh-async) 2017-01-24 20:01:27 -07:00
Eric Freese
e33eb570c4 Send only the prefix to the suggestion server 2017-01-24 20:01:11 -07:00
Eric Freese
fba20b042e Use %1 instead of tracking pid 2017-01-24 20:00:50 -07:00
Eric Freese
0308ed797e Rename worker to server 2017-01-24 20:00:34 -07:00
Eric Freese
e72c2d87e5 add a bunch of comments 2017-01-24 19:53:59 -07:00
Eric Freese
ab8f295225 First pass at async functionality 2017-01-24 19:45:11 -07:00
Eric Freese
debbffc79a Add rspec test around accepting suggestions 2017-01-19 22:38:19 -07:00
Eric Freese
4850119887 Add separate test task for RSpec 2017-01-19 22:38:19 -07:00
Eric Freese
c22ab0e399 Implement suggestion integration tests in RSpec + tmux 2017-01-19 22:38:19 -07:00
Eric Freese
07a6768fcb Add TerminalSession helper for managing a tmux session 2017-01-19 22:38:19 -07:00
Eric Freese
e6591d5de0 Add RSpec for high-level integration testing 2017-01-19 22:33:17 -07:00
Eric Freese
af671fb406 Add ruby settings to editor config 2017-01-19 01:03:24 -07:00
Eric Freese
fedc22e9bb Merge pull request #169 from zsh-users/develop
v0.3.3
2016-10-17 07:45:49 -06:00
Eric Freese
9cfaf5d342 v0.3.3 2016-10-17 07:45:09 -06:00
Eric Freese
9333f0653f Update changelog for v0.3.3 2016-10-17 07:43:56 -06:00
Eric Freese
b377c39d0e Only fetch a new suggestion if buffer has changed 2016-07-31 20:10:22 -06:00
Eric Freese
a44aa59321 Remove unnecessary reset of POSTDISPLAy 2016-07-31 20:09:26 -06:00
Eric Freese
25f4afb058 Add ZSH_AUTOSUGGEST_IGNORE_WIDGETS array 2016-07-31 19:35:30 -06:00
Eric Freese
472394681e Merge pull request #178 from lbolla/issue141-develop
Disable autosuggest if buffer is too large
2016-07-18 10:28:45 -06:00
Lorenzo Bolla
a9c8efa048 Update README 2016-07-18 16:06:50 +01:00
Lorenzo Bolla
cdf56a3305 Include result of make 2016-07-18 10:56:21 +01:00
Lorenzo Bolla
2450c95d8a Rename and document new config var 2016-07-18 10:55:19 +01:00
Lorenzo Bolla
7b81eb79b8 Disable autosuggest if buffer is too large
Make buffer max size configurable, defaulted to infinity
2016-07-18 08:56:00 +01:00
Eric Freese
63816c5da8 Fix #164: Use fc builtin instead of $history array for lookup
According to a few tests, the `fc` builtin appears to be quite a bit
faster than searching through the `$history` associative array when
dealing with large history files (500K+).
2016-06-10 13:52:24 -06:00
Eric Freese
b4b3a82ee3 Fix #168 and #130: Escape tildes when fetching suggestions 2016-06-10 13:51:20 -06:00
Eric Freese
85ea943789 Merge pull request #163 from tsdh/develop
Document caveats of match_prev_cmd strategy
2016-05-28 13:34:00 -06:00
Tassilo Horn
ca70612d3c Document caveats of match_prev_cmd strategy
This strategy relies on the history being exactly in the order in which
commands have been entered.  Therefore, options like suppressing
duplicates or expiring duplicates first will lead to unexpected
suggestions.
2016-05-28 21:23:35 +02:00
Eric Freese
62cdba4e0f Merge pull request #142 from zsh-users/v0.3.x
v0.3.2
2016-05-28 08:28:18 -06:00
Eric Freese
cce68de46d v0.3.2 2016-05-28 08:27:08 -06:00
Eric Freese
4a2d9f9049 Fix Makefile to not create symbolic link after PR #156 was merged 2016-05-28 08:26:24 -06:00
Eric Freese
7c688ec20c Add note to readme about PRs going to develop branch 2016-05-28 08:23:53 -06:00
Eric Freese
96eb0fae77 Changelog updates for v0.3.2 2016-05-28 08:18:52 -06:00
Eric Freese
5e5bfdb659 Merge pull request #156 from adamkruszewski/v0.3.x
Adjust plugin.zsh file to run on zsh 5.1 in mSYS2.
2016-05-12 08:53:20 -06:00
adamkruszewski
0a6c34947c Adjust plugin.zsh file to run on zsh 5.1 in mSYS2. 2016-05-11 17:02:41 +02:00
Eric Freese
e87bc74654 Fix 118: Clear suggestion before original widget to fix completions
See PR #149
2016-04-25 14:42:09 -06:00
Eric Freese
964773aa75 Use array indices for forward compatibility
See issue #152
2016-04-25 14:26:37 -06:00
Eric Freese
945c660856 Fix #152 by escaping widget names inside evals
Solves problems when dealing with widget names with irregular
characters such as those that come from `opp.zsh`.
2016-04-25 14:26:35 -06:00
Eric Freese
011d8bdfd1 Refactor to remove prev cmd function and simplify escaping 2016-04-25 14:19:26 -06:00
Eric Freese
c5f57da2b8 Pull duplicated test logic from strategies into single test file 2016-04-22 14:14:29 -06:00
Eric Freese
c477db2696 Remove unused test variables from Makefile 2016-04-15 13:41:41 -06:00
Eric Freese
d7001f2c34 Fix readme test script reference. 2016-04-15 13:40:18 -06:00
Eric Freese
6d6e7820f3 Fix #143: Add vi-add-eol to list of accept widgets. 2016-04-15 13:37:50 -06:00
Eric Freese
9bef50fac1 Merge pull request #140 from zsh-users/fixes/bound_widget_return_code
Keep track of return value from original widget (#135)
2016-04-14 11:18:01 -06:00
Eric Freese
1d4f7e157e Keep track of return value from original widget (#135) 2016-04-06 17:13:04 -06:00
Eric Freese
2acf25e065 Formatting 2016-03-15 09:24:13 -06:00
Eric Freese
ba7109169d Clean up tests with setUp and tearDown functions 2016-03-15 09:05:55 -06:00
Eric Freese
a28e72e84a Test Runner now supports running specific tests and choosing zsh bin 2016-03-15 09:04:17 -06:00
Eric Freese
87facd9b85 bump version v0.3.1 2016-03-14 22:33:26 -06:00
Eric Freese
dc822c54f8 Fix #133 2016-03-14 21:50:04 -06:00
Eric Freese
15c5db898f Fix #137 2016-03-14 21:41:14 -06:00
Eric Freese
46d5fe174d Remove extra newline 2016-03-14 18:02:16 -06:00
Eric Freese
6c31a02892 Remove old test script 2016-03-14 17:44:24 -06:00
Eric Freese
9ec62a1a23 Split out a separate test file for each widget 2016-03-14 17:38:37 -06:00
Eric Freese
1ed9155f89 Add Suggestion Strategy section to Configuration section of Readme. 2016-03-14 17:25:09 -06:00
Eric Freese
97b51a2c57 bump version v0.3.0 2016-03-14 17:18:40 -06:00
Eric Freese
b54c8a15c6 Add changelog 2016-03-14 17:07:04 -06:00
Eric Freese
2a6d401106 Add quotes 2016-03-14 16:51:27 -06:00
Eric Freese
a314a01a6a Fix various bugs found while testing 2016-03-14 16:51:27 -06:00
Eric Freese
e5cdbb6c33 Lots of test cleanup. 2016-03-14 16:51:27 -06:00
Eric Freese
dffd9beae1 Add stub.sh submodule 2016-03-14 16:51:20 -06:00
Eric Freese
d202b32ae9 Add shunit2 submodule 2016-03-14 16:51:20 -06:00
Eric Freese
ab0f4c0bd0 Grammar and formatting 2016-03-14 16:51:20 -06:00
Eric Freese
ee6dde9ee8 Rename escape command function 2016-03-14 16:51:20 -06:00
Geza Lore
73f774bd5d Add match_prev_cmd strategy.
A new suggestion strategy 'match_prev_cmd' is available. This is a bit
more context aware variaton on the default strategy.
The suggestion will be:
- The newest history entry that matches the current prefix, AND
- Whose preceding history entry also matches the previously executed
command.

See src/strategies/match_prev_cmd.zsh for an example.
2016-03-14 16:51:20 -06:00
Geza Lore
976acc708c Fix default suggestion strategy and add testing 2016-03-14 16:51:20 -06:00
Eric Freese
83f78d0760 Add suggestion "strategy" concept with default strategy 2016-03-14 16:51:20 -06:00
Eric Freese
9df362f783 Merge pull request #125 from zsh-users/features/execute_widget
Add a widget to accept and execute the current suggestion.
2016-03-04 18:57:43 -07:00
Eric Freese
125f48c7f2 Add test for execute widget 2016-03-01 13:18:46 -07:00
Eric Freese
8c3fdea75d Call original accept-line widget when executing suggestion 2016-03-01 13:18:46 -07:00
Kordan Ou
aa597eea6d Add an autosuggest widget: autosuggest-execute. 2016-03-01 13:18:46 -07:00
Eric Freese
f0a745576f bump version v0.2.17 2016-03-01 13:16:10 -07:00
Eric Freese
8935a39e9b Remove plugin compatibility section from readme 2016-03-01 13:14:20 -07:00
Eric Freese
c7c9929490 Add zsh-history-substring-search widgets to CLEAR array by default. 2016-03-01 13:13:01 -07:00
Eric Freese
985de56f6e Update asciinema to reflect new repo 2016-02-25 13:20:21 -07:00
Eric Freese
0a42f872b8 bump version v0.2.16 2016-02-25 13:05:03 -07:00
Eric Freese
dd54925b06 Update URL references after repo move to zsh-users 2016-02-25 13:04:32 -07:00
Eric Freese
c761dc8150 bump version v0.2.15 2016-02-24 18:40:24 -07:00
Eric Freese
cc921994e6 Fix #127 by adding quotes 2016-02-24 18:35:17 -07:00
Eric Freese
0242c7eff1 Fix highlighting when sh_word_split option is enabled 2016-02-24 13:59:49 -07:00
Eric Freese
9d100f4f32 bump version v0.2.14 2016-02-23 20:12:16 -07:00
Eric Freese
ddb7284852 Fix backslash escaping problems with echo -E. 2016-02-23 20:11:56 -07:00
Eric Freese
2a5791710a bump version v0.2.13 2016-02-23 18:14:12 -07:00
Eric Freese
03fac1f0d7 Revert "Use zle -w flag to set WIDGET appropriately when calling orig widget"
This reverts commit 70438d233d.
2016-02-23 18:13:03 -07:00
Eric Freese
aa859a282d bump version v0.2.12 2016-02-23 10:37:57 -07:00
Eric Freese
f08a5a1baa [Formatting] Remove extra space in test script. 2016-02-23 10:24:35 -07:00
Eric Freese
70438d233d Use zle -w flag to set WIDGET appropriately when calling orig widget 2016-02-23 10:21:35 -07:00
Eric Freese
ba029e83d0 bump version v0.2.11 2016-02-17 13:44:52 -07:00
Eric Freese
acc129de6c Fix error when using autosuggest widgets 2016-02-17 13:44:52 -07:00
Eric Freese
aa5ceee256 Make asciinema a bit smaller. 2016-02-16 21:55:56 -07:00
Eric Freese
113ca0ad10 Add asciinema recording to README 2016-02-16 21:35:44 -07:00
Eric Freese
2b449a62f8 bump version v0.2.10 2016-02-16 20:59:31 -07:00
Eric Freese
6d25df6864 Revert usage of fc for suggestions and fix for sh_word_split.
Force field splitting on \0 to support sh_word_split option.
2016-02-16 10:51:01 -07:00
Eric Freese
0faa2b6584 bump version v0.2.9 2016-02-16 09:34:35 -07:00
Eric Freese
a2d8d91196 Actually fix suggestions when sh_split_words option is enabled. 2016-02-16 09:33:26 -07:00
Eric Freese
dd9a8789a7 bump version v0.2.8 2016-02-16 07:59:36 -07:00
Eric Freese
1b98af5b33 Fix suggestions when sh_split_words option is enabled 2016-02-16 07:57:44 -07:00
Eric Freese
45ab49d1f2 bump version v0.2.7 2016-02-15 08:46:23 -07:00
Eric Freese
41f15d5c9f Forgot a pesky backslash 2016-02-15 08:45:52 -07:00
Eric Freese
3ce1adb55d bump version v0.2.6 2016-02-15 08:31:50 -07:00
Eric Freese
2461a98857 Fix segfaults once and for all? 2016-02-15 08:31:00 -07:00
Eric Freese
76f415bf43 bump version v0.2.5 2016-02-14 08:55:20 -07:00
Eric Freese
5e419da326 Remove list of modify widgets and make 'modify' the default behavior. 2016-02-14 08:54:34 -07:00
Eric Freese
cd71081303 bump version v0.2.4 2016-02-14 01:08:21 -07:00
Eric Freese
9788c2ee49 Fix deprecation warnings. 2016-02-14 01:07:05 -07:00
Eric Freese
ebcfc46b72 Comment formatting 2016-02-14 00:29:43 -07:00
Eric Freese
b49d002888 [README] Add example of minimal .zshrc. 2016-02-13 23:53:32 -07:00
Eric Freese
266437c98a bump version v0.2.3 2016-02-13 23:47:24 -07:00
Eric Freese
51b39e210e Bound widgets now handle arguments correctly 2016-02-13 23:46:34 -07:00
Eric Freese
011f5420fc Version bump v0.2.2 2016-02-13 21:15:33 -07:00
Eric Freese
1a38fbf6a5 [README] Add note about workaround for bpm bug. 2016-02-13 21:14:59 -07:00
Eric Freese
31452887d2 [Test] Add tests widgets 2016-02-13 14:13:21 -07:00
Eric Freese
cf146b6696 [Test] Clean up highlight tests 2016-02-13 14:13:21 -07:00
Eric Freese
74197498fc [Test] Add stub.sh from ericfreese/stub.sh for stubbing/mocking. 2016-02-13 14:13:21 -07:00
Eric Freese
abe577d519 Fix build 2016-02-13 14:13:21 -07:00
Eric Freese
0ae5907294 [README] Add note about Oh My Zsh paste bug. 2016-02-13 09:59:16 -07:00
Eric Freese
03bd381112 Add .plugin file and installation instructions for Oh My Zsh (#104). 2016-02-07 15:35:54 -07:00
Eric Freese
00bd0e9125 Use add-zsh-hook to remove need to call autosuggest_start. 2016-02-07 15:35:43 -07:00
Eric Freese
48d2dc1091 Remove absolute link in readme. 2016-02-07 14:50:00 -07:00
Eric Freese
f154d25fb3 Fix typo when setting up autosuggest-clear widget. 2016-02-07 08:58:09 -07:00
Eric Freese
e91db46ce0 Add Development section to README (#106) 2016-02-07 08:52:52 -07:00
Eric Freese
28836f15c5 Test script should be a .zsh file instead of .sh 2016-02-07 08:52:34 -07:00
Eric Freese
0dd1b7febb Add Troubleshooting section to README 2016-02-07 08:52:07 -07:00
Eric Freese
6a4b2b3865 Update README 2016-02-06 21:57:34 -07:00
Eric Freese
3449ae505c Merge pull request #91 from tarruda/v0.1.x
Cleanup
2016-02-06 21:15:47 -07:00
Eric Freese
775dd20706 Rewrite 2016-02-06 21:05:02 -07:00
64 changed files with 2829 additions and 747 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

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

4
.gitignore vendored
View File

@@ -1,4 +0,0 @@
/node_modules/
/build/
*.log
*~

3
.rspec Normal file
View File

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

30
.rubocop.yml Normal file
View File

@@ -0,0 +1,30 @@
# Rails:
# Enabled: true
AllCops:
TargetRubyVersion: 2.3
Include:
- '**/Rakefile'
- '**/config.ru'
- '**/Gemfile'
Metrics/LineLength:
Max: 120
Style/Documentation:
Enabled: false
Style/DotPosition:
EnforcedStyle: trailing
Style/FrozenStringLiteralComment:
Enabled: false
Style/Lambda:
Enabled: false
Style/MultilineMethodCallIndentation:
EnforcedStyle: indented
Style/TrailingUnderscoreVariable:
Enabled: false

1
.ruby-version Normal file
View File

@@ -0,0 +1 @@
2.5.3

79
CHANGELOG.md Normal file
View File

@@ -0,0 +1,79 @@
# 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)
- Add `vi-add-eol` to list of accept widgets (#143)
- Escapes widget names within evals to fix problems with irregular widget names (#152)
- Plugin now clears suggestion while within a completion menu (#149)
- .plugin file no longer relies on symbolic link support, fixing issues on Windows (#156)
## v0.3.1
- Fixes issue with `vi-next-char` not accepting suggestion (#137).
- Fixes global variable warning when WARN_CREATE_GLOBAL option enabled (#133).
- Split out a separate test file for each widget.
## v0.3.0
- Adds `autosuggest-execute` widget (PR #124).
- Adds concept of suggestion "strategies" for different ways of fetching suggestions.
- Adds "match_prev_cmd" strategy (PR #131).
- Uses git submodules for testing dependencies.
- Lots of test cleanup.
- Various bug fixes for zsh 5.0.x and `sh_word_split` option.
## v0.2.17
Start of changelog.

1
DESCRIPTION Normal file
View File

@@ -0,0 +1 @@
Fish-like fast/unobtrusive autosuggestions for zsh.

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

5
Gemfile Normal file
View File

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

41
Gemfile.lock Normal file
View File

@@ -0,0 +1,41 @@
GEM
remote: https://rubygems.org/
specs:
byebug (9.0.5)
coderay (1.1.1)
diff-lcs (1.3)
method_source (0.8.2)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pry-byebug (3.4.0)
byebug (~> 9.0)
pry (~> 0.10)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.4)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
rspec-wait (0.0.9)
rspec (>= 3, < 4)
slop (3.6.0)
PLATFORMS
ruby
DEPENDENCIES
pry-byebug
rspec
rspec-wait
BUNDLED WITH
1.13.6

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

37
Makefile Normal file
View File

@@ -0,0 +1,37 @@
SRC_DIR := ./src
SRC_FILES := \
$(SRC_DIR)/setup.zsh \
$(SRC_DIR)/config.zsh \
$(SRC_DIR)/util.zsh \
$(SRC_DIR)/features.zsh \
$(SRC_DIR)/bind.zsh \
$(SRC_DIR)/highlight.zsh \
$(SRC_DIR)/widgets.zsh \
$(SRC_DIR)/strategies/*.zsh \
$(SRC_DIR)/fetch.zsh \
$(SRC_DIR)/async.zsh \
$(SRC_DIR)/start.zsh
HEADER_FILES := \
DESCRIPTION \
URL \
VERSION \
LICENSE
PLUGIN_TARGET := zsh-autosuggestions.zsh
all: $(PLUGIN_TARGET)
$(PLUGIN_TARGET): $(HEADER_FILES) $(SRC_FILES)
cat $(HEADER_FILES) | sed -e 's/^/# /g' > $@
cat $(SRC_FILES) >> $@
.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)

243
README.md
View File

@@ -4,139 +4,154 @@ _[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
If you already use [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting) plugin, then make sure to be loaded **before** zsh-autosuggestions.
Note: _.zshrc_ is a file that contains user-specific ZSH configuration.
ZSH assumes this file in your home directory (i.e. `~/.zshrc`), but the location can be changed using `ZDOTDIR` variable.
### Using zgen
[Zgen](https://github.com/tarjoilija/zgen) is a simple and fast plugin manager for ZSH.
If you dont use zgen, then use instructions for the manual installation.
1. Load `tarruda/zsh-autosuggestions` and `zsh-users/zsh-syntax-highlighting` using zgen in your .zshrc file, for example:
```sh
if ! zgen saved; then
echo "Creating a zgen save"
zgen load zsh-users/zsh-syntax-highlighting
# autosuggestions should be loaded last
zgen load tarruda/zsh-autosuggestions
zgen save
fi
```
2. Enable zsh-autosuggestions; copy the following snippet and put it after the zgen config section in your .zshrc file:
```sh
# Enable autosuggestions automatically.
zle-line-init() {
zle autosuggest-start
}
zle -N zle-line-init
```
3. Run `zgen reset` and reopen your terminal.
See [INSTALL.md](INSTALL.md).
### Manually
## Usage
1. Clone this repository to `~/.zsh/zsh-autosuggestions` (or anywhere else):
As you type commands, you will see a completion offered after the cursor in a muted gray color. This color can be changed by setting the `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` variable. See [configuration](#configuration).
```sh
git clone git://github.com/tarruda/zsh-autosuggestions ~/.zsh/zsh-autosuggestions
```
If you press the <kbd>→</kbd> key (`forward-char` widget) or <kbd>End</kbd> (`end-of-line` widget) with the cursor at the end of the buffer, it will accept the suggestion, replacing the contents of the command line buffer with the suggestion.
2. Clone zsh-syntax-highlighting repository to `~/.zsh/zsh-syntax-highlighting` (or anywhere else):
```sh
git clone git://github.com/zsh-users/zsh-syntax-highlighting ~/.zsh/zsh-syntax-highlighting
```
3. Load and enable autosuggestions; copy the following snippet and put it to your .zshrc file:
```sh
# Load zsh-syntax-highlighting.
source ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
# Load zsh-autosuggestions.
source ~/.zsh/zsh-autosuggestions/autosuggestions.zsh
# Enable autosuggestions automatically.
zle-line-init() {
zle autosuggest-start
}
zle -N zle-line-init
```
4. Reopen your terminal.
## Uninstallation
Just remove the config lines from .zshrc that youve added during “installation.”
If you dont use zgen, then also delete `~/.zsh/zsh-autosuggestions` and `~/.zsh/zsh-syntax-highlighting`.
## How to use
As you type commands, you will see a completion offered after the cursor, in a muted gray color (which can be changed, see [Configuration](#configuration)).
To accept the autosuggestion (replacing the command line contents), hit <kbd>End</kbd>, <kbd>Alt+F</kbd>, <kbd>Ctrl+F</kbd>, or any other key that moves the cursor to the right.
If the autosuggestion is not what you want, just ignore it: it wont execute unless you accept it.
Any widget that moves the cursor to the right (forward-word, forward-char, end-of-line…) will accept parts of the suggested text.
For example, vi-mode users can do this:
```sh
# Accept suggestions without leaving insert mode
bindkey '^f' vi-forward-word
# or
bindkey '^f' vi-forward-blank-word
```
You can also use right arrow key to accept the suggested text as in Fish shell; see [Configuration](#configuration) section to enable it.
### Exposed widgets
This plugin defines some ZLE widgets (think about them as functions) which you can bind to some key using [bindkey](http://zshwiki.org/home/zle/bindkeys).
For example, to toggle autosuggestions using <kbd>Ctrl+T</kbd> add this to your .zshrc:
```sh
bindkey '^T' autosuggest-toggle
```
List of widgets:
- `autosuggest-toggle` disable/enable autosuggestions.
- `autosuggest-execute-suggestion` accept the suggestion and execute it.
If you invoke the `forward-word` widget, it will partially accept the suggestion up to the point that the cursor moves to.
## Configuration
You may override default global config variables after plugin load, i.e. put it to your .zshrc after the code that loads plugins.
You may want to override the default global config variables. Default values of these variables can be found [here](src/config.zsh).
- `AUTOSUGGESTION_HIGHLIGHT_COLOR` suggestion highlight color, default is `'fg=8'`.
- `AUTOSUGGESTION_HIGHLIGHT_CURSOR` highlight word after cursor, or not. Must be integer value `1` or `0`, default is `1`.
- `AUTOSUGGESTION_ACCEPT_RIGHT_ARROW` complete entire suggestion with right arrow. Must be integer value `1` or `0`, default is `0` (right arrow completes one letter at a time).
**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).
## Known Issues
### Suggestion Highlight Style
> When I hit <kbd>Tab</kbd> and autosuggestions is enabled, it deletes the previous line, and scrolls up the terminal.
Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion is shown with. The default is `fg=8`.
This usually happens when autosuggestions is used along with something like [“completion waiting dots.”](http://michael.thegrebs.com/2012/09/04/zsh-completion-waiting-dots/)
Check which widget is bind to the Tab key; run `bindkey "^I"`.
If it prints something other than `"^I" expand-or-complete`, then this may be the problem.
If you use [Oh My Zsh](https://github.com/robbyrussell/oh-my-zsh), then make sure that the variable `COMPLETION_WAITING_DOTS` is not set (it enables [this](https://github.com/robbyrussell/oh-my-zsh/blob/e55c715508a2f652fed741f2047c66dda2c6e5b0/lib/completion.zsh#L56-L64) problematic code).
### Suggestion Strategy
If you use module [editor](https://github.com/sorin-ionescu/prezto/tree/master/modules/editor) from [Prezto](https://github.com/sorin-ionescu/prezto), then you must comment out [these lines](https://github.com/sorin-ionescu/prezto/blob/a84ac5b0023d71c98bb28a68c550dc13f6c51945/modules/editor/init.zsh#L303-L304).
`ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. The strategies in the array are tried successively until a suggestion is found. There are currently two built-in strategies to choose from:
- `history`: Chooses the most recent match from history.
- `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`.
### Widget Mapping
This plugin works by triggering custom behavior when certain [zle widgets](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets) are invoked. You can add and remove widgets from these arrays to change the behavior of this plugin:
- `ZSH_AUTOSUGGEST_CLEAR_WIDGETS`: Widgets in this array will clear the suggestion when invoked.
- `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.
**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`:
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.
```sh
bindkey '^ ' autosuggest-accept
```
## Troubleshooting
If you have a problem, please search through [the list of issues on GitHub](https://github.com/zsh-users/zsh-autosuggestions/issues) to see if someone else has already reported it.
### Reporting an Issue
Before reporting an issue, please try temporarily disabling sections of your configuration and other plugins that may be conflicting with this plugin to isolate the problem.
When reporting an issue, please include:
- The smallest, simplest `.zshrc` configuration that will reproduce the problem. See [this comment](https://github.com/zsh-users/zsh-autosuggestions/issues/102#issuecomment-180944764) for a good example of what this means.
- The version of zsh you're using (`zsh --version`)
- Which operating system you're running
## Uninstallation
1. Remove the code referencing this plugin from `~/.zshrc`.
2. Remove the git repository from your hard drive
```sh
rm -rf ~/.zsh/zsh-autosuggestions # Or wherever you installed
```
## Development
### Build Process
Edit the source files in `src/`. Run `make` to build `zsh-autosuggestions.zsh` from those source files.
### Pull Requests
Pull requests are welcome! If you send a pull request, please:
- Request to merge into the `develop` branch (*NOT* `master`)
- Match the existing coding conventions.
- Include helpful comments to keep the barrier-to-entry low for people new to the project.
- Write tests that cover your code as much as possible.
### 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.
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

1
URL Normal file
View File

@@ -0,0 +1 @@
https://github.com/zsh-users/zsh-autosuggestions

1
VERSION Normal file
View File

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

View File

@@ -1,294 +0,0 @@
# Fish-like autosuggestions for zsh. Some of the code was based on the code
# for 'predict-on'
#
# ```zsh
# zle-line-init() {
# autosuggest-enable
# }
# zle -N zle-line-init
# ```
zmodload zsh/net/socket
source "${0:a:h}/completion-client.zsh"
# configuration variables
AUTOSUGGESTION_HIGHLIGHT_COLOR='fg=8'
AUTOSUGGESTION_HIGHLIGHT_CURSOR=1
function {
if [[ -n $ZLE_DISABLE_AUTOSUGGEST ]]; then
ZSH_HIGHLIGHT_HIGHLIGHTERS=()
return
fi
autoload -U is-at-least
# if is-at-least 5.0.3; then
# autosuggest-ensure-server
# fi
}
ZLE_AUTOSUGGEST_SUSPEND_WIDGETS=(
vi-cmd-mode vi-backward-char backward-char backward-word beginning-of-line
history-search-forward history-search-backward up-line-or-history
history-beginning-search-forward history-beginning-search-backward
down-line-or-history history-substring-search-up history-substring-search-down
backward-kill-word
)
ZLE_AUTOSUGGEST_COMPLETION_WIDGETS=(
complete-word expand-or-complete expand-or-complete-prefix list-choices
menu-complete reverse-menu-complete menu-expand-or-complete menu-select
accept-and-menu-complete
)
ZLE_AUTOSUGGEST_ACCEPT_WIDGETS=(
vi-forward-char forward-char vi-forward-word forward-word vi-add-eol
vi-add-next vi-forward-blank-word vi-end-of-line end-of-line
)
ZLE_AUTOSUGGEST_ALL_WIDGETS=(
self-insert magic-space backward-delete-char accept-line
$ZLE_AUTOSUGGEST_ACCEPT_WIDGETS
$ZLE_AUTOSUGGEST_SUSPEND_WIDGETS
$ZLE_AUTOSUGGEST_COMPLETION_WIDGETS
)
autosuggest-pause() {
[[ -z $ZLE_AUTOSUGGESTING ]] && return
unset ZLE_AUTOSUGGESTING
# Restore standard widgets except for self-insert, which triggers resume
autosuggest-restore-widgets
zle -A autosuggest-paused-self-insert self-insert
# When autosuggestions are disabled, kill the unmaterialized part
RBUFFER=''
autosuggest-highlight-suggested-text
if [[ -n $ZLE_AUTOSUGGEST_CONNECTION ]]; then
zle -F $ZLE_AUTOSUGGEST_CONNECTION
fi
}
autosuggest-resume() {
[[ -n $ZLE_AUTOSUGGESTING ]] && return
ZLE_AUTOSUGGESTING=1
autosuggest-hook-widgets
if [[ -n $ZLE_AUTOSUGGEST_CONNECTION ]]; then
# install listen for suggestions asynchronously
zle -Fw $ZLE_AUTOSUGGEST_CONNECTION autosuggest-pop-suggestion
fi
}
autosuggest-start() {
if [[ -z $ZLE_DISABLE_AUTOSUGGEST && -n $functions[_zsh_highlight] ]]; then
if [[ ${ZSH_HIGHLIGHT_HIGHLIGHTERS[(i)autosuggest]} -gt ${#ZSH_HIGHLIGHT_HIGHLIGHTERS} ]]; then
ZSH_HIGHLIGHT_HIGHLIGHTERS+=(autosuggest)
fi
fi
autosuggest-resume
}
# Toggles autosuggestions on/off
autosuggest-toggle() {
if [[ -n $ZLE_AUTOSUGGESTING ]]; then
autosuggest-pause
zle -A .self-insert self-insert
else
autosuggest-resume
fi
}
autosuggest-highlight-suggested-text() {
if (( $+functions[_zsh_highlight_buffer_modified] > 0 )); then
_zsh_highlight
else
region_highlight=()
_zsh_highlight_autosuggest_highlighter
fi
}
_zsh_highlight_autosuggest_highlighter_predicate() {
[[ -n $ZLE_AUTOSUGGESTING ]] && (( $#RBUFFER > 0 ))
}
_zsh_highlight_autosuggest_highlighter() {
region_highlight+=("$(( $CURSOR + $AUTOSUGGESTION_HIGHLIGHT_CURSOR )) $(( $CURSOR + $#RBUFFER )) $AUTOSUGGESTION_HIGHLIGHT_COLOR")
}
autosuggest-insert-or-space() {
setopt localoptions noshwordsplit noksharrays
if [[ $LBUFFER == *$'\012'* ]] || (( PENDING )); then
# Editing multiline buffer or pasting a chunk of text, pause
autosuggest-suspend
return
fi
if [[ ${RBUFFER[1]} == ${KEYS[-1]} ]]; then
# Same as what's typed, just move on
((++CURSOR))
autosuggest-invalidate-highlight-cache
else
LBUFFER="$LBUFFER$KEYS"
if [[ $LASTWIDGET == (self-insert|magic-space|backward-delete-char) || $LASTWIDGET == (complete-word|accept-*|zle-line-init) ]]; then
if ! zle .history-beginning-search-backward; then
RBUFFER=''
if [[ ${KEYS[-1]} != ' ' ]]; then
autosuggest-send-request ${LBUFFER}
fi
fi
fi
fi
autosuggest-highlight-suggested-text
}
autosuggest-backward-delete-char() {
if (( $#LBUFFER > 1 )); then
setopt localoptions noshwordsplit noksharrays
if [[ $LBUFFER = *$'\012'* || $LASTWIDGET != (self-insert|magic-space|backward-delete-char) ]]; then
LBUFFER="$LBUFFER[1,-2]"
else
((--CURSOR))
autosuggest-invalidate-highlight-cache
zle .history-beginning-search-forward || RBUFFER=''
fi
autosuggest-highlight-suggested-text
else
zle .kill-whole-line
fi
}
# When autosuggesting, ignore RBUFFER which corresponds to the 'unmaterialized'
# section when the user accepts the line
autosuggest-accept-line() {
RBUFFER=''
if ! (( $+functions[_zsh_highlight_buffer_modified] )); then
# Only clear the colors if the user doesn't have zsh-highlight installed
region_highlight=()
fi
zle .accept-line
}
autosuggest-paused-self-insert() {
if [[ $RBUFFER == '' ]]; then
# Resume autosuggestions when inserting at the end of the line
autosuggest-resume
zle self-insert
else
zle .self-insert
fi
}
autosuggest-pop-suggestion() {
local words last_word suggestion
if ! IFS= read -r -u $ZLE_AUTOSUGGEST_CONNECTION suggestion; then
# server closed the connection, stop listenting
zle -F $ZLE_AUTOSUGGEST_CONNECTION
unset ZLE_AUTOSUGGEST_CONNECTION
return
fi
if [[ -n $suggestion ]]; then
local prefix=${suggestion%$'\2'*}
suggestion=${suggestion#*$'\2'}
# only use the suggestion if the prefix is still compatible with
# the suggestion(prefix should be contained in LBUFFER)
if [[ ${LBUFFER#$prefix*} != ${LBUFFER} ]]; then
words=(${(z)LBUFFER})
last_word=${words[-1]}
suggestion=${suggestion:$#last_word}
RBUFFER="$suggestion"
autosuggest-highlight-suggested-text
else
RBUFFER=''
fi
else
RBUFFER=''
fi
zle -Rc
}
autosuggest-suspend() {
autosuggest-pause
zle .${WIDGET} "$@"
}
autosuggest-tab() {
RBUFFER=''
zle .${WIDGET} "$@"
autosuggest-invalidate-highlight-cache
autosuggest-highlight-suggested-text
}
autosuggest-accept-suggestion() {
if [[ AUTOSUGGESTION_ACCEPT_RIGHT_ARROW -eq 1 && ("$WIDGET" == 'forward-char' || "$WIDGET" == 'vi-forward-char') ]]; then
zle .end-of-line "$@"
else
zle .${WIDGET} "$@"
fi
if [[ -n $ZLE_AUTOSUGGESTING ]]; then
autosuggest-invalidate-highlight-cache
autosuggest-highlight-suggested-text
fi
}
autosuggest-execute-suggestion() {
if [[ -n $ZLE_AUTOSUGGESTING ]]; then
zle .end-of-line
autosuggest-invalidate-highlight-cache
autosuggest-highlight-suggested-text
fi
zle .accept-line
}
autosuggest-invalidate-highlight-cache() {
# invalidate the buffer for zsh-syntax-highlighting
_zsh_highlight_autosuggest_highlighter_cache=()
}
autosuggest-restore-widgets() {
for widget in $ZLE_AUTOSUGGEST_ALL_WIDGETS; do
[[ -z $widgets[$widget] ]] && continue
zle -A .${widget} ${widget}
done
}
autosuggest-hook-widgets() {
local widget
# Replace prediction widgets by versions that will also highlight RBUFFER
zle -A autosuggest-insert-or-space self-insert
zle -A autosuggest-insert-or-space magic-space
zle -A autosuggest-backward-delete-char backward-delete-char
zle -A autosuggest-accept-line accept-line
# Hook into some default widgets that should suspend autosuggestion
# automatically
for widget in $ZLE_AUTOSUGGEST_ACCEPT_WIDGETS; do
[[ -z $widgets[$widget] ]] && continue
eval "zle -A autosuggest-accept-suggestion $widget"
done
for widget in $ZLE_AUTOSUGGEST_SUSPEND_WIDGETS; do
[[ -z $widgets[$widget] ]] && continue
eval "zle -A autosuggest-suspend $widget"
done
for widget in $ZLE_AUTOSUGGEST_COMPLETION_WIDGETS; do
[[ -z $widgets[$widget] ]] && continue
eval "zle -A autosuggest-tab $widget"
done
}
zle -N autosuggest-toggle
zle -N autosuggest-start
zle -N autosuggest-accept-suggested-small-word
zle -N autosuggest-accept-suggested-word
zle -N autosuggest-execute-suggestion
zle -N autosuggest-paused-self-insert
zle -N autosuggest-insert-or-space
zle -N autosuggest-backward-delete-char
zle -N autosuggest-accept-line
zle -N autosuggest-tab
zle -N autosuggest-suspend
zle -N autosuggest-accept-suggestion
autosuggest-restore-widgets

View File

@@ -1,40 +0,0 @@
#!/usr/bin/env zsh
zmodload zsh/net/socket
AUTOSUGGEST_SERVER_SCRIPT="${0:a:h}/completion-server.zsh"
autosuggest-ensure-server() {
setopt local_options no_hup
local server_dir="/tmp/zsh-autosuggest-$USER"
local pid_file="$server_dir/pid"
local socket_path="$server_dir/socket"
if [[ ! -d $server_dir || ! -r $pid_file ]] || ! kill -0 $(<$pid_file) &> /dev/null; then
if which setsid &> /dev/null; then
setsid zsh $AUTOSUGGEST_SERVER_SCRIPT $server_dir $pid_file $socket_path &!
else
zsh $AUTOSUGGEST_SERVER_SCRIPT $server_dir $pid_file $socket_path &!
fi
fi
autosuggest-server-connect
}
autosuggest-server-connect() {
unset ZLE_AUTOSUGGEST_CONNECTION
integer remaining_tries=10
while (( --remaining_tries )) && ! zsocket $socket_path &>/dev/null; do
sleep 0.3
done
[[ -z $REPLY ]] && return 1
ZLE_AUTOSUGGEST_CONNECTION=$REPLY
}
autosuggest-send-request() {
[[ -z $ZLE_AUTOSUGGEST_CONNECTION ]] && return 1
setopt local_options noglob
print -u $ZLE_AUTOSUGGEST_CONNECTION - $1 &> /dev/null || return 1
}

View File

@@ -1,121 +0,0 @@
# Based on:
# https://github.com/Valodim/zsh-capture-completion/blob/master/.zshrc
ZLE_DISABLE_AUTOSUGGEST=1
# no prompt!
PROMPT=
# load completion system
autoload compinit
compinit
# never run a command
bindkey '\C-m' .kill-buffer
bindkey '\C-j' .kill-buffer
bindkey '\C-i' complete-word
# send an emtpy line before completions are output
empty-line() {
print
# handler needs to reinsert itself after being called
compprefuncs+=empty-line
}
compprefuncs+=empty-line
# send a line with null-byte after completions are output
null-line() {
print $'\0'
# handler needs to reinsert itself after being called
comppostfuncs+=null-line
}
comppostfuncs+=null-line
zstyle ':completion:*' completer _complete
# never group stuff!
zstyle ':completion:*' list-grouped false
# don't insert tab when attempting completion on empty line
zstyle ':completion:*' insert-tab false
# no list separator, this saves some stripping later on
zstyle ':completion:*' list-separator ''
# dont use matchers
zstyle -d ':completion:*' matcher-list
# dont format
zstyle -d ':completion:*' format
# no color formatting
zstyle -d ':completion:*' list-colors
# we use zparseopts
zmodload zsh/zutil
# override compadd (this our hook)
compadd() {
# check if any of -O, -A or -D are given
if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then
# if that is the case, just delegate and leave
builtin compadd "$@"
return $?
fi
# be careful with namespacing here, we don't want to mess with stuff that
# should be passed to compadd!
typeset -a __hits __dscr __tmp
# do we have a description parameter?
# note we don't use zparseopts here because of combined option parameters
# with arguments like -default- confuse it.
if (( $@[(I)-d] )); then # kind of a hack, $+@[(r)-d] doesn't work because of line noise overload
# next param after -d
__tmp=${@[$[${@[(i)-d]}+1]]}
# description can be given as an array parameter name, or inline () array
if [[ $__tmp == \(* ]]; then
eval "__dscr=$__tmp"
else
__dscr=( "${(@P)__tmp}" )
fi
fi
# capture completions by injecting -A parameter into the compadd call.
# this takes care of matching for us.
builtin compadd -A __hits -D __dscr "$@"
# JESUS CHRIST IT TOOK ME FOREVER TO FIGURE OUT THIS OPTION WAS SET AND WAS MESSING WITH MY SHIT HERE
setopt localoptions norcexpandparam extendedglob
# extract prefixes and suffixes from compadd call. we can't do zsh's cool
# -r remove-func magic, but it's better than nothing.
typeset -A apre hpre hsuf asuf
zparseopts -E P:=apre p:=hpre S:=asuf s:=hsuf
# append / to directories? we are only emulating -f in a half-assed way
# here, but it's better than nothing.
integer dirsuf=0
# don't be fooled by -default- >.>
if [[ -z $hsuf && "${${@//-default-/}% -# *}" == *-[[:alnum:]]#f* ]]; then
dirsuf=1
fi
# just drop
[[ -n $__hits ]] || return
# this is the point where we have all matches in $__hits and all
# descriptions in $__dscr!
# display all matches
local dsuf dscr
for i in {1..$#__hits}; do
# add a dir suffix?
(( dirsuf )) && [[ -d $__hits[$i] ]] && dsuf=/ || dsuf=
# description to be displayed afterwards
# (( $#__dscr >= $i )) && dscr=" -- ${${__dscr[$i]}##$__hits[$i] #}" || dscr=
print - $'\1'$IPREFIX$apre$hpre$__hits[$i]$dsuf$hsuf$asuf$dscr
done
unset __hits __dscr __tmp
}
# signal the daemon we are ready for input
print $'\0'

View File

@@ -1,128 +0,0 @@
#!/usr/bin/env zsh
# Based on:
# https://github.com/Valodim/zsh-capture-completion/blob/master/capture.zsh
# read everything until a line containing the byte 0 is found
read-to-null() {
while zpty -r z chunk; do
[[ $chunk == *$'\0'* ]] && break
[[ $chunk != $'\1'* ]] && continue # ignore what doesnt start with '1'
print -n - ${chunk:1}
done
}
accept-connection() {
zsocket -a $server
fds[$REPLY]=1
print "connection accepted, fd: $REPLY" >&2
}
handle-request() {
local connection=$1 current line
integer read_something=0
print "request received from fd $connection"
while read -u $connection prefix &> /dev/null; do
read_something=1
# send the prefix to be completed followed by a TAB to force
# completion
zpty -w -n z $prefix$'\t'
zpty -r z chunk &> /dev/null # read empty line before completions
current=''
# read completions one by one, storing the longest match
read-to-null | while IFS= read -r line; do
(( $#line > $#current )) && current=$line
done
# send the longest completion back to the client, strip the last
# non-printable character
if (( $#current )); then
print -u $connection - $prefix$'\2'${current:0:-1}
else
print -u $connection ''
fi
# clear input buffer
zpty -w z $'\n'
break # handle more requests/return to zselect
done
if ! (( read_something )); then
print "connection with fd $connection closed" >&2
unset fds[$connection]
exec {connection}>&- # free the file descriptor
fi
}
if [[ -n $ZLE_AUTOSUGGEST_SERVER_LOG ]]; then
exec >> "$HOME/.autosuggest-server.log"
else
exec > /dev/null
fi
if [[ -n $ZLE_AUTOSUGGEST_SERVER_LOG_ERRORS ]]; then
exec 2>> "$HOME/.autosuggest-server-errors.log"
else
exec 2> /dev/null
fi
exec < /dev/null
zmodload zsh/zpty
zmodload zsh/zselect
zmodload zsh/net/socket
setopt noglob
print "autosuggestion server started, pid: $$" >&2
# Start an interactive zsh connected to a zpty
zpty z ZLE_DISABLE_AUTOSUGGEST=1 zsh -i
print 'interactive shell started'
# Source the init script
zpty -w z "source '${0:a:h}/completion-server-init.zsh'"
# wait for ok from shell
read-to-null &> /dev/null
print 'interactive shell ready'
# listen on a socket for completion requests
server_dir=$1
pid_file=$2
socket_path=$3
cleanup() {
print 'removing socket and pid file...'
rm -f $socket_path $pid_file
print "autosuggestion server stopped, pid: $$"
exit
}
trap cleanup TERM INT HUP EXIT
mkdir -m 700 $server_dir
while ! zsocket -l $socket_path; do
if [[ ! -r $pid_file ]] || ! kill -0 $(<$pid_file); then
rm -f $socket_path
else
exit 1
fi
print "will retry listening on '$socket_path'"
done
server=$REPLY
print "server listening on '$socket_path'"
print $$ > $pid_file
typeset -A fds ready
fds[$server]=1
while zselect -A ready ${(k)fds}; do
queue=(${(k)ready})
for fd in $queue; do
if (( fd == server )); then
accept-connection
else
handle-request $fd
fi
done
done

44
install
View File

@@ -1,44 +0,0 @@
#!/bin/bash
# Install script for zsh-autocomplete
config="$HOME/.zshrc"
for config in "$HOME/.zshrc" "$ZDOTDIR/.zshrc" "$1"; do
echo $config
#first checks if ~/.zshrc file exists and is readable
if [ -r "$config" ]; then
break
elif [ "$config" = "$1" ]; then
echo "\nError: Please specify as first argument the file in which to load zsh-autosuggestions (usually ~/.zshrc)!\n"
exit 1
fi
done
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
# appends the string to $config (usually ~/.zshrc) file
cat >> "$config" <<-EOF
# Setup zsh-autosuggestions
source $DIR/autosuggestions.zsh
# Enable autosuggestions automatically
zle-line-init() {
zle autosuggest-start
}
zle -N zle-line-init
# use ctrl+t to toggle autosuggestions(hopefully this wont be needed as
# zsh-autosuggestions is designed to be unobtrusive)
bindkey '^T' autosuggest-toggle
EOF
echo "\nSetup completed successfully!\n"
exit 0

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

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

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

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

@@ -0,0 +1,39 @@
describe 'a wrapped widget' do
let(:widget) { 'backward-delete-char' }
context 'initialized before sourcing the plugin' do
let(:before_sourcing) do
-> do
session.
run_command("_orig_#{widget}() { zle .#{widget} }").
run_command("zle -N orig-#{widget} _orig_#{widget}").
run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }").
run_command("zle -N #{widget} #{widget}-magic")
end
end
it 'executes the custom behavior and the built-in behavior' do
with_history('foobar', 'foodar') do
session.send_string('food').send_keys('C-h')
wait_for { session.content }.to eq('foobar')
end
end
end
context 'initialized after sourcing the plugin' do
before do
session.
run_command("zle -N orig-#{widget} ${widgets[#{widget}]#*:}").
run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }").
run_command("zle -N #{widget} #{widget}-magic").
clear_screen
end
it 'executes the custom behavior and the built-in behavior' do
with_history('foobar', 'foodar') do
session.send_string('food').send_keys('C-h')
wait_for { session.content }.to eq('foobar')
end
end
end
end

View File

@@ -0,0 +1,24 @@
describe 'using `zle -U`' do
let(:before_sourcing) do
-> do
session.
run_command('_zsh_autosuggest_strategy_test() { sleep 1; _zsh_autosuggest_strategy_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

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

13
spec/multi_line_spec.rb Normal file
View File

@@ -0,0 +1,13 @@
describe 'a multi-line suggestion' do
it 'should be displayed on multiple lines' do
with_history(-> {
session.send_string('echo "')
session.send_keys('enter')
session.send_string('"')
session.send_keys('enter')
}) do
session.send_keys('e')
wait_for { session.content }.to eq("echo \"\n\"")
end
end
end

View File

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

View File

@@ -0,0 +1,30 @@
describe 'a suggestion' do
let(:term_opts) { { width: 200 } }
let(:long_command) { "echo #{'a' * 100}" }
around do |example|
with_history(long_command) { example.run }
end
it 'is provided for any buffer length' do
session.send_string(long_command[0...-1])
wait_for { session.content }.to eq(long_command)
end
context 'when ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE is specified' do
let(:buffer_max_size) { 10 }
let(:options) { ["ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=#{buffer_max_size}"] }
it 'is provided when the buffer is shorter than the specified length' do
session.send_string(long_command[0...(buffer_max_size - 1)])
wait_for { session.content }.to eq(long_command)
end
it 'is provided when the buffer is equal to the specified length' do
session.send_string(long_command[0...(buffer_max_size)])
wait_for { session.content }.to eq(long_command)
end
it 'is not provided when the buffer is longer than the specified length'
end
end

View File

@@ -0,0 +1,7 @@
describe 'a displayed suggestion' do
it 'is shown in the default style'
describe 'when ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE is set to a zle_highlight string' do
it 'is shown in the specified style'
end
end

View File

@@ -0,0 +1,7 @@
describe 'an original zle widget' do
context 'is accessible with the default prefix'
context 'when ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX is set' do
it 'is accessible with the specified prefix'
end
end

View File

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

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

View File

@@ -0,0 +1,97 @@
describe 'a zle widget' do
let(:widget) { 'my-widget' }
let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } }
context 'when added to ZSH_AUTOSUGGEST_ACCEPT_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})"] }
it 'accepts the suggestion when invoked' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to eq('echo hello')
end
end
end
context 'when added to ZSH_AUTOSUGGEST_CLEAR_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(#{widget})"] }
it 'clears the suggestion when invoked' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content }.to eq('e')
end
end
end
context 'when added to ZSH_AUTOSUGGEST_EXECUTE_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_EXECUTE_WIDGETS+=(#{widget})"] }
it 'executes the suggestion when invoked' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content }.to end_with("\nhello")
end
end
end
context 'when added to ZSH_AUTOSUGGEST_IGNORE_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(#{widget})"] }
it 'should not be wrapped with an autosuggest widget' do
session.run_command("echo $widgets[#{widget}]")
wait_for { session.content }.to end_with("\nuser:#{widget}")
end
end
context 'that moves the cursor forward' do
before { session.run_command("#{widget}() { zle forward-char }") }
context 'when added to ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(#{widget})"] }
it 'accepts the suggestion as far as the cursor is moved when invoked' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to start_with('echo hello')
session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to match(/\Aec\e\[[0-9]+mho hello/)
end
end
end
end
context 'that modifies the buffer' do
before { session.run_command("#{widget}() { BUFFER=\"foo\" }") }
context 'when not added to any of the widget lists' do
it 'modifies the buffer and fetches a new suggestion' do
with_history('foobar') do
session.send_keys('C-b')
wait_for { session.content }.to eq('foobar')
end
end
end
end
end
describe 'a modification to the widget lists' do
let(:widget) { 'my-widget' }
let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } }
before { session.run_command("ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})") }
it 'takes effect on the next cmd line' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to eq('echo hello')
end
end
end

52
spec/spec_helper.rb Normal file
View File

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

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

View File

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

@@ -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

99
spec/terminal_session.rb Normal file
View File

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

@@ -0,0 +1,19 @@
describe 'the `autosuggest-disable` widget' do
before do
session.run_command('bindkey ^B autosuggest-disable')
end
it 'disables suggestions and clears the suggestion' do
with_history('echo hello') do
session.send_string('echo')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content }.to eq('echo')
session.send_string(' h')
sleep 1
expect(session.content).to eq('echo h')
end
end
end

View File

@@ -0,0 +1,42 @@
describe 'the `autosuggest-enable` widget' do
before do
session.
run_command('typeset -g _ZSH_AUTOSUGGEST_DISABLED').
run_command('bindkey ^B autosuggest-enable')
end
it 'enables suggestions and fetches a suggestion' do
with_history('echo hello') do
session.send_string('e')
sleep 1
expect(session.content).to eq('e')
session.send_keys('C-b')
session.send_string('c')
wait_for { session.content }.to eq('echo hello')
end
end
context 'invoked on an empty buffer' do
it 'does not fetch a suggestion' do
with_history('echo hello') do
session.send_keys('C-b')
sleep 1
expect(session.content).to eq('')
end
end
end
context 'invoked on a non-empty buffer' do
it 'fetches a suggestion' do
with_history('echo hello') do
session.send_string('e')
sleep 1
expect(session.content).to eq('e')
session.send_keys('C-b')
wait_for { session.content }.to eq('echo hello')
end
end
end
end

View File

@@ -0,0 +1,24 @@
describe 'the `autosuggest-fetch` widget' do
context 'when suggestions are disabled' do
before do
session.
run_command('bindkey ^B autosuggest-disable').
run_command('bindkey ^F autosuggest-fetch').
send_keys('C-b')
end
it 'will fetch and display a suggestion' do
with_history('echo hello') do
session.send_string('echo h')
sleep 1
expect(session.content).to eq('echo h')
session.send_keys('C-f')
wait_for { session.content }.to eq('echo hello')
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
end
end
end
end

View File

@@ -0,0 +1,26 @@
describe 'the `autosuggest-toggle` widget' do
before do
session.run_command('bindkey ^B autosuggest-toggle')
end
it 'toggles suggestions' do
with_history('echo world', 'echo hello') do
session.send_string('echo')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
wait_for { session.content }.to eq('echo')
session.send_string(' h')
sleep 1
expect(session.content).to eq('echo h')
session.send_keys('C-b')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-h')
session.send_string('w')
wait_for { session.content }.to eq('echo world')
end
end
end

109
src/async.zsh Normal file
View File

@@ -0,0 +1,109 @@
#--------------------------------------------------------------------#
# Async #
#--------------------------------------------------------------------#
# Zpty process is spawned running this function
_zsh_autosuggest_async_server() {
emulate -R zsh
# There is a bug in zpty module (fixed in zsh/master) by which a
# zpty that exits will kill all zpty processes that were forked
# before it. Here we set up a zsh exit hook to SIGKILL the zpty
# process immediately, before it has a chance to kill any other
# zpty processes.
zshexit() {
kill -KILL $$
sleep 1 # Block for long enough for the signal to come through
}
# 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
}

120
src/bind.zsh Normal file
View File

@@ -0,0 +1,120 @@
#--------------------------------------------------------------------#
# 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
user:_zsh_autosuggest_(bound|orig)_*);;
# User-defined widget
user:*)
_zsh_autosuggest_incr_bind_count $widget
zle -N $prefix${bind_count}-$widget ${widgets[$widget]#*:}
;;
# Built-in widget
builtin)
_zsh_autosuggest_incr_bind_count $widget
eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }"
zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget
;;
# Completion widget
completion:*)
_zsh_autosuggest_incr_bind_count $widget
eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
;;
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} \$@
}"
# Create the bound widget
zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$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
)
# 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
_zsh_autosuggest_bind_widget $widget clear
elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget accept
elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget execute
elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget partial_accept
else
# Assume any unspecified widget might modify the buffer
_zsh_autosuggest_bind_widget $widget modify
fi
done
}
# 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
local original_widget_name="$1"
shift
if (( ${+widgets[$original_widget_name]} )); then
zle $original_widget_name -- $@
fi
}

73
src/config.zsh Normal file
View File

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

19
src/features.zsh Normal file
View File

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

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
}

26
src/highlight.zsh Normal file
View File

@@ -0,0 +1,26 @@
#--------------------------------------------------------------------#
# Highlighting #
#--------------------------------------------------------------------#
# If there was a highlight, remove it
_zsh_autosuggest_highlight_reset() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if [[ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]]; then
region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}")
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi
}
# If there's a suggestion, highlight it
_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"
region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT")
else
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi
}

10
src/setup.zsh Normal file
View File

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

24
src/start.zsh Normal file
View File

@@ -0,0 +1,24 @@
#--------------------------------------------------------------------#
# Start #
#--------------------------------------------------------------------#
# Start the autosuggestion widgets
_zsh_autosuggest_start() {
add-zsh-hook -d precmd _zsh_autosuggest_start
_zsh_autosuggest_bind_widgets
# Re-bind widgets on every precmd to ensure we wrap other wrappers.
# Specifically, highlighting breaks if our widgets are wrapped by
# zsh-syntax-highlighting widgets. This also allows modifications
# to the widget list variables to take effect on the next precmd.
add-zsh-hook precmd _zsh_autosuggest_bind_widgets
if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
_zsh_autosuggest_async_start
fi
}
# Start the autosuggestion widgets on the next precmd
add-zsh-hook precmd _zsh_autosuggest_start

View File

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

View File

@@ -0,0 +1,59 @@
#--------------------------------------------------------------------#
# Match Previous Command Suggestion Strategy #
#--------------------------------------------------------------------#
# Suggests the most recent history item that matches the given
# prefix and whose preceding history item also matches the most
# recently executed command.
#
# For example, suppose your history has the following entries:
# - pwd
# - ls foo
# - ls bar
# - pwd
#
# Given the history list above, when you type 'ls', the suggestion
# 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}"
# Get all history event numbers that correspond to history
# entries that match pattern $prefix*
local history_match_keys
history_match_keys=(${(k)history[(R)$prefix*]})
# By default we use the first history number (most recent history entry)
local histkey="${history_match_keys[1]}"
# Get the previously executed command
local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")"
# Iterate up to the first 200 history event numbers that match $prefix
for key in "${(@)history_match_keys[1,200]}"; do
# Stop if we ran out of history
[[ $key -gt 1 ]] || break
# See if the history entry preceding the suggestion matches the
# previous command, and use it if it does
if [[ "${history[$((key - 1))]}" == "$prev_cmd" ]]; then
histkey="$key"
break
fi
done
# Give back the matched history entry
typeset -g suggestion="$history[$histkey]"
}

11
src/util.zsh Normal file
View File

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

217
src/widgets.zsh Normal file
View File

@@ -0,0 +1,217 @@
#--------------------------------------------------------------------#
# 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
unset POSTDISPLAY
_zsh_autosuggest_invoke_original_widget $@
}
# Modify the buffer and get a new suggestion
_zsh_autosuggest_modify() {
emulate -L zsh
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
unset POSTDISPLAY
# Original widget may modify the buffer
_zsh_autosuggest_invoke_original_widget $@
retval=$?
# Don't fetch a new suggestion if there's more input to be read immediately
if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
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
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
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
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
# Add the suggestion to the buffer
BUFFER="$BUFFER$POSTDISPLAY"
# Remove the suggestion
unset POSTDISPLAY
# Move the cursor to the end of the buffer
CURSOR=${#BUFFER}
fi
_zsh_autosuggest_invoke_original_widget $@
}
# Accept the entire suggestion and execute it
_zsh_autosuggest_execute() {
# Add the suggestion to the buffer
BUFFER="$BUFFER$POSTDISPLAY"
# Remove the suggestion
unset POSTDISPLAY
# Call the original `accept-line` to handle syntax highlighting or
# other potential custom behavior
_zsh_autosuggest_invoke_original_widget "accept-line"
}
# Partially accept the suggestion
_zsh_autosuggest_partial_accept() {
local -i retval cursor_loc
# Save the contents of the buffer so we can restore later if needed
local original_buffer="$BUFFER"
# Temporarily accept the suggestion.
BUFFER="$BUFFER$POSTDISPLAY"
# Original widget moves the cursor
_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
# Set POSTDISPLAY to text right of the cursor
POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}"
# Clip the buffer at the cursor
BUFFER="${BUFFER[1,$cursor_loc]}"
else
# Restore the original buffer
BUFFER="$original_buffer"
fi
return $retval
}
for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
eval "_zsh_autosuggest_widget_$action() {
local -i retval
_zsh_autosuggest_highlight_reset
_zsh_autosuggest_$action \$@
retval=\$?
_zsh_autosuggest_highlight_apply
zle -R
return \$retval
}"
done
zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest
zle -N autosuggest-accept _zsh_autosuggest_widget_accept
zle -N autosuggest-clear _zsh_autosuggest_widget_clear
zle -N autosuggest-execute _zsh_autosuggest_widget_execute
zle -N autosuggest-enable _zsh_autosuggest_widget_enable
zle -N autosuggest-disable _zsh_autosuggest_widget_disable
zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle

View File

@@ -1 +0,0 @@
autosuggestions.zsh

View File

@@ -0,0 +1 @@
source ${0:A:h}/zsh-autosuggestions.zsh

742
zsh-autosuggestions.zsh Normal file
View File

@@ -0,0 +1,742 @@
# Fish-like fast/unobtrusive autosuggestions for zsh.
# https://github.com/zsh-users/zsh-autosuggestions
# v0.5.0
# Copyright (c) 2013 Thiago de Arruda
# Copyright (c) 2016-2018 Eric Freese
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# 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 #
#--------------------------------------------------------------------#
# Color to use when highlighting suggestion
# Uses format of `region_highlight`
# More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets
: ${ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'}
# Prefix to use when saving original versions of bound widgets
: ${ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-}
# Strategies to use to fetch a suggestion
# Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && ZSH_AUTOSUGGEST_STRATEGY=(history)
# Widgets that clear the suggestion
(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward
history-search-backward
history-beginning-search-forward
history-beginning-search-backward
history-substring-search-up
history-substring-search-down
up-line-or-beginning-search
down-line-or-beginning-search
up-line-or-history
down-line-or-history
accept-line
)
# Widgets that accept the entire suggestion
(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char
end-of-line
vi-forward-char
vi-end-of-line
vi-add-eol
)
# Widgets that accept the entire suggestion and execute it
(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
)
# Widgets that accept the suggestion as far as the cursor moves
(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word
emacs-forward-word
vi-forward-word
vi-forward-word-end
vi-forward-blank-word
vi-forward-blank-word-end
vi-find-next-char
vi-find-next-char-skip
)
# Widgets that should be ignored (globbing supported but must be escaped)
(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
orig-\*
beep
run-help
set-local-history
which-command
yank
yank-pop
)
# Max size of buffer to trigger autosuggestion. Leave null for no upper bound.
: ${ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=}
# Pty name for calculating autosuggestions asynchronously
: ${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
fi
zpty -d zsh_autosuggest_feature_detect
}
#--------------------------------------------------------------------#
# 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
user:_zsh_autosuggest_(bound|orig)_*);;
# User-defined widget
user:*)
_zsh_autosuggest_incr_bind_count $widget
zle -N $prefix${bind_count}-$widget ${widgets[$widget]#*:}
;;
# Built-in widget
builtin)
_zsh_autosuggest_incr_bind_count $widget
eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }"
zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget
;;
# Completion widget
completion:*)
_zsh_autosuggest_incr_bind_count $widget
eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}"
;;
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} \$@
}"
# Create the bound widget
zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$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
)
# 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
_zsh_autosuggest_bind_widget $widget clear
elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget accept
elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget execute
elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then
_zsh_autosuggest_bind_widget $widget partial_accept
else
# Assume any unspecified widget might modify the buffer
_zsh_autosuggest_bind_widget $widget modify
fi
done
}
# 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
local original_widget_name="$1"
shift
if (( ${+widgets[$original_widget_name]} )); then
zle $original_widget_name -- $@
fi
}
#--------------------------------------------------------------------#
# Highlighting #
#--------------------------------------------------------------------#
# If there was a highlight, remove it
_zsh_autosuggest_highlight_reset() {
typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
if [[ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]]; then
region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}")
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi
}
# If there's a suggestion, highlight it
_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"
region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT")
else
unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT
fi
}
#--------------------------------------------------------------------#
# 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
unset POSTDISPLAY
_zsh_autosuggest_invoke_original_widget $@
}
# Modify the buffer and get a new suggestion
_zsh_autosuggest_modify() {
emulate -L zsh
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
unset POSTDISPLAY
# Original widget may modify the buffer
_zsh_autosuggest_invoke_original_widget $@
retval=$?
# Don't fetch a new suggestion if there's more input to be read immediately
if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
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
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
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
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
# Add the suggestion to the buffer
BUFFER="$BUFFER$POSTDISPLAY"
# Remove the suggestion
unset POSTDISPLAY
# Move the cursor to the end of the buffer
CURSOR=${#BUFFER}
fi
_zsh_autosuggest_invoke_original_widget $@
}
# Accept the entire suggestion and execute it
_zsh_autosuggest_execute() {
# Add the suggestion to the buffer
BUFFER="$BUFFER$POSTDISPLAY"
# Remove the suggestion
unset POSTDISPLAY
# Call the original `accept-line` to handle syntax highlighting or
# other potential custom behavior
_zsh_autosuggest_invoke_original_widget "accept-line"
}
# Partially accept the suggestion
_zsh_autosuggest_partial_accept() {
local -i retval cursor_loc
# Save the contents of the buffer so we can restore later if needed
local original_buffer="$BUFFER"
# Temporarily accept the suggestion.
BUFFER="$BUFFER$POSTDISPLAY"
# Original widget moves the cursor
_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
# Set POSTDISPLAY to text right of the cursor
POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}"
# Clip the buffer at the cursor
BUFFER="${BUFFER[1,$cursor_loc]}"
else
# Restore the original buffer
BUFFER="$original_buffer"
fi
return $retval
}
for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
eval "_zsh_autosuggest_widget_$action() {
local -i retval
_zsh_autosuggest_highlight_reset
_zsh_autosuggest_$action \$@
retval=\$?
_zsh_autosuggest_highlight_apply
zle -R
return \$retval
}"
done
zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch
zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest
zle -N autosuggest-accept _zsh_autosuggest_widget_accept
zle -N autosuggest-clear _zsh_autosuggest_widget_clear
zle -N autosuggest-execute _zsh_autosuggest_widget_execute
zle -N autosuggest-enable _zsh_autosuggest_widget_enable
zle -N autosuggest-disable _zsh_autosuggest_widget_disable
zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
#--------------------------------------------------------------------#
# 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}*]}"
}
#--------------------------------------------------------------------#
# Match Previous Command Suggestion Strategy #
#--------------------------------------------------------------------#
# Suggests the most recent history item that matches the given
# prefix and whose preceding history item also matches the most
# recently executed command.
#
# For example, suppose your history has the following entries:
# - pwd
# - ls foo
# - ls bar
# - pwd
#
# Given the history list above, when you type 'ls', the suggestion
# 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}"
# Get all history event numbers that correspond to history
# entries that match pattern $prefix*
local history_match_keys
history_match_keys=(${(k)history[(R)$prefix*]})
# By default we use the first history number (most recent history entry)
local histkey="${history_match_keys[1]}"
# Get the previously executed command
local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")"
# Iterate up to the first 200 history event numbers that match $prefix
for key in "${(@)history_match_keys[1,200]}"; do
# Stop if we ran out of history
[[ $key -gt 1 ]] || break
# See if the history entry preceding the suggestion matches the
# previous command, and use it if it does
if [[ "${history[$((key - 1))]}" == "$prev_cmd" ]]; then
histkey="$key"
break
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
# 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
}
#--------------------------------------------------------------------#
# Start #
#--------------------------------------------------------------------#
# Start the autosuggestion widgets
_zsh_autosuggest_start() {
add-zsh-hook -d precmd _zsh_autosuggest_start
_zsh_autosuggest_bind_widgets
# Re-bind widgets on every precmd to ensure we wrap other wrappers.
# Specifically, highlighting breaks if our widgets are wrapped by
# zsh-syntax-highlighting widgets. This also allows modifications
# to the widget list variables to take effect on the next precmd.
add-zsh-hook precmd _zsh_autosuggest_bind_widgets
if [[ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]]; then
_zsh_autosuggest_async_start
fi
}
# Start the autosuggestion widgets on the next precmd
add-zsh-hook precmd _zsh_autosuggest_start