The eval line is doing several things. Maybe too many things. One approach would be to separate out the zoxide call, and to check whether it has returned something. Another need would be to check whether ~/.rangerdir has a directory (or something) in it, and to empty that file after use. These new checks create multiple points the function must cleanup and exit from:
function zoxide_to_ranger { local zz; zz=$(zoxide query -i) [[ -z $zz ]] && { zle reset-prompt; return } # not sure if the 'eval' is necessary; does ranger ever exit with # shell commands to be run? ranger $zz --choosedir=~/.rangerdir < $TTY local dd; dd=$(< ~/.rangerdir) [[ -z $dd ]] && { zle reset-prompt; return } :> ~/.rangerdir # this could be "local" unless you use it elsewhere LASTDIR=$dd cd $LASTDIR || { zle reset-prompt; return } local precmd; for precmd in $precmd_functions; $precmd zle reset-prompt }