Skip to content

Add support for tab-local directories#4212

Closed
yegappan wants to merge 16 commits intovim:masterfrom
yegappan:tcd
Closed

Add support for tab-local directories#4212
yegappan wants to merge 16 commits intovim:masterfrom
yegappan:tcd

Conversation

@yegappan
Copy link
Member

@yegappan yegappan commented Apr 5, 2019

Add support for tab-local directories. Introduces the ":tcd" command
to change the directory of the current tabpage.

Credit: Idea from the neovim implementation.

@codecov-io
Copy link

codecov-io commented Apr 5, 2019

Codecov Report

Merging #4212 into master will decrease coverage by 0.05%.
The diff coverage is 98.3%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4212      +/-   ##
==========================================
- Coverage   79.93%   79.88%   -0.06%     
==========================================
  Files         108      108              
  Lines      141624   141774     +150     
==========================================
+ Hits       113211   113257      +46     
- Misses      28413    28517     +104
Impacted Files Coverage Δ
src/window.c 86.62% <100%> (+0.03%) ⬆️
src/if_py_both.h 76.59% <100%> (ø) ⬆️
src/eval.c 85.94% <100%> (+0.03%) ⬆️
src/evalfunc.c 88.74% <100%> (+0.01%) ⬆️
src/ex_docmd.c 80.19% <96%> (+0.05%) ⬆️
src/version.c 60% <0%> (-28.94%) ⬇️
src/gui_gtk_x11.c 48.47% <0%> (-0.2%) ⬇️
src/syntax.c 79.61% <0%> (-0.13%) ⬇️
src/screen.c 80.66% <0%> (-0.11%) ⬇️
src/getchar.c 76.4% <0%> (-0.05%) ⬇️
... and 9 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e61e548...15dca09. Read the comment docs.

@chdiza
Copy link

chdiza commented Apr 5, 2019

I don't see why this is necessary. Some Vimscript and :lcd'ing should handle all of this.

@vim-ml
Copy link

vim-ml commented Apr 5, 2019 via email

@brammool
Copy link
Contributor

brammool commented Apr 5, 2019 via email

@yegappan yegappan force-pushed the tcd branch 2 times, most recently from 8da916c to 186b4c5 Compare April 7, 2019 16:51
@ychin
Copy link
Contributor

ychin commented Apr 8, 2019

I do like the idea of a tab-local cwd. Few comments though:

  1. I found a bug in the current implementation. Do the following to reproduce the bug:

    tcd foo
    lcd bar
    mksession session.vim
    source session.vim
    echo getcwd()
    

    This script should output "bar", but it shows "foo" instead. Note that this only happens for the first split of a tab. It works properly for the other splits as far as I can tell.

  2. If my current window already as a local window dir (:lcd), then I do :tcd, it overrides the current localdir. I guess this makes sense since a :cd would override both :tcd and :lcd anyway so it's consistent but just wanted to point that out.

  3. I'm wondering if the logical extension of this would balloon to tab vs window-local settings (e.g. tab versions of :setlocal) and other related requests. But maybe that's for another day.

@vim-ml
Copy link

vim-ml commented Apr 8, 2019 via email

@LucHermitte
Copy link

Is :tcd just a convenience command equivalent to :tabdo :lcd? Or will Vim remember there is a current directory in the current tab?

@vim-ml
Copy link

vim-ml commented Apr 8, 2019 via email

@chdiza
Copy link

chdiza commented Apr 8, 2019

Vim will remember a separate working directory for each of the tab pages

But what is gained by this that couldn't just be had by making all windows in a tab have the same window-local directory?

I looked at the links mentioned, but none of them seem to really justify this tab-local business. ("I don't want to put some Vimscript in my .vimrc" is not a justification.) Some of them even show that there's a plugin that works, and in another, the OP was completely satisifed with just opening a new tab and doing :lcd foo, which means all new windows in that tab will have cwd of foo, once that method was pointed out by Christian.

@chdiza
Copy link

chdiza commented Apr 8, 2019

When you have more than one tab page with multiple windows in each, changing the global directory using ":cd" will change the directories of all the windows across the tab pages unless you have specified a window local directory using :lcd in each of the windows.

So why don't you just do the part after the word "unless"?

