nerdtree
nerdtree copied to clipboard
[Windows] Node path could have different case if the initial directory is entered in all lower case
Self-Diagnosis
- [x] I have searched the issues for an answer to my question.
- [ ] I have reviewed the NERDTree documentation.
:h NERDTree
- [ ] I have reviewed the Wiki.
- [ ] I have searched the web for an answer to my question.
Steps to Reproduce the Issue
- Edit a directory using lower case drive name (e.g.
tab edit c:\dir
) - Try to open a file or directory two level below the initial directory
Current Result (Include screenshots where appropriate.)
Some file and/or directory cannot be opened depending on whether the node path is stored with lower case drive name or upper case drive name.
Expected Result
File and or directory can be opened.
The Cause
- The initial glob of path names uses the directory name as entered by the user (
c:\dir
), this results in path names starting with lower case drive name (or whatever case the user entered since path names are case insensitive on Windows) - The glob for file names under the subdirectories of the initial directory is done using relative path to
cwd
(which have been set toc:\dir
, butgetcwd()
return its properly cased nameC:\dir
), thus when joined it results in different caseC:\dir\file
- This causes mismatch between the stored path names inside the tree nodes.
This is one example if I put call confirm('skipped '.a:path.str().' | '.self.path.str())
in tree_dir_node.vim#L125.
Although a:path
should be under self.path
, it is skipped because the drive name cases are different. NERDTree was opened by running tabed e:\projects\pip
.
Temporary fix
diff --git a/lib/nerdtree/creator.vim b/lib/nerdtree/creator.vim
index b9d45dc..53fbc9a 100644
--- a/lib/nerdtree/creator.vim
+++ b/lib/nerdtree/creator.vim
@@ -76,7 +76,8 @@ endfunction
" FUNCTION: s:Creator.CreateWindowTree(dir) {{{1
function! s:Creator.CreateWindowTree(dir)
let creator = s:Creator.New()
- call creator.createWindowTree(a:dir)
+ exec 'cd '.a:dir
+ call creator.createWindowTree(getcwd())
endfunction
" FUNCTION: s:Creator.createWindowTree(dir) {{{1
@char101, I'm not seeing the error you describe. Putting your call confirm(...)
statement in, I'm getting matching case, whether I use :tabe c:\projects
or :tabe C:\projects
, and directories and files open as expected. Perhaps we have some relevant differences in our Vim or NERDTree settings. Please provide a minimal .vimrc
file that will produce the error. Thanks.
Did you open a file or directory two level under the initial directory?
To demonstrate the problem you also can run echo glob("c:\projects\*')
the results should have lowercase prefix. That itself is a cause for case comparison mismatch. Because like I wrote before, the initial file list is retrieved using glob()
but for subsequent directory expansion the list is retrieved using the combination of getcwd()
and relative path.
I can't find any relevan vim settings that affects the glob result case.
I tried it again and it does not always happen. This is a sample directory structure where the file cannot be opened:
C:\ (or any drive)
projects\
pip\
test\
a.txt
Load nerdtree using ed c:\projects\pip
(without ending slash). Then try to edit a.txt
.
It seems like this behavior is affected by autochdir
.
If I don't set autochdir
the problem does not happen.
If I set autochdir
, and run ed c:\projects\pip
, then cwd
is set to C:\projects
and s:TreeDirNode._glob
uses relative path.
If I set autochdir
, and run ed c:\projects\pip\
, then cwd
is set to C:\projects\pip
but the problem does not happen.
So like I wrote, this is caused by s:TreeDirNode._glob
which uses absolute or relative path depending on whether the path is under cwd
or not. And that causes case mismatch, because glob('c\projects\pip\*') != getcwd().glob('pip\*')
if cwd
is c:\projects
.
The problem can also be fixed by not trying to make glob return relative path
diff --git a/lib/nerdtree/tree_dir_node.vim b/lib/nerdtree/tree_dir_node.vim
index f5f7682..3b1823c 100644
--- a/lib/nerdtree/tree_dir_node.vim
+++ b/lib/nerdtree/tree_dir_node.vim
@@ -273,16 +273,18 @@ function! s:TreeDirNode._glob(pattern, all)
" Construct a path specification such that globpath() will return
" relative pathnames, if possible.
- if self.path.str() ==# getcwd()
- let l:pathSpec = ','
- else
- let l:pathSpec = escape(fnamemodify(self.path.str({'format': 'Glob'}), ':.'), ',')
-
- " On Windows, the drive letter may be removed by "fnamemodify()".
- if nerdtree#runningWindows() && l:pathSpec[0] == nerdtree#slash()
- let l:pathSpec = self.path.drive . l:pathSpec
- endif
- endif
+ " if self.path.str() ==# getcwd()
+ " let l:pathSpec = ','
+ " else
+ " let l:pathSpec = escape(fnamemodify(self.path.str({'format': 'Glob'}), ':.'), ',')
+ "
+ " " On Windows, the drive letter may be removed by "fnamemodify()".
+ " if nerdtree#runningWindows() && l:pathSpec[0] == nerdtree#slash()
+ " let l:pathSpec = self.path.drive . l:pathSpec
+ " endif
+ " endif
+
+ let l:pathSpec = self.path.str({'format': 'Glob'})
let l:globList = []
This comparison in function! s:TreeDirNode._glob(pattern, all)
could also be problematic if self.path.str()
is lowercase.
if self.path.str() ==# getcwd()
Which version of Vim are you using? I'm on Neovim, and I don't see any of the issues you're describing. Maybe it's been fixed in Neovim.
I don't want to abandon the relative paths because I believe they're needed for g:NERDTreeRespectWildIgnore
to work. Try the change below and see if it solves the issue. It ignores the case of the first character (a drive letter or /
), but respects everything else. Then again, what happens if you try something like this, where the rest of the path differs in case from what was created?
C:\>mkdir foobar
C:\>mkdir foobar\SNAFU
C:\>mkdir foobar\SNAFU\boHIca
C:\>mkdir foobar\SNAFU\TarFu
C:\>dir > foobar\SNAFU\boHIca\dir.txt
C:\>vim c:\FOObar
diff --git a/lib/nerdtree/tree_dir_node.vim b/lib/nerdtree/tree_dir_node.vim
index f5f7682..de117ac 100644
--- a/lib/nerdtree/tree_dir_node.vim
+++ b/lib/nerdtree/tree_dir_node.vim
@@ -273,7 +273,8 @@ function! s:TreeDirNode._glob(pattern, all)
" Construct a path specification such that globpath() will return
" relative pathnames, if possible.
- if self.path.str() ==# getcwd()
+ if self.path.str()[0] ==? getcwd()[0] && self.path.str()[1:] ==# getcwd()[1:]
let l:pathSpec = ','
else
let l:pathSpec = escape(fnamemodify(self.path.str({'format': 'Glob'}), ':.'), ',')
I am using GVim 8.2.2825.
I feel that you don't get the idea of what I was trying to explain here.
The problem is not with using relative path (although the relative path detection does not always work, based on whether that path ends with \
or not, as I explained above).
The idea is:
If you have a directory and files in all uppercase:
C:\DIRECTORY
A.TXT
B.TXT
SUBDIRECTORY\
AA.TXT
BB.TXT
And you run this in vim: echo glob('c:\directory\*')
you get the output
c:\directory\A.TXT
c:\directory\B.TXT
c:\directory\SUBDIRECTORY
I also tried this in neovim
, the output is the same.
Now if I browse to C:\DIRECTORY\SUBDIRECTORY
in nerdtree
, it uses relative globbing combined with getcwd()
by cd
-ing to C:\DIRECTORY\SUBDIRECTORY
first.
Now what is the value of getcwd()
: C:\DIRECTORY\SUBDIRECTORY
What is the value of glob('*')
AA.TXT
BB.TXT
What is the value when the paths as combined
C;\DIRECTORY\SUBDIRECTORY\AA.TXT
C:\DIRECTORY\SUBDIRECTORY\BB.TXT
What is the path value of its parent node?
c:\directory\SUBDIRECTORY
And now when trying to open C:\DIRECTORY\SUBDIRECTORY\AA.TXT
, it checks whether C:\DIRECTORY\SUBDIRECTORY\AA.TXT
is under c:\directory\SUBDIRECTORY
which should be TRUE
but instead it is FALSE
because the case differs.
The solution is not to abandon relative path if relative path is required. You need to normalize the initial directory case to its real case. One way is by cd
-ing to the directory and then retrieving the directory name using getcwd()
.
I am using GVim 8.2.2825.
I feel that you don't get the idea of what I was trying to explain here.
The problem is not with using relative path (although the relative path detection does not always work, based on whether that path ends with
\
or not, as I explained above).The idea is:
If you have a directory and files in all uppercase:
C:\DIRECTORY A.TXT B.TXT SUBDIRECTORY\ AA.TXT BB.TXT
And you run this in vim:
echo glob('c:\directory\*')
you get the outputc:\directory\A.TXT c:\directory\B.TXT c:\directory\SUBDIRECTORY
I also tried this in
neovim
, the output is the same.Now if I browse to
C:\DIRECTORY\SUBDIRECTORY
innerdtree
, it uses relative globbing combined withgetcwd()
bycd
-ing toC:\DIRECTORY\SUBDIRECTORY
first.Now what is the value of
getcwd()
:C:\DIRECTORY\SUBDIRECTORY
What is the value of
glob('*')
AA.TXT BB.TXT
What is the value when the paths as combined
C;\DIRECTORY\SUBDIRECTORY\AA.TXT C:\DIRECTORY\SUBDIRECTORY\BB.TXT
What is the path value of its parent node?
c:\directory\SUBDIRECTORY
And now when trying to open
C:\DIRECTORY\SUBDIRECTORY\AA.TXT
, it checks whetherC:\DIRECTORY\SUBDIRECTORY\AA.TXT
is underc:\directory\SUBDIRECTORY
which should beTRUE
but instead it isFALSE
because the case differs.The solution is not to abandon relative path if relative path is required. You need to normalize the initial directory case to its real case. One way is by
cd
-ing to the directory and then retrieving the directory name usinggetcwd()
.
I've checked this example with the latest version of NERDTree, It seems that it has been fixed after the #1387 PR.