nerdtree

A tree explorer plugin for vim.
Index Commits Files Refs
commit b877fc65d80421fe44bb719e53c7806186337293
parent b0c44c7be1a094dc6b9e8e93bd5c4325ab99ad04
Author: Jason Franklin <j_fra@fastmail.us>
Date:   Sat, 24 Jun 2017 14:09:21 -0400

Rename and expand the TreeDirNode glob method

Pull request #710 also brought to my attention some glaring code
duplication in the TreeDirNode class. This commit renames and
expands the glob method defined in PR #710 into a more general
purpose helper method.

The new method also ensures that 'wildignore' rules are applied
consistently. Issue #569 noted that the application of the
'wildignore' setting in populating a node's children was
unpredictable. When a node was rendered, "_initChildren()" would
load the children with its own call to "globpath()". Refreshing the
same node would then invoke "globpath()" again, but with different
arguments.

The result was that a node's children were initialized using the
'g:NERDTreeRespectWildIgnore' setting, but refreshing ignored this
setting. So, if it the setting was 0, "test.class" might render when
the parent was initialized, and then be removed on refresh (if
'wildignore' contained '*.class').

This commit solves this problem by ensuring that the NERDTree
setting mentioned above is only checked and applied to a node's
children in one place by cleaning up the duplication.

Fixes #569.

Diffstat:
Mlib/nerdtree/tree_dir_node.vim | 152++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
1 file changed, 84 insertions(+), 68 deletions(-)
diff --git a/lib/nerdtree/tree_dir_node.vim b/lib/nerdtree/tree_dir_node.vim
@@ -206,15 +206,23 @@ function! s:TreeDirNode.getDirChildren()
     return filter(self.children, 'v:val.path.isDirectory == 1')
 endfunction
 
-" FUNCTION: TreeDirNode._getGlobDir() {{{1
-" Return a path specification for this TreeDirNode that is suitable as an
-" argument to "globpath()".
+" FUNCTION: TreeDirNode._glob(pattern, all) {{{1
+" Return a list of strings naming the descendants of the directory in this
+" TreeDirNode object that match the specified glob pattern.
 "
-" Note: The result is constructed such that "globpath()" will return paths
-" relative to the working directory, if possible. This is necessary to ensure
-" that 'wildignore' rules for relative paths are obeyed.
-function! s:TreeDirNode._getGlobDir()
-
+" Args:
+" pattern: (string) the glob pattern to apply
+" all: (0 or 1) if 1, include "." and ".." if they match "pattern"; if 0,
+"      always exclude them
+"
+" Note: If the pathnames in the result list are below the working directory,
+" they are returned as pathnames relative to that directory. This is because
+" this function, internally, attempts to obey 'wildignore' rules that use
+" relative paths.
+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
@@ -226,7 +234,48 @@ function! s:TreeDirNode._getGlobDir()
         endif
     endif
 
-    return l:pathSpec
+    let l:globList = []
+
+    " See ":h version7.txt" for the details of the progression of the "glob()"
+    " and "globpath()" functions.
+    if v:version >= 704
+        let l:globList = globpath(l:pathSpec, a:pattern, !g:NERDTreeRespectWildIgnore, 1)
+    elseif v:version >= 703
+        let l:globString = globpath(l:pathSpec, a:pattern, !g:NERDTreeRespectWildIgnore)
+        let l:globList = split(l:globString, "\n")
+    else
+        let l:globString = globpath(l:pathSpec, a:pattern)
+        let l:globList = split(l:globString, "\n")
+    endif
+
+    " If "a:all" is false, filter "." and ".." from the output.
+    if !a:all
+
+        let l:toRemove = []
+
+        for l:file in l:globList
+            let l:tail = fnamemodify(l:file, ':t')
+
+            " Double the modifier if only a separator was stripped.
+            if l:tail == ''
+                let l:tail = fnamemodify(l:file, ':t:t')
+            endif
+
+            if l:tail == '.' || l:tail == '..'
+                call add(l:toRemove, l:file)
+                if len(l:toRemove) == 2
+                    break
+                endif
+            endif
+        endfor
+
+        if !empty(l:toRemove)
+            call remove(l:globList, index(l:globList, l:toRemove[0]))
+            call remove(l:globList, index(l:globList, l:toRemove[1]))
+        endif
+    endif
+
+    return l:globList
 endfunction
 
 "FUNCTION: TreeDirNode.GetSelected() {{{1
