diff --git a/.github/dependencies.yml b/.github/dependencies.yml
index 9ada57348..2c33dd28b 100644
--- a/.github/dependencies.yml
+++ b/.github/dependencies.yml
@@ -55,3 +55,46 @@ dependencies:
set -e
test -e _zshz && mv -f _zshz _z
test -e zsh-z.plugin.zsh && mv -f zsh-z.plugin.zsh z.plugin.zsh
+ plugins/zsh-autosuggestions:
+ repo: zsh-users/zsh-autosuggestions
+ branch: master
+ version: 85919cd1ffa7d2d5412f6d3fe437ebdbeeec4fc5
+ precopy: |
+ set -e
+ test -e README.md && mv -f README.md MANUAL.md
+ sed -i 's|\[INSTALL.md\](INSTALL.md)|[the Oh My Zsh plugin README](README.md)|' MANUAL.md
+ rm -f zsh-autosuggestions.plugin.zsh
+ mv -f zsh-autosuggestions.zsh zsh-autosuggestions.plugin.zsh
+ find . -depth -mindepth 1 \
+ ! -path './LICENSE' \
+ ! -path './MANUAL.md' \
+ ! -path './src' \
+ ! -path './src/*' \
+ ! -path './zsh-autosuggestions.plugin.zsh' \
+ -delete
+ plugins/zsh-syntax-highlighting:
+ repo: zsh-users/zsh-syntax-highlighting
+ branch: master
+ version: 2fc57d63067c18b1100ecdbf684fa5baf49459d1
+ precopy: |
+ set -e
+ version=$(<.version)
+ test -e README.md && mv -f README.md MANUAL.md
+ sed -i 's|\[INSTALL.md\](INSTALL.md)|[the Oh My Zsh plugin README](README.md)|' MANUAL.md
+ rm -f zsh-syntax-highlighting.plugin.zsh
+ mv -f zsh-syntax-highlighting.zsh zsh-syntax-highlighting.plugin.zsh
+ mv -f COPYING.md LICENSE
+ sed -i "s|typeset -g ZSH_HIGHLIGHT_VERSION=.*|typeset -g ZSH_HIGHLIGHT_VERSION=$version|" zsh-syntax-highlighting.plugin.zsh
+ sed -i 's|typeset -g ZSH_HIGHLIGHT_REVISION=.*|typeset -g ZSH_HIGHLIGHT_REVISION=HEAD|' zsh-syntax-highlighting.plugin.zsh
+ find highlighters -type d -name test-data -prune -exec rm -rf {} +
+ find . -depth -mindepth 1 \
+ ! -path './LICENSE' \
+ ! -path './MANUAL.md' \
+ ! -path './docs' \
+ ! -path './docs/*' \
+ ! -path './highlighters' \
+ ! -path './highlighters/*' \
+ ! -path './images' \
+ ! -path './images/*' \
+ ! -path './zsh-syntax-highlighting.plugin.zsh' \
+ -delete
diff --git a/plugins/zsh-autosuggestions/LICENSE b/plugins/zsh-autosuggestions/LICENSE
new file mode 100644
index 000000000..7ea78cc64
--- /dev/null
+++ b/plugins/zsh-autosuggestions/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2013 Thiago de Arruda
+Copyright (c) 2016-2021 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.
diff --git a/plugins/zsh-autosuggestions/MANUAL.md b/plugins/zsh-autosuggestions/MANUAL.md
new file mode 100644
index 000000000..d4e14952b
--- /dev/null
+++ b/plugins/zsh-autosuggestions/MANUAL.md
@@ -0,0 +1,188 @@
+# zsh-autosuggestions
+
+_[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._
+
+It suggests commands as you type based on history and completions.
+
+Requirements: Zsh v4.3.11 or later
+
+[](https://gitter.im/zsh-users/zsh-autosuggestions)
+
+
+
+
+## Installation
+
+See [the Oh My Zsh plugin README](README.md).
+
+
+## Usage
+
+As you type commands, you will see a completion offered after the cursor in a muted gray color. This color can be changed by setting the `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` variable. See [configuration](#configuration).
+
+If you press the → key (`forward-char` widget) or End (`end-of-line` widget) with the cursor at the end of the buffer, it will accept the suggestion, replacing the contents of the command line buffer with the suggestion.
+
+If you invoke the `forward-word` widget, it will partially accept the suggestion up to the point that the cursor moves to.
+
+
+## Configuration
+
+You may want to override the default global config variables. Default values of these variables can be found [here](src/config.zsh).
+
+**Note:** If you are using Oh My Zsh, you can put this configuration in a file in the `$ZSH_CUSTOM` directory. See their comments on [overriding internals](https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals).
+
+
+### Suggestion Highlight Style
+
+Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion is shown with. The default is `fg=8`, which will set the foreground color to color 8 from the [256-color palette](https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg). If your terminal only supports 8 colors, you will need to use a number between 0 and 7.
+
+Background color can also be set, and the suggestion can be styled bold, underlined, or standout. For example, this would show suggestions with bold, underlined, pink text on a cyan background:
+
+```sh
+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#ff00ff,bg=cyan,bold,underline"
+```
+
+For more info, read the Character Highlighting section of the zsh manual: `man zshzle` or [online](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting).
+
+**Note:** Some iTerm2 users have reported [not being able to see the suggestions](https://github.com/zsh-users/zsh-autosuggestions/issues/416#issuecomment-486516333). If this affects you, the problem is likely caused by incorrect color settings. In order to correct this, go into iTerm2's setting, navigate to profile > colors and make sure that the colors for Basic Colors > Background and ANSI Colors > Bright Black are **different**.
+
+
+### Suggestion Strategy
+
+`ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. The strategies in the array are tried successively until a suggestion is found. There are currently three built-in strategies to choose from:
+
+- `history`: Chooses the most recent match from history.
+- `completion`: Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module, which is included with zsh since 4.0.1)
+- `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`.
+
+For example, setting `ZSH_AUTOSUGGEST_STRATEGY=(history completion)` will first try to find a suggestion from your history, but, if it can't find a match, will find a suggestion from the completion engine.
+
+
+### Widget Mapping
+
+This plugin works by triggering custom behavior when certain [zle widgets](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets) are invoked. You can add and remove widgets from these arrays to change the behavior of this plugin:
+
+- `ZSH_AUTOSUGGEST_CLEAR_WIDGETS`: Widgets in this array will clear the suggestion when invoked.
+- `ZSH_AUTOSUGGEST_ACCEPT_WIDGETS`: Widgets in this array will accept the suggestion when invoked.
+- `ZSH_AUTOSUGGEST_EXECUTE_WIDGETS`: Widgets in this array will execute the suggestion when invoked.
+- `ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS`: Widgets in this array will partially accept the suggestion when invoked.
+- `ZSH_AUTOSUGGEST_IGNORE_WIDGETS`: Widgets in this array will not trigger any custom behavior.
+
+Widgets that modify the buffer and are not found in any of these arrays will fetch a new suggestion after they are invoked.
+
+**Note:** A widget shouldn't belong to more than one of the above arrays.
+
+
+### Disabling suggestion for large buffers
+
+Set `ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE` to an integer value to disable autosuggestion for large buffers. The default is unset, which means that autosuggestion will be tried for any buffer size. Recommended value is 20.
+This can be useful when pasting large amount of text in the terminal, to avoid triggering autosuggestion for strings that are too long.
+
+### Asynchronous Mode
+
+Suggestions are fetched asynchronously by default in zsh versions 5.0.8 and greater. To disable asynchronous suggestions and fetch them synchronously instead, `unset ZSH_AUTOSUGGEST_USE_ASYNC` after sourcing the plugin.
+
+Alternatively, if you are using a version of zsh older than 5.0.8 and want to enable asynchronous mode, set the `ZSH_AUTOSUGGEST_USE_ASYNC` variable after sourcing the plugin (it can be set to anything). Note that there is [a bug](https://github.com/zsh-users/zsh-autosuggestions/issues/364#issuecomment-481423232) in versions of zsh older than 5.0.8 where ctrl + c will fail to reset the prompt immediately after fetching a suggestion asynchronously.
+
+### Disabling automatic widget re-binding
+
+Set `ZSH_AUTOSUGGEST_MANUAL_REBIND` (it can be set to anything) to disable automatic widget re-binding on each precmd. This can be a big boost to performance, but you'll need to handle re-binding yourself if any of the widget lists change or if you or another plugin wrap any of the autosuggest widgets. To re-bind widgets, run `_zsh_autosuggest_bind_widgets`.
+
+### Ignoring history suggestions that match a pattern
+
+Set `ZSH_AUTOSUGGEST_HISTORY_IGNORE` to a [glob pattern](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators) to prevent offering suggestions for history entries that match the pattern. For example, set it to `"cd *"` to never suggest any `cd` commands from history. Or set to `"?(#c50,)"` to never suggest anything 50 characters or longer.
+
+**Note:** This only affects the `history` and `match_prev_cmd` suggestion strategies.
+
+### Skipping completion suggestions for certain cases
+
+Set `ZSH_AUTOSUGGEST_COMPLETION_IGNORE` to a [glob pattern](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators) to prevent offering completion suggestions when the buffer matches that pattern. For example, set it to `"git *"` to disable completion suggestions for git subcommands.
+
+**Note:** This only affects the `completion` suggestion strategy.
+
+
+### Key Bindings
+
+This plugin provides a few widgets that you can use with `bindkey`:
+
+1. `autosuggest-accept`: Accepts the current suggestion.
+2. `autosuggest-execute`: Accepts and executes the current suggestion.
+3. `autosuggest-clear`: Clears the current suggestion.
+4. `autosuggest-fetch`: Fetches a suggestion (works even when suggestions are disabled).
+5. `autosuggest-disable`: Disables suggestions.
+6. `autosuggest-enable`: Re-enables suggestions.
+7. `autosuggest-toggle`: Toggles between enabled/disabled suggestions.
+
+For example, this would bind ctrl + space to accept the current suggestion.
+
+```sh
+bindkey '^ ' autosuggest-accept
+```
+
+
+## Troubleshooting
+
+If you have a problem, please search through [the list of issues on GitHub](https://github.com/zsh-users/zsh-autosuggestions/issues?q=) to see if someone else has already reported it.
+
+### Reporting an Issue
+
+Before reporting an issue, please try temporarily disabling sections of your configuration and other plugins that may be conflicting with this plugin to isolate the problem.
+
+When reporting an issue, please include:
+
+- The smallest, simplest `.zshrc` configuration that will reproduce the problem. See [this comment](https://github.com/zsh-users/zsh-autosuggestions/issues/102#issuecomment-180944764) for a good example of what this means.
+- The version of zsh you're using (`zsh --version`)
+- Which operating system you're running
+
+
+## Uninstallation
+
+1. Remove the code referencing this plugin from `~/.zshrc`.
+
+2. Remove the git repository from your hard drive
+
+ ```sh
+ rm -rf ~/.zsh/zsh-autosuggestions # Or wherever you installed
+ ```
+
+
+## Development
+
+### Build Process
+
+Edit the source files in `src/`. Run `make` to build `zsh-autosuggestions.zsh` from those source files.
+
+
+### Pull Requests
+
+Pull requests are welcome! If you send a pull request, please:
+
+- Request to merge into the `develop` branch (*NOT* `master`)
+- Match the existing coding conventions.
+- Include helpful comments to keep the barrier-to-entry low for people new to the project.
+- Write tests that cover your code as much as possible.
+
+
+### Testing
+
+Tests are written in ruby using the [`rspec`](http://rspec.info/) framework. They use [`tmux`](https://tmux.github.io/) to drive a pseudoterminal, sending simulated keystrokes and making assertions on the terminal content.
+
+Test files live in `spec/`. To run the tests, run `make test`. To run a specific test, run `TESTS=spec/some_spec.rb make test`. You can also specify a `zsh` binary to use by setting the `TEST_ZSH_BIN` environment variable (ex: `TEST_ZSH_BIN=/bin/zsh make test`).
+
+It's possible to run the tests for any supported version of zsh in a Docker image by building an image from the provided Dockerfile. To build the docker image for a specific version of zsh (where `` below is substituted with the contents of a line from the [`ZSH_VERSIONS`](ZSH_VERSIONS) file), run:
+
+```sh
+docker build --build-arg TEST_ZSH_VERSION= -t zsh-autosuggestions-test .
+```
+
+After building the image, run the tests via:
+
+```sh
+docker run -it -v $PWD:/zsh-autosuggestions zsh-autosuggestions-test make test
+```
+
+
+## License
+
+This project is licensed under [MIT license](http://opensource.org/licenses/MIT).
+For the full text of the license, see the [LICENSE](LICENSE) file.
diff --git a/plugins/zsh-autosuggestions/README.md b/plugins/zsh-autosuggestions/README.md
new file mode 100644
index 000000000..7afac2773
--- /dev/null
+++ b/plugins/zsh-autosuggestions/README.md
@@ -0,0 +1,16 @@
+# zsh-autosuggestions
+
+This plugin suggests commands as you type, based on command history and completions.
+
+To use it, add `zsh-autosuggestions` to the plugins array in your zshrc file:
+
+```zsh
+plugins=(... zsh-autosuggestions)
+```
+
+Start a new terminal session after updating your zshrc file.
+
+Suggestions appear in muted gray text by default. Press → or End at the end of the
+command line to accept the displayed suggestion.
+
+For configuration options and additional key bindings, see the [upstream documentation](MANUAL.md).
diff --git a/plugins/zsh-autosuggestions/src/async.zsh b/plugins/zsh-autosuggestions/src/async.zsh
new file mode 100644
index 000000000..e1797345f
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/async.zsh
@@ -0,0 +1,77 @@
+
+#--------------------------------------------------------------------#
+# 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
+ builtin 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
+ builtin 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
+ autoload -Uz is-at-least
+ is-at-least 5.8 || 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
+ builtin exec {1}<&-
+ fi
+
+ # Always remove the handler
+ zle -F "$1"
+ _ZSH_AUTOSUGGEST_ASYNC_FD=
+}
diff --git a/plugins/zsh-autosuggestions/src/bind.zsh b/plugins/zsh-autosuggestions/src/bind.zsh
new file mode 100644
index 000000000..1dde137c0
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/bind.zsh
@@ -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=(
+ .\*
+ _\*
+ ${_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS/#/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
+}
diff --git a/plugins/zsh-autosuggestions/src/config.zsh b/plugins/zsh-autosuggestions/src/config.zsh
new file mode 100644
index 000000000..32d32b250
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/config.zsh
@@ -0,0 +1,95 @@
+
+#--------------------------------------------------------------------#
+# 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-beginning-search-forward-end
+ history-beginning-search-backward-end
+ 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
diff --git a/plugins/zsh-autosuggestions/src/fetch.zsh b/plugins/zsh-autosuggestions/src/fetch.zsh
new file mode 100644
index 000000000..fef271520
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/fetch.zsh
@@ -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
+}
diff --git a/plugins/zsh-autosuggestions/src/highlight.zsh b/plugins/zsh-autosuggestions/src/highlight.zsh
new file mode 100644
index 000000000..273c03d53
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/highlight.zsh
@@ -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
+}
diff --git a/plugins/zsh-autosuggestions/src/start.zsh b/plugins/zsh-autosuggestions/src/start.zsh
new file mode 100644
index 000000000..5d4ee521e
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/start.zsh
@@ -0,0 +1,33 @@
+
+#--------------------------------------------------------------------#
+# 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
+}
+
+# Mark for auto-loading the functions that we use
+autoload -Uz add-zsh-hook is-at-least
+
+# Automatically enable asynchronous mode in newer versions of zsh. Disable for
+# older versions because there is a bug when using async mode where ^C does not
+# work immediately after fetching a suggestion.
+# See https://github.com/zsh-users/zsh-autosuggestions/issues/364
+if is-at-least 5.0.8; then
+ typeset -g ZSH_AUTOSUGGEST_USE_ASYNC=
+fi
+
+# Start the autosuggestion widgets on the next precmd
+add-zsh-hook precmd _zsh_autosuggest_start
diff --git a/plugins/zsh-autosuggestions/src/strategies/completion.zsh b/plugins/zsh-autosuggestions/src/strategies/completion.zsh
new file mode 100644
index 000000000..e2d114c8a
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/strategies/completion.zsh
@@ -0,0 +1,137 @@
+
+#--------------------------------------------------------------------#
+# 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() {
+ # 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() {
+ # Reset options to defaults and enable LOCAL_OPTIONS
+ emulate -L zsh
+
+ # Enable extended glob for completion ignore pattern
+ setopt EXTENDED_GLOB
+
+ 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
+
+ # Exit if our search string matches the ignore pattern
+ [[ -n "$ZSH_AUTOSUGGEST_COMPLETION_IGNORE" ]] && [[ "$1" == $~ZSH_AUTOSUGGEST_COMPLETION_IGNORE ]] && 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="${${(@0)line}[2]}"
+ } always {
+ # Destroy the pty
+ zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME
+ }
+}
diff --git a/plugins/zsh-autosuggestions/src/strategies/history.zsh b/plugins/zsh-autosuggestions/src/strategies/history.zsh
new file mode 100644
index 000000000..0672a1364
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/strategies/history.zsh
@@ -0,0 +1,32 @@
+
+#--------------------------------------------------------------------#
+# 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) and (x~y) glob operator
+ 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 the prefix, excluding those that match
+ # the ignore pattern
+ local pattern="$prefix*"
+ if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then
+ pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)"
+ fi
+
+ # Give the first history item matching the pattern as the suggestion
+ # - (r) subscript flag makes the pattern match on values
+ typeset -g suggestion="${history[(r)$pattern]}"
+}
diff --git a/plugins/zsh-autosuggestions/src/strategies/match_prev_cmd.zsh b/plugins/zsh-autosuggestions/src/strategies/match_prev_cmd.zsh
new file mode 100644
index 000000000..b7097837e
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/strategies/match_prev_cmd.zsh
@@ -0,0 +1,66 @@
+
+#--------------------------------------------------------------------#
+# 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) and (x~y) glob operator
+ 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 the history items that match the prefix, excluding those that match
+ # the ignore pattern
+ local pattern="$prefix*"
+ if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then
+ pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)"
+ fi
+
+ # Get all history event numbers that correspond to history
+ # entries that match the pattern
+ local history_match_keys
+ history_match_keys=(${(k)history[(R)$~pattern]})
+
+ # 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]"
+}
diff --git a/plugins/zsh-autosuggestions/src/util.zsh b/plugins/zsh-autosuggestions/src/util.zsh
new file mode 100644
index 000000000..1f55d3634
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/util.zsh
@@ -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}"
+}
diff --git a/plugins/zsh-autosuggestions/src/widgets.zsh b/plugins/zsh-autosuggestions/src/widgets.zsh
new file mode 100644
index 000000000..7562897ea
--- /dev/null
+++ b/plugins/zsh-autosuggestions/src/widgets.zsh
@@ -0,0 +1,231 @@
+
+#--------------------------------------------------------------------#
+# 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 (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); then
+ _zsh_autosuggest_enable
+ else
+ _zsh_autosuggest_disable
+ fi
+}
+
+# Clear the suggestion
+_zsh_autosuggest_clear() {
+ # Remove the suggestion
+ 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
+ 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 or if buffer hasn't changed
+ if [[ "$BUFFER" = "$orig_buffer"* && "$orig_postdisplay" = "${BUFFER:$#orig_buffer}"* ]]; then
+ POSTDISPLAY="${orig_postdisplay:$(($#BUFFER - $#orig_buffer))}"
+ return $retval
+ fi
+
+ # Bail out if suggestions are disabled
+ if (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); 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
+ POSTDISPLAY=
+ fi
+}
+
+# Accept the entire suggestion
+_zsh_autosuggest_accept() {
+ local -i retval 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
+
+ # If we're not in a valid state to accept a suggestion, just run the
+ # original widget and bail out
+ if (( $CURSOR != $max_cursor_pos || !$#POSTDISPLAY )); then
+ _zsh_autosuggest_invoke_original_widget $@
+ return
+ fi
+
+ # Only accept if the cursor is at the end of the buffer
+ # Add the suggestion to the buffer
+ BUFFER="$BUFFER$POSTDISPLAY"
+
+ # Remove the suggestion
+ POSTDISPLAY=
+
+ # Run the original widget before manually moving the cursor so that the
+ # cursor movement doesn't make the widget do something unexpected
+ _zsh_autosuggest_invoke_original_widget $@
+ retval=$?
+
+ # Move the cursor to the end of the buffer
+ if [[ "$KEYMAP" = "vicmd" ]]; then
+ CURSOR=$(($#BUFFER - 1))
+ else
+ CURSOR=$#BUFFER
+ fi
+
+ return $retval
+}
+
+# Accept the entire suggestion and execute it
+_zsh_autosuggest_execute() {
+ # Add the suggestion to the buffer
+ BUFFER="$BUFFER$POSTDISPLAY"
+
+ # Remove the suggestion
+ 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
+}
+
+() {
+ typeset -ga _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS
+
+ _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS=(
+ clear
+ fetch
+ suggest
+ accept
+ execute
+ enable
+ disable
+ toggle
+ )
+
+ local action
+ for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS modify partial_accept; 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
+
+ for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS; do
+ zle -N autosuggest-$action _zsh_autosuggest_widget_$action
+ done
+}
diff --git a/plugins/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh b/plugins/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh
new file mode 100644
index 000000000..e78022516
--- /dev/null
+++ b/plugins/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh
@@ -0,0 +1,867 @@
+# Fish-like fast/unobtrusive autosuggestions for zsh.
+# https://github.com/zsh-users/zsh-autosuggestions
+# v0.7.1
+# Copyright (c) 2013 Thiago de Arruda
+# Copyright (c) 2016-2021 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-beginning-search-forward-end
+ history-beginning-search-backward-end
+ 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=(
+ .\*
+ _\*
+ ${_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS/#/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 (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); then
+ _zsh_autosuggest_enable
+ else
+ _zsh_autosuggest_disable
+ fi
+}
+
+# Clear the suggestion
+_zsh_autosuggest_clear() {
+ # Remove the suggestion
+ 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
+ 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 or if buffer hasn't changed
+ if [[ "$BUFFER" = "$orig_buffer"* && "$orig_postdisplay" = "${BUFFER:$#orig_buffer}"* ]]; then
+ POSTDISPLAY="${orig_postdisplay:$(($#BUFFER - $#orig_buffer))}"
+ return $retval
+ fi
+
+ # Bail out if suggestions are disabled
+ if (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); 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
+ POSTDISPLAY=
+ fi
+}
+
+# Accept the entire suggestion
+_zsh_autosuggest_accept() {
+ local -i retval 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
+
+ # If we're not in a valid state to accept a suggestion, just run the
+ # original widget and bail out
+ if (( $CURSOR != $max_cursor_pos || !$#POSTDISPLAY )); then
+ _zsh_autosuggest_invoke_original_widget $@
+ return
+ fi
+
+ # Only accept if the cursor is at the end of the buffer
+ # Add the suggestion to the buffer
+ BUFFER="$BUFFER$POSTDISPLAY"
+
+ # Remove the suggestion
+ POSTDISPLAY=
+
+ # Run the original widget before manually moving the cursor so that the
+ # cursor movement doesn't make the widget do something unexpected
+ _zsh_autosuggest_invoke_original_widget $@
+ retval=$?
+
+ # Move the cursor to the end of the buffer
+ if [[ "$KEYMAP" = "vicmd" ]]; then
+ CURSOR=$(($#BUFFER - 1))
+ else
+ CURSOR=$#BUFFER
+ fi
+
+ return $retval
+}
+
+# Accept the entire suggestion and execute it
+_zsh_autosuggest_execute() {
+ # Add the suggestion to the buffer
+ BUFFER="$BUFFER$POSTDISPLAY"
+
+ # Remove the suggestion
+ 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
+}
+
+() {
+ typeset -ga _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS
+
+ _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS=(
+ clear
+ fetch
+ suggest
+ accept
+ execute
+ enable
+ disable
+ toggle
+ )
+
+ local action
+ for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS modify partial_accept; 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
+
+ for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS; do
+ zle -N autosuggest-$action _zsh_autosuggest_widget_$action
+ done
+}
+
+#--------------------------------------------------------------------#
+# 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() {
+ # 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() {
+ # Reset options to defaults and enable LOCAL_OPTIONS
+ emulate -L zsh
+
+ # Enable extended glob for completion ignore pattern
+ setopt EXTENDED_GLOB
+
+ 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
+
+ # Exit if our search string matches the ignore pattern
+ [[ -n "$ZSH_AUTOSUGGEST_COMPLETION_IGNORE" ]] && [[ "$1" == $~ZSH_AUTOSUGGEST_COMPLETION_IGNORE ]] && 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="${${(@0)line}[2]}"
+ } 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) and (x~y) glob operator
+ 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 the prefix, excluding those that match
+ # the ignore pattern
+ local pattern="$prefix*"
+ if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then
+ pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)"
+ fi
+
+ # Give the first history item matching the pattern as the suggestion
+ # - (r) subscript flag makes the pattern match on values
+ typeset -g suggestion="${history[(r)$pattern]}"
+}
+
+#--------------------------------------------------------------------#
+# 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) and (x~y) glob operator
+ 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 the history items that match the prefix, excluding those that match
+ # the ignore pattern
+ local pattern="$prefix*"
+ if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then
+ pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)"
+ fi
+
+ # Get all history event numbers that correspond to history
+ # entries that match the pattern
+ local history_match_keys
+ history_match_keys=(${(k)history[(R)$~pattern]})
+
+ # 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
+ builtin 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
+ builtin 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
+ autoload -Uz is-at-least
+ is-at-least 5.8 || 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
+ builtin exec {1}<&-
+ fi
+
+ # Always remove the handler
+ zle -F "$1"
+ _ZSH_AUTOSUGGEST_ASYNC_FD=
+}
+
+#--------------------------------------------------------------------#
+# 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
+}
+
+# Mark for auto-loading the functions that we use
+autoload -Uz add-zsh-hook is-at-least
+
+# Automatically enable asynchronous mode in newer versions of zsh. Disable for
+# older versions because there is a bug when using async mode where ^C does not
+# work immediately after fetching a suggestion.
+# See https://github.com/zsh-users/zsh-autosuggestions/issues/364
+if is-at-least 5.0.8; then
+ typeset -g ZSH_AUTOSUGGEST_USE_ASYNC=
+fi
+
+# Start the autosuggestion widgets on the next precmd
+add-zsh-hook precmd _zsh_autosuggest_start
diff --git a/plugins/zsh-syntax-highlighting/LICENSE b/plugins/zsh-syntax-highlighting/LICENSE
new file mode 100644
index 000000000..cacbcbee0
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2010-2020 zsh-syntax-highlighting contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted
+provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ conditions and the following disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+ may be used to endorse or promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/plugins/zsh-syntax-highlighting/MANUAL.md b/plugins/zsh-syntax-highlighting/MANUAL.md
new file mode 100644
index 000000000..e4a34bff0
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/MANUAL.md
@@ -0,0 +1,97 @@
+zsh-syntax-highlighting [![Build Status][build-status-image]][build-status]
+=======================
+
+**[Fish shell][fish]-like syntax highlighting for [Zsh][zsh].**
+
+*Requirements: zsh 4.3.11+.*
+
+[fish]: https://fishshell.com/
+[zsh]: https://www.zsh.org/
+
+This package provides syntax highlighting for the shell zsh. It enables
+highlighting of commands whilst they are typed at a zsh prompt into an
+interactive terminal. This helps in reviewing commands before running
+them, particularly in catching syntax errors.
+
+Some examples:
+
+Before: [](images/before1.png)
+
+After: [](images/after1.png)
+
+Before: [](images/before2.png)
+
+After: [](images/after2.png)
+
+Before: [](images/before3.png)
+
+After: [](images/after3.png)
+
+Before: [](images/before4-smaller.png)
+
+After: [](images/after4-smaller.png)
+
+
+
+How to install
+--------------
+
+See [the Oh My Zsh plugin README](README.md).
+
+
+FAQ
+---
+
+### Why must `zsh-syntax-highlighting.zsh` be sourced at the end of the `.zshrc` file?
+
+zsh-syntax-highlighting works by hooking into the Zsh Line Editor (ZLE) and
+computing syntax highlighting for the command-line buffer as it stands at the
+time z-sy-h's hook is invoked.
+
+In zsh 5.2 and older,
+`zsh-syntax-highlighting.zsh` hooks into ZLE by wrapping ZLE widgets. It must
+be sourced after all custom widgets have been created (i.e., after all `zle -N`
+calls and after running `compinit`) in order to be able to wrap all of them.
+Widgets created after z-sy-h is sourced will work, but will not update the
+syntax highlighting.
+
+In zsh newer than 5.8 (not including 5.8 itself),
+zsh-syntax-highlighting uses the `add-zle-hook-widget` facility to install
+a `zle-line-pre-redraw` hook. Hooks are run in order of registration,
+therefore, z-sy-h must be sourced (and register its hook) after anything else
+that adds hooks that modify the command-line buffer.
+
+### Does syntax highlighting work during incremental history search?
+
+Highlighting the command line during an incremental history search (by default bound to
+to Ctrl+R in zsh's emacs keymap) requires zsh 5.4 or newer.
+
+Under zsh versions older than 5.4, the zsh-default [underlining][zshzle-Character-Highlighting]
+of the matched portion of the buffer remains available, but zsh-syntax-highlighting's
+additional highlighting is unavailable. (Those versions of zsh do not provide
+enough information to allow computing the highlighting correctly.)
+
+See issues [#288][i288] and [#415][i415] for details.
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
+[i288]: https://github.com/zsh-users/zsh-syntax-highlighting/pull/288
+[i415]: https://github.com/zsh-users/zsh-syntax-highlighting/pull/415
+
+### How are new releases announced?
+
+There is currently no "push" announcements channel. However, the following
+alternatives exist:
+
+- GitHub's RSS feed of releases: https://github.com/zsh-users/zsh-syntax-highlighting/releases.atom
+- An anitya entry: https://release-monitoring.org/project/7552/
+
+
+How to tweak
+------------
+
+Syntax highlighting is done by pluggable highlighter scripts. See the
+[documentation on highlighters](docs/highlighters.md) for details and
+configuration settings.
+
+[build-status]: https://github.com/zsh-users/zsh-syntax-highlighting/actions
+[build-status-image]: https://github.com/zsh-users/zsh-syntax-highlighting/workflows/Tests/badge.svg
diff --git a/plugins/zsh-syntax-highlighting/README.md b/plugins/zsh-syntax-highlighting/README.md
new file mode 100644
index 000000000..6b45dc40e
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/README.md
@@ -0,0 +1,15 @@
+# zsh-syntax-highlighting
+
+This plugin enables Fish-like syntax highlighting for commands as they are typed at a zsh prompt.
+
+To use it, add `zsh-syntax-highlighting` to the end of the plugins array in your zshrc file:
+
+```zsh
+plugins=(... zsh-autosuggestions zsh-syntax-highlighting)
+```
+
+The plugin must be loaded after plugins that create or wrap ZLE widgets, so it should remain the last plugin
+in the array. Start a new terminal session after updating your zshrc file.
+
+For available highlighters and configuration options, see the [upstream documentation](MANUAL.md) and the
+[highlighter documentation](docs/highlighters.md).
diff --git a/plugins/zsh-syntax-highlighting/docs/highlighters.md b/plugins/zsh-syntax-highlighting/docs/highlighters.md
new file mode 100644
index 000000000..fb64c3afe
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/docs/highlighters.md
@@ -0,0 +1,132 @@
+zsh-syntax-highlighting / highlighters
+======================================
+
+Syntax highlighting is done by pluggable highlighters:
+
+* `main` - the base highlighter, and the only one [active by default][main].
+* `brackets` - [matches brackets][brackets] and parenthesis.
+* `pattern` - matches [user-defined patterns][pattern].
+* `regexp` - matches [user-defined regular expressions][regexp].
+* `cursor` - matches [the cursor position][cursor].
+* `root` - highlights the whole command line [if the current user is root][root].
+* `line` - applied to [the whole command line][line].
+
+[main]: highlighters/main.md
+[brackets]: highlighters/brackets.md
+[pattern]: highlighters/pattern.md
+[regexp]: highlighters/regexp.md
+[cursor]: highlighters/cursor.md
+[root]: highlighters/root.md
+[line]: highlighters/line.md
+
+
+Highlighter-independent settings
+--------------------------------
+
+By default, all command lines are highlighted. However, it is possible to
+prevent command lines longer than a fixed number of characters from being
+highlighted by setting the variable `${ZSH_HIGHLIGHT_MAXLENGTH}` to the maximum
+length (in characters) of command lines to be highlighter. This is useful when
+editing very long command lines (for example, with the [`fned`][fned] utility
+function). Example:
+
+[fned]: https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#index-zed
+
+```zsh
+ZSH_HIGHLIGHT_MAXLENGTH=512
+```
+
+
+How to activate highlighters
+----------------------------
+
+To activate an highlighter, add it to the `ZSH_HIGHLIGHT_HIGHLIGHTERS` array.
+By default `ZSH_HIGHLIGHT_HIGHLIGHTERS` is `(main)`. For example to activate
+`brackets`, `pattern`, and `cursor` highlighters, in `~/.zshrc` do:
+
+```zsh
+ZSH_HIGHLIGHT_HIGHLIGHTERS+=(brackets pattern cursor)
+```
+
+
+How to tweak highlighters
+-------------------------
+
+Highlighters look up styles from the `ZSH_HIGHLIGHT_STYLES` associative array.
+Navigate into the [individual highlighters' documentation](highlighters/) to
+see what styles (keys) each highlighter defines; the syntax for values is the
+same as the syntax of "types of highlighting" of the zsh builtin
+`$zle_highlight` array, which is documented in [the `zshzle(1)` manual
+page][zshzle-Character-Highlighting].
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
+
+Some highlighters support additional configuration parameters; see each
+highlighter's documentation for details and examples.
+
+
+How to implement a new highlighter
+----------------------------------
+
+To create your own `acme` highlighter:
+
+* Create your script at
+ `highlighters/acme/acme-highlighter.zsh`.
+
+* Implement the `_zsh_highlight_highlighter_acme_predicate` function.
+ This function must return 0 when the highlighter needs to be called and
+ non-zero otherwise, for example:
+
+ ```zsh
+ _zsh_highlight_highlighter_acme_predicate() {
+ # Call this highlighter in SVN working copies
+ [[ -d .svn ]]
+ }
+ ```
+
+* Implement the `_zsh_highlight_highlighter_acme_paint` function.
+ This function does the actual syntax highlighting, by calling
+ `_zsh_highlight_add_highlight` with the start and end of the region to
+ be highlighted and the `ZSH_HIGHLIGHT_STYLES` key to use. Define the default
+ style for that key in the highlighter script outside of any function with
+ `: ${ZSH_HIGHLIGHT_STYLES[key]:=value}`, being sure to prefix
+ the key with your highlighter name and a colon. For example:
+
+ ```zsh
+ : ${ZSH_HIGHLIGHT_STYLES[acme:aurora]:=fg=green}
+
+ _zsh_highlight_highlighter_acme_paint() {
+ # Colorize the whole buffer with the 'aurora' style
+ _zsh_highlight_add_highlight 0 $#BUFFER acme:aurora
+ }
+ ```
+
+ If you need to test which options the user has set, test `zsyh_user_options`
+ with a sensible default if the option is not present in supported zsh
+ versions. For example:
+
+ ```zsh
+ [[ ${zsyh_user_options[ignoreclosebraces]:-off} == on ]]
+ ```
+
+ The option name must be all lowercase with no underscores and not an alias.
+
+* Name your own functions and global variables `_zsh_highlight_acme_*`.
+
+ - In zsh-syntax-highlighting 0.4.0 and earlier, the entrypoints
+ `_zsh_highlight_highlighter_acme_predicate` and
+ `_zsh_highlight_highlighter_acme_paint`
+ were named
+ `_zsh_highlight_acme_highlighter_predicate` and
+ `_zsh_highlight_highlighter_acme_paint` respectively.
+
+ These names are still supported for backwards compatibility;
+ however, support for them will be removed in a future major or minor release (v0.x.0 or v1.0.0).
+
+* Activate your highlighter in `~/.zshrc`:
+
+ ```zsh
+ ZSH_HIGHLIGHT_HIGHLIGHTERS+=(acme)
+ ```
+
+* [Write tests](../tests/README.md).
diff --git a/plugins/zsh-syntax-highlighting/docs/highlighters/brackets.md b/plugins/zsh-syntax-highlighting/docs/highlighters/brackets.md
new file mode 100644
index 000000000..0101699e0
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/docs/highlighters/brackets.md
@@ -0,0 +1,31 @@
+zsh-syntax-highlighting / highlighters / brackets
+-------------------------------------------------
+
+This is the `brackets` highlighter, that highlights brackets and parentheses, and
+matches them.
+
+
+### How to tweak it
+
+This highlighter defines the following styles:
+
+* `bracket-error` - unmatched brackets
+* `bracket-level-N` - brackets with nest level N
+* `cursor-matchingbracket` - the matching bracket, if cursor is on a bracket
+
+To override one of those styles, change its entry in `ZSH_HIGHLIGHT_STYLES`,
+for example in `~/.zshrc`:
+
+```zsh
+# To define styles for nested brackets up to level 4
+ZSH_HIGHLIGHT_STYLES[bracket-level-1]='fg=blue,bold'
+ZSH_HIGHLIGHT_STYLES[bracket-level-2]='fg=red,bold'
+ZSH_HIGHLIGHT_STYLES[bracket-level-3]='fg=yellow,bold'
+ZSH_HIGHLIGHT_STYLES[bracket-level-4]='fg=magenta,bold'
+```
+
+The syntax for values is the same as the syntax of "types of highlighting" of
+the zsh builtin `$zle_highlight` array, which is documented in [the `zshzle(1)`
+manual page][zshzle-Character-Highlighting].
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
diff --git a/plugins/zsh-syntax-highlighting/docs/highlighters/cursor.md b/plugins/zsh-syntax-highlighting/docs/highlighters/cursor.md
new file mode 100644
index 000000000..3f87d422e
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/docs/highlighters/cursor.md
@@ -0,0 +1,24 @@
+zsh-syntax-highlighting / highlighters / cursor
+-----------------------------------------------
+
+This is the `cursor` highlighter, that highlights the cursor.
+
+
+### How to tweak it
+
+This highlighter defines the following styles:
+
+* `cursor` - the style for the current cursor position
+
+To override one of those styles, change its entry in `ZSH_HIGHLIGHT_STYLES`,
+for example in `~/.zshrc`:
+
+```zsh
+ZSH_HIGHLIGHT_STYLES[cursor]='bg=blue'
+```
+
+The syntax for values is the same as the syntax of "types of highlighting" of
+the zsh builtin `$zle_highlight` array, which is documented in [the `zshzle(1)`
+manual page][zshzle-Character-Highlighting].
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
diff --git a/plugins/zsh-syntax-highlighting/docs/highlighters/line.md b/plugins/zsh-syntax-highlighting/docs/highlighters/line.md
new file mode 100644
index 000000000..f76639ee9
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/docs/highlighters/line.md
@@ -0,0 +1,24 @@
+zsh-syntax-highlighting / highlighters / line
+---------------------------------------------
+
+This is the `line` highlighter, that highlights the whole line.
+
+
+### How to tweak it
+
+This highlighter defines the following styles:
+
+* `line` - the style for the whole line
+
+To override one of those styles, change its entry in `ZSH_HIGHLIGHT_STYLES`,
+for example in `~/.zshrc`:
+
+```zsh
+ZSH_HIGHLIGHT_STYLES[line]='bold'
+```
+
+The syntax for values is the same as the syntax of "types of highlighting" of
+the zsh builtin `$zle_highlight` array, which is documented in [the `zshzle(1)`
+manual page][zshzle-Character-Highlighting].
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
diff --git a/plugins/zsh-syntax-highlighting/docs/highlighters/main.md b/plugins/zsh-syntax-highlighting/docs/highlighters/main.md
new file mode 100644
index 000000000..4a27653f0
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/docs/highlighters/main.md
@@ -0,0 +1,121 @@
+zsh-syntax-highlighting / highlighters / main
+---------------------------------------------
+
+This is the `main` highlighter, that highlights:
+
+* Commands
+* Options
+* Arguments
+* Paths
+* Strings
+
+This highlighter is active by default.
+
+
+### How to tweak it
+
+This highlighter defines the following styles:
+
+* `unknown-token` - unknown tokens / errors
+* `reserved-word` - shell reserved words (`if`, `for`)
+* `alias` - aliases
+* `suffix-alias` - suffix aliases (requires zsh 5.1.1 or newer)
+* `global-alias` - global aliases
+* `builtin` - shell builtin commands (`shift`, `pwd`, `zstyle`)
+* `function` - function names
+* `command` - command names
+* `precommand` - precommand modifiers (e.g., `noglob`, `builtin`)
+* `commandseparator` - command separation tokens (`;`, `&&`)
+* `hashed-command` - hashed commands
+* `autodirectory` - a directory name in command position when the `AUTO_CD` option is set
+* `path` - existing filenames
+* `path_pathseparator` - path separators in filenames (`/`); if unset, `path` is used (default)
+* `path_prefix` - prefixes of existing filenames
+* `path_prefix_pathseparator` - path separators in prefixes of existing filenames (`/`); if unset, `path_prefix` is used (default)
+* `globbing` - globbing expressions (`*.txt`)
+* `history-expansion` - history expansion expressions (`!foo` and `^foo^bar`)
+* `command-substitution` - command substitutions (`$(echo foo)`)
+* `command-substitution-unquoted` - an unquoted command substitution (`$(echo foo)`)
+* `command-substitution-quoted` - a quoted command substitution (`"$(echo foo)"`)
+* `command-substitution-delimiter` - command substitution delimiters (`$(` and `)`)
+* `command-substitution-delimiter-unquoted` - an unquoted command substitution delimiters (`$(` and `)`)
+* `command-substitution-delimiter-quoted` - a quoted command substitution delimiters (`"$(` and `)"`)
+* `process-substitution` - process substitutions (`<(echo foo)`)
+* `process-substitution-delimiter` - process substitution delimiters (`<(` and `)`)
+* `arithmetic-expansion` - arithmetic expansion `$(( 42 ))`)
+* `single-hyphen-option` - single-hyphen options (`-o`)
+* `double-hyphen-option` - double-hyphen options (`--option`)
+* `back-quoted-argument` - backtick command substitution (`` `foo` ``)
+* `back-quoted-argument-unclosed` - unclosed backtick command substitution (`` `foo ``)
+* `back-quoted-argument-delimiter` - backtick command substitution delimiters (`` ` ``)
+* `single-quoted-argument` - single-quoted arguments (`` 'foo' ``)
+* `single-quoted-argument-unclosed` - unclosed single-quoted arguments (`` 'foo ``)
+* `double-quoted-argument` - double-quoted arguments (`` "foo" ``)
+* `double-quoted-argument-unclosed` - unclosed double-quoted arguments (`` "foo ``)
+* `dollar-quoted-argument` - dollar-quoted arguments (`` $'foo' ``)
+* `dollar-quoted-argument-unclosed` - unclosed dollar-quoted arguments (`` $'foo ``)
+* `rc-quote` - two single quotes inside single quotes when the `RC_QUOTES` option is set (`` 'foo''bar' ``)
+* `dollar-double-quoted-argument` - parameter expansion inside double quotes (`$foo` inside `""`)
+* `back-double-quoted-argument` - backslash escape sequences inside double-quoted arguments (`\"` in `"foo\"bar"`)
+* `back-dollar-quoted-argument` - backslash escape sequences inside dollar-quoted arguments (`\x` in `$'\x48'`)
+* `assign` - parameter assignments (`x=foo` and `x=( )`)
+* `redirection` - redirection operators (`<`, `>`, etc)
+* `comment` - comments, when `setopt INTERACTIVE_COMMENTS` is in effect (`echo # foo`)
+* `comment` - elided parameters in command position (`$x ls` when `$x` is unset or empty)
+* `named-fd` - named file descriptor (the `fd` in `echo foo {fd}>&2`)
+* `numeric-fd` - numeric file descriptor (the `2` in `echo foo {fd}>&2`)
+* `arg0` - a command word other than one of those enumerated above (other than a command, precommand, alias, function, or shell builtin command).
+* `default` - everything else
+
+To override one of those styles, change its entry in `ZSH_HIGHLIGHT_STYLES`,
+for example in `~/.zshrc`:
+
+```zsh
+# Declare the variable
+typeset -A ZSH_HIGHLIGHT_STYLES
+
+# To differentiate aliases from other command types
+ZSH_HIGHLIGHT_STYLES[alias]='fg=magenta,bold'
+
+# To have paths colored instead of underlined
+ZSH_HIGHLIGHT_STYLES[path]='fg=cyan'
+
+# To disable highlighting of globbing expressions
+ZSH_HIGHLIGHT_STYLES[globbing]='none'
+```
+
+The syntax for values is the same as the syntax of "types of highlighting" of
+the zsh builtin `$zle_highlight` array, which is documented in [the `zshzle(1)`
+manual page][zshzle-Character-Highlighting].
+
+#### Parameters
+
+To avoid partial path lookups on a path, add the path to the `ZSH_HIGHLIGHT_DIRS_BLACKLIST` array.
+
+```zsh
+ZSH_HIGHLIGHT_DIRS_BLACKLIST+=(/mnt/slow_share)
+```
+
+### Useless trivia
+
+#### Forward compatibility.
+
+zsh-syntax-highlighting attempts to be forward-compatible with zsh.
+Specifically, we attempt to facilitate highlighting _command word_ types that
+had not yet been invented when this version of zsh-syntax-highlighting was
+released.
+
+A _command word_ is something like a function name, external command name, et
+cetera. (See
+[Simple Commands & Pipelines in `zshmisc(1)`][zshmisc-Simple-Commands-And-Pipelines]
+for a formal definition.)
+
+If a new _kind_ of command word is ever added to zsh — something conceptually
+different than "function" and "alias" and "external command" — then command words
+of that (new) kind will be highlighted by the style `arg0_$kind`,
+where `$kind` is the output of `type -w` on the new kind of command word. If that
+style is not defined, then the style `arg0` will be used instead.
+
+[zshmisc-Simple-Commands-And-Pipelines]: https://zsh.sourceforge.io/Doc/Release/Shell-Grammar.html#Simple-Commands-_0026-Pipelines
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
diff --git a/plugins/zsh-syntax-highlighting/docs/highlighters/pattern.md b/plugins/zsh-syntax-highlighting/docs/highlighters/pattern.md
new file mode 100644
index 000000000..ecaa6a7f1
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/docs/highlighters/pattern.md
@@ -0,0 +1,24 @@
+zsh-syntax-highlighting / highlighters / pattern
+------------------------------------------------
+
+This is the `pattern` highlighter, that highlights user-defined patterns.
+
+
+### How to tweak it
+
+To use this highlighter, associate patterns with styles in the
+`ZSH_HIGHLIGHT_PATTERNS` associative array, for example in `~/.zshrc`:
+
+```zsh
+# Declare the variable
+typeset -A ZSH_HIGHLIGHT_PATTERNS
+
+# To have commands starting with `rm -rf` in red:
+ZSH_HIGHLIGHT_PATTERNS+=('rm -rf *' 'fg=white,bold,bg=red')
+```
+
+The syntax for values is the same as the syntax of "types of highlighting" of
+the zsh builtin `$zle_highlight` array, which is documented in [the `zshzle(1)`
+manual page][zshzle-Character-Highlighting].
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
diff --git a/plugins/zsh-syntax-highlighting/docs/highlighters/regexp.md b/plugins/zsh-syntax-highlighting/docs/highlighters/regexp.md
new file mode 100644
index 000000000..8c711a3c7
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/docs/highlighters/regexp.md
@@ -0,0 +1,65 @@
+zsh-syntax-highlighting / highlighters / regexp
+------------------------------------------------
+
+This is the `regexp` highlighter, that highlights user-defined regular
+expressions. It's similar to the `pattern` highlighter, but allows more complex
+patterns.
+
+### How to tweak it
+
+To use this highlighter, associate regular expressions with styles in the
+`ZSH_HIGHLIGHT_REGEXP` associative array, for example in `~/.zshrc`:
+
+```zsh
+typeset -A ZSH_HIGHLIGHT_REGEXP
+ZSH_HIGHLIGHT_REGEXP+=('^rm .*' fg=red,bold)
+```
+
+This will highlight lines that start with a call to the `rm` command.
+
+The regular expressions flavour used is [PCRE][pcresyntax] when the
+`RE_MATCH_PCRE` option is set and POSIX Extended Regular Expressions (ERE),
+as implemented by the platform's C library, otherwise. For details on the
+latter, see [the `zsh/regex` module's documentation][MAN_ZSH_REGEX] and the
+`regcomp(3)` and `re_format(7)` manual pages on your system.
+
+For instance, to highlight `sudo` only as a complete word, i.e., `sudo cmd`,
+but not `sudoedit`, one might use:
+
+* When the `RE_MATCH_PCRE` is set:
+
+ ```zsh
+ typeset -A ZSH_HIGHLIGHT_REGEXP
+ ZSH_HIGHLIGHT_REGEXP+=('\bsudo\b' fg=123,bold)
+ ```
+
+* When the `RE_MATCH_PCRE` is unset, on platforms with GNU `libc` (e.g., many GNU/Linux distributions):
+
+ ```zsh
+ typeset -A ZSH_HIGHLIGHT_REGEXP
+ ZSH_HIGHLIGHT_REGEXP+=('\' fg=123,bold)
+ ```
+
+* When the `RE_MATCH_PCRE` is unset, on BSD-based platforms (e.g., macOS):
+
+ ```zsh
+ typeset -A ZSH_HIGHLIGHT_REGEXP
+ ZSH_HIGHLIGHT_REGEXP+=('[[:<:]]sudo[[:>:]]' fg=123,bold)
+ ```
+
+Note, however, that PCRE and POSIX ERE have a large common subset:
+for instance, the regular expressions `[abc]`, `a*`, and `(a|b)` have the same
+meaning in both flavours.
+
+The syntax for values is the same as the syntax of "types of highlighting" of
+the zsh builtin `$zle_highlight` array, which is documented in [the `zshzle(1)`
+manual page][zshzle-Character-Highlighting].
+
+See also: [regular expressions tutorial][perlretut], zsh regexp operator `=~`
+in [the `zshmisc(1)` manual page][zshmisc-Conditional-Expressions]
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
+[perlretut]: https://perldoc.perl.org/perlretut
+[zshmisc-Conditional-Expressions]: https://zsh.sourceforge.io/Doc/Release/Conditional-Expressions.html#Conditional-Expressions
+[MAN_ZSH_REGEX]: https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#The-zsh_002fregex-Module
+[pcresyntax]: https://www.pcre.org/original/doc/html/pcresyntax.html
diff --git a/plugins/zsh-syntax-highlighting/docs/highlighters/root.md b/plugins/zsh-syntax-highlighting/docs/highlighters/root.md
new file mode 100644
index 000000000..8197e4bd4
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/docs/highlighters/root.md
@@ -0,0 +1,25 @@
+zsh-syntax-highlighting / highlighters / root
+---------------------------------------------
+
+This is the `root` highlighter, that highlights the whole line if the current
+user is root.
+
+
+### How to tweak it
+
+This highlighter defines the following styles:
+
+* `root` - the style for the whole line if the current user is root.
+
+To override one of those styles, change its entry in `ZSH_HIGHLIGHT_STYLES`,
+for example in `~/.zshrc`:
+
+```zsh
+ZSH_HIGHLIGHT_STYLES[root]='bg=red'
+```
+
+The syntax for values is the same as the syntax of "types of highlighting" of
+the zsh builtin `$zle_highlight` array, which is documented in [the `zshzle(1)`
+manual page][zshzle-Character-Highlighting].
+
+[zshzle-Character-Highlighting]: https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting
diff --git a/plugins/zsh-syntax-highlighting/highlighters/README.md b/plugins/zsh-syntax-highlighting/highlighters/README.md
new file mode 100644
index 000000000..8490ddf43
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/README.md
@@ -0,0 +1,8 @@
+zsh-syntax-highlighting / highlighters
+======================================
+
+Navigate into the individual highlighters' documentation to see
+what styles (`$ZSH_HIGHLIGHT_STYLES` keys) each highlighter defines.
+
+Refer to the [documentation on highlighters](../docs/highlighters.md) for further
+information.
diff --git a/plugins/zsh-syntax-highlighting/highlighters/brackets/README.md b/plugins/zsh-syntax-highlighting/highlighters/brackets/README.md
new file mode 120000
index 000000000..6997bd60d
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/brackets/README.md
@@ -0,0 +1 @@
+../../docs/highlighters/brackets.md
\ No newline at end of file
diff --git a/plugins/zsh-syntax-highlighting/highlighters/brackets/brackets-highlighter.zsh b/plugins/zsh-syntax-highlighting/highlighters/brackets/brackets-highlighter.zsh
new file mode 100644
index 000000000..bc388fdbf
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/brackets/brackets-highlighter.zsh
@@ -0,0 +1,107 @@
+# -------------------------------------------------------------------------------------------------
+# Copyright (c) 2010-2017 zsh-syntax-highlighting contributors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+# may be used to endorse or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -------------------------------------------------------------------------------------------------
+# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
+# vim: ft=zsh sw=2 ts=2 et
+# -------------------------------------------------------------------------------------------------
+
+
+# Define default styles.
+: ${ZSH_HIGHLIGHT_STYLES[bracket-error]:=fg=red,bold}
+: ${ZSH_HIGHLIGHT_STYLES[bracket-level-1]:=fg=blue,bold}
+: ${ZSH_HIGHLIGHT_STYLES[bracket-level-2]:=fg=green,bold}
+: ${ZSH_HIGHLIGHT_STYLES[bracket-level-3]:=fg=magenta,bold}
+: ${ZSH_HIGHLIGHT_STYLES[bracket-level-4]:=fg=yellow,bold}
+: ${ZSH_HIGHLIGHT_STYLES[bracket-level-5]:=fg=cyan,bold}
+: ${ZSH_HIGHLIGHT_STYLES[cursor-matchingbracket]:=standout}
+
+# Whether the brackets highlighter should be called or not.
+_zsh_highlight_highlighter_brackets_predicate()
+{
+ [[ $WIDGET == zle-line-finish ]] || _zsh_highlight_cursor_moved || _zsh_highlight_buffer_modified
+}
+
+# Brackets highlighting function.
+_zsh_highlight_highlighter_brackets_paint()
+{
+ local char style
+ local -i bracket_color_size=${#ZSH_HIGHLIGHT_STYLES[(I)bracket-level-*]} buflen=${#BUFFER} level=0 matchingpos pos
+ local -A levelpos lastoflevel matching
+
+ # Find all brackets and remember which one is matching
+ pos=0
+ for char in ${(s..)BUFFER} ; do
+ (( ++pos ))
+ case $char in
+ ["([{"])
+ levelpos[$pos]=$((++level))
+ lastoflevel[$level]=$pos
+ ;;
+ [")]}"])
+ if (( level > 0 )); then
+ matchingpos=$lastoflevel[$level]
+ levelpos[$pos]=$((level--))
+ if _zsh_highlight_brackets_match $matchingpos $pos; then
+ matching[$matchingpos]=$pos
+ matching[$pos]=$matchingpos
+ fi
+ else
+ levelpos[$pos]=-1
+ fi
+ ;;
+ esac
+ done
+
+ # Now highlight all found brackets
+ for pos in ${(k)levelpos}; do
+ if (( $+matching[$pos] )); then
+ if (( bracket_color_size )); then
+ _zsh_highlight_add_highlight $((pos - 1)) $pos bracket-level-$(( (levelpos[$pos] - 1) % bracket_color_size + 1 ))
+ fi
+ else
+ _zsh_highlight_add_highlight $((pos - 1)) $pos bracket-error
+ fi
+ done
+
+ # If cursor is on a bracket, then highlight corresponding bracket, if any.
+ if [[ $WIDGET != zle-line-finish ]]; then
+ pos=$((CURSOR + 1))
+ if (( $+levelpos[$pos] )) && (( $+matching[$pos] )); then
+ local -i otherpos=$matching[$pos]
+ _zsh_highlight_add_highlight $((otherpos - 1)) $otherpos cursor-matchingbracket
+ fi
+ fi
+}
+
+# Helper function to differentiate type
+_zsh_highlight_brackets_match()
+{
+ case $BUFFER[$1] in
+ \() [[ $BUFFER[$2] == \) ]];;
+ \[) [[ $BUFFER[$2] == \] ]];;
+ \{) [[ $BUFFER[$2] == \} ]];;
+ *) false;;
+ esac
+}
diff --git a/plugins/zsh-syntax-highlighting/highlighters/cursor/README.md b/plugins/zsh-syntax-highlighting/highlighters/cursor/README.md
new file mode 120000
index 000000000..70e0c8c42
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/cursor/README.md
@@ -0,0 +1 @@
+../../docs/highlighters/cursor.md
\ No newline at end of file
diff --git a/plugins/zsh-syntax-highlighting/highlighters/cursor/cursor-highlighter.zsh b/plugins/zsh-syntax-highlighting/highlighters/cursor/cursor-highlighter.zsh
new file mode 100644
index 000000000..81633a3c3
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/cursor/cursor-highlighter.zsh
@@ -0,0 +1,47 @@
+# -------------------------------------------------------------------------------------------------
+# Copyright (c) 2010-2011 zsh-syntax-highlighting contributors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+# may be used to endorse or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -------------------------------------------------------------------------------------------------
+# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
+# vim: ft=zsh sw=2 ts=2 et
+# -------------------------------------------------------------------------------------------------
+
+
+# Define default styles.
+: ${ZSH_HIGHLIGHT_STYLES[cursor]:=standout}
+
+# Whether the cursor highlighter should be called or not.
+_zsh_highlight_highlighter_cursor_predicate()
+{
+ # remove cursor highlighting when the line is finished
+ [[ $WIDGET == zle-line-finish ]] || _zsh_highlight_cursor_moved
+}
+
+# Cursor highlighting function.
+_zsh_highlight_highlighter_cursor_paint()
+{
+ [[ $WIDGET == zle-line-finish ]] && return
+
+ _zsh_highlight_add_highlight $CURSOR $(( $CURSOR + 1 )) cursor
+}
diff --git a/plugins/zsh-syntax-highlighting/highlighters/line/README.md b/plugins/zsh-syntax-highlighting/highlighters/line/README.md
new file mode 120000
index 000000000..052fe20b5
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/line/README.md
@@ -0,0 +1 @@
+../../docs/highlighters/line.md
\ No newline at end of file
diff --git a/plugins/zsh-syntax-highlighting/highlighters/line/line-highlighter.zsh b/plugins/zsh-syntax-highlighting/highlighters/line/line-highlighter.zsh
new file mode 100644
index 000000000..f922dc9b2
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/line/line-highlighter.zsh
@@ -0,0 +1,44 @@
+# -------------------------------------------------------------------------------------------------
+# Copyright (c) 2010-2011 zsh-syntax-highlighting contributors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+# may be used to endorse or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -------------------------------------------------------------------------------------------------
+# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
+# vim: ft=zsh sw=2 ts=2 et
+# -------------------------------------------------------------------------------------------------
+
+
+# Define default styles.
+: ${ZSH_HIGHLIGHT_STYLES[line]:=}
+
+# Whether the root highlighter should be called or not.
+_zsh_highlight_highlighter_line_predicate()
+{
+ _zsh_highlight_buffer_modified
+}
+
+# root highlighting function.
+_zsh_highlight_highlighter_line_paint()
+{
+ _zsh_highlight_add_highlight 0 $#BUFFER line
+}
diff --git a/plugins/zsh-syntax-highlighting/highlighters/main/README.md b/plugins/zsh-syntax-highlighting/highlighters/main/README.md
new file mode 120000
index 000000000..035473184
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/main/README.md
@@ -0,0 +1 @@
+../../docs/highlighters/main.md
\ No newline at end of file
diff --git a/plugins/zsh-syntax-highlighting/highlighters/main/main-highlighter.zsh b/plugins/zsh-syntax-highlighting/highlighters/main/main-highlighter.zsh
new file mode 100644
index 000000000..061e2a489
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/main/main-highlighter.zsh
@@ -0,0 +1,1848 @@
+# -------------------------------------------------------------------------------------------------
+# Copyright (c) 2010-2020 zsh-syntax-highlighting contributors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+# may be used to endorse or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -------------------------------------------------------------------------------------------------
+# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
+# vim: ft=zsh sw=2 ts=2 et
+# -------------------------------------------------------------------------------------------------
+
+
+# Define default styles.
+: ${ZSH_HIGHLIGHT_STYLES[default]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[unknown-token]:=fg=red,bold}
+: ${ZSH_HIGHLIGHT_STYLES[reserved-word]:=fg=yellow}
+: ${ZSH_HIGHLIGHT_STYLES[suffix-alias]:=fg=green,underline}
+: ${ZSH_HIGHLIGHT_STYLES[global-alias]:=fg=cyan}
+: ${ZSH_HIGHLIGHT_STYLES[precommand]:=fg=green,underline}
+: ${ZSH_HIGHLIGHT_STYLES[commandseparator]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[autodirectory]:=fg=green,underline}
+: ${ZSH_HIGHLIGHT_STYLES[path]:=underline}
+: ${ZSH_HIGHLIGHT_STYLES[path_pathseparator]:=}
+: ${ZSH_HIGHLIGHT_STYLES[path_prefix_pathseparator]:=}
+: ${ZSH_HIGHLIGHT_STYLES[globbing]:=fg=blue}
+: ${ZSH_HIGHLIGHT_STYLES[history-expansion]:=fg=blue}
+: ${ZSH_HIGHLIGHT_STYLES[command-substitution]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[command-substitution-delimiter]:=fg=magenta}
+: ${ZSH_HIGHLIGHT_STYLES[process-substitution]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[process-substitution-delimiter]:=fg=magenta}
+: ${ZSH_HIGHLIGHT_STYLES[single-hyphen-option]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[double-hyphen-option]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[back-quoted-argument]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[back-quoted-argument-delimiter]:=fg=magenta}
+: ${ZSH_HIGHLIGHT_STYLES[single-quoted-argument]:=fg=yellow}
+: ${ZSH_HIGHLIGHT_STYLES[double-quoted-argument]:=fg=yellow}
+: ${ZSH_HIGHLIGHT_STYLES[dollar-quoted-argument]:=fg=yellow}
+: ${ZSH_HIGHLIGHT_STYLES[rc-quote]:=fg=cyan}
+: ${ZSH_HIGHLIGHT_STYLES[dollar-double-quoted-argument]:=fg=cyan}
+: ${ZSH_HIGHLIGHT_STYLES[back-double-quoted-argument]:=fg=cyan}
+: ${ZSH_HIGHLIGHT_STYLES[back-dollar-quoted-argument]:=fg=cyan}
+: ${ZSH_HIGHLIGHT_STYLES[assign]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[redirection]:=fg=yellow}
+: ${ZSH_HIGHLIGHT_STYLES[comment]:=fg=black,bold}
+: ${ZSH_HIGHLIGHT_STYLES[named-fd]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[numeric-fd]:=none}
+: ${ZSH_HIGHLIGHT_STYLES[arg0]:=fg=green}
+
+# Whether the highlighter should be called or not.
+_zsh_highlight_highlighter_main_predicate()
+{
+ # may need to remove path_prefix highlighting when the line ends
+ [[ $WIDGET == zle-line-finish ]] || _zsh_highlight_buffer_modified
+}
+
+# Helper to deal with tokens crossing line boundaries.
+_zsh_highlight_main_add_region_highlight() {
+ integer start=$1 end=$2
+ shift 2
+
+ if (( $#in_alias )); then
+ [[ $1 == unknown-token ]] && alias_style=unknown-token
+ return
+ fi
+ if (( in_param )); then
+ if [[ $1 == unknown-token ]]; then
+ param_style=unknown-token
+ fi
+ if [[ -n $param_style ]]; then
+ return
+ fi
+ param_style=$1
+ return
+ fi
+
+ # The calculation was relative to $buf but region_highlight is relative to $BUFFER.
+ (( start += buf_offset ))
+ (( end += buf_offset ))
+
+ list_highlights+=($start $end $1)
+}
+
+_zsh_highlight_main_add_many_region_highlights() {
+ for 1 2 3; do
+ _zsh_highlight_main_add_region_highlight $1 $2 $3
+ done
+}
+
+_zsh_highlight_main_calculate_fallback() {
+ local -A fallback_of; fallback_of=(
+ alias arg0
+ suffix-alias arg0
+ global-alias dollar-double-quoted-argument
+ builtin arg0
+ function arg0
+ command arg0
+ precommand arg0
+ hashed-command arg0
+ autodirectory arg0
+ arg0_\* arg0
+
+ # TODO: Maybe these? —
+ # named-fd file-descriptor
+ # numeric-fd file-descriptor
+
+ path_prefix path
+ # The path separator fallback won't ever be used, due to the optimisation
+ # in _zsh_highlight_main_highlighter_highlight_path_separators().
+ path_pathseparator path
+ path_prefix_pathseparator path_prefix
+
+ single-quoted-argument{-unclosed,}
+ double-quoted-argument{-unclosed,}
+ dollar-quoted-argument{-unclosed,}
+ back-quoted-argument{-unclosed,}
+
+ command-substitution{-quoted,,-unquoted,}
+ command-substitution-delimiter{-quoted,,-unquoted,}
+
+ command-substitution{-delimiter,}
+ process-substitution{-delimiter,}
+ back-quoted-argument{-delimiter,}
+ )
+ local needle=$1 value
+ reply=($1)
+ while [[ -n ${value::=$fallback_of[(k)$needle]} ]]; do
+ unset "fallback_of[$needle]" # paranoia against infinite loops
+ reply+=($value)
+ needle=$value
+ done
+}
+
+# Get the type of a command.
+#
+# Uses the zsh/parameter module if available to avoid forks, and a
+# wrapper around 'type -w' as fallback.
+#
+# If $2 is 0, do not consider aliases.
+#
+# The result will be stored in REPLY.
+_zsh_highlight_main__type() {
+ integer -r aliases_allowed=${2-1}
+ # We won't cache replies of anything that exists as an alias at all, to
+ # ensure the cached value is correct regardless of $aliases_allowed.
+ #
+ # ### We probably _should_ cache them in a cache that's keyed on the value of
+ # ### $aliases_allowed, on the assumption that aliases are the common case.
+ integer may_cache=1
+
+ # Cache lookup
+ if (( $+_zsh_highlight_main__command_type_cache )); then
+ REPLY=$_zsh_highlight_main__command_type_cache[(e)$1]
+ if [[ -n "$REPLY" ]]; then
+ return
+ fi
+ fi
+
+ # Main logic
+ if (( $#options_to_set )); then
+ setopt localoptions $options_to_set;
+ fi
+ unset REPLY
+ if zmodload -e zsh/parameter; then
+ if (( $+aliases[(e)$1] )); then
+ may_cache=0
+ fi
+ if (( ${+galiases[(e)$1]} )) && (( aliases_allowed )); then
+ REPLY='global alias'
+ elif (( $+aliases[(e)$1] )) && (( aliases_allowed )); then
+ REPLY=alias
+ elif [[ $1 == *.* && -n ${1%.*} ]] && (( $+saliases[(e)${1##*.}] )); then
+ REPLY='suffix alias'
+ elif (( $reswords[(Ie)$1] )); then
+ REPLY=reserved
+ elif (( $+functions[(e)$1] )); then
+ REPLY=function
+ elif (( $+builtins[(e)$1] )); then
+ REPLY=builtin
+ elif (( $+commands[(e)$1] )); then
+ REPLY=command
+ # None of the special hashes had a match, so fall back to 'type -w', for
+ # forward compatibility with future versions of zsh that may add new command
+ # types.
+ #
+ # zsh 5.2 and older have a bug whereby running 'type -w ./sudo' implicitly
+ # runs 'hash ./sudo=/usr/local/bin/./sudo' (assuming /usr/local/bin/sudo
+ # exists and is in $PATH). Avoid triggering the bug, at the expense of
+ # falling through to the $() below, incurring a fork. (Issue #354.)
+ #
+ # The first disjunct mimics the isrelative() C call from the zsh bug.
+ elif { [[ $1 != */* ]] || is-at-least 5.3 } &&
+ # Add a subshell to avoid a zsh upstream bug; see issue #606.
+ # ### Remove the subshell when we stop supporting zsh 5.7.1 (I assume 5.8 will have the bugfix).
+ ! (builtin type -w -- "$1") >/dev/null 2>&1; then
+ REPLY=none
+ fi
+ fi
+ if ! (( $+REPLY )); then
+ # zsh/parameter not available or had no matches.
+ #
+ # Note that 'type -w' will run 'rehash' implicitly.
+ #
+ # We 'unalias' in a subshell, so the parent shell is not affected.
+ #
+ # The colon command is there just to avoid a command substitution that
+ # starts with an arithmetic expression [«((…))» as the first thing inside
+ # «$(…)»], which is area that has had some parsing bugs before 5.6
+ # (approximately).
+ REPLY="${$(:; (( aliases_allowed )) || unalias -- "$1" 2>/dev/null; LC_ALL=C builtin type -w -- "$1" 2>/dev/null)##*: }"
+ if [[ $REPLY == 'alias' ]]; then
+ may_cache=0
+ fi
+ fi
+
+ # Cache population
+ if (( may_cache )) && (( $+_zsh_highlight_main__command_type_cache )); then
+ _zsh_highlight_main__command_type_cache[(e)$1]=$REPLY
+ fi
+ [[ -n $REPLY ]]
+ return $?
+}
+
+# Checks whether $1 is something that can be run.
+#
+# Return 0 if runnable, 1 if not runnable, 2 if trouble.
+_zsh_highlight_main__is_runnable() {
+ if _zsh_highlight_main__type "$1"; then
+ [[ $REPLY != none ]]
+ else
+ return 2
+ fi
+}
+
+# Check whether the first argument is a redirection operator token.
+# Report result via the exit code.
+_zsh_highlight_main__is_redirection() {
+ # A redirection operator token:
+ # - starts with an optional single-digit number;
+ # - is one of the tokens listed in zshmisc(1)
+ # - however (z) normalizes ! to |
+ [[ ${1#[0-9]} == (\<|\<\>|(\>|\>\>)(|\|)|\<\<(|-)|\<\<\<|\<\&|\&\<|(\>|\>\>)\&(|\|)|\&(\>|\>\>)(|\||\!)) ]]
+}
+
+# Resolve alias.
+#
+# Takes a single argument.
+#
+# The result will be stored in REPLY.
+_zsh_highlight_main__resolve_alias() {
+ if zmodload -e zsh/parameter; then
+ REPLY=${aliases[$arg]}
+ else
+ REPLY="${"$(alias -- $arg)"#*=}"
+ fi
+}
+
+# Return true iff $1 is a global alias
+_zsh_highlight_main__is_global_alias() {
+ if zmodload -e zsh/parameter; then
+ (( ${+galiases[$arg]} ))
+ elif [[ $arg == '='* ]]; then
+ # avoid running into «alias -L '=foo'» erroring out with 'bad assignment'
+ return 1
+ else
+ alias -L -g -- "$1" >/dev/null
+ fi
+}
+
+# Check that the top of $braces_stack has the expected value. If it does, set
+# the style according to $2; otherwise, set style=unknown-token.
+#
+# $1: character expected to be at the top of $braces_stack
+# $2: optional assignment to style it if matches
+# return value is 0 if there is a match else 1
+_zsh_highlight_main__stack_pop() {
+ if [[ $braces_stack[1] == $1 ]]; then
+ braces_stack=${braces_stack:1}
+ if (( $+2 )); then
+ style=$2
+ fi
+ return 0
+ else
+ style=unknown-token
+ return 1
+ fi
+}
+
+# Main syntax highlighting function.
+_zsh_highlight_highlighter_main_paint()
+{
+ setopt localoptions extendedglob
+
+ # At the PS3 prompt and in vared, highlight nothing.
+ #
+ # (We can't check this in _zsh_highlight_highlighter_main_predicate because
+ # if the predicate returns false, the previous value of region_highlight
+ # would be reused.)
+ if [[ $CONTEXT == (select|vared) ]]; then
+ return
+ fi
+
+ typeset -a ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR
+ typeset -a ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW
+ local -a options_to_set reply # used in callees
+ local REPLY
+
+ # $flags_with_argument is a set of letters, corresponding to the option letters
+ # that would be followed by a colon in a getopts specification.
+ local flags_with_argument
+ # $flags_sans_argument is a set of letters, corresponding to the option letters
+ # that wouldn't be followed by a colon in a getopts specification.
+ local flags_sans_argument
+ # $flags_solo is a set of letters, corresponding to option letters that, if
+ # present, mean the precommand will not be acting as a precommand, i.e., will
+ # not be followed by a :start: word.
+ local flags_solo
+ # $precommand_options maps precommand name to values of $flags_with_argument,
+ # $flags_sans_argument, and flags_solo for that precommand, joined by a
+ # colon. (The value is NOT a getopt(3) spec, although it resembles one.)
+ #
+ # Currently, setting $flags_sans_argument is only important for commands that
+ # have a non-empty $flags_with_argument; see test-data/precommand4.zsh.
+ local -A precommand_options
+ precommand_options=(
+ # Precommand modifiers as of zsh 5.6.2 cf. zshmisc(1).
+ '-' ''
+ 'builtin' ''
+ 'command' :pvV
+ 'exec' a:cl
+ 'noglob' ''
+ # 'time' and 'nocorrect' shouldn't be added here; they're reserved words, not precommands.
+
+ # miscellaneous commands
+ 'doas' aCu:Lns # as of OpenBSD's doas(1) dated September 4, 2016
+ 'nice' n: # as of current POSIX spec
+ 'pkexec' '' # doesn't take short options; immune to #121 because it's usually not passed --option flags
+ # Not listed: -h, which has two different meanings.
+ 'sudo' Cgprtu:AEHPSbilns:eKkVv # as of sudo 1.8.21p2
+ 'run0' ugD:h # systemd 256.6
+ 'stdbuf' ioe:
+ 'eatmydata' ''
+ 'catchsegv' ''
+ 'nohup' ''
+ 'setsid' :wc
+ 'env' u:i
+ 'ionice' cn:t:pPu # util-linux 2.33.1-0.1
+ 'strace' IbeaosXPpEuOS:ACdfhikqrtTvVxyDc # strace 4.26-0.2
+ 'proxychains' f:q # proxychains 4.4.0
+ 'torsocks' idq:upaP # Torsocks 2.3.0
+ 'torify' idq:upaP # Torsocks 2.3.0
+ 'ssh-agent' aEPt:csDd:k # As of OpenSSH 8.1p1
+ 'tabbed' gnprtTuU:cdfhs:v # suckless-tools v44
+ 'chronic' :ev # moreutils 0.62-1
+ 'ifne' :n # moreutils 0.62-1
+ 'grc' :se # grc - a "generic colouriser" (that's their spelling, not mine)
+ 'cpulimit' elp:ivz # cpulimit 0.2
+ 'ktrace' fgpt:aBCcdiT
+ 'caffeinate' tw:dimsu # as of macOS's caffeinate(8) dated November 9, 2012
+ )
+ # Commands that would need to skip one positional argument:
+ # flock
+ # ssh
+ # _wanted (skip two)
+
+ if [[ $zsyh_user_options[ignorebraces] == on || ${zsyh_user_options[ignoreclosebraces]:-off} == on ]]; then
+ local right_brace_is_recognised_everywhere=false
+ else
+ local right_brace_is_recognised_everywhere=true
+ fi
+
+ if [[ $zsyh_user_options[pathdirs] == on ]]; then
+ options_to_set+=( PATH_DIRS )
+ fi
+
+ ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR=(
+ '|' '||' ';' '&' '&&'
+ $'\n' # ${(z)} returns ';' but we convert it to $'\n'
+ '|&'
+ '&!' '&|'
+ # ### 'case' syntax, but followed by a pattern, not by a command
+ # ';;' ';&' ';|'
+ )
+
+ # Tokens that, at (naively-determined) "command position", are followed by
+ # a de jure command position. All of these are reserved words.
+ ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW=(
+ $'\x7b' # block
+ $'\x28' # subshell
+ '()' # anonymous function
+ 'while'
+ 'until'
+ 'if'
+ 'then'
+ 'elif'
+ 'else'
+ 'do'
+ 'time'
+ 'coproc'
+ '!' # reserved word; unrelated to $histchars[1]
+ )
+
+ if (( $+X_ZSH_HIGHLIGHT_DIRS_BLACKLIST )); then
+ print >&2 'zsh-syntax-highlighting: X_ZSH_HIGHLIGHT_DIRS_BLACKLIST is deprecated. Please use ZSH_HIGHLIGHT_DIRS_BLACKLIST.'
+ ZSH_HIGHLIGHT_DIRS_BLACKLIST=($X_ZSH_HIGHLIGHT_DIRS_BLACKLIST)
+ unset X_ZSH_HIGHLIGHT_DIRS_BLACKLIST
+ fi
+
+ _zsh_highlight_main_highlighter_highlight_list -$#PREBUFFER '' 1 "$PREBUFFER$BUFFER"
+
+ # end is a reserved word
+ local start end_ style
+ for start end_ style in $reply; do
+ (( start >= end_ )) && { print -r -- >&2 "zsh-syntax-highlighting: BUG: _zsh_highlight_highlighter_main_paint: start($start) >= end($end_)"; return }
+ (( end_ <= 0 )) && continue
+ (( start < 0 )) && start=0 # having start<0 is normal with e.g. multiline strings
+ _zsh_highlight_main_calculate_fallback $style
+ _zsh_highlight_add_highlight $start $end_ $reply
+ done
+}
+
+# Try to expand $1, if it's possible to do so safely.
+#
+# Uses two parameters from the caller: $parameter_name_pattern and $res.
+#
+# If expansion was done, set $reply to the expansion and return true.
+# Otherwise, return false.
+_zsh_highlight_main_highlighter__try_expand_parameter()
+{
+ local arg="$1"
+ unset reply
+ {
+ # ### For now, expand just '$foo' or '${foo}', possibly with braces, but with
+ # ### no other features of the parameter expansion syntax. (No ${(x)foo},
+ # ### no ${foo[x]}, no ${foo:-x}.)
+ {
+ local -a match mbegin mend
+ local MATCH; integer MBEGIN MEND
+ local parameter_name
+ local -a words
+ if [[ $arg[1] != '$' ]]; then
+ return 1
+ fi
+ if [[ ${arg[2]} == '{' ]] && [[ ${arg[-1]} == '}' ]]; then
+ parameter_name=${${arg:2}%?}
+ else
+ parameter_name=${arg:1}
+ fi
+ if [[ $res == none ]] &&
+ [[ ${parameter_name} =~ ^${~parameter_name_pattern}$ ]] &&
+ [[ ${(tP)MATCH} != *special* ]]
+ then
+ # Set $arg and update $res.
+ case ${(tP)MATCH} in
+ (*array*|*assoc*)
+ words=( ${(P)MATCH} )
+ ;;
+ ("")
+ # not set
+ words=( )
+ ;;
+ (*)
+ # scalar, presumably
+ if [[ $zsyh_user_options[shwordsplit] == on ]]; then
+ words=( ${(P)=MATCH} )
+ else
+ words=( ${(P)MATCH} )
+ fi
+ ;;
+ esac
+ reply=( "${words[@]}" )
+ else
+ return 1
+ fi
+ }
+ }
+}
+
+# $1 is the offset of $4 from the parent buffer. Added to the returned highlights.
+# $2 is the initial braces_stack (for a closing paren).
+# $3 is 1 if $4 contains the end of $BUFFER, else 0.
+# $4 is the buffer to highlight.
+# Returns:
+# $REPLY: $buf[REPLY] is the last character parsed.
+# $reply is an array of region_highlight additions.
+# exit code is 0 if the braces_stack is empty, 1 otherwise.
+_zsh_highlight_main_highlighter_highlight_list()
+{
+ integer start_pos end_pos=0 buf_offset=$1 has_end=$3
+ # alias_style is the style to apply to an alias once $#in_alias == 0
+ # Usually 'alias' but set to 'unknown-token' if any word expanded from
+ # the alias would be highlighted as unknown-token
+ # param_style is analogous for parameter expansions
+ local alias_style param_style last_arg arg buf=$4 highlight_glob=true saw_assignment=false style
+ local in_array_assignment=false # true between 'a=(' and the matching ')'
+ # in_alias is an array of integers with each element equal to the number
+ # of shifts needed until arg=args[1] pops an arg from the next level up
+ # alias or from BUFFER.
+ # in_param is analogous for parameter expansions
+ integer in_param=0 len=$#buf
+ local -a in_alias match mbegin mend list_highlights
+ # seen_alias is a map of aliases already seen to avoid loops like alias a=b b=a
+ local -A seen_alias
+ # Pattern for parameter names
+ readonly parameter_name_pattern='([A-Za-z_][A-Za-z0-9_]*|[0-9]+)'
+ list_highlights=()
+
+ # "R" for round
+ # "Q" for square
+ # "Y" for curly
+ # "T" for [[ ]]
+ # "S" for $( ), =( ), <( ), >( )
+ # "D" for do/done
+ # "$" for 'end' (matches 'foreach' always; also used with cshjunkiequotes in repeat/while)
+ # "?" for 'if'/'fi'; also checked by 'elif'/'else'
+ # ":" for 'then'
+ local braces_stack=$2
+
+ # State machine
+ #
+ # The states are:
+ # - :start: Command word
+ # - :start_of_pipeline: Start of a 'pipeline' as defined in zshmisc(1).
+ # Only valid when :start: is present
+ # - :sudo_opt: A leading-dash option to a precommand, whether it takes an
+ # argument or not. (Example: sudo's "-u" or "-i".)
+ # - :sudo_arg: The argument to a precommand's leading-dash option,
+ # when given as a separate word; i.e., "foo" in "-u foo" (two
+ # words) but not in "-ufoo" (one word).
+ # Note: :sudo_opt: and :sudo_arg: are used for any precommand
+ # declared in ${precommand_options}, not just for sudo(8).
+ # The naming is historical.
+ # - :regular: "Not a command word", and command delimiters are permitted.
+ # Mainly used to detect premature termination of commands.
+ # - :always: The word 'always' in the «{ foo } always { bar }» syntax.
+ #
+ # When the kind of a word is not yet known, $this_word / $next_word may contain
+ # multiple states. For example, after "sudo -i", the next word may be either
+ # another --flag or a command name, hence the state would include both ':start:'
+ # and ':sudo_opt:'.
+ #
+ # The tokens are always added with both leading and trailing colons to serve as
+ # word delimiters (an improvised array); [[ $x == *':foo:'* ]] and x=${x//:foo:/}
+ # will DTRT regardless of how many elements or repetitions $x has.
+ #
+ # Handling of redirections: upon seeing a redirection token, we must stall
+ # the current state --- that is, the value of $this_word --- for two iterations
+ # (one for the redirection operator, one for the word following it representing
+ # the redirection target). Therefore, we set $in_redirection to 2 upon seeing a
+ # redirection operator, decrement it each iteration, and stall the current state
+ # when it is non-zero. Thus, upon reaching the next word (the one that follows
+ # the redirection operator and target), $this_word will still contain values
+ # appropriate for the word immediately following the word that preceded the
+ # redirection operator.
+ #
+ # The "the previous word was a redirection operator" state is not communicated
+ # to the next iteration via $next_word/$this_word as usual, but via
+ # $in_redirection. The value of $next_word from the iteration that processed
+ # the operator is discarded.
+ #
+ # $in_redirection is currently used for:
+ # - comments
+ # - aliases
+ # - redirections
+ # - parameter elision in command position
+ # - 'repeat' loops
+ #
+ local this_word next_word=':start::start_of_pipeline:'
+ integer in_redirection
+ # Processing buffer
+ local proc_buf="$buf"
+ local -a args
+ if [[ $zsyh_user_options[interactivecomments] == on ]]; then
+ args=(${(zZ+c+)buf})
+ else
+ args=(${(z)buf})
+ fi
+
+ # Special case: $(<*) isn't globbing.
+ if [[ $braces_stack == 'S' ]] && (( $+args[3] && ! $+args[4] )) && [[ $args[3] == $'\x29' ]] &&
+ [[ $args[1] == *'<'* ]] && _zsh_highlight_main__is_redirection $args[1]; then
+ highlight_glob=false
+ fi
+
+ while (( $#args )); do
+ last_arg=$arg
+ arg=$args[1]
+ shift args
+ if (( $#in_alias )); then
+ (( in_alias[1]-- ))
+ # Remove leading 0 entries
+ in_alias=($in_alias[$in_alias[(i)<1->],-1])
+ if (( $#in_alias == 0 )); then
+ seen_alias=()
+ # start_pos and end_pos are of the alias (previous $arg) here
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos $alias_style
+ else
+ # We can't unset keys that contain special characters (] \ and some others).
+ # More details: https://www.zsh.org/workers/43269
+ (){
+ local alias_name
+ for alias_name in ${(k)seen_alias[(R)<$#in_alias->]}; do
+ seen_alias=("${(@kv)seen_alias[(I)^$alias_name]}")
+ done
+ }
+ fi
+ fi
+ if (( in_param )); then
+ (( in_param-- ))
+ if (( in_param == 0 )); then
+ # start_pos and end_pos are of the '$foo' word (previous $arg) here
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos $param_style
+ param_style=""
+ fi
+ fi
+
+ # Initialize this_word and next_word.
+ if (( in_redirection == 0 )); then
+ this_word=$next_word
+ next_word=':regular:'
+ elif (( !in_param )); then
+ # Stall $next_word.
+ (( --in_redirection ))
+ fi
+
+ # Initialize per-"simple command" [zshmisc(1)] variables:
+ #
+ # $style how to highlight $arg
+ # $in_array_assignment boolean flag for "between '(' and ')' of array assignment"
+ # $highlight_glob boolean flag for "'noglob' is in effect"
+ # $saw_assignment boolean flag for "was preceded by an assignment"
+ #
+ style=unknown-token
+ if [[ $this_word == *':start:'* ]]; then
+ in_array_assignment=false
+ if [[ $arg == 'noglob' ]]; then
+ highlight_glob=false
+ fi
+ fi
+
+ if (( $#in_alias == 0 && in_param == 0 )); then
+ # Compute the new $start_pos and $end_pos, skipping over whitespace in $buf.
+ [[ "$proc_buf" = (#b)(#s)(''([ $'\t']|[\\]$'\n')#)(?|)* ]]
+ # The first, outer parenthesis
+ integer offset="${#match[1]}"
+ (( start_pos = end_pos + offset ))
+ (( end_pos = start_pos + $#arg ))
+
+ # The zsh lexer considers ';' and newline to be the same token, so
+ # ${(z)} converts all newlines to semicolons. Convert them back here to
+ # make later processing simpler.
+ [[ $arg == ';' && ${match[3]} == $'\n' ]] && arg=$'\n'
+
+ # Compute the new $proc_buf. We advance it
+ # (chop off characters from the beginning)
+ # beyond what end_pos points to, by skipping
+ # as many characters as end_pos was advanced.
+ #
+ # end_pos was advanced by $offset (via start_pos)
+ # and by $#arg. Note the `start_pos=$end_pos`
+ # below.
+ #
+ # As for the [,len]. We could use [,len-start_pos+offset]
+ # here, but to make it easier on eyes, we use len and
+ # rely on the fact that Zsh simply handles that. The
+ # length of proc_buf is len-start_pos+offset because
+ # we're chopping it to match current start_pos, so its
+ # length matches the previous value of start_pos.
+ #
+ # Why [,-1] is slower than [,length] isn't clear.
+ proc_buf="${proc_buf[offset + $#arg + 1,len]}"
+ fi
+
+ # Handle the INTERACTIVE_COMMENTS option.
+ #
+ # We use the (Z+c+) flag so the entire comment is presented as one token in $arg.
+ if [[ $zsyh_user_options[interactivecomments] == on && $arg[1] == $histchars[3] ]]; then
+ if [[ $this_word == *(':regular:'|':start:')* ]]; then
+ style=comment
+ else
+ style=unknown-token # prematurely terminated
+ fi
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos $style
+ # Stall this arg
+ in_redirection=1
+ continue
+ fi
+
+ if [[ $this_word == *':start:'* ]] && ! (( in_redirection )); then
+ # Expand aliases.
+ # An alias is ineligible for expansion while it's being expanded (see #652/#653).
+ _zsh_highlight_main__type "$arg" "$(( ! ${+seen_alias[$arg]} ))"
+ local res="$REPLY"
+ if [[ $res == "alias" ]]; then
+ # Mark insane aliases as unknown-token (cf. #263).
+ if [[ $arg == ?*=* ]]; then
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token
+ continue
+ fi
+ seen_alias[$arg]=$#in_alias
+ _zsh_highlight_main__resolve_alias $arg
+ local -a alias_args
+ # Elision is desired in case alias x=''
+ if [[ $zsyh_user_options[interactivecomments] == on ]]; then
+ alias_args=(${(zZ+c+)REPLY})
+ else
+ alias_args=(${(z)REPLY})
+ fi
+ args=( $alias_args $args )
+ if (( $#in_alias == 0 )); then
+ alias_style=alias
+ else
+ # Transfer the count of this arg to the new element about to be appended.
+ (( in_alias[1]-- ))
+ fi
+ # Add one because we will in_alias[1]-- on the next loop iteration so
+ # this iteration should be considered in in_alias as well
+ in_alias=( $(($#alias_args + 1)) $in_alias )
+ (( in_redirection++ )) # Stall this arg
+ continue
+ else
+ _zsh_highlight_main_highlighter_expand_path $arg
+ _zsh_highlight_main__type "$REPLY" 0
+ res="$REPLY"
+ fi
+ fi
+
+ # Analyse the current word.
+ if _zsh_highlight_main__is_redirection $arg ; then
+ if (( in_redirection == 1 )); then
+ # Two consecutive redirection operators is an error.
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos unknown-token
+ else
+ in_redirection=2
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos redirection
+ fi
+ continue
+ elif [[ $arg == '{'${~parameter_name_pattern}'}' ]] && _zsh_highlight_main__is_redirection $args[1]; then
+ # named file descriptor: {foo}>&2
+ in_redirection=3
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos named-fd
+ continue
+ fi
+
+ # Expand parameters.
+ if (( ! in_param )) && _zsh_highlight_main_highlighter__try_expand_parameter "$arg"; then
+ # That's not entirely correct --- if the parameter's value happens to be a reserved
+ # word, the parameter expansion will be highlighted as a reserved word --- but that
+ # incorrectness is outweighed by the usability improvement of permitting the use of
+ # parameters that refer to commands, functions, and builtins.
+ () {
+ local -a words; words=( "${reply[@]}" )
+ if (( $#words == 0 )) && (( ! in_redirection )); then
+ # Parameter elision is happening
+ (( ++in_redirection ))
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos comment
+ continue
+ else
+ (( in_param = 1 + $#words ))
+ args=( $words $args )
+ arg=$args[1]
+ _zsh_highlight_main__type "$arg" 0
+ res=$REPLY
+ fi
+ }
+ fi
+
+ # Parse the sudo command line
+ if (( ! in_redirection )); then
+ if [[ $this_word == *':sudo_opt:'* ]]; then
+ if [[ -n $flags_with_argument ]] &&
+ {
+ # Trenary
+ if [[ -n $flags_sans_argument ]]
+ then [[ $arg == '-'[$flags_sans_argument]#[$flags_with_argument] ]]
+ else [[ $arg == '-'[$flags_with_argument] ]]
+ fi
+ } then
+ # Flag that requires an argument
+ this_word=${this_word//:start:/}
+ next_word=':sudo_arg:'
+ elif [[ -n $flags_with_argument ]] &&
+ {
+ # Trenary
+ if [[ -n $flags_sans_argument ]]
+ then [[ $arg == '-'[$flags_sans_argument]#[$flags_with_argument]* ]]
+ else [[ $arg == '-'[$flags_with_argument]* ]]
+ fi
+ } then
+ # Argument attached in the same word
+ this_word=${this_word//:start:/}
+ next_word+=':start:'
+ next_word+=':sudo_opt:'
+ elif [[ -n $flags_sans_argument ]] &&
+ [[ $arg == '-'[$flags_sans_argument]# ]]; then
+ # Flag that requires no argument
+ this_word=':sudo_opt:'
+ next_word+=':start:'
+ next_word+=':sudo_opt:'
+ elif [[ -n $flags_solo ]] &&
+ {
+ # Trenary
+ if [[ -n $flags_sans_argument ]]
+ then [[ $arg == '-'[$flags_sans_argument]#[$flags_solo]* ]]
+ else [[ $arg == '-'[$flags_solo]* ]]
+ fi
+ } then
+ # Solo flags
+ this_word=':sudo_opt:'
+ next_word=':regular:' # no :start:, nor :sudo_opt: since we don't know whether the solo flag takes an argument or not
+ elif [[ $arg == '-'* ]]; then
+ # Unknown flag. We don't know whether it takes an argument or not,
+ # so modify $next_word as we do for flags that require no argument.
+ # With that behaviour, if the flag in fact takes no argument we'll
+ # highlight the inner command word correctly, and if it does take an
+ # argument we'll highlight the command word correctly if the argument
+ # was given in the same shell word as the flag (as in '-uphy1729' or
+ # '--user=phy1729' without spaces).
+ this_word=':sudo_opt:'
+ next_word+=':start:'
+ next_word+=':sudo_opt:'
+ else
+ # Not an option flag; nothing to do. (If the command line is
+ # syntactically valid, ${this_word//:sudo_opt:/} should be
+ # non-empty now.)
+ this_word=${this_word//:sudo_opt:/}
+ fi
+ elif [[ $this_word == *':sudo_arg:'* ]]; then
+ next_word+=':sudo_opt:'
+ next_word+=':start:'
+ fi
+ fi
+
+ # The Great Fork: is this a command word? Is this a non-command word?
+ if [[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_COMMANDSEPARATOR:#"$arg"} ]] &&
+ [[ $braces_stack != *T* || $arg != ('||'|'&&') ]]; then
+
+ # First, determine the style of the command separator itself.
+ if _zsh_highlight_main__stack_pop T || _zsh_highlight_main__stack_pop Q; then
+ # Missing closing square bracket(s)
+ style=unknown-token
+ elif $in_array_assignment; then
+ case $arg in
+ # Literal newlines are just fine.
+ ($'\n') style=commandseparator;;
+ # Semicolons are parsed the same way as literal newlines. Nevertheless,
+ # highlight them as errors since they're probably unintended. Compare
+ # issue #691.
+ (';') style=unknown-token;;
+ # Other command separators aren't allowed.
+ (*) style=unknown-token;;
+ esac
+ elif [[ $this_word == *':regular:'* ]]; then
+ style=commandseparator
+ elif [[ $this_word == *':start:'* ]] && [[ $arg == $'\n' ]]; then
+ style=commandseparator
+ elif [[ $this_word == *':start:'* ]] && [[ $arg == ';' ]] && (( $#in_alias )); then
+ style=commandseparator
+ else
+ # Empty commands (semicolon follows nothing) are valid syntax.
+ # However, in interactive use they are likely to be erroneous;
+ # therefore, we highlight them as errors.
+ #
+ # Alias definitions are exempted from this check to allow multiline aliases
+ # with explicit (redundant) semicolons: «alias foo=$'bar;\nbaz'» (issue #677).
+ #
+ # See also #691 about possibly changing the style used here.
+ style=unknown-token
+ fi
+
+ # Second, determine the style of next_word.
+ if [[ $arg == $'\n' ]] && $in_array_assignment; then
+ # literal newline inside an array assignment
+ next_word=':regular:'
+ elif [[ $arg == ';' ]] && $in_array_assignment; then
+ # literal semicolon inside an array assignment
+ next_word=':regular:'
+ else
+ next_word=':start:'
+ highlight_glob=true
+ saw_assignment=false
+ (){
+ local alias_name
+ for alias_name in ${(k)seen_alias[(R)<$#in_alias->]}; do
+ # We can't unset keys that contain special characters (] \ and some others).
+ # More details: https://www.zsh.org/workers/43269
+ seen_alias=("${(@kv)seen_alias[(I)^$alias_name]}")
+ done
+ }
+ if [[ $arg != '|' && $arg != '|&' ]]; then
+ next_word+=':start_of_pipeline:'
+ fi
+ fi
+
+ elif ! (( in_redirection)) && [[ $this_word == *':always:'* && $arg == 'always' ]]; then
+ # try-always construct
+ style=reserved-word # de facto a reserved word, although not de jure
+ highlight_glob=true
+ saw_assignment=false
+ next_word=':start::start_of_pipeline:' # only left brace is allowed, apparently
+ elif ! (( in_redirection)) && [[ $this_word == *':start:'* ]]; then # $arg is the command word
+ if (( ${+precommand_options[$arg]} )) && _zsh_highlight_main__is_runnable $arg; then
+ style=precommand
+ () {
+ set -- "${(@s.:.)precommand_options[$arg]}"
+ flags_with_argument=$1
+ flags_sans_argument=$2
+ flags_solo=$3
+ }
+ next_word=${next_word//:regular:/}
+ next_word+=':sudo_opt:'
+ next_word+=':start:'
+ if [[ $arg == 'exec' || $arg == 'env' ]]; then
+ # To allow "exec 2>&1;" and "env | grep" where there's no command word
+ next_word+=':regular:'
+ fi
+ else
+ case $res in
+ (reserved) # reserved word
+ style=reserved-word
+ # Match braces and handle special cases.
+ case $arg in
+ (time|nocorrect)
+ next_word=${next_word//:regular:/}
+ next_word+=':start:'
+ ;;
+ ($'\x7b')
+ braces_stack='Y'"$braces_stack"
+ ;;
+ ($'\x7d')
+ # We're at command word, so no need to check $right_brace_is_recognised_everywhere
+ _zsh_highlight_main__stack_pop 'Y' reserved-word
+ if [[ $style == reserved-word ]]; then
+ next_word+=':always:'
+ fi
+ ;;
+ ($'\x5b\x5b')
+ braces_stack='T'"$braces_stack"
+ ;;
+ ('do')
+ braces_stack='D'"$braces_stack"
+ ;;
+ ('done')
+ _zsh_highlight_main__stack_pop 'D' reserved-word
+ ;;
+ ('if')
+ braces_stack=':?'"$braces_stack"
+ ;;
+ ('then')
+ _zsh_highlight_main__stack_pop ':' reserved-word
+ ;;
+ ('elif')
+ if [[ ${braces_stack[1]} == '?' ]]; then
+ braces_stack=':'"$braces_stack"
+ else
+ style=unknown-token
+ fi
+ ;;
+ ('else')
+ if [[ ${braces_stack[1]} == '?' ]]; then
+ :
+ else
+ style=unknown-token
+ fi
+ ;;
+ ('fi')
+ _zsh_highlight_main__stack_pop '?'
+ ;;
+ ('foreach')
+ braces_stack='$'"$braces_stack"
+ ;;
+ ('end')
+ _zsh_highlight_main__stack_pop '$' reserved-word
+ ;;
+ ('repeat')
+ # skip the repeat-count word
+ in_redirection=2
+ # The redirection mechanism assumes $this_word describes the word
+ # following the redirection. Make it so.
+ #
+ # That word can be a command word with shortloops (`repeat 2 ls`)
+ # or a command separator (`repeat 2; ls` or `repeat 2; do ls; done`).
+ #
+ # The repeat-count word will be handled like a redirection target.
+ this_word=':start::regular:'
+ ;;
+ ('!')
+ if [[ $this_word != *':start_of_pipeline:'* ]]; then
+ style=unknown-token
+ else
+ # '!' reserved word at start of pipeline; style already set above
+ fi
+ ;;
+ esac
+ if $saw_assignment && [[ $style != unknown-token ]]; then
+ style=unknown-token
+ fi
+ ;;
+ ('suffix alias')
+ style=suffix-alias
+ ;;
+ ('global alias')
+ style=global-alias
+ ;;
+ (alias) :;;
+ (builtin) style=builtin
+ [[ $arg == $'\x5b' ]] && braces_stack='Q'"$braces_stack"
+ ;;
+ (function) style=function;;
+ (command) style=command;;
+ (hashed) style=hashed-command;;
+ (none) if (( ! in_param )) && _zsh_highlight_main_highlighter_check_assign; then
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos assign
+ local i=$(( arg[(i)=] + 1 ))
+ saw_assignment=true
+ if [[ $arg[i] == '(' ]]; then
+ in_array_assignment=true
+ _zsh_highlight_main_add_region_highlight start_pos+i-1 start_pos+i reserved-word
+ else
+ # assignment to a scalar parameter.
+ # (For array assignments, the command doesn't start until the ")" token.)
+ #
+ # Discard :start_of_pipeline:, if present, as '!' is not valid
+ # after assignments.
+ next_word+=':start:'
+ if (( i <= $#arg )); then
+ () {
+ local highlight_glob=false
+ [[ $zsyh_user_options[globassign] == on ]] && highlight_glob=true
+ _zsh_highlight_main_highlighter_highlight_argument $i
+ }
+ fi
+ fi
+ continue
+ elif (( ! in_param )) &&
+ [[ $arg[0,1] = $histchars[0,1] ]] && (( $#arg[0,2] == 2 )); then
+ style=history-expansion
+ elif (( ! in_param )) &&
+ [[ $arg[0,1] == $histchars[2,2] ]]; then
+ style=history-expansion
+ elif (( ! in_param )) &&
+ ! $saw_assignment &&
+ [[ $arg[1,2] == '((' ]]; then
+ # Arithmetic evaluation.
+ #
+ # Note: prior to zsh-5.1.1-52-g4bed2cf (workers/36669), the ${(z)...}
+ # splitter would only output the '((' token if the matching '))' had
+ # been typed. Therefore, under those versions of zsh, BUFFER="(( 42"
+ # would be highlighted as an error until the matching "))" are typed.
+ #
+ # We highlight just the opening parentheses, as a reserved word; this
+ # is how [[ ... ]] is highlighted, too.
+ _zsh_highlight_main_add_region_highlight $start_pos $((start_pos + 2)) reserved-word
+ if [[ $arg[-2,-1] == '))' ]]; then
+ _zsh_highlight_main_add_region_highlight $((end_pos - 2)) $end_pos reserved-word
+ fi
+ continue
+ elif (( ! in_param )) &&
+ [[ $arg == '()' ]]; then
+ # anonymous function
+ style=reserved-word
+ elif (( ! in_param )) &&
+ ! $saw_assignment &&
+ [[ $arg == $'\x28' ]]; then
+ # subshell
+ style=reserved-word
+ braces_stack='R'"$braces_stack"
+ elif (( ! in_param )) &&
+ [[ $arg == $'\x29' ]]; then
+ # end of subshell or command substitution
+ if _zsh_highlight_main__stack_pop 'S'; then
+ REPLY=$start_pos
+ reply=($list_highlights)
+ return 0
+ fi
+ _zsh_highlight_main__stack_pop 'R' reserved-word
+ else
+ if _zsh_highlight_main_highlighter_check_path $arg 1; then
+ style=$REPLY
+ else
+ style=unknown-token
+ fi
+ fi
+ ;;
+ (*) _zsh_highlight_main_add_region_highlight $start_pos $end_pos arg0_$res
+ continue
+ ;;
+ esac
+ fi
+ if [[ -n ${(M)ZSH_HIGHLIGHT_TOKENS_CONTROL_FLOW:#"$arg"} ]]; then
+ next_word=':start::start_of_pipeline:'
+ fi
+ elif _zsh_highlight_main__is_global_alias "$arg"; then # $arg is a global alias that isn't in command position
+ style=global-alias
+ else # $arg is a non-command word
+ case $arg in
+ ($'\x29')
+ # subshell or end of array assignment
+ if $in_array_assignment; then
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos assign
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos reserved-word
+ in_array_assignment=false
+ next_word+=':start:'
+ continue
+ elif (( in_redirection )); then
+ style=unknown-token
+ else
+ if _zsh_highlight_main__stack_pop 'S'; then
+ REPLY=$start_pos
+ reply=($list_highlights)
+ return 0
+ fi
+ _zsh_highlight_main__stack_pop 'R' reserved-word
+ fi
+ ;;
+ ($'\x28\x29')
+ # possibly a function definition
+ if (( in_redirection )) || $in_array_assignment; then
+ style=unknown-token
+ else
+ if [[ $zsyh_user_options[multifuncdef] == on ]] || false # TODO: or if the previous word was a command word
+ then
+ next_word+=':start::start_of_pipeline:'
+ fi
+ style=reserved-word
+ fi
+ ;;
+ (*) if false; then
+ elif [[ $arg = $'\x7d' ]] && $right_brace_is_recognised_everywhere; then
+ # Parsing rule: {
+ #
+ # Additionally, `tt(})' is recognized in any position if neither the
+ # tt(IGNORE_BRACES) option nor the tt(IGNORE_CLOSE_BRACES) option is set.
+ if (( in_redirection )) || $in_array_assignment; then
+ style=unknown-token
+ else
+ _zsh_highlight_main__stack_pop 'Y' reserved-word
+ if [[ $style == reserved-word ]]; then
+ next_word+=':always:'
+ fi
+ fi
+ elif [[ $arg[0,1] = $histchars[0,1] ]] && (( $#arg[0,2] == 2 )); then
+ style=history-expansion
+ elif [[ $arg == $'\x5d\x5d' ]] && _zsh_highlight_main__stack_pop 'T' reserved-word; then
+ :
+ elif [[ $arg == $'\x5d' ]] && _zsh_highlight_main__stack_pop 'Q' builtin; then
+ :
+ else
+ _zsh_highlight_main_highlighter_highlight_argument 1 $(( 1 != in_redirection ))
+ continue
+ fi
+ ;;
+ esac
+ fi
+ _zsh_highlight_main_add_region_highlight $start_pos $end_pos $style
+ done
+ (( $#in_alias )) && in_alias=() _zsh_highlight_main_add_region_highlight $start_pos $end_pos $alias_style
+ (( in_param == 1 )) && in_param=0 _zsh_highlight_main_add_region_highlight $start_pos $end_pos $param_style
+ [[ "$proc_buf" = (#b)(#s)(([[:space:]]|\\$'\n')#) ]]
+ REPLY=$(( end_pos + ${#match[1]} - 1 ))
+ reply=($list_highlights)
+ return $(( $#braces_stack > 0 ))
+}
+
+# Check if $arg is variable assignment
+_zsh_highlight_main_highlighter_check_assign()
+{
+ setopt localoptions extended_glob
+ [[ $arg == [[:alpha:]_][[:alnum:]_]#(|\[*\])(|[+])=* ]] ||
+ [[ $arg == [0-9]##(|[+])=* ]]
+}
+
+_zsh_highlight_main_highlighter_highlight_path_separators()
+{
+ local pos style_pathsep
+ style_pathsep=$1_pathseparator
+ reply=()
+ [[ -z "$ZSH_HIGHLIGHT_STYLES[$style_pathsep]" || "$ZSH_HIGHLIGHT_STYLES[$1]" == "$ZSH_HIGHLIGHT_STYLES[$style_pathsep]" ]] && return 0
+ for (( pos = start_pos; $pos <= end_pos; pos++ )) ; do
+ if [[ $BUFFER[pos] == / ]]; then
+ reply+=($((pos - 1)) $pos $style_pathsep)
+ fi
+ done
+}
+
+# Check if $1 is a path.
+# If yes, return 0 and in $REPLY the style to use.
+# Else, return non-zero (and the contents of $REPLY is undefined).
+#
+# $2 should be non-zero iff we're in command position.
+_zsh_highlight_main_highlighter_check_path()
+{
+ _zsh_highlight_main_highlighter_expand_path "$1"
+ local expanded_path="$REPLY" tmp_path
+ integer in_command_position=$2
+
+ if [[ $zsyh_user_options[autocd] == on ]]; then
+ integer autocd=1
+ else
+ integer autocd=0
+ fi
+
+ if (( in_command_position )); then
+ # ### Currently, this value is never returned: either it's overwritten
+ # ### below, or the return code is non-zero
+ REPLY=arg0
+ else
+ REPLY=path
+ fi
+
+ if [[ ${1[1]} == '=' && $1 == ??* && ${1[2]} != $'\x28' && $zsyh_user_options[equals] == 'on' && $expanded_path[1] != '/' ]]; then
+ REPLY=unknown-token # will error out if executed
+ return 0
+ fi
+
+ [[ -z $expanded_path ]] && return 1
+
+ # Check if this is a blacklisted path
+ if [[ $expanded_path[1] == / ]]; then
+ tmp_path=$expanded_path
+ else
+ tmp_path=$PWD/$expanded_path
+ fi
+ tmp_path=$tmp_path:a
+
+ while [[ $tmp_path != / ]]; do
+ [[ -n ${(M)ZSH_HIGHLIGHT_DIRS_BLACKLIST:#$tmp_path} ]] && return 1
+ tmp_path=$tmp_path:h
+ done
+
+ if (( in_command_position )); then
+ if [[ -x $expanded_path ]]; then
+ if (( autocd )); then
+ if [[ -d $expanded_path ]]; then
+ REPLY=autodirectory
+ fi
+ return 0
+ elif [[ ! -d $expanded_path ]]; then
+ # ### This seems unreachable for the current callers
+ return 0
+ fi
+ fi
+ else
+ if [[ -L $expanded_path || -e $expanded_path ]]; then
+ return 0
+ fi
+ fi
+
+ # Search the path in CDPATH
+ if [[ $expanded_path != /* ]] && (( autocd || ! in_command_position )); then
+ # TODO: When we've dropped support for pre-5.0.6 zsh, use the *(Y1) glob qualifier here.
+ local cdpath_dir
+ for cdpath_dir in $cdpath ; do
+ if [[ -d "$cdpath_dir/$expanded_path" && -x "$cdpath_dir/$expanded_path" ]]; then
+ if (( in_command_position && autocd )); then
+ REPLY=autodirectory
+ fi
+ return 0
+ fi
+ done
+ fi
+
+ # If dirname($1) doesn't exist, neither does $1.
+ [[ ! -d ${expanded_path:h} ]] && return 1
+
+ # If this word ends the buffer, check if it's the prefix of a valid path.
+ if (( has_end && (len == end_pos) )) &&
+ (( ! $#in_alias )) &&
+ [[ $WIDGET != zle-line-finish ]]; then
+ # TODO: When we've dropped support for pre-5.0.6 zsh, use the *(Y1) glob qualifier here.
+ local -a tmp
+ if (( in_command_position )); then
+ # We include directories even when autocd is enabled, because those
+ # directories might contain executable files: e.g., BUFFER="/bi" en route
+ # to typing "/bin/sh".
+ tmp=( ${expanded_path}*(N-*,N-/) )
+ else
+ tmp=( ${expanded_path}*(N) )
+ fi
+ (( ${+tmp[1]} )) && REPLY=path_prefix && return 0
+ fi
+
+ # It's not a path.
+ return 1
+}
+
+# Highlight an argument and possibly special chars in quotes starting at $1 in $arg
+# This command will at least highlight $1 to end_pos with the default style
+# If $2 is set to 0, the argument cannot be highlighted as an option.
+#
+# This function currently assumes it's never called for the command word.
+_zsh_highlight_main_highlighter_highlight_argument()
+{
+ local base_style=default i=$1 option_eligible=${2:-1} path_eligible=1 ret start style
+ local -a highlights
+
+ local -a match mbegin mend
+ local MATCH; integer MBEGIN MEND
+
+ case "$arg[i]" in
+ '%')
+ if [[ $arg[i+1] == '?' ]]; then
+ (( i += 2 ))
+ fi
+ ;;
+ '-')
+ if (( option_eligible )); then
+ if [[ $arg[i+1] == - ]]; then
+ base_style=double-hyphen-option
+ else
+ base_style=single-hyphen-option
+ fi
+ path_eligible=0
+ fi
+ ;;
+ '=')
+ if [[ $arg[i+1] == $'\x28' ]]; then
+ (( i += 2 ))
+ _zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg[i,-1]
+ ret=$?
+ (( i += REPLY ))
+ highlights+=(
+ $(( start_pos + $1 - 1 )) $(( start_pos + i )) process-substitution
+ $(( start_pos + $1 - 1 )) $(( start_pos + $1 + 1 )) process-substitution-delimiter
+ $reply
+ )
+ if (( ret == 0 )); then
+ highlights+=($(( start_pos + i - 1 )) $(( start_pos + i )) process-substitution-delimiter)
+ fi
+ fi
+ esac
+
+ # This loop is a hot path. Keep it fast!
+ (( --i ))
+ while (( ++i <= $#arg )); do
+ i=${arg[(ib.i.)[\\\'\"\`\$\<\>\*\?]]}
+ case "$arg[$i]" in
+ "") break;;
+ "\\") (( i += 1 )); continue;;
+ "'")
+ _zsh_highlight_main_highlighter_highlight_single_quote $i
+ (( i = REPLY ))
+ highlights+=($reply)
+ ;;
+ '"')
+ _zsh_highlight_main_highlighter_highlight_double_quote $i
+ (( i = REPLY ))
+ highlights+=($reply)
+ ;;
+ '`')
+ _zsh_highlight_main_highlighter_highlight_backtick $i
+ (( i = REPLY ))
+ highlights+=($reply)
+ ;;
+ '$')
+ if [[ $arg[i+1] != "'" ]]; then
+ path_eligible=0
+ fi
+ if [[ $arg[i+1] == "'" ]]; then
+ _zsh_highlight_main_highlighter_highlight_dollar_quote $i
+ (( i = REPLY ))
+ highlights+=($reply)
+ continue
+ elif [[ $arg[i+1] == $'\x28' ]]; then
+ if [[ $arg[i+2] == $'\x28' ]] && _zsh_highlight_main_highlighter_highlight_arithmetic $i; then
+ # Arithmetic expansion
+ (( i = REPLY ))
+ highlights+=($reply)
+ continue
+ fi
+ start=$i
+ (( i += 2 ))
+ _zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg[i,-1]
+ ret=$?
+ (( i += REPLY ))
+ highlights+=(
+ $(( start_pos + start - 1)) $(( start_pos + i )) command-substitution-unquoted
+ $(( start_pos + start - 1)) $(( start_pos + start + 1)) command-substitution-delimiter-unquoted
+ $reply
+ )
+ if (( ret == 0 )); then
+ highlights+=($(( start_pos + i - 1)) $(( start_pos + i )) command-substitution-delimiter-unquoted)
+ fi
+ continue
+ fi
+ while [[ $arg[i+1] == [=~#+'^'] ]]; do
+ (( i += 1 ))
+ done
+ if [[ $arg[i+1] == [*@#?$!-] ]]; then
+ (( i += 1 ))
+ fi;;
+ [\<\>])
+ if [[ $arg[i+1] == $'\x28' ]]; then # \x28 = open paren
+ start=$i
+ (( i += 2 ))
+ _zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg[i,-1]
+ ret=$?
+ (( i += REPLY ))
+ highlights+=(
+ $(( start_pos + start - 1)) $(( start_pos + i )) process-substitution
+ $(( start_pos + start - 1)) $(( start_pos + start + 1 )) process-substitution-delimiter
+ $reply
+ )
+ if (( ret == 0 )); then
+ highlights+=($(( start_pos + i - 1)) $(( start_pos + i )) process-substitution-delimiter)
+ fi
+ continue
+ fi
+ ;|
+ *)
+ if $highlight_glob &&
+ [[ $zsyh_user_options[multios] == on || $in_redirection -eq 0 ]] &&
+ [[ ${arg[$i]} =~ ^[*?] || ${arg:$i-1} =~ ^\<[0-9]*-[0-9]*\> ]]; then
+ highlights+=($(( start_pos + i - 1 )) $(( start_pos + i + $#MATCH - 1)) globbing)
+ (( i += $#MATCH - 1 ))
+ path_eligible=0
+ else
+ continue
+ fi
+ ;;
+ esac
+ done
+
+ if (( path_eligible )); then
+ if (( in_redirection )) && [[ $last_arg == *['<>']['&'] && $arg[$1,-1] == (<0->|p|-) ]]; then
+ if [[ $arg[$1,-1] == (p|-) ]]; then
+ base_style=redirection
+ else
+ base_style=numeric-fd
+ fi
+ # This function is currently never called for the command word, so $2 is hard-coded as 0.
+ elif _zsh_highlight_main_highlighter_check_path $arg[$1,-1] 0; then
+ base_style=$REPLY
+ _zsh_highlight_main_highlighter_highlight_path_separators $base_style
+ highlights+=($reply)
+ fi
+ fi
+
+ highlights=($(( start_pos + $1 - 1 )) $end_pos $base_style $highlights)
+ _zsh_highlight_main_add_many_region_highlights $highlights
+}
+
+# Quote Helper Functions
+#
+# $arg is expected to be set to the current argument
+# $start_pos is expected to be set to the start of $arg in $BUFFER
+# $1 is the index in $arg which starts the quote
+# $REPLY is returned as the end of quote index in $arg
+# $reply is returned as an array of region_highlight additions
+
+# Highlight single-quoted strings
+_zsh_highlight_main_highlighter_highlight_single_quote()
+{
+ local arg1=$1 i q=\' style
+ i=$arg[(ib:arg1+1:)$q]
+ reply=()
+
+ if [[ $zsyh_user_options[rcquotes] == on ]]; then
+ while [[ $arg[i+1] == "'" ]]; do
+ reply+=($(( start_pos + i - 1 )) $(( start_pos + i + 1 )) rc-quote)
+ (( i++ ))
+ i=$arg[(ib:i+1:)$q]
+ done
+ fi
+
+ if [[ $arg[i] == "'" ]]; then
+ style=single-quoted-argument
+ else
+ # If unclosed, i points past the end
+ (( i-- ))
+ style=single-quoted-argument-unclosed
+ fi
+ reply=($(( start_pos + arg1 - 1 )) $(( start_pos + i )) $style $reply)
+ REPLY=$i
+}
+
+# Highlight special chars inside double-quoted strings
+_zsh_highlight_main_highlighter_highlight_double_quote()
+{
+ local -a breaks match mbegin mend saved_reply
+ local MATCH; integer last_break=$(( start_pos + $1 - 1 )) MBEGIN MEND
+ local i j k ret style
+ reply=()
+
+ for (( i = $1 + 1 ; i <= $#arg ; i += 1 )) ; do
+ (( j = i + start_pos - 1 ))
+ (( k = j + 1 ))
+ case "$arg[$i]" in
+ ('"') break;;
+ ('`') saved_reply=($reply)
+ _zsh_highlight_main_highlighter_highlight_backtick $i
+ (( i = REPLY ))
+ reply=($saved_reply $reply)
+ continue
+ ;;
+ ('$') style=dollar-double-quoted-argument
+ # Look for an alphanumeric parameter name.
+ if [[ ${arg:$i} =~ ^([A-Za-z_][A-Za-z0-9_]*|[0-9]+) ]] ; then
+ (( k += $#MATCH )) # highlight the parameter name
+ (( i += $#MATCH )) # skip past it
+ elif [[ ${arg:$i} =~ ^[{]([A-Za-z_][A-Za-z0-9_]*|[0-9]+)[}] ]] ; then
+ (( k += $#MATCH )) # highlight the parameter name and braces
+ (( i += $#MATCH )) # skip past it
+ elif [[ $arg[i+1] == '$' ]]; then
+ # $$ - pid
+ (( k += 1 )) # highlight both dollar signs
+ (( i += 1 )) # don't consider the second one as introducing another parameter expansion
+ elif [[ $arg[i+1] == [-#*@?] ]]; then
+ # $#, $*, $@, $?, $- - like $$ above
+ (( k += 1 )) # highlight both dollar signs
+ (( i += 1 )) # don't consider the second one as introducing another parameter expansion
+ elif [[ $arg[i+1] == $'\x28' ]]; then
+ saved_reply=($reply)
+ if [[ $arg[i+2] == $'\x28' ]] && _zsh_highlight_main_highlighter_highlight_arithmetic $i; then
+ # Arithmetic expansion
+ (( i = REPLY ))
+ reply=($saved_reply $reply)
+ continue
+ fi
+
+ breaks+=( $last_break $(( start_pos + i - 1 )) )
+ (( i += 2 ))
+ _zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg[i,-1]
+ ret=$?
+ (( i += REPLY ))
+ last_break=$(( start_pos + i ))
+ reply=(
+ $saved_reply
+ $j $(( start_pos + i )) command-substitution-quoted
+ $j $(( j + 2 )) command-substitution-delimiter-quoted
+ $reply
+ )
+ if (( ret == 0 )); then
+ reply+=($(( start_pos + i - 1 )) $(( start_pos + i )) command-substitution-delimiter-quoted)
+ fi
+ continue
+ else
+ continue
+ fi
+ ;;
+ "\\") style=back-double-quoted-argument
+ if [[ \\\`\"\$${histchars[1]} == *$arg[$i+1]* ]]; then
+ (( k += 1 )) # Color following char too.
+ (( i += 1 )) # Skip parsing the escaped char.
+ else
+ continue
+ fi
+ ;;
+ ($histchars[1]) # ! - may be a history expansion
+ if [[ $arg[i+1] != ('='|$'\x28'|$'\x7b'|[[:blank:]]) ]]; then
+ style=history-expansion
+ else
+ continue
+ fi
+ ;;
+ *) continue ;;
+
+ esac
+ reply+=($j $k $style)
+ done
+
+ if [[ $arg[i] == '"' ]]; then
+ style=double-quoted-argument
+ else
+ # If unclosed, i points past the end
+ (( i-- ))
+ style=double-quoted-argument-unclosed
+ fi
+ (( last_break != start_pos + i )) && breaks+=( $last_break $(( start_pos + i )) )
+ saved_reply=($reply)
+ reply=()
+ for 1 2 in $breaks; do
+ (( $1 != $2 )) && reply+=($1 $2 $style)
+ done
+ reply+=($saved_reply)
+ REPLY=$i
+}
+
+# Highlight special chars inside dollar-quoted strings
+_zsh_highlight_main_highlighter_highlight_dollar_quote()
+{
+ local -a match mbegin mend
+ local MATCH; integer MBEGIN MEND
+ local i j k style
+ local AA
+ integer c
+ reply=()
+
+ for (( i = $1 + 2 ; i <= $#arg ; i += 1 )) ; do
+ (( j = i + start_pos - 1 ))
+ (( k = j + 1 ))
+ case "$arg[$i]" in
+ "'") break;;
+ "\\") style=back-dollar-quoted-argument
+ for (( c = i + 1 ; c <= $#arg ; c += 1 )); do
+ [[ "$arg[$c]" != ([0-9xXuUa-fA-F]) ]] && break
+ done
+ AA=$arg[$i+1,$c-1]
+ # Matching for HEX and OCT values like \0xA6, \xA6 or \012
+ if [[ "$AA" =~ "^(x|X)[0-9a-fA-F]{1,2}"
+ || "$AA" =~ "^[0-7]{1,3}"
+ || "$AA" =~ "^u[0-9a-fA-F]{1,4}"
+ || "$AA" =~ "^U[0-9a-fA-F]{1,8}"
+ ]]; then
+ (( k += $#MATCH ))
+ (( i += $#MATCH ))
+ else
+ if (( $#arg > $i+1 )) && [[ $arg[$i+1] == [xXuU] ]]; then
+ # \x not followed by hex digits is probably an error
+ style=unknown-token
+ fi
+ (( k += 1 )) # Color following char too.
+ (( i += 1 )) # Skip parsing the escaped char.
+ fi
+ ;;
+ *) continue ;;
+
+ esac
+ reply+=($j $k $style)
+ done
+
+ if [[ $arg[i] == "'" ]]; then
+ style=dollar-quoted-argument
+ else
+ # If unclosed, i points past the end
+ (( i-- ))
+ style=dollar-quoted-argument-unclosed
+ fi
+ reply=($(( start_pos + $1 - 1 )) $(( start_pos + i )) $style $reply)
+ REPLY=$i
+}
+
+# Highlight backtick substitutions
+_zsh_highlight_main_highlighter_highlight_backtick()
+{
+ # buf is the contents of the backticks with a layer of backslashes removed.
+ # last is the index of arg for the start of the string to be copied into buf.
+ # It is either one past the beginning backtick or one past the last backslash.
+ # offset is a count of consumed \ (the delta between buf and arg).
+ # offsets is an array indexed by buf offset of when the delta between buf and arg changes.
+ # It is sparse, so search backwards to the last value
+ local buf highlight style=back-quoted-argument-unclosed style_end
+ local -i arg1=$1 end_ i=$1 last offset=0 start subshell_has_end=0
+ local -a highlight_zone highlights offsets
+ reply=()
+
+ last=$(( arg1 + 1 ))
+ # Remove one layer of backslashes and find the end
+ while i=$arg[(ib:i+1:)[\\\\\`]]; do # find the next \ or `
+ if (( i > $#arg )); then
+ buf=$buf$arg[last,i]
+ offsets[i-arg1-offset]='' # So we never index past the end
+ (( i-- ))
+ subshell_has_end=$(( has_end && (start_pos + i == len) ))
+ break
+ fi
+
+ if [[ $arg[i] == '\' ]]; then
+ (( i++ ))
+ # POSIX XCU 2.6.3
+ if [[ $arg[i] == ('$'|'`'|'\') ]]; then
+ buf=$buf$arg[last,i-2]
+ (( offset++ ))
+ # offsets is relative to buf, so adjust by -arg1
+ offsets[i-arg1-offset]=$offset
+ else
+ buf=$buf$arg[last,i-1]
+ fi
+ else # it's an unquoted ` and this is the end
+ style=back-quoted-argument
+ style_end=back-quoted-argument-delimiter
+ buf=$buf$arg[last,i-1]
+ offsets[i-arg1-offset]='' # So we never index past the end
+ break
+ fi
+ last=$i
+ done
+
+ _zsh_highlight_main_highlighter_highlight_list 0 '' $subshell_has_end $buf
+
+ # Munge the reply to account for removed backslashes
+ for start end_ highlight in $reply; do
+ start=$(( start_pos + arg1 + start + offsets[(Rb:start:)?*] ))
+ end_=$(( start_pos + arg1 + end_ + offsets[(Rb:end_:)?*] ))
+ highlights+=($start $end_ $highlight)
+ if [[ $highlight == back-quoted-argument-unclosed && $style == back-quoted-argument ]]; then
+ # An inner backtick command substitution is unclosed, but this level is closed
+ style_end=unknown-token
+ fi
+ done
+
+ reply=(
+ $(( start_pos + arg1 - 1 )) $(( start_pos + i )) $style
+ $(( start_pos + arg1 - 1 )) $(( start_pos + arg1 )) back-quoted-argument-delimiter
+ $highlights
+ )
+ if (( $#style_end )); then
+ reply+=($(( start_pos + i - 1)) $(( start_pos + i )) $style_end)
+ fi
+ REPLY=$i
+}
+
+# Highlight special chars inside arithmetic expansions
+_zsh_highlight_main_highlighter_highlight_arithmetic()
+{
+ local -a saved_reply
+ local style
+ integer i j k paren_depth ret
+ reply=()
+
+ for (( i = $1 + 3 ; i <= end_pos - start_pos ; i += 1 )) ; do
+ (( j = i + start_pos - 1 ))
+ (( k = j + 1 ))
+ case "$arg[$i]" in
+ [\'\"\\@{}])
+ style=unknown-token
+ ;;
+ '(')
+ (( paren_depth++ ))
+ continue
+ ;;
+ ')')
+ if (( paren_depth )); then
+ (( paren_depth-- ))
+ continue
+ fi
+ [[ $arg[i+1] == ')' ]] && { (( i++ )); break; }
+ # Special case ) at the end of the buffer to avoid flashing command substitution for a character
+ (( has_end && (len == k) )) && break
+ # This is a single paren and there are no open parens, so this isn't an arithmetic expansion
+ return 1
+ ;;
+ '`')
+ saved_reply=($reply)
+ _zsh_highlight_main_highlighter_highlight_backtick $i
+ (( i = REPLY ))
+ reply=($saved_reply $reply)
+ continue
+ ;;
+ '$' )
+ if [[ $arg[i+1] == $'\x28' ]]; then
+ saved_reply=($reply)
+ if [[ $arg[i+2] == $'\x28' ]] && _zsh_highlight_main_highlighter_highlight_arithmetic $i; then
+ # Arithmetic expansion
+ (( i = REPLY ))
+ reply=($saved_reply $reply)
+ continue
+ fi
+
+ (( i += 2 ))
+ _zsh_highlight_main_highlighter_highlight_list $(( start_pos + i - 1 )) S $has_end $arg[i,end_pos]
+ ret=$?
+ (( i += REPLY ))
+ reply=(
+ $saved_reply
+ $j $(( start_pos + i )) command-substitution-quoted
+ $j $(( j + 2 )) command-substitution-delimiter-quoted
+ $reply
+ )
+ if (( ret == 0 )); then
+ reply+=($(( start_pos + i - 1 )) $(( start_pos + i )) command-substitution-delimiter)
+ fi
+ continue
+ else
+ continue
+ fi
+ ;;
+ ($histchars[1]) # ! - may be a history expansion
+ if [[ $arg[i+1] != ('='|$'\x28'|$'\x7b'|[[:blank:]]) ]]; then
+ style=history-expansion
+ else
+ continue
+ fi
+ ;;
+ *)
+ continue
+ ;;
+
+ esac
+ reply+=($j $k $style)
+ done
+
+ if [[ $arg[i] != ')' ]]; then
+ # If unclosed, i points past the end
+ (( i-- ))
+ fi
+ style=arithmetic-expansion
+ reply=($(( start_pos + $1 - 1)) $(( start_pos + i )) arithmetic-expansion $reply)
+ REPLY=$i
+}
+
+
+# Called with a single positional argument.
+# Perform filename expansion (tilde expansion) on the argument and set $REPLY to the expanded value.
+#
+# Does not perform filename generation (globbing).
+_zsh_highlight_main_highlighter_expand_path()
+{
+ (( $# == 1 )) || print -r -- >&2 "zsh-syntax-highlighting: BUG: _zsh_highlight_main_highlighter_expand_path: called without argument"
+
+ # The $~1 syntax normally performs filename generation, but not when it's on the right-hand side of ${x:=y}.
+ setopt localoptions nonomatch
+ unset REPLY
+ : ${REPLY:=${(Q)${~1}}}
+}
+
+# -------------------------------------------------------------------------------------------------
+# Main highlighter initialization
+# -------------------------------------------------------------------------------------------------
+
+_zsh_highlight_main__precmd_hook() {
+ # Unset the WARN_NESTED_VAR option, taking care not to error if the option
+ # doesn't exist (zsh older than zsh-5.3.1-test-2).
+ setopt localoptions
+ if eval '[[ -o warnnestedvar ]]' 2>/dev/null; then
+ unsetopt warnnestedvar
+ fi
+
+ _zsh_highlight_main__command_type_cache=()
+}
+
+autoload -Uz add-zsh-hook
+if add-zsh-hook precmd _zsh_highlight_main__precmd_hook 2>/dev/null; then
+ # Initialize command type cache
+ typeset -gA _zsh_highlight_main__command_type_cache
+else
+ print -r -- >&2 'zsh-syntax-highlighting: Failed to load add-zsh-hook. Some speed optimizations will not be used.'
+ # Make sure the cache is unset
+ unset _zsh_highlight_main__command_type_cache
+fi
+typeset -ga ZSH_HIGHLIGHT_DIRS_BLACKLIST
diff --git a/plugins/zsh-syntax-highlighting/highlighters/pattern/README.md b/plugins/zsh-syntax-highlighting/highlighters/pattern/README.md
new file mode 120000
index 000000000..5ff00e645
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/pattern/README.md
@@ -0,0 +1 @@
+../../docs/highlighters/pattern.md
\ No newline at end of file
diff --git a/plugins/zsh-syntax-highlighting/highlighters/pattern/pattern-highlighter.zsh b/plugins/zsh-syntax-highlighting/highlighters/pattern/pattern-highlighter.zsh
new file mode 100644
index 000000000..e0422d084
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/pattern/pattern-highlighter.zsh
@@ -0,0 +1,60 @@
+# -------------------------------------------------------------------------------------------------
+# Copyright (c) 2010-2011 zsh-syntax-highlighting contributors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+# may be used to endorse or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -------------------------------------------------------------------------------------------------
+# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
+# vim: ft=zsh sw=2 ts=2 et
+# -------------------------------------------------------------------------------------------------
+
+
+# List of keyword and color pairs.
+typeset -gA ZSH_HIGHLIGHT_PATTERNS
+
+# Whether the pattern highlighter should be called or not.
+_zsh_highlight_highlighter_pattern_predicate()
+{
+ _zsh_highlight_buffer_modified
+}
+
+# Pattern syntax highlighting function.
+_zsh_highlight_highlighter_pattern_paint()
+{
+ setopt localoptions extendedglob
+ local pattern
+ for pattern in ${(k)ZSH_HIGHLIGHT_PATTERNS}; do
+ _zsh_highlight_pattern_highlighter_loop "$BUFFER" "$pattern"
+ done
+}
+
+_zsh_highlight_pattern_highlighter_loop()
+{
+ # This does *not* do its job syntactically, sorry.
+ local buf="$1" pat="$2"
+ local -a match mbegin mend
+ local MATCH; integer MBEGIN MEND
+ if [[ "$buf" == (#b)(*)(${~pat})* ]]; then
+ region_highlight+=("$((mbegin[2] - 1)) $mend[2] $ZSH_HIGHLIGHT_PATTERNS[$pat], memo=zsh-syntax-highlighting")
+ "$0" "$match[1]" "$pat"; return $?
+ fi
+}
diff --git a/plugins/zsh-syntax-highlighting/highlighters/regexp/README.md b/plugins/zsh-syntax-highlighting/highlighters/regexp/README.md
new file mode 120000
index 000000000..9f2522d51
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/regexp/README.md
@@ -0,0 +1 @@
+../../docs/highlighters/regexp.md
\ No newline at end of file
diff --git a/plugins/zsh-syntax-highlighting/highlighters/regexp/regexp-highlighter.zsh b/plugins/zsh-syntax-highlighting/highlighters/regexp/regexp-highlighter.zsh
new file mode 100644
index 000000000..0d43aacd3
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/regexp/regexp-highlighter.zsh
@@ -0,0 +1,62 @@
+# -------------------------------------------------------------------------------------------------
+# Copyright (c) 2010-2016 zsh-syntax-highlighting contributors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+# may be used to endorse or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -------------------------------------------------------------------------------------------------
+# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
+# vim: ft=zsh sw=2 ts=2 et
+# -------------------------------------------------------------------------------------------------
+
+
+# List of keyword and color pairs.
+typeset -gA ZSH_HIGHLIGHT_REGEXP
+
+# Whether the pattern highlighter should be called or not.
+_zsh_highlight_highlighter_regexp_predicate()
+{
+ _zsh_highlight_buffer_modified
+}
+
+# Pattern syntax highlighting function.
+_zsh_highlight_highlighter_regexp_paint()
+{
+ setopt localoptions extendedglob
+ local pattern
+ for pattern in ${(k)ZSH_HIGHLIGHT_REGEXP}; do
+ _zsh_highlight_regexp_highlighter_loop "$BUFFER" "$pattern"
+ done
+}
+
+_zsh_highlight_regexp_highlighter_loop()
+{
+ local buf="$1" pat="$2"
+ integer OFFSET=0
+ local MATCH; integer MBEGIN MEND
+ local -a match mbegin mend
+ while true; do
+ [[ "$buf" =~ "$pat" ]] || return;
+ region_highlight+=("$((MBEGIN - 1 + OFFSET)) $((MEND + OFFSET)) $ZSH_HIGHLIGHT_REGEXP[$pat], memo=zsh-syntax-highlighting")
+ buf="$buf[$(($MEND+1)),-1]"
+ OFFSET=$((MEND+OFFSET));
+ done
+}
diff --git a/plugins/zsh-syntax-highlighting/highlighters/root/README.md b/plugins/zsh-syntax-highlighting/highlighters/root/README.md
new file mode 120000
index 000000000..ca6799d6f
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/root/README.md
@@ -0,0 +1 @@
+../../docs/highlighters/root.md
\ No newline at end of file
diff --git a/plugins/zsh-syntax-highlighting/highlighters/root/root-highlighter.zsh b/plugins/zsh-syntax-highlighting/highlighters/root/root-highlighter.zsh
new file mode 100644
index 000000000..3718c449a
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/highlighters/root/root-highlighter.zsh
@@ -0,0 +1,44 @@
+# -------------------------------------------------------------------------------------------------
+# Copyright (c) 2010-2011 zsh-syntax-highlighting contributors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+# may be used to endorse or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -------------------------------------------------------------------------------------------------
+# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
+# vim: ft=zsh sw=2 ts=2 et
+# -------------------------------------------------------------------------------------------------
+
+
+# Define default styles.
+: ${ZSH_HIGHLIGHT_STYLES[root]:=standout}
+
+# Whether the root highlighter should be called or not.
+_zsh_highlight_highlighter_root_predicate()
+{
+ _zsh_highlight_buffer_modified
+}
+
+# root highlighting function.
+_zsh_highlight_highlighter_root_paint()
+{
+ if (( EUID == 0 )) { _zsh_highlight_add_highlight 0 $#BUFFER root }
+}
diff --git a/plugins/zsh-syntax-highlighting/images/after1-smaller.png b/plugins/zsh-syntax-highlighting/images/after1-smaller.png
new file mode 100644
index 000000000..768294c3b
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/after1-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/after1.png b/plugins/zsh-syntax-highlighting/images/after1.png
new file mode 100644
index 000000000..ea378d3ef
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/after1.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/after2-smaller.png b/plugins/zsh-syntax-highlighting/images/after2-smaller.png
new file mode 100644
index 000000000..8b5b1f8f2
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/after2-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/after2.png b/plugins/zsh-syntax-highlighting/images/after2.png
new file mode 100644
index 000000000..ddcbfad70
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/after2.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/after3-smaller.png b/plugins/zsh-syntax-highlighting/images/after3-smaller.png
new file mode 100644
index 000000000..d6aaa7bdf
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/after3-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/after3.png b/plugins/zsh-syntax-highlighting/images/after3.png
new file mode 100644
index 000000000..7a0af8874
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/after3.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/after4-smaller.png b/plugins/zsh-syntax-highlighting/images/after4-smaller.png
new file mode 100644
index 000000000..fc946cf03
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/after4-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/before1-smaller.png b/plugins/zsh-syntax-highlighting/images/before1-smaller.png
new file mode 100644
index 000000000..b29551eaf
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/before1-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/before1.png b/plugins/zsh-syntax-highlighting/images/before1.png
new file mode 100644
index 000000000..80c6f54ff
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/before1.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/before2-smaller.png b/plugins/zsh-syntax-highlighting/images/before2-smaller.png
new file mode 100644
index 000000000..991a716d5
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/before2-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/before2.png b/plugins/zsh-syntax-highlighting/images/before2.png
new file mode 100644
index 000000000..48cfc327a
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/before2.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/before3-smaller.png b/plugins/zsh-syntax-highlighting/images/before3-smaller.png
new file mode 100644
index 000000000..e525c6d4b
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/before3-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/before3.png b/plugins/zsh-syntax-highlighting/images/before3.png
new file mode 100644
index 000000000..40c360151
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/before3.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/before4-smaller.png b/plugins/zsh-syntax-highlighting/images/before4-smaller.png
new file mode 100644
index 000000000..2dbd0588b
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/before4-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/preview-smaller.png b/plugins/zsh-syntax-highlighting/images/preview-smaller.png
new file mode 100644
index 000000000..6fb84d18e
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/preview-smaller.png differ
diff --git a/plugins/zsh-syntax-highlighting/images/preview.png b/plugins/zsh-syntax-highlighting/images/preview.png
new file mode 100644
index 000000000..545cc511a
Binary files /dev/null and b/plugins/zsh-syntax-highlighting/images/preview.png differ
diff --git a/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh b/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh
new file mode 100644
index 000000000..51096a857
--- /dev/null
+++ b/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh
@@ -0,0 +1,587 @@
+# -------------------------------------------------------------------------------------------------
+# Copyright (c) 2010-2020 zsh-syntax-highlighting contributors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this list of conditions
+# and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice, this list of
+# conditions and the following disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of the zsh-syntax-highlighting contributors nor the names of its contributors
+# may be used to endorse or promote products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -------------------------------------------------------------------------------------------------
+# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
+# vim: ft=zsh sw=2 ts=2 et
+# -------------------------------------------------------------------------------------------------
+
+# First of all, ensure predictable parsing.
+typeset zsh_highlight__aliases="$(builtin alias -Lm '[^+]*')"
+# In zsh <= 5.2, aliases that begin with a plus sign ('alias -- +foo=42')
+# are emitted by `alias -L` without a '--' guard, so they don't round trip.
+#
+# Hence, we exclude them from unaliasing:
+builtin unalias -m '[^+]*'
+
+# Set $0 to the expected value, regardless of functionargzero.
+0=${(%):-%N}
+if true; then
+ # $0 is reliable
+ typeset -g ZSH_HIGHLIGHT_VERSION=0.8.1-dev
+ typeset -g ZSH_HIGHLIGHT_REVISION=HEAD
+ if [[ $ZSH_HIGHLIGHT_REVISION == \$Format:* ]]; then
+ # When running from a source tree without 'make install', $ZSH_HIGHLIGHT_REVISION
+ # would be set to '$Format:%H$' literally. That's an invalid value, and obtaining
+ # the valid value (via `git rev-parse HEAD`, as Makefile does) might be costly, so:
+ ZSH_HIGHLIGHT_REVISION=HEAD
+ fi
+fi
+
+# This function takes a single argument F and returns True iff F is an autoload stub.
+_zsh_highlight__function_is_autoload_stub_p() {
+ if zmodload -e zsh/parameter; then
+ #(( ${+functions[$1]} )) &&
+ [[ "$functions[$1]" == *"builtin autoload -X"* ]]
+ else
+ #[[ $(type -wa -- "$1") == *'function'* ]] &&
+ [[ "${${(@f)"$(which -- "$1")"}[2]}" == $'\t'$histchars[3]' undefined' ]]
+ fi
+ # Do nothing here: return the exit code of the if.
+}
+
+# Return True iff the argument denotes a function name.
+_zsh_highlight__is_function_p() {
+ if zmodload -e zsh/parameter; then
+ (( ${+functions[$1]} ))
+ else
+ [[ $(type -wa -- "$1") == *'function'* ]]
+ fi
+}
+
+# This function takes a single argument F and returns True iff F denotes the
+# name of a callable function. A function is callable if it is fully defined
+# or if it is marked for autoloading and autoloading it at the first call to it
+# will succeed. In particular, if F has been marked for autoloading
+# but is not available in $fpath, then calling this function on F will return False.
+#
+# See users/21671 https://www.zsh.org/cgi-bin/mla/redirect?USERNUMBER=21671
+_zsh_highlight__function_callable_p() {
+ if _zsh_highlight__is_function_p "$1" &&
+ ! _zsh_highlight__function_is_autoload_stub_p "$1"
+ then
+ # Already fully loaded.
+ return 0 # true
+ else
+ # "$1" is either an autoload stub, or not a function at all.
+ #
+ # Use a subshell to avoid affecting the calling shell.
+ #
+ # We expect 'autoload +X' to return non-zero if it fails to fully load
+ # the function.
+ ( autoload -U +X -- "$1" 2>/dev/null )
+ return $?
+ fi
+}
+
+# -------------------------------------------------------------------------------------------------
+# Core highlighting update system
+# -------------------------------------------------------------------------------------------------
+
+# Use workaround for bug in ZSH?
+# zsh-users/zsh@48cadf4 https://www.zsh.org/mla/workers/2017/msg00034.html
+autoload -Uz is-at-least
+if is-at-least 5.4; then
+ typeset -g zsh_highlight__pat_static_bug=false
+else
+ typeset -g zsh_highlight__pat_static_bug=true
+fi
+
+# Array declaring active highlighters names.
+typeset -ga ZSH_HIGHLIGHT_HIGHLIGHTERS
+
+# Update ZLE buffer syntax highlighting.
+#
+# Invokes each highlighter that needs updating.
+# This function is supposed to be called whenever the ZLE state changes.
+_zsh_highlight()
+{
+ # Store the previous command return code to restore it whatever happens.
+ local ret=$?
+ # Make it read-only. Can't combine this with the previous line when POSIX_BUILTINS may be set.
+ typeset -r ret
+
+ # $region_highlight should be predefined, either by zle or by the test suite's mock (non-special) array.
+ (( ${+region_highlight[@]} )) || {
+ echo >&2 'zsh-syntax-highlighting: error: $region_highlight is not defined'
+ echo >&2 'zsh-syntax-highlighting: (Check whether zsh-syntax-highlighting was installed according to the instructions.)'
+ return $ret
+ }
+
+ # Probe the memo= feature, once.
+ (( ${+zsh_highlight__memo_feature} )) || {
+ region_highlight+=( " 0 0 fg=red, memo=zsh-syntax-highlighting" )
+ case ${region_highlight[-1]} in
+ ("0 0 fg=red")
+ # zsh 5.8 or earlier
+ integer -gr zsh_highlight__memo_feature=0
+ ;;
+ ("0 0 fg=red memo=zsh-syntax-highlighting")
+ # zsh 5.9 or later
+ integer -gr zsh_highlight__memo_feature=1
+ ;;
+ (" 0 0 fg=red, memo=zsh-syntax-highlighting") ;&
+ (*)
+ # We can get here in two ways:
+ #
+ # 1. When not running as a widget. In that case, $region_highlight is
+ # not a special variable (= one with custom getter/setter functions
+ # written in C) but an ordinary one, so the third case pattern matches
+ # and we fall through to this block. (The test suite uses this codepath.)
+ #
+ # 2. When running under a future version of zsh that will have changed
+ # the serialization of $region_highlight elements from their underlying
+ # C structs, so that none of the previous case patterns will match.
+ #
+ # In either case, fall back to a version check.
+ if is-at-least 5.9; then
+ integer -gr zsh_highlight__memo_feature=1
+ else
+ integer -gr zsh_highlight__memo_feature=0
+ fi
+ ;;
+ esac
+ region_highlight[-1]=()
+ }
+
+ # Reset region_highlight to build it from scratch
+ if (( zsh_highlight__memo_feature )); then
+ region_highlight=( "${(@)region_highlight:#*memo=zsh-syntax-highlighting*}" )
+ else
+ # Legacy codepath. Not very interoperable with other plugins (issue #418).
+ region_highlight=()
+ fi
+
+ # Remove all highlighting in isearch, so that only the underlining done by zsh itself remains.
+ # For details see FAQ entry 'Why does syntax highlighting not work while searching history?'.
+ # This disables highlighting during isearch (for reasons explained in README.md) unless zsh is new enough
+ # and doesn't have the pattern matching bug
+ if [[ $WIDGET == zle-isearch-update ]] && { $zsh_highlight__pat_static_bug || ! (( $+ISEARCHMATCH_ACTIVE )) }; then
+ return $ret
+ fi
+
+ # Before we 'emulate -L', save the user's options
+ local -A zsyh_user_options
+ if zmodload -e zsh/parameter; then
+ zsyh_user_options=("${(kv)options[@]}")
+ else
+ local canonical_options onoff option raw_options
+ raw_options=(${(f)"$(emulate -R zsh; set -o)"})
+ canonical_options=(${${${(M)raw_options:#*off}%% *}#no} ${${(M)raw_options:#*on}%% *})
+ for option in "${canonical_options[@]}"; do
+ [[ -o $option ]]
+ case $? in
+ (0) zsyh_user_options+=($option on);;
+ (1) zsyh_user_options+=($option off);;
+ (*) # Can't happen, surely?
+ echo "zsh-syntax-highlighting: warning: '[[ -o $option ]]' returned $?"
+ ;;
+ esac
+ done
+ fi
+ typeset -r zsyh_user_options
+
+ emulate -L zsh
+ setopt localoptions warncreateglobal nobashrematch
+ local REPLY # don't leak $REPLY into global scope
+
+ # Do not highlight if there are more than 300 chars in the buffer. It's most
+ # likely a pasted command or a huge list of files in that case..
+ [[ -n ${ZSH_HIGHLIGHT_MAXLENGTH:-} ]] && [[ $#BUFFER -gt $ZSH_HIGHLIGHT_MAXLENGTH ]] && return $ret
+
+ # Do not highlight if there are pending inputs (copy/paste).
+ (( KEYS_QUEUED_COUNT > 0 )) && return $ret
+ (( PENDING > 0 )) && return $ret
+
+ {
+ local cache_place
+ local -a region_highlight_copy
+
+ # Select which highlighters in ZSH_HIGHLIGHT_HIGHLIGHTERS need to be invoked.
+ local highlighter; for highlighter in $ZSH_HIGHLIGHT_HIGHLIGHTERS; do
+
+ # eval cache place for current highlighter and prepare it
+ cache_place="_zsh_highlight__highlighter_${highlighter}_cache"
+ typeset -ga ${cache_place}
+
+ # If highlighter needs to be invoked
+ if ! type "_zsh_highlight_highlighter_${highlighter}_predicate" >&/dev/null; then
+ echo "zsh-syntax-highlighting: warning: disabling the ${(qq)highlighter} highlighter as it has not been loaded" >&2
+ # TODO: use ${(b)} rather than ${(q)} if supported
+ ZSH_HIGHLIGHT_HIGHLIGHTERS=( ${ZSH_HIGHLIGHT_HIGHLIGHTERS:#${highlighter}} )
+ elif "_zsh_highlight_highlighter_${highlighter}_predicate"; then
+
+ # save a copy, and cleanup region_highlight
+ region_highlight_copy=("${region_highlight[@]}")
+ region_highlight=()
+
+ # Execute highlighter and save result
+ {
+ "_zsh_highlight_highlighter_${highlighter}_paint"
+ } always {
+ : ${(AP)cache_place::="${region_highlight[@]}"}
+ }
+
+ # Restore saved region_highlight
+ region_highlight=("${region_highlight_copy[@]}")
+
+ fi
+
+ # Use value form cache if any cached
+ region_highlight+=("${(@P)cache_place}")
+
+ done
+
+ # Re-apply zle_highlight settings
+
+ # region
+ () {
+ (( REGION_ACTIVE )) || return
+ integer min max
+ if (( MARK > CURSOR )) ; then
+ min=$CURSOR max=$MARK
+ else
+ min=$MARK max=$CURSOR
+ fi
+ if (( REGION_ACTIVE == 1 )); then
+ [[ $KEYMAP = vicmd ]] && (( max++ ))
+ elif (( REGION_ACTIVE == 2 )); then
+ local needle=$'\n'
+ # CURSOR and MARK are 0 indexed between letters like region_highlight
+ # Do not include the newline in the highlight
+ (( min = ${BUFFER[(Ib:min:)$needle]} ))
+ (( max = ${BUFFER[(ib:max:)$needle]} - 1 ))
+ fi
+ _zsh_highlight_apply_zle_highlight region standout "$min" "$max"
+ }
+
+ # yank / paste (zsh-5.1.1 and newer)
+ (( $+YANK_ACTIVE )) && (( YANK_ACTIVE )) && _zsh_highlight_apply_zle_highlight paste standout "$YANK_START" "$YANK_END"
+
+ # isearch
+ (( $+ISEARCHMATCH_ACTIVE )) && (( ISEARCHMATCH_ACTIVE )) && _zsh_highlight_apply_zle_highlight isearch underline "$ISEARCHMATCH_START" "$ISEARCHMATCH_END"
+
+ # suffix
+ (( $+SUFFIX_ACTIVE )) && (( SUFFIX_ACTIVE )) && _zsh_highlight_apply_zle_highlight suffix bold "$SUFFIX_START" "$SUFFIX_END"
+
+
+ return $ret
+
+
+ } always {
+ typeset -g _ZSH_HIGHLIGHT_PRIOR_BUFFER="$BUFFER"
+ typeset -gi _ZSH_HIGHLIGHT_PRIOR_CURSOR=$CURSOR
+ }
+}
+
+# Apply highlighting based on entries in the zle_highlight array.
+# This function takes four arguments:
+# 1. The exact entry (no patterns) in the zle_highlight array:
+# region, paste, isearch, or suffix
+# 2. The default highlighting that should be applied if the entry is unset
+# 3. and 4. Two integer values describing the beginning and end of the
+# range. The order does not matter.
+_zsh_highlight_apply_zle_highlight() {
+ local entry="$1" default="$2"
+ integer first="$3" second="$4"
+
+ # read the relevant entry from zle_highlight
+ #
+ # ### In zsh≥5.0.8 we'd use ${(b)entry}, but we support older zsh's, so we don't
+ # ### add (b). The only effect is on the failure mode for callers that violate
+ # ### the precondition.
+ local region="${zle_highlight[(r)${entry}:*]-}"
+
+ if [[ -z "$region" ]]; then
+ # entry not specified at all, use default value
+ region=$default
+ else
+ # strip prefix
+ region="${region#${entry}:}"
+
+ # no highlighting when set to the empty string or to 'none'
+ if [[ -z "$region" ]] || [[ "$region" == none ]]; then
+ return
+ fi
+ fi
+
+ integer start end
+ if (( first < second )); then
+ start=$first end=$second
+ else
+ start=$second end=$first
+ fi
+ region_highlight+=("$start $end $region, memo=zsh-syntax-highlighting")
+}
+
+
+# -------------------------------------------------------------------------------------------------
+# API/utility functions for highlighters
+# -------------------------------------------------------------------------------------------------
+
+# Array used by highlighters to declare user overridable styles.
+typeset -gA ZSH_HIGHLIGHT_STYLES
+
+# Whether the command line buffer has been modified or not.
+#
+# Returns 0 if the buffer has changed since _zsh_highlight was last called.
+_zsh_highlight_buffer_modified()
+{
+ [[ "${_ZSH_HIGHLIGHT_PRIOR_BUFFER:-}" != "$BUFFER" ]]
+}
+
+# Whether the cursor has moved or not.
+#
+# Returns 0 if the cursor has moved since _zsh_highlight was last called.
+_zsh_highlight_cursor_moved()
+{
+ [[ -n $CURSOR ]] && [[ -n ${_ZSH_HIGHLIGHT_PRIOR_CURSOR-} ]] && (($_ZSH_HIGHLIGHT_PRIOR_CURSOR != $CURSOR))
+}
+
+# Add a highlight defined by ZSH_HIGHLIGHT_STYLES.
+#
+# Should be used by all highlighters aside from 'pattern' (cf. ZSH_HIGHLIGHT_PATTERN).
+# Overwritten in tests/test-highlighting.zsh when testing.
+_zsh_highlight_add_highlight()
+{
+ local -i start end
+ local highlight
+ start=$1
+ end=$2
+ shift 2
+ for highlight; do
+ if (( $+ZSH_HIGHLIGHT_STYLES[$highlight] )); then
+ region_highlight+=("$start $end $ZSH_HIGHLIGHT_STYLES[$highlight], memo=zsh-syntax-highlighting")
+ break
+ fi
+ done
+}
+
+# -------------------------------------------------------------------------------------------------
+# Setup functions
+# -------------------------------------------------------------------------------------------------
+
+# Helper for _zsh_highlight_bind_widgets
+# $1 is name of widget to call
+_zsh_highlight_call_widget()
+{
+ builtin zle "$@" &&
+ _zsh_highlight
+}
+
+# Decide whether to use the zle-line-pre-redraw codepath (colloquially known as
+# "feature/redrawhook", after the topic branch's name) or the legacy "bind all
+# widgets" codepath.
+#
+# We use the new codepath under two conditions:
+#
+# 1. If it's available, which we check by testing for add-zle-hook-widget's availability.
+#
+# 2. If zsh has the memo= feature, which is required for interoperability reasons.
+# See issues #579 and #735, and the issues referenced from them.
+#
+# We check this with a plain version number check, since a functional check,
+# as done by _zsh_highlight, can only be done from inside a widget
+# function — a catch-22.
+if is-at-least 5.9 && _zsh_highlight__function_callable_p add-zle-hook-widget
+then
+ autoload -U add-zle-hook-widget
+ _zsh_highlight__zle-line-finish() {
+ # Reset $WIDGET since the 'main' highlighter depends on it.
+ #
+ # Since $WIDGET is declared by zle as read-only in this function's scope,
+ # a nested function is required in order to shadow its built-in value;
+ # see "User-defined widgets" in zshall.
+ () {
+ local -h -r WIDGET=zle-line-finish
+ _zsh_highlight
+ }
+ }
+ _zsh_highlight__zle-line-pre-redraw() {
+ # Set $? to 0 for _zsh_highlight. Without this, subsequent
+ # zle-line-pre-redraw hooks won't run, since add-zle-hook-widget happens to
+ # call us with $? == 1 in the common case.
+ true && _zsh_highlight "$@"
+ }
+ _zsh_highlight_bind_widgets(){}
+ if [[ -o zle ]]; then
+ add-zle-hook-widget zle-line-pre-redraw _zsh_highlight__zle-line-pre-redraw
+ add-zle-hook-widget zle-line-finish _zsh_highlight__zle-line-finish
+ fi
+else
+ # Rebind all ZLE widgets to make them invoke _zsh_highlights.
+ _zsh_highlight_bind_widgets()
+ {
+ setopt localoptions noksharrays
+ typeset -F SECONDS
+ local prefix=orig-s$SECONDS-r$RANDOM # unique each time, in case we're sourced more than once
+
+ # Load ZSH module zsh/zleparameter, needed to override user defined widgets.
+ zmodload zsh/zleparameter 2>/dev/null || {
+ print -r -- >&2 'zsh-syntax-highlighting: failed loading zsh/zleparameter.'
+ return 1
+ }
+
+ # Override ZLE widgets to make them invoke _zsh_highlight.
+ local -U widgets_to_bind
+ widgets_to_bind=(${${(k)widgets}:#(.*|run-help|which-command|beep|set-local-history|yank|yank-pop)})
+
+ # Always wrap special zle-line-finish widget. This is needed to decide if the
+ # current line ends and special highlighting logic needs to be applied.
+ # E.g. remove cursor imprint, don't highlight partial paths, ...
+ widgets_to_bind+=(zle-line-finish)
+
+ # Always wrap special zle-isearch-update widget to be notified of updates in isearch.
+ # This is needed because we need to disable highlighting in that case.
+ widgets_to_bind+=(zle-isearch-update)
+
+ local cur_widget
+ for cur_widget in $widgets_to_bind; do
+ case ${widgets[$cur_widget]:-""} in
+
+ # Already rebound event: do nothing.
+ user:_zsh_highlight_widget_*);;
+
+ # The "eval"'s are required to make $cur_widget a closure: the value of the parameter at function
+ # definition time is used.
+ #
+ # We can't use ${0/_zsh_highlight_widget_} because these widgets are always invoked with
+ # NO_function_argzero, regardless of the option's setting here.
+
+ # User defined widget: override and rebind old one with prefix "orig-".
+ user:*) zle -N $prefix-$cur_widget ${widgets[$cur_widget]#*:}
+ eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget ${(q)prefix}-${(q)cur_widget} -- \"\$@\" }"
+ zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;;
+
+ # Completion widget: override and rebind old one with prefix "orig-".
+ completion:*) zle -C $prefix-$cur_widget ${${(s.:.)widgets[$cur_widget]}[2,3]}
+ eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget ${(q)prefix}-${(q)cur_widget} -- \"\$@\" }"
+ zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;;
+
+ # Builtin widget: override and make it call the builtin ".widget".
+ builtin) eval "_zsh_highlight_widget_${(q)prefix}-${(q)cur_widget}() { _zsh_highlight_call_widget .${(q)cur_widget} -- \"\$@\" }"
+ zle -N $cur_widget _zsh_highlight_widget_$prefix-$cur_widget;;
+
+ # Incomplete or nonexistent widget: Bind to z-sy-h directly.
+ *)
+ if [[ $cur_widget == zle-* ]] && (( ! ${+widgets[$cur_widget]} )); then
+ _zsh_highlight_widget_${cur_widget}() { :; _zsh_highlight }
+ zle -N $cur_widget _zsh_highlight_widget_$cur_widget
+ else
+ # Default: unhandled case.
+ print -r -- >&2 "zsh-syntax-highlighting: unhandled ZLE widget ${(qq)cur_widget}"
+ print -r -- >&2 "zsh-syntax-highlighting: (This is sometimes caused by doing \`bindkey ${(q-)cur_widget}\` without creating the ${(qq)cur_widget} widget with \`zle -N\` or \`zle -C\`.)"
+ fi
+ esac
+ done
+ }
+fi
+
+# Load highlighters from directory.
+#
+# Arguments:
+# 1) Path to the highlighters directory.
+_zsh_highlight_load_highlighters()
+{
+ setopt localoptions noksharrays bareglobqual
+
+ # Check the directory exists.
+ [[ -d "$1" ]] || {
+ print -r -- >&2 "zsh-syntax-highlighting: highlighters directory ${(qq)1} not found."
+ return 1
+ }
+
+ # Load highlighters from highlighters directory and check they define required functions.
+ local highlighter highlighter_dir
+ for highlighter_dir ($1/*/(/)); do
+ highlighter="${highlighter_dir:t}"
+ [[ -f "$highlighter_dir${highlighter}-highlighter.zsh" ]] &&
+ . "$highlighter_dir${highlighter}-highlighter.zsh"
+ if type "_zsh_highlight_highlighter_${highlighter}_paint" &> /dev/null &&
+ type "_zsh_highlight_highlighter_${highlighter}_predicate" &> /dev/null;
+ then
+ # New (0.5.0) function names
+ elif type "_zsh_highlight_${highlighter}_highlighter" &> /dev/null &&
+ type "_zsh_highlight_${highlighter}_highlighter_predicate" &> /dev/null;
+ then
+ # Old (0.4.x) function names
+ if false; then
+ # TODO: only show this warning for plugin authors/maintainers, not for end users
+ print -r -- >&2 "zsh-syntax-highlighting: warning: ${(qq)highlighter} highlighter uses deprecated entry point names; please ask its maintainer to update it: https://github.com/zsh-users/zsh-syntax-highlighting/issues/329"
+ fi
+ # Make it work.
+ eval "_zsh_highlight_highlighter_${(q)highlighter}_paint() { _zsh_highlight_${(q)highlighter}_highlighter \"\$@\" }"
+ eval "_zsh_highlight_highlighter_${(q)highlighter}_predicate() { _zsh_highlight_${(q)highlighter}_highlighter_predicate \"\$@\" }"
+ else
+ print -r -- >&2 "zsh-syntax-highlighting: ${(qq)highlighter} highlighter should define both required functions '_zsh_highlight_highlighter_${highlighter}_paint' and '_zsh_highlight_highlighter_${highlighter}_predicate' in ${(qq):-"$highlighter_dir${highlighter}-highlighter.zsh"}."
+ fi
+ done
+}
+
+
+# -------------------------------------------------------------------------------------------------
+# Setup
+# -------------------------------------------------------------------------------------------------
+
+# Try binding widgets.
+_zsh_highlight_bind_widgets || {
+ print -r -- >&2 'zsh-syntax-highlighting: failed binding ZLE widgets, exiting.'
+ return 1
+}
+
+# Resolve highlighters directory location.
+_zsh_highlight_load_highlighters "${ZSH_HIGHLIGHT_HIGHLIGHTERS_DIR:-${${0:A}:h}/highlighters}" || {
+ print -r -- >&2 'zsh-syntax-highlighting: failed loading highlighters, exiting.'
+ return 1
+}
+
+# Reset scratch variables when commandline is done.
+_zsh_highlight_preexec_hook()
+{
+ typeset -g _ZSH_HIGHLIGHT_PRIOR_BUFFER=
+ typeset -gi _ZSH_HIGHLIGHT_PRIOR_CURSOR=
+}
+autoload -Uz add-zsh-hook
+add-zsh-hook preexec _zsh_highlight_preexec_hook 2>/dev/null || {
+ print -r -- >&2 'zsh-syntax-highlighting: failed loading add-zsh-hook.'
+ }
+
+# Load zsh/parameter module if available
+zmodload zsh/parameter 2>/dev/null || true
+
+# Initialize the array of active highlighters if needed.
+[[ $#ZSH_HIGHLIGHT_HIGHLIGHTERS -eq 0 ]] && ZSH_HIGHLIGHT_HIGHLIGHTERS=(main)
+
+if (( $+X_ZSH_HIGHLIGHT_DIRS_BLACKLIST )); then
+ print >&2 'zsh-syntax-highlighting: X_ZSH_HIGHLIGHT_DIRS_BLACKLIST is deprecated. Please use ZSH_HIGHLIGHT_DIRS_BLACKLIST.'
+ ZSH_HIGHLIGHT_DIRS_BLACKLIST=($X_ZSH_HIGHLIGHT_DIRS_BLACKLIST)
+ unset X_ZSH_HIGHLIGHT_DIRS_BLACKLIST
+fi
+
+# Restore the aliases we unned
+eval "$zsh_highlight__aliases"
+builtin unset zsh_highlight__aliases
+
+# Set $?.
+true