2

I have the following code in my ~/.zshrc:

nv() (
  if vim --serverlist | grep -q VIM; then
    if [[ $# -eq 0 ]]; then
      vim
    elif [[ $1 == -b ]]; then
      shift 1
      IFS=' '
      vim --remote "$@"
      vim --remote-send ":argdo setl binary ft=xxd<cr>"
      vim --remote-send ":argdo %!xxd<cr><cr>"
    elif [[ $1 == -d ]]; then
      shift 1
      IFS=' '
      vim --remote-send ":tabnew<cr>"
      vim --remote "$@"
      vim --remote-send ":argdo vsplit<cr>:q<cr>"
      vim --remote-send ":windo diffthis<cr>"
    elif [[ $1 == -o ]]; then
      shift 1
      IFS=' '
      vim --remote "$@"
      vim --remote-send ":argdo split<cr>:q<cr><cr>"
    elif [[ $1 == -O ]]; then
      shift 1
      IFS=' '
      vim --remote "$@"
      vim --remote-send ":argdo vsplit<cr>:q<cr><cr>"
    elif [[ $1 == -p ]]; then
      shift 1
      IFS=' '
      vim --remote "$@"
      vim --remote-send ":argdo tabedit<cr>:q<cr>"
    elif [[ $1 == -q ]]; then
      shift 1
      IFS=' '
      vim --remote-send ":cexpr system('$*')<cr>"
    else
      vim --remote "$@"
    fi
  else
    vim -w /tmp/.vimkeys --servername VIM "$@"
  fi
)

Its purpose is to install a nv function to start a Vim instance as well as a Vim server. And if a Vim server is already running, the function should send the file arguments it received to the server.

So far, it worked well.


I have the following mapping in my ~/.vimrc:

nno  <silent><unique>  <space>R  :<c-u>sil call <sid>vim_quit_reload()<cr>
fu! s:vim_quit_reload() abort
    sil! update
    call system('kill -USR1 $(ps -p $(ps -p $$ -o ppid=) -o ppid=)')
    qa!
endfu

Its purpose is to restart Vim, by sending the signal USR1 to the parent shell.

I also have the following trap in my ~/.zshrc which restarts Vim when it catches the signal USR1.

catch_signal_usr1() {
  trap catch_signal_usr1 USR1
  clear
  vim
}
trap catch_signal_usr1 USR1

So far, it worked well too.


But I have noticed that if I suspend Vim by pressing C-z, from the shell, even though the Vim process is still running, I can't resume it (with $ fg) because the shell doesn't have any job.

Here's a minimal zshrc with which I'm able to reproduce the issue:

catch_signal_usr1() {
  trap catch_signal_usr1 USR1
  vim
}
trap catch_signal_usr1 USR1
func() {
  vim
}

And here's a minimal vimrc:

nnoremap  <space>R  :call Func()<cr>
function! Func()
    call system('kill -USR1 $(ps -p $(ps -p $$ -o ppid=) -o ppid=)')
    qa!
endfunction

If I start Vim with the function:

$ func

Then, restart Vim by pressing Space R, then suspend it by pressing C-z, once I'm back in the shell, I can see the Vim process running:

$ ps -efH | grep vim
user     24803 24684 10 03:56 pts/9    00:00:01             vim
user     24990 24684  0 03:56 pts/9    00:00:00             grep vim

But I can't resume it:

$ fg
fg: no current job

If I start Vim with the $ vim command instead of the $ func function, I can restart the Vim process, suspend it and resume it. The issue seems to come from the function $ func.


Here's my environment:

  • vim --version: VIM - Vi IMproved 8.1 Compiled by user
  • Operating system: Ubuntu 16.04.4 LTS
  • Terminal emulator: rxvt-unicode v9.22
  • Terminal multiplexer: tmux 2.7
  • $TERM: tmux-256color
  • Shell: zsh 5.5.1

How to start Vim from a function and still be able to resume it after suspending it?


Edit:

More information:

(1) What shows up on your terminal when you type Ctrl+Z?

Nothing is displayed when I type C-z.

(A) If I start Vim with the $ vim command here's what is displayed after pressing C-z:

ubuntu% vim

zsh: suspended  vim

I can resume with $ fg.


(B) If I start Vim with the $ func function:

ubuntu% func
zsh: suspended  func

I can also resume with $ fg.


(C) If I start Vim with the $ vim command, then restart Vim by pressing Space R:

ubuntu% vim
zsh: suspended  catch_signal_usr1

Again, I can resume with $ fg.


(D) But, if I start Vim with the $ func function and restart it by pressing Space R:

ubuntu% func
ubuntu%

Nothing is displayed when I'm back at the prompt, and I can't resume Vim with $ fg.


(2) What does your shell say if you type jobs?

$ jobs has no output. Here's its output in the four previous cases:

(A)

ubuntu% jobs
[1]  + suspended  vim

(B)

ubuntu% jobs
[1]  + suspended (signal)  func

(C)

ubuntu% jobs
[1]  + suspended (signal)  catch_signal_usr1

(D)

ubuntu% jobs
ubuntu%

It seems the issue is specific to zsh at least up to 5.5.1, as I can't reproduce with bash 4.4.

1

The problem is starting a background job from a trap. The job seems to get “lost” sometimes. Changing vim to vim & makes the job be retained sometimes, so there may be a race condition.

You could avoid this by not starting the job from a trap. Set a flag in the trap, and fire up vim outside the trap, in the precmd hook. Here's an adaptation of your minimum example.

restart_vim=
catch_signal_usr1() {
  trap catch_signal_usr1 USR1
  restart_vim=1
}
precmd () {
  if [[ -n $restart_vim ]]; then
    restart_vim=
    vim
  fi
}
trap catch_signal_usr1 USR1
func() {
  vim
}

You lose the ability of popping Vim up to the foreground while editing a command prompt, but that doesn't really work anyway since vim and zsh would be competing for the terminal.

In your real code, you may run into trouble because you're starting vim from a subshell. Don't run the nv function in a subshell: use braces { … }around the body, not parentheses. Uselocal IFSto make theIFS` variable local.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.