" ============================================================================ " CLASS: TreeDirNode " " A subclass of NERDTreeFileNode. " " The 'composite' part of the file/dir composite. " ============================================================================ let s:TreeDirNode = copy(g:NERDTreeFileNode) let g:NERDTreeDirNode = s:TreeDirNode " FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{1 " Class method that returns the highest cached ancestor of the current root. function! s:TreeDirNode.AbsoluteTreeRoot() let currentNode = b:NERDTree.root while currentNode.parent != {} let currentNode = currentNode.parent endwhile return currentNode endfunction " FUNCTION: TreeDirNode.activate([options]) {{{1 unlet s:TreeDirNode.activate function! s:TreeDirNode.activate(...) let opts = a:0 ? a:1 : {} call self.toggleOpen(opts) call self.getNerdtree().render() call self.putCursorHere(0, 0) endfunction " FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{1 " Adds the given treenode to the list of children for this node " " Args: " -treenode: the node to add " -inOrder: 1 if the new node should be inserted in sorted order function! s:TreeDirNode.addChild(treenode, inOrder) call add(self.children, a:treenode) let a:treenode.parent = self if a:inOrder call self.sortChildren() endif endfunction " FUNCTION: TreeDirNode.close() {{{1 " Mark this TreeDirNode as closed. function! s:TreeDirNode.close() " Close all directories in this directory node's cascade. This is " necessary to ensure consistency when cascades are rendered. for l:dirNode in self.getCascade() let l:dirNode.isOpen = 0 endfor endfunction " FUNCTION: TreeDirNode.closeChildren() {{{1 " Closes all the child dir nodes of this node function! s:TreeDirNode.closeChildren() for i in self.children if i.path.isDirectory call i.close() call i.closeChildren() endif endfor endfunction " FUNCTION: TreeDirNode.createChild(path, inOrder) {{{1 " Instantiates a new child node for this node with the given path. The new " nodes parent is set to this node. " " Args: " path: a Path object that this node will represent/contain " inOrder: 1 if the new node should be inserted in sorted order " " Returns: " the newly created node function! s:TreeDirNode.createChild(path, inOrder) let newTreeNode = g:NERDTreeFileNode.New(a:path, self.getNerdtree()) call self.addChild(newTreeNode, a:inOrder) return newTreeNode endfunction " FUNCTION: TreeDirNode.displayString() {{{1 " Assemble and return a string that can represent this TreeDirNode object in " the NERDTree window. function! s:TreeDirNode.displayString() let l:result = '' " Build a label that identifies this TreeDirNode. let l:label = '' let l:cascade = self.getCascade() for l:dirNode in l:cascade let l:label .= l:dirNode.path.displayString() endfor " Select the appropriate open/closed status indicator symbol. if l:cascade[-1].isOpen let l:symbol = g:NERDTreeDirArrowCollapsible else let l:symbol = g:NERDTreeDirArrowExpandable endif let l:flags = l:cascade[-1].path.flagSet.renderToString() let l:result = l:symbol . ' ' . l:flags . l:label return l:result endfunction " FUNCTION: TreeDirNode.findNode(path) {{{1 " Will find one of the children (recursively) that has the given path " " Args: " path: a path object unlet s:TreeDirNode.findNode function! s:TreeDirNode.findNode(path) if a:path.equals(self.path) return self endif if stridx(a:path.str(), self.path.str(), 0) ==# -1 return {} endif if self.path.isDirectory for i in self.children let retVal = i.findNode(a:path) if retVal != {} return retVal endif endfor endif return {} endfunction " FUNCTION: TreeDirNode.getCascade() {{{1 " Return an array of dir nodes (starting from self) that can be cascade opened. function! s:TreeDirNode.getCascade() if !self.isCascadable() return [self] endif let vc = self.getVisibleChildren() let visChild = vc[0] return [self] + visChild.getCascade() endfunction " FUNCTION: TreeDirNode.getChildCount() {{{1 " Returns the number of children this node has function! s:TreeDirNode.getChildCount() return len(self.children) endfunction " FUNCTION: TreeDirNode.getChild(path) {{{1 " Returns child node of this node that has the given path or {} if no such node " exists. " " This function doesnt not recurse into child dir nodes " " Args: " path: a path object function! s:TreeDirNode.getChild(path) if stridx(a:path.str(), self.path.str(), 0) ==# -1 return {} endif let index = self.getChildIndex(a:path) if index ==# -1 return {} else return self.children[index] endif endfunction " FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{1 " returns the child at the given index " " Args: " indx: the index to get the child from " visible: 1 if only the visible children array should be used, 0 if all the " children should be searched. function! s:TreeDirNode.getChildByIndex(indx, visible) let array_to_search = a:visible? self.getVisibleChildren() : self.children if a:indx > len(array_to_search) throw "NERDTree.InvalidArgumentsError: Index is out of bounds." endif return array_to_search[a:indx] endfunction " FUNCTION: TreeDirNode.getChildIndex(path) {{{1 " Returns the index of the child node of this node that has the given path or " -1 if no such node exists. " " This function doesnt not recurse into child dir nodes " " Args: " path: a path object function! s:TreeDirNode.getChildIndex(path) if stridx(a:path.str(), self.path.str(), 0) ==# -1 return -1 endif "do a binary search for the child let a = 0 let z = self.getChildCount() while a < z let mid = (a+z)/2 let diff = a:path.compareTo(self.children[mid].path) if diff ==# -1 let z = mid elseif diff ==# 1 let a = mid+1 else return mid endif endwhile return -1 endfunction " FUNCTION: TreeDirNode.getDirChildren() {{{1 " Return a list of all child nodes from "self.children" that are of type " TreeDirNode. function! s:TreeDirNode.getDirChildren() return filter(self.children, 'v:val.path.isDirectory == 1') endfunction " 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. " " 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 let l:pathSpec = fnamemodify(self.path.str({'format': 'Glob'}), ':.') " On Windows, the drive letter may be removed by "fnamemodify()". if nerdtree#runningWindows() && l:pathSpec[0] == '\' let l:pathSpec = self.path.drive . l:pathSpec endif endif let l:globList = [] " See ":h version7.txt" and ":h version8.txt" for details on the " development of the "glob()" and "globpath()" functions. if v:version > 704 || (v:version == 704 && has('patch654')) let l:globList = globpath(l:pathSpec, a:pattern, !g:NERDTreeRespectWildIgnore, 1, 0) elseif v:version == 704 && has('patch279') let l:globList = globpath(l:pathSpec, a:pattern, !g:NERDTreeRespectWildIgnore, 1) elseif v:version > 702 || (v:version == 702 && has('patch051')) 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 for l:file in l:toRemove call remove(l:globList, index(l:globList, l:file)) endfor endif return l:globList endfunction " FUNCTION: TreeDirNode.GetSelected() {{{1 " Returns the current node if it is a dir node, or else returns the current " nodes parent unlet s:TreeDirNode.GetSelected function! s:TreeDirNode.GetSelected() let currentDir = g:NERDTreeFileNode.GetSelected() if currentDir != {} && !currentDir.isRoot() if currentDir.path.isDirectory ==# 0 let currentDir = currentDir.parent endif endif return currentDir endfunction " FUNCTION: TreeDirNode.getVisibleChildCount() {{{1 " Returns the number of visible children this node has function! s:TreeDirNode.getVisibleChildCount() return len(self.getVisibleChildren()) endfunction " FUNCTION: TreeDirNode.getVisibleChildren() {{{1 " Returns a list of children to display for this node, in the correct order " " Return: " an array of treenodes function! s:TreeDirNode.getVisibleChildren() let toReturn = [] for i in self.children if i.path.ignore(self.getNerdtree()) ==# 0 call add(toReturn, i) endif endfor return toReturn endfunction " FUNCTION: TreeDirNode.hasVisibleChildren() {{{1 " returns 1 if this node has any childre, 0 otherwise.. function! s:TreeDirNode.hasVisibleChildren() return self.getVisibleChildCount() != 0 endfunction " FUNCTION: TreeDirNode.isCascadable() {{{1 " true if this dir has only one visible child - which is also a dir function! s:TreeDirNode.isCascadable() if g:NERDTreeCascadeSingleChildDir == 0 return 0 endif let c = self.getVisibleChildren() return len(c) == 1 && c[0].path.isDirectory endfunction " FUNCTION: TreeDirNode._initChildren() {{{1 " Removes all childen from this node and re-reads them " " Args: " silent: 1 if the function should not echo any "please wait" messages for " large directories " " Return: the number of child nodes read function! s:TreeDirNode._initChildren(silent) "remove all the current child nodes let self.children = [] let files = self._glob('*', 1) + self._glob('.*', 0) if !a:silent && len(files) > g:NERDTreeNotificationThreshold call nerdtree#echo("Please wait, caching a large dir ...") endif let invalidFilesFound = 0 for i in files 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() if !a:silent && len(files) > g:NERDTreeNotificationThreshold call nerdtree#echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).") endif if invalidFilesFound call nerdtree#echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree") endif return self.getChildCount() endfunction " FUNCTION: TreeDirNode.New(path, nerdtree) {{{1 " Return a new TreeDirNode object with the given path and parent. " " Args: " path: dir that the node represents " nerdtree: the tree the node belongs to function! s:TreeDirNode.New(path, nerdtree) if a:path.isDirectory != 1 throw "NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object." endif let newTreeNode = copy(self) let newTreeNode.path = a:path let newTreeNode.isOpen = 0 let newTreeNode.children = [] let newTreeNode.parent = {} let newTreeNode._nerdtree = a:nerdtree return newTreeNode endfunction " FUNCTION: TreeDirNode.open([options]) {{{1 " Open this directory node in the current tree or elsewhere if special options " are provided. Return 0 if options were processed. Otherwise, return the " number of new cached nodes. function! s:TreeDirNode.open(...) let l:options = a:0 ? a:1 : {} " If special options were specified, process them and return. if has_key(l:options, 'where') && !empty(l:options['where']) let l:opener = g:NERDTreeOpener.New(self.path, l:options) call l:opener.open(self) return 0 endif " Open any ancestors of this node that render within the same cascade. let l:parent = self.parent while !empty(l:parent) && !l:parent.isRoot() if index(l:parent.getCascade(), self) >= 0 let l:parent.isOpen = 1 let l:parent = l:parent.parent else break endif endwhile let self.isOpen = 1 let l:numChildrenCached = 0 if empty(self.children) let l:numChildrenCached = self._initChildren(0) endif return l:numChildrenCached endfunction " FUNCTION: TreeDirNode.openAlong([opts]) {{{1 " recursive open the dir if it has only one directory child. " " return the level of opened directories. function! s:TreeDirNode.openAlong(...) let opts = a:0 ? a:1 : {} let level = 0 let node = self while node.path.isDirectory call node.open(opts) let level += 1 if node.getVisibleChildCount() == 1 let node = node.getChildByIndex(0, 1) else break endif endwhile return level endfunction " FUNCTION: TreeDirNode.openExplorer() {{{1 " Open an explorer window for this node in the previous window. The explorer " can be a NERDTree window or a netrw window. function! s:TreeDirNode.openExplorer() call self.open({'where': 'p'}) endfunction " FUNCTION: TreeDirNode.openInNewTab(options) {{{1 unlet s:TreeDirNode.openInNewTab function! s:TreeDirNode.openInNewTab(options) call nerdtree#deprecated('TreeDirNode.openInNewTab', 'is deprecated, use open() instead') call self.open({'where': 't'}) endfunction " FUNCTION: TreeDirNode._openInNewTab() {{{1 function! s:TreeDirNode._openInNewTab() tabnew call g:NERDTreeCreator.CreateTabTree(self.path.str()) endfunction " FUNCTION: TreeDirNode.openRecursively() {{{1 " Open this directory node and any descendant directory nodes whose pathnames " are not ignored. function! s:TreeDirNode.openRecursively() silent call self.open() for l:child in self.children if l:child.path.isDirectory && !l:child.path.ignore(l:child.getNerdtree()) call l:child.openRecursively() endif endfor endfunction " FUNCTION: TreeDirNode.refresh() {{{1 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) let files = self._glob('*', 1) + self._glob('.*', 0) let newChildNodes = [] let invalidFilesFound = 0 for i in files 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 let self.children = newChildNodes call self.sortChildren() if invalidFilesFound call nerdtree#echoWarning("some files could not be loaded into the NERD tree") endif endif endfunction " FUNCTION: TreeDirNode.refreshFlags() {{{1 unlet s:TreeDirNode.refreshFlags function! s:TreeDirNode.refreshFlags() call self.path.refreshFlags(self.getNerdtree()) for i in self.children call i.refreshFlags() endfor endfunction " FUNCTION: TreeDirNode.refreshDirFlags() {{{1 function! s:TreeDirNode.refreshDirFlags() call self.path.refreshFlags(self.getNerdtree()) endfunction " FUNCTION: TreeDirNode.reveal(path) {{{1 " reveal the given path, i.e. cache and open all treenodes needed to display it " in the UI " Returns the revealed node function! s:TreeDirNode.reveal(path, ...) let opts = a:0 ? a:1 : {} if !a:path.isUnder(self.path) throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str() endif call self.open() if self.path.equals(a:path.getParent()) let n = self.findNode(a:path) if has_key(opts, "open") call n.open() endif return n endif let p = a:path while !p.getParent().equals(self.path) let p = p.getParent() endwhile let n = self.findNode(p) return n.reveal(a:path, opts) endfunction " FUNCTION: TreeDirNode.removeChild(treenode) {{{1 " Remove the given treenode from "self.children". " Throws "NERDTree.ChildNotFoundError" if the node is not found. " " Args: " treenode: the node object to remove function! s:TreeDirNode.removeChild(treenode) for i in range(0, self.getChildCount()-1) if self.children[i].equals(a:treenode) call remove(self.children, i) return endif endfor throw "NERDTree.ChildNotFoundError: child node was not found" endfunction " FUNCTION: TreeDirNode.sortChildren() {{{1 " Sort "self.children" by alphabetical order and directory priority. function! s:TreeDirNode.sortChildren() let CompareFunc = function("nerdtree#compareNodesBySortKey") call sort(self.children, CompareFunc) endfunction " FUNCTION: TreeDirNode.toggleOpen([options]) {{{1 " Opens this directory if it is closed and vice versa function! s:TreeDirNode.toggleOpen(...) let opts = a:0 ? a:1 : {} if self.isOpen ==# 1 call self.close() else if g:NERDTreeCascadeOpenSingleChildDir == 0 call self.open(opts) else call self.openAlong(opts) endif endif endfunction " FUNCTION: TreeDirNode.transplantChild(newNode) {{{1 " Replaces the child of this with the given node (where the child node's full " path matches a:newNode's fullpath). The search for the matching node is " non-recursive " " Arg: " newNode: the node to graft into the tree function! s:TreeDirNode.transplantChild(newNode) for i in range(0, self.getChildCount()-1) if self.children[i].equals(a:newNode) let self.children[i] = a:newNode let a:newNode.parent = self break endif endfor endfunction " vim: set sw=4 sts=4 et fdm=marker: