mirror of
https://github.com/preservim/nerdtree.git
synced 2025-11-09 11:53:48 -05:00
Add the NERDTreeCreator class. Stick all functions related to creating a primary/secondary/mirror nerdtree in there. We may break this down further in the future, but this is a good starting point. Make some of the interface binding functions in autoload/nerdtree public. This is needed since we are accessing some of them from NERDTreeCreator. Should be temporary until we get some kind of proper interface binding system set up.
529 lines
15 KiB
VimL
529 lines
15 KiB
VimL
"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:NERDTreeRoot
|
|
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 nerdtree#renderView()
|
|
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
|
|
"Closes this directory
|
|
function! s:TreeDirNode.close()
|
|
let self.isOpen = 0
|
|
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)
|
|
call self.addChild(newTreeNode, a:inOrder)
|
|
return newTreeNode
|
|
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.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.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() ==# 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._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 = []
|
|
|
|
"get an array of all the files in the nodes dir
|
|
let dir = self.path
|
|
let globDir = dir.str({'format': 'Glob'})
|
|
|
|
if version >= 703
|
|
let filesStr = globpath(globDir, '*', 1) . "\n" . globpath(globDir, '.*', 1)
|
|
else
|
|
let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
|
|
endif
|
|
|
|
let files = split(filesStr, "\n")
|
|
|
|
if !a:silent && len(files) > g:NERDTreeNotificationThreshold
|
|
call nerdtree#echo("Please wait, caching a large dir ...")
|
|
endif
|
|
|
|
let invalidFilesFound = 0
|
|
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 !~# '\/\.\/\?$'
|
|
|
|
"put the next file in a new node and attach it
|
|
try
|
|
let path = g:NERDTreePath.New(i)
|
|
call self.createChild(path, 0)
|
|
catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
|
|
let invalidFilesFound += 1
|
|
endtry
|
|
endif
|
|
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) {{{1
|
|
"Returns a new TreeNode object with the given path and parent
|
|
"
|
|
"Args:
|
|
"path: a path object representing the full filesystem path to the file/dir that the node represents
|
|
unlet s:TreeDirNode.New
|
|
function! s:TreeDirNode.New(path)
|
|
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 = {}
|
|
|
|
return newTreeNode
|
|
endfunction
|
|
|
|
"FUNCTION: TreeDirNode.open([opts]) {{{1
|
|
"Open the dir in the current tree or in a new tree elsewhere.
|
|
"
|
|
"If opening in the current tree, return the number of cached nodes.
|
|
unlet s:TreeDirNode.open
|
|
function! s:TreeDirNode.open(...)
|
|
let opts = a:0 ? a:1 : {}
|
|
|
|
if has_key(opts, 'where') && !empty(opts['where'])
|
|
let opener = g:NERDTreeOpener.New(self.path, opts)
|
|
call opener.open(self)
|
|
else
|
|
let self.isOpen = 1
|
|
if self.children ==# []
|
|
return self._initChildren(0)
|
|
else
|
|
return 0
|
|
endif
|
|
endif
|
|
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
|
|
" opens an explorer window for this node in the previous window (could be a
|
|
" nerd tree or a netrw)
|
|
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.New().createPrimary(self.path.str())
|
|
endfunction
|
|
|
|
"FUNCTION: TreeDirNode.openRecursively() {{{1
|
|
"Opens this treenode and all of its children whose paths arent 'ignored'
|
|
"because of the file filters.
|
|
"
|
|
"This method is actually a wrapper for the OpenRecursively2 method which does
|
|
"the work.
|
|
function! s:TreeDirNode.openRecursively()
|
|
call self._openRecursively2(1)
|
|
endfunction
|
|
|
|
"FUNCTION: TreeDirNode._openRecursively2() {{{1
|
|
"Opens this all children of this treenode recursively if either:
|
|
" *they arent filtered by file filters
|
|
" *a:forceOpen is 1
|
|
"
|
|
"Args:
|
|
"forceOpen: 1 if this node should be opened regardless of file filters
|
|
function! s:TreeDirNode._openRecursively2(forceOpen)
|
|
if self.path.ignore() ==# 0 || a:forceOpen
|
|
let self.isOpen = 1
|
|
if self.children ==# []
|
|
call self._initChildren(1)
|
|
endif
|
|
|
|
for i in self.children
|
|
if i.path.isDirectory ==# 1
|
|
call i._openRecursively2(0)
|
|
endif
|
|
endfor
|
|
endif
|
|
endfunction
|
|
|
|
"FUNCTION: TreeDirNode.refresh() {{{1
|
|
unlet s:TreeDirNode.refresh
|
|
function! s:TreeDirNode.refresh()
|
|
call self.path.refresh()
|
|
|
|
"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 newChildNodes = []
|
|
let invalidFilesFound = 0
|
|
let dir = self.path
|
|
let globDir = dir.str({'format': 'Glob'})
|
|
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 !~# '\/\.\/\?$'
|
|
|
|
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)
|
|
let newNode.parent = self
|
|
call add(newChildNodes, newNode)
|
|
endif
|
|
|
|
|
|
catch /^NERDTree.InvalidArgumentsError/
|
|
let invalidFilesFound = 1
|
|
endtry
|
|
endif
|
|
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.reveal(path) {{{1
|
|
"reveal the given path, i.e. cache and open all treenodes needed to display it
|
|
"in the UI
|
|
function! s:TreeDirNode.reveal(path)
|
|
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)
|
|
call nerdtree#renderView()
|
|
call n.putCursorHere(1,0)
|
|
return
|
|
endif
|
|
|
|
let p = a:path
|
|
while !p.getParent().equals(self.path)
|
|
let p = p.getParent()
|
|
endwhile
|
|
|
|
let n = self.findNode(p)
|
|
call n.reveal(a:path)
|
|
endfunction
|
|
|
|
"FUNCTION: TreeDirNode.removeChild(treenode) {{{1
|
|
"
|
|
"Removes the given treenode from this nodes set of children
|
|
"
|
|
"Args:
|
|
"treenode: the node to remove
|
|
"
|
|
"Throws a NERDTree.ChildNotFoundError if the given treenode is not found
|
|
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
|
|
"
|
|
"Sorts the children of this node according to alphabetical order and the
|
|
"directory priority.
|
|
"
|
|
function! s:TreeDirNode.sortChildren()
|
|
let CompareFunc = function("nerdtree#compareNodes")
|
|
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:NERDTreeCasadeOpenSingleChildDir == 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:
|