@LucHermitte
Copy link

Or will Vim remember there is a current directory in the current tab?

Yes. Vim will remember a separate working directory for each of the tab pages.
You now have a global working directory, a tab-local working directory and a
window-local working directory.

In that case, it'll be interesting to have access to this information from vim scripts. This means that has_localdir() should return 1 or 2 depending on the locality of the directory (*). We can also imagine a 3 value the day we have real project-local directories.

(*) It's my cd_without_sideeffects() function that makes me guess we'll need more information

function! lh#path#cd_without_sideeffects(path) abort
  let cd = exists('*haslocaldir') && haslocaldir()
        \ ? 'lcd '
        \ : 'cd '
  exe cd . a:path
endfunction

(I've seem a very similar pattern in plugins other than mine: I'm thinking about fugitive)

@brammool
Copy link
Contributor

brammool commented Apr 8, 2019 via email

@vim-ml
Copy link

vim-ml commented Apr 8, 2019 via email

@chdiza
Copy link

chdiza commented Apr 8, 2019

That's the whole point, you can ":tcd somewhere" and all windows follow. Without scripts or other stuff

I'm not questioning whether there's a use case for this; I'm questioning why this needs to be a built-in function rather than done by scripts/plugins. (E.g., What's the point of the new "win_id" functions if not to use them for something like this?)

Perhaps a better idea instead is a more general new command, :wintabdo {blah}, which will execute {blah} in all windows of the current tabpage. (Then one could do :wintabdo lcd /some/path, or wrap that in a custom one-line command.) That has the advantage of being of more general use beyond cd'ing.

@chdiza
Copy link

chdiza commented Apr 8, 2019

That you don't need to have the same window-local directory in all windows in the tab. That's the whole point, you can ":tcd somewhere" and all windows follow.

I don't understand. If you ":tcd somewhere" and all windows follow, then all windows in the tab have the same window-local directory. (Or else, I have no idea what "follow" means.)

@ychin
Copy link
Contributor

ychin commented Apr 8, 2019

:tcd will only change the local dir of all windows in a tab if they don’t already have :lcd set. It’s just an extra hierarchy in the cd -> lcd chain. Now it’s cd -> tcd -> lcd.

Otherwise you could use the same logic and argue you don’t actually need :cd as you could use Vimscript to do :tabdo windo lcd <folder> every time you want to do :cd, but that’s annoying to keep track of.

@chdiza
Copy link

chdiza commented Apr 8, 2019

as you could use Vimscript to do :tabdo windo lcd every time you want to do :cd, but that’s annoying to keep track of.

It wouldn't be annoying to keep track of, if you wrapped it in a custom command called ":Cd"!

For the life of me, I don't see why something like that can't be done instead of :tcd. Just define a function in Vimscript and call it from a command called ":Tcd". That's what Vimscript is for. Make it one of the standard plugins that's shipped if need be.

@ychin
Copy link
Contributor

ychin commented Apr 8, 2019

How do you keep track of whether a window’s lcd is set by a user’s manual lcd command or by your Tcd plugin (you need to remember this so that the next Tcd won’t stomp the tab windows with custom lcd)? I think it’s actually quite tricky to store that information. You could set up some complicated autocmd handling using DirChanged but gut feeling is it won’t handle all the cases.

Even with a plugin working not everyone will use the same plugin (even if it’s shipped with Vim) so if you are writing scripts to take advantage of this new feature, you suddenly need to decide if you want to bundle this random “Tcd” plugin as well. Functions like getcwd will no longer work natively and every time you use it you need to call the separate Tcd_getcwd() function, and for sessions you need an extra .x session file (actually I think the script that does that could be quite prone to bugs) so you can’t call raw mksession and need wrapper around that as well. It’s not just about a single command. Adding a global state like this involves a lot of moving parts that are hard to fully replicate in scripts without at least some quirks.

A lot of features and key bindings can technically be implemented in scripts too but it is a matter of compromise (ongoing maintainence cost vs benefit of built in availability).

@vim-ml
Copy link

vim-ml commented Apr 8, 2019 via email

@chdiza
Copy link

chdiza commented Apr 9, 2019

You also need to provide new getcwd() and haslocaldir() functions that return the proper win-local or tab-local directories.

