1
0
mirror of https://github.com/DazAh/dotfiles synced 2026-05-21 18:25:46 +01:00

morefiles

This commit is contained in:
DazAh
2019-10-13 01:29:05 +01:00
parent a7723448a0
commit 58d232b8f5
1411 changed files with 64341 additions and 0 deletions
@@ -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
@@ -0,0 +1,15 @@
version: 2
jobs:
build:
parallelism: 4
shell: /bin/bash --login
docker:
- image: ericfreese/zsh-autosuggestions-test:latest
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
@@ -0,0 +1,10 @@
4016ff795bb184461996c5539327befc8da2af6f branch 'master' of https://github.com/zsh-users/zsh-autosuggestions
bdbe43e667824e0a4e5e16cdc500ce00b9001dc0 not-for-merge branch 'develop' of https://github.com/zsh-users/zsh-autosuggestions
b27277e7ff0f877f3189211ccd57da9ebec5e743 not-for-merge branch 'experiments/strategies-by-pattern' of https://github.com/zsh-users/zsh-autosuggestions
36dae44064a7bf088fb1dec74c8463d5b6f3d3c8 not-for-merge branch 'features/hist-words-suggestions' of https://github.com/zsh-users/zsh-autosuggestions
161de32912a3426dc686db38250913227dd91948 not-for-merge branch 'features/redraw-hook' of https://github.com/zsh-users/zsh-autosuggestions
15bcfd712664bb301113761f886718fd3bea7bc2 not-for-merge branch 'fixes/copy-earlier-word' of https://github.com/zsh-users/zsh-autosuggestions
141f497d1751ea7c8f3bd7e4fb0e2e031d6d302b not-for-merge branch 'fixes/kill-multiple-words' of https://github.com/zsh-users/zsh-autosuggestions
9fb96753069b919deaaacb6f659be2339dfeb4d2 not-for-merge branch 'fixes/slow_bracketed_paste_magic' of https://github.com/zsh-users/zsh-autosuggestions
20c0ea841bafa78d8fb75c767e780248bfdf11a4 not-for-merge branch 'no-server' of https://github.com/zsh-users/zsh-autosuggestions
0f0f221180b1fec3254e74d8d657c3a929870b1b not-for-merge branch 'refactor-null-byte-split' of https://github.com/zsh-users/zsh-autosuggestions
@@ -0,0 +1 @@
ref: refs/heads/master
@@ -0,0 +1 @@
4016ff795bb184461996c5539327befc8da2af6f
@@ -0,0 +1,11 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = https://github.com/zsh-users/zsh-autosuggestions
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
@@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.
@@ -0,0 +1,15 @@
#!/bin/sh
#
# An example hook script to check the commit log message taken by
# applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit. The hook is
# allowed to edit the commit message file.
#
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
:
@@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}
@@ -0,0 +1,114 @@
#!/usr/bin/perl
use strict;
use warnings;
use IPC::Open2;
# An example hook script to integrate Watchman
# (https://facebook.github.io/watchman/) with git to speed up detecting
# new and modified files.
#
# The hook is passed a version (currently 1) and a time in nanoseconds
# formatted as a string and outputs to stdout all files that have been
# modified since the given time. Paths must be relative to the root of
# the working tree and separated by a single NUL.
#
# To enable this hook, rename this file to "query-watchman" and set
# 'git config core.fsmonitor .git/hooks/query-watchman'
#
my ($version, $time) = @ARGV;
# Check the hook interface version
if ($version == 1) {
# convert nanoseconds to seconds
$time = int $time / 1000000000;
} else {
die "Unsupported query-fsmonitor hook version '$version'.\n" .
"Falling back to scanning...\n";
}
my $git_work_tree;
if ($^O =~ 'msys' || $^O =~ 'cygwin') {
$git_work_tree = Win32::GetCwd();
$git_work_tree =~ tr/\\/\//;
} else {
require Cwd;
$git_work_tree = Cwd::cwd();
}
my $retry = 1;
launch_watchman();
sub launch_watchman {
my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
or die "open2() failed: $!\n" .
"Falling back to scanning...\n";
# In the query expression below we're asking for names of files that
# changed since $time but were not transient (ie created after
# $time but no longer exist).
#
# To accomplish this, we're using the "since" generator to use the
# recency index to select candidate nodes and "fields" to limit the
# output to file names only. Then we're using the "expression" term to
# further constrain the results.
#
# The category of transient files that we want to ignore will have a
# creation clock (cclock) newer than $time_t value and will also not
# currently exist.
my $query = <<" END";
["query", "$git_work_tree", {
"since": $time,
"fields": ["name"],
"expression": ["not", ["allof", ["since", $time, "cclock"], ["not", "exists"]]]
}]
END
print CHLD_IN $query;
close CHLD_IN;
my $response = do {local $/; <CHLD_OUT>};
die "Watchman: command returned no output.\n" .
"Falling back to scanning...\n" if $response eq "";
die "Watchman: command returned invalid output: $response\n" .
"Falling back to scanning...\n" unless $response =~ /^\{/;
my $json_pkg;
eval {
require JSON::XS;
$json_pkg = "JSON::XS";
1;
} or do {
require JSON::PP;
$json_pkg = "JSON::PP";
};
my $o = $json_pkg->new->utf8->decode($response);
if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) {
print STDERR "Adding '$git_work_tree' to watchman's watch list.\n";
$retry--;
qx/watchman watch "$git_work_tree"/;
die "Failed to make watchman watch '$git_work_tree'.\n" .
"Falling back to scanning...\n" if $? != 0;
# Watchman will always return all files on the first query so
# return the fast "everything is dirty" flag to git and do the
# Watchman query just to get it over with now so we won't pay
# the cost in git to look up each individual file.
print "/\0";
eval { launch_watchman() };
exit 0;
}
die "Watchman: $o->{error}.\n" .
"Falling back to scanning...\n" if $o->{error};
binmode STDOUT, ":utf8";
local $, = "\0";
print @{$o->{files}};
}
@@ -0,0 +1,8 @@
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".
exec git update-server-info
@@ -0,0 +1,14 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed
# by applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
precommit="$(git rev-parse --git-path hooks/pre-commit)"
test -x "$precommit" && exec "$precommit" ${1+"$@"}
:
@@ -0,0 +1,49 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --bool hooks.allownonascii)
# Redirect output to stderr.
exec 1>&2
# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
git config hooks.allownonascii true
EOF
exit 1
fi
# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --
@@ -0,0 +1,53 @@
#!/bin/sh
# An example hook script to verify what is about to be pushed. Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed. If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
# <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
remote="$1"
url="$2"
z40=0000000000000000000000000000000000000000
while read local_ref local_sha remote_ref remote_sha
do
if [ "$local_sha" = $z40 ]
then
# Handle delete
:
else
if [ "$remote_sha" = $z40 ]
then
# New branch, examine all commits
range="$local_sha"
else
# Update to existing branch, examine new commits
range="$remote_sha..$local_sha"
fi
# Check for WIP commit
commit=`git rev-list -n 1 --grep '^WIP' "$range"`
if [ -n "$commit" ]
then
echo >&2 "Found WIP commit in $local_ref, not pushing"
exit 1
fi
fi
done
exit 0
@@ -0,0 +1,169 @@
#!/bin/sh
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
# The hook is called with the following parameters:
#
# $1 -- the upstream the series was forked from.
# $2 -- the branch being rebased (or empty when rebasing the current branch).
#
# This sample shows how to prevent topic branches that are already
# merged to 'next' branch from getting rebased, because allowing it
# would result in rebasing already published history.
publish=next
basebranch="$1"
if test "$#" = 2
then
topic="refs/heads/$2"
else
topic=`git symbolic-ref HEAD` ||
exit 0 ;# we do not interrupt rebasing detached HEAD
fi
case "$topic" in
refs/heads/??/*)
;;
*)
exit 0 ;# we do not interrupt others.
;;
esac
# Now we are dealing with a topic branch being rebased
# on top of master. Is it OK to rebase it?
# Does the topic really exist?
git show-ref -q "$topic" || {
echo >&2 "No such branch $topic"
exit 1
}
# Is topic fully merged to master?
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
echo >&2 "$topic is fully merged to master; better remove it."
exit 1 ;# we could allow it, but there is no point.
fi
# Is topic ever merged to next? If so you should not be rebasing it.
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
only_next_2=`git rev-list ^master ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
not_in_topic=`git rev-list "^$topic" master`
if test -z "$not_in_topic"
then
echo >&2 "$topic is already up to date with master"
exit 1 ;# we could allow it, but there is no point.
else
exit 0
fi
else
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
/usr/bin/perl -e '
my $topic = $ARGV[0];
my $msg = "* $topic has commits already merged to public branch:\n";
my (%not_in_next) = map {
/^([0-9a-f]+) /;
($1 => 1);
} split(/\n/, $ARGV[1]);
for my $elem (map {
/^([0-9a-f]+) (.*)$/;
[$1 => $2];
} split(/\n/, $ARGV[2])) {
if (!exists $not_in_next{$elem->[0]}) {
if ($msg) {
print STDERR $msg;
undef $msg;
}
print STDERR " $elem->[1]\n";
}
}
' "$topic" "$not_in_next" "$not_in_master"
exit 1
fi
<<\DOC_END
This sample hook safeguards topic branches that have been
published from being rewound.
The workflow assumed here is:
* Once a topic branch forks from "master", "master" is never
merged into it again (either directly or indirectly).
* Once a topic branch is fully cooked and merged into "master",
it is deleted. If you need to build on top of it to correct
earlier mistakes, a new topic branch is created by forking at
the tip of the "master". This is not strictly necessary, but
it makes it easier to keep your history simple.
* Whenever you need to test or publish your changes to topic
branches, merge them into "next" branch.
The script, being an example, hardcodes the publish branch name
to be "next", but it is trivial to make it configurable via
$GIT_DIR/config mechanism.
With this workflow, you would want to know:
(1) ... if a topic branch has ever been merged to "next". Young
topic branches can have stupid mistakes you would rather
clean up before publishing, and things that have not been
merged into other branches can be easily rebased without
affecting other people. But once it is published, you would
not want to rewind it.
(2) ... if a topic branch has been fully merged to "master".
Then you can delete it. More importantly, you should not
build on top of it -- other people may already want to
change things related to the topic as patches against your
"master", so if you need further changes, it is better to
fork the topic (perhaps with the same name) afresh from the
tip of "master".
Let's look at this example:
o---o---o---o---o---o---o---o---o---o "next"
/ / / /
/ a---a---b A / /
/ / / /
/ / c---c---c---c B /
/ / / \ /
/ / / b---b C \ /
/ / / / \ /
---o---o---o---o---o---o---o---o---o---o---o "master"
A, B and C are topic branches.
* A has one fix since it was merged up to "next".
* B has finished. It has been fully merged up to "master" and "next",
and is ready to be deleted.
* C has not merged to "next" at all.
We would want to allow C to be rebased, refuse A, and encourage
B to be deleted.
To compute (1):
git rev-list ^master ^topic next
git rev-list ^master next
if these match, topic has not merged in next at all.
To compute (2):
git rev-list master..topic
if this is empty, it is fully merged to "master".
DOC_END
@@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to make use of push options.
# The example simply echoes all push options that start with 'echoback='
# and rejects all pushes when the "reject" push option is used.
#
# To enable this hook, rename this file to "pre-receive".
if test -n "$GIT_PUSH_OPTION_COUNT"
then
i=0
while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
do
eval "value=\$GIT_PUSH_OPTION_$i"
case "$value" in
echoback=*)
echo "echo from the pre-receive-hook: ${value#*=}" >&2
;;
reject)
exit 1
esac
i=$((i + 1))
done
fi
@@ -0,0 +1,42 @@
#!/bin/sh
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source. The hook's purpose is to edit the commit
# message file. If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".
# This hook includes three examples. The first one removes the
# "# Please enter the commit message..." help message.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output. It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited. This is rarely a good idea.
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
# case "$COMMIT_SOURCE,$SHA1" in
# ,|template,)
# /usr/bin/perl -i.bak -pe '
# print "\n" . `git diff --cached --name-status -r`
# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
# *) ;;
# esac
# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
# if test -z "$COMMIT_SOURCE"
# then
# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
# fi
@@ -0,0 +1,128 @@
#!/bin/sh
#
# An example hook script to block unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
# hooks.allowdeletetag
# This boolean sets whether deleting tags will be allowed in the
# repository. By default they won't be.
# hooks.allowmodifytag
# This boolean sets whether a tag may be modified after creation. By default
# it won't be.
# hooks.allowdeletebranch
# This boolean sets whether deleting branches will be allowed in the
# repository. By default they won't be.
# hooks.denycreatebranch
# This boolean sets whether remotely creating branches will be denied
# in the repository. By default this is allowed.
#
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
# --- Config
allowunannotated=$(git config --bool hooks.allowunannotated)
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
denycreatebranch=$(git config --bool hooks.denycreatebranch)
allowdeletetag=$(git config --bool hooks.allowdeletetag)
allowmodifytag=$(git config --bool hooks.allowmodifytag)
# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
echo "*** Project description file hasn't been set" >&2
exit 1
;;
esac
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero="0000000000000000000000000000000000000000"
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
newrev_type=$(git cat-file -t $newrev)
fi
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,delete)
# delete tag
if [ "$allowdeletetag" != "true" ]; then
echo "*** Deleting a tag is not allowed in this repository" >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
then
echo "*** Tag '$refname' already exists." >&2
echo "*** Modifying a tag is not allowed in this repository." >&2
exit 1
fi
;;
refs/heads/*,commit)
# branch
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
echo "*** Creating a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/heads/*,delete)
# delete branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/remotes/*,commit)
# tracking branch
;;
refs/remotes/*,delete)
# delete tracking branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
exit 1
fi
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
# --- Finished
exit 0
@@ -0,0 +1,6 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
@@ -0,0 +1,2 @@
0000000000000000000000000000000000000000 43f3bc4010b2c697d2252fdd8b36a577ea125881 Darren Leatherbarrow <darren@DarrenPC.dazeerrcloud.xyz> 1566677621 +0100 clone: from https://github.com/zsh-users/zsh-autosuggestions
43f3bc4010b2c697d2252fdd8b36a577ea125881 4016ff795bb184461996c5539327befc8da2af6f Darren Leatherbarrow <darren@DarrenPC.dazeerrcloud.xyz> 1570875469 +0100 pull: Fast-forward
@@ -0,0 +1,2 @@
0000000000000000000000000000000000000000 43f3bc4010b2c697d2252fdd8b36a577ea125881 Darren Leatherbarrow <darren@DarrenPC.dazeerrcloud.xyz> 1566677621 +0100 clone: from https://github.com/zsh-users/zsh-autosuggestions
43f3bc4010b2c697d2252fdd8b36a577ea125881 4016ff795bb184461996c5539327befc8da2af6f Darren Leatherbarrow <darren@DarrenPC.dazeerrcloud.xyz> 1570875469 +0100 pull: Fast-forward
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 43f3bc4010b2c697d2252fdd8b36a577ea125881 Darren Leatherbarrow <darren@DarrenPC.dazeerrcloud.xyz> 1566677621 +0100 clone: from https://github.com/zsh-users/zsh-autosuggestions
@@ -0,0 +1 @@
3da421aa47fdcb10bf3d19f1c35946b03c1bd90e bdbe43e667824e0a4e5e16cdc500ce00b9001dc0 Darren Leatherbarrow <darren@DarrenPC.dazeerrcloud.xyz> 1570875469 +0100 pull: fast-forward
@@ -0,0 +1 @@
43f3bc4010b2c697d2252fdd8b36a577ea125881 4016ff795bb184461996c5539327befc8da2af6f Darren Leatherbarrow <darren@DarrenPC.dazeerrcloud.xyz> 1570875469 +0100 pull: fast-forward
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 0f0f221180b1fec3254e74d8d657c3a929870b1b Darren Leatherbarrow <darren@DarrenPC.dazeerrcloud.xyz> 1570875469 +0100 pull: storing head
@@ -0,0 +1,2 @@
xO;N1¥ö)¦K{üYK¢ŽCŒ=ãd¥ì:rÎ…Ä(ßÓûÖ¾më„ö骠hF¯èeQ_SÌ16* K"ÎÍÜyè>ÂÞYfŸšÔb±4››­²©Ú"õOï©Q©§ÒÕ˜“8\Y
E))ÛY²XÃ_ǵxk…¹ì¡pÖ ¤ý‚·ËÆëí¥öílˆÙû„à#¢™ì<uè?íæSÇE¡ ÞëN?fÔ Öýè ú­·~7?oF^~
@@ -0,0 +1 @@
4016ff795bb184461996c5539327befc8da2af6f
@@ -0,0 +1,47 @@
# pack-refs with: peeled fully-peeled sorted
3da421aa47fdcb10bf3d19f1c35946b03c1bd90e refs/remotes/origin/develop
b27277e7ff0f877f3189211ccd57da9ebec5e743 refs/remotes/origin/experiments/strategies-by-pattern
36dae44064a7bf088fb1dec74c8463d5b6f3d3c8 refs/remotes/origin/features/hist-words-suggestions
161de32912a3426dc686db38250913227dd91948 refs/remotes/origin/features/redraw-hook
15bcfd712664bb301113761f886718fd3bea7bc2 refs/remotes/origin/fixes/copy-earlier-word
141f497d1751ea7c8f3bd7e4fb0e2e031d6d302b refs/remotes/origin/fixes/kill-multiple-words
9fb96753069b919deaaacb6f659be2339dfeb4d2 refs/remotes/origin/fixes/slow_bracketed_paste_magic
43f3bc4010b2c697d2252fdd8b36a577ea125881 refs/remotes/origin/master
20c0ea841bafa78d8fb75c767e780248bfdf11a4 refs/remotes/origin/no-server
5f13490bae58bc42045b2a19fc3e446df75417a5 refs/tags/0.0.1
7dc9e0f92631d03d289bdcf67530c84e72b18aa3 refs/tags/pre-v0.1.0
6a4b2b386534461d479155c7f9503b1063800398 refs/tags/v0.1.0
f154d25fb3b734235bff7d508d885e7db851c4ca refs/tags/v0.1.1
0ae590729428a0b6aea682a04d74404a124cb909 refs/tags/v0.2.0
31452887d20069d882e31eafaf46890e6500b4af refs/tags/v0.2.1
2b449a62f8b6fc69e44db3eaf46c5c36c1853bfa refs/tags/v0.2.10
ba029e83d0aa2d859d7ac46299462fa5b5a43a68 refs/tags/v0.2.11
aa859a282dddf97cadce45255fad95930fc3f503 refs/tags/v0.2.12
2a5791710a9038ad21b9f528e6923f33fe3b8edd refs/tags/v0.2.13
9d100f4f321000b80b73638a6406e8486c35ce16 refs/tags/v0.2.14
c761dc81509b137748190d254e8beb6845dedf21 refs/tags/v0.2.15
0a42f872b84ecdd17b2d4d4e4422f3af8bf8959b refs/tags/v0.2.16
f0a745576ff69fa608421ee7214d4cd77b43e62f refs/tags/v0.2.17
011f5420fcc61d1c8fdd7510c4b560b0ca7f6412 refs/tags/v0.2.2
266437c98a7e3d9b24fa82e6a35d8dc54946f1d2 refs/tags/v0.2.3
cd71081303287498f940e6158cfce13583d4293f refs/tags/v0.2.4
76f415bf4360705a819c92787b188ddd813d374c refs/tags/v0.2.5
3ce1adb55dfb558c7f9b60056356fdb7d3567403 refs/tags/v0.2.6
45ab49d1f2024f9422ab9eb41178e4d9edf2e91c refs/tags/v0.2.7
dd9a8789a758966eed6943adad5338b2213df4a2 refs/tags/v0.2.8
0faa2b6584928a17fef8904e8439d1268729f37a refs/tags/v0.2.9
1ed9155f89956310eb0cfce59f95e6f3e2ecbcd2 refs/tags/v0.3.0
87facd9b85630f288433aa0a20a963cffc612ee5 refs/tags/v0.3.1
cce68de46d37697f561a23c51db629ee2bbd18db refs/tags/v0.3.2
9cfaf5d3424ceb5fedd2c7e3253f823faae74383 refs/tags/v0.3.3
2cb6eb6e29852e64a146b0284275ecdc0661b082 refs/tags/v0.4.0
9f9237ab8a530eeff389161202bbc7283ad6af3e refs/tags/v0.4.1
15931f04ffac91a2f9a1a044b6b3ee4050751064 refs/tags/v0.4.2
d7c796719e6352666f7a9c94da9ddaed10f3217d refs/tags/v0.4.3
a7f0106b31c2538a36cab30428e6ca65d9a2ae60 refs/tags/v0.5.0
cbf0e24b1863c44606bbdd1edcb1c1b40efbcb55 refs/tags/v0.5.1
733abd4af0f23f217caa2a303fbef66382d19d6f refs/tags/v0.5.2
b3b34eab49b8e44f2df93d746640329a45722c0b refs/tags/v0.6.0
112dd3e3c7d1e038c46a2af92d38f4b9aa2ffb17 refs/tags/v0.6.1
c80605595c209e4ee60c3d121ab159ebc6d66547 refs/tags/v0.6.2
146020d9b28c70a5edd93d19bbef9c5cb05d4b62 refs/tags/v0.6.3
@@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
### Describe the bug
<!-- A clear and concise description of what the bug is. -->
### To Reproduce
Steps to reproduce the behavior:
<!-- If you are not able to reproduce it by running `zsh -df` and sourcing the plugin manually, it means there that the issue is caused by something in your local config file(s). Temporarily comment out or remove sections of your config and restart `zsh` until you narrow down exactly what is causing the issue. -->
```sh
% zsh -df
% source path/to/zsh-autosuggestions.zsh
% ... # what do you do to reproduce?
```
### Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
### Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->
### Desktop
- OS + distribution: <!-- e.g. Arch Linux 2019.07.01 -->
- Zsh version: <!-- `echo $ZSH_VERSION` -->
- Plugin version: <!-- or git commit hash if installed via git -->
### Additional context
<!-- Add any other context about the problem here. -->
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
### Is your feature request related to a problem? Please describe.
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
### Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. -->
### Describe alternatives you've considered
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
### Additional context
<!-- Add any other context or screenshots about the feature request here. -->
@@ -0,0 +1,70 @@
context 'with asynchronous suggestions enabled' do
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
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
before do
skip 'Workaround does not work below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8')
end
it 'terminates the prompt and begins a new one' do
session.send_keys('e')
sleep 0.5
session.send_keys('C-c')
sleep 0.5
session.send_keys('echo')
wait_for { session.content }.to eq("e\necho")
end
end
end
@@ -0,0 +1,14 @@
describe 'with `AUTO_CD` option set' do
let(:after_sourcing) do
-> {
session.run_command('setopt AUTO_CD')
session.run_command('autoload compinit && compinit')
}
end
it 'directory names are still completed' do
session.send_string('sr')
session.send_keys('C-i')
wait_for { session.content }.to eq('src/')
end
end
@@ -0,0 +1,43 @@
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
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
@@ -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
@@ -0,0 +1,12 @@
describe 'with `GLOB_SUBST` option set' do
let(:after_sourcing) do
-> {
session.run_command('setopt GLOB_SUBST')
}
end
it 'error messages are not printed' do
session.send_string('[[')
wait_for { session.content }.to eq('[[')
end
end
@@ -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
@@ -0,0 +1,80 @@
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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,121 @@
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 and moves the cursor to the end of the buffer 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')
wait_for { session.cursor }.to eq([10, 0])
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
context 'when manual rebind is enabled' do
let(:options) { ["ZSH_AUTOSUGGEST_MANUAL_REBIND=true"] }
it 'does not take effect until bind command is re-run' do
with_history('echo hello') do
session.send_string('e')
wait_for { session.content }.to eq('echo hello')
session.send_keys('C-b')
sleep 1
expect(session.content(esc_seqs: true)).not_to eq('echo hello')
session.send_keys('C-c')
session.run_command('_zsh_autosuggest_bind_widgets').clear_screen
wait_for { session.content }.to eq('')
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
end
@@ -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
@@ -0,0 +1,12 @@
require 'strategies/special_characters_helper'
describe 'the `history` suggestion strategy' do
it 'suggests the last matching history entry' do
with_history('ls foo', 'ls bar', 'echo baz') do
session.send_string('ls')
wait_for { session.content }.to eq('ls bar')
end
end
include_examples 'special characters'
end
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,51 @@
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(['source zsh-autosuggestions.zsh', *options].join('; '))
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
@@ -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
@@ -0,0 +1,75 @@
#--------------------------------------------------------------------#
# Async #
#--------------------------------------------------------------------#
_zsh_autosuggest_async_request() {
zmodload zsh/system 2>/dev/null # For `$sysparams`
typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
# If we've got a pending request, cancel it
if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
# Close the file descriptor and remove the handler
exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
# We won't know the pid unless the user has zsh/system module installed
if [[ -n "$_ZSH_AUTOSUGGEST_CHILD_PID" ]]; then
# Zsh will make a new process group for the child process only if job
# control is enabled (MONITOR option)
if [[ -o MONITOR ]]; then
# Send the signal to the process group to kill any processes that may
# have been forked by the suggestion strategy
kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
else
# Kill just the child process since it wasn't placed in a new process
# 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
# Fork a process to fetch a suggestion and open a pipe to read from it
exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
# Tell parent process our pid
echo $sysparams[pid]
# Fetch and print the suggestion
local suggestion
_zsh_autosuggest_fetch_suggestion "$1"
echo -nE "$suggestion"
)
# There's a weird bug here where ^C stops working unless we force a fork
# See https://github.com/zsh-users/zsh-autosuggestions/issues/364
command true
# Read the pid from the child process
read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD
# When the fd is readable, call the response handler
zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
}
# Called when new data is ready to be read from the pipe
# First arg will be fd ready for reading
# Second arg will be passed in case of error
_zsh_autosuggest_async_response() {
emulate -L zsh
local suggestion
if [[ -z "$2" || "$2" == "hup" ]]; then
# Read everything from the fd and give it as a suggestion
IFS='' read -rd '' -u $1 suggestion
zle autosuggest-suggest -- "$suggestion"
# Close the fd
exec {1}<&-
fi
# Always remove the handler
zle -F "$1"
}
@@ -0,0 +1,106 @@
#--------------------------------------------------------------------#
# Widget Helpers #
#--------------------------------------------------------------------#
_zsh_autosuggest_incr_bind_count() {
typeset -gi bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]+1))
_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=$bind_count
}
# 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)_*)
bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$widget]))
;;
# 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
# 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=(
.\*
_\*
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
}
@@ -0,0 +1,93 @@
#--------------------------------------------------------------------#
# 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} )) &&
typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
# Prefix to use when saving original versions of bound widgets
(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) &&
typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
# Strategies to use to fetch a suggestion
# Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
typeset -ga ZSH_AUTOSUGGEST_STRATEGY
ZSH_AUTOSUGGEST_STRATEGY=(history)
}
# Widgets that clear the suggestion
(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS
ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward
history-search-backward
history-beginning-search-forward
history-beginning-search-backward
history-substring-search-up
history-substring-search-down
up-line-or-beginning-search
down-line-or-beginning-search
up-line-or-history
down-line-or-history
accept-line
copy-earlier-word
)
}
# Widgets that accept the entire suggestion
(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char
end-of-line
vi-forward-char
vi-end-of-line
vi-add-eol
)
}
# Widgets that accept the entire suggestion and execute it
(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS
ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
)
}
# Widgets that accept the suggestion as far as the cursor moves
(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word
emacs-forward-word
vi-forward-word
vi-forward-word-end
vi-forward-blank-word
vi-forward-blank-word-end
vi-find-next-char
vi-find-next-char-skip
)
}
# Widgets that should be ignored (globbing supported but must be escaped)
(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS
ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
orig-\*
beep
run-help
set-local-history
which-command
yank
yank-pop
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
@@ -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
}
}
@@ -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}*]}"
}
@@ -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]"
}
@@ -0,0 +1,27 @@
#--------------------------------------------------------------------#
# Fetch Suggestion #
#--------------------------------------------------------------------#
# Loops through all specified strategies and returns a suggestion
# from the first strategy to provide one.
#
_zsh_autosuggest_fetch_suggestion() {
typeset -g suggestion
local -a strategies
local strategy
# Ensure we are working with an array
strategies=(${=ZSH_AUTOSUGGEST_STRATEGY})
for strategy in $strategies; do
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Ensure the suggestion matches the prefix
[[ "$suggestion" != "$1"* ]] && unset suggestion
# Break once we've found a valid suggestion
[[ -n "$suggestion" ]] && break
done
}
@@ -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
}
@@ -0,0 +1,23 @@
#--------------------------------------------------------------------#
# Start #
#--------------------------------------------------------------------#
# Start the autosuggestion widgets
_zsh_autosuggest_start() {
# By default we 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. However this has
# a decent performance hit, so users can set ZSH_AUTOSUGGEST_MANUAL_REBIND
# to disable the automatic re-binding.
if (( ${+ZSH_AUTOSUGGEST_MANUAL_REBIND} )); then
add-zsh-hook -d precmd _zsh_autosuggest_start
fi
_zsh_autosuggest_bind_widgets
}
# Start the autosuggestion widgets on the next precmd
autoload -Uz add-zsh-hook
add-zsh-hook precmd _zsh_autosuggest_start
@@ -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}"
}
@@ -0,0 +1,224 @@
#--------------------------------------------------------------------#
# 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() {
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=$?
emulate -L zsh
# Don't fetch a new suggestion if there's more input to be read immediately
if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
POSTDISPLAY="$orig_postdisplay"
return $retval
fi
# Optimize if manually typing in the suggestion
if (( $#BUFFER > $#orig_buffer )); then
local added=${BUFFER#$orig_buffer}
# If the string added matches the beginning of the postdisplay
if [[ "$added" = "${orig_postdisplay:0:$#added}" ]]; then
POSTDISPLAY="${orig_postdisplay:$#added}"
return $retval
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 (( ${+ZSH_AUTOSUGGEST_USE_ASYNC} )); 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
if [[ "$KEYMAP" = "vicmd" ]]; then
CURSOR=$(($#BUFFER - 1))
else
CURSOR=$#BUFFER
fi
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
}
() {
local action
for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
eval "_zsh_autosuggest_widget_$action() {
local -i retval
_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
}
@@ -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' \
--with-tcsetpgrp \
--program-suffix="-$v"
make install.bin
make install.modules
make install.fns
cd ..
rm -rf zsh-$v
done
@@ -0,0 +1 @@
source ${0:A:h}/zsh-autosuggestions.zsh
@@ -0,0 +1,825 @@
# Fish-like fast/unobtrusive autosuggestions for zsh.
# https://github.com/zsh-users/zsh-autosuggestions
# v0.6.3
# Copyright (c) 2013 Thiago de Arruda
# Copyright (c) 2016-2019 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.
#--------------------------------------------------------------------#
# 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} )) &&
typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
# Prefix to use when saving original versions of bound widgets
(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) &&
typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig-
# Strategies to use to fetch a suggestion
# Will try each strategy in order until a suggestion is returned
(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && {
typeset -ga ZSH_AUTOSUGGEST_STRATEGY
ZSH_AUTOSUGGEST_STRATEGY=(history)
}
# Widgets that clear the suggestion
(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS
ZSH_AUTOSUGGEST_CLEAR_WIDGETS=(
history-search-forward
history-search-backward
history-beginning-search-forward
history-beginning-search-backward
history-substring-search-up
history-substring-search-down
up-line-or-beginning-search
down-line-or-beginning-search
up-line-or-history
down-line-or-history
accept-line
copy-earlier-word
)
}
# Widgets that accept the entire suggestion
(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(
forward-char
end-of-line
vi-forward-char
vi-end-of-line
vi-add-eol
)
}
# Widgets that accept the entire suggestion and execute it
(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS
ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=(
)
}
# Widgets that accept the suggestion as far as the cursor moves
(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS
ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(
forward-word
emacs-forward-word
vi-forward-word
vi-forward-word-end
vi-forward-blank-word
vi-forward-blank-word-end
vi-find-next-char
vi-find-next-char-skip
)
}
# Widgets that should be ignored (globbing supported but must be escaped)
(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && {
typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS
ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(
orig-\*
beep
run-help
set-local-history
which-command
yank
yank-pop
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 #
#--------------------------------------------------------------------#
_zsh_autosuggest_escape_command() {
setopt localoptions EXTENDED_GLOB
# Escape special chars in the string (requires EXTENDED_GLOB)
echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}"
}
#--------------------------------------------------------------------#
# Widget Helpers #
#--------------------------------------------------------------------#
_zsh_autosuggest_incr_bind_count() {
typeset -gi bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]+1))
_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=$bind_count
}
# 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)_*)
bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$widget]))
;;
# 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
# 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=(
.\*
_\*
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() {
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=$?
emulate -L zsh
# Don't fetch a new suggestion if there's more input to be read immediately
if (( $PENDING > 0 )) || (( $KEYS_QUEUED_COUNT > 0 )); then
POSTDISPLAY="$orig_postdisplay"
return $retval
fi
# Optimize if manually typing in the suggestion
if (( $#BUFFER > $#orig_buffer )); then
local added=${BUFFER#$orig_buffer}
# If the string added matches the beginning of the postdisplay
if [[ "$added" = "${orig_postdisplay:0:$#added}" ]]; then
POSTDISPLAY="${orig_postdisplay:$#added}"
return $retval
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 (( ${+ZSH_AUTOSUGGEST_USE_ASYNC} )); 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
if [[ "$KEYMAP" = "vicmd" ]]; then
CURSOR=$(($#BUFFER - 1))
else
CURSOR=$#BUFFER
fi
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
}
() {
local action
for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do
eval "_zsh_autosuggest_widget_$action() {
local -i retval
_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
}
#--------------------------------------------------------------------#
# 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 #
#--------------------------------------------------------------------#
# 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
local strategy
# Ensure we are working with an array
strategies=(${=ZSH_AUTOSUGGEST_STRATEGY})
for strategy in $strategies; do
# Try to get a suggestion from this strategy
_zsh_autosuggest_strategy_$strategy "$1"
# Ensure the suggestion matches the prefix
[[ "$suggestion" != "$1"* ]] && unset suggestion
# Break once we've found a valid suggestion
[[ -n "$suggestion" ]] && break
done
}
#--------------------------------------------------------------------#
# Async #
#--------------------------------------------------------------------#
_zsh_autosuggest_async_request() {
zmodload zsh/system 2>/dev/null # For `$sysparams`
typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID
# If we've got a pending request, cancel it
if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then
# Close the file descriptor and remove the handler
exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&-
zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD
# We won't know the pid unless the user has zsh/system module installed
if [[ -n "$_ZSH_AUTOSUGGEST_CHILD_PID" ]]; then
# Zsh will make a new process group for the child process only if job
# control is enabled (MONITOR option)
if [[ -o MONITOR ]]; then
# Send the signal to the process group to kill any processes that may
# have been forked by the suggestion strategy
kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null
else
# Kill just the child process since it wasn't placed in a new process
# 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
# Fork a process to fetch a suggestion and open a pipe to read from it
exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <(
# Tell parent process our pid
echo $sysparams[pid]
# Fetch and print the suggestion
local suggestion
_zsh_autosuggest_fetch_suggestion "$1"
echo -nE "$suggestion"
)
# There's a weird bug here where ^C stops working unless we force a fork
# See https://github.com/zsh-users/zsh-autosuggestions/issues/364
command true
# Read the pid from the child process
read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD
# When the fd is readable, call the response handler
zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response
}
# Called when new data is ready to be read from the pipe
# First arg will be fd ready for reading
# Second arg will be passed in case of error
_zsh_autosuggest_async_response() {
emulate -L zsh
local suggestion
if [[ -z "$2" || "$2" == "hup" ]]; then
# Read everything from the fd and give it as a suggestion
IFS='' read -rd '' -u $1 suggestion
zle autosuggest-suggest -- "$suggestion"
# Close the fd
exec {1}<&-
fi
# Always remove the handler
zle -F "$1"
}
#--------------------------------------------------------------------#
# Start #
#--------------------------------------------------------------------#
# Start the autosuggestion widgets
_zsh_autosuggest_start() {
# By default we 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. However this has
# a decent performance hit, so users can set ZSH_AUTOSUGGEST_MANUAL_REBIND
# to disable the automatic re-binding.
if (( ${+ZSH_AUTOSUGGEST_MANUAL_REBIND} )); then
add-zsh-hook -d precmd _zsh_autosuggest_start
fi
_zsh_autosuggest_bind_widgets
}
# Start the autosuggestion widgets on the next precmd
autoload -Uz add-zsh-hook
add-zsh-hook precmd _zsh_autosuggest_start
@@ -0,0 +1,4 @@
# Put your custom themes in this folder.
# Example:
PROMPT="%{$fg[red]%}%n%{$reset_color%}@%{$fg[blue]%}%m %{$fg[yellow]%}%~ %{$reset_color%}%% "
@@ -0,0 +1,10 @@
# You can put files here to add functionality separated per file, which
# will be ignored by git.
# Files on the custom/ directory will be automatically loaded by the init
# script, in alphabetical order.
# For example: add yourself some shortcuts to projects you often work on.
#
# brainstormr=~/Projects/development/planetargon/brainstormr
# cd $brainstormr
#

Some files were not shown because too many files have changed in this diff Show More