mirror of
				https://github.com/zsh-users/zsh-autosuggestions.git
				synced 2025-10-23 15:16:27 +08:00 
			
		
		
		
	Merge pull request #180 from zsh-users/features/async
Asynchronous suggestions
This commit is contained in:
		
						commit
						a109f52fe4
					
				| @ -8,3 +8,7 @@ indent_size = 4 | |||||||
| 
 | 
 | ||||||
| [*.md] | [*.md] | ||||||
| indent_style = space | indent_style = space | ||||||
|  | 
 | ||||||
|  | [*.rb] | ||||||
|  | indent_style = space | ||||||
|  | indent_size = 2 | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @ -1,6 +0,0 @@ | |||||||
| [submodule "vendor/shunit2"] |  | ||||||
| 	path = vendor/shunit2 |  | ||||||
| 	url = https://github.com/kward/shunit2 |  | ||||||
| [submodule "vendor/stub.sh"] |  | ||||||
| 	path = vendor/stub.sh |  | ||||||
| 	url = https://github.com/ericfreese/stub.sh |  | ||||||
							
								
								
									
										30
									
								
								.rubocop.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.rubocop.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | # Rails: | ||||||
|  | #   Enabled: true | ||||||
|  | 
 | ||||||
|  | AllCops: | ||||||
|  | 	TargetRubyVersion: 2.3 | ||||||
|  | 	Include: | ||||||
|  | 		- '**/Rakefile' | ||||||
|  | 		- '**/config.ru' | ||||||
|  | 		- '**/Gemfile' | ||||||
|  | 
 | ||||||
|  | Metrics/LineLength: | ||||||
|  | 	Max: 120 | ||||||
|  | 
 | ||||||
|  | Style/Documentation: | ||||||
|  | 	Enabled: false | ||||||
|  | 
 | ||||||
|  | Style/DotPosition: | ||||||
|  | 	EnforcedStyle: trailing | ||||||
|  | 
 | ||||||
|  | Style/FrozenStringLiteralComment: | ||||||
|  | 	Enabled: false | ||||||
|  | 
 | ||||||
|  | Style/Lambda: | ||||||
|  | 	Enabled: false | ||||||
|  | 
 | ||||||
|  | Style/MultilineMethodCallIndentation: | ||||||
|  | 	EnforcedStyle: indented | ||||||
|  | 
 | ||||||
|  | Style/TrailingUnderscoreVariable: | ||||||
|  | 	Enabled: false | ||||||
							
								
								
									
										1
									
								
								.ruby-version
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.ruby-version
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | 2.3.1 | ||||||
							
								
								
									
										5
									
								
								Gemfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Gemfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | source 'https://rubygems.org' | ||||||
|  | 
 | ||||||
|  | gem 'rspec' | ||||||
|  | gem 'rspec-wait' | ||||||
|  | gem 'pry' | ||||||
							
								
								
									
										37
									
								
								Gemfile.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								Gemfile.lock
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | GEM | ||||||
|  |   remote: https://rubygems.org/ | ||||||
|  |   specs: | ||||||
|  |     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) | ||||||
|  |     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 | ||||||
|  |   rspec | ||||||
|  |   rspec-wait | ||||||
|  | 
 | ||||||
|  | BUNDLED WITH | ||||||
|  |    1.13.6 | ||||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							| @ -1,5 +1,5 @@ | |||||||
| Copyright (c) 2013 Thiago de Arruda | Copyright (c) 2013 Thiago de Arruda | ||||||
| Copyright (c) 2016 Eric Freese | Copyright (c) 2016-2017 Eric Freese | ||||||
| 
 | 
 | ||||||
| Permission is hereby granted, free of charge, to any person | Permission is hereby granted, free of charge, to any person | ||||||
| obtaining a copy of this software and associated documentation | obtaining a copy of this software and associated documentation | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile
									
									
									
									
									
								
							| @ -1,14 +1,16 @@ | |||||||
| SRC_DIR    := ./src | SRC_DIR    := ./src | ||||||
| VENDOR_DIR := ./vendor |  | ||||||
| 
 | 
 | ||||||
| SRC_FILES := \
 | SRC_FILES := \
 | ||||||
|  | 	$(SRC_DIR)/setup.zsh \
 | ||||||
| 	$(SRC_DIR)/config.zsh \
 | 	$(SRC_DIR)/config.zsh \
 | ||||||
|  | 	$(SRC_DIR)/util.zsh \
 | ||||||
|  | 	$(SRC_DIR)/features.zsh \
 | ||||||
| 	$(SRC_DIR)/deprecated.zsh \
 | 	$(SRC_DIR)/deprecated.zsh \
 | ||||||
| 	$(SRC_DIR)/bind.zsh \
 | 	$(SRC_DIR)/bind.zsh \
 | ||||||
| 	$(SRC_DIR)/highlight.zsh \
 | 	$(SRC_DIR)/highlight.zsh \
 | ||||||
| 	$(SRC_DIR)/widgets.zsh \
 | 	$(SRC_DIR)/widgets.zsh \
 | ||||||
| 	$(SRC_DIR)/suggestion.zsh \
 |  | ||||||
