如何从陷阱启动 Vim 并且在暂停后仍然能够恢复它?

如何从陷阱启动 Vim 并且在暂停后仍然能够恢复它?

我的中有以下代码~/.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
)

其目的是安装nv启动 Vim 实例和 Vim 服务器的功能。如果 Vim 服务器已经在运行,该函数应该将收到的文件参数发送到服务器。

到目前为止,效果很好。


我的 中有以下映射~/.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

USR1其目的是通过向父 shell发送信号来重新启动 Vim 。

我的程序中还有以下陷阱,~/.zshrc当 Vim 捕获到信号时,它会重新启动 Vim USR1

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

到目前为止,效果也很好。


但我注意到,如果我通过C-z在 shell 中按 , 来暂停 Vim,即使 Vim 进程仍在运行,我也无法恢复它(使用$ fg),因为 shell 没有任何工作。

zshrc这是我能够重现该问题的最低限度:

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

这是一个最小的vimrc

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

如果我用以下函数启动 Vim:

$ func

然后,按 重新启动 Vim Space R,然后按 暂停它C-z,一旦我回到 shell,我就可以看到 Vim 进程正在运行:

$ 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

但我无法恢复它:

$ fg
fg: no current job

如果我使用$ vim命令而不是$ func函数启动 Vim,我可以重新启动 Vim 进程、挂起它并恢复它。问题似乎来自于功能$ func


这是我的环境:

  • vim --version:VIM - Vi IMproved 8.1 由用户编译
  • 操作系统:Ubuntu 16.04.4 LTS
  • 终端模拟器:rxvt-unicode v9.22
  • 终端多路复用器:tmux 2.7
  • $TERM:tmux-256color
  • 外壳:zsh 5.5.1

如何从一个函数启动 Vim 并且在暂停后仍然能够恢复它?


编辑:

更多信息:

(1) 当您输入 Ctrl+Z 时,终端上会显示什么?

当我输入 时,什么也没有显示C-z

(A) 如果我使用$ vim以下命令启动 Vim,则按下 后将显示以下内容C-z

ubuntu% vim

zsh: suspended  vim

我可以继续使用$ fg.


(B) 如果我用以下$ func函数启动 Vim:

ubuntu% func
zsh: suspended  func

我也可以继续使用$ fg.


(C) 如果我使用以下$ vim命令启动 Vim,则按以下命令重新启动 Vim Space R

ubuntu% vim
zsh: suspended  catch_signal_usr1

同样,我可以继续使用$ fg.


(D) 但是,如果我使用该$ func函数启动 Vim 并按 重新启动它Space R

ubuntu% func
ubuntu%

当我回到提示符时没有显示任何内容,并且我无法使用 恢复 Vim $ fg


(2) 如果您输入 jobs,您的 shell 会说什么?

$ jobs没有输出。这是前面四种情况下的输出:

(A)

ubuntu% jobs
[1]  + suspended  vim

(二)

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

(C)

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

(四)

ubuntu% jobs
ubuntu%

看来这个问题zsh至少是针对 up to 的5.5.1,因为我无法用 bash 重现4.4

答案1

问题是从陷阱启动后台作业。这项工作有时似乎会“迷失”。更改vimvim &有时会保留作业,因此可能会出现竞争条件。

您可以通过不从陷阱开始作业来避免这种情况。在陷阱中设置一个标志,并在陷阱外的钩子中启动 vim precmd。这是您的最小示例的改编。

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
}

在编辑命令提示符时,您将失去将 Vim 弹出到前台的能力,但这实际上并不起作用,因为 vim 和 zsh 会竞争终端。

在您的实际代码中,您可能会遇到麻烦,因为您是从子 shell 启动 vim。不要nv在子 shell 中运行该函数:使用大括号 { … } around the body, not parentheses. Uselocal IFS to make theIFS` 变量 local。

相关内容