zsh-users / zsh-syntax-highlighting

Fish shell like syntax highlighting for Zsh.
github.com/zsh-users/zsh-syntax-highlighting
BSD 3-Clause "New" or "Revised" License
19.8k stars 1.32k forks source link

zsh-syntax-highlighting sends add-zle-hook-widget into an infinite loop #816

Open marlonrichert opened 3 years ago

marlonrichert commented 3 years ago

Given

When

Then

This happens because

  1. zsh-syntax-highlighting makes $widgets[<hook>] equal to user:_zsh_highlight_widget_orig-s<float>-r<integer>-zle-<hook>,
  2. which causes [[ ${widgets[$hook]:-} != "user:azhw:$hook" ]] in add-zle-hook-widget to evaluate to true,
  3. which in turn causes add-zle-hook-widget to add the same widget again to the list,
  4. so that the wrapped zle-<hook> widget now ends up calling itself.

Steps to reproduce :

% cd $(mktemp -d)
% print '
bindkey >/dev/null # Strangely enough, if we dont call bindkey, then the bug never occurs.
foo bar () {}
autoload -Uz add-zle-hook-widget
add-zle-hook-widget line-init foo
source /<path to>/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh
' > .zshrc
% exec env -i HOME=$PWD TERM=$TERM SHELL=$SHELL $SHELL -d 
% functions -T add-zle-hook-widget
% add-zle-hook-widget line-init bar; functions -T azhw:zle-line-init
+add-zle-hook-widget:1> local -a hooktypes
+add-zle-hook-widget:2> zstyle -a zle-hook types hooktypes
+add-zle-hook-widget:5> local usage='Usage: add-zle-hook-widget hook widgetname\nValid hooks are:\n  isearch-exit isearch-update line-pre-redraw line-init line-finish history-line-set keymap-select'
+add-zle-hook-widget:7> local opt
+add-zle-hook-widget:8> local -a autoopts
+add-zle-hook-widget:9> integer del list help
+add-zle-hook-widget:11> getopts dDhLUzk opt
+add-zle-hook-widget:38> shift 0
+add-zle-hook-widget:40> 1=line-init 
+add-zle-hook-widget:42> ((  list  ))
+add-zle-hook-widget:45> ((  help || 2 != 2 || 4 == 0  ))
+add-zle-hook-widget:50> local -aU extant_hooks
+add-zle-hook-widget:51> local hook=zle-line-init
+add-zle-hook-widget:52> local fn=bar
+add-zle-hook-widget:54> ((  del  ))
+add-zle-hook-widget:71> [[ bar = zle-line-init ]]
+add-zle-hook-widget:81> integer i=3-2
+add-zle-hook-widget:82> zstyle -g extant_hooks zle-line-init widgets
+add-zle-hook-widget:84> [[ user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init != user:azhw:zle-line-init ]]
+add-zle-hook-widget:85> [[ -n user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init ]]
+add-zle-hook-widget:86> zle -A zle-line-init user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init
+add-zle-hook-widget:87> extant_hooks=( 0:user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init 1:foo ) 
+add-zle-hook-widget:89> zle -N zle-line-init azhw:zle-line-init
+add-zle-hook-widget:92> [[ -z '' ]]
+add-zle-hook-widget:95> i=1+1 
+add-zle-hook-widget:99> extant_hooks+=( 2:bar ) 
+add-zle-hook-widget:100> zstyle -- zle-line-init widgets 0:user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init 1:foo 2:bar
+add-zle-hook-widget:101> ((  ! 0  ))
+add-zle-hook-widget:102> autoload -- bar
+add-zle-hook-widget:103> zle -N -- bar
+add-zle-hook-widget:105> ((  ! 1  ))
% +azhw:zle-line-init:1> local -a hook_widgets
+azhw:zle-line-init:2> local hook
+azhw:zle-line-init:5> zstyle -a zle-line-init widgets hook_widgets
+azhw:zle-line-init:6> hook=user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init
+azhw:zle-line-init:7> [[ user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init = user:* ]]
+azhw:zle-line-init:9> zle user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init -N --
+azhw:zle-line-init:1> local -a hook_widgets
+azhw:zle-line-init:2> local hook
+azhw:zle-line-init:5> zstyle -a zle-line-init widgets hook_widgets
+azhw:zle-line-init:6> hook=user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init
+azhw:zle-line-init:7> [[ user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init = user:* ]]
+azhw:zle-line-init:9> zle user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init -N --
+azhw:zle-line-init:1> local -a hook_widgets
+azhw:zle-line-init:2> local hook
+azhw:zle-line-init:5> zstyle -a zle-line-init widgets hook_widgets
+azhw:zle-line-init:6> hook=user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init
+azhw:zle-line-init:7> [[ user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init = user:* ]]
+azhw:zle-line-init:9> zle user:_zsh_highlight_widget_orig-s0.0000030000-r28564-zle-line-init -N --
<...>

[Process completed]

danielshahaf commented 3 years ago

Only affects the non-redrawhook codepath.

Sounds like add-zle-hook-widget should detect this situation?

marlonrichert commented 3 years ago

All right, I reported it on zsh-workers.