如何在Vim和终端中始终拥有相同的当前目录?

5
我想让终端的当前目录与我的VIM一致。
例如:
在终端中:
> pwd
=> /Users/rege
> vim

然后在VIM中
:cd /Users/rege/project
<Ctrl-z>(for suspend)

在终端中
> pwd
=> /Users/rege/project

我正在使用MacOS,zsh和tmux。
我需要这个,因为当我尝试在VIM中使用标签时,标签是从我的终端目录而不是vim目录中检查的。
因此,每当我更改VIM当前目录时,我需要始终更改终端当前目录。

1
:shell 是什么?它会在 Vim 当前目录中打开一个新的 shell。 - romainl
我解释得更清楚了。 - tomekfranek
4个回答

2
在bash或zsh以及Unix上,你可以这样做:进程当前工作目录在/proc/{PID}/cwd中表示为指向实际目录的符号链接。关于zsh,下面的代码可以完成这项工作:
function precmd()
{
    emulate -L zsh
    (( $#jobstates == 1 )) || return
    local -i PID=${${${(s.:.)${(v)jobstates[1]}}[3]}%\=*}
    cd $(readlink /proc/$PID/cwd)
}

注意:使用此代码后,您将无法在终端中永久切换目录,只能在vim中或在一个命令的持续时间内(使用cd other-dir && some command)。

注2:我不知道如何在bash中表达这个意思。直接的方法是获取shell的所有子进程的PID(使用ps --ppid $$ -o CMD),过滤掉ps进程(它也会显示为子进程),检查是否只有一个其他子进程,并像上面的最后一行那样使用其PID。但我相信还有更好的方法,可以使用一些shell内置命令,就像我使用zsh的$jobstates关联数组一样。我也不记得precmd在bash中的类比是什么。


另一个想法是,在您执行<C-z>时,使vim将其当前目录保存到某个文件中,并使shell在precmd中读取该文件:

" In .vimrc:
function s:CtrlZ()
    call writefile([fnamemodify('.', ':p')], $CWDFILE, 'b')
    return "\<C-z>"
endfunction
nnoremap <expr> <C-z> <SID>CtrlZ()

# In .zshrc
function vim()
{
    local -x CWDFILE=~/.workdirs/$$
    test -d $CWDFILE:h || mkdir $CWDFILE:h
    vim $@
}
function precmd()
{
    local CWDFILE=~/.workdirs/$$
    test -e $CWDFILE && cd "$(cat $CWDFILE)"
}

将以上代码移植到bash应该更加容易。


在 Mac OS X 上不可用 /proc。你的第二个想法看起来不错。 - romainl
看起来很好,但我希望不是在<C-z>上固定它,而是当我在VIM中运行:cd方法时。 - tomekfranek
@regedarek 为什么?在任何情况下都不会更改目录,直到您暂停vim(当前进程正在运行时,shell无事可做),运行:cd只会产生一堆无用的写入。 - ZyX
TK!! 我搜寻了那么久... 在 bash 中,PROMPT_COMMAND 允许在返回目录时更改目录。 我正在制作另一个答案(从你的复制而来)。 - Tinmarino

2
在你暂停Vim后,在shell中发出什么样的命令?Vim的`:!command`是否足够?
使用`set autochdir`,Vim的当前目录会随着你从一个文件跳转到另一个文件而改变。使用这个设置,一个简单的`:!ctags -R .`命令将始终在当前文件所在的目录中创建一个`tags`文件。
另一个有用的设置是`set tags=./tags,tags;$HOME`,它告诉Vim在当前文件所在的目录中查找`tags`文件,然后一直向上查找,直到达到你的`~/`目录。你可以修改终点以适应你的需求。这使得你可以在编辑项目中的任何文件时使用位于项目根目录下的`tags`文件。
因此,基本上你可以不用离开Vim就能完成很多工作。
如果你确实需要返回到shell中发出命令,则可以使用`:shell`(或`:sh`)来启动一个新的shell,并带有Vim的当前目录。当你完成后,只需输入`$ exit`即可返回Vim。
$ pwd
/home/romainl
$ vim
:cd Projects
:sh
$ pwd
/home/romainl/Projects
$ exit

0

针对使用bash的用户:

Vim:pwd保存在<c-z>(使用mapgetpwd())。
Bash:在提示命令之前,使用PROMPT_COMMAND进入vim指定的目录。

.bashrc

PROMPT_COMMAND='read -r line 2>/dev/null </tmp/cd_vim'\
'&& > /tmp/cd_vim && cd ${line##\r};'$PROMPT_COMMAND

vimrc

function! s:CtrlZ() call writefile([getcwd(),''], '/tmp/cd_vim', 'b')
    return "\<C-z>"
endfunction
nnoremap <expr> <C-z> <SID>CtrlZ()

这是ZyX的答案,为bash编辑过的https://dev59.com/r2fWa4cB1Zd3GeqPmPz9#12241861


0

你可以像这样打开一个新的终端

:!xterm -e bash -c "cd %:p:h;bash" &

实际上我是在我的 .vimrc 文件中写的这个

nmap <F3> :!xterm -e bash -c "cd %:p:h;bash" &<CR> | :redraw!

抱歉,但这不是我想要实现的。 - tomekfranek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接