You would store that info in some tablocal or windowlocal variable, in whatever function gets called by :Tcd. Then if you want to retrieve it, it's just :ec t:mytabdir or :ec w:did_i_lcd. (Or with simple functions that return those.)

Functions like getcwd will no longer work natively

This makes no sense. Every window has but one current working dir, and getcwd() tells you what it is. It doesn't stop working just because you lcd'd.

so if you are writing scripts to take advantage of this new feature, you suddenly need to decide if you want to bundle this random “Tcd” plugin as well.

There's nothing "random" about a built-in plugin. To make sure it's there, use v:version like with everything else. There's nothing onerous about having scripts that use standard plugins. Does nobody have scripts that make use of e.g. matchparen?

@justinmk
Copy link
Contributor

Something I don't like in the Neovim implementation: creating a new tabpage (:tabnew, :tabedit) "inherits" the tab-local dir of the current tabpage. Example:

:tcd /
:tabnew
:echo getcwd()  " => /

This is annoying. We did it this way to mimic window-local directories, but I don't think it's necessary. If someone wants "sticky" behavior they can use :lcd. Tab-local directories are more useful as an "alternative global CWD" for only a particular tab.

What is the behavior of this PR is that respect?

@brammool
Copy link
Contributor

brammool commented Apr 13, 2019 via email

@vim-ml
Copy link

vim-ml commented Apr 13, 2019 via email

@justinmk
Copy link
Contributor

Are you suggesting that we use the tab-local directory (if set) of the previous tabpage for the new window in the new tabpage? If it is not set, then use the global directory. T

No. I am speaking specifically of the case where there is no window-local directory, only a tab-local directory. Having that tab-local directory "stick" to all new tabs is really annoying.

@vim-ml
Copy link

vim-ml commented Apr 13, 2019 via email

@vim-ml
Copy link

vim-ml commented Apr 13, 2019 via email

@yegappan yegappan force-pushed the tcd branch 2 times, most recently from 5f2c556 to 4517142 Compare April 27, 2019 05:39
@skywind3000
Copy link

Great, now plugins have to change directories like this:

" change directory with right command
function! s:chdir(path)
	if has('nvim')
		let cmd = haslocaldir()? 'lcd' : (haslocaldir(-1, 0)? 'tcd' : 'cd')
	else
		let cmd = haslocaldir()? ((haslocaldir() == 1)? 'lcd' : 'tcd') : 'cd'
	endif
	silent execute cmd . ' '. fnameescape(a:path)
endfunc

let previous= getcwd()
call s:chdir(somewhere)
...
call s:chdir(previous)

k-takata added a commit to k-takata/minpac that referenced this pull request Apr 28, 2019
@brammool
Copy link
Contributor

brammool commented Apr 28, 2019 via email

@skywind3000
Copy link

temporarily changing directory is used widely in plugins, maybe a function form is better:

function chdir(path, mode = 0)

It will change current directory to path, and returns the previous path for success, empty string for failure. When mode is zero (by default), it behaves like normal cd command, otherwise it will take care of window/tab local directory like ecd.

And life will be much easier with chdir():

let previous = chdir(somewhere, 1)
...
chdir(previous)

No need for "exe" statement, no need for "fnameescape" too.

@skywind3000
Copy link

Or, introduce a bang modifier to command cd:

:cd! somewhere

zeertzjq added a commit to zeertzjq/neovim that referenced this pull request Oct 9, 2021
Problem:    Cannot set a directory for a tab page.
Solution:   Add the tab-local directory. (Yegappan Lakshmanan, closes vim/vim#4212)
vim/vim@00aa069
zeertzjq added a commit to zeertzjq/neovim that referenced this pull request Oct 9, 2021
Problem:    Cannot set a directory for a tab page.
Solution:   Add the tab-local directory. (Yegappan Lakshmanan, closes vim/vim#4212)
vim/vim@00aa069
zeertzjq added a commit to zeertzjq/neovim that referenced this pull request Nov 13, 2023
Problem:    Cannot set a directory for a tab page.
Solution:   Add the tab-local directory. (Yegappan Lakshmanan, closes vim/vim#4212)

vim/vim@00aa069

Session-related changes only.

Co-authored-by: Bram Moolenaar <Bram@vim.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants