widgets can only be called when ZLE is active
Asked Answered
O

4

25

I have been dealing with this problem for almost a month now, and I feel frustrated, Any help would be greatly appreciated.

I am trying to write a widget for my takenote command. The purpose of the widget is to feed all the markdown files in ~/notes folder into fzf so that the user can select one of them and starts editing it. After the user types takenote and presses <tab> I expect the widget to run.

Here is the _takenote.zsh widget definition:

#compdef takenote
local file=$( find -L "$HOME/notes/" -print 2> /dev/null | fzf-tmux +m )
zle reset-prompt
compadd $file
return 1

Unfortunately, the above code doesn't work because of zle reset-prompt, if I remove it then the result would be like this:

before selection

And after selecting the file it would turn into:

After selecting the file

Which as you see will corrupt the prompt and the command itself. It appears to me that what I need to do is do a zle reset-prompt before calling compadd but this can only work when I bind the function to a key otherwise, I will get the following error:

widgets can only be called when ZLE is active

Olia answered 2/1, 2018 at 4:30 Comment(5)
any luck so far? I would use the solution if you managed.Jacalynjacamar
No, sorry! It has been a long time and I found no solution. Still I really would like to know the answer. Maybe you could upvote the question?Olia
@animaacija, I tried giving it a shot, but seems like it would require a lot more effort. I can give you a pointer that may help out. This was done for vim and you may be able to adapt it to yours. See github.com/junegunn/fzf/issues/227Brisling
@ExistMe, third bounty in a row :-). Did you get a chance to look at the link I postedBrisling
@tarun-lalwani thanks, I spent a day on the link you've suggested with no success :) How complicated it can be, I feel that I know nothing about zle and it's states. I would say this is mainly a zle widget question rather than fzfOlia
F
3

After two days, I finally managed to find a hint on how to achieve it thanks to the excellent fzf-tab-completion project:

https://github.com/lincheney/fzf-tab-completion/blob/c91959d81320935ae88c090fedde8dcf1ca70a6f/zsh/fzf-zsh-completion.sh#L120

So actually, all that you need to do is:

#compdef takenote
local file=$( find -L "$HOME/notes/" -print 2> /dev/null | fzf-tmux +m )
compadd $file
    
TRAPEXIT() {
   zle reset-prompt
}
return 0

And it finally works. Cheers!

Francophile answered 16/8, 2022 at 22:42 Comment(2)
Great! You finally found the correct answer after 4 years :) Thank you very much for sharing and mentioning the source. Cheers!Olia
I only posted since 2 years ago you commented that you are still interested in a better solution ;) also yes it did take a while ;)Francophile
O
6

I finally found a workaround for the issue. Although I am not satisfied with the strategy since it is not self contained in the widget itself, but it works. The solution involves trapping fzf-completion after it is invoked and calling zle reset-prompt.

For registering the trap add the following snippet to your .zshrc file (see Zsh menu completion causes problems after zle reset-prompt ):

TMOUT=1
TRAPALRM() {
   if [[ "$WIDGET" =~ ^(complete-word|fzf-completion)$ ]]; then
      # limit the reset-prompt functionality to the `takenote` script
      if [[ "$LBUFFER" == "takenote "* ]]; then
         zle reset-prompt
      fi
   fi
}

The _takenote widget:

#compdef takenote
local file=$( find -L "$HOME/notes/" -print 2> /dev/null | fzf-tmux +m )
compadd $file
return 0

p.s: I would still love to move the trap inside the widget, and avoid registering it in the init script (.zshrc)

Olia answered 4/5, 2018 at 5:16 Comment(0)
G
4

I was getting the same error when trying to use bindkey for a widget to use vim to open the fzf selected file. Turns out I have to open the file in function1 and then have a function2 calling function1 and then reset-prompt to avoid this widgets can only be called when ZLE is active error. Like you said, it is really frustrating and took me almost a day to figure out!

Example code:

## use rg to get file list
export FZF_DEFAULT_COMMAND='rg --files --hidden'

## file open (function1)
__my-fo() (
  setopt localoptions pipefail no_aliases 2> /dev/null
  local file=$(eval "${FZF_DEFAULT_COMMAND}" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS --preview 'bat --color=always --line-range :500 {}'" $(__fzfcmd) -m "$@" | while read item; do
    echo -n "${(q)item}"
  done)
  local ret=$?
  if [[ -n $file ]]; then
    $EDITOR $file
  fi
  return $ret
)

## define zsh widget(function2)
__my-fo-widget(){
  __my-fo
  local ret=$?
  zle reset-prompt
  return $ret
}

zle -N __my-fo-widget
bindkey ^p __my-fo-widget

Gerrard answered 29/4, 2020 at 4:20 Comment(1)
This question is quite old now and I am still interested in a better solution. As far as I remember when binding to a key combination using bindkey a zle reset-prompt was enough for doing the job. The real pain is when you want to have it with the tab completion. I couldn't find any alternative than the TRAPALRM method.Olia
F
3

After two days, I finally managed to find a hint on how to achieve it thanks to the excellent fzf-tab-completion project:

https://github.com/lincheney/fzf-tab-completion/blob/c91959d81320935ae88c090fedde8dcf1ca70a6f/zsh/fzf-zsh-completion.sh#L120

So actually, all that you need to do is:

#compdef takenote
local file=$( find -L "$HOME/notes/" -print 2> /dev/null | fzf-tmux +m )
compadd $file
    
TRAPEXIT() {
   zle reset-prompt
}
return 0

And it finally works. Cheers!

Francophile answered 16/8, 2022 at 22:42 Comment(2)
Great! You finally found the correct answer after 4 years :) Thank you very much for sharing and mentioning the source. Cheers!Olia
I only posted since 2 years ago you commented that you are still interested in a better solution ;) also yes it did take a while ;)Francophile
R
0

The TRAPEXIT technique didn't work for me. I also get the same error message when I do zle reset-prompt from the command-line, when it's clearly an interactive shell. So I experimented with redirecting stdout and stderr to /dev/null, and the error message goes away, with no ill effect (except of course that any other errors that might occur are also lost, grrrr).

zle reset-prompt > /dev/null 2>&1

Remissible answered 27/6, 2024 at 0:36 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.