diff --git a/plugins/fancy-ctrl-z/README.md b/plugins/fancy-ctrl-z/README.md
index 388e402d0..2bd009ae2 100644
--- a/plugins/fancy-ctrl-z/README.md
+++ b/plugins/fancy-ctrl-z/README.md
@@ -1,8 +1,8 @@
# fancy-ctrl-z
-Allows pressing Ctrl-Z again to switch back to the most recently backgrounded
-job. If there are precisely two backgrounded jobs, Ctrl-Z will toggle between
-them, bringing the other one back.
+Allows pressing Ctrl-Z is an empty prompt to bring to the
+foreground the only suspended job, or, if there are more than one such jobs, to
+switch between the two most recently suspended ones.
To use it, add `fancy-ctrl-z` to the `plugins` array in your `.zshrc` file:
@@ -12,15 +12,29 @@ plugins=(... fancy-ctrl-z)
## Motivation
-I frequently need to execute random commands in my shell. To achieve it I pause
-Vim by pressing Ctrl-Z, type command and press fg to switch back to Vim.
-The fg part really hurts me. I just wanted to hit Ctrl-Z once again to get back
-to Vim. I could not find a solution, so I developed one on my own that
-works wonderfully with ZSH.
+I frequently need to execute random commands in my shell.
+To achieve it, I often pause Vim by pressing Ctrl-Z, type
+a command and then would use `fg`↵ Enter to switch back to Vim.
+Having to type in the `fg` part really hurt me.
+I just wanted to hit Ctrl-Z once again to get back to Vim.
+I could not find a solution, so I developed one on my own that works wonderfully
+with Zsh.
-Source: http://sheerun.net/2014/03/21/how-to-boost-your-vim-productivity/
+Switching between the last two suspended jobs is motivated by both TV remotes
+that had such feature, and tools like `cd -` and `git checkout -` that switch
+between the current and the second most recent state (directory, branch, etc.).
+Sometimes, you have your Vim where code is changed, and another longer-running
+process (e.g., a `tail` on some logs, or a Python interpreter) where you want
+to test or observe your changes.
+There is no point in time where you would "have the editor open" and "have the
+program open" together, and the workflow clearly mandates always switching
+back and forth between the two.
+That's why the original version of _fancy-ctrl-z_ was extended with this "even
+fancier" behaviour, because the original version would've opened Vim back again
+and again.
-Credits:
-- original idea by [@sheerun](https://github.com/sheerun)
-- added to OMZ by [@mbologna](https://github.com/mbologna)
-- two-job switching added by [@Whisperity](https://github.com/Whisperity)
+## Credits
+
+- Original idea by [@sheerun](https://github.com/sheerun), http://sheerun.net/2014/03/21/how-to-boost-your-vim-productivity
+- Added to OMZ by [@mbologna](https://github.com/mbologna)
+- Two-job switching added by [@Whisperity](https://github.com/Whisperity)
diff --git a/plugins/fancy-ctrl-z/fancy-ctrl-z.plugin.zsh b/plugins/fancy-ctrl-z/fancy-ctrl-z.plugin.zsh
index c32512174..bc0a16a54 100644
--- a/plugins/fancy-ctrl-z/fancy-ctrl-z.plugin.zsh
+++ b/plugins/fancy-ctrl-z/fancy-ctrl-z.plugin.zsh
@@ -1,16 +1,38 @@
fancy-ctrl-z () {
- if [[ $#BUFFER -eq 0 ]]; then
- BUFFER="fg"
+ local -i hascmd=$(( ${#BUFFER} > 0 ))
+ local -a sjobs=("${(@f)$(jobs -s)}")
+ local -a opts=("${(@f)$(setopt)}")
+ local -i isverbose=$opts[(Ie)verbose]
+ # In Zsh, arrays are 1-indexed, and an empty array has size 1, not 0.
+ # So we must check the first element's length to see whether it describes a
+ # suspended job.
+ local -i nsjobs="${#${(@)sjobs}}"
+ local -i hassjob=$(( ${#sjobs[1]} > 0 ))
+ local -i hassjobs=$(( nsjobs >= 2 ))
+ # Whether the ^Z action will result in a side effect to the terminal.
+ local -i sideeffect=$(( hassjob || hassjobs || isverbose ))
- IFS=$'\n' local num_jobs=($(jobs))
- if [[ "${#num_jobs[@]}" -eq 2 ]]; then
- BUFFER="fg %-"
- fi
-
- zle accept-line -w
- else
+ if (( hascmd && sideeffect )); then
+ # Save the current command.
+ # It will be restored after the side-effect is over, e.g., if the
+ # foregrounded job is put to the background again.
zle push-input -w
- zle clear-screen -w
+ fi
+
+ if (( hassjobs )); then
+ # Multiple suspended jobs: foreground the second-to-last suspended job.
+ BUFFER="fg %-"
+ zle accept-line -w
+ elif (( hassjob )); then
+ # Single suspended job: foreground it.
+ # "fg %-" would result in an error as the only suspended job is the
+ # last one, which is referenced by "fg" or "fg %+".
+ BUFFER="fg %+"
+ zle accept-line -w
+ elif (( isverbose )); then
+ # No suspended jobs, but verbose mode, let show the "fg: no current job".
+ BUFFER="fg"
+ zle accept-line -w
fi
}