Open masakistan opened 1 year ago
@masakistan Can you reproduce this by running zsh -df
(to skip your .zshrc
) and sourcing the plugin manually? If not, what is the minimal content from your .zshrc
that you can add to start seeing the problem?
i saw similar errors, and by reducing my ~/.zshrc
with a binary search (after backing it up), i found that the culprit was this:
# somewhere in my ~/.zshrc:
IFS=$'\n'
i forget why i put it there (and i'm going to try to avoid using it in the future, and instead solve whatever problem i had with parameter expansion flags if possible), but the effect of setting IFS like this (as seen by setting setopt xtrace
just above the zle -C
line) is that i then see a bunch of evaluations like this:
+_zsh_autosuggest_bind_widget:33> eval $'zle -C autosuggest-orig-1-complete-word .complete-word\n_main_complete'
+(eval):1> zle -C autosuggest-orig-1-complete-word .complete-word
(eval):zle:1: not enough arguments for -C
+(eval):2> _main_complete
+_main_complete:0> -ftb-complete
+-ftb-complete:1> local -a _ftb_compcap
+-ftb-complete:2> local -Ua _ftb_groups
+-ftb-complete:3> local choice choices _ftb_curcontext continuous_trigger print_query accept_line bs=$'\C-B' nul=$'\C-@'
+-ftb-complete:4> local ret=0
+-ftb-complete:7> (( 0 ))
(note the errant literal \n
in .complete-word\n_main_complete
)
So _main_complete
, the word that was intended to serve as the function
argument in:
zle -C widget completion-widget function
is here instead being evaluated as a separate command, because eval
translated the errant literal \n
into a logical newline.
But if IFS has its default value, then ${${(s.:.)widgets[$widget]}[2,3]}
gets expanded as intended:
+_zsh_autosuggest_bind_widget:33> eval 'zle -C autosuggest-orig-1-complete-word .complete-word _main_complete'
+(eval):1> zle -C autosuggest-orig-1-complete-word .complete-word _main_complete
Side note: i am very lucky that i did not have any completion widgets set up for any commands that have lasting side-effects, or the downstream symptoms might have been very bad.
@ericfreese, do you think we can modify the expression ${${(s.:.)widgets[$widget]}[2,3]}
so that it gives the result we want without depending on IFS? (I'm probably not the only zsh user who was silly enough to set it in my ~/.zshrc
)
[edit: it seems like the expansion flag@
does the trick, as in: ${(@)${(s.:.)widgets[$widget]}[2,3]}
]
for anyone else who was confused by this (like i was), a key rule is rule 5 ("Double-quoted joining") of the parameter expansion rules:
If the value after this process is an array, and the substitution appears in double quotes, and neither an ‘(@)’ flag nor a ‘#’ length operator is present at the current level, then words of the value are joined with the first character of the parameter $IFS, by default a space, between each word (single word arrays are not modified). If the ‘(j)’ flag is present, that is used for joining instead of $IFS.
so, to apply this to the xtrace
output from above: an expression like ${widgets[autosuggest-orig-1-complete-word]}
produces a scalar value (not an array) and its value in this case happens to be completion:.complete-word:_main_complete
. But in ${(s.:.)widgets[autosuggest-orig-1-complete-word]}
, the flag (s.:.)
means that the result of the expansion will be an array formed by splitting that scalar at each instance of ':'.
So our array value has 3 elements:
completion .complete-word _main_complete
After that, rule 5 kicks in.
[edit: well, we did evaluate rule 5 once before, when zsh was ticking through the expansion rules for the nested expansion ${(s.:.)...}
, but at that time our input value was a scalar because we hadn't yet reached rule 11 (where the (s.:.)
flag is applied, which replaces our scalar with array). And rule 5 applies only to arrays, not scalars, so this was a no-op. After that round of the rules, we then had to go through all of the rules again to process the outer expansion:
${<result from previous round>[2,3]}
So now our current value is an array, so rule 5 applies.
(rule 3 (parameter subscripting) took effect in this round of the rules before we hit rule 5, so the input to rule 5 is an array with 2 elements instead of 3.)
--end edit]
This entire expansion appears inside a quoted string, and the (@)
flag is not present, so the words of the array value "are joined with the first character of the parameter $IFS
", which in my case (and presumably @masakistan's) happened to be a newline. (By default, it's a space character.)
So now i feel more confident that (@)
should be used in this expression, because that way (it seems), the value of IFS does not affect the result.
[edit: alternatively, issue an error at the top of the script if IFS is set ([[ -v IFS ]]
) and the first character is anything other than a space, since it looks like there may be other quoted array values in there.]
I get the following error on startup:
I believe that the issue is related to this line: https://github.com/zsh-users/zsh-autosuggestions/blob/a411ef3e0992d4839f0732ebeb9823024afaaaa8/zsh-autosuggestions.zsh#L174
documentation indicates that it needs 3 arguments whereas the indicated line only has 2 arguments. The documentation reads as follows:
thanks so much for the great plugin, please let me know if I can provide any additional information.