From 974231f48372fd00af8f7e98de86f0690168d340 Mon Sep 17 00:00:00 2001 From: steveokard Date: Tue, 1 Aug 2017 15:52:56 -0400 Subject: [PATCH] Added auto complete to ZSH --- .oh-my-zsh/custom/autocomplete_config.zsh | 65 ++ .../plugins/zsh-autosuggestions/.editorconfig | 18 + .../custom/plugins/zsh-autosuggestions/.rspec | 3 + .../plugins/zsh-autosuggestions/.rubocop.yml | 30 + .../plugins/zsh-autosuggestions/.ruby-version | 1 + .../plugins/zsh-autosuggestions/CHANGELOG.md | 49 ++ .../plugins/zsh-autosuggestions/DESCRIPTION | 1 + .../plugins/zsh-autosuggestions/Gemfile | 5 + .../plugins/zsh-autosuggestions/Gemfile.lock | 41 ++ .../plugins/zsh-autosuggestions/LICENSE | 23 + .../plugins/zsh-autosuggestions/Makefile | 36 + .../plugins/zsh-autosuggestions/README.md | 208 ++++++ .../custom/plugins/zsh-autosuggestions/URL | 1 + .../plugins/zsh-autosuggestions/VERSION | 1 + .../plugins/zsh-autosuggestions/circle.yml | 12 + .../bracketed_paste_magic_spec.rb | 36 + .../spec/integrations/client_zpty_spec.rb | 10 + .../spec/integrations/wrapped_widget_spec.rb | 39 + .../spec/integrations/zle_input_stack_spec.rb | 24 + .../spec/multi_line_spec.rb | 13 + .../spec/options/async_zpty_name_spec.rb | 19 + .../spec/options/buffer_max_size_spec.rb | 30 + .../spec/options/highlight_style_spec.rb | 7 + .../options/original_widget_prefix_spec.rb | 7 + .../spec/options/strategy_spec.rb | 20 + .../spec/options/use_async_spec.rb | 7 + .../spec/options/widget_lists_spec.rb | 97 +++ .../zsh-autosuggestions/spec/spec_helper.rb | 50 ++ .../spec/special_characters_spec.rb | 60 ++ .../spec/strategies/default_spec.rb | 8 + .../spec/strategies/match_prev_cmd_spec.rb | 17 + .../spec/terminal_session.rb | 91 +++ .../spec/widgets/disable_spec.rb | 19 + .../spec/widgets/enable_spec.rb | 42 ++ .../spec/widgets/fetch_spec.rb | 24 + .../spec/widgets/toggle_spec.rb | 26 + .../plugins/zsh-autosuggestions/src/async.zsh | 109 +++ .../plugins/zsh-autosuggestions/src/bind.zsh | 118 +++ .../zsh-autosuggestions/src/features.zsh | 19 + .../zsh-autosuggestions/src/highlight.zsh | 26 + .../plugins/zsh-autosuggestions/src/setup.zsh | 10 + .../plugins/zsh-autosuggestions/src/start.zsh | 24 + .../src/strategies/default.zsh | 25 + .../src/strategies/match_prev_cmd.zsh | 52 ++ .../plugins/zsh-autosuggestions/src/util.zsh | 11 + .../zsh-autosuggestions/src/widgets.zsh | 206 ++++++ .../zsh-autosuggestions.plugin.zsh | 1 + .../zsh-autosuggestions.zsh | 691 ++++++++++++++++++ .zshrc | 4 +- 49 files changed, 2434 insertions(+), 2 deletions(-) create mode 100644 .oh-my-zsh/custom/autocomplete_config.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/.editorconfig create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/.rspec create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/.rubocop.yml create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/.ruby-version create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/CHANGELOG.md create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/DESCRIPTION create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/Gemfile create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/Gemfile.lock create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/LICENSE create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/Makefile create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/README.md create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/URL create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/VERSION create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/circle.yml create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/bracketed_paste_magic_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/client_zpty_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/wrapped_widget_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/zle_input_stack_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/multi_line_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/async_zpty_name_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/buffer_max_size_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/highlight_style_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/original_widget_prefix_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/strategy_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/use_async_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/widget_lists_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/spec_helper.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/special_characters_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/strategies/default_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/strategies/match_prev_cmd_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/terminal_session.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/disable_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/enable_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/fetch_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/toggle_spec.rb create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/async.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/bind.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/features.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/highlight.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/setup.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/start.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/strategies/default.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/strategies/match_prev_cmd.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/util.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/src/widgets.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh create mode 100644 .oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh diff --git a/.oh-my-zsh/custom/autocomplete_config.zsh b/.oh-my-zsh/custom/autocomplete_config.zsh new file mode 100644 index 0000000..c7fc55a --- /dev/null +++ b/.oh-my-zsh/custom/autocomplete_config.zsh @@ -0,0 +1,65 @@ + +#--------------------------------------------------------------------# +# Global Configuration Variables # +#--------------------------------------------------------------------# + +# Color to use when highlighting suggestion +# Uses format of `region_highlight` +# More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets +ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' + +# Prefix to use when saving original versions of bound widgets +ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- + +ZSH_AUTOSUGGEST_STRATEGY=default + +# Widgets that clear the suggestion +ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( + history-search-forward + history-search-backward + history-beginning-search-forward + history-beginning-search-backward + history-substring-search-up + history-substring-search-down + up-line-or-history + down-line-or-history + accept-line +) + +# Widgets that accept the entire suggestion +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=( +) + +# Widgets that accept the suggestion as far as the cursor moves +ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=( + forward-word + vi-forward-word + vi-forward-word-end + vi-forward-blank-word + vi-forward-blank-word-end +) + +# Widgets that should be ignored (globbing supported but must be escaped) +ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( + orig-\* + beep + run-help + set-local-history + which-command + yank +) + +# Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. +ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= + +# Pty name for calculating autosuggestions asynchronously +ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.editorconfig b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.editorconfig new file mode 100644 index 0000000..ddabb17 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.editorconfig @@ -0,0 +1,18 @@ +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab +indent_size = 4 + +[*.md] +indent_style = space + +[*.rb] +indent_style = space +indent_size = 2 + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.rspec b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.rspec new file mode 100644 index 0000000..43ae203 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.rspec @@ -0,0 +1,3 @@ +--color +--require spec_helper +--format documentation diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.rubocop.yml b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.rubocop.yml new file mode 100644 index 0000000..9e0792f --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.rubocop.yml @@ -0,0 +1,30 @@ +# Rails: +# Enabled: true + +AllCops: + TargetRubyVersion: 2.3 + Include: + - '**/Rakefile' + - '**/config.ru' + - '**/Gemfile' + +Metrics/LineLength: + Max: 120 + +Style/Documentation: + Enabled: false + +Style/DotPosition: + EnforcedStyle: trailing + +Style/FrozenStringLiteralComment: + Enabled: false + +Style/Lambda: + Enabled: false + +Style/MultilineMethodCallIndentation: + EnforcedStyle: indented + +Style/TrailingUnderscoreVariable: + Enabled: false diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.ruby-version b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.ruby-version new file mode 100644 index 0000000..2bf1c1c --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/.ruby-version @@ -0,0 +1 @@ +2.3.1 diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/CHANGELOG.md b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/CHANGELOG.md new file mode 100644 index 0000000..ac0f8e6 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/CHANGELOG.md @@ -0,0 +1,49 @@ +# Changelog + +## v0.4.0 +- High-level integration tests using RSpec and tmux +- Add continuous integration with Circle CI +- Experimental support for asynchronous suggestions (#) +- Fix problems with multi-line suggestions (#) +- Optimize case where manually typing in suggestion +- Avoid wrapping any zle-* widgets (#) +- Remove support for deprecated options from v0.0.x +- Handle history entries that begin with dashes (#) +- Gracefully handle being sourced multiple times (#126) +- Add enable/disable/toggle widgets to disable/enable suggestions (#219) + + +## v0.3.3 +- Switch from $history array to fc builtin for better performance with large HISTFILEs (#164) +- Fix tilde handling when extended_glob is set (#168) +- Add config option for maximum buffer length to fetch suggestions for (#178) +- Add config option for list of widgets to ignore (#184) +- Don't fetch a new suggestion unless a modification widget actually modifies the buffer (#183) + +## v0.3.2 +- Test runner now supports running specific tests and choosing zsh binary +- Return code from original widget is now correctly passed through (#135) +- Add `vi-add-eol` to list of accept widgets (#143) +- Escapes widget names within evals to fix problems with irregular widget names (#152) +- Plugin now clears suggestion while within a completion menu (#149) +- .plugin file no longer relies on symbolic link support, fixing issues on Windows (#156) + +## v0.3.1 + +- Fixes issue with `vi-next-char` not accepting suggestion (#137). +- Fixes global variable warning when WARN_CREATE_GLOBAL option enabled (#133). +- Split out a separate test file for each widget. + +## v0.3.0 + +- Adds `autosuggest-execute` widget (PR #124). +- Adds concept of suggestion "strategies" for different ways of fetching suggestions. +- Adds "match_prev_cmd" strategy (PR #131). +- Uses git submodules for testing dependencies. +- Lots of test cleanup. +- Various bug fixes for zsh 5.0.x and `sh_word_split` option. + + +## v0.2.17 + +Start of changelog. diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/DESCRIPTION b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/DESCRIPTION new file mode 100644 index 0000000..b69200f --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/DESCRIPTION @@ -0,0 +1 @@ +Fish-like fast/unobtrusive autosuggestions for zsh. diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Gemfile b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Gemfile new file mode 100644 index 0000000..8b5deec --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'rspec' +gem 'rspec-wait' +gem 'pry-byebug' diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Gemfile.lock b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Gemfile.lock new file mode 100644 index 0000000..63ee778 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Gemfile.lock @@ -0,0 +1,41 @@ +GEM + remote: https://rubygems.org/ + specs: + byebug (9.0.5) + coderay (1.1.1) + diff-lcs (1.3) + method_source (0.8.2) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-byebug (3.4.0) + byebug (~> 9.0) + pry (~> 0.10) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + rspec-wait (0.0.9) + rspec (>= 3, < 4) + slop (3.6.0) + +PLATFORMS + ruby + +DEPENDENCIES + pry-byebug + rspec + rspec-wait + +BUNDLED WITH + 1.13.6 diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/LICENSE b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/LICENSE new file mode 100644 index 0000000..ad6594e --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2013 Thiago de Arruda +Copyright (c) 2016-2017 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/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Makefile b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Makefile new file mode 100644 index 0000000..d5d162c --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/Makefile @@ -0,0 +1,36 @@ +SRC_DIR := ./src + +SRC_FILES := \ + $(SRC_DIR)/setup.zsh \ + $(SRC_DIR)/config.zsh \ + $(SRC_DIR)/util.zsh \ + $(SRC_DIR)/features.zsh \ + $(SRC_DIR)/bind.zsh \ + $(SRC_DIR)/highlight.zsh \ + $(SRC_DIR)/widgets.zsh \ + $(SRC_DIR)/strategies/*.zsh \ + $(SRC_DIR)/async.zsh \ + $(SRC_DIR)/start.zsh + +HEADER_FILES := \ + DESCRIPTION \ + URL \ + VERSION \ + LICENSE + +PLUGIN_TARGET := zsh-autosuggestions.zsh + +all: $(PLUGIN_TARGET) + +$(PLUGIN_TARGET): $(HEADER_FILES) $(SRC_FILES) + cat $(HEADER_FILES) | sed -e 's/^/# /g' > $@ + cat $(SRC_FILES) >> $@ + +.PHONY: clean +clean: + rm $(PLUGIN_TARGET) + +.PHONY: test +test: all + @test -n "$$TEST_ZSH_BIN" && echo "Testing zsh binary: $(TEST_ZSH_BIN)" || true + bundle exec rspec $(TESTS) diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/README.md b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/README.md new file mode 100644 index 0000000..4df4e90 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/README.md @@ -0,0 +1,208 @@ +# zsh-autosuggestions + +_[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._ + +It suggests commands as you type, based on command history. + +[![CircleCI](https://circleci.com/gh/zsh-users/zsh-autosuggestions.svg?style=svg)](https://circleci.com/gh/zsh-users/zsh-autosuggestions) + + + + +## Installation + +### Manual + +1. Clone this repository somewhere on your machine. This guide will assume `~/.zsh/zsh-autosuggestions`. + + ```sh + git clone git://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions + ``` + +2. Add the following to your `.zshrc`: + + ```sh + source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh + ``` + +3. Start a new terminal session. + + +### Oh My Zsh + +1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`) + + ```sh + git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions + ``` + +2. Add the plugin to the list of plugins for Oh My Zsh to load: + + ```sh + plugins=(zsh-autosuggestions) + ``` + +3. Start a new terminal session. + +### Arch Linux via the AUR +1. Install the [`zsh-autosuggestions`](https://aur.archlinux.org/packages/zsh-autosuggestions/) or the [`zsh-autosuggestions-git`](https://aur.archlinux.org/packages/zsh-autosuggestions-git/) packages from the [AUR](https://wiki.archlinux.org/index.php/Arch_User_Repository). + + ```sh + pacaur -S zsh-autosuggestions + ``` + or + ``` + pacaur -S zsh-autosuggestions-git + ``` + +2. Add the following to your `.zshrc`: + + ```sh + source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh + ``` + +3. Start a new terminal session. + +### macOS via Homebrew +1. Install the `zsh-autosuggestions` package using [Homebrew](https://brew.sh/). + + ```sh + brew install zsh-autosuggestions + ``` + +2. Add the following to your `.zshrc`: + + ```sh + source /usr/local/share/zsh-autosuggestions/zsh-autosuggestions.zsh + ``` + +3. Start a new terminal session. + +## 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 after sourcing the plugin. 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`. + + +### Suggestion Strategy + +Set `ZSH_AUTOSUGGEST_STRATEGY` to choose the strategy for generating suggestions. There are currently two to choose from: + +- `default`: Chooses the most recent match. +- `match_prev_cmd`: Chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`. + + +### Widget Mapping + +This plugin works by triggering custom behavior when certain [zle widgets](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets) are invoked. You can add and remove widgets from these arrays to change the behavior of this plugin: + +- `ZSH_AUTOSUGGEST_CLEAR_WIDGETS`: Widgets in this array will clear the suggestion when invoked. +- `ZSH_AUTOSUGGEST_ACCEPT_WIDGETS`: Widgets in this array will accept the suggestion when invoked. +- `ZSH_AUTOSUGGEST_EXECUTE_WIDGETS`: Widgets in this array will execute the suggestion when invoked. +- `ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS`: Widgets in this array will partially accept the suggestion when invoked. +- `ZSH_AUTOSUGGEST_IGNORE_WIDGETS`: Widgets in this array will not trigger any custom behavior. + +Widgets that modify the buffer and are not found in any of these arrays will fetch a new suggestion after they are invoked. + +**Note:** A widget shouldn't belong to more than one of the above arrays. + + +### Disabling suggestion for large buffers + +Set `ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE` to an integer value to disable autosuggestion for large buffers. The default is unset, which means that autosuggestion will be tried for any buffer size. Recommended value is 20. +This can be useful when pasting large amount of text in the terminal, to avoid triggering autosuggestion for too long strings. + +### Enable Asynchronous Mode + +As of `v0.4.0`, suggestions can be fetched asynchronously using the `zsh/zpty` module. To enable this behavior, set the `ZSH_AUTOSUGGEST_USE_ASYNC` variable (it can be set to anything). + + +### Key Bindings + +This plugin provides a few widgets that you can use with `bindkey`: + +1. `autosuggest-accept`: Accepts the current suggestion. +2. `autosuggest-execute`: Accepts and executes the current suggestion. +3. `autosuggest-clear`: Clears the current suggestion. +4. `autosuggest-fetch`: Fetches a suggestion (works even when suggestions are disabled). +5. `autosuggest-disable`: Disables suggestions. +6. `autosuggest-enable`: Re-enables suggestions. +7. `autosuggest-toggle`: Toggles between enabled/disabled suggestions. + +For example, this would bind 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) 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`). + + +## 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/.oh-my-zsh/custom/plugins/zsh-autosuggestions/URL b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/URL new file mode 100644 index 0000000..4e2bd94 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/URL @@ -0,0 +1 @@ +https://github.com/zsh-users/zsh-autosuggestions diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/VERSION b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/VERSION new file mode 100644 index 0000000..fb7a04c --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/VERSION @@ -0,0 +1 @@ +v0.4.0 diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/circle.yml b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/circle.yml new file mode 100644 index 0000000..5e3a6f6 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/circle.yml @@ -0,0 +1,12 @@ +machine: + environment: + ZSH_VERSIONS: 5.0.8 5.1.1 5.2 5.3.1 + +dependencies: + pre: + - for v in $(echo $ZSH_VERSIONS | awk "{ for (i=$((1+CIRCLE_NODE_INDEX));i<=NF;i+=$CIRCLE_NODE_TOTAL) print \$i }"); do wget https://sourceforge.net/projects/zsh/files/zsh/$v/zsh-$v.tar.gz && tar xzf zsh-$v.tar.gz && cd zsh-$v && ./configure && sudo make install || exit 1; done + +test: + override: + - for v in $(echo $ZSH_VERSIONS | awk "{ for (i=$((1+CIRCLE_NODE_INDEX));i<=NF;i+=$CIRCLE_NODE_TOTAL) print \$i }"); do TEST_ZSH_BIN=/usr/local/bin/zsh-$v make test || exit 1; done: + parallel: true diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/bracketed_paste_magic_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/bracketed_paste_magic_spec.rb new file mode 100644 index 0000000..f01b0e0 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/bracketed_paste_magic_spec.rb @@ -0,0 +1,36 @@ +describe 'pasting using bracketed-paste-magic' do + let(:before_sourcing) do + -> do + session. + run_command('autoload -Uz bracketed-paste-magic'). + run_command('zle -N bracketed-paste bracketed-paste-magic') + end + end + + context 'with suggestions disabled while pasting' do + before do + session. + run_command('bpm_init() { zle autosuggest-disable }'). + run_command('bpm_finish() { zle autosuggest-enable }'). + run_command('zstyle :bracketed-paste-magic paste-init bpm_init'). + run_command('zstyle :bracketed-paste-magic paste-finish bpm_finish') + end + + it 'does not show an incorrect suggestion' do + with_history('echo hello') do + session.paste_string("echo #{'a' * 60}") + sleep 1 + expect(session.content).to eq("echo #{'a' * 60}") + end + end + + it 'shows a suggestion after a non-modifying keystroke' do + with_history('echo hello') do + session.paste_string('echo') + sleep 1 + session.send_keys('left') + wait_for { session.content }.to eq('echo hello') + end + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/client_zpty_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/client_zpty_spec.rb new file mode 100644 index 0000000..8f1550e --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/client_zpty_spec.rb @@ -0,0 +1,10 @@ +describe 'a running zpty command' do + let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } } + + it 'is not affected by running zsh-autosuggestions' do + sleep 1 # Give a little time for precmd hooks to run + session.run_command('zpty -t kitty; echo $?') + + wait_for { session.content }.to end_with("\n0") + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/wrapped_widget_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/wrapped_widget_spec.rb new file mode 100644 index 0000000..61dfc2d --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/wrapped_widget_spec.rb @@ -0,0 +1,39 @@ +describe 'a wrapped widget' do + let(:widget) { 'backward-delete-char' } + + context 'initialized before sourcing the plugin' do + let(:before_sourcing) do + -> do + session. + run_command("_orig_#{widget}() { zle .#{widget} }"). + run_command("zle -N orig-#{widget} _orig_#{widget}"). + run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }"). + run_command("zle -N #{widget} #{widget}-magic") + end + end + + it 'executes the custom behavior and the built-in behavior' do + with_history('foobar', 'foodar') do + session.send_string('food').send_keys('C-h') + wait_for { session.content }.to eq('foobar') + end + end + end + + context 'initialized after sourcing the plugin' do + before do + session. + run_command("zle -N orig-#{widget} ${widgets[#{widget}]#*:}"). + run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }"). + run_command("zle -N #{widget} #{widget}-magic"). + clear_screen + end + + it 'executes the custom behavior and the built-in behavior' do + with_history('foobar', 'foodar') do + session.send_string('food').send_keys('C-h') + wait_for { session.content }.to eq('foobar') + end + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/zle_input_stack_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/zle_input_stack_spec.rb new file mode 100644 index 0000000..8a2c990 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/integrations/zle_input_stack_spec.rb @@ -0,0 +1,24 @@ +describe 'using `zle -U`' do + let(:before_sourcing) do + -> do + session. + run_command('_zsh_autosuggest_strategy_test() { sleep 1; _zsh_autosuggest_strategy_default "$1" }'). + run_command('foo() { zle -U - "echo hello" }; zle -N foo; bindkey ^B foo') + end + end + + let(:options) { ['unset ZSH_AUTOSUGGEST_USE_ASYNC', 'ZSH_AUTOSUGGEST_STRATEGY=test'] } + + # TODO: This is only possible with the $KEYS_QUEUED_COUNT widget parameter, coming soon... + xit 'does not fetch a suggestion for every inserted character' do + session.send_keys('C-b') + wait_for { session.content }.to eq('echo hello') + end + + it 'shows a suggestion when the widget completes' do + with_history('echo hello world') do + session.send_keys('C-b') + wait_for { session.content(esc_seqs: true) }.to match(/\Aecho hello\e\[[0-9]+m world/) + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/multi_line_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/multi_line_spec.rb new file mode 100644 index 0000000..4ff2ae1 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/multi_line_spec.rb @@ -0,0 +1,13 @@ +describe 'a multi-line suggestion' do + it 'should be displayed on multiple lines' do + with_history(-> { + session.send_string('echo "') + session.send_keys('enter') + session.send_string('"') + session.send_keys('enter') + }) do + session.send_keys('e') + wait_for { session.content }.to eq("echo \"\n\"") + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/async_zpty_name_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/async_zpty_name_spec.rb new file mode 100644 index 0000000..407ee70 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/async_zpty_name_spec.rb @@ -0,0 +1,19 @@ +context 'when async suggestions are enabled' do + let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] } + + describe 'the zpty for async suggestions' do + it 'is created with the default name' do + session.run_command('zpty -t zsh_autosuggest_pty &>/dev/null; echo $?') + wait_for { session.content }.to end_with("\n0") + end + + context 'when ZSH_AUTOSUGGEST_ASYNC_PTY_NAME is set' do + let(:options) { super() + ['ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=foo_pty'] } + + it 'is created with the specified name' do + session.run_command('zpty -t foo_pty &>/dev/null; echo $?') + wait_for { session.content }.to end_with("\n0") + end + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/buffer_max_size_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/buffer_max_size_spec.rb new file mode 100644 index 0000000..29ca8bc --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/buffer_max_size_spec.rb @@ -0,0 +1,30 @@ +describe 'a suggestion' do + let(:term_opts) { { width: 200 } } + let(:long_command) { "echo #{'a' * 100}" } + + around do |example| + with_history(long_command) { example.run } + end + + it 'is provided for any buffer length' do + session.send_string(long_command[0...-1]) + wait_for { session.content }.to eq(long_command) + end + + context 'when ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE is specified' do + let(:buffer_max_size) { 10 } + let(:options) { ["ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=#{buffer_max_size}"] } + + it 'is provided when the buffer is shorter than the specified length' do + session.send_string(long_command[0...(buffer_max_size - 1)]) + wait_for { session.content }.to eq(long_command) + end + + it 'is provided when the buffer is equal to the specified length' do + session.send_string(long_command[0...(buffer_max_size)]) + wait_for { session.content }.to eq(long_command) + end + + it 'is not provided when the buffer is longer than the specified length' + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/highlight_style_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/highlight_style_spec.rb new file mode 100644 index 0000000..a7e39b3 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/highlight_style_spec.rb @@ -0,0 +1,7 @@ +describe 'a displayed suggestion' do + it 'is shown in the default style' + + describe 'when ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE is set to a zle_highlight string' do + it 'is shown in the specified style' + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/original_widget_prefix_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/original_widget_prefix_spec.rb new file mode 100644 index 0000000..a4b6e98 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/original_widget_prefix_spec.rb @@ -0,0 +1,7 @@ +describe 'an original zle widget' do + context 'is accessible with the default prefix' + + context 'when ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX is set' do + it 'is accessible with the specified prefix' + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/strategy_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/strategy_spec.rb new file mode 100644 index 0000000..c9f01e1 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/strategy_spec.rb @@ -0,0 +1,20 @@ +describe 'a suggestion for a given prefix' do + let(:options) { ['_zsh_autosuggest_strategy_default() { suggestion="echo foo" }'] } + + it 'is determined by calling the default strategy function' do + session.send_string('e') + wait_for { session.content }.to eq('echo foo') + end + + context 'when ZSH_AUTOSUGGEST_STRATEGY is set' do + let(:options) { [ + '_zsh_autosuggest_strategy_custom() { suggestion="echo foo" }', + 'ZSH_AUTOSUGGEST_STRATEGY=custom' + ] } + + it 'is determined by calling the specified strategy function' do + session.send_string('e') + wait_for { session.content }.to eq('echo foo') + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/use_async_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/use_async_spec.rb new file mode 100644 index 0000000..420dcc3 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/use_async_spec.rb @@ -0,0 +1,7 @@ +describe 'suggestion fetching' do + it 'is performed synchronously' + + context 'when ZSH_AUTOSUGGEST_USE_ASYNC is set' do + it 'is performed asynchronously' + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/widget_lists_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/widget_lists_spec.rb new file mode 100644 index 0000000..c207c0c --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/options/widget_lists_spec.rb @@ -0,0 +1,97 @@ +describe 'a zle widget' do + let(:widget) { 'my-widget' } + let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } } + + context 'when added to ZSH_AUTOSUGGEST_ACCEPT_WIDGETS' do + let(:options) { ["ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})"] } + + it 'accepts the suggestion when invoked' do + with_history('echo hello') do + session.send_string('e') + wait_for { session.content }.to eq('echo hello') + session.send_keys('C-b') + wait_for { session.content(esc_seqs: true) }.to eq('echo hello') + end + end + end + + context 'when added to ZSH_AUTOSUGGEST_CLEAR_WIDGETS' do + let(:options) { ["ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(#{widget})"] } + + it 'clears the suggestion when invoked' do + with_history('echo hello') do + session.send_string('e') + wait_for { session.content }.to eq('echo hello') + session.send_keys('C-b') + wait_for { session.content }.to eq('e') + end + end + end + + context 'when added to ZSH_AUTOSUGGEST_EXECUTE_WIDGETS' do + let(:options) { ["ZSH_AUTOSUGGEST_EXECUTE_WIDGETS+=(#{widget})"] } + + it 'executes the suggestion when invoked' do + with_history('echo hello') do + session.send_string('e') + wait_for { session.content }.to eq('echo hello') + session.send_keys('C-b') + wait_for { session.content }.to end_with("\nhello") + end + end + end + + context 'when added to ZSH_AUTOSUGGEST_IGNORE_WIDGETS' do + let(:options) { ["ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(#{widget})"] } + + it 'should not be wrapped with an autosuggest widget' do + session.run_command("echo $widgets[#{widget}]") + wait_for { session.content }.to end_with("\nuser:#{widget}") + end + end + + context 'that moves the cursor forward' do + before { session.run_command("#{widget}() { zle forward-char }") } + + context 'when added to ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS' do + let(:options) { ["ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(#{widget})"] } + + it 'accepts the suggestion as far as the cursor is moved when invoked' do + with_history('echo hello') do + session.send_string('e') + wait_for { session.content }.to start_with('echo hello') + session.send_keys('C-b') + wait_for { session.content(esc_seqs: true) }.to match(/\Aec\e\[[0-9]+mho hello/) + end + end + end + end + + context 'that modifies the buffer' do + before { session.run_command("#{widget}() { BUFFER=\"foo\" }") } + + context 'when not added to any of the widget lists' do + it 'modifies the buffer and fetches a new suggestion' do + with_history('foobar') do + session.send_keys('C-b') + wait_for { session.content }.to eq('foobar') + end + end + end + end +end + +describe 'a modification to the widget lists' do + let(:widget) { 'my-widget' } + let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } } + before { session.run_command("ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})") } + + it 'takes effect on the next cmd line' do + with_history('echo hello') do + session.send_string('e') + wait_for { session.content }.to eq('echo hello') + session.send_keys('C-b') + wait_for { session.content(esc_seqs: true) }.to eq('echo hello') + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/spec_helper.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/spec_helper.rb new file mode 100644 index 0000000..64115d2 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/spec_helper.rb @@ -0,0 +1,50 @@ +require 'pry' +require 'rspec/wait' +require 'terminal_session' + +RSpec.shared_context 'terminal session' do + let(:term_opts) { {} } + let(:session) { TerminalSession.new(term_opts) } + let(:before_sourcing) { -> {} } + let(:options) { [] } + + around do |example| + before_sourcing.call + + session.run_command((['source zsh-autosuggestions.zsh'] + options).join('; ')) + session.clear_screen + + example.run + + session.destroy + end + + def with_history(*commands, &block) + session.run_command('fc -p') + + commands.each do |c| + c.respond_to?(:call) ? c.call : session.run_command(c) + end + + session.clear_screen + + yield block + + session.send_keys('C-c') + session.run_command('fc -P') + end +end + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.wait_timeout = 2 + + config.include_context 'terminal session' +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/special_characters_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/special_characters_spec.rb new file mode 100644 index 0000000..ce7810b --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/special_characters_spec.rb @@ -0,0 +1,60 @@ +describe 'a special character in the buffer' do + it 'should be treated like any other character' do + with_history('echo "hello*"', 'echo "hello."') do + session.send_string('echo "hello*') + wait_for { session.content }.to eq('echo "hello*"') + end + + with_history('echo "hello?"', 'echo "hello."') do + session.send_string('echo "hello?') + wait_for { session.content }.to eq('echo "hello?"') + end + + with_history('echo "hello\nworld"') do + session.send_string('echo "hello\\') + wait_for { session.content }.to eq('echo "hello\nworld"') + end + + with_history('echo "\\\\"') do + session.send_string('echo "\\\\') + wait_for { session.content }.to eq('echo "\\\\"') + end + + with_history('echo ~/foo') do + session.send_string('echo ~') + wait_for { session.content }.to eq('echo ~/foo') + end + + with_history('echo "$(ls foo)"') do + session.send_string('echo "$(') + wait_for { session.content }.to eq('echo "$(ls foo)"') + end + + with_history('echo "$history[123]"') do + session.send_string('echo "$history[') + wait_for { session.content }.to eq('echo "$history[123]"') + session.send_string('123]') + wait_for { session.content }.to eq('echo "$history[123]"') + end + + with_history('echo "#yolo"') do + session.send_string('echo "#') + wait_for { session.content }.to eq('echo "#yolo"') + end + + with_history('echo "#foo"', 'echo $#abc') do + session.send_string('echo "#') + wait_for { session.content }.to eq('echo "#foo"') + end + + with_history('echo "^A"', 'echo "^B"') do + session.send_string('echo "^A') + wait_for { session.content }.to eq('echo "^A"') + end + + with_history('-foo() {}') do + session.send_string('-') + wait_for { session.content }.to eq('-foo() {}') + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/strategies/default_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/strategies/default_spec.rb new file mode 100644 index 0000000..94f3450 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/strategies/default_spec.rb @@ -0,0 +1,8 @@ +describe 'the default suggestion strategy' do + it 'suggests the last matching history entry' do + with_history('ls foo', 'ls bar', 'echo baz') do + session.send_string('ls') + wait_for { session.content }.to eq('ls bar') + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/strategies/match_prev_cmd_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/strategies/match_prev_cmd_spec.rb new file mode 100644 index 0000000..21be712 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/strategies/match_prev_cmd_spec.rb @@ -0,0 +1,17 @@ +describe 'the match_prev_cmd strategy' do + let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd'] } + + it 'suggests the last matching history entry after the previous command' do + with_history( + 'echo what', + 'ls foo', + 'echo what', + 'ls bar', + 'ls baz', + 'echo what' + ) do + session.send_string('ls') + wait_for { session.content }.to eq('ls bar') + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/terminal_session.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/terminal_session.rb new file mode 100644 index 0000000..82705d3 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/terminal_session.rb @@ -0,0 +1,91 @@ +require 'securerandom' + +class TerminalSession + ZSH_BIN = ENV['TEST_ZSH_BIN'] || 'zsh' + + def initialize(opts = {}) + opts = { + width: 80, + height: 24, + prompt: '', + term: 'xterm-256color', + zsh_bin: ZSH_BIN + }.merge(opts) + + @opts = opts + + cmd="PS1=\"#{opts[:prompt]}\" TERM=#{opts[:term]} #{ZSH_BIN} -f" + tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'") + end + + def tmux_socket_name + @tmux_socket_name ||= SecureRandom.hex(6) + end + + def run_command(command) + send_string(command) + send_keys('enter') + + self + end + + def send_string(str) + tmux_command("send-keys -t 0 -l -- '#{str.gsub("'", "\\'")}'") + + self + end + + def send_keys(*keys) + tmux_command("send-keys -t 0 #{keys.join(' ')}") + + self + end + + def paste_string(str) + tmux_command("set-buffer -- '#{str}'") + tmux_command("paste-buffer -dpr -t 0") + + self + end + + def content(esc_seqs: false) + cmd = 'capture-pane -p -t 0' + cmd += ' -e' if esc_seqs + tmux_command(cmd).strip + end + + def clear_screen + send_keys('C-l') + + i = 0 + until content == opts[:prompt] || i > 20 do + sleep(0.1) + i = i + 1 + end + + self + end + + def destroy + tmux_command('kill-session') + end + + def cursor + tmux_command("display-message -t 0 -p '\#{cursor_x},\#{cursor_y}'"). + strip. + split(','). + map(&:to_i) + end + + private + + attr_reader :opts + + def tmux_command(cmd) + out = `tmux -u -L #{tmux_socket_name} #{cmd}` + + raise("tmux error running: '#{cmd}'") unless $?.success? + + out + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/disable_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/disable_spec.rb new file mode 100644 index 0000000..b387a59 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/disable_spec.rb @@ -0,0 +1,19 @@ +describe 'the `autosuggest-disable` widget' do + before do + session.run_command('bindkey ^B autosuggest-disable') + end + + it 'disables suggestions and clears the suggestion' do + with_history('echo hello') do + session.send_string('echo') + wait_for { session.content }.to eq('echo hello') + + session.send_keys('C-b') + wait_for { session.content }.to eq('echo') + + session.send_string(' h') + sleep 1 + expect(session.content).to eq('echo h') + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/enable_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/enable_spec.rb new file mode 100644 index 0000000..3ad35a8 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/enable_spec.rb @@ -0,0 +1,42 @@ +describe 'the `autosuggest-enable` widget' do + before do + session. + run_command('typeset -g _ZSH_AUTOSUGGEST_DISABLED'). + run_command('bindkey ^B autosuggest-enable') + end + + it 'enables suggestions and fetches a suggestion' do + with_history('echo hello') do + session.send_string('e') + sleep 1 + expect(session.content).to eq('e') + + session.send_keys('C-b') + session.send_string('c') + wait_for { session.content }.to eq('echo hello') + end + end + + context 'invoked on an empty buffer' do + it 'does not fetch a suggestion' do + with_history('echo hello') do + session.send_keys('C-b') + sleep 1 + expect(session.content).to eq('') + end + end + end + + context 'invoked on a non-empty buffer' do + it 'fetches a suggestion' do + with_history('echo hello') do + session.send_string('e') + sleep 1 + expect(session.content).to eq('e') + + session.send_keys('C-b') + wait_for { session.content }.to eq('echo hello') + end + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/fetch_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/fetch_spec.rb new file mode 100644 index 0000000..eb8f2ba --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/fetch_spec.rb @@ -0,0 +1,24 @@ +describe 'the `autosuggest-fetch` widget' do + context 'when suggestions are disabled' do + before do + session. + run_command('bindkey ^B autosuggest-disable'). + run_command('bindkey ^F autosuggest-fetch'). + send_keys('C-b') + end + + it 'will fetch and display a suggestion' do + with_history('echo hello') do + session.send_string('echo h') + sleep 1 + expect(session.content).to eq('echo h') + + session.send_keys('C-f') + wait_for { session.content }.to eq('echo hello') + + session.send_string('e') + wait_for { session.content }.to eq('echo hello') + end + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/toggle_spec.rb b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/toggle_spec.rb new file mode 100644 index 0000000..8f9f3c3 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/spec/widgets/toggle_spec.rb @@ -0,0 +1,26 @@ +describe 'the `autosuggest-toggle` widget' do + before do + session.run_command('bindkey ^B autosuggest-toggle') + end + + it 'toggles suggestions' do + with_history('echo world', 'echo hello') do + session.send_string('echo') + wait_for { session.content }.to eq('echo hello') + + session.send_keys('C-b') + wait_for { session.content }.to eq('echo') + + session.send_string(' h') + sleep 1 + expect(session.content).to eq('echo h') + + session.send_keys('C-b') + wait_for { session.content }.to eq('echo hello') + + session.send_keys('C-h') + session.send_string('w') + wait_for { session.content }.to eq('echo world') + end + end +end diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/async.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/async.zsh new file mode 100644 index 0000000..124c9ac --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/async.zsh @@ -0,0 +1,109 @@ + +#--------------------------------------------------------------------# +# Async # +#--------------------------------------------------------------------# + +# Zpty process is spawned running this function +_zsh_autosuggest_async_server() { + emulate -R zsh + + # There is a bug in zpty module (fixed in zsh/master) by which a + # zpty that exits will kill all zpty processes that were forked + # before it. Here we set up a zsh exit hook to SIGKILL the zpty + # process immediately, before it has a chance to kill any other + # zpty processes. + zshexit() { + kill -KILL $$ + sleep 1 # Block for long enough for the signal to come through + } + + # Output only newlines (not carriage return + newline) + stty -onlcr + + # Silence any error messages + exec 2>/dev/null + + local strategy=$1 + local last_pid + + while IFS='' read -r -d $'\0' query; do + # Kill last bg process + kill -KILL $last_pid &>/dev/null + + # Run suggestion search in the background + ( + local suggestion + _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query" + echo -n -E "$suggestion"$'\0' + ) & + + last_pid=$! + done +} + +_zsh_autosuggest_async_request() { + # Write the query to the zpty process to fetch a suggestion + zpty -w -n $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "${1}"$'\0' +} + +# Called when new data is ready to be read from the pty +# First arg will be fd ready for reading +# Second arg will be passed in case of error +_zsh_autosuggest_async_response() { + setopt LOCAL_OPTIONS EXTENDED_GLOB + + local suggestion + + zpty -rt $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME suggestion '*'$'\0' 2>/dev/null + zle autosuggest-suggest -- "${suggestion%%$'\0'##}" +} + +_zsh_autosuggest_async_pty_create() { + # With newer versions of zsh, REPLY stores the fd to read from + typeset -h REPLY + + # If we won't get a fd back from zpty, try to guess it + if [ $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD -eq 0 ]; then + integer -l zptyfd + exec {zptyfd}>&1 # Open a new file descriptor (above 10). + exec {zptyfd}>&- # Close it so it's free to be used by zpty. + fi + + # Fork a zpty process running the server function + zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "_zsh_autosuggest_async_server _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" + + # Store the fd so we can remove the handler later + if (( REPLY )); then + _ZSH_AUTOSUGGEST_PTY_FD=$REPLY + else + _ZSH_AUTOSUGGEST_PTY_FD=$zptyfd + fi + + # Set up input handler from the zpty + zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response +} + +_zsh_autosuggest_async_pty_destroy() { + if zpty -t $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null; then + # Remove the input handler + zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null + + # Destroy the zpty + zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null + fi +} + +_zsh_autosuggest_async_pty_recreate() { + _zsh_autosuggest_async_pty_destroy + _zsh_autosuggest_async_pty_create +} + +_zsh_autosuggest_async_start() { + typeset -g _ZSH_AUTOSUGGEST_PTY_FD + + _zsh_autosuggest_feature_detect_zpty_returns_fd + _zsh_autosuggest_async_pty_recreate + + # We recreate the pty to get a fresh list of history events + add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate +} diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/bind.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/bind.zsh new file mode 100644 index 0000000..d8629f1 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/bind.zsh @@ -0,0 +1,118 @@ + +#--------------------------------------------------------------------# +# Widget Helpers # +#--------------------------------------------------------------------# + +_zsh_autosuggest_incr_bind_count() { + if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then + ((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]++)) + else + _ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=1 + fi + + bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1] +} + +_zsh_autosuggest_get_bind_count() { + if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then + bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1] + else + bind_count=0 + fi +} + +# Bind a single widget to an autosuggest widget, saving a reference to the original widget +_zsh_autosuggest_bind_widget() { + typeset -gA _ZSH_AUTOSUGGEST_BIND_COUNTS + + local widget=$1 + local autosuggest_action=$2 + local prefix=$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX + + local -i bind_count + + # Save a reference to the original widget + case $widgets[$widget] in + # Already bound + user:_zsh_autosuggest_(bound|orig)_*);; + + # User-defined widget + user:*) + _zsh_autosuggest_incr_bind_count $widget + zle -N $prefix${bind_count}-$widget ${widgets[$widget]#*:} + ;; + + # Built-in widget + builtin) + _zsh_autosuggest_incr_bind_count $widget + eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }" + zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget + ;; + + # Completion widget + completion:*) + _zsh_autosuggest_incr_bind_count $widget + eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}" + ;; + esac + + _zsh_autosuggest_get_bind_count $widget + + # Pass the original widget's name explicitly into the autosuggest + # function. Use this passed in widget name to call the original + # widget instead of relying on the $WIDGET variable being set + # correctly. $WIDGET cannot be trusted because other plugins call + # zle without the `-w` flag (e.g. `zle self-insert` instead of + # `zle self-insert -w`). + eval "_zsh_autosuggest_bound_${bind_count}_${(q)widget}() { + _zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@ + }" + + # Create the bound widget + zle -N $widget _zsh_autosuggest_bound_${bind_count}_$widget +} + +# Map all configured widgets to the right autosuggest widgets +_zsh_autosuggest_bind_widgets() { + local widget + local ignore_widgets + + ignore_widgets=( + .\* + _\* + zle-\* + autosuggest-\* + $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\* + $ZSH_AUTOSUGGEST_IGNORE_WIDGETS + ) + + # Find every widget we might want to bind and bind it appropriately + for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do + if [ ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]; then + _zsh_autosuggest_bind_widget $widget clear + elif [ ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]; then + _zsh_autosuggest_bind_widget $widget accept + elif [ ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]; then + _zsh_autosuggest_bind_widget $widget execute + elif [ ${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 + [ $# -gt 0 ] || return + + local original_widget_name="$1" + + shift + + if [ $widgets[$original_widget_name] ]; then + zle $original_widget_name -- $@ + fi +} diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/features.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/features.zsh new file mode 100644 index 0000000..7a5248f --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/features.zsh @@ -0,0 +1,19 @@ + +#--------------------------------------------------------------------# +# Feature Detection # +#--------------------------------------------------------------------# + +_zsh_autosuggest_feature_detect_zpty_returns_fd() { + typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD + typeset -h REPLY + + zpty zsh_autosuggest_feature_detect '{ zshexit() { kill -KILL $$; sleep 1 } }' + + if (( REPLY )); then + _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1 + else + _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0 + fi + + zpty -d zsh_autosuggest_feature_detect +} diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/highlight.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/highlight.zsh new file mode 100644 index 0000000..a8ff08a --- /dev/null +++ b/.oh-my-zsh/custom/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 -gt 0 ]; then + _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/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/setup.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/setup.zsh new file mode 100644 index 0000000..c74489f --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/setup.zsh @@ -0,0 +1,10 @@ + +#--------------------------------------------------------------------# +# Setup # +#--------------------------------------------------------------------# + +# Precmd hooks for initializing the library and starting pty's +autoload -Uz add-zsh-hook + +# Asynchronous suggestions are generated in a pty +zmodload zsh/zpty diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/start.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/start.zsh new file mode 100644 index 0000000..6fa8ce9 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/start.zsh @@ -0,0 +1,24 @@ + +#--------------------------------------------------------------------# +# Start # +#--------------------------------------------------------------------# + +# Start the autosuggestion widgets +_zsh_autosuggest_start() { + add-zsh-hook -d precmd _zsh_autosuggest_start + + _zsh_autosuggest_bind_widgets + + # Re-bind widgets on every precmd to ensure we wrap other wrappers. + # Specifically, highlighting breaks if our widgets are wrapped by + # zsh-syntax-highlighting widgets. This also allows modifications + # to the widget list variables to take effect on the next precmd. + add-zsh-hook precmd _zsh_autosuggest_bind_widgets + + if [ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]; then + _zsh_autosuggest_async_start + fi +} + +# Start the autosuggestion widgets on the next precmd +add-zsh-hook precmd _zsh_autosuggest_start diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/strategies/default.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/strategies/default.zsh new file mode 100644 index 0000000..60c0494 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/strategies/default.zsh @@ -0,0 +1,25 @@ + +#--------------------------------------------------------------------# +# Default Suggestion Strategy # +#--------------------------------------------------------------------# +# Suggests the most recent history item that matches the given +# prefix. +# + +_zsh_autosuggest_strategy_default() { + # Reset options to defaults and enable LOCAL_OPTIONS + emulate -L zsh + + # Enable globbing flags so that we can use (#m) + setopt EXTENDED_GLOB + + # Escape backslashes and all of the glob operators so we can use + # this string as a pattern to search the $history associative array. + # - (#m) globbing flag enables setting references for match data + local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}" + + # Get the history items that match + # - (r) subscript flag makes the pattern match on values + suggestion="${history[(r)$prefix*]}" + +} diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/strategies/match_prev_cmd.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/strategies/match_prev_cmd.zsh new file mode 100644 index 0000000..ee26346 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/strategies/match_prev_cmd.zsh @@ -0,0 +1,52 @@ + +#--------------------------------------------------------------------# +# 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() { + local prefix="${1//(#m)[\\()\[\]|*?~]/\\$MATCH}" + + # Get all history event numbers that correspond to history + # entries that match pattern $prefix* + local history_match_keys + history_match_keys=(${(k)history[(R)$prefix*]}) + + # By default we use the first history number (most recent history entry) + local histkey="${history_match_keys[1]}" + + # Get the previously executed command + local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")" + + # Iterate up to the first 200 history event numbers that match $prefix + for key in "${(@)history_match_keys[1,200]}"; do + # Stop if we ran out of history + [[ $key -gt 1 ]] || break + + # See if the history entry preceding the suggestion matches the + # previous command, and use it if it does + if [[ "${history[$((key - 1))]}" == "$prev_cmd" ]]; then + histkey="$key" + break + fi + done + + # Give back the matched history entry + suggestion="$history[$histkey]" +} diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/util.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/util.zsh new file mode 100644 index 0000000..1f55d36 --- /dev/null +++ b/.oh-my-zsh/custom/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/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/widgets.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/widgets.zsh new file mode 100644 index 0000000..aa3f248 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/src/widgets.zsh @@ -0,0 +1,206 @@ + +#--------------------------------------------------------------------# +# 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 -gt 0 ]; then + _zsh_autosuggest_fetch + fi +} + +# Toggle suggestions (enable/disable) +_zsh_autosuggest_toggle() { + if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then + _zsh_autosuggest_enable + else + _zsh_autosuggest_disable + fi +} + +# Clear the suggestion +_zsh_autosuggest_clear() { + # Remove the suggestion + unset POSTDISPLAY + + _zsh_autosuggest_invoke_original_widget $@ +} + +# Modify the buffer and get a new suggestion +_zsh_autosuggest_modify() { + local -i retval + + # Only added to zsh very recently + local -i KEYS_QUEUED_COUNT + + # Save the contents of the buffer/postdisplay + local orig_buffer="$BUFFER" + local orig_postdisplay="$POSTDISPLAY" + + # Clear suggestion while waiting for next one + unset POSTDISPLAY + + # Original widget may modify the buffer + _zsh_autosuggest_invoke_original_widget $@ + retval=$? + + # Don't fetch a new suggestion if there's more input to be read immediately + if [[ $PENDING > 0 ]] || [[ $KEYS_QUEUED_COUNT > 0 ]]; then + return $retval + fi + + # Optimize if manually typing in the suggestion + if [ $#BUFFER -gt $#orig_buffer ]; then + local added=${BUFFER#$orig_buffer} + + # If the string added matches the beginning of the postdisplay + if [ "$added" = "${orig_postdisplay:0:$#added}" ]; then + POSTDISPLAY="${orig_postdisplay:$#added}" + return $retval + fi + fi + + # Don't fetch a new suggestion if the buffer hasn't changed + if [ "$BUFFER" = "$orig_buffer" ]; then + POSTDISPLAY="$orig_postdisplay" + return $retval + fi + + # Bail out if suggestions are disabled + if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then + return $? + fi + + # Get a new suggestion if the buffer is not empty after modification + if [ $#BUFFER -gt 0 ]; then + if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -le "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then + _zsh_autosuggest_fetch + fi + fi + + return $retval +} + +# Fetch a new suggestion based on what's currently in the buffer +_zsh_autosuggest_fetch() { + if zpty -t "$ZSH_AUTOSUGGEST_ASYNC_PTY_NAME" &>/dev/null; then + _zsh_autosuggest_async_request "$BUFFER" + else + local suggestion + _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER" + _zsh_autosuggest_suggest "$suggestion" + fi +} + +# Offer a suggestion +_zsh_autosuggest_suggest() { + local suggestion="$1" + + if [ -n "$suggestion" ] && [ $#BUFFER -gt 0 ]; then + POSTDISPLAY="${suggestion#$BUFFER}" + else + unset POSTDISPLAY + fi +} + +# Accept the entire suggestion +_zsh_autosuggest_accept() { + local -i max_cursor_pos=$#BUFFER + + # When vicmd keymap is active, the cursor can't move all the way + # to the end of the buffer + if [ "$KEYMAP" = "vicmd" ]; then + max_cursor_pos=$((max_cursor_pos - 1)) + fi + + # Only accept if the cursor is at the end of the buffer + if [ $CURSOR -eq $max_cursor_pos ]; then + # Add the suggestion to the buffer + BUFFER="$BUFFER$POSTDISPLAY" + + # Remove the suggestion + unset POSTDISPLAY + + # Move the cursor to the end of the buffer + CURSOR=${#BUFFER} + fi + + _zsh_autosuggest_invoke_original_widget $@ +} + +# Accept the entire suggestion and execute it +_zsh_autosuggest_execute() { + # Add the suggestion to the buffer + BUFFER="$BUFFER$POSTDISPLAY" + + # Remove the suggestion + unset POSTDISPLAY + + # Call the original `accept-line` to handle syntax highlighting or + # other potential custom behavior + _zsh_autosuggest_invoke_original_widget "accept-line" +} + +# Partially accept the suggestion +_zsh_autosuggest_partial_accept() { + local -i retval + + # 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=$? + + # If we've moved past the end of the original buffer + if [ $CURSOR -gt $#original_buffer ]; then + # Set POSTDISPLAY to text right of the cursor + POSTDISPLAY="$RBUFFER" + + # Clip the buffer at the cursor + BUFFER="$LBUFFER" + else + # Restore the original buffer + BUFFER="$original_buffer" + fi + + return $retval +} + +for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do + eval "_zsh_autosuggest_widget_$action() { + local -i retval + + _zsh_autosuggest_highlight_reset + + _zsh_autosuggest_$action \$@ + retval=\$? + + _zsh_autosuggest_highlight_apply + + zle -R + + return \$retval + }" +done + +zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch +zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest +zle -N autosuggest-accept _zsh_autosuggest_widget_accept +zle -N autosuggest-clear _zsh_autosuggest_widget_clear +zle -N autosuggest-execute _zsh_autosuggest_widget_execute +zle -N autosuggest-enable _zsh_autosuggest_widget_enable +zle -N autosuggest-disable _zsh_autosuggest_widget_disable +zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh new file mode 100644 index 0000000..16c2256 --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh @@ -0,0 +1 @@ +source ${0:A:h}/zsh-autosuggestions.zsh diff --git a/.oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh new file mode 100644 index 0000000..0d389ec --- /dev/null +++ b/.oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh @@ -0,0 +1,691 @@ +# Fish-like fast/unobtrusive autosuggestions for zsh. +# https://github.com/zsh-users/zsh-autosuggestions +# v0.4.0 +# Copyright (c) 2013 Thiago de Arruda +# Copyright (c) 2016-2017 Eric Freese +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +#--------------------------------------------------------------------# +# Setup # +#--------------------------------------------------------------------# + +# Precmd hooks for initializing the library and starting pty's +autoload -Uz add-zsh-hook + +# Asynchronous suggestions are generated in a pty +zmodload zsh/zpty + +#--------------------------------------------------------------------# +# Global Configuration Variables # +#--------------------------------------------------------------------# + +# Color to use when highlighting suggestion +# Uses format of `region_highlight` +# More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets +ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' + +# Prefix to use when saving original versions of bound widgets +ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- + +ZSH_AUTOSUGGEST_STRATEGY=default + +# Widgets that clear the suggestion +ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( + history-search-forward + history-search-backward + history-beginning-search-forward + history-beginning-search-backward + history-substring-search-up + history-substring-search-down + up-line-or-history + down-line-or-history + accept-line +) + +# Widgets that accept the entire suggestion +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=( +) + +# Widgets that accept the suggestion as far as the cursor moves +ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=( + forward-word + vi-forward-word + vi-forward-word-end + vi-forward-blank-word + vi-forward-blank-word-end +) + +# Widgets that should be ignored (globbing supported but must be escaped) +ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( + orig-\* + beep + run-help + set-local-history + which-command + yank +) + +# Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. +ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= + +# Pty name for calculating autosuggestions asynchronously +ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty + +#--------------------------------------------------------------------# +# Utility Functions # +#--------------------------------------------------------------------# + +_zsh_autosuggest_escape_command() { + setopt localoptions EXTENDED_GLOB + + # Escape special chars in the string (requires EXTENDED_GLOB) + echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}" +} + +#--------------------------------------------------------------------# +# Feature Detection # +#--------------------------------------------------------------------# + +_zsh_autosuggest_feature_detect_zpty_returns_fd() { + typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD + typeset -h REPLY + + zpty zsh_autosuggest_feature_detect '{ zshexit() { kill -KILL $$; sleep 1 } }' + + if (( REPLY )); then + _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=1 + else + _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD=0 + fi + + zpty -d zsh_autosuggest_feature_detect +} + +#--------------------------------------------------------------------# +# Widget Helpers # +#--------------------------------------------------------------------# + +_zsh_autosuggest_incr_bind_count() { + if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then + ((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]++)) + else + _ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=1 + fi + + bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1] +} + +_zsh_autosuggest_get_bind_count() { + if ((${+_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]})); then + bind_count=$_ZSH_AUTOSUGGEST_BIND_COUNTS[$1] + else + bind_count=0 + fi +} + +# Bind a single widget to an autosuggest widget, saving a reference to the original widget +_zsh_autosuggest_bind_widget() { + typeset -gA _ZSH_AUTOSUGGEST_BIND_COUNTS + + local widget=$1 + local autosuggest_action=$2 + local prefix=$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX + + local -i bind_count + + # Save a reference to the original widget + case $widgets[$widget] in + # Already bound + user:_zsh_autosuggest_(bound|orig)_*);; + + # User-defined widget + user:*) + _zsh_autosuggest_incr_bind_count $widget + zle -N $prefix${bind_count}-$widget ${widgets[$widget]#*:} + ;; + + # Built-in widget + builtin) + _zsh_autosuggest_incr_bind_count $widget + eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }" + zle -N $prefix${bind_count}-$widget _zsh_autosuggest_orig_$widget + ;; + + # Completion widget + completion:*) + _zsh_autosuggest_incr_bind_count $widget + eval "zle -C $prefix${bind_count}-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}" + ;; + esac + + _zsh_autosuggest_get_bind_count $widget + + # Pass the original widget's name explicitly into the autosuggest + # function. Use this passed in widget name to call the original + # widget instead of relying on the $WIDGET variable being set + # correctly. $WIDGET cannot be trusted because other plugins call + # zle without the `-w` flag (e.g. `zle self-insert` instead of + # `zle self-insert -w`). + eval "_zsh_autosuggest_bound_${bind_count}_${(q)widget}() { + _zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@ + }" + + # Create the bound widget + zle -N $widget _zsh_autosuggest_bound_${bind_count}_$widget +} + +# Map all configured widgets to the right autosuggest widgets +_zsh_autosuggest_bind_widgets() { + local widget + local ignore_widgets + + ignore_widgets=( + .\* + _\* + zle-\* + autosuggest-\* + $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\* + $ZSH_AUTOSUGGEST_IGNORE_WIDGETS + ) + + # Find every widget we might want to bind and bind it appropriately + for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do + if [ ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]; then + _zsh_autosuggest_bind_widget $widget clear + elif [ ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]; then + _zsh_autosuggest_bind_widget $widget accept + elif [ ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]; then + _zsh_autosuggest_bind_widget $widget execute + elif [ ${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 + [ $# -gt 0 ] || return + + 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 -gt 0 ]; then + _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 -gt 0 ]; then + _zsh_autosuggest_fetch + fi +} + +# Toggle suggestions (enable/disable) +_zsh_autosuggest_toggle() { + if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then + _zsh_autosuggest_enable + else + _zsh_autosuggest_disable + fi +} + +# Clear the suggestion +_zsh_autosuggest_clear() { + # Remove the suggestion + unset POSTDISPLAY + + _zsh_autosuggest_invoke_original_widget $@ +} + +# Modify the buffer and get a new suggestion +_zsh_autosuggest_modify() { + local -i retval + + # Only added to zsh very recently + local -i KEYS_QUEUED_COUNT + + # Save the contents of the buffer/postdisplay + local orig_buffer="$BUFFER" + local orig_postdisplay="$POSTDISPLAY" + + # Clear suggestion while waiting for next one + unset POSTDISPLAY + + # Original widget may modify the buffer + _zsh_autosuggest_invoke_original_widget $@ + retval=$? + + # Don't fetch a new suggestion if there's more input to be read immediately + if [[ $PENDING > 0 ]] || [[ $KEYS_QUEUED_COUNT > 0 ]]; then + return $retval + fi + + # Optimize if manually typing in the suggestion + if [ $#BUFFER -gt $#orig_buffer ]; then + local added=${BUFFER#$orig_buffer} + + # If the string added matches the beginning of the postdisplay + if [ "$added" = "${orig_postdisplay:0:$#added}" ]; then + POSTDISPLAY="${orig_postdisplay:$#added}" + return $retval + fi + fi + + # Don't fetch a new suggestion if the buffer hasn't changed + if [ "$BUFFER" = "$orig_buffer" ]; then + POSTDISPLAY="$orig_postdisplay" + return $retval + fi + + # Bail out if suggestions are disabled + if [ -n "${_ZSH_AUTOSUGGEST_DISABLED+x}" ]; then + return $? + fi + + # Get a new suggestion if the buffer is not empty after modification + if [ $#BUFFER -gt 0 ]; then + if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -le "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then + _zsh_autosuggest_fetch + fi + fi + + return $retval +} + +# Fetch a new suggestion based on what's currently in the buffer +_zsh_autosuggest_fetch() { + if zpty -t "$ZSH_AUTOSUGGEST_ASYNC_PTY_NAME" &>/dev/null; then + _zsh_autosuggest_async_request "$BUFFER" + else + local suggestion + _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER" + _zsh_autosuggest_suggest "$suggestion" + fi +} + +# Offer a suggestion +_zsh_autosuggest_suggest() { + local suggestion="$1" + + if [ -n "$suggestion" ] && [ $#BUFFER -gt 0 ]; then + POSTDISPLAY="${suggestion#$BUFFER}" + else + unset POSTDISPLAY + fi +} + +# Accept the entire suggestion +_zsh_autosuggest_accept() { + local -i max_cursor_pos=$#BUFFER + + # When vicmd keymap is active, the cursor can't move all the way + # to the end of the buffer + if [ "$KEYMAP" = "vicmd" ]; then + max_cursor_pos=$((max_cursor_pos - 1)) + fi + + # Only accept if the cursor is at the end of the buffer + if [ $CURSOR -eq $max_cursor_pos ]; then + # Add the suggestion to the buffer + BUFFER="$BUFFER$POSTDISPLAY" + + # Remove the suggestion + unset POSTDISPLAY + + # Move the cursor to the end of the buffer + CURSOR=${#BUFFER} + fi + + _zsh_autosuggest_invoke_original_widget $@ +} + +# Accept the entire suggestion and execute it +_zsh_autosuggest_execute() { + # Add the suggestion to the buffer + BUFFER="$BUFFER$POSTDISPLAY" + + # Remove the suggestion + unset POSTDISPLAY + + # Call the original `accept-line` to handle syntax highlighting or + # other potential custom behavior + _zsh_autosuggest_invoke_original_widget "accept-line" +} + +# Partially accept the suggestion +_zsh_autosuggest_partial_accept() { + local -i retval + + # 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=$? + + # If we've moved past the end of the original buffer + if [ $CURSOR -gt $#original_buffer ]; then + # Set POSTDISPLAY to text right of the cursor + POSTDISPLAY="$RBUFFER" + + # Clip the buffer at the cursor + BUFFER="$LBUFFER" + else + # Restore the original buffer + BUFFER="$original_buffer" + fi + + return $retval +} + +for action in clear modify fetch suggest accept partial_accept execute enable disable toggle; do + eval "_zsh_autosuggest_widget_$action() { + local -i retval + + _zsh_autosuggest_highlight_reset + + _zsh_autosuggest_$action \$@ + retval=\$? + + _zsh_autosuggest_highlight_apply + + zle -R + + return \$retval + }" +done + +zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch +zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest +zle -N autosuggest-accept _zsh_autosuggest_widget_accept +zle -N autosuggest-clear _zsh_autosuggest_widget_clear +zle -N autosuggest-execute _zsh_autosuggest_widget_execute +zle -N autosuggest-enable _zsh_autosuggest_widget_enable +zle -N autosuggest-disable _zsh_autosuggest_widget_disable +zle -N autosuggest-toggle _zsh_autosuggest_widget_toggle + +#--------------------------------------------------------------------# +# Default Suggestion Strategy # +#--------------------------------------------------------------------# +# Suggests the most recent history item that matches the given +# prefix. +# + +_zsh_autosuggest_strategy_default() { + # Reset options to defaults and enable LOCAL_OPTIONS + emulate -L zsh + + # Enable globbing flags so that we can use (#m) + setopt EXTENDED_GLOB + + # Escape backslashes and all of the glob operators so we can use + # this string as a pattern to search the $history associative array. + # - (#m) globbing flag enables setting references for match data + local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}" + + # Get the history items that match + # - (r) subscript flag makes the pattern match on values + suggestion="${history[(r)$prefix*]}" + +} + +#--------------------------------------------------------------------# +# Match Previous Command Suggestion Strategy # +#--------------------------------------------------------------------# +# Suggests the most recent history item that matches the given +# prefix and whose preceding history item also matches the most +# recently executed command. +# +# For example, suppose your history has the following entries: +# - pwd +# - ls foo +# - ls bar +# - pwd +# +# Given the history list above, when you type 'ls', the suggestion +# will be 'ls foo' rather than 'ls bar' because your most recently +# executed command (pwd) was previously followed by 'ls foo'. +# +# Note that this strategy won't work as expected with ZSH options that don't +# preserve the history order such as `HIST_IGNORE_ALL_DUPS` or +# `HIST_EXPIRE_DUPS_FIRST`. + +_zsh_autosuggest_strategy_match_prev_cmd() { + local prefix="${1//(#m)[\\()\[\]|*?~]/\\$MATCH}" + + # Get all history event numbers that correspond to history + # entries that match pattern $prefix* + local history_match_keys + history_match_keys=(${(k)history[(R)$prefix*]}) + + # By default we use the first history number (most recent history entry) + local histkey="${history_match_keys[1]}" + + # Get the previously executed command + local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")" + + # Iterate up to the first 200 history event numbers that match $prefix + for key in "${(@)history_match_keys[1,200]}"; do + # Stop if we ran out of history + [[ $key -gt 1 ]] || break + + # See if the history entry preceding the suggestion matches the + # previous command, and use it if it does + if [[ "${history[$((key - 1))]}" == "$prev_cmd" ]]; then + histkey="$key" + break + fi + done + + # Give back the matched history entry + suggestion="$history[$histkey]" +} + +#--------------------------------------------------------------------# +# Async # +#--------------------------------------------------------------------# + +# Zpty process is spawned running this function +_zsh_autosuggest_async_server() { + emulate -R zsh + + # There is a bug in zpty module (fixed in zsh/master) by which a + # zpty that exits will kill all zpty processes that were forked + # before it. Here we set up a zsh exit hook to SIGKILL the zpty + # process immediately, before it has a chance to kill any other + # zpty processes. + zshexit() { + kill -KILL $$ + sleep 1 # Block for long enough for the signal to come through + } + + # Output only newlines (not carriage return + newline) + stty -onlcr + + # Silence any error messages + exec 2>/dev/null + + local strategy=$1 + local last_pid + + while IFS='' read -r -d $'\0' query; do + # Kill last bg process + kill -KILL $last_pid &>/dev/null + + # Run suggestion search in the background + ( + local suggestion + _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query" + echo -n -E "$suggestion"$'\0' + ) & + + last_pid=$! + done +} + +_zsh_autosuggest_async_request() { + # Write the query to the zpty process to fetch a suggestion + zpty -w -n $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "${1}"$'\0' +} + +# Called when new data is ready to be read from the pty +# First arg will be fd ready for reading +# Second arg will be passed in case of error +_zsh_autosuggest_async_response() { + setopt LOCAL_OPTIONS EXTENDED_GLOB + + local suggestion + + zpty -rt $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME suggestion '*'$'\0' 2>/dev/null + zle autosuggest-suggest -- "${suggestion%%$'\0'##}" +} + +_zsh_autosuggest_async_pty_create() { + # With newer versions of zsh, REPLY stores the fd to read from + typeset -h REPLY + + # If we won't get a fd back from zpty, try to guess it + if [ $_ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD -eq 0 ]; then + integer -l zptyfd + exec {zptyfd}>&1 # Open a new file descriptor (above 10). + exec {zptyfd}>&- # Close it so it's free to be used by zpty. + fi + + # Fork a zpty process running the server function + zpty -b $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME "_zsh_autosuggest_async_server _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" + + # Store the fd so we can remove the handler later + if (( REPLY )); then + _ZSH_AUTOSUGGEST_PTY_FD=$REPLY + else + _ZSH_AUTOSUGGEST_PTY_FD=$zptyfd + fi + + # Set up input handler from the zpty + zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response +} + +_zsh_autosuggest_async_pty_destroy() { + if zpty -t $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null; then + # Remove the input handler + zle -F $_ZSH_AUTOSUGGEST_PTY_FD &>/dev/null + + # Destroy the zpty + zpty -d $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME &>/dev/null + fi +} + +_zsh_autosuggest_async_pty_recreate() { + _zsh_autosuggest_async_pty_destroy + _zsh_autosuggest_async_pty_create +} + +_zsh_autosuggest_async_start() { + typeset -g _ZSH_AUTOSUGGEST_PTY_FD + + _zsh_autosuggest_feature_detect_zpty_returns_fd + _zsh_autosuggest_async_pty_recreate + + # We recreate the pty to get a fresh list of history events + add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate +} + +#--------------------------------------------------------------------# +# Start # +#--------------------------------------------------------------------# + +# Start the autosuggestion widgets +_zsh_autosuggest_start() { + add-zsh-hook -d precmd _zsh_autosuggest_start + + _zsh_autosuggest_bind_widgets + + # Re-bind widgets on every precmd to ensure we wrap other wrappers. + # Specifically, highlighting breaks if our widgets are wrapped by + # zsh-syntax-highlighting widgets. This also allows modifications + # to the widget list variables to take effect on the next precmd. + add-zsh-hook precmd _zsh_autosuggest_bind_widgets + + if [ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]; then + _zsh_autosuggest_async_start + fi +} + +# Start the autosuggestion widgets on the next precmd +add-zsh-hook precmd _zsh_autosuggest_start diff --git a/.zshrc b/.zshrc index a00940a..2e1839c 100644 --- a/.zshrc +++ b/.zshrc @@ -7,7 +7,7 @@ # Set name of the theme to load. Optionally, if you set this to "random" # it'll load a random theme each time that oh-my-zsh is loaded. # See https://github.com/robbyrussell/oh-my-zsh/wiki/Themes -ZSH_THEME="bureau" +ZSH_THEME="avit" # Uncomment the following line to use case-sensitive completion. # CASE_SENSITIVE="true" @@ -51,7 +51,7 @@ ZSH_THEME="bureau" # Custom plugins may be added to ~/.oh-my-zsh/custom/plugins/ # Example format: plugins=(rails git textmate ruby lighthouse) # Add wisely, as too many plugins slow down shell startup. -plugins=(git dirhistory history systemd tmux vi-mode) +plugins=(git dirhistory history systemd tmux vi-mode zsh-autosuggestions) source $ZSH/oh-my-zsh.sh