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 -