| 	$(SRC_DIR)/strategies/*.zsh \
 | 	$(SRC_DIR)/strategies/*.zsh \
 | ||||||
|  | 	$(SRC_DIR)/async.zsh \
 | ||||||
| 	$(SRC_DIR)/start.zsh | 	$(SRC_DIR)/start.zsh | ||||||
| 
 | 
 | ||||||
| HEADER_FILES := \
 | HEADER_FILES := \
 | ||||||
| @ -19,29 +21,16 @@ HEADER_FILES := \ | |||||||
| 
 | 
 | ||||||
| PLUGIN_TARGET := zsh-autosuggestions.zsh | PLUGIN_TARGET := zsh-autosuggestions.zsh | ||||||
| 
 | 
 | ||||||
| SHUNIT2 := $(VENDOR_DIR)/shunit2/2.1.6 |  | ||||||
| STUB_SH := $(VENDOR_DIR)/stub.sh/stub.sh |  | ||||||
| 
 |  | ||||||
| TEST_PREREQS := \
 |  | ||||||
| 	$(SHUNIT2) \
 |  | ||||||
| 	$(STUB_SH) |  | ||||||
| 
 |  | ||||||
| all: $(PLUGIN_TARGET) | all: $(PLUGIN_TARGET) | ||||||
| 
 | 
 | ||||||
| $(PLUGIN_TARGET): $(HEADER_FILES) $(SRC_FILES) | $(PLUGIN_TARGET): $(HEADER_FILES) $(SRC_FILES) | ||||||
| 	cat $(HEADER_FILES) | sed -e 's/^/# /g' > $@ | 	cat $(HEADER_FILES) | sed -e 's/^/# /g' > $@ | ||||||
| 	cat $(SRC_FILES) >> $@ | 	cat $(SRC_FILES) >> $@ | ||||||
| 
 | 
 | ||||||
| $(SHUNIT2): |  | ||||||
| 	git submodule update --init vendor/shunit2 |  | ||||||
| 
 |  | ||||||
| $(STUB_SH): |  | ||||||
| 	git submodule update --init vendor/stub.sh |  | ||||||
| 
 |  | ||||||
| .PHONY: clean | .PHONY: clean | ||||||
| clean: | clean: | ||||||
| 	rm $(PLUGIN_TARGET) | 	rm $(PLUGIN_TARGET) | ||||||
| 
 | 
 | ||||||
| .PHONY: test | .PHONY: test | ||||||
| test: all $(TEST_PREREQS) | test: all | ||||||
| 	script/test_runner.zsh $(TESTS) | 	bundle exec rspec $(TESTS) | ||||||
|  | |||||||
| @ -92,6 +92,10 @@ Widgets that modify the buffer and are not found in any of these arrays will fet | |||||||
| 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. | 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. | This can be useful when pasting large amount of text in the terminal, to avoid triggering autosuggestion for too long strings. | ||||||
| 
 | 
 | ||||||
|  | ### Disable Asynchronous Mode | ||||||
|  | 
 | ||||||
|  | As of `v0.4.0`, suggestions are fetched asynchronously using the `zsh/zpty` module. To disable this behavior and fall back to fetching suggestions synchronously, unset the `ZSH_AUTOSUGGEST_USE_ASYNC` variable. | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| ### Key Bindings | ### Key Bindings | ||||||
| 
 | 
 | ||||||
| @ -154,9 +158,9 @@ Pull requests are welcome! If you send a pull request, please: | |||||||
| 
 | 
 | ||||||
| ### Testing | ### Testing | ||||||
| 
 | 
 | ||||||
| Testing is performed with [`shunit2`](https://github.com/kward/shunit2) (v2.1.6). Documentation can be found [here](http://shunit2.googlecode.com/svn/trunk/source/2.1/doc/shunit2.html). | 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. | ||||||
| 
 | 
 | ||||||
| The test script lives at `script/test_runner.zsh`. To run the tests, run `make test`. | 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 | ## License | ||||||
|  | |||||||
| @ -1,54 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| DIR="${0:a:h}" |  | ||||||
| ROOT_DIR="$DIR/.." |  | ||||||
| TEST_DIR="$ROOT_DIR/test" |  | ||||||
| 
 |  | ||||||
| header() { |  | ||||||
| 	local message="$1" |  | ||||||
| 
 |  | ||||||
| 	cat <<-EOF |  | ||||||
| 
 |  | ||||||
| #====================================================================# |  | ||||||
| # $message |  | ||||||
| #====================================================================# |  | ||||||
| 	EOF |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| # ZSH binary to use |  | ||||||
| local zsh_bin="zsh" |  | ||||||
| 
 |  | ||||||
| while getopts ":z:" opt; do |  | ||||||
| 	case $opt in |  | ||||||
| 		z) |  | ||||||
| 			zsh_bin="$OPTARG" |  | ||||||
| 			;; |  | ||||||
| 		\?) |  | ||||||
| 			echo "Invalid option: -$OPTARG" >&2 |  | ||||||
| 			exit 1 |  | ||||||
| 			;; |  | ||||||
| 		:) |  | ||||||
| 			echo "Option -$OPTARG requires an argument" >&2 |  | ||||||
| 			exit 1 |  | ||||||
| 			;; |  | ||||||
| 	esac |  | ||||||
| done |  | ||||||
| 
 |  | ||||||
| shift $((OPTIND -1)) |  | ||||||
| 
 |  | ||||||
| # Test suites to run |  | ||||||
| local -a tests |  | ||||||
| if [ $#@ -gt 0 ]; then |  | ||||||
| 	tests=($@) |  | ||||||
| else |  | ||||||
| 	tests=($TEST_DIR/**/*_test.zsh) |  | ||||||
| fi |  | ||||||
| 
 |  | ||||||
| local -i retval=0 |  | ||||||
| 
 |  | ||||||
| for suite in $tests; do |  | ||||||
| 	header "${suite#"$ROOT_DIR/"}" |  | ||||||
| 	"$zsh_bin" -f "$suite" || retval=$? |  | ||||||
| done |  | ||||||
| 
 |  | ||||||
| exit $retval |  | ||||||
							
								
								
									
										10
									
								
								spec/integrations/client_zpty_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								spec/integrations/client_zpty_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | describe 'a running zpty command' do | ||||||
|  |   let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } } | ||||||
|  | 
 | ||||||
|  |   it 'is not affected by running zsh-autosuggestions' do | ||||||
|  |     sleep 1 # Give a little time for precmd hooks to run | ||||||
|  |     session.run_command('zpty -t kitty; echo $?') | ||||||
|  | 
 | ||||||
|  |     wait_for(session.content).to end_with("\n0") | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										13
									
								
								spec/multi_line_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								spec/multi_line_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | describe 'a multi-line suggestion' do | ||||||
