diff --git a/CHANGELOG.md b/CHANGELOG.md index 11ef027..608d17f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## v0.3.2 +- Test runner now supports running specific tests and choosing zsh binary +- Return code from original widget is now correctly passed through (#135) +- Add `vi-add-eol` to list of accept widgets (#143) +- Escapes widget names within evals to fix problems with irregular widget names (#152) +- Plugin now clears suggestion while within a completion menu (#149) +- .plugin file no longer relies on symbolic link support, fixing issues on Windows (#156) + ## v0.3.1 - Fixes issue with `vi-next-char` not accepting suggestion (#137). diff --git a/Makefile b/Makefile index 6a7679e..fde3691 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ SRC_DIR := ./src -TEST_DIR := ./script VENDOR_DIR := ./vendor SRC_FILES := \ @@ -19,11 +18,6 @@ HEADER_FILES := \ LICENSE PLUGIN_TARGET := zsh-autosuggestions.zsh -OH_MY_ZSH_LINK_TARGET := zsh-autosuggestions.plugin.zsh - -ALL_TARGETS := \ - $(PLUGIN_TARGET) \ - $(OH_MY_ZSH_LINK_TARGET) SHUNIT2 := $(VENDOR_DIR)/shunit2/2.1.6 STUB_SH := $(VENDOR_DIR)/stub.sh/stub.sh @@ -32,18 +26,12 @@ TEST_PREREQS := \ $(SHUNIT2) \ $(STUB_SH) -TEST_FILES := \ - $(TEST_DIR)/**/*.zsh - -all: $(ALL_TARGETS) +all: $(PLUGIN_TARGET) $(PLUGIN_TARGET): $(HEADER_FILES) $(SRC_FILES) cat $(HEADER_FILES) | sed -e 's/^/# /g' > $@ cat $(SRC_FILES) >> $@ -$(OH_MY_ZSH_LINK_TARGET): $(PLUGIN_TARGET) - ln -s $(PLUGIN_TARGET) $@ - $(SHUNIT2): git submodule update --init vendor/shunit2 @@ -52,8 +40,8 @@ $(STUB_SH): .PHONY: clean clean: - rm $(ALL_TARGETS) + rm $(PLUGIN_TARGET) .PHONY: test test: all $(TEST_PREREQS) - script/test_runner.zsh + script/test_runner.zsh $(TESTS) diff --git a/README.md b/README.md index 442baa6..bc6de0f 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ Edit the source files in `src/`. Run `make` to build `zsh-autosuggestions.zsh` f Pull requests are welcome! If you send a pull request, please: +- Request to merge into the `develop` branch (*NOT* `master`) - Match the existing coding conventions. - Include helpful comments to keep the barrier-to-entry low for people new to the project. - Write tests that cover your code as much as possible. @@ -148,7 +149,7 @@ Pull requests are welcome! If you send a pull request, please: 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). -The test script lives at `script/test.zsh`. To run the tests, run `make test`. +The test script lives at `script/test_runner.zsh`. To run the tests, run `make test`. ## License diff --git a/VERSION b/VERSION index 937cd78..7becae1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.3.1 +v0.3.2 diff --git a/script/test_runner.zsh b/script/test_runner.zsh index 5ab06d6..0ff4173 100755 --- a/script/test_runner.zsh +++ b/script/test_runner.zsh @@ -15,15 +15,40 @@ header() { EOF } -local -a tests +# ZSH binary to use +local zsh_bin="zsh" -# Test suites to run -tests=($TEST_DIR/**/*_test.zsh) - -local retval=0 -for suite in $tests; do - header "${suite#"$TEST_DIR"}" - zsh -f "$suite" || retval=$? +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 -exit retval +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 diff --git a/src/bind.zsh b/src/bind.zsh index b1e68f1..384ae08 100644 --- a/src/bind.zsh +++ b/src/bind.zsh @@ -21,13 +21,13 @@ _zsh_autosuggest_bind_widget() { # Built-in widget builtin) - eval "_zsh_autosuggest_orig_$widget() { zle .$widget }" + eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }" zle -N $prefix$widget _zsh_autosuggest_orig_$widget ;; # Completion widget completion:*) - eval "zle -C $prefix$widget ${${widgets[$widget]#*:}/:/ }" + eval "zle -C $prefix${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}" ;; esac @@ -37,8 +37,8 @@ _zsh_autosuggest_bind_widget() { # correctly. $WIDGET cannot be trusted because other plugins call # zle without the `-w` flag (e.g. `zle self-insert` instead of # `zle self-insert -w`). - eval "_zsh_autosuggest_bound_$widget() { - _zsh_autosuggest_widget_$autosuggest_action $prefix$widget \$@ + eval "_zsh_autosuggest_bound_${(q)widget}() { + _zsh_autosuggest_widget_$autosuggest_action $prefix${(q)widget} \$@ }" # Create the bound widget diff --git a/src/config.zsh b/src/config.zsh index 8a83e4d..73d98fe 100644 --- a/src/config.zsh +++ b/src/config.zsh @@ -32,6 +32,7 @@ ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=( end-of-line vi-forward-char vi-end-of-line + vi-add-eol ) # Widgets that accept the entire suggestion and execute it diff --git a/src/strategies/default.zsh b/src/strategies/default.zsh index d2d1780..42da24e 100644 --- a/src/strategies/default.zsh +++ b/src/strategies/default.zsh @@ -7,7 +7,7 @@ # _zsh_autosuggest_strategy_default() { - local prefix="$(_zsh_autosuggest_escape_command "$1")" + local prefix="$1" # Get the keys of the history items that match local -a histkeys diff --git a/src/strategies/match_prev_cmd.zsh b/src/strategies/match_prev_cmd.zsh index c6b4c0c..e71957e 100644 --- a/src/strategies/match_prev_cmd.zsh +++ b/src/strategies/match_prev_cmd.zsh @@ -18,7 +18,7 @@ # _zsh_autosuggest_strategy_match_prev_cmd() { - local prefix="$(_zsh_autosuggest_escape_command "$1")" + local prefix="$1" # Get all history event numbers that correspond to history # entries that match pattern $prefix* @@ -29,8 +29,7 @@ _zsh_autosuggest_strategy_match_prev_cmd() { local histkey="${history_match_keys[1]}" # Get the previously executed command - local prev_cmd="$(_zsh_autosuggest_prev_command)" - prev_cmd="$(_zsh_autosuggest_escape_command "$prev_cmd")" + local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")" # Iterate up to the first 200 history event numbers that match $prefix for key in "${(@)history_match_keys[1,200]}"; do diff --git a/src/suggestion.zsh b/src/suggestion.zsh index 16f2798..0a7ca1e 100644 --- a/src/suggestion.zsh +++ b/src/suggestion.zsh @@ -5,11 +5,11 @@ # Delegate to the selected strategy to determine a suggestion _zsh_autosuggest_suggestion() { - local prefix="$1" + 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 "$prefix")" + echo -E "$($strategy_function "$escaped_prefix")" fi } @@ -19,8 +19,3 @@ _zsh_autosuggest_escape_command() { # Escape special chars in the string (requires EXTENDED_GLOB) echo -E "${1//(#m)[\\()\[\]|*?]/\\$MATCH}" } - -# Get the previously executed command -_zsh_autosuggest_prev_command() { - echo -E "${history[$((HISTCMD-1))]}" -} diff --git a/src/widgets.zsh b/src/widgets.zsh index be7d2e6..ee1129f 100644 --- a/src/widgets.zsh +++ b/src/widgets.zsh @@ -13,8 +13,14 @@ _zsh_autosuggest_clear() { # Modify the buffer and get a new suggestion _zsh_autosuggest_modify() { + local -i retval + + # Clear suggestion while original widget runs + unset POSTDISPLAY + # Original widget modifies the buffer _zsh_autosuggest_invoke_original_widget $@ + retval=$? # Get a new suggestion if the buffer is not empty after modification local suggestion @@ -28,6 +34,8 @@ _zsh_autosuggest_modify() { else unset POSTDISPLAY fi + + return $retval } # Accept the entire suggestion @@ -70,6 +78,8 @@ _zsh_autosuggest_execute() { # Partially accept the suggestion _zsh_autosuggest_partial_accept() { + local -i retval + # Save the contents of the buffer so we can restore later if needed local original_buffer="$BUFFER" @@ -78,6 +88,7 @@ _zsh_autosuggest_partial_accept() { # Original widget moves the cursor _zsh_autosuggest_invoke_original_widget $@ + retval=$? # If we've moved past the end of the original buffer if [ $CURSOR -gt $#original_buffer ]; then @@ -90,13 +101,22 @@ _zsh_autosuggest_partial_accept() { # Restore the original buffer BUFFER="$original_buffer" fi + + return $retval } for action in clear modify accept partial_accept execute; do eval "_zsh_autosuggest_widget_$action() { + local -i retval + _zsh_autosuggest_highlight_reset + _zsh_autosuggest_$action \$@ + retval=\$? + _zsh_autosuggest_highlight_apply + + return \$retval }" done diff --git a/test/bind_test.zsh b/test/bind_test.zsh new file mode 100644 index 0000000..f73ea7f --- /dev/null +++ b/test/bind_test.zsh @@ -0,0 +1,45 @@ +#!/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" diff --git a/test/strategies/default_test.zsh b/test/strategies/default_test.zsh index 723d0df..f5200e6 100755 --- a/test/strategies/default_test.zsh +++ b/test/strategies/default_test.zsh @@ -53,62 +53,4 @@ testMostRecentMatch() { 'cd quux' } -testBackslash() { - set_history <<-'EOF' - echo "hello\nworld" - EOF - - assertSuggestion \ - 'echo "hello\' \ - 'echo "hello\nworld"' -} - -testDoubleBackslash() { - set_history <<-'EOF' - echo "\\" - EOF - - assertSuggestion \ - 'echo "\\' \ - 'echo "\\"' -} - -testTilde() { - set_history <<-'EOF' - cd ~/something - EOF - - assertSuggestion \ - 'cd' \ - 'cd ~/something' - - assertSuggestion \ - 'cd ~' \ - 'cd ~/something' - - assertSuggestion \ - 'cd ~/s' \ - 'cd ~/something' -} - -testParentheses() { - set_history <<-'EOF' - echo "$(ls foo)" - EOF - - assertSuggestion \ - 'echo "$(' \ - 'echo "$(ls foo)"' -} - -testSquareBrackets() { - set_history <<-'EOF' - echo "$history[123]" - EOF - - assertSuggestion \ - 'echo "$history[' \ - 'echo "$history[123]"' -} - run_tests "$0" diff --git a/test/strategies/match_prev_cmd_test.zsh b/test/strategies/match_prev_cmd_test.zsh index 768bdc0..bf3fc64 100755 --- a/test/strategies/match_prev_cmd_test.zsh +++ b/test/strategies/match_prev_cmd_test.zsh @@ -55,64 +55,6 @@ testMostRecentMatch() { 'cd quux' } -testBackslash() { - set_history <<-'EOF' - echo "hello\nworld" - EOF - - assertSuggestion \ - 'echo "hello\' \ - 'echo "hello\nworld"' -} - -testDoubleBackslash() { - set_history <<-'EOF' - echo "\\" - EOF - - assertSuggestion \ - 'echo "\\' \ - 'echo "\\"' -} - -testTilde() { - set_history <<-'EOF' - cd ~/something - EOF - - assertSuggestion \ - 'cd' \ - 'cd ~/something' - - assertSuggestion \ - 'cd ~' \ - 'cd ~/something' - - assertSuggestion \ - 'cd ~/s' \ - 'cd ~/something' -} - -testParentheses() { - set_history <<-'EOF' - echo "$(ls foo)" - EOF - - assertSuggestion \ - 'echo "$(' \ - 'echo "$(ls foo)"' -} - -testSquareBrackets() { - set_history <<-'EOF' - echo "$history[123]" - EOF - - assertSuggestion \ - 'echo "$history[' \ - 'echo "$history[123]"' -} - testMatchMostRecentAfterPreviousCmd() { set_history <<-'EOF' echo what diff --git a/test/strategies_test.zsh b/test/strategies_test.zsh new file mode 100644 index 0000000..50d0a24 --- /dev/null +++ b/test/strategies_test.zsh @@ -0,0 +1,95 @@ +#!/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' +} + +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 + assertParenthesesSuggestion + assertSquareBracketsSuggestion + done +} + +run_tests "$0" diff --git a/test/suggestion_test.zsh b/test/suggestion_test.zsh index 12b732b..fc6330d 100644 --- a/test/suggestion_test.zsh +++ b/test/suggestion_test.zsh @@ -43,30 +43,4 @@ testEscapeCommand() { "$(_zsh_autosuggest_escape_command '?')" } -testPrevCommand() { - set_history <<-'EOF' - ls foo - ls bar - ls baz - EOF - - assertEquals \ - 'Did not output the last command' \ - 'ls baz' \ - "$(_zsh_autosuggest_prev_command)" - - set_history <<-'EOF' - ls foo - ls bar - ls baz - ls quux - ls foobar - EOF - - assertEquals \ - 'Did not output the last command' \ - 'ls foobar' \ - "$(_zsh_autosuggest_prev_command)" -} - run_tests "$0" diff --git a/test/test_helper.zsh b/test/test_helper.zsh index d4cc94c..7e7dbc0 100644 --- a/test/test_helper.zsh +++ b/test/test_helper.zsh @@ -54,7 +54,7 @@ assertSuggestion() { local expected_suggestion="$2" assertEquals \ - "Did not get correct suggestion for prefix:<$prefix>" \ + "Did not get correct suggestion for prefix:<$prefix> using strategy <$ZSH_AUTOSUGGEST_STRATEGY>" \ "$expected_suggestion" \ "$(_zsh_autosuggest_suggestion "$prefix")" } diff --git a/test/widgets/accept_test.zsh b/test/widgets/accept_test.zsh index 48bcf3c..f126091 100644 --- a/test/widgets/accept_test.zsh +++ b/test/widgets/accept_test.zsh @@ -6,6 +6,17 @@ oneTimeSetUp() { source_autosuggestions } +setUp() { + BUFFER='' + POSTDISPLAY='' + CURSOR=0 + KEYMAP='main' +} + +tearDown() { + restore _zsh_autosuggest_invoke_original_widget +} + testCursorAtEnd() { BUFFER='echo' POSTDISPLAY=' hello' @@ -104,6 +115,19 @@ testViCursorNotAtEnd() { "$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 @@ -128,6 +152,10 @@ testWidget() { 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" diff --git a/test/widgets/clear_test.zsh b/test/widgets/clear_test.zsh index e9f3a54..f0500c5 100644 --- a/test/widgets/clear_test.zsh +++ b/test/widgets/clear_test.zsh @@ -6,6 +6,15 @@ oneTimeSetUp() { source_autosuggestions } +setUp() { + BUFFER='' + POSTDISPLAY='' +} + +tearDown() { + restore _zsh_autosuggest_invoke_original_widget +} + testClear() { BUFFER='ec' POSTDISPLAY='ho hello' @@ -22,6 +31,19 @@ testClear() { "$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 @@ -46,6 +68,10 @@ testWidget() { 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" diff --git a/test/widgets/execute_test.zsh b/test/widgets/execute_test.zsh new file mode 100644 index 0000000..cb346db --- /dev/null +++ b/test/widgets/execute_test.zsh @@ -0,0 +1,26 @@ +#!/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" diff --git a/test/widgets/modify_test.zsh b/test/widgets/modify_test.zsh index d0646a2..4dfd30d 100644 --- a/test/widgets/modify_test.zsh +++ b/test/widgets/modify_test.zsh @@ -6,10 +6,17 @@ oneTimeSetUp() { source_autosuggestions } -testModify() { +setUp() { BUFFER='' POSTDISPLAY='' +} +tearDown() { + restore _zsh_autosuggest_invoke_original_widget + restore _zsh_autosuggest_suggestion +} + +testModify() { stub_and_eval \ _zsh_autosuggest_invoke_original_widget \ 'BUFFER+="e"' @@ -33,9 +40,19 @@ testModify() { 'POSTDISPLAY does not contain suggestion' \ 'cho hello' \ "$POSTDISPLAY" +} - restore _zsh_autosuggest_invoke_original_widget - restore _zsh_autosuggest_suggestion +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" diff --git a/test/widgets/partial_accept_test.zsh b/test/widgets/partial_accept_test.zsh index b137943..60c78a6 100644 --- a/test/widgets/partial_accept_test.zsh +++ b/test/widgets/partial_accept_test.zsh @@ -6,6 +6,16 @@ oneTimeSetUp() { source_autosuggestions } +setUp() { + BUFFER='' + POSTDISPLAY='' + CURSOR=0 +} + +tearDown() { + restore _zsh_autosuggest_invoke_original_widget +} + testCursorMovesOutOfBuffer() { BUFFER='ec' POSTDISPLAY='ho hello' @@ -58,4 +68,17 @@ testCursorStaysInBuffer() { "$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" diff --git a/zsh-autosuggestions.plugin.zsh b/zsh-autosuggestions.plugin.zsh deleted file mode 120000 index e41b4f7..0000000 --- a/zsh-autosuggestions.plugin.zsh +++ /dev/null @@ -1 +0,0 @@ -zsh-autosuggestions.zsh \ No newline at end of file diff --git a/zsh-autosuggestions.plugin.zsh b/zsh-autosuggestions.plugin.zsh new file mode 100644 index 0000000..16c2256 --- /dev/null +++ b/zsh-autosuggestions.plugin.zsh @@ -0,0 +1 @@ +source ${0:A:h}/zsh-autosuggestions.zsh diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index 17d1eaa..4e3c62e 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -1,6 +1,6 @@ # Fish-like fast/unobtrusive autosuggestions for zsh. # https://github.com/zsh-users/zsh-autosuggestions -# v0.3.1 +# v0.3.2 # Copyright (c) 2013 Thiago de Arruda # Copyright (c) 2016 Eric Freese # @@ -58,6 +58,7 @@ ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=( end-of-line vi-forward-char vi-end-of-line + vi-add-eol ) # Widgets that accept the entire suggestion and execute it @@ -131,13 +132,13 @@ _zsh_autosuggest_bind_widget() { # Built-in widget builtin) - eval "_zsh_autosuggest_orig_$widget() { zle .$widget }" + eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }" zle -N $prefix$widget _zsh_autosuggest_orig_$widget ;; # Completion widget completion:*) - eval "zle -C $prefix$widget ${${widgets[$widget]#*:}/:/ }" + eval "zle -C $prefix${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}" ;; esac @@ -147,8 +148,8 @@ _zsh_autosuggest_bind_widget() { # correctly. $WIDGET cannot be trusted because other plugins call # zle without the `-w` flag (e.g. `zle self-insert` instead of # `zle self-insert -w`). - eval "_zsh_autosuggest_bound_$widget() { - _zsh_autosuggest_widget_$autosuggest_action $prefix$widget \$@ + eval "_zsh_autosuggest_bound_${(q)widget}() { + _zsh_autosuggest_widget_$autosuggest_action $prefix${(q)widget} \$@ }" # Create the bound widget @@ -230,8 +231,14 @@ _zsh_autosuggest_clear() { # Modify the buffer and get a new suggestion _zsh_autosuggest_modify() { + local -i retval + + # Clear suggestion while original widget runs + unset POSTDISPLAY + # Original widget modifies the buffer _zsh_autosuggest_invoke_original_widget $@ + retval=$? # Get a new suggestion if the buffer is not empty after modification local suggestion @@ -245,6 +252,8 @@ _zsh_autosuggest_modify() { else unset POSTDISPLAY fi + + return $retval } # Accept the entire suggestion @@ -287,6 +296,8 @@ _zsh_autosuggest_execute() { # Partially accept the suggestion _zsh_autosuggest_partial_accept() { + local -i retval + # Save the contents of the buffer so we can restore later if needed local original_buffer="$BUFFER" @@ -295,6 +306,7 @@ _zsh_autosuggest_partial_accept() { # Original widget moves the cursor _zsh_autosuggest_invoke_original_widget $@ + retval=$? # If we've moved past the end of the original buffer if [ $CURSOR -gt $#original_buffer ]; then @@ -307,13 +319,22 @@ _zsh_autosuggest_partial_accept() { # Restore the original buffer BUFFER="$original_buffer" fi + + return $retval } for action in clear modify accept partial_accept execute; do eval "_zsh_autosuggest_widget_$action() { + local -i retval + _zsh_autosuggest_highlight_reset + _zsh_autosuggest_$action \$@ + retval=\$? + _zsh_autosuggest_highlight_apply + + return \$retval }" done @@ -327,11 +348,11 @@ zle -N autosuggest-execute _zsh_autosuggest_widget_execute # Delegate to the selected strategy to determine a suggestion _zsh_autosuggest_suggestion() { - local prefix="$1" + 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 "$prefix")" + echo -E "$($strategy_function "$escaped_prefix")" fi } @@ -342,11 +363,6 @@ _zsh_autosuggest_escape_command() { echo -E "${1//(#m)[\\()\[\]|*?]/\\$MATCH}" } -# Get the previously executed command -_zsh_autosuggest_prev_command() { - echo -E "${history[$((HISTCMD-1))]}" -} - #--------------------------------------------------------------------# # Default Suggestion Strategy # #--------------------------------------------------------------------# @@ -355,7 +371,7 @@ _zsh_autosuggest_prev_command() { # _zsh_autosuggest_strategy_default() { - local prefix="$(_zsh_autosuggest_escape_command "$1")" + local prefix="$1" # Get the keys of the history items that match local -a histkeys @@ -384,7 +400,7 @@ _zsh_autosuggest_strategy_default() { # _zsh_autosuggest_strategy_match_prev_cmd() { - local prefix="$(_zsh_autosuggest_escape_command "$1")" + local prefix="$1" # Get all history event numbers that correspond to history # entries that match pattern $prefix* @@ -395,8 +411,7 @@ _zsh_autosuggest_strategy_match_prev_cmd() { local histkey="${history_match_keys[1]}" # Get the previously executed command - local prev_cmd="$(_zsh_autosuggest_prev_command)" - prev_cmd="$(_zsh_autosuggest_escape_command "$prev_cmd")" + local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")" # Iterate up to the first 200 history event numbers that match $prefix for key in "${(@)history_match_keys[1,200]}"; do