diff --git a/plugins/fancy-ctrl-z/README.md b/plugins/fancy-ctrl-z/README.md
index 7766c51eb..2bd009ae2 100644
--- a/plugins/fancy-ctrl-z/README.md
+++ b/plugins/fancy-ctrl-z/README.md
@@ -1,8 +1,10 @@
# fancy-ctrl-z
-Allows pressing Ctrl-Z again to switch back to a background job.
+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:
+To use it, add `fancy-ctrl-z` to the `plugins` array in your `.zshrc` file:
```zsh
plugins=(... fancy-ctrl-z)
@@ -10,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
-- added to OMZ by @mbologna
+## 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 82b968894..bc0a16a54 100644
--- a/plugins/fancy-ctrl-z/fancy-ctrl-z.plugin.zsh
+++ b/plugins/fancy-ctrl-z/fancy-ctrl-z.plugin.zsh
@@ -1,12 +1,40 @@
fancy-ctrl-z () {
- if [[ $#BUFFER -eq 0 ]]; then
+ 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 ))
+
+ 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
+ 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
- else
- zle push-input -w
- zle clear-screen -w
fi
}
+
zle -N fancy-ctrl-z
bindkey '^Z' fancy-ctrl-z
-