@@ -293,16 +342,7 @@ function! s:TreeDirNode._initChildren(silent)
     "remove all the current child nodes
     let self.children = []
 
-    "get an array of all the files in the nodes dir
-    let globDir = self._getGlobDir()
-
-    if version >= 703
-        let filesStr = globpath(globDir, '*', !g:NERDTreeRespectWildIgnore) . "\n" . globpath(globDir, '.*', !g:NERDTreeRespectWildIgnore)
-    else
-        let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
-    endif
-
-    let files = split(filesStr, "\n")
+    let files = self._glob('*', 1) + self._glob('.*', 0)
 
     if !a:silent && len(files) > g:NERDTreeNotificationThreshold
         call nerdtree#echo("Please wait, caching a large dir ...")
@@ -310,21 +350,13 @@ function! s:TreeDirNode._initChildren(silent)
 
     let invalidFilesFound = 0
     for i in files
-
-        "filter out the .. and . directories
-        "Note: we must match .. AND ../ since sometimes the globpath returns
-        "../ for path with strange chars (eg $)
-        if i[len(i)-3:2] != ".." && i[len(i)-2:2] != ".." &&
-         \ i[len(i)-2:1] != "." && i[len(i)-1] != "."
-            "put the next file in a new node and attach it
-            try
-                let path = g:NERDTreePath.New(i)
-                call self.createChild(path, 0)
-                call g:NERDTreePathNotifier.NotifyListeners('init', path, self.getNerdtree(), {})
-            catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
-                let invalidFilesFound += 1
-            endtry
-        endif
+        try
+            let path = g:NERDTreePath.New(i)
+            call self.createChild(path, 0)
+            call g:NERDTreePathNotifier.NotifyListeners('init', path, self.getNerdtree(), {})
+        catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
+            let invalidFilesFound += 1
+        endtry
     endfor
 
     call self.sortChildren()
@@ -457,48 +489,32 @@ function! s:TreeDirNode._openRecursively2(forceOpen)
 endfunction
 
 "FUNCTION: TreeDirNode.refresh() {{{1
-unlet s:TreeDirNode.refresh
 function! s:TreeDirNode.refresh()
     call self.path.refresh(self.getNerdtree())
 
     "if this node was ever opened, refresh its children
     if self.isOpen || !empty(self.children)
-        "go thru all the files/dirs under this node
+        let files = self._glob('*', 1) + self._glob('.*', 0)
         let newChildNodes = []
         let invalidFilesFound = 0
-        let globDir = self._getGlobDir()
-        let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
-        let files = split(filesStr, "\n")
         for i in files
-            "filter out the .. and . directories
-            "Note: we must match .. AND ../ cos sometimes the globpath returns
-            "../ for path with strange chars (eg $)
-            "if i !~# '\/\.\.\/\?$' && i !~# '\/\.\/\?$'
-
-            " Regular expression is too expensive. Use simply string comparison
-            " instead
-            if i[len(i)-3:2] != ".." && i[len(i)-2:2] != ".." &&
-             \ i[len(i)-2:1] != "." && i[len(i)-1] != "."
-                try
-                    "create a new path and see if it exists in this nodes children
-                    let path = g:NERDTreePath.New(i)
-                    let newNode = self.getChild(path)
-                    if newNode != {}
-                        call newNode.refresh()
-                        call add(newChildNodes, newNode)
-
-                    "the node doesnt exist so create it
-                    else
-                        let newNode = g:NERDTreeFileNode.New(path, self.getNerdtree())
-                        let newNode.parent = self
-                        call add(newChildNodes, newNode)
-                    endif
-
-
-                catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
-                    let invalidFilesFound = 1
-                endtry
-            endif
+            try
+                "create a new path and see if it exists in this nodes children
+                let path = g:NERDTreePath.New(i)
+                let newNode = self.getChild(path)
+                if newNode != {}
+                    call newNode.refresh()
+                    call add(newChildNodes, newNode)
+
+                "the node doesnt exist so create it
+                else
+                    let newNode = g:NERDTreeFileNode.New(path, self.getNerdtree())
+                    let newNode.parent = self
+                    call add(newChildNodes, newNode)
+                endif
+            catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
+                let invalidFilesFound = 1
+            endtry
         endfor
 
         "swap this nodes children out for the children we just read/refreshed