Compare commits

...

54 Commits

Author SHA1 Message Date
Eric Freese
b27277e7ff wip playing with different strategies when for different patterns 2019-06-29 13:49:16 -06:00
Eric Freese
146020d9b2 Merge pull request #454 from zsh-users/develop
v0.6.3
2019-06-24 09:13:46 -06:00
Eric Freese
0636a39b51 0.6.3 2019-06-24 09:10:42 -06:00
Eric Freese
6769c941ba Update changelog for v0.6.3 release 2019-06-24 09:10:42 -06:00
Eric Freese
371d6441c0 Merge pull request #453 from zsh-users/fixes/move-cursor-on-accept
Fix moving cursor to end of buffer when suggestion accepted
2019-06-24 07:16:35 -06:00
Eric Freese
78e4379711 Fix moving cursor to end of buffer when suggestion accepted
$max_cursor_pos in this case was not the correct value to use. It was
calculated based on the old length of the $BUFFER. After the suggestion
is accepted, we need to recalculate the new max cursor length and use it
to set the $CURSOR.

Fixes issue #452. Follow-up to issue #302 (PR #450).
2019-06-23 12:37:08 -06:00
Eric Freese
c80605595c Merge pull request #451 from zsh-users/develop
v0.6.2
2019-06-22 16:57:19 -06:00
Eric Freese
cb52adf429 v0.6.2 2019-06-22 16:54:32 -06:00
Eric Freese
cdf6be4f06 Update readme description to reference new completion strategy 2019-06-22 16:54:03 -06:00
Eric Freese
66a6de3fb5 Update changelog for v0.6.2 release 2019-06-22 16:52:42 -06:00
Eric Freese
2498dbab75 Merge pull request #450 from zsh-users/fixes/vi-delete
Fix deleting last character in vi mode (#302)
2019-06-22 16:42:52 -06:00
Eric Freese
676aebdf44 Fix deleting last character in vi mode (#302)
Typing `d` and then `l` runs `vi-delete` and then `vi-forward-char`.  However,
by default, `vi-forward-char` is configured to accept the suggestion. So in
that case, the suggestion was being accepted and the cursor set to the end of
the buffer before the deletion was run.

The reason the user doesn't see the suggestion accepted is that `vi-delete`
doesn't finish until the movement widget is run, so we're already inside of a
`modify` when `accept` is called. `modify` unsets `POSTDISPLAY` before calling
the original widget so when we get to the accept function, `POSTDISPLAY` is
empty and thus accepting the suggestion is a no-op.

The fix is to make sure we reset the cursor to the correct place before running
the original widget.

We skip the test for versions of zsh below 5.0.8 since there was a bug in
earlier versions where deleting the last char did not work.

See http://www.zsh.org/mla/workers/2014/msg01316.html
2019-06-22 16:34:42 -06:00
Eric Freese
ec43fcfccf Merge pull request #449 from Warkst/i416
Possible fix for #416
2019-06-22 10:56:34 -06:00
Eric Freese
f5236c4351 Merge pull request #448 from ondrej-fabry/patch-1
Update INSTALL.md
2019-06-22 10:50:17 -06:00
Eric Freese
d27983b7ca Merge pull request #447 from zsh-users/features/without-system-module
Degrade gracefully on systems missing zsh/system module
2019-06-22 10:46:26 -06:00
Robin De Mol
d14b17fb09 Possible fix for #416
* Added a note for iTerm2 users under readme/configuration/highlight
style.

* Changed the link to the issue list under troubleshooting so that the
default filters (only open issues) are not applied.
2019-06-20 09:32:47 +02:00
Ondrej Fabry
b24b607fbf Update INSTALL.md
- minor formatting change for better readability
2019-06-19 23:30:36 +02:00
Eric Freese
a437544cc5 Degrade gracefully on systems missing zsh/system module
When using async mode, stale background processes will not be cancelled
when a new one starts. This shouldn't cause any real issues since the
processes should eventually finish and be cleaned up anyway, and
removing the handler with `zle -F` means that stale suggestions should
never be shown.
2019-06-19 13:41:43 -06:00
Eric Freese
112dd3e3c7 Merge pull request #446 from zsh-users/develop
v0.6.1
2019-06-18 11:11:59 -06:00
Eric Freese
8a812bdfd2 v0.6.1 2019-06-18 11:04:24 -06:00
Eric Freese
f178efb847 Update changelog for v0.6.1 release 2019-06-18 11:04:04 -06:00
Eric Freese
48ffc1bf92 Merge pull request #443 from zsh-users/fixes/alias-parse-error
Prefix custom `_complete` implementation with "function" keyword
2019-06-18 07:50:07 -06:00
Eric Freese
adb02c44a2 Prefix custom _complete implementation with "function" keyword
For some reason, when `_complete` is aliased before sourcing the plugin,
zsh was blowing up with a parse error. Adding "function" keyword makes
it parse successfully.

See similar issue: https://github.com/robbyrussell/oh-my-zsh/issues/6723#issuecomment-381220834

Fixes GitHub #442
2019-06-17 22:04:31 -06:00
Eric Freese
3654b83ec0 Update license copyright year in built plugin file
Should have been committed with 19c976f
2019-06-17 21:58:31 -06:00
Eric Freese
b3b34eab49 Merge pull request #440 from zsh-users/develop
v0.6.0
2019-06-15 21:46:24 -06:00
Eric Freese
19c976f62a Update license copyright year 2019-06-15 21:40:39 -06:00
Eric Freese
3250778ce8 v0.6.0 2019-06-15 21:39:57 -06:00
Eric Freese
43ba841742 Update changelog for v0.6.0 release 2019-06-15 21:39:10 -06:00
Eric Freese
86f64c30ca Merge pull request #420 from zsh-users/fixes/completion-newline-issues
Fixes/completion newline issues
2019-06-15 21:24:01 -06:00
Eric Freese
f543ba08c3 Fix cr/lf handling in completion strategy 2019-06-15 21:17:58 -06:00
Eric Freese
68343c8de4 Allow suggestions to suggest trailing newlines
Command substitution via $() trims trailing newlines so the old approach
to reading everything from the fd was preventing suggestions from ending
with newlines.

Found the read solution here: https://stackoverflow.com/a/15184414/154703
2019-06-15 21:17:58 -06:00
Eric Freese
e263845bed Add an extra completion to better exercise choosing first 2019-06-15 21:17:58 -06:00
Eric Freese
d94475ca1b Simplify logic to extract suggestion from between null bytes
Just remove up to and including the first null byte and after and
including the last null byte.

I also looked into using `${${(0)line}[2]}`, but it fails when `$line`
starts with a null byte, since the first split string will be empty and
thus not included in the resulting array.
2019-06-15 21:17:58 -06:00
Eric Freese
c04e015d13 Add to explanation of extra stuff after zpty -r 2019-06-15 21:17:58 -06:00
Eric Freese
e9ce05c4c1 Ensure that kill succeeds even in older buggy versions of zsh
See https://unix.stackexchange.com/a/477647/156673
2019-06-15 21:17:58 -06:00
Eric Freese
fdf4502c5c We need to autoload is-at-least for it to be available
This error was hidden inside the zpty
2019-06-15 21:17:58 -06:00
Eric Freese
7e048c3f53 Fix error that was hidden in zpty
Without the quotes, this was blowing up with:
  _zsh_autosuggest_capture_postcompletion:unset:5: not enough arguments
2019-06-15 21:17:58 -06:00
Eric Freese
2b05f5ed2d Add a comment explaining post-completion hook 2019-06-15 21:17:58 -06:00
Eric Freese
5339d2703d Merge pull request #432 from zsh-users/features/wrap-zle-line-init
Wrap zle-line-init to support fetching suggestions on line init
2019-06-14 21:04:02 -06:00
Eric Freese
0d3bbaf8e6 Remove hard-coded ignore of zle-* widgets and add to default config
Use case suggested by @romkatv uses zle-line-init to restore buffer
after running a widget to cd up one level (GitHub #431).

As far as I can tell, the ignoring of zle-line-* was added in commit
9788c2e to support some deprecation warnings that were removed some time
ago.

The pattern was then widened in commit 0c940e7 to zle-* to fix problems
encountered when wrapping zle-isearch-update.

This commit removes the hard coded ignore of all zle-* widgets and adds
zle-* to the default list of widgets to be ignored. Users who want the
plugin to wrap zle-line-init or zle-line-finish can override the
default.
2019-06-14 16:38:28 -06:00
Eric Freese
848aa741f4 Revert "Merge pull request #439 from zsh-users/fixes/copy-earlier-word"
This reverts commit 1c3e3fd939, reversing
changes made to 733abd4af0.

This PR should have been merged to develop instead of master.
2019-06-14 13:21:27 -06:00
Henré Botha
15bcfd7126 Don't fetch suggestions after copy-earlier-word
Like {up,down}-line-or-beginning-search, this widget relies on
`$LASTWIDGET` being set to function correctly on subsequent invocations.

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.
2019-06-14 13:19:07 -06:00
Eric Freese
9ceeacc797 Try to make async ^C spec less flaky
Was intermittently failing, sleep for a little longer to increase
chances of false negatives.
2019-06-14 13:19:07 -06:00
Eric Freese
1c3e3fd939 Merge pull request #439 from zsh-users/fixes/copy-earlier-word
Don't fetch suggestions after copy-earlier-word
2019-06-14 13:17:34 -06:00
Henré Botha
527fd17ffc Don't fetch suggestions after copy-earlier-word
Like {up,down}-line-or-beginning-search, this widget relies on
`$LASTWIDGET` being set to function correctly on subsequent invocations.

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.
2019-06-14 12:51:27 -06:00
Eric Freese
1912726460 Try to make async ^C spec less flaky
Was intermittently failing, sleep for a little longer to increase
chances of false negatives.
2019-06-14 12:50:44 -06:00
Eric Freese
de266574d3 Merge pull request #434 from zsh-users/meta/improve-highlight-style-docs
Improve documentation for configuring highlight style
2019-05-29 01:40:20 -06:00
Eric Freese
48d04447cc Merge pull request #433 from zsh-users/fixes/ignore-empty-max-size
Allow setting max size to empty string (GitHub #422)
2019-05-29 01:39:24 -06:00
Eric Freese
b714f6667c Improve documentation for configuring highlight style
Supersedes PR #430 and gives additional info.

https://stackoverflow.com/questions/47310537/how-to-change-zsh-autosuggestions-color
2019-05-27 14:56:19 -06:00
Eric Freese
474c577f3c Allow setting max size to empty string (GitHub #422)
Some people have gotten used to setting ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE
to an empty string rather than leaving it unset. This lets them continue
to do that without getting any warnings (e.g. "bad math expression:
operand expected at end of string").
2019-05-27 14:18:21 -06:00
Eric Freese
e9d8231ba7 Merge pull request #401 from zsh-users/features/completion-suggestions
Features/completion suggestions
2019-04-15 12:55:35 -06:00
Eric Freese
ce6ee94f30 Add spec to demonstrate bracketed-paste-magic bug workaround
Add `bracketed-paste` to list of widgets that clear the suggestion as a
workaround for GitHub #351
2019-04-15 12:47:21 -06:00
Eric Freese
e7c7efe2e2 Set options immediately after sourcing the plugin before next precmd
So that you can access defaults to e.g. add elements to an existing
array.
2019-04-15 12:46:21 -06:00
Eric Freese
c1910348c7 Implement completion suggestion strategy (#111)
Based on https://github.com/Valodim/zsh-capture-completion

`zpty -r` with a pattern seems to have some funky behavior on older
versions, giving unpredictable results

Don't use `-s` option to `zmodload`. It is not available in zsh versions
older than 5.3

If running in sync mode and a completion takes a long time, the user can
^C out of it. We need to use `always` in the strategy function or the
pty will not be destroyed in this case and the next time we go to create
it, it will fail, making the shell unusable.

User can have many different completion styles set that will modify what
they've already typed. These styles will result in suggestions that
don't match what the user has already typed. We try our best to unset
some of the more problematic ones, but add some code to fetch to
invalidate suggestions that don't match what the user's already typed.
2019-04-11 09:53:55 -06:00
23 changed files with 602 additions and 96 deletions

View File

@@ -1,5 +1,22 @@
# Changelog # Changelog
## v0.6.3
- Fixed bug moving cursor to end of buffer after accepting suggestion (#453)
## v0.6.2
- Fixed bug deleting the last character in the buffer in vi mode (#450)
- Degrade gracefully when user doesn't have `zsh/system` module installed (#447)
## v0.6.1
- Fixed bug occurring when `_complete` had been aliased (#443)
## v0.6.0
- Added `completion` suggestion strategy powered by completion system (#111)
- Allow setting `ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE` to an empty string (#422)
- Don't fetch suggestions after copy-earlier-word (#439)
- Allow users to unignore zle-\* widgets (e.g. zle-line-init) (#432)
## v0.5.2 ## v0.5.2
- Allow disabling automatic widget re-binding for better performance (#418) - Allow disabling automatic widget re-binding for better performance (#418)
- Fix async suggestions when `SH_WORD_SPLIT` is set - Fix async suggestions when `SH_WORD_SPLIT` is set
@@ -46,7 +63,7 @@
- Experimental support for asynchronous suggestions (#170) - Experimental support for asynchronous suggestions (#170)
- Fix problems with multi-line suggestions (#225) - Fix problems with multi-line suggestions (#225)
- Optimize case where manually typing in suggestion - Optimize case where manually typing in suggestion
- Avoid wrapping any zle-* widgets (#206) - Avoid wrapping any zle-\* widgets (#206)
- Remove support for deprecated options from v0.0.x - Remove support for deprecated options from v0.0.x
- Handle history entries that begin with dashes - Handle history entries that begin with dashes
- Gracefully handle being sourced multiple times (#126) - Gracefully handle being sourced multiple times (#126)

View File

@@ -1,6 +1,12 @@
## Installation # Installation
### Manual (Git Clone) * [Manual](#manual-git-clone)
* [Antigen](#antigen)
* [Oh My Zsh](#oh-my-zsh)
* [Arch Linux](#arch-linux)
* [macOS via Homebrew](#macos-via-homebrew)
## Manual (Git Clone)
1. Clone this repository somewhere on your machine. This guide will assume `~/.zsh/zsh-autosuggestions`. 1. Clone this repository somewhere on your machine. This guide will assume `~/.zsh/zsh-autosuggestions`.
@@ -16,7 +22,7 @@
3. Start a new terminal session. 3. Start a new terminal session.
### Antigen ## Antigen
1. Add the following to your `.zshrc`: 1. Add the following to your `.zshrc`:
@@ -26,7 +32,7 @@
2. Start a new terminal session. 2. Start a new terminal session.
### Oh My Zsh ## Oh My Zsh
1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`) 1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`)
@@ -42,7 +48,7 @@
3. Start a new terminal session. 3. Start a new terminal session.
### Arch Linux ## Arch Linux
1. Install [`zsh-autosuggestions`](https://www.archlinux.org/packages/community/any/zsh-autosuggestions/) from the `community` repository. 1. Install [`zsh-autosuggestions`](https://www.archlinux.org/packages/community/any/zsh-autosuggestions/) from the `community` repository.
@@ -60,7 +66,8 @@
3. Start a new terminal session. 3. Start a new terminal session.
### macOS via Homebrew ## macOS via Homebrew
1. Install the `zsh-autosuggestions` package using [Homebrew](https://brew.sh/). 1. Install the `zsh-autosuggestions` package using [Homebrew](https://brew.sh/).
```sh ```sh
@@ -74,4 +81,3 @@
``` ```
3. Start a new terminal session. 3. Start a new terminal session.

View File

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

View File

@@ -1,7 +1,6 @@
SRC_DIR := ./src SRC_DIR := ./src
SRC_FILES := \ SRC_FILES := \
$(SRC_DIR)/setup.zsh \
$(SRC_DIR)/config.zsh \ $(SRC_DIR)/config.zsh \
$(SRC_DIR)/util.zsh \ $(SRC_DIR)/util.zsh \
$(SRC_DIR)/bind.zsh \ $(SRC_DIR)/bind.zsh \

View File

@@ -2,7 +2,7 @@
_[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._ _[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._
It suggests commands as you type, based on command history. It suggests commands as you type based on history and completions.
Requirements: Zsh v4.3.11 or later Requirements: Zsh v4.3.11 or later
@@ -34,15 +34,28 @@ You may want to override the default global config variables. Default values of
### Suggestion Highlight Style ### Suggestion Highlight Style
Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion is shown with. The default is `fg=8`. Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion is shown with. The default is `fg=8`, which will set the foreground color to color 8 from the 256-color palette. If your terminal only supports 8 colors, you will need to use a number between 0 and 7.
Background color can also be set, and the suggestion can be styled bold, underlined, or standout. For example, this would show suggestions with bold, underlined, pink text on a cyan background:
```sh
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#ff00ff,bg=cyan,bold,underline"
```
For more info, read the Character Highlighting section of the zsh manual: `man zshzle` or [online](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting).
**Note:** Some iTerm2 users have reported [not being able to see the suggestions](https://github.com/zsh-users/zsh-autosuggestions/issues/416#issuecomment-486516333). If this affects you, the problem is likely caused by incorrect color settings. In order to correct this, go into iTerm2's setting, navigate to profile > colors and make sure that the colors for Basic Colors > Background and ANSI Colors > Bright Black are **different**.
### Suggestion Strategy ### Suggestion Strategy
`ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. The strategies in the array are tried successively until a suggestion is found. There are currently two built-in strategies to choose from: `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 three built-in strategies to choose from:
- `history`: Chooses the most recent match from history. - `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`. - `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`.
- `completion`: (experimental) Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module)
For example, setting `ZSH_AUTOSUGGEST_STRATEGY=(history completion)` will first try to find a suggestion from your history, but, if it can't find a match, will find a suggestion from the completion engine.
### Widget Mapping ### Widget Mapping
@@ -95,8 +108,7 @@ bindkey '^ ' autosuggest-accept
## Troubleshooting ## 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. If you have a problem, please search through [the list of issues on GitHub](https://github.com/zsh-users/zsh-autosuggestions/issues?q=) to see if someone else has already reported it.
### Reporting an Issue ### Reporting an Issue

View File

@@ -1 +1 @@
v0.5.2 v0.6.3

View File

@@ -27,6 +27,29 @@ context 'with asynchronous suggestions enabled' do
end end
end end
describe '`copy-earlier-word`' do
let(:before_sourcing) do
-> do
session.
run_command('autoload -Uz copy-earlier-word').
run_command('zle -N copy-earlier-word').
run_command('bindkey "^N" copy-earlier-word')
end
end
it 'should cycle through previous words in the buffer' do
session.clear_screen
session.send_string('foo bar baz')
sleep 0.5
session.send_keys('C-n')
wait_for { session.content }.to eq('foo bar bazbaz')
session.send_keys('C-n')
wait_for { session.content }.to eq('foo bar bazbar')
session.send_keys('C-n')
wait_for { session.content }.to eq('foo bar bazfoo')
end
end
describe 'pressing ^C after fetching a suggestion' do describe 'pressing ^C after fetching a suggestion' do
before do before do
skip 'Workaround does not work below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8') skip 'Workaround does not work below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
@@ -34,9 +57,9 @@ context 'with asynchronous suggestions enabled' do
it 'terminates the prompt and begins a new one' do it 'terminates the prompt and begins a new one' do
session.send_keys('e') session.send_keys('e')
sleep 0.1 sleep 0.5
session.send_keys('C-c') session.send_keys('C-c')
sleep 0.1 sleep 0.5
session.send_keys('echo') session.send_keys('echo')
wait_for { session.content }.to eq("e\necho") wait_for { session.content }.to eq("e\necho")

View File

@@ -24,4 +24,20 @@ describe 'pasting using bracketed-paste-magic' do
end end
end end
end end
context 'with `bracketed-paste` added to the list of widgets that clear the suggestion' do
let(:options) { ['ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(bracketed-paste)'] }
it 'does not retain an old suggestion' do
with_history ('echo foo') do
session.send_string('echo ')
wait_for { session.content }.to eq('echo foo')
session.paste_string('bar')
wait_for { session.content }.to eq('echo bar')
session.send_keys('C-a') # Any cursor movement works
sleep 1
expect(session.content).to eq('echo bar')
end
end
end
end end

View File

@@ -0,0 +1,14 @@
describe 'a running zpty command' do
let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } }
context 'when using `completion` strategy' do
let(:options) { ["ZSH_AUTOSUGGEST_STRATEGY=completion"] }
it 'is not affected' do
session.send_keys('a').send_keys('C-h')
session.run_command('zpty -t kitty; echo $?')
wait_for { session.content }.to end_with("\n0")
end
end
end

View File

@@ -63,5 +63,18 @@ describe 'when using vi mode' do
end end
end end
end end
end
describe '`vi-delete`' do
it 'should be able to remove the last character in the buffer' do
skip 'deleting last char did not work below zsh version 5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
session.
send_string('echo foo').
send_keys('escape').
send_keys('d').
send_keys('l')
wait_for { session.content }.to eq('echo fo')
end
end
end

17
spec/line_init_spec.rb Normal file
View File

@@ -0,0 +1,17 @@
context 'with zle-line-init unignored' do
let(:after_sourcing) do
-> do
session.
run_command('setopt extendedglob').
run_command('ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(${(@)ZSH_AUTOSUGGEST_IGNORE_WIDGETS:#zle-\*} zle-\^line-init)').
run_command('zle-line-init() { BUFFER="echo" }')
end
end
it 'should fetch a suggestion on each line initialization' do
with_history('echo foo') do
session.run_command('zle -N zle-line-init')
wait_for { session.content }.to end_with('echo foo')
end
end
end

View File

@@ -5,12 +5,13 @@ describe 'a zle widget' do
context 'when added to ZSH_AUTOSUGGEST_ACCEPT_WIDGETS' do context 'when added to ZSH_AUTOSUGGEST_ACCEPT_WIDGETS' do
let(:options) { ["ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})"] } let(:options) { ["ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})"] }
it 'accepts the suggestion when invoked' do it 'accepts the suggestion and moves the cursor to the end of the buffer when invoked' do
with_history('echo hello') do with_history('echo hello') do
session.send_string('e') session.send_string('e')
wait_for { session.content }.to eq('echo hello') wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b') session.send_keys('C-b')
wait_for { session.content(esc_seqs: true) }.to eq('echo hello') wait_for { session.content(esc_seqs: true) }.to eq('echo hello')
wait_for { session.cursor }.to eq([10, 0])
end end
end end
end end

View File

@@ -11,8 +11,7 @@ RSpec.shared_context 'terminal session' do
around do |example| around do |example|
before_sourcing.call before_sourcing.call
session.run_command(options.join('; ')) session.run_command(['source zsh-autosuggestions.zsh', *options].join('; '))
session.run_command('source zsh-autosuggestions.zsh')
after_sourcing.call after_sourcing.call
session.clear_screen session.clear_screen

View File

@@ -0,0 +1,55 @@
describe 'the `completion` suggestion strategy' do
let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=completion'] }
let(:before_sourcing) do
-> do
session.
run_command('autoload compinit && compinit').
run_command('_foo() { compadd bar; compadd bat }').
run_command('compdef _foo baz')
end
end
it 'suggests the first completion result' do
session.send_string('baz ')
wait_for { session.content }.to eq('baz bar')
end
it 'does not add extra carriage returns when prefix has a line feed' do
skip '`stty` does not work inside zpty below zsh version 5.0.3' if session.zsh_version < Gem::Version.new('5.0.3')
session.send_string('baz \\').send_keys('C-v', 'C-j')
wait_for { session.content }.to eq("baz \\\nbar")
end
context 'when `_complete` is aliased' do
let(:before_sourcing) do
-> do
session.
run_command('autoload compinit && compinit').
run_command('_foo() { compadd bar; compadd bat }').
run_command('compdef _foo baz').
run_command('alias _complete=_complete')
end
end
it 'suggests the first completion result' do
session.send_string('baz ')
wait_for { session.content }.to eq('baz bar')
end
end
context 'when async mode is enabled' do
let(:options) { ['ZSH_AUTOSUGGEST_USE_ASYNC=true', 'ZSH_AUTOSUGGEST_STRATEGY=completion'] }
it 'suggests the first completion result' do
session.send_string('baz ')
wait_for { session.content }.to eq('baz bar')
end
it 'does not add extra carriage returns when prefix has a line feed' do
skip '`stty` does not work inside zpty below zsh version 5.0.3' if session.zsh_version < Gem::Version.new('5.0.3')
session.send_string('baz \\').send_keys('C-v', 'C-j')
wait_for { session.content }.to eq("baz \\\nbar")
end
end
end

View File

@@ -3,9 +3,9 @@
# Async # # Async #
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
zmodload zsh/system
_zsh_autosuggest_async_request() { _zsh_autosuggest_async_request() {
zmodload zsh/system 2>/dev/null # For `$sysparams`
typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
# If we've got a pending request, cancel it # If we've got a pending request, cancel it
@@ -14,17 +14,20 @@ _zsh_autosuggest_async_request() {
exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&- exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
# Zsh will make a new process group for the child process only if job # We won't know the pid unless the user has zsh/system module installed
# control is enabled (MONITOR option) if [[ -n "$_ZSH_AUTOSUGGEST_CHILD_PID" ]]; then
if [[ -o MONITOR ]]; then # Zsh will make a new process group for the child process only if job
# Send the signal to the process group to kill any processes that may # control is enabled (MONITOR option)
# have been forked by the suggestion strategy if [[ -o MONITOR ]]; then
kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null # Send the signal to the process group to kill any processes that may
else # have been forked by the suggestion strategy
# Kill just the child process since it wasn't placed in a new process kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
# group. If the suggestion strategy forked any child processes they may else
# be orphaned and left behind. # Kill just the child process since it wasn't placed in a new process
kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null # group. If the suggestion strategy forked any child processes they may
# be orphaned and left behind.
kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
fi
fi fi
fi fi
@@ -56,9 +59,12 @@ _zsh_autosuggest_async_request() {
_zsh_autosuggest_async_response() { _zsh_autosuggest_async_response() {
emulate -L zsh emulate -L zsh
local suggestion
if [[ -z "$2" || "$2" == "hup" ]]; then if [[ -z "$2" || "$2" == "hup" ]]; then
# Read everything from the fd and give it as a suggestion # Read everything from the fd and give it as a suggestion
zle autosuggest-suggest -- "$(cat <&$1)" IFS='' read -rd '' -u $1 suggestion
zle autosuggest-suggest -- "$suggestion"
# Close the fd # Close the fd
exec {1}<&- exec {1}<&-

View File

@@ -69,7 +69,6 @@ _zsh_autosuggest_bind_widgets() {
ignore_widgets=( ignore_widgets=(
.\* .\*
_\* _\*
zle-\*
autosuggest-\* autosuggest-\*
$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\* $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\*
$ZSH_AUTOSUGGEST_IGNORE_WIDGETS $ZSH_AUTOSUGGEST_IGNORE_WIDGETS

View File

@@ -17,7 +17,10 @@ typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
# Will try each strategy in order until a suggestion is returned # Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && { (( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
typeset -ga ZSH_AUTOSUGGEST_STRATEGY typeset -ga ZSH_AUTOSUGGEST_STRATEGY
ZSH_AUTOSUGGEST_STRATEGY=(history) ZSH_AUTOSUGGEST_STRATEGY=(
'cd *:completion'
'*:history,completion'
)
} }
# Widgets that clear the suggestion # Widgets that clear the suggestion
@@ -35,6 +38,7 @@ typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
up-line-or-history up-line-or-history
down-line-or-history down-line-or-history
accept-line accept-line
copy-earlier-word
) )
} }
@@ -83,5 +87,10 @@ typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
which-command which-command
yank yank
yank-pop yank-pop
zle-\*
) )
} }
# Pty name for capturing completions for completion suggestion strategy
(( ! ${+ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME} )) &&
typeset -g ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty

View File

@@ -8,17 +8,45 @@
_zsh_autosuggest_fetch_suggestion() { _zsh_autosuggest_fetch_suggestion() {
typeset -g suggestion typeset -g suggestion
local -a strategies local -a strategy_specs spec_parts strategies
local strategy local strategy_spec strategy prefix
# Ensure we are working with an array # Ensure we are working with an array
strategies=(${=ZSH_AUTOSUGGEST_STRATEGY}) strategy_specs=(${ZSH_AUTOSUGGEST_STRATEGY})
for strategy in $strategies; do echo "fetching.." >> debug.log
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Break once we've found a suggestion for strategy_spec in $strategy_specs; do
[[ -n "$suggestion" ]] && break echo "trying spec: '$strategy_spec'" >> debug.log
spec_parts=(${(s/:/)strategy_spec})
prefix="${spec_parts[1]}"
echo "checking prefix: $prefix" >> debug.log
echo " spec parts: $spec_parts" >> debug.log
if [[ "$1" != ${~prefix} ]]; then
echo " '$1' didn't match prefix: '$prefix'" >> debug.log
continue;
fi
strategies=(${(s/,/)${spec_parts[2]}})
echo " trying strategies: $strategies" >> debug.log
for strategy in $strategies; do
echo " trying strategy $strategy" >> debug.log
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Ensure the suggestion matches the prefix
[[ "$suggestion" != "$1"* ]] && unset suggestion
# Break once we've found a valid suggestion
[[ -n "$suggestion" ]] && return
echo " didn't get suggestion" >> debug.log
done
done done
} }

View File

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

View File

@@ -19,4 +19,5 @@ _zsh_autosuggest_start() {
} }
# Start the autosuggestion widgets on the next precmd # Start the autosuggestion widgets on the next precmd
autoload -Uz add-zsh-hook
add-zsh-hook precmd _zsh_autosuggest_start add-zsh-hook precmd _zsh_autosuggest_start

View File

@@ -0,0 +1,130 @@
#--------------------------------------------------------------------#
# Completion Suggestion Strategy #
#--------------------------------------------------------------------#
# Fetches a suggestion from the completion engine
#
_zsh_autosuggest_capture_postcompletion() {
# Always insert the first completion into the buffer
compstate[insert]=1
# Don't list completions
unset 'compstate[list]'
}
_zsh_autosuggest_capture_completion_widget() {
# Add a post-completion hook to be called after all completions have been
# gathered. The hook can modify compstate to affect what is done with the
# gathered completions.
local -a +h comppostfuncs
comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
# Only capture completions at the end of the buffer
CURSOR=$#BUFFER
# Run the original widget wrapping `.complete-word` so we don't
# recursively try to fetch suggestions, since our pty is forked
# after autosuggestions is initialized.
zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]}
if is-at-least 5.0.3; then
# Don't do any cr/lf transformations. We need to do this immediately before
# output because if we do it in setup, onlcr will be re-enabled when we enter
# vared in the async code path. There is a bug in zpty module in older versions
# where the tty is not properly attached to the pty slave, resulting in stty
# getting stopped with a SIGTTOU. See zsh-workers thread 31660 and upstream
# commit f75904a38
stty -onlcr -ocrnl -F /dev/tty
fi
# The completion has been added, print the buffer as the suggestion
echo -nE - $'\0'$BUFFER$'\0'
}
zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
_zsh_autosuggest_capture_setup() {
autoload -Uz is-at-least
# There is a bug in zpty module in older zsh versions 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.
if ! is-at-least 5.4; then
zshexit() {
# The zsh builtin `kill` fails sometimes in older versions
# https://unix.stackexchange.com/a/477647/156673
kill -KILL $$ 2>&- || command kill -KILL $$
# Block for long enough for the signal to come through
sleep 1
}
fi
# Try to avoid any suggestions that wouldn't match the prefix
zstyle ':completion:*' matcher-list ''
zstyle ':completion:*' path-completion false
zstyle ':completion:*' max-errors 0 not-numeric
bindkey '^I' autosuggest-capture-completion
}
_zsh_autosuggest_capture_completion_sync() {
_zsh_autosuggest_capture_setup
zle autosuggest-capture-completion
}
_zsh_autosuggest_capture_completion_async() {
_zsh_autosuggest_capture_setup
zmodload zsh/parameter 2>/dev/null || return # For `$functions`
# Make vared completion work as if for a normal command line
# https://stackoverflow.com/a/7057118/154703
autoload +X _complete
functions[_original_complete]=$functions[_complete]
function _complete() {
unset 'compstate[vared]'
_original_complete "$@"
}
# Open zle with buffer set so we can capture completions for it
vared 1
}
_zsh_autosuggest_strategy_completion() {
typeset -g suggestion
local line REPLY
# Exit if we don't have completions
whence compdef >/dev/null || return
# Exit if we don't have zpty
zmodload zsh/zpty 2>/dev/null || return
# Zle will be inactive if we are in async mode
if zle; then
zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync
else
zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$1"
zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t'
fi
{
# The completion result is surrounded by null bytes, so read the
# content between the first two null bytes.
zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0'
# Extract the suggestion from between the null bytes. On older
# versions of zsh (older than 5.3), we sometimes get extra bytes after
# the second null byte, so trim those off the end.
# See http://www.zsh.org/mla/workers/2015/msg03290.html
suggestion="${${line#*$'\0'}%$'\0'*}"
} always {
# Destroy the pty
zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
}
}

View File

@@ -85,7 +85,7 @@ _zsh_autosuggest_modify() {
# Get a new suggestion if the buffer is not empty after modification # Get a new suggestion if the buffer is not empty after modification
if (( $#BUFFER > 0 )); then if (( $#BUFFER > 0 )); then
if (( ! ${+ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE} )) || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then
_zsh_autosuggest_fetch _zsh_autosuggest_fetch
fi fi
fi fi
@@ -136,7 +136,11 @@ _zsh_autosuggest_accept() {
unset POSTDISPLAY unset POSTDISPLAY
# Move the cursor to the end of the buffer # Move the cursor to the end of the buffer
CURSOR=${#BUFFER} if [[ "$KEYMAP" = "vicmd" ]]; then
CURSOR=$(($#BUFFER - 1))
else
CURSOR=$#BUFFER
fi
fi fi
_zsh_autosuggest_invoke_original_widget $@ _zsh_autosuggest_invoke_original_widget $@

View File

@@ -1,8 +1,8 @@
# Fish-like fast/unobtrusive autosuggestions for zsh. # Fish-like fast/unobtrusive autosuggestions for zsh.
# https://github.com/zsh-users/zsh-autosuggestions # https://github.com/zsh-users/zsh-autosuggestions
# v0.5.2 # v0.6.3
# Copyright (c) 2013 Thiago de Arruda # Copyright (c) 2013 Thiago de Arruda
# Copyright (c) 2016-2018 Eric Freese # Copyright (c) 2016-2019 Eric Freese
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation # obtaining a copy of this software and associated documentation
@@ -25,16 +25,6 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE. # 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 # # Global Configuration Variables #
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
@@ -53,7 +43,10 @@ typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
# Will try each strategy in order until a suggestion is returned # Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && { (( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
typeset -ga ZSH_AUTOSUGGEST_STRATEGY typeset -ga ZSH_AUTOSUGGEST_STRATEGY
ZSH_AUTOSUGGEST_STRATEGY=(history) ZSH_AUTOSUGGEST_STRATEGY=(
'cd *:completion'
'*:history,completion'
)
} }
# Widgets that clear the suggestion # Widgets that clear the suggestion
@@ -71,6 +64,7 @@ typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
up-line-or-history up-line-or-history
down-line-or-history down-line-or-history
accept-line accept-line
copy-earlier-word
) )
} }
@@ -119,9 +113,14 @@ typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
which-command which-command
yank yank
yank-pop yank-pop
zle-\*
) )
} }
# Pty name for capturing completions for completion suggestion strategy
(( ! ${+ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME} )) &&
typeset -g ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
# Utility Functions # # Utility Functions #
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
@@ -203,7 +202,6 @@ _zsh_autosuggest_bind_widgets() {
ignore_widgets=( ignore_widgets=(
.\* .\*
_\* _\*
zle-\*
autosuggest-\* autosuggest-\*
$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\* $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\*
$ZSH_AUTOSUGGEST_IGNORE_WIDGETS $ZSH_AUTOSUGGEST_IGNORE_WIDGETS
@@ -352,7 +350,7 @@ _zsh_autosuggest_modify() {
# Get a new suggestion if the buffer is not empty after modification # Get a new suggestion if the buffer is not empty after modification
if (( $#BUFFER > 0 )); then if (( $#BUFFER > 0 )); then
if (( ! ${+ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE} )) || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then
_zsh_autosuggest_fetch _zsh_autosuggest_fetch
fi fi
fi fi
@@ -403,7 +401,11 @@ _zsh_autosuggest_accept() {
unset POSTDISPLAY unset POSTDISPLAY
# Move the cursor to the end of the buffer # Move the cursor to the end of the buffer
CURSOR=${#BUFFER} if [[ "$KEYMAP" = "vicmd" ]]; then
CURSOR=$(($#BUFFER - 1))
else
CURSOR=$#BUFFER
fi
fi fi
_zsh_autosuggest_invoke_original_widget $@ _zsh_autosuggest_invoke_original_widget $@
@@ -486,6 +488,136 @@ _zsh_autosuggest_partial_accept() {
zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle
} }
#--------------------------------------------------------------------#
# Completion Suggestion Strategy #
#--------------------------------------------------------------------#
# Fetches a suggestion from the completion engine
#
_zsh_autosuggest_capture_postcompletion() {
# Always insert the first completion into the buffer
compstate[insert]=1
# Don't list completions
unset 'compstate[list]'
}
_zsh_autosuggest_capture_completion_widget() {
# Add a post-completion hook to be called after all completions have been
# gathered. The hook can modify compstate to affect what is done with the
# gathered completions.
local -a +h comppostfuncs
comppostfuncs=(_zsh_autosuggest_capture_postcompletion)
# Only capture completions at the end of the buffer
CURSOR=$#BUFFER
# Run the original widget wrapping `.complete-word` so we don't
# recursively try to fetch suggestions, since our pty is forked
# after autosuggestions is initialized.
zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]}
if is-at-least 5.0.3; then
# Don't do any cr/lf transformations. We need to do this immediately before
# output because if we do it in setup, onlcr will be re-enabled when we enter
# vared in the async code path. There is a bug in zpty module in older versions
# where the tty is not properly attached to the pty slave, resulting in stty
# getting stopped with a SIGTTOU. See zsh-workers thread 31660 and upstream
# commit f75904a38
stty -onlcr -ocrnl -F /dev/tty
fi
# The completion has been added, print the buffer as the suggestion
echo -nE - $'\0'$BUFFER$'\0'
}
zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget
_zsh_autosuggest_capture_setup() {
autoload -Uz is-at-least
# There is a bug in zpty module in older zsh versions 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.
if ! is-at-least 5.4; then
zshexit() {
# The zsh builtin `kill` fails sometimes in older versions
# https://unix.stackexchange.com/a/477647/156673
kill -KILL $$ 2>&- || command kill -KILL $$
# Block for long enough for the signal to come through
sleep 1
}
fi
# Try to avoid any suggestions that wouldn't match the prefix
zstyle ':completion:*' matcher-list ''
zstyle ':completion:*' path-completion false
zstyle ':completion:*' max-errors 0 not-numeric
bindkey '^I' autosuggest-capture-completion
}
_zsh_autosuggest_capture_completion_sync() {
_zsh_autosuggest_capture_setup
zle autosuggest-capture-completion
}
_zsh_autosuggest_capture_completion_async() {
_zsh_autosuggest_capture_setup
zmodload zsh/parameter 2>/dev/null || return # For `$functions`
# Make vared completion work as if for a normal command line
# https://stackoverflow.com/a/7057118/154703
autoload +X _complete
functions[_original_complete]=$functions[_complete]
function _complete() {
unset 'compstate[vared]'
_original_complete "$@"
}
# Open zle with buffer set so we can capture completions for it
vared 1
}
_zsh_autosuggest_strategy_completion() {
typeset -g suggestion
local line REPLY
# Exit if we don't have completions
whence compdef >/dev/null || return
# Exit if we don't have zpty
zmodload zsh/zpty 2>/dev/null || return
# Zle will be inactive if we are in async mode
if zle; then
zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync
else
zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$1"
zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t'
fi
{
# The completion result is surrounded by null bytes, so read the
# content between the first two null bytes.
zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0'
# Extract the suggestion from between the null bytes. On older
# versions of zsh (older than 5.3), we sometimes get extra bytes after
# the second null byte, so trim those off the end.
# See http://www.zsh.org/mla/workers/2015/msg03290.html
suggestion="${${line#*$'\0'}%$'\0'*}"
} always {
# Destroy the pty
zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
}
}
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
# History Suggestion Strategy # # History Suggestion Strategy #
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
@@ -579,18 +711,46 @@ _zsh_autosuggest_strategy_match_prev_cmd() {
_zsh_autosuggest_fetch_suggestion() { _zsh_autosuggest_fetch_suggestion() {
typeset -g suggestion typeset -g suggestion
local -a strategies local -a strategy_specs spec_parts strategies
local strategy local strategy_spec strategy prefix
# Ensure we are working with an array # Ensure we are working with an array
strategies=(${=ZSH_AUTOSUGGEST_STRATEGY}) strategy_specs=(${ZSH_AUTOSUGGEST_STRATEGY})
for strategy in $strategies; do echo "fetching.." >> debug.log
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Break once we've found a suggestion for strategy_spec in $strategy_specs; do
[[ -n "$suggestion" ]] && break echo "trying spec: '$strategy_spec'" >> debug.log
spec_parts=(${(s/:/)strategy_spec})
prefix="${spec_parts[1]}"
echo "checking prefix: $prefix" >> debug.log
echo " spec parts: $spec_parts" >> debug.log
if [[ "$1" != ${~prefix} ]]; then
echo " '$1' didn't match prefix: '$prefix'" >> debug.log
continue;
fi
strategies=(${(s/,/)${spec_parts[2]}})
echo " trying strategies: $strategies" >> debug.log
for strategy in $strategies; do
echo " trying strategy $strategy" >> debug.log
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Ensure the suggestion matches the prefix
[[ "$suggestion" != "$1"* ]] && unset suggestion
# Break once we've found a valid suggestion
[[ -n "$suggestion" ]] && return
echo " didn't get suggestion" >> debug.log
done
done done
} }
@@ -598,9 +758,9 @@ _zsh_autosuggest_fetch_suggestion() {
# Async # # Async #
#--------------------------------------------------------------------# #--------------------------------------------------------------------#
zmodload zsh/system
_zsh_autosuggest_async_request() { _zsh_autosuggest_async_request() {
zmodload zsh/system 2>/dev/null # For `$sysparams`
typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
# If we've got a pending request, cancel it # If we've got a pending request, cancel it
@@ -609,17 +769,20 @@ _zsh_autosuggest_async_request() {
exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&- exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
# Zsh will make a new process group for the child process only if job # We won't know the pid unless the user has zsh/system module installed
# control is enabled (MONITOR option) if [[ -n "$_ZSH_AUTOSUGGEST_CHILD_PID" ]]; then
if [[ -o MONITOR ]]; then # Zsh will make a new process group for the child process only if job
# Send the signal to the process group to kill any processes that may # control is enabled (MONITOR option)
# have been forked by the suggestion strategy if [[ -o MONITOR ]]; then
kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null # Send the signal to the process group to kill any processes that may
else # have been forked by the suggestion strategy
# Kill just the child process since it wasn't placed in a new process kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
# group. If the suggestion strategy forked any child processes they may else
# be orphaned and left behind. # Kill just the child process since it wasn't placed in a new process
kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null # group. If the suggestion strategy forked any child processes they may
# be orphaned and left behind.
kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
fi
fi fi
fi fi
@@ -651,9 +814,12 @@ _zsh_autosuggest_async_request() {
_zsh_autosuggest_async_response() { _zsh_autosuggest_async_response() {
emulate -L zsh emulate -L zsh
local suggestion
if [[ -z "$2" || "$2" == "hup" ]]; then if [[ -z "$2" || "$2" == "hup" ]]; then
# Read everything from the fd and give it as a suggestion # Read everything from the fd and give it as a suggestion
zle autosuggest-suggest -- "$(cat <&$1)" IFS='' read -rd '' -u $1 suggestion
zle autosuggest-suggest -- "$suggestion"
# Close the fd # Close the fd
exec {1}<&- exec {1}<&-
@@ -683,4 +849,5 @@ _zsh_autosuggest_start() {
} }
# Start the autosuggestion widgets on the next precmd # Start the autosuggestion widgets on the next precmd
autoload -Uz add-zsh-hook
add-zsh-hook precmd _zsh_autosuggest_start add-zsh-hook precmd _zsh_autosuggest_start