|  |   it 'should be displayed on multiple lines' do | ||||||
|  |     with_history(-> { | ||||||
|  |       session.send_string('echo "') | ||||||
|  |       session.send_keys('enter') | ||||||
|  |       session.send_string('"') | ||||||
|  |       session.send_keys('enter') | ||||||
|  |     }) do | ||||||
|  |       session.send_keys('e') | ||||||
|  |       wait_for { session.content }.to eq("echo \"\n\"") | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										15
									
								
								spec/options/async_zpty_name_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								spec/options/async_zpty_name_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | 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) { ['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 | ||||||
							
								
								
									
										30
									
								
								spec/options/buffer_max_size_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								spec/options/buffer_max_size_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | describe 'a suggestion' do | ||||||
|  |   let(:term_opts) { { width: 200 } } | ||||||
|  |   let(:long_command) { "echo #{'a' * 100}" } | ||||||
|  | 
 | ||||||
|  |   around do |example| | ||||||
|  |     with_history(long_command) { example.run } | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   it 'is provided for any buffer length' do | ||||||
|  |     session.send_string(long_command[0...-1]) | ||||||
|  |     wait_for { session.content }.to eq(long_command) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   context 'when ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE is specified' do | ||||||
|  |     let(:buffer_max_size) { 10 } | ||||||
|  |     let(:options) { ["ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=#{buffer_max_size}"] } | ||||||
|  | 
 | ||||||
|  |     it 'is provided when the buffer is shorter than the specified length' do | ||||||
|  |       session.send_string(long_command[0...(buffer_max_size - 1)]) | ||||||
|  |       wait_for { session.content }.to eq(long_command) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'is provided when the buffer is equal to the specified length' do | ||||||
|  |       session.send_string(long_command[0...(buffer_max_size)]) | ||||||
|  |       wait_for { session.content }.to eq(long_command) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'is not provided when the buffer is longer than the specified length' | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										7
									
								
								spec/options/highlight_style_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								spec/options/highlight_style_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | describe 'a displayed suggestion' do | ||||||
|  |   it 'is shown in the default style' | ||||||
|  | 
 | ||||||
|  |   describe 'when ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE is set to a zle_highlight string' do | ||||||
|  |     it 'is shown in the specified style' | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										7
									
								
								spec/options/original_widget_prefix_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								spec/options/original_widget_prefix_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | describe 'an original zle widget' do | ||||||
|  |   context 'is accessible with the default prefix' | ||||||
|  | 
 | ||||||
|  |   context 'when ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX is set' do | ||||||
|  |     it 'is accessible with the specified prefix' | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										20
									
								
								spec/options/strategy_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								spec/options/strategy_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
							
								
								
									
										7
									
								
								spec/options/use_async_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								spec/options/use_async_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | describe 'suggestion fetching' do | ||||||
|  |   it 'is performed asynchronously' | ||||||
|  | 
 | ||||||
|  |   context 'when ZSH_AUTOSUGGEST_USE_ASYNC is unset' do | ||||||
|  |     it 'is performed synchronously' | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										72
									
								
								spec/options/widget_lists_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								spec/options/widget_lists_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | describe 'a zle widget' do | ||||||
|  |   let(:before_sourcing) { -> { session.run_command('my-widget() {}; zle -N my-widget; bindkey ^B my-widget') } } | ||||||
|  | 
 | ||||||
|  |   context 'when added to ZSH_AUTOSUGGEST_ACCEPT_WIDGETS' do | ||||||
|  |     let(:options) { ['ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(my-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=(my-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=(my-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 | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | describe 'a zle widget that moves the cursor forward' do | ||||||
|  |   let(:before_sourcing) { -> { session.run_command('my-widget() { zle forward-char }; zle -N my-widget; bindkey ^B my-widget') } } | ||||||
|  | 
 | ||||||
|  |   context 'when added to ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS' do | ||||||
|  |     let(:options) { ['ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(my-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 | ||||||
|  | 
 | ||||||
|  | describe 'a builtin zle widget' do | ||||||
|  |   let(:widget) { 'beep' } | ||||||
|  | 
 | ||||||
|  |   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("\nbuiltin") | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										50
									
								
								spec/spec_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								spec/spec_helper.rb
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
							
								
								
									
										55
									
								
								spec/special_characters_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								spec/special_characters_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | 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 | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										8
									
								
								spec/strategies/default_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								spec/strategies/default_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
							
								
								
									
										17
									
								
								spec/strategies/match_prev_cmd_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								spec/strategies/match_prev_cmd_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
							
								
								
									
										84
									
								
								spec/terminal_session.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								spec/terminal_session.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | |||||||
|  | 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 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 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_socket_name | ||||||
|  |     @tmux_socket_name ||= SecureRandom.hex(6) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def tmux_command(cmd) | ||||||
|  |     out = `tmux -u -L #{tmux_socket_name} #{cmd}` | ||||||
|  | 
 | ||||||
|  |     raise("tmux error running: '#{cmd}'") unless $?.success? | ||||||
|  | 
 | ||||||
|  |     out | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										109
									
								
								src/async.zsh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/async.zsh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,109 @@ | |||||||
|  | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Async                                                              # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | 
 | ||||||
|  | # Zpty process is spawned running this function | ||||||
|  | _zsh_autosuggest_async_server() { | ||||||
|  | 	emulate -R zsh | ||||||
|  | 
 | ||||||
|  | 	# There is a bug in zpty module (fixed in zsh/master) by which a | ||||||
|  | 	# zpty that exits will kill all zpty processes that were forked | ||||||
|  | 	# before it. Here we set up a zsh exit hook to SIGKILL the zpty | ||||||
|  | 	# process immediately, before it has a chance to kill any other | ||||||
|  | 	# zpty processes. | ||||||
|  | 	zshexit() { | ||||||
|  | 		kill -KILL $$ | ||||||
|  | 		sleep 1 # Block for long enough for the signal to come through | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	# 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 [ -n "$_ZSH_AUTOSUGGEST_PTY_FD" ]; then | ||||||
|  | 		# Remove the input handler | ||||||
|  | 		zle -F $_ZSH_AUTOSUGGEST_PTY_FD | ||||||
|  | 
 | ||||||
|  | 		# 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_async_pty_create | ||||||
|  | 
 | ||||||
|  | 	# We recreate the pty to get a fresh list of history events | ||||||
|  | 	add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate | ||||||
|  | } | ||||||
| @ -60,3 +60,9 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( | |||||||
| 
 | 
 | ||||||
| # Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. | # Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. | ||||||
| ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= | ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= | ||||||
|  | 
 | ||||||
|  | # Use asynchronous mode by default. Unset this variable to use sync mode. | ||||||
|  | ZSH_AUTOSUGGEST_USE_ASYNC= | ||||||
|  | 
 | ||||||
|  | # Pty name for calculating autosuggestions asynchronously | ||||||
|  | ZSH_AUTOSUGGEST_ASYNC_PTY_NAME=zsh_autosuggest_pty | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								src/features.zsh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/features.zsh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Feature Detection                                                  # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_feature_detect() { | ||||||
|  | 	typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD | ||||||
|  | 	typeset -h REPLY | ||||||
|  | 
 | ||||||
|  | 	zpty $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME '{ 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_ASYNC_PTY_NAME | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/setup.zsh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/setup.zsh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Setup                                                              # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | 
 | ||||||
|  | # Precmd hooks for initializing the library and starting pty's | ||||||
|  | autoload -Uz add-zsh-hook | ||||||
|  | 
 | ||||||
|  | # Asynchronous suggestions are generated in a pty | ||||||
|  | zmodload zsh/zpty | ||||||
| @ -5,9 +5,16 @@ | |||||||
| 
 | 
 | ||||||
| # Start the autosuggestion widgets | # Start the autosuggestion widgets | ||||||
| _zsh_autosuggest_start() { | _zsh_autosuggest_start() { | ||||||
|  | 	add-zsh-hook -d precmd _zsh_autosuggest_start | ||||||
|  | 
 | ||||||
|  | 	_zsh_autosuggest_feature_detect | ||||||
| 	_zsh_autosuggest_check_deprecated_config | 	_zsh_autosuggest_check_deprecated_config | ||||||
| 	_zsh_autosuggest_bind_widgets | 	_zsh_autosuggest_bind_widgets | ||||||
|  | 
 | ||||||
|  | 	if [ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]; then | ||||||
|  | 		_zsh_autosuggest_async_start | ||||||
|  | 	fi | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| autoload -Uz add-zsh-hook | # Start the autosuggestion widgets on the next precmd | ||||||
| add-zsh-hook precmd _zsh_autosuggest_start | add-zsh-hook precmd _zsh_autosuggest_start | ||||||
|  | |||||||
| @ -7,5 +7,19 @@ | |||||||
| # | # | ||||||
| 
 | 
 | ||||||
| _zsh_autosuggest_strategy_default() { | _zsh_autosuggest_strategy_default() { | ||||||
| 	fc -lnrm "$1*" 1 2>/dev/null | head -n 1 | 	# 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*]}" | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ | |||||||
| # `HIST_EXPIRE_DUPS_FIRST`. | # `HIST_EXPIRE_DUPS_FIRST`. | ||||||
| 
 | 
 | ||||||
| _zsh_autosuggest_strategy_match_prev_cmd() { | _zsh_autosuggest_strategy_match_prev_cmd() { | ||||||
| 	local prefix="$1" | 	local prefix="${1//(#m)[\\()\[\]|*?~]/\\$MATCH}" | ||||||
| 
 | 
 | ||||||
| 	# Get all history event numbers that correspond to history | 	# Get all history event numbers that correspond to history | ||||||
| 	# entries that match pattern $prefix* | 	# entries that match pattern $prefix* | ||||||
| @ -47,6 +47,6 @@ _zsh_autosuggest_strategy_match_prev_cmd() { | |||||||
| 		fi | 		fi | ||||||
| 	done | 	done | ||||||
| 
 | 
 | ||||||
| 	# Echo the matched history entry | 	# Give back the matched history entry | ||||||
| 	echo -E "$history[$histkey]" | 	suggestion="$history[$histkey]" | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,21 +0,0 @@ | |||||||
| 
 |  | ||||||
| #--------------------------------------------------------------------# |  | ||||||
| # Suggestion                                                         # |  | ||||||
| #--------------------------------------------------------------------# |  | ||||||
| 
 |  | ||||||
| # Delegate to the selected strategy to determine a suggestion |  | ||||||
| _zsh_autosuggest_suggestion() { |  | ||||||
| 	local escaped_prefix="$(_zsh_autosuggest_escape_command "$1")" |  | ||||||
| 	local strategy_function="_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" |  | ||||||
| 
 |  | ||||||
| 	if [ -n "$functions[$strategy_function]" ]; then |  | ||||||
| 		echo -E "$($strategy_function "$escaped_prefix")" |  | ||||||
| 	fi |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| _zsh_autosuggest_escape_command() { |  | ||||||
| 	setopt localoptions EXTENDED_GLOB |  | ||||||
| 
 |  | ||||||
| 	# Escape special chars in the string (requires EXTENDED_GLOB) |  | ||||||
| 	echo -E "${1//(#m)[\\()\[\]|*?~]/\\$MATCH}" |  | ||||||
| } |  | ||||||
							
								
								
									
										11
									
								
								src/util.zsh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/util.zsh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Utility Functions                                                  # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | 
 | ||||||
|  | _zsh_autosuggest_escape_command() { | ||||||
|  | 	setopt localoptions EXTENDED_GLOB | ||||||
|  | 
 | ||||||
|  | 	# Escape special chars in the string (requires EXTENDED_GLOB) | ||||||
|  | 	echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}" | ||||||
|  | } | ||||||
| @ -19,13 +19,24 @@ _zsh_autosuggest_modify() { | |||||||
| 	local orig_buffer="$BUFFER" | 	local orig_buffer="$BUFFER" | ||||||
| 	local orig_postdisplay="$POSTDISPLAY" | 	local orig_postdisplay="$POSTDISPLAY" | ||||||
| 
 | 
 | ||||||
| 	# Clear suggestion while original widget runs | 	# Clear suggestion while waiting for next one | ||||||
| 	unset POSTDISPLAY | 	unset POSTDISPLAY | ||||||
| 
 | 
 | ||||||
| 	# Original widget may modify the buffer | 	# Original widget may modify the buffer | ||||||
| 	_zsh_autosuggest_invoke_original_widget $@ | 	_zsh_autosuggest_invoke_original_widget $@ | ||||||
| 	retval=$? | 	retval=$? | ||||||
| 
 | 
 | ||||||
|  | 	# 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 | 	# Don't fetch a new suggestion if the buffer hasn't changed | ||||||
| 	if [ "$BUFFER" = "$orig_buffer" ]; then | 	if [ "$BUFFER" = "$orig_buffer" ]; then | ||||||
| 		POSTDISPLAY="$orig_postdisplay" | 		POSTDISPLAY="$orig_postdisplay" | ||||||
| @ -33,21 +44,37 @@ _zsh_autosuggest_modify() { | |||||||
| 	fi | 	fi | ||||||
| 
 | 
 | ||||||
| 	# Get a new suggestion if the buffer is not empty after modification | 	# Get a new suggestion if the buffer is not empty after modification | ||||||
| 	local suggestion |  | ||||||
| 	if [ $#BUFFER -gt 0 ]; then | 	if [ $#BUFFER -gt 0 ]; then | ||||||
| 		if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -lt "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then | 		if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -le "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then | ||||||
| 			suggestion="$(_zsh_autosuggest_suggestion "$BUFFER")" | 			_zsh_autosuggest_fetch | ||||||
| 		fi | 		fi | ||||||
| 	fi | 	fi | ||||||
| 
 | 
 | ||||||
| 	# Add the suggestion to the POSTDISPLAY |  | ||||||
| 	if [ -n "$suggestion" ]; then |  | ||||||
| 		POSTDISPLAY="${suggestion#$BUFFER}" |  | ||||||
| 	fi |  | ||||||
| 
 |  | ||||||
| 	return $retval | 	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 | # Accept the entire suggestion | ||||||
| _zsh_autosuggest_accept() { | _zsh_autosuggest_accept() { | ||||||
| 	local -i max_cursor_pos=$#BUFFER | 	local -i max_cursor_pos=$#BUFFER | ||||||
| @ -115,7 +142,7 @@ _zsh_autosuggest_partial_accept() { | |||||||
| 	return $retval | 	return $retval | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| for action in clear modify accept partial_accept execute; do | for action in clear modify fetch suggest accept partial_accept execute; do | ||||||
| 	eval "_zsh_autosuggest_widget_$action() { | 	eval "_zsh_autosuggest_widget_$action() { | ||||||
| 		local -i retval | 		local -i retval | ||||||
| 
 | 
 | ||||||
| @ -126,10 +153,14 @@ for action in clear modify accept partial_accept execute; do | |||||||
| 
 | 
 | ||||||
| 		_zsh_autosuggest_highlight_apply | 		_zsh_autosuggest_highlight_apply | ||||||
| 
 | 
 | ||||||
|  | 		zle -R | ||||||
|  | 
 | ||||||
| 		return \$retval | 		return \$retval | ||||||
| 	}" | 	}" | ||||||
| done | 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-accept _zsh_autosuggest_widget_accept | ||||||
| zle -N autosuggest-clear _zsh_autosuggest_widget_clear | zle -N autosuggest-clear _zsh_autosuggest_widget_clear | ||||||
| zle -N autosuggest-execute _zsh_autosuggest_widget_execute | zle -N autosuggest-execute _zsh_autosuggest_widget_execute | ||||||
|  | |||||||
| @ -1,45 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testInvokeOriginalWidgetDefined() { |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		zle \ |  | ||||||
| 		'return 1' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_invoke_original_widget 'self-insert' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'1' \ |  | ||||||
| 		"$?" |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'zle was not invoked' \ |  | ||||||
| 		'stub_called zle' |  | ||||||
| 
 |  | ||||||
| 	restore zle |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testInvokeOriginalWidgetUndefined() { |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		zle \ |  | ||||||
| 		'return 1' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_invoke_original_widget 'some-undefined-widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'0' \ |  | ||||||
| 		"$?" |  | ||||||
| 
 |  | ||||||
| 	assertFalse \ |  | ||||||
| 		'zle was invoked' \ |  | ||||||
| 		'stub_called zle' |  | ||||||
| 
 |  | ||||||
| 	restore zle |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,73 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testHighlightDefaultStyle() { |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'fg=8' \ |  | ||||||
| 		"$ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testHighlightApplyWithSuggestion() { |  | ||||||
| 	local orig_style=ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE |  | ||||||
| 	ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=4' |  | ||||||
| 
 |  | ||||||
| 	BUFFER='ec' |  | ||||||
| 	POSTDISPLAY='ho hello' |  | ||||||
| 	region_highlight=('0 2 fg=1') |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_highlight_apply |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'highlight did not use correct style' \ |  | ||||||
| 		"0 2 fg=1 2 10 $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" \ |  | ||||||
| 		"$region_highlight" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'higlight was not saved to be removed later' \ |  | ||||||
| 		"2 10 $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" \ |  | ||||||
| 		"$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" |  | ||||||
| 
 |  | ||||||
| 	ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=orig_style |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testHighlightApplyWithoutSuggestion() { |  | ||||||
| 	BUFFER='echo hello' |  | ||||||
| 	POSTDISPLAY='' |  | ||||||
| 	region_highlight=('0 4 fg=1') |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_highlight_apply |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'region_highlight was modified' \ |  | ||||||
| 		'0 4 fg=1' \ |  | ||||||
| 		"$region_highlight" |  | ||||||
| 
 |  | ||||||
| 	assertNull \ |  | ||||||
| 		'last highlight region was not cleared' \ |  | ||||||
| 		"$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testHighlightReset() { |  | ||||||
| 	BUFFER='ec' |  | ||||||
| 	POSTDISPLAY='ho hello' |  | ||||||
| 	region_highlight=('0 1 fg=1' '2 10 fg=8' '1 2 fg=1') |  | ||||||
| 	_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT='2 10 fg=8' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_highlight_reset |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'last highlight region was not removed' \ |  | ||||||
| 		'0 1 fg=1 1 2 fg=1' \ |  | ||||||
| 		"$region_highlight" |  | ||||||
| 
 |  | ||||||
| 	assertNull \ |  | ||||||
| 		'last highlight variable was not cleared' \ |  | ||||||
| 		"$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,56 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/../test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testNoMatch() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		ls foo |  | ||||||
| 		ls bar |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'foo' \ |  | ||||||
| 		'' |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls q' \ |  | ||||||
| 		'' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testBasicMatches() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		ls foo |  | ||||||
| 		ls bar |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls f' \ |  | ||||||
| 		'ls foo' |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls b' \ |  | ||||||
| 		'ls bar' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testMostRecentMatch() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		ls foo |  | ||||||
| 		cd bar |  | ||||||
| 		ls baz |  | ||||||
| 		cd quux |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls' \ |  | ||||||
| 		'ls baz' |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'cd' \ |  | ||||||
| 		'cd quux' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,74 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/../test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| 
 |  | ||||||
| 	ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testNoMatch() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		ls foo |  | ||||||
| 		ls bar |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'foo' \ |  | ||||||
| 		'' |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls q' \ |  | ||||||
| 		'' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testBasicMatches() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		ls foo |  | ||||||
| 		ls bar |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls f' \ |  | ||||||
| 		'ls foo' |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls b' \ |  | ||||||
| 		'ls bar' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testMostRecentMatch() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		ls foo |  | ||||||
| 		cd bar |  | ||||||
| 		ls baz |  | ||||||
| 		cd quux |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls' \ |  | ||||||
| 		'ls baz' |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'cd' \ |  | ||||||
| 		'cd quux' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testMatchMostRecentAfterPreviousCmd() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		echo what |  | ||||||
| 		ls foo |  | ||||||
| 		ls bar |  | ||||||
| 		echo what |  | ||||||
| 		ls baz |  | ||||||
| 		ls quux |  | ||||||
| 		echo what |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'ls' \ |  | ||||||
| 		'ls baz' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,102 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| assertBackslashSuggestion() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		echo "hello\nworld" |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'echo "hello\' \ |  | ||||||
| 		'echo "hello\nworld"' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| assertDoubleBackslashSuggestion() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		echo "\\" |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'echo "\\' \ |  | ||||||
| 		'echo "\\"' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| assertTildeSuggestion() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		cd ~/something |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'cd' \ |  | ||||||
| 		'cd ~/something' |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'cd ~' \ |  | ||||||
| 		'cd ~/something' |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'cd ~/s' \ |  | ||||||
| 		'cd ~/something' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| assertTildeSuggestionWithExtendedGlob() { |  | ||||||
| 	setopt local_options extended_glob |  | ||||||
| 
 |  | ||||||
| 	assertTildeSuggestion |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| assertParenthesesSuggestion() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		echo "$(ls foo)" |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'echo "$(' \ |  | ||||||
| 		'echo "$(ls foo)"' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| assertSquareBracketsSuggestion() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		echo "$history[123]" |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'echo "$history[' \ |  | ||||||
| 		'echo "$history[123]"' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| assertHashSuggestion() { |  | ||||||
| 	set_history <<-'EOF' |  | ||||||
| 		echo "#yolo" |  | ||||||
| 	EOF |  | ||||||
| 
 |  | ||||||
| 	assertSuggestion \ |  | ||||||
| 		'echo "#' \ |  | ||||||
| 		'echo "#yolo"' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testSpecialCharsForAllStrategies() { |  | ||||||
| 	local strategies |  | ||||||
| 	strategies=( |  | ||||||
| 		"default" |  | ||||||
| 		"match_prev_cmd" |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	for s in $strategies; do |  | ||||||
| 		ZSH_AUTOSUGGEST_STRATEGY="$s" |  | ||||||
| 
 |  | ||||||
| 		assertBackslashSuggestion |  | ||||||
| 		assertDoubleBackslashSuggestion |  | ||||||
| 		assertTildeSuggestion |  | ||||||
| 		assertTildeSuggestionWithExtendedGlob |  | ||||||
| 		assertParenthesesSuggestion |  | ||||||
| 		assertSquareBracketsSuggestion |  | ||||||
| 	done |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,46 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testEscapeCommand() { |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not escape single backslash' \ |  | ||||||
| 		'\\' \ |  | ||||||
| 		"$(_zsh_autosuggest_escape_command '\')" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not escape two backslashes' \ |  | ||||||
| 		'\\\\' \ |  | ||||||
| 		"$(_zsh_autosuggest_escape_command '\\')" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not escape parentheses' \ |  | ||||||
| 		'\(\)' \ |  | ||||||
| 		"$(_zsh_autosuggest_escape_command '()')" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not escape square brackets' \ |  | ||||||
| 		'\[\]' \ |  | ||||||
| 		"$(_zsh_autosuggest_escape_command '[]')" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not escape pipe' \ |  | ||||||
| 		'\|' \ |  | ||||||
| 		"$(_zsh_autosuggest_escape_command '|')" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not escape star' \ |  | ||||||
| 		'\*' \ |  | ||||||
| 		"$(_zsh_autosuggest_escape_command '*')" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not escape question mark' \ |  | ||||||
| 		'\?' \ |  | ||||||
| 		"$(_zsh_autosuggest_escape_command '?')" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,60 +0,0 @@ | |||||||
| DIR="${0:a:h}" |  | ||||||
| ROOT_DIR="$DIR/.." |  | ||||||
| VENDOR_DIR="$ROOT_DIR/vendor" |  | ||||||
| 
 |  | ||||||
| # Use stub.sh for stubbing/mocking |  | ||||||
| source "$VENDOR_DIR/stub.sh/stub.sh" |  | ||||||
| 
 |  | ||||||
| #--------------------------------------------------------------------# |  | ||||||
| # Helper Functions                                                   # |  | ||||||
| #--------------------------------------------------------------------# |  | ||||||
| 
 |  | ||||||
| # Source the autosuggestions plugin file |  | ||||||
| source_autosuggestions() { |  | ||||||
| 	source "$ROOT_DIR/zsh-autosuggestions.zsh" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| # Set history list from stdin |  | ||||||
| set_history() { |  | ||||||
| 	# Make a tmp file in shunit's tmp dir |  | ||||||
| 	local tmp=$(mktemp "$SHUNIT_TMPDIR/hist.XXX") |  | ||||||
| 
 |  | ||||||
| 	# Write from stdin to the tmp file |  | ||||||
| 	> "$tmp" |  | ||||||
| 
 |  | ||||||
| 	# Write an extra line to simulate history active mode |  | ||||||
| 	# See https://github.com/zsh-users/zsh/blob/ca3bc0d95d7deab4f5381f12b15047de748c0814/Src/hist.c#L69-L82 |  | ||||||
| 	echo >> "$tmp" |  | ||||||
| 
 |  | ||||||
| 	# Clear history and re-read from the tmp file |  | ||||||
| 	fc -P; fc -p; fc -R "$tmp" |  | ||||||
| 
 |  | ||||||
| 	rm "$tmp" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| # Should be called at the bottom of every test suite file |  | ||||||
| # Pass in the name of the test script ($0) for shunit |  | ||||||
| run_tests() { |  | ||||||
| 	local test_script="$1" |  | ||||||
| 	shift |  | ||||||
| 
 |  | ||||||
| 	# Required for shunit to work with zsh |  | ||||||
| 	setopt localoptions shwordsplit |  | ||||||
| 	SHUNIT_PARENT="$test_script" |  | ||||||
| 
 |  | ||||||
| 	source "$VENDOR_DIR/shunit2/2.1.6/src/shunit2" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #--------------------------------------------------------------------# |  | ||||||
| # Custom Assertions                                                  # |  | ||||||
| #--------------------------------------------------------------------# |  | ||||||
| 
 |  | ||||||
| assertSuggestion() { |  | ||||||
| 	local prefix="$1" |  | ||||||
| 	local expected_suggestion="$2" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		"Did not get correct suggestion for prefix:<$prefix> using strategy <$ZSH_AUTOSUGGEST_STRATEGY>" \ |  | ||||||
| 		"$expected_suggestion" \ |  | ||||||
| 		"$(_zsh_autosuggest_suggestion "$prefix")" |  | ||||||
| } |  | ||||||
| @ -1,161 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/../test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| setUp() { |  | ||||||
| 	BUFFER='' |  | ||||||
| 	POSTDISPLAY='' |  | ||||||
| 	CURSOR=0 |  | ||||||
| 	KEYMAP='main' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| tearDown() { |  | ||||||
| 	restore _zsh_autosuggest_invoke_original_widget |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testCursorAtEnd() { |  | ||||||
| 	BUFFER='echo' |  | ||||||
| 	POSTDISPLAY=' hello' |  | ||||||
| 	CURSOR=4 |  | ||||||
| 
 |  | ||||||
| 	stub _zsh_autosuggest_invoke_original_widget |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_accept 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'original widget not invoked' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_invoke_original_widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was not modified' \ |  | ||||||
| 		'echo hello' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'POSTDISPLAY was not cleared' \ |  | ||||||
| 		'' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testCursorNotAtEnd() { |  | ||||||
| 	BUFFER='echo' |  | ||||||
| 	POSTDISPLAY=' hello' |  | ||||||
| 	CURSOR=2 |  | ||||||
| 
 |  | ||||||
| 	stub _zsh_autosuggest_invoke_original_widget |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_accept 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'original widget not invoked' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_invoke_original_widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was modified' \ |  | ||||||
| 		'echo' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'POSTDISPLAY was modified' \ |  | ||||||
| 		' hello' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testViCursorAtEnd() { |  | ||||||
| 	BUFFER='echo' |  | ||||||
| 	POSTDISPLAY=' hello' |  | ||||||
| 	CURSOR=3 |  | ||||||
| 	KEYMAP='vicmd' |  | ||||||
| 
 |  | ||||||
| 	stub _zsh_autosuggest_invoke_original_widget |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_accept 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'original widget not invoked' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_invoke_original_widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was not modified' \ |  | ||||||
| 		'echo hello' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'POSTDISPLAY was not cleared' \ |  | ||||||
| 		'' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testViCursorNotAtEnd() { |  | ||||||
| 	BUFFER='echo' |  | ||||||
| 	POSTDISPLAY=' hello' |  | ||||||
| 	CURSOR=2 |  | ||||||
| 	KEYMAP='vicmd' |  | ||||||
| 
 |  | ||||||
| 	stub _zsh_autosuggest_invoke_original_widget |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_accept 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'original widget not invoked' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_invoke_original_widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was modified' \ |  | ||||||
| 		'echo' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'POSTDISPLAY was modified' \ |  | ||||||
| 		' hello' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testRetval() { |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'return 1' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_widget_accept 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not return correct value from original widget' \ |  | ||||||
| 		'1' \ |  | ||||||
| 		"$?" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testWidget() { |  | ||||||
| 	stub _zsh_autosuggest_highlight_reset |  | ||||||
| 	stub _zsh_autosuggest_accept |  | ||||||
| 	stub _zsh_autosuggest_highlight_apply |  | ||||||
| 
 |  | ||||||
| 	# Call the function pointed to by the widget since we can't call |  | ||||||
| 	# the widget itself when zle is not active |  | ||||||
| 	${widgets[autosuggest-accept]#*:} 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'autosuggest-accept widget does not exist' \ |  | ||||||
| 		'zle -l autosuggest-accept' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'highlight_reset was not called' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_highlight_reset' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'widget function was not called' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_accept' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'highlight_apply was not called' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_highlight_apply' |  | ||||||
| 
 |  | ||||||
| 	restore _zsh_autosuggest_highlight_reset |  | ||||||
| 	restore _zsh_autosuggest_accept |  | ||||||
| 	restore _zsh_autosuggest_highlight_apply |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,77 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/../test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| setUp() { |  | ||||||
| 	BUFFER='' |  | ||||||
| 	POSTDISPLAY='' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| tearDown() { |  | ||||||
| 	restore _zsh_autosuggest_invoke_original_widget |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testClear() { |  | ||||||
| 	BUFFER='ec' |  | ||||||
| 	POSTDISPLAY='ho hello' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_clear 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was modified' \ |  | ||||||
| 		'ec' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertNull \ |  | ||||||
| 		'POSTDISPLAY was not cleared' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testRetval() { |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'return 1' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_widget_clear 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not return correct value from original widget' \ |  | ||||||
| 		'1' \ |  | ||||||
| 		"$?" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testWidget() { |  | ||||||
| 	stub _zsh_autosuggest_highlight_reset |  | ||||||
| 	stub _zsh_autosuggest_clear |  | ||||||
| 	stub _zsh_autosuggest_highlight_apply |  | ||||||
| 
 |  | ||||||
| 	# Call the function pointed to by the widget since we can't call |  | ||||||
| 	# the widget itself when zle is not active |  | ||||||
| 	${widgets[autosuggest-clear]#*:} 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'autosuggest-clear widget does not exist' \ |  | ||||||
| 		'zle -l autosuggest-clear' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'highlight_reset was not called' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_highlight_reset' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'widget function was not called' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_clear' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'highlight_apply was not called' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_highlight_apply' |  | ||||||
| 
 |  | ||||||
| 	restore _zsh_autosuggest_highlight_reset |  | ||||||
| 	restore _zsh_autosuggest_clear |  | ||||||
| 	restore _zsh_autosuggest_highlight_apply |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/../test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| tearDown() { |  | ||||||
| 	restore _zsh_autosuggest_invoke_original_widget |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testRetval() { |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'return 1' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_widget_execute 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not return correct value from original widget' \ |  | ||||||
| 		'1' \ |  | ||||||
| 		"$?" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,88 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/../test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| setUp() { |  | ||||||
| 	BUFFER='' |  | ||||||
| 	POSTDISPLAY='' |  | ||||||
| 	ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE='' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| tearDown() { |  | ||||||
| 	restore _zsh_autosuggest_invoke_original_widget |  | ||||||
| 	restore _zsh_autosuggest_suggestion |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testModify() { |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'BUFFER+="e"' |  | ||||||
| 
 |  | ||||||
| 	stub_and_echo \ |  | ||||||
| 		_zsh_autosuggest_suggestion \ |  | ||||||
| 		'echo hello' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_modify 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'original widget not invoked' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_invoke_original_widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was not modified' \ |  | ||||||
| 		'e' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'POSTDISPLAY does not contain suggestion' \ |  | ||||||
| 		'cho hello' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testModifyBufferTooLarge() { |  | ||||||
| 
 |  | ||||||
| 	ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE='20' |  | ||||||
| 
 |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'BUFFER+="012345678901234567890"' |  | ||||||
| 
 |  | ||||||
| 	stub_and_echo \ |  | ||||||
| 		_zsh_autosuggest_suggestion \ |  | ||||||
| 		'012345678901234567890123456789' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_modify 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'original widget not invoked' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_invoke_original_widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was not modified' \ |  | ||||||
| 		'012345678901234567890' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'POSTDISPLAY does not contain suggestion' \ |  | ||||||
| 		'' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testRetval() { |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'return 1' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_widget_modify 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not return correct value from original widget' \ |  | ||||||
| 		'1' \ |  | ||||||
| 		"$?" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
| @ -1,84 +0,0 @@ | |||||||
| #!/usr/bin/env zsh |  | ||||||
| 
 |  | ||||||
| source "${0:a:h}/../test_helper.zsh" |  | ||||||
| 
 |  | ||||||
| oneTimeSetUp() { |  | ||||||
| 	source_autosuggestions |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| setUp() { |  | ||||||
| 	BUFFER='' |  | ||||||
| 	POSTDISPLAY='' |  | ||||||
| 	CURSOR=0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| tearDown() { |  | ||||||
| 	restore _zsh_autosuggest_invoke_original_widget |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testCursorMovesOutOfBuffer() { |  | ||||||
| 	BUFFER='ec' |  | ||||||
| 	POSTDISPLAY='ho hello' |  | ||||||
| 	CURSOR=1 |  | ||||||
| 
 |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'CURSOR=5; LBUFFER="echo "; RBUFFER="hello"' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_partial_accept 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'original widget not invoked' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_invoke_original_widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was not modified correctly' \ |  | ||||||
| 		'echo ' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'POSTDISPLAY was not modified correctly' \ |  | ||||||
| 		'hello' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testCursorStaysInBuffer() { |  | ||||||
| 	BUFFER='echo hello' |  | ||||||
| 	POSTDISPLAY=' world' |  | ||||||
| 	CURSOR=1 |  | ||||||
| 
 |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'CURSOR=5; LBUFFER="echo "; RBUFFER="hello"' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_partial_accept 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertTrue \ |  | ||||||
| 		'original widget not invoked' \ |  | ||||||
| 		'stub_called _zsh_autosuggest_invoke_original_widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'BUFFER was modified' \ |  | ||||||
| 		'echo hello' \ |  | ||||||
| 		"$BUFFER" |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'POSTDISPLAY was modified' \ |  | ||||||
| 		' world' \ |  | ||||||
| 		"$POSTDISPLAY" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| testRetval() { |  | ||||||
| 	stub_and_eval \ |  | ||||||
| 		_zsh_autosuggest_invoke_original_widget \ |  | ||||||
| 		'return 1' |  | ||||||
| 
 |  | ||||||
| 	_zsh_autosuggest_widget_partial_accept 'original-widget' |  | ||||||
| 
 |  | ||||||
| 	assertEquals \ |  | ||||||
| 		'Did not return correct value from original widget' \ |  | ||||||
| 		'1' \ |  | ||||||
| 		"$?" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| run_tests "$0" |  | ||||||
							
								
								
									
										1
									
								
								vendor/shunit2
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								vendor/shunit2
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | |||||||
| Subproject commit 46973db9df87bd5fdadea74cb472a99f212a0d3a |  | ||||||
							
								
								
									
										1
									
								
								vendor/stub.sh
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								vendor/stub.sh
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | |||||||
| Subproject commit bd6f3c4666cd2a702e388e09d77b8543a1f6b672 |  | ||||||
| @ -2,7 +2,7 @@ | |||||||
| # https://github.com/zsh-users/zsh-autosuggestions | # https://github.com/zsh-users/zsh-autosuggestions | ||||||
| # v0.3.3 | # v0.3.3 | ||||||
| # Copyright (c) 2013 Thiago de Arruda | # Copyright (c) 2013 Thiago de Arruda | ||||||
| # Copyright (c) 2016 Eric Freese | # Copyright (c) 2016-2017 Eric Freese | ||||||
| #  | #  | ||||||
| # Permission is hereby granted, free of charge, to any person | # Permission is hereby granted, free of charge, to any person | ||||||
| # obtaining a copy of this software and associated documentation | # obtaining a copy of this software and associated documentation | ||||||
| @ -25,6 +25,16 @@ | |||||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||||||
| # OTHER DEALINGS IN THE SOFTWARE. | # OTHER DEALINGS IN THE SOFTWARE. | ||||||
| 
 | 
 | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | # Setup                                                              # | ||||||
|  | #--------------------------------------------------------------------# | ||||||
|  | 
 | ||||||
|  | # Precmd hooks for initializing the library and starting pty's | ||||||
|  | autoload -Uz add-zsh-hook | ||||||
|  | 
 | ||||||
|  | # Asynchronous suggestions are generated in a pty | ||||||
|  | zmodload zsh/zpty | ||||||
|  | 
 | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Global Configuration Variables                                     # | # Global Configuration Variables                                     # | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| @ -87,6 +97,42 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( | |||||||
| # Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. | # Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. | ||||||
| ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= | ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= | ||||||
| 
 | 
 | ||||||
|  | # Use asynchronous mode by default. Unset this variable to use sync mode. | ||||||
|  | ZSH_AUTOSUGGEST_USE_ASYNC= | ||||||
|  | 
 | ||||||
|  | # 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() { | ||||||
|  | 	typeset -g _ZSH_AUTOSUGGEST_ZPTY_RETURNS_FD | ||||||
|  | 	typeset -h REPLY | ||||||
|  | 
 | ||||||
|  | 	zpty $ZSH_AUTOSUGGEST_ASYNC_PTY_NAME '{ 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_ASYNC_PTY_NAME | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Handle Deprecated Variables/Widgets                                # | # Handle Deprecated Variables/Widgets                                # | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| @ -260,13 +306,24 @@ _zsh_autosuggest_modify() { | |||||||
| 	local orig_buffer="$BUFFER" | 	local orig_buffer="$BUFFER" | ||||||
| 	local orig_postdisplay="$POSTDISPLAY" | 	local orig_postdisplay="$POSTDISPLAY" | ||||||
| 
 | 
 | ||||||
| 	# Clear suggestion while original widget runs | 	# Clear suggestion while waiting for next one | ||||||
| 	unset POSTDISPLAY | 	unset POSTDISPLAY | ||||||
| 
 | 
 | ||||||
| 	# Original widget may modify the buffer | 	# Original widget may modify the buffer | ||||||
| 	_zsh_autosuggest_invoke_original_widget $@ | 	_zsh_autosuggest_invoke_original_widget $@ | ||||||
| 	retval=$? | 	retval=$? | ||||||
| 
 | 
 | ||||||
|  | 	# 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 | 	# Don't fetch a new suggestion if the buffer hasn't changed | ||||||
| 	if [ "$BUFFER" = "$orig_buffer" ]; then | 	if [ "$BUFFER" = "$orig_buffer" ]; then | ||||||
| 		POSTDISPLAY="$orig_postdisplay" | 		POSTDISPLAY="$orig_postdisplay" | ||||||
| @ -274,21 +331,37 @@ _zsh_autosuggest_modify() { | |||||||
| 	fi | 	fi | ||||||
| 
 | 
 | ||||||
| 	# Get a new suggestion if the buffer is not empty after modification | 	# Get a new suggestion if the buffer is not empty after modification | ||||||
| 	local suggestion |  | ||||||
| 	if [ $#BUFFER -gt 0 ]; then | 	if [ $#BUFFER -gt 0 ]; then | ||||||
| 		if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -lt "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then | 		if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -le "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then | ||||||
| 			suggestion="$(_zsh_autosuggest_suggestion "$BUFFER")" | 			_zsh_autosuggest_fetch | ||||||
| 		fi | 		fi | ||||||
| 	fi | 	fi | ||||||
| 
 | 
 | ||||||
| 	# Add the suggestion to the POSTDISPLAY |  | ||||||
| 	if [ -n "$suggestion" ]; then |  | ||||||
| 		POSTDISPLAY="${suggestion#$BUFFER}" |  | ||||||
| 	fi |  | ||||||
| 
 |  | ||||||
| 	return $retval | 	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 | # Accept the entire suggestion | ||||||
| _zsh_autosuggest_accept() { | _zsh_autosuggest_accept() { | ||||||
| 	local -i max_cursor_pos=$#BUFFER | 	local -i max_cursor_pos=$#BUFFER | ||||||
| @ -356,7 +429,7 @@ _zsh_autosuggest_partial_accept() { | |||||||
| 	return $retval | 	return $retval | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| for action in clear modify accept partial_accept execute; do | for action in clear modify fetch suggest accept partial_accept execute; do | ||||||
| 	eval "_zsh_autosuggest_widget_$action() { | 	eval "_zsh_autosuggest_widget_$action() { | ||||||
| 		local -i retval | 		local -i retval | ||||||
| 
 | 
 | ||||||
| @ -367,35 +440,18 @@ for action in clear modify accept partial_accept execute; do | |||||||
| 
 | 
 | ||||||
| 		_zsh_autosuggest_highlight_apply | 		_zsh_autosuggest_highlight_apply | ||||||
| 
 | 
 | ||||||
|  | 		zle -R | ||||||
|  | 
 | ||||||
| 		return \$retval | 		return \$retval | ||||||
| 	}" | 	}" | ||||||
| done | 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-accept _zsh_autosuggest_widget_accept | ||||||
| zle -N autosuggest-clear _zsh_autosuggest_widget_clear | zle -N autosuggest-clear _zsh_autosuggest_widget_clear | ||||||
| zle -N autosuggest-execute _zsh_autosuggest_widget_execute | zle -N autosuggest-execute _zsh_autosuggest_widget_execute | ||||||
| 
 | 
 | ||||||
| #--------------------------------------------------------------------# |  | ||||||
| # Suggestion                                                         # |  | ||||||
| #--------------------------------------------------------------------# |  | ||||||
| 
 |  | ||||||
| # Delegate to the selected strategy to determine a suggestion |  | ||||||
| _zsh_autosuggest_suggestion() { |  | ||||||
| 	local escaped_prefix="$(_zsh_autosuggest_escape_command "$1")" |  | ||||||
| 	local strategy_function="_zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" |  | ||||||
| 
 |  | ||||||
| 	if [ -n "$functions[$strategy_function]" ]; then |  | ||||||
| 		echo -E "$($strategy_function "$escaped_prefix")" |  | ||||||
| 	fi |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| _zsh_autosuggest_escape_command() { |  | ||||||
| 	setopt localoptions EXTENDED_GLOB |  | ||||||
| 
 |  | ||||||
| 	# Escape special chars in the string (requires EXTENDED_GLOB) |  | ||||||
| 	echo -E "${1//(#m)[\\()\[\]|*?~]/\\$MATCH}" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| # Default Suggestion Strategy                                        # | # Default Suggestion Strategy                                        # | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| @ -404,7 +460,21 @@ _zsh_autosuggest_escape_command() { | |||||||
| # | # | ||||||
| 
 | 
 | ||||||
| _zsh_autosuggest_strategy_default() { | _zsh_autosuggest_strategy_default() { | ||||||
| 	fc -lnrm "$1*" 1 2>/dev/null | head -n 1 | 	# 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*]}" | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| @ -429,7 +499,7 @@ _zsh_autosuggest_strategy_default() { | |||||||
| # `HIST_EXPIRE_DUPS_FIRST`. | # `HIST_EXPIRE_DUPS_FIRST`. | ||||||
| 
 | 
 | ||||||
| _zsh_autosuggest_strategy_match_prev_cmd() { | _zsh_autosuggest_strategy_match_prev_cmd() { | ||||||
| 	local prefix="$1" | 	local prefix="${1//(#m)[\\()\[\]|*?~]/\\$MATCH}" | ||||||
| 
 | 
 | ||||||
| 	# Get all history event numbers that correspond to history | 	# Get all history event numbers that correspond to history | ||||||
| 	# entries that match pattern $prefix* | 	# entries that match pattern $prefix* | ||||||
| @ -455,8 +525,117 @@ _zsh_autosuggest_strategy_match_prev_cmd() { | |||||||
| 		fi | 		fi | ||||||
| 	done | 	done | ||||||
| 
 | 
 | ||||||
| 	# Echo the matched history entry | 	# Give back the matched history entry | ||||||
| 	echo -E "$history[$histkey]" | 	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 [ -n "$_ZSH_AUTOSUGGEST_PTY_FD" ]; then | ||||||
|  | 		# Remove the input handler | ||||||
|  | 		zle -F $_ZSH_AUTOSUGGEST_PTY_FD | ||||||
|  | 
 | ||||||
|  | 		# 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_async_pty_create | ||||||
|  | 
 | ||||||
|  | 	# We recreate the pty to get a fresh list of history events | ||||||
|  | 	add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #--------------------------------------------------------------------# | #--------------------------------------------------------------------# | ||||||
| @ -465,9 +644,16 @@ _zsh_autosuggest_strategy_match_prev_cmd() { | |||||||
| 
 | 
 | ||||||
| # Start the autosuggestion widgets | # Start the autosuggestion widgets | ||||||
| _zsh_autosuggest_start() { | _zsh_autosuggest_start() { | ||||||
|  | 	add-zsh-hook -d precmd _zsh_autosuggest_start | ||||||
|  | 
 | ||||||
|  | 	_zsh_autosuggest_feature_detect | ||||||
| 	_zsh_autosuggest_check_deprecated_config | 	_zsh_autosuggest_check_deprecated_config | ||||||
| 	_zsh_autosuggest_bind_widgets | 	_zsh_autosuggest_bind_widgets | ||||||
|  | 
 | ||||||
|  | 	if [ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]; then | ||||||
|  | 		_zsh_autosuggest_async_start | ||||||
|  | 	fi | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| autoload -Uz add-zsh-hook | # Start the autosuggestion widgets on the next precmd | ||||||
| add-zsh-hook precmd _zsh_autosuggest_start | add-zsh-hook precmd _zsh_autosuggest_start | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Eric Freese
						Eric Freese