4

After :term ++curwin zsh, I use that buffer as an alternative of tmux window.

But there is one caveat, if I cd in zsh, vim will not know.

Which cause problem in below case

In terminal normal mode, gf will not work correctly, since vim don't know zsh has changed directory.

After googling, I found zsh support hook for directory change.

The problem is, my vim & shell expertise is insufficient to use that hook to tell vim change local directory of corresponding terminal buffer.

5
  1. Create a special function in your vimrc that's callable from terminal, it's name must starts with Tapi_.
" arglist : [ cwd ]
" change window local working directory
function! Tapi_lcd(bufnum, arglist)
  let winid = bufwinid(a:bufnum)
  let cwd = get(a:arglist, 0, '')
  if winid == -1 || empty(cwd)
    return
  endif
  call win_execute(winid, 'lcd ' . cwd)
endfunction
  1. create a function in your zshrc to call Tapi_lcd in terminal:
cdv()
{
  printf '\033]51;["call", "Tapi_lcd", ["%s"]]\007' "$(pwd)"
}

The leading \033]51; and the trailing \007 are special escape sequence recognized by vim.

check :h terminal-api for more detail.

You can now use cdv in terminal to change window local directory now (personally I would stop here, it's good enough for me).

  1. hook cd and cdv
autoload -Uz chpwd_recent_dirs cdr add-zsh-hook
add-zsh-hook chpwd cdv

Now they are hooked, cdv is called everytime you cd . There are some side effect you should know, as some subshell enviroment (such as $()) is a duplicate of the current shell enviroment, the hook works in such subshell too. For example:

( cd / )

Above command will trigger cdv, it change your working directory to /, if you want to avoid that, you need to filter out subshell cd:

cdv()
{
  if [[ $ZSH_SUBSHELL -eq 0 ]] ; then
    printf '\033]51;["call", "Tapi_lcd", ["%s"]]\007' "$(pwd)"
  fi
}

I'm not a shell veteran, the hook make me nervous, I wouldn't use it,.

update for bash

There has no hook in bash, but we can use alias and function to hook cd and cdv. After step 2, Put this in your bashrc:

cdv()
{
  printf '\033]51;["call", "Tapi_lcd", ["%s"]]\007' "$(pwd)"
}

cd_and_cdv()
{
  cd "$@" && cdv
}

hookvim()
{
    alias cd=cd_and_cdv
}

execute hookvim after entering terminal, done. This solution works for zsh too.

update: set up hook automatically

:h terminal-unix add a bunch of enviroment variable to terminal inside vim, we can use it to setup hook.

Put this to you zshrc/bashrc :

if [[ ! -z "$VIM_TERMINAL" ]]; then
   hookvim
fi
  • Wouldn't work in bash, right? – eyal karni Nov 8 at 10:11
  • 1
    @eyalkarni See update. – dedowsdi Nov 8 at 11:43
  • 1
    you could also hook into $PROMPT_COMMAND – Christian Brabandt Nov 8 at 12:14
  • fantasy! Never thought it could be so simple. – qeatzy Nov 10 at 6:36
  • Didn't work for me in neoterm. cdv does nothing. – eyal karni Nov 16 at 11:27
0

A slight adaptation of @dedowsdi answer for neoterm. This plugin https://github.com/kassio/neoterm that provides improved terminal.

you can use this in the bash_profile

if [[ ! -z "$NVIM_LISTEN_ADDRESS" ]]; then
hookvim
fi

But I don't. Because in the code I assume that the terminal is in an opened tab to the right (switch to the window and back). one can upgrade the code a bit to avoid this. Notice that you also need to have neovim-remote for this to work.

cdv()
{
  #printf '\033]51;["call", "Tapi_lcd", ["%s"]]\007' "$(pwd)"
  nvr --servername $NVIM_LISTEN_ADDRESS --remote-send '<c-\><c-N><c-w>h:lcd '"$(pwd)"'<CR><c-w>li'
}

cd_and_cdv()
{
  cd "$@" && cdv
}

hookvim()
{
alias cd=cd_and_cdv
}

I open the terminal with (the argument is 0 for this to work)

set splitright
function! TermOV(use_file_dir)    
    let k=g:neoterm.last_id+1
    vertical Tnew
    exe k."T . /etc/bashrc"
    exe k."T . ~/.bash_profile"
    if a:use_file_dir
        exe k."T cd " . expand('%:p:h')
    else
        exe k."T hookvim" 
    endif  

    exe k."Tclear"
endfunction

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.