7

I frequently have one root project open which I've :cd'd to. I also have other tabs with :lcd's set. This is great and works really nicely, but occasionally I end up looking at something in the main project as a result of opening a Buffer from the main project in one of my other windows...

at this point I'd like to do something to swap out of :lcd mode and back into :cd mode.

Reading :h cd I can't work it out.

It states:

When a :cd command is used, the current window will lose his local current
directory and will use the global current directory from now on.

However as I'm running on linux just running a blank :cd actually wipes out my global directory and sets it to ~

I can't find a way of accessing the :cd value that isn't modified by having an :lcd set

2
  • 1
    Just FYI, in the bit of documentation you quoted, when it refers to a :cd command, it's specifically referring to the variants of the command that do change the directory. So your question isn't actually Linux-specific; hence I'm removing the tag.
    – Rich
    Commented Feb 2, 2018 at 15:56
  • That documentation is misleading at best. To wipeout the local directory I need to issue :cd . or similar, changing the global dir. Commented Oct 26, 2020 at 14:54

3 Answers 3

6

As of vim 8.0.1489, one may use getcwd(-1) to retrieve the global directory. Thus, the following will work to restore a window to the global directory and cause it to forget its local directory (as explained in the documentation):

if haslocaldir()
    execute 'cd' getcwd(-1)
endif

This feature was available in neovim before it made it to vim. Prior to vim 8.0.1489, there was no simple way to determine the global directory (though there is haslocaldir() to tell if a window has been issued an :lcd). Two possible ways are mksession and set cpo-=s+new. One practical option would be to wrap the :cd command. For example,

au VimEnter * let g:my_project_dir = getcwd()
command! -nargs=? -complete=dir Cd execute 'cd' <q-args> | let g:my_project_dir=getcwd()
cnoreabbrev <expr> cd getcmdtype()==':' && getcmdline()=='cd' ? 'Cd' : 'cd'

When you want to restore a window to the global directory you can use

command! RestoreCwd execute 'cd' g:my_project_dir
7
  • Are you the one behind github.com/vim/vim/pull/2606 ? Commented Feb 1, 2018 at 17:55
  • @LucHermitte yes
    – Mass
    Commented Feb 2, 2018 at 17:35
  • this seems like the least hacky way to do it, thanks!
    – JonnyRaa
    Commented Feb 5, 2018 at 10:58
  • 1
    I've updated my answer to reflect that vim (patch 1489) now allows allows you to get the global directory
    – Mass
    Commented Feb 10, 2018 at 3:08
  • Hey, unless I mis-read, I think you mean getcwd(-1, -1)? Because, at least according to my NeoVim's docs / my experimentation, getcwd(-1) gets the tab-local working-dir, not the process-global working-dir. Passing both arguments as -1 seems to work, though! Commented Feb 1, 2021 at 11:12
3

You can access the working directory of another window with the function getcwd().

So although this isn't the most convenient or robust solution, if your root project (with the global working directory) is in the first, leftmost tab page, you could use the command:

:execute 'cd' getcwd(1, 1)

This will remove the local current directory and set the global current directory to be the current directory of the topmost window in the leftmost tab.

If the root project is always in the leftmost tab page, then you could set up a mapping or a command to make this easier:

:command! Gcd execute 'cd' getcwd(1, 1)

You can then invoke the command with :Gcd.

2

One way to find out the current global directory, in case there exists a window where we aren't using a current local directory could be to search for this window.

For each tab, we could filter the windows with no current local dir with:

let wins = filter(range(1, tabpagewinnr(tab, '$')), '! haslocaldir(v:val, tab)')

If the result isn't empty, then we have the current global directory with getwcd(wins[0], tab).

A complete solution would then be:

let wins_per_tabs = map(range(1, tabpagenr('$')), "range(1, tabpagewinnr(v:val, '$'))")

let wins_tabs_with_glob_dir = [] " list filled only when it matches
call map(deepcopy(wins_per_tabs), {tab, wins -> map(wins, 'haslocaldir(v:val,tab)? [0,0] : add(g:wins_tabs_with_glob_dir, [v:val, tab+1])')})

if !empty(wins_tabs_with_glob_dir)
    let glob_cwd = call('getcwd', wins_tabs_with_glob_dir[0])
    " and do whatever you want with it
else
    " we have no idea what it was...
endif

(As you see, I don't like :for loops, they are usually slower, and they are a nightmare to debug)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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