1 " ============================================================================
3 " Description: vim global plugin that provides a nice tree explorer
4 " Maintainer: Martin Grenfell <martin_grenfell at msn dot com>
5 " Last Change: 20 July, 2008
6 " License: This program is free software. It comes without any warranty,
7 " to the extent permitted by applicable law. You can redistribute
8 " it and/or modify it under the terms of the Do What The Fuck You
9 " Want To Public License, Version 2, as published by Sam Hocevar.
10 " See http://sam.zoy.org/wtfpl/COPYING for more details.
12 " ============================================================================
13 let s:NERD_tree_version = '2.14.0'
15 " SECTION: Script init stuff {{{1
16 "============================================================
17 if exists("loaded_nerd_tree")
21 echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!"
24 let loaded_nerd_tree = 1
26 "for line continuation - i.e dont want C in &cpo
30 "Function: s:initVariable() function {{{2
31 "This function is used to initialise a given variable to a given value. The
32 "variable is only initialised if it does not exist prior
35 "var: the name of the var to be initialised
36 "value: the value to initialise var to
39 "1 if the var is set, 0 otherwise
40 function! s:initVariable(var, value)
42 exec 'let ' . a:var . ' = ' . "'" . a:value . "'"
48 "SECTION: Init variable calls and other random constants {{{2
49 call s:initVariable("g:NERDChristmasTree", 1)
50 call s:initVariable("g:NERDTreeAutoCenter", 1)
51 call s:initVariable("g:NERDTreeAutoCenterThreshold", 3)
52 call s:initVariable("g:NERDTreeCaseSensitiveSort", 0)
53 call s:initVariable("g:NERDTreeChDirMode", 0)
54 if !exists("g:NERDTreeIgnore")
55 let g:NERDTreeIgnore = ['\~$']
57 call s:initVariable("g:NERDTreeHighlightCursorline", 1)
58 call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks')
59 call s:initVariable("g:NERDTreeMouseMode", 1)
60 call s:initVariable("g:NERDTreeNotificationThreshold", 100)
61 call s:initVariable("g:NERDTreeQuitOnOpen", 0)
62 call s:initVariable("g:NERDTreeShowBookmarks", 0)
63 call s:initVariable("g:NERDTreeShowFiles", 1)
64 call s:initVariable("g:NERDTreeShowHidden", 0)
65 call s:initVariable("g:NERDTreeShowLineNumbers", 0)
66 call s:initVariable("g:NERDTreeSortDirs", 1)
68 if !exists("g:NERDTreeSortOrder")
69 let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$']
71 "if there isnt a * in the sort sequence then add one
72 if count(g:NERDTreeSortOrder, '*') < 1
73 call add(g:NERDTreeSortOrder, '*')
77 "we need to use this number many times for sorting... so we calculate it only
79 let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*')
81 call s:initVariable("g:NERDTreeWinPos", "left")
82 call s:initVariable("g:NERDTreeWinSize", 31)
84 let s:running_windows = has("win16") || has("win32") || has("win64")
86 "init the shell commands that will be used to copy nodes, and remove dir trees
88 "Note: the space after the command is important
90 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ')
92 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ')
93 call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ')
97 "SECTION: Init variable calls for key mappings {{{2
98 call s:initVariable("g:NERDTreeMapActivateNode", "o")
99 call s:initVariable("g:NERDTreeMapChangeRoot", "C")
100 call s:initVariable("g:NERDTreeMapChdir", "cd")
101 call s:initVariable("g:NERDTreeMapCloseChildren", "X")
102 call s:initVariable("g:NERDTreeMapCloseDir", "x")
103 call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
104 call s:initVariable("g:NERDTreeMapExecute", "!")
105 call s:initVariable("g:NERDTreeMapFilesystemMenu", "m")
106 call s:initVariable("g:NERDTreeMapHelp", "?")
107 call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
108 call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
109 call s:initVariable("g:NERDTreeMapJumpNextSibling", "<C-j>")
110 call s:initVariable("g:NERDTreeMapJumpParent", "p")
111 call s:initVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>")
112 call s:initVariable("g:NERDTreeMapJumpRoot", "P")
113 call s:initVariable("g:NERDTreeMapOpenExpl", "e")
114 call s:initVariable("g:NERDTreeMapOpenInTab", "t")
115 call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T")
116 call s:initVariable("g:NERDTreeMapOpenRecursively", "O")
117 call s:initVariable("g:NERDTreeMapOpenSplit", "<tab>")
118 call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode)
119 call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit)
120 call s:initVariable("g:NERDTreeMapQuit", "q")
121 call s:initVariable("g:NERDTreeMapRefresh", "r")
122 call s:initVariable("g:NERDTreeMapRefreshRoot", "R")
123 call s:initVariable("g:NERDTreeMapToggleBookmarks", "B")
124 call s:initVariable("g:NERDTreeMapToggleFiles", "F")
125 call s:initVariable("g:NERDTreeMapToggleFilters", "f")
126 call s:initVariable("g:NERDTreeMapToggleHidden", "H")
127 call s:initVariable("g:NERDTreeMapUpdir", "u")
128 call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U")
130 "SECTION: Script level variable declaration{{{2
131 let s:escape_chars = " \\`\|\"#%&,?()\*^<>"
132 let s:NERDTreeWinName = '_NERD_tree_'
135 let s:tree_markup_reg = '[ \-+~`|]'
136 let s:tree_markup_reg_neg = '[^ \-+~`|]'
137 let s:tree_up_dir_line = '.. (up a dir)'
145 " SECTION: Commands {{{1
146 "============================================================
147 "init the command that users start the nerd tree with
148 command! -n=? -complete=dir NERDTree :call s:initNerdTree('<args>')
149 command! -n=? -complete=dir NERDTreeToggle :call s:toggle('<args>')
150 command! -n=0 NERDTreeClose :call s:closeTreeIfOpen()
151 command! -n=1 -complete=customlist,s:completeBookmarks NERDTreeFromBookmark call s:initNerdTree('<args>')
152 " SECTION: Auto commands {{{1
153 "============================================================
154 "Save the cursor position whenever we close the nerd tree
155 exec "autocmd BufWinLeave *". s:NERDTreeWinName ." call <SID>saveScreenState()"
156 "cache bookmarks when vim loads
157 autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
159 "SECTION: Classes {{{1
160 "============================================================
161 "CLASS: Bookmark {{{2
162 "============================================================
164 " FUNCTION: Bookmark.AddBookmark(name, path) {{{3
165 " Class method to add a new bookmark to the list, if a previous bookmark exists
166 " with the same name, just update the path for that bookmark
167 function! s:Bookmark.AddBookmark(name, path)
168 for i in s:Bookmark.Bookmarks()
174 call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
175 call s:Bookmark.Sort()
177 " Function: Bookmark.Bookmarks() {{{3
178 " Class method to get all bookmarks. Lazily initializes the bookmarks global
180 function! s:Bookmark.Bookmarks()
181 if !exists("g:NERDTreeBookmarks")
182 let g:NERDTreeBookmarks = []
184 return g:NERDTreeBookmarks
186 " Function: Bookmark.BookmarkExistsFor(name) {{{3
187 " class method that returns 1 if a bookmark with the given name is found, 0
189 function! s:Bookmark.BookmarkExistsFor(name)
191 call s:Bookmark.BookmarkFor(a:name)
193 catch /NERDTree.BookmarkNotFound/
197 " Function: Bookmark.BookmarkFor(name) {{{3
198 " Class method to get the bookmark that has the given name. {} is return if no
200 function! s:Bookmark.BookmarkFor(name)
201 for i in s:Bookmark.Bookmarks()
206 throw "NERDTree.BookmarkNotFound exception: no bookmark found for name: \"". a:name .'"'
208 " Function: Bookmark.BookmarkNames() {{{3
209 " Class method to return an array of all bookmark names
210 function! s:Bookmark.BookmarkNames()
212 for i in s:Bookmark.Bookmarks()
213 call add(names, i.name)
217 " FUNCTION: Bookmark.CacheBookmarks(silent) {{{3
218 " Class method to read all bookmarks from the bookmarks file intialize
219 " bookmark objects for each one.
222 " silent - dont echo an error msg if invalid bookmarks are found
223 function! s:Bookmark.CacheBookmarks(silent)
224 if filereadable(g:NERDTreeBookmarksFile)
225 let g:NERDTreeBookmarks = []
226 let g:NERDTreeInvalidBookmarks = []
227 let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
228 let invalidBookmarksFound = 0
229 for i in bookmarkStrings
234 let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
235 let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
238 let bookmark = s:Bookmark.New(name, s:Path.New(path))
239 call add(g:NERDTreeBookmarks, bookmark)
240 catch /NERDTree.Path.InvalidArguments/
241 call add(g:NERDTreeInvalidBookmarks, i)
242 let invalidBookmarksFound += 1
246 if invalidBookmarksFound
247 call s:Bookmark.Write()
249 call s:echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
252 call s:Bookmark.Sort()
255 " FUNCTION: Bookmark.compareTo(otherbookmark) {{{3
256 " Compare these two bookmarks for sorting purposes
257 function! s:Bookmark.compareTo(otherbookmark)
258 return a:otherbookmark.name < self.name
260 " FUNCTION: Bookmark.ClearAll() {{{3
261 " Class method to delete all bookmarks.
262 function! s:Bookmark.ClearAll()
263 for i in s:Bookmark.Bookmarks()
266 call s:Bookmark.Write()
268 " FUNCTION: Bookmark.delete() {{{3
269 " Delete this bookmark. If the node for this bookmark is under the current
270 " root, then recache bookmarks for its Path object
271 function! s:Bookmark.delete()
274 let node = self.getNode(1)
275 catch /NERDTree.BookmarkedNodeNotFound/
277 call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
279 call node.path.cacheDisplayString()
281 call s:Bookmark.Write()
283 " FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{3
284 " Gets the treenode for this bookmark
287 " searchFromAbsoluteRoot: specifies whether we should search from the current
288 " tree root, or the highest cached node
289 function! s:Bookmark.getNode(searchFromAbsoluteRoot)
290 let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : t:NERDTreeRoot
291 let targetNode = searchRoot.findNode(self.path)
293 throw "NERDTree.BookmarkedNodeNotFound no node was found for bookmark: " . self.name
297 " FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{3
298 " Class method that finds the bookmark with the given name and returns the
300 function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
301 let bookmark = s:Bookmark.BookmarkFor(a:name)
302 return bookmark.getNode(a:searchFromAbsoluteRoot)
304 " Function: Bookmark.InvalidBookmarks() {{{3
305 " Class method to get all invalid bookmark strings read from the bookmarks
307 function! s:Bookmark.InvalidBookmarks()
308 if !exists("g:NERDTreeInvalidBookmarks")
309 let g:NERDTreeInvalidBookmarks = []
311 return g:NERDTreeInvalidBookmarks
313 " FUNCTION: Bookmark.mustExist() {{{3
314 function! s:Bookmark.mustExist()
315 if !self.path.exists()
316 call s:Bookmark.CacheBookmarks(1)
317 throw "NERDTree.BookmarkPointsToInvalidLocation exception: the bookmark \"".
318 \ self.name ."\" points to a non existing location: \"". self.path.strForOS(0)
321 " FUNCTION: Bookmark.New(name, path) {{{3
322 " Create a new bookmark object with the given name and path object
323 function! s:Bookmark.New(name, path)
325 throw "NERDTree.IllegalBookmarkName illegal name:" . a:name
328 let newBookmark = copy(self)
329 let newBookmark.name = a:name
330 let newBookmark.path = a:path
333 " Function: Bookmark.setPath(path) {{{3
334 " makes this bookmark point to the given path
335 function! s:Bookmark.setPath(path)
336 let self.path = a:path
338 " Function: Bookmark.Sort() {{{3
339 " Class method that sorts all bookmarks
340 function! s:Bookmark.Sort()
341 let CompareFunc = function("s:compareBookmarks")
342 call sort(s:Bookmark.Bookmarks(), CompareFunc)
344 " Function: Bookmark.str() {{{3
345 " Get the string that should be rendered in the view for this bookmark
346 function! s:Bookmark.str()
347 let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name)
349 let pathStrMaxLen = pathStrMaxLen - &numberwidth
352 let pathStr = self.path.strForOS(0)
353 if len(pathStr) > pathStrMaxLen
354 let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
356 return '>' . self.name . ' ' . pathStr
358 " FUNCTION: Bookmark.toRoot() {{{3
359 " Make the node for this bookmark the new tree root
360 function! s:Bookmark.toRoot()
363 let targetNode = self.getNode(1)
364 catch /NERDTree.BookmarkedNodeNotFound/
365 let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
367 call targetNode.makeRoot()
369 call s:putCursorOnNode(targetNode, 0, 0)
372 " FUNCTION: Bookmark.ToRoot(name) {{{3
373 " Make the node for this bookmark the new tree root
374 function! s:Bookmark.ToRoot(name)
375 let bookmark = s:Bookmark.BookmarkFor(a:name)
376 call bookmark.toRoot()
380 "FUNCTION: Bookmark.validate() {{{3
381 function! s:Bookmark.validate()
382 if self.path.exists()
385 call s:Bookmark.CacheBookmarks(1)
387 call s:echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
392 " Function: Bookmark.Write() {{{3
393 " Class method to write all bookmarks to the bookmarks file
394 function! s:Bookmark.Write()
395 let bookmarkStrings = []
396 for i in s:Bookmark.Bookmarks()
397 call add(bookmarkStrings, i.name . ' ' . i.path.strForOS(0))
400 "add a blank line before the invalid ones
401 call add(bookmarkStrings, "")
403 for j in s:Bookmark.InvalidBookmarks()
404 call add(bookmarkStrings, j)
406 call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
408 "CLASS: TreeFileNode {{{2
409 "This class is the parent of the TreeDirNode class and constitures the
410 "'Component' part of the composite design pattern between the treenode
412 "============================================================
413 let s:TreeFileNode = {}
414 "FUNCTION: TreeFileNode.bookmark(name) {{{3
415 "bookmark this node with a:name
416 function! s:TreeFileNode.bookmark(name)
418 let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1)
419 call oldMarkedNode.path.cacheDisplayString()
420 catch /NERDTree.Bookmark\(DoesntExist\|NotFound\)/
423 call s:Bookmark.AddBookmark(a:name, self.path)
424 call self.path.cacheDisplayString()
425 call s:Bookmark.Write()
427 "FUNCTION: TreeFileNode.cacheParent() {{{3
428 "initializes self.parent if it isnt already
429 function! s:TreeFileNode.cacheParent()
430 if empty(self.parent)
431 let parentPath = self.path.getParent()
432 if parentPath.equals(self.path)
433 throw "NERDTree.CannotCacheParent exception: already at root"
435 let self.parent = s:TreeFileNode.New(parentPath)
438 "FUNCTION: TreeFileNode.compareNodes {{{3
439 "This is supposed to be a class level method but i cant figure out how to
440 "get func refs to work from a dict..
442 "A class level method that compares two nodes
445 "n1, n2: the 2 nodes to compare
446 function! s:compareNodes(n1, n2)
447 return a:n1.path.compareTo(a:n2.path)
450 "FUNCTION: TreeFileNode.clearBoomarks() {{{3
451 function! s:TreeFileNode.clearBoomarks()
452 for i in s:Bookmark.Bookmarks()
453 if i.path.equals(self.path)
457 call self.path.cacheDisplayString()
459 "FUNCTION: TreeFileNode.copy(dest) {{{3
460 function! s:TreeFileNode.copy(dest)
461 call self.path.copy(a:dest)
462 let newPath = s:Path.New(a:dest)
463 let parent = t:NERDTreeRoot.findNode(newPath.getParent())
465 call parent.refresh()
467 return parent.findNode(newPath)
470 "FUNCTION: TreeFileNode.delete {{{3
471 "Removes this node from the tree and calls the Delete method for its path obj
472 function! s:TreeFileNode.delete()
473 call self.path.delete()
474 call self.parent.removeChild(self)
477 "FUNCTION: TreeFileNode.equals(treenode) {{{3
479 "Compares this treenode to the input treenode and returns 1 if they are the
482 "Use this method instead of == because sometimes when the treenodes contain
483 "many children, vim seg faults when doing ==
486 "treenode: the other treenode to compare to
487 function! s:TreeFileNode.equals(treenode)
488 return self.path.str(1) == a:treenode.path.str(1)
491 "FUNCTION: TreeFileNode.findNode(path) {{{3
492 "Returns self if this node.path.Equals the given path.
493 "Returns {} if not equal.
496 "path: the path object to compare against
497 function! s:TreeFileNode.findNode(path)
498 if a:path.equals(self.path)
503 "FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3
505 "Finds the next sibling for this node in the indicated direction. This sibling
506 "must be a directory and may/may not have children as specified.
509 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
512 "a treenode object or {} if no appropriate sibling could be found
513 function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
514 "if we have no parent then we can have no siblings
516 let nextSibling = self.findSibling(a:direction)
518 while nextSibling != {}
519 if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen
522 let nextSibling = nextSibling.findSibling(a:direction)
528 "FUNCTION: TreeFileNode.findSibling(direction) {{{3
530 "Finds the next sibling for this node in the indicated direction
533 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
536 "a treenode object or {} if no sibling could be found
537 function! s:TreeFileNode.findSibling(direction)
538 "if we have no parent then we can have no siblings
541 "get the index of this node in its parents children
542 let siblingIndx = self.parent.getChildIndex(self.path)
545 "move a long to the next potential sibling node
546 let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1
548 "keep moving along to the next sibling till we find one that is valid
549 let numSiblings = self.parent.getChildCount()
550 while siblingIndx >= 0 && siblingIndx < numSiblings
552 "if the next node is not an ignored node (i.e. wont show up in the
553 "view) then return it
554 if self.parent.children[siblingIndx].path.ignore() == 0
555 return self.parent.children[siblingIndx]
559 let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1
567 "FUNCTION: TreeFileNode.isVisible() {{{3
568 "returns 1 if this node should be visible according to the tree filters and
569 "hidden file filters (and their on/off status)
570 function! s:TreeFileNode.isVisible()
571 return !self.path.ignore()
575 "FUNCTION: TreeFileNode.isRoot() {{{3
576 "returns 1 if this node is t:NERDTreeRoot
577 function! s:TreeFileNode.isRoot()
578 if !s:treeExistsForTab()
579 throw "NERDTree.TreeFileNode.IsRoot exception: No tree exists for the current tab"
581 return self.equals(t:NERDTreeRoot)
584 "FUNCTION: TreeFileNode.makeRoot() {{{3
585 "Make this node the root of the tree
586 function! s:TreeFileNode.makeRoot()
587 if self.path.isDirectory
588 let t:NERDTreeRoot = self
590 call self.cacheParent()
591 let t:NERDTreeRoot = self.parent
594 call t:NERDTreeRoot.open()
596 "change dir to the dir of the new root if instructed to
597 if g:NERDTreeChDirMode == 2
598 exec "cd " . t:NERDTreeRoot.path.strForEditCmd()
601 "FUNCTION: TreeFileNode.New(path) {{{3
602 "Returns a new TreeNode object with the given path and parent
605 "path: a path object representing the full filesystem path to the file/dir that the node represents
606 function! s:TreeFileNode.New(path)
607 if a:path.isDirectory
608 return s:TreeDirNode.New(a:path)
611 let newTreeNode = copy(self)
612 let newTreeNode.path = a:path
613 let newTreeNode.parent = {}
618 "FUNCTION: TreeFileNode.refresh() {{{3
619 function! s:TreeFileNode.refresh()
620 call self.path.refresh()
622 "FUNCTION: TreeFileNode.rename() {{{3
623 "Calls the rename method for this nodes path obj
624 function! s:TreeFileNode.rename(newName)
625 let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
626 call self.path.rename(newName)
627 call self.parent.removeChild(self)
629 let parentPath = self.path.getPathTrunk()
630 let newParent = t:NERDTreeRoot.findNode(parentPath)
633 call newParent.createChild(self.path, 1)
634 call newParent.refresh()
637 "FUNCTION: TreeFileNode.strDisplay() {{{3
639 "Returns a string that specifies how the node should be represented as a
643 "a string that can be used in the view to represent this node
644 function! s:TreeFileNode.strDisplay()
645 return self.path.strDisplay()
648 "CLASS: TreeDirNode {{{2
649 "This class is a child of the TreeFileNode class and constitutes the
650 "'Composite' part of the composite design pattern between the treenode
652 "============================================================
653 let s:TreeDirNode = copy(s:TreeFileNode)
654 "FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
655 "class method that returns the highest cached ancestor of the current root
656 function! s:TreeDirNode.AbsoluteTreeRoot()
657 let currentNode = t:NERDTreeRoot
658 while currentNode.parent != {}
659 let currentNode = currentNode.parent
663 "FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{3
664 "Adds the given treenode to the list of children for this node
667 "-treenode: the node to add
668 "-inOrder: 1 if the new node should be inserted in sorted order
669 function! s:TreeDirNode.addChild(treenode, inOrder)
670 call add(self.children, a:treenode)
671 let a:treenode.parent = self
674 call self.sortChildren()
678 "FUNCTION: TreeDirNode.close() {{{3
679 "Closes this directory
680 function! s:TreeDirNode.close()
684 "FUNCTION: TreeDirNode.closeChildren() {{{3
685 "Closes all the child dir nodes of this node
686 function! s:TreeDirNode.closeChildren()
687 for i in self.children
688 if i.path.isDirectory
690 call i.closeChildren()
695 "FUNCTION: TreeDirNode.createChild(path, inOrder) {{{3
696 "Instantiates a new child node for this node with the given path. The new
697 "nodes parent is set to this node.
700 "path: a Path object that this node will represent/contain
701 "inOrder: 1 if the new node should be inserted in sorted order
704 "the newly created node
705 function! s:TreeDirNode.createChild(path, inOrder)
706 let newTreeNode = s:TreeFileNode.New(a:path)
707 call self.addChild(newTreeNode, a:inOrder)
711 "FUNCTION: TreeDirNode.findNode(path) {{{3
712 "Will find one of the children (recursively) that has the given path
716 function! s:TreeDirNode.findNode(path)
717 if a:path.equals(self.path)
720 if stridx(a:path.str(1), self.path.str(1), 0) == -1
724 if self.path.isDirectory
725 for i in self.children
726 let retVal = i.findNode(a:path)
735 "FUNCTION: TreeDirNode.getChildCount() {{{3
736 "Returns the number of children this node has
737 function! s:TreeDirNode.getChildCount()
738 return len(self.children)
741 "FUNCTION: TreeDirNode.getChild(path) {{{3
742 "Returns child node of this node that has the given path or {} if no such node
745 "This function doesnt not recurse into child dir nodes
749 function! s:TreeDirNode.getChild(path)
750 if stridx(a:path.str(1), self.path.str(1), 0) == -1
754 let index = self.getChildIndex(a:path)
758 return self.children[index]
763 "FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{3
764 "returns the child at the given index
766 "indx: the index to get the child from
767 "visible: 1 if only the visible children array should be used, 0 if all the
768 "children should be searched.
769 function! s:TreeDirNode.getChildByIndex(indx, visible)
770 let array_to_search = a:visible? self.getVisibleChildren() : self.children
771 if a:indx > len(array_to_search)
772 throw "NERDTree.TreeDirNode.InvalidArguments exception. Index is out of bounds."
774 return array_to_search[a:indx]
777 "FUNCTION: TreeDirNode.getChildIndex(path) {{{3
778 "Returns the index of the child node of this node that has the given path or
779 "-1 if no such node exists.
781 "This function doesnt not recurse into child dir nodes
785 function! s:TreeDirNode.getChildIndex(path)
786 if stridx(a:path.str(1), self.path.str(1), 0) == -1
790 "do a binary search for the child
792 let z = self.getChildCount()
795 let diff = a:path.compareTo(self.children[mid].path)
808 "FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
809 "Returns the number of visible children this node has
810 function! s:TreeDirNode.getVisibleChildCount()
811 return len(self.getVisibleChildren())
814 "FUNCTION: TreeDirNode.getVisibleChildren() {{{3
815 "Returns a list of children to display for this node, in the correct order
818 "an array of treenodes
819 function! s:TreeDirNode.getVisibleChildren()
821 for i in self.children
822 if i.path.ignore() == 0
823 call add(toReturn, i)
829 "FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
830 "returns 1 if this node has any childre, 0 otherwise..
831 function! s:TreeDirNode.hasVisibleChildren()
832 return self.getVisibleChildCount() != 0
835 "FUNCTION: TreeDirNode._initChildren() {{{3
836 "Removes all childen from this node and re-reads them
839 "silent: 1 if the function should not echo any "please wait" messages for
842 "Return: the number of child nodes read
843 function! s:TreeDirNode._initChildren(silent)
844 "remove all the current child nodes
845 let self.children = []
847 "get an array of all the files in the nodes dir
849 let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
850 let files = split(filesStr, "\n")
852 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
853 call s:echo("Please wait, caching a large dir ...")
856 let invalidFilesFound = 0
859 "filter out the .. and . directories
860 "Note: we must match .. AND ../ cos sometimes the globpath returns
861 "../ for path with strange chars (eg $)
862 if i !~ '\.\.\/\?$' && i !~ '\.\/\?$'
864 "put the next file in a new node and attach it
866 let path = s:Path.New(i)
867 call self.createChild(path, 0)
868 catch /^NERDTree.Path.\(InvalidArguments\|InvalidFiletype\)/
869 let invalidFilesFound += 1
874 call self.sortChildren()
876 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
877 call s:echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).")
881 call s:echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree")
883 return self.getChildCount()
885 "FUNCTION: TreeDirNode.New(path) {{{3
886 "Returns a new TreeNode object with the given path and parent
889 "path: a path object representing the full filesystem path to the file/dir that the node represents
890 function! s:TreeDirNode.New(path)
891 if a:path.isDirectory != 1
892 throw "NERDTree.TreeDirNode.InvalidArguments exception. A TreeDirNode object must be instantiated with a directory Path object."
895 let newTreeNode = copy(self)
896 let newTreeNode.path = a:path
898 let newTreeNode.isOpen = 0
899 let newTreeNode.children = []
901 let newTreeNode.parent = {}
905 "FUNCTION: TreeDirNode.open() {{{3
906 "Reads in all this nodes children
908 "Return: the number of child nodes read
909 function! s:TreeDirNode.open()
911 if self.children == []
912 return self._initChildren(0)
918 "FUNCTION: TreeDirNode.openRecursively() {{{3
919 "Opens this treenode and all of its children whose paths arent 'ignored'
920 "because of the file filters.
922 "This method is actually a wrapper for the OpenRecursively2 method which does
924 function! s:TreeDirNode.openRecursively()
925 call self._openRecursively2(1)
928 "FUNCTION: TreeDirNode._openRecursively2() {{{3
929 "Opens this all children of this treenode recursively if either:
930 " *they arent filtered by file filters
934 "forceOpen: 1 if this node should be opened regardless of file filters
935 function! s:TreeDirNode._openRecursively2(forceOpen)
936 if self.path.ignore() == 0 || a:forceOpen
938 if self.children == []
939 call self._initChildren(1)
942 for i in self.children
943 if i.path.isDirectory == 1
944 call i._openRecursively2(0)
950 "FUNCTION: TreeDirNode.refresh() {{{3
951 function! s:TreeDirNode.refresh()
952 call self.path.refresh()
954 "if this node was ever opened, refresh its children
955 if self.isOpen || !empty(self.children)
956 "go thru all the files/dirs under this node
957 let newChildNodes = []
958 let invalidFilesFound = 0
960 let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
961 let files = split(filesStr, "\n")
963 if i !~ '\.\.$' && i !~ '\.$'
966 "create a new path and see if it exists in this nodes children
967 let path = s:Path.New(i)
968 let newNode = self.getChild(path)
970 call newNode.refresh()
971 call add(newChildNodes, newNode)
973 "the node doesnt exist so create it
975 let newNode = s:TreeFileNode.New(path)
976 let newNode.parent = self
977 call add(newChildNodes, newNode)
981 catch /^NERDTree.InvalidArguments/
982 let invalidFilesFound = 1
987 "swap this nodes children out for the children we just read/refreshed
988 let self.children = newChildNodes
989 call self.sortChildren()
992 call s:echoWarning("some files could not be loaded into the NERD tree")
997 "FUNCTION: TreeDirNode.removeChild(treenode) {{{3
999 "Removes the given treenode from this nodes set of children
1002 "treenode: the node to remove
1004 "Throws a NERDTree.TreeDirNode exception if the given treenode is not found
1005 function! s:TreeDirNode.removeChild(treenode)
1006 for i in range(0, self.getChildCount()-1)
1007 if self.children[i].equals(a:treenode)
1008 call remove(self.children, i)
1013 throw "NERDTree.TreeDirNode exception: child node was not found"
1016 "FUNCTION: TreeDirNode.sortChildren() {{{3
1018 "Sorts the children of this node according to alphabetical order and the
1019 "directory priority.
1021 function! s:TreeDirNode.sortChildren()
1022 let CompareFunc = function("s:compareNodes")
1023 call sort(self.children, CompareFunc)
1026 "FUNCTION: TreeDirNode.toggleOpen() {{{3
1027 "Opens this directory if it is closed and vice versa
1028 function! s:TreeDirNode.toggleOpen()
1036 "FUNCTION: TreeDirNode.transplantChild(newNode) {{{3
1037 "Replaces the child of this with the given node (where the child node's full
1038 "path matches a:newNode's fullpath). The search for the matching node is
1042 "newNode: the node to graft into the tree
1043 function! s:TreeDirNode.transplantChild(newNode)
1044 for i in range(0, self.getChildCount()-1)
1045 if self.children[i].equals(a:newNode)
1046 let self.children[i] = a:newNode
1047 let a:newNode.parent = self
1052 "============================================================
1054 "============================================================
1056 "FUNCTION: Path.bookmarkNames() {{{3
1057 function! s:Path.bookmarkNames()
1058 if !exists("self._bookmarkNames")
1059 call self.cacheDisplayString()
1061 return self._bookmarkNames
1063 "FUNCTION: Path.cacheDisplayString() {{{3
1064 function! s:Path.cacheDisplayString()
1065 let self.cachedDisplayString = self.getLastPathComponent(1)
1067 if self.isExecutable
1068 let self.cachedDisplayString = self.cachedDisplayString . '*'
1071 let self._bookmarkNames = []
1072 for i in s:Bookmark.Bookmarks()
1073 if i.path.equals(self)
1074 call add(self._bookmarkNames, i.name)
1077 if !empty(self._bookmarkNames)
1078 let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
1082 let self.cachedDisplayString .= ' -> ' . self.symLinkDest
1086 let self.cachedDisplayString .= ' [RO]'
1089 "FUNCTION: Path.changeToDir() {{{3
1090 function! s:Path.changeToDir()
1091 let dir = self.strForCd()
1092 if self.isDirectory == 0
1093 let dir = self.getPathTrunk().strForCd()
1098 call s:echo("CWD is now: " . getcwd())
1100 throw "NERDTree.Path.Change exception: cannot change to " . dir
1104 "FUNCTION: Path.compareTo() {{{3
1106 "Compares this Path to the given path and returns 0 if they are equal, -1 if
1107 "this Path is "less than" the given path, or 1 if it is "greater".
1110 "path: the path object to compare this to
1114 function! s:Path.compareTo(path)
1115 let thisPath = self.getLastPathComponent(1)
1116 let thatPath = a:path.getLastPathComponent(1)
1118 "if the paths are the same then clearly we return 0
1119 if thisPath == thatPath
1123 let thisSS = self.getSortOrderIndex()
1124 let thatSS = a:path.getSortOrderIndex()
1126 "compare the sort sequences, if they are different then the return
1130 elseif thisSS > thatSS
1133 "if the sort sequences are the same then compare the paths
1135 let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath
1144 "FUNCTION: Path.Create(fullpath) {{{3
1148 "Creates a path object with the given path. The path is also created on the
1149 "filesystem. If the path already exists, a NERDTree.Path.Exists exception is
1150 "thrown. If any other errors occur, a NERDTree.Path exception is thrown.
1153 "fullpath: the full filesystem path to the file/dir to create
1154 function! s:Path.Create(fullpath)
1155 "bail if the a:fullpath already exists
1156 if isdirectory(a:fullpath) || filereadable(a:fullpath)
1157 throw "NERDTree.Path.Exists Exception: Directory Exists: '" . a:fullpath . "'"
1162 "if it ends with a slash, assume its a dir create it
1163 if a:fullpath =~ '\(\\\|\/\)$'
1164 "whack the trailing slash off the end if it exists
1165 let fullpath = substitute(a:fullpath, '\(\\\|\/\)$', '', '')
1167 call mkdir(fullpath, 'p')
1169 "assume its a file and create
1171 call writefile([], a:fullpath)
1174 throw "NERDTree.Path Exception: Could not create path: '" . a:fullpath . "'"
1177 return s:Path.New(a:fullpath)
1180 "FUNCTION: Path.copy(dest) {{{3
1182 "Copies the file/dir represented by this Path to the given location
1185 "dest: the location to copy this dir/file to
1186 function! s:Path.copy(dest)
1187 if !s:Path.CopyingSupported()
1188 throw "NERDTree.Path.CopyingNotSupported Exception: Copying is not supported on this OS"
1191 let dest = s:Path.WinToUnixPath(a:dest)
1193 let cmd = g:NERDTreeCopyCmd . " " . self.strForOS(0) . " " . dest
1194 let success = system(cmd)
1196 throw "NERDTree.Path Exception: Could not copy ''". self.strForOS(0) ."'' to: '" . a:dest . "'"
1200 "FUNCTION: Path.CopyingSupported() {{{3
1202 "returns 1 if copying is supported for this OS
1203 function! s:Path.CopyingSupported()
1204 return exists('g:NERDTreeCopyCmd')
1208 "FUNCTION: Path.copyingWillOverwrite(dest) {{{3
1210 "returns 1 if copy this path to the given location will cause files to
1214 "dest: the location this path will be copied to
1215 function! s:Path.copyingWillOverwrite(dest)
1216 if filereadable(a:dest)
1220 if isdirectory(a:dest)
1221 let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
1222 if filereadable(path)
1228 "FUNCTION: Path.delete() {{{3
1230 "Deletes the file represented by this path.
1231 "Deletion of directories is not supported
1233 "Throws NERDTree.Path.Deletion exceptions
1234 function! s:Path.delete()
1238 if s:running_windows
1239 "if we are runnnig windows then put quotes around the pathstring
1240 let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
1242 let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
1244 let success = system(cmd)
1246 if v:shell_error != 0
1247 throw "NERDTree.Path.Deletion Exception: Could not delete directory: '" . self.strForOS(0) . "'"
1250 let success = delete(self.strForOS(0))
1252 throw "NERDTree.Path.Deletion Exception: Could not delete file: '" . self.str(0) . "'"
1256 "delete all bookmarks for this path
1257 for i in self.bookmarkNames()
1258 let bookmark = s:Bookmark.BookmarkFor(i)
1259 call bookmark.delete()
1263 "FUNCTION: Path.extractDriveLetter(fullpath) {{{3
1265 "If running windows, cache the drive letter for this path
1266 function! s:Path.extractDriveLetter(fullpath)
1267 if s:running_windows
1268 let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
1274 "FUNCTION: Path.exists() {{{3
1275 "return 1 if this path points to a location that is readable or is a directory
1276 function! s:Path.exists()
1277 return filereadable(self.strForOS(0)) || isdirectory(self.strForOS(0))
1279 "FUNCTION: Path.getDir() {{{3
1281 "Returns this path if it is a directory, else this paths parent.
1285 function! s:Path.getDir()
1289 return self.getParent()
1292 "FUNCTION: Path.getParent() {{{3
1294 "Returns a new path object for this paths parent
1298 function! s:Path.getParent()
1299 let path = '/'. join(self.pathSegments[0:-2], '/')
1300 return s:Path.New(path)
1302 "FUNCTION: Path.getLastPathComponent(dirSlash) {{{3
1304 "Gets the last part of this path.
1307 "dirSlash: if 1 then a trailing slash will be added to the returned value for
1309 function! s:Path.getLastPathComponent(dirSlash)
1310 if empty(self.pathSegments)
1313 let toReturn = self.pathSegments[-1]
1314 if a:dirSlash && self.isDirectory
1315 let toReturn = toReturn . '/'
1320 "FUNCTION: Path.getPathTrunk() {{{3
1321 "Gets the path without the last segment on the end.
1322 function! s:Path.getPathTrunk()
1323 return s:Path.New(self.strTrunk())
1326 "FUNCTION: Path.getSortOrderIndex() {{{3
1327 "returns the index of the pattern in g:NERDTreeSortOrder that this path matches
1328 function! s:Path.getSortOrderIndex()
1330 while i < len(g:NERDTreeSortOrder)
1331 if self.getLastPathComponent(1) =~ g:NERDTreeSortOrder[i]
1336 return s:NERDTreeSortStarIndex
1339 "FUNCTION: Path.ignore() {{{3
1340 "returns true if this path should be ignored
1341 function! s:Path.ignore()
1342 let lastPathComponent = self.getLastPathComponent(0)
1344 "filter out the user specified paths to ignore
1345 if t:NERDTreeIgnoreEnabled
1346 for i in g:NERDTreeIgnore
1347 if lastPathComponent =~ i
1353 "dont show hidden files unless instructed to
1354 if t:NERDTreeShowHidden == 0 && lastPathComponent =~ '^\.'
1358 if t:NERDTreeShowFiles == 0 && self.isDirectory == 0
1365 "FUNCTION: Path.JoinPathStrings(...) {{{3
1366 function! s:Path.JoinPathStrings(...)
1369 let components = extend(components, split(i, '/'))
1371 return '/' . join(components, '/')
1374 "FUNCTION: Path.equals() {{{3
1376 "Determines whether 2 path objects are "equal".
1377 "They are equal if the paths they represent are the same
1380 "path: the other path obj to compare this with
1381 function! s:Path.equals(path)
1382 return self.str(0) == a:path.str(0)
1385 "FUNCTION: Path.New() {{{3
1387 "The Constructor for the Path object
1388 "Throws NERDTree.Path.InvalidArguments exception.
1389 function! s:Path.New(fullpath)
1390 let newPath = copy(self)
1392 call newPath.readInfoFromDisk(a:fullpath)
1394 let newPath.cachedDisplayString = ""
1399 "FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
1402 "Throws NERDTree.Path.InvalidArguments exception.
1403 function! s:Path.readInfoFromDisk(fullpath)
1404 call self.extractDriveLetter(a:fullpath)
1406 let fullpath = s:Path.WinToUnixPath(a:fullpath)
1408 if getftype(fullpath) == "fifo"
1409 throw "NERDTree.Path.InvalidFiletype Exception: Cant handle FIFO files: " . a:fullpath
1412 let self.pathSegments = split(fullpath, '/')
1415 let self.isReadOnly = 0
1416 if isdirectory(a:fullpath)
1417 let self.isDirectory = 1
1418 elseif filereadable(a:fullpath)
1419 let self.isDirectory = 0
1420 let self.isReadOnly = filewritable(a:fullpath) == 0
1422 throw "NERDTree.Path.InvalidArguments Exception: Invalid path = " . a:fullpath
1425 let self.isExecutable = 0
1426 if !self.isDirectory
1427 let self.isExecutable = getfperm(a:fullpath) =~ 'x'
1430 "grab the last part of the path (minus the trailing slash)
1431 let lastPathComponent = self.getLastPathComponent(0)
1433 "get the path to the new node with the parent dir fully resolved
1434 let hardPath = resolve(self.strTrunk()) . '/' . lastPathComponent
1436 "if the last part of the path is a symlink then flag it as such
1437 let self.isSymLink = (resolve(hardPath) != hardPath)
1439 let self.symLinkDest = resolve(fullpath)
1441 "if the link is a dir then slap a / on the end of its dest
1442 if isdirectory(self.symLinkDest)
1444 "we always wanna treat MS windows shortcuts as files for
1446 if hardPath !~ '\.lnk$'
1448 let self.symLinkDest = self.symLinkDest . '/'
1454 "FUNCTION: Path.refresh() {{{3
1455 function! s:Path.refresh()
1456 call self.readInfoFromDisk(self.strForOS(0))
1457 call self.cacheDisplayString()
1460 "FUNCTION: Path.rename() {{{3
1462 "Renames this node on the filesystem
1463 function! s:Path.rename(newPath)
1465 throw "NERDTree.Path.InvalidArguments exception. Invalid newPath for renaming = ". a:newPath
1468 let success = rename(self.strForOS(0), a:newPath)
1470 throw "NERDTree.Path.Rename Exception: Could not rename: '" . self.strForOS(0) . "'" . 'to:' . a:newPath
1472 call self.readInfoFromDisk(a:newPath)
1474 for i in self.bookmarkNames()
1475 let b = s:Bookmark.BookmarkFor(i)
1476 call b.setPath(copy(self))
1478 call s:Bookmark.Write()
1481 "FUNCTION: Path.str(esc) {{{3
1483 "Gets the actual string path that this obj represents.
1486 "esc: if 1 then all the tricky chars in the returned string will be escaped
1487 function! s:Path.str(esc)
1488 let toReturn = '/' . join(self.pathSegments, '/')
1489 if self.isDirectory && toReturn != '/'
1490 let toReturn = toReturn . '/'
1494 let toReturn = escape(toReturn, s:escape_chars)
1499 "FUNCTION: Path.strAbs() {{{3
1501 "Returns a string representing this path with all the symlinks resolved
1505 function! s:Path.strAbs()
1506 return resolve(self.str(1))
1509 "FUNCTION: Path.strForCd() {{{3
1511 " returns a string that can be used with :cd
1514 "a string that can be used in the view to represent this path
1515 function! s:Path.strForCd()
1516 if s:running_windows
1517 return self.strForOS(0)
1519 return self.strForOS(1)
1522 "FUNCTION: Path.strDisplay() {{{3
1524 "Returns a string that specifies how the path should be represented as a
1528 "a string that can be used in the view to represent this path
1529 function! s:Path.strDisplay()
1530 if self.cachedDisplayString == ""
1531 call self.cacheDisplayString()
1534 return self.cachedDisplayString
1537 "FUNCTION: Path.strForEditCmd() {{{3
1539 "Return: the string for this path that is suitable to be used with the :edit
1541 function! s:Path.strForEditCmd()
1542 if s:running_windows
1543 return self.strForOS(0)
1549 "FUNCTION: Path.strForGlob() {{{3
1550 function! s:Path.strForGlob()
1551 let lead = s:os_slash
1553 "if we are running windows then slap a drive letter on the front
1554 if s:running_windows
1555 let lead = self.drive . '\'
1558 let toReturn = lead . join(self.pathSegments, s:os_slash)
1560 if !s:running_windows
1561 let toReturn = escape(toReturn, s:escape_chars)
1565 "FUNCTION: Path.strForOS(esc) {{{3
1567 "Gets the string path for this path object that is appropriate for the OS.
1568 "EG, in windows c:\foo\bar
1572 "esc: if 1 then all the tricky chars in the returned string will be
1573 " escaped. If we are running windows then the str is double quoted instead.
1574 function! s:Path.strForOS(esc)
1575 let lead = s:os_slash
1577 "if we are running windows then slap a drive letter on the front
1578 if s:running_windows
1579 let lead = self.drive . '\'
1582 let toReturn = lead . join(self.pathSegments, s:os_slash)
1585 if s:running_windows
1586 let toReturn = '"' . toReturn . '"'
1588 let toReturn = escape(toReturn, s:escape_chars)
1594 "FUNCTION: Path.strTrunk() {{{3
1595 "Gets the path without the last segment on the end.
1596 function! s:Path.strTrunk()
1597 return self.drive . '/' . join(self.pathSegments[0:-2], '/')
1600 "FUNCTION: Path.WinToUnixPath(pathstr){{{3
1601 "Takes in a windows path and returns the unix equiv
1603 "A class level method
1606 "pathstr: the windows path to convert
1607 function! s:Path.WinToUnixPath(pathstr)
1608 if !s:running_windows
1612 let toReturn = a:pathstr
1614 "remove the x:\ of the front
1615 let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "")
1617 "convert all \ chars to /
1618 let toReturn = substitute(toReturn, '\', '/', "g")
1623 " SECTION: General Functions {{{1
1624 "============================================================
1625 "FUNCTION: s:bufInWindows(bnum){{{2
1626 "[[STOLEN FROM VTREEEXPLORER.VIM]]
1627 "Determine the number of windows open to this buffer number.
1628 "Care of Yegappan Lakshman. Thanks!
1631 "bnum: the subject buffers buffer number
1632 function! s:bufInWindows(bnum)
1636 let bufnum = winbufnr(winnum)
1643 let winnum = winnum + 1
1649 "FUNCTION: s:compareBookmarks(first, second) {{{2
1650 "Compares two bookmarks
1651 function! s:compareBookmarks(first, second)
1652 return a:first.compareTo(a:second)
1655 " FUNCTION: s:completeBookmarks(A,L,P) {{{2
1656 " completion function for the bookmark commands
1657 function! s:completeBookmarks(A,L,P)
1658 return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"')
1660 "FUNCTION: s:initNerdTree(name) {{{2
1661 "Initialise the nerd tree for this tab. The tree will start in either the
1662 "given directory, or the directory associated with the given bookmark
1665 "name: the name of a bookmark or a directory
1666 function! s:initNerdTree(name)
1668 if s:Bookmark.BookmarkExistsFor(a:name)
1669 let path = s:Bookmark.BookmarkFor(a:name).path
1671 let dir = a:name == '' ? expand('%:p:h') : a:name
1672 let dir = resolve(dir)
1674 let path = s:Path.New(dir)
1675 catch /NERDTree.Path.InvalidArguments/
1676 call s:echo("No bookmark or directory found for: " . a:name)
1680 if !path.isDirectory
1681 let path = path.getParent()
1684 "if instructed to, then change the vim CWD to the dir the NERDTree is
1686 if g:NERDTreeChDirMode != 0
1687 exec 'cd ' . path.strForCd()
1690 let t:treeShowHelp = 0
1691 let t:NERDTreeIgnoreEnabled = 1
1692 let t:NERDTreeShowFiles = g:NERDTreeShowFiles
1693 let t:NERDTreeShowHidden = g:NERDTreeShowHidden
1694 let t:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
1696 if s:treeExistsForTab()
1700 unlet t:NERDTreeRoot
1703 let t:NERDTreeRoot = s:TreeDirNode.New(path)
1704 call t:NERDTreeRoot.open()
1706 call s:createTreeWin()
1708 call s:putCursorOnNode(t:NERDTreeRoot, 0, 0)
1710 " Function: s:treeExistsForTab() {{{2
1711 " Returns 1 if a nerd tree root exists in the current tab
1712 function! s:treeExistsForTab()
1713 return exists("t:NERDTreeRoot")
1715 " SECTION: Public Functions {{{1
1716 "============================================================
1717 "Returns the node that the cursor is currently on.
1719 "If the cursor is not in the NERDTree window, it is temporarily put there.
1721 "If no NERD tree window exists for the current tab, a NERDTree.NoTreeForTab
1722 "exception is thrown.
1724 "If the cursor is not on a node then an empty dictionary {} is returned.
1725 function! NERDTreeGetCurrentNode()
1726 if !s:treeExistsForTab() || !s:isTreeOpen()
1727 throw "NERDTree.NoTreeForTab exception: there is no NERD tree open for the current tab"
1731 if winnr != s:getTreeWinNum()
1732 call s:putCursorInTreeWin()
1735 let treenode = s:getSelectedNode()
1744 "Returns the path object for the current node.
1746 "Subject to the same conditions as NERDTreeGetCurrentNode
1747 function! NERDTreeGetCurrentPath()
1748 let node = NERDTreeGetCurrentNode()
1756 " SECTION: View Functions {{{1
1757 "============================================================
1758 "FUNCTION: s:centerView() {{{2
1759 "centers the nerd tree window around the cursor (provided the nerd tree
1761 function! s:centerView()
1762 if g:NERDTreeAutoCenter
1763 let current_line = winline()
1764 let lines_to_top = current_line
1765 let lines_to_bottom = winheight(s:getTreeWinNum()) - current_line
1766 if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold
1771 "FUNCTION: s:closeTree() {{{2
1772 "Closes the NERD tree window
1773 function! s:closeTree()
1775 throw "NERDTree.view.closeTree exception: no NERDTree is open"
1779 execute s:getTreeWinNum() . " wincmd w"
1787 "FUNCTION: s:closeTreeIfOpen() {{{2
1788 "Closes the NERD tree window if it is open
1789 function! s:closeTreeIfOpen()
1794 "FUNCTION: s:closeTreeIfQuitOnOpen() {{{2
1795 "Closes the NERD tree window if the close on open option is set
1796 function! s:closeTreeIfQuitOnOpen()
1797 if g:NERDTreeQuitOnOpen
1801 "FUNCTION: s:createTreeWin() {{{2
1802 "Inits the NERD tree window. ie. opens it, sizes it, sets all the local
1804 function! s:createTreeWin()
1805 "create the nerd tree window
1806 let splitLocation = (g:NERDTreeWinPos == "top" || g:NERDTreeWinPos == "left") ? "topleft " : "botright "
1807 let splitMode = s:shouldSplitVertically() ? "vertical " : ""
1808 let splitSize = g:NERDTreeWinSize
1809 let t:NERDTreeWinName = localtime() . s:NERDTreeWinName
1810 let cmd = splitLocation . splitMode . splitSize . ' new ' . t:NERDTreeWinName
1813 setlocal winfixwidth
1815 "throwaway buffer options
1817 setlocal buftype=nofile
1818 setlocal bufhidden=delete
1820 setlocal foldcolumn=0
1821 setlocal nobuflisted
1823 if g:NERDTreeShowLineNumbers
1831 if g:NERDTreeHighlightCursorline
1837 call s:bindMappings()
1838 setfiletype nerdtree
1839 " syntax highlighting
1840 if has("syntax") && exists("g:syntax_on") && !has("syntax_items")
1841 call s:setupSyntaxHighlighting()
1845 "FUNCTION: s:drawTree {{{2
1846 "Draws the given node recursively
1849 "curNode: the node that is being rendered with this call
1850 "depth: the current depth in the tree for this call
1851 "drawText: 1 if we should actually draw the line for this node (if 0 then the
1852 "child nodes are rendered only)
1853 "vertMap: a binary array that indicates whether a vertical bar should be draw
1854 "for each depth in the tree
1855 "isLastChild:true if this curNode is the last child of its parent
1856 function! s:drawTree(curNode, depth, drawText, vertMap, isLastChild)
1861 "get all the leading spaces and vertical tree parts for this line
1863 for j in a:vertMap[0:-2]
1865 let treeParts = treeParts . '| '
1867 let treeParts = treeParts . ' '
1872 "get the last vertical tree part for this line which will be different
1873 "if this node is the last child of its parent
1875 let treeParts = treeParts . '`'
1877 let treeParts = treeParts . '|'
1881 "smack the appropriate dir/file symbol on the line before the file/dir
1883 if a:curNode.path.isDirectory
1885 let treeParts = treeParts . '~'
1887 let treeParts = treeParts . '+'
1890 let treeParts = treeParts . '-'
1892 let line = treeParts . a:curNode.strDisplay()
1894 call setline(line(".")+1, line)
1895 call cursor(line(".")+1, col("."))
1898 "if the node is an open dir, draw its children
1899 if a:curNode.path.isDirectory == 1 && a:curNode.isOpen == 1
1901 let childNodesToDraw = a:curNode.getVisibleChildren()
1902 if len(childNodesToDraw) > 0
1904 "draw all the nodes children except the last
1905 let lastIndx = len(childNodesToDraw)-1
1907 for i in childNodesToDraw[0:lastIndx-1]
1908 call s:drawTree(i, a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
1912 "draw the last child, indicating that it IS the last
1913 call s:drawTree(childNodesToDraw[lastIndx], a:depth + 1, 1, add(copy(a:vertMap), 0), 1)
1919 "FUNCTION: s:dumpHelp {{{2
1920 "prints out the quick help
1921 function! s:dumpHelp()
1923 if t:treeShowHelp == 1
1924 let @h= "\" NERD tree (" . s:NERD_tree_version . ") quickhelp~\n"
1925 let @h=@h."\" ============================\n"
1926 let @h=@h."\" File node mappings~\n"
1927 let @h=@h."\" ". (g:NERDTreeMouseMode == 3 ? "single" : "double") ."-click,\n"
1928 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n"
1929 let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n"
1930 let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
1931 let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
1932 let @h=@h."\" middle-click,\n"
1933 let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n"
1934 let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n"
1935 let @h=@h."\" ". g:NERDTreeMapExecute.": Execute file\n"
1937 let @h=@h."\"\n\" ----------------------------\n"
1938 let @h=@h."\" Directory node mappings~\n"
1939 let @h=@h."\" ". (g:NERDTreeMouseMode == 1 ? "double" : "single") ."-click,\n"
1940 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open & close node\n"
1941 let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n"
1942 let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n"
1943 let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n"
1944 let @h=@h."\" current node recursively\n"
1945 let @h=@h."\" middle-click,\n"
1946 let @h=@h."\" ". g:NERDTreeMapOpenExpl.": Open netrw for selected\n"
1947 let @h=@h."\" node\n"
1949 let @h=@h."\"\n\" ----------------------------\n"
1950 let @h=@h."\" Bookmark table mappings~\n"
1951 let @h=@h."\" double-click,\n"
1952 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open bookmark\n"
1953 let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
1954 let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
1955 let @h=@h."\" ". g:NERDTreeMapDeleteBookmark .": delete bookmark\n"
1957 let @h=@h."\"\n\" ----------------------------\n"
1958 let @h=@h."\" Tree navigation mappings~\n"
1959 let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n"
1960 let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n"
1961 let @h=@h."\" ". g:NERDTreeMapJumpFirstChild .": go to first child\n"
1962 let @h=@h."\" ". g:NERDTreeMapJumpLastChild .": go to last child\n"
1963 let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n"
1964 let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n"
1966 let @h=@h."\"\n\" ----------------------------\n"
1967 let @h=@h."\" Filesystem mappings~\n"
1968 let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n"
1969 let @h=@h."\" selected dir\n"
1970 let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n"
1971 let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n"
1972 let @h=@h."\" but leave old root open\n"
1973 let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n"
1974 let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n"
1975 let @h=@h."\" ". g:NERDTreeMapFilesystemMenu .": Show filesystem menu\n"
1976 let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n"
1977 let @h=@h."\" selected dir\n"
1979 let @h=@h."\"\n\" ----------------------------\n"
1980 let @h=@h."\" Tree filtering mappings~\n"
1981 let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (t:NERDTreeShowHidden ? "on" : "off") . ")\n"
1982 let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (t:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n"
1983 let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (t:NERDTreeShowFiles ? "on" : "off") . ")\n"
1984 let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (t:NERDTreeShowBookmarks ? "on" : "off") . ")\n"
1986 let @h=@h."\"\n\" ----------------------------\n"
1987 let @h=@h."\" Other mappings~\n"
1988 let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n"
1989 let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n"
1990 let @h=@h."\"\n\" ----------------------------\n"
1991 let @h=@h."\" Bookmark commands~\n"
1992 let @h=@h."\" :Bookmark <name>\n"
1993 let @h=@h."\" :BookmarkToRoot <name>\n"
1994 let @h=@h."\" :RevealBookmark <name>\n"
1995 let @h=@h."\" :OpenBookmark <name>\n"
1996 let @h=@h."\" :ClearBookmarks [<names>]\n"
1997 let @h=@h."\" :ClearAllBookmarks\n"
1999 let @h="\" Press ". g:NERDTreeMapHelp ." for help\n"
2006 "FUNCTION: s:echo {{{2
2007 "A wrapper for :echo. Appends 'NERDTree:' on the front of all messages
2010 "msg: the message to echo
2011 function! s:echo(msg)
2013 echomsg "NERDTree: " . a:msg
2015 "FUNCTION: s:echoWarning {{{2
2016 "Wrapper for s:echo, sets the message type to warningmsg for this message
2018 "msg: the message to echo
2019 function! s:echoWarning(msg)
2024 "FUNCTION: s:echoError {{{2
2025 "Wrapper for s:echo, sets the message type to errormsg for this message
2027 "msg: the message to echo
2028 function! s:echoError(msg)
2033 "FUNCTION: s:findNodeLineNumber(treenode){{{2
2034 "Finds the line number for the given tree node
2037 "treenode: the node to find the line no. for
2038 function! s:findNodeLineNumber(treenode)
2039 "if the node is the root then return the root line no.
2040 if a:treenode.isRoot()
2041 return s:findRootNodeLineNumber()
2044 let totalLines = line("$")
2046 "the path components we have matched so far
2047 let pathcomponents = [substitute(t:NERDTreeRoot.path.str(0), '/ *$', '', '')]
2048 "the index of the component we are searching for
2049 let curPathComponent = 1
2051 let fullpath = a:treenode.path.str(0)
2054 let lnum = s:findRootNodeLineNumber()
2057 "have we reached the bottom of the tree?
2058 if lnum == totalLines+1
2062 let curLine = getline(lnum)
2064 let indent = match(curLine,s:tree_markup_reg_neg) / s:tree_wid
2065 if indent == curPathComponent
2066 let curLine = s:stripMarkupFromLine(curLine, 1)
2068 let curPath = join(pathcomponents, '/') . '/' . curLine
2069 if stridx(fullpath, curPath, 0) == 0
2070 if fullpath == curPath || strpart(fullpath, len(curPath)-1,1) == '/'
2071 let curLine = substitute(curLine, '/ *$', '', '')
2072 call add(pathcomponents, curLine)
2073 let curPathComponent = curPathComponent + 1
2075 if fullpath == curPath
2085 "FUNCTION: s:findRootNodeLineNumber(){{{2
2086 "Finds the line number of the root node
2087 function! s:findRootNodeLineNumber()
2089 while getline(rootLine) !~ '^/'
2090 let rootLine = rootLine + 1
2095 "FUNCTION: s:getPath(ln) {{{2
2096 "Gets the full path to the node that is rendered on the given line number
2099 "ln: the line number to get the path for
2102 "A path if a node was selected, {} if nothing is selected.
2103 "If the 'up a dir' line was selected then the path to the parent of the
2104 "current root is returned
2105 function! s:getPath(ln)
2106 let line = getline(a:ln)
2108 "check to see if we have the root node
2110 return t:NERDTreeRoot.path
2113 " in case called from outside the tree
2114 if line !~ '^ *[|`]' || line =~ '^$'
2118 if line == s:tree_up_dir_line
2119 return t:NERDTreeRoot.path.getParent()
2122 "get the indent level for the file (i.e. how deep in the tree it is)
2123 let indent = match(line, s:tree_markup_reg_neg) / s:tree_wid
2126 "remove the tree parts and the leading space
2127 let curFile = s:stripMarkupFromLine(line, 0)
2132 let curFile = substitute(curFile, '/\?$', '/', "")
2140 let curLine = getline(lnum)
2141 let curLineStripped = s:stripMarkupFromLine(curLine, 1)
2143 "have we reached the top of the tree?
2145 let dir = substitute (curLine, ' *$', "", "") . dir
2148 if curLineStripped =~ '/$'
2149 let lpindent = match(curLine,s:tree_markup_reg_neg) / s:tree_wid
2150 if lpindent < indent
2151 let indent = indent - 1
2153 let dir = substitute (curLineStripped,'^\\', "", "") . dir
2158 let curFile = t:NERDTreeRoot.path.drive . dir . curFile
2159 let toReturn = s:Path.New(curFile)
2163 "FUNCTION: s:getSelectedBookmark() {{{2
2164 "returns the bookmark the cursor is over in the bookmarks table or {}
2165 function! s:getSelectedBookmark()
2166 let line = getline(".")
2167 let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
2170 return s:Bookmark.BookmarkFor(name)
2171 catch /NERDTree.BookmarkNotFound/
2178 "FUNCTION: s:getSelectedDir() {{{2
2179 "Returns the current node if it is a dir node, or else returns the current
2181 function! s:getSelectedDir()
2182 let currentDir = s:getSelectedNode()
2183 if currentDir != {} && !currentDir.isRoot()
2184 if currentDir.path.isDirectory == 0
2185 let currentDir = currentDir.parent
2190 "FUNCTION: s:getSelectedNode() {{{2
2191 "gets the treenode that the cursor is currently over
2192 function! s:getSelectedNode()
2194 let path = s:getPath(line("."))
2198 return t:NERDTreeRoot.findNode(path)
2203 "FUNCTION: s:getTreeWinNum() {{{2
2204 "gets the nerd tree window number for this tab
2205 function! s:getTreeWinNum()
2206 if exists("t:NERDTreeWinName")
2207 return bufwinnr(t:NERDTreeWinName)
2213 "FUNCTION: s:isTreeOpen() {{{2
2214 function! s:isTreeOpen()
2215 return s:getTreeWinNum() != -1
2218 " FUNCTION: s:jumpToChild(direction) {{{2
2220 " direction: 0 if going to first child, 1 if going to last
2221 function! s:jumpToChild(direction)
2222 let currentNode = s:getSelectedNode()
2223 if currentNode == {} || currentNode.isRoot()
2224 call s:echo("cannot jump to " . (a:direction ? "last" : "first") . " child")
2227 let dirNode = currentNode.parent
2228 let childNodes = dirNode.getVisibleChildren()
2230 let targetNode = childNodes[0]
2232 let targetNode = childNodes[len(childNodes) - 1]
2235 if targetNode.equals(currentNode)
2236 let siblingDir = currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction)
2238 let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0
2239 let targetNode = siblingDir.getChildByIndex(indx, 1)
2243 call s:putCursorOnNode(targetNode, 1, 0)
2249 "FUNCTION: s:openDirNodeSplit(treenode) {{{2
2250 "Open the file represented by the given node in a new window.
2251 "No action is taken for file nodes
2254 "treenode: file node to open
2255 function! s:openDirNodeSplit(treenode)
2256 if a:treenode.path.isDirectory == 1
2257 call s:openNodeSplit(a:treenode)
2261 " FUNCTION: s:openExplorerFor(treenode) {{{2
2262 " opens a netrw window for the given dir treenode
2263 function! s:openExplorerFor(treenode)
2264 let oldwin = winnr()
2266 if oldwin == winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2)
2268 call s:openDirNodeSplit(a:treenode)
2270 exec ("silent edit " . a:treenode.path.strForEditCmd())
2273 "FUNCTION: s:openFileNode(treenode) {{{2
2274 "Open the file represented by the given node in the current window, splitting
2275 "the window if needed
2278 "treenode: file node to open
2279 function! s:openFileNode(treenode)
2280 call s:putCursorInTreeWin()
2282 "if the file is already open in this tab then just stick the cursor in it
2283 let winnr = bufwinnr('^' . a:treenode.path.strForOS(0) . '$')
2285 exec winnr . "wincmd w"
2287 elseif s:shouldSplitToOpen(winnr("#"))
2288 call s:openFileNodeSplit(a:treenode)
2292 exec ("edit " . a:treenode.path.strForEditCmd())
2293 catch /^Vim\%((\a\+)\)\=:E37/
2294 call s:putCursorInTreeWin()
2295 call s:echo("Cannot open file, it is already open and modified")
2296 catch /^Vim\%((\a\+)\)\=:/
2302 "FUNCTION: s:openFileNodeSplit(treenode) {{{2
2303 "Open the file represented by the given node in a new window.
2304 "No action is taken for dir nodes
2307 "treenode: file node to open
2308 function! s:openFileNodeSplit(treenode)
2309 if a:treenode.path.isDirectory == 0
2311 call s:openNodeSplit(a:treenode)
2312 catch /^NERDTree.view.FileOpen/
2313 call s:echo("Cannot open file, it is already open and modified" )
2318 "FUNCTION: s:openNodeSplit(treenode) {{{2
2319 "Open the file/dir represented by the given node in a new window
2322 "treenode: file node to open
2323 function! s:openNodeSplit(treenode)
2324 call s:putCursorInTreeWin()
2326 " Save the user's settings for splitbelow and splitright
2327 let savesplitbelow=&splitbelow
2328 let savesplitright=&splitright
2330 " Figure out how to do the split based on the user's preferences.
2331 " We want to split to the (left,right,top,bottom) of the explorer
2332 " window, but we want to extract the screen real-estate from the
2333 " window next to the explorer if possible.
2335 " 'there' will be set to a command to move from the split window
2336 " back to the explorer window
2338 " 'back' will be set to a command to move from the explorer window
2339 " back to the newly split window
2341 " 'right' and 'below' will be set to the settings needed for
2342 " splitbelow and splitright IF the explorer is the only window.
2344 if s:shouldSplitVertically()
2345 let there= g:NERDTreeWinPos == "left" ? "wincmd h" : "wincmd l"
2346 let back = g:NERDTreeWinPos == "left" ? "wincmd l" : "wincmd h"
2347 let right= g:NERDTreeWinPos == "left"
2350 let there= g:NERDTreeWinPos == "top" ? "wincmd k" : "wincmd j"
2351 let back = g:NERDTreeWinPos == "top" ? "wincmd j" : "wincmd k"
2352 let below= g:NERDTreeWinPos == "top"
2356 " Attempt to go to adjacent window
2359 let onlyOneWin = (winnr() == s:getTreeWinNum())
2361 " If no adjacent window, set splitright and splitbelow appropriately
2363 let &splitright=right
2364 let &splitbelow=below
2366 " found adjacent window - invert split direction
2367 let &splitright=!right
2368 let &splitbelow=!below
2371 " Create a variable to use if splitting vertically
2373 if (onlyOneWin && s:shouldSplitVertically()) || (!onlyOneWin && !s:shouldSplitVertically())
2374 let splitMode = "vertical"
2379 " Open the new window
2381 exec(splitMode." sp " . a:treenode.path.strForEditCmd())
2382 catch /^Vim\%((\a\+)\)\=:E37/
2383 call s:putCursorInTreeWin()
2384 throw "NERDTree.view.FileOpen exception: ". a:treenode.path.str(0) ." is already open and modified."
2385 catch /^Vim\%((\a\+)\)\=:/
2389 "resize the tree window if no other window was open before
2391 let size = exists("t:NERDTreeOldWindowSize") ? t:NERDTreeOldWindowSize : g:NERDTreeWinSize
2393 exec("silent ". splitMode ." resize ". size)
2397 " Restore splitmode settings
2398 let &splitbelow=savesplitbelow
2399 let &splitright=savesplitright
2402 "FUNCTION: s:promptToDelBuffer(bufnum, msg){{{2
2403 "prints out the given msg and, if the user responds by pushing 'y' then the
2404 "buffer with the given bufnum is deleted
2407 "bufnum: the buffer that may be deleted
2408 "msg: a message that will be echoed to the user asking them if they wish to
2410 function! s:promptToDelBuffer(bufnum, msg)
2412 if nr2char(getchar()) == 'y'
2413 exec "silent bdelete! " . a:bufnum
2417 "FUNCTION: s:putCursorOnBookmarkTable(){{{2
2418 "Places the cursor at the top of the bookmarks table
2419 function! s:putCursorOnBookmarkTable()
2420 if !t:NERDTreeShowBookmarks
2421 throw "NERDTree.IllegalOperation exception: cant find bookmark table, bookmarks arent active"
2424 let rootNodeLine = s:findRootNodeLineNumber()
2427 while getline(line) !~ '^>-\+Bookmarks-\+$'
2429 if line >= rootNodeLine
2430 throw "NERDTree.BookmarkTableNotFound exception: didnt find the bookmarks table"
2433 call cursor(line, 0)
2436 "FUNCTION: s:putCursorOnNode(treenode, isJump, recurseUpward){{{2
2437 "Places the cursor on the line number representing the given node
2440 "treenode: the node to put the cursor on
2441 "isJump: 1 if this cursor movement should be counted as a jump by vim
2442 "recurseUpward: try to put the cursor on the parent if the this node isnt
2444 function! s:putCursorOnNode(treenode, isJump, recurseUpward)
2445 let ln = s:findNodeLineNumber(a:treenode)
2450 call cursor(ln, col("."))
2453 let node = a:treenode
2454 while s:findNodeLineNumber(node) == -1 && node != {}
2455 let node = node.parent
2459 call s:putCursorOnNode(a:treenode, a:isJump, 0)
2464 "FUNCTION: s:putCursorInTreeWin(){{{2
2465 "Places the cursor in the nerd tree window
2466 function! s:putCursorInTreeWin()
2468 throw "NERDTree.view.InvalidOperation Exception: No NERD tree window exists"
2471 exec s:getTreeWinNum() . "wincmd w"
2474 "FUNCTION: s:renderBookmarks {{{2
2475 function! s:renderBookmarks()
2477 call setline(line(".")+1, ">----------Bookmarks----------")
2478 call cursor(line(".")+1, col("."))
2480 for i in s:Bookmark.Bookmarks()
2481 call setline(line(".")+1, i.str())
2482 call cursor(line(".")+1, col("."))
2485 call setline(line(".")+1, '')
2486 call cursor(line(".")+1, col("."))
2488 "FUNCTION: s:renderView {{{2
2489 "The entry function for rendering the tree. Renders the root then calls
2490 "s:drawTree to draw the children of the root
2493 function! s:renderView()
2494 execute s:getTreeWinNum() . "wincmd w"
2498 "remember the top line of the buffer and the current line so we can
2499 "restore the view exactly how it was
2500 let curLine = line(".")
2501 let curCol = col(".")
2502 let topLine = line("w0")
2504 "delete all lines in the buffer (being careful not to clobber a register)
2509 "delete the blank line before the help and add one after it
2510 call setline(line(".")+1, "")
2511 call cursor(line(".")+1, col("."))
2513 if t:NERDTreeShowBookmarks
2514 call s:renderBookmarks()
2517 "add the 'up a dir' line
2518 call setline(line(".")+1, s:tree_up_dir_line)
2519 call cursor(line(".")+1, col("."))
2521 "draw the header line
2522 call setline(line(".")+1, t:NERDTreeRoot.path.str(0))
2523 call cursor(line(".")+1, col("."))
2526 call s:drawTree(t:NERDTreeRoot, 0, 0, [], t:NERDTreeRoot.getChildCount() == 1)
2528 "delete the blank line at the top of the buffer
2532 let old_scrolloff=&scrolloff
2534 call cursor(topLine, 1)
2536 call cursor(curLine, curCol)
2537 let &scrolloff = old_scrolloff
2539 setlocal nomodifiable
2542 "FUNCTION: s:renderViewSavingPosition {{{2
2543 "Renders the tree and ensures the cursor stays on the current node or the
2544 "current nodes parent if it is no longer available upon re-rendering
2545 function! s:renderViewSavingPosition()
2546 let currentNode = s:getSelectedNode()
2548 "go up the tree till we find a node that will be visible or till we run
2550 while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
2551 let currentNode = currentNode.parent
2556 if currentNode != {}
2557 call s:putCursorOnNode(currentNode, 0, 0)
2560 "FUNCTION: s:restoreScreenState() {{{2
2562 "Sets the screen state back to what it was when s:saveScreenState was last
2565 "Assumes the cursor is in the NERDTree window
2566 function! s:restoreScreenState()
2567 if !exists("t:NERDTreeOldTopLine") || !exists("t:NERDTreeOldPos") || !exists("t:NERDTreeOldWindowSize")
2570 exec("silent ". (s:shouldSplitVertically() ? "vertical" : "") ." resize ".t:NERDTreeOldWindowSize)
2572 let old_scrolloff=&scrolloff
2574 call cursor(t:NERDTreeOldTopLine, 0)
2576 call setpos(".", t:NERDTreeOldPos)
2577 let &scrolloff=old_scrolloff
2580 "FUNCTION: s:saveScreenState() {{{2
2581 "Saves the current cursor position in the current buffer and the window
2583 function! s:saveScreenState()
2585 call s:putCursorInTreeWin()
2586 let t:NERDTreeOldPos = getpos(".")
2587 let t:NERDTreeOldTopLine = line("w0")
2588 let t:NERDTreeOldWindowSize = s:shouldSplitVertically() ? winwidth("") : winheight("")
2589 exec win . "wincmd w"
2592 "FUNCTION: s:setupSyntaxHighlighting() {{{2
2593 function! s:setupSyntaxHighlighting()
2594 "treeFlags are syntax items that should be invisible, but give clues as to
2595 "how things should be highlighted
2596 syn match treeFlag #\~#
2597 syn match treeFlag #\[RO\]#
2599 "highlighting for the .. (up dir) line at the top of the tree
2600 execute "syn match treeUp #". s:tree_up_dir_line ."#"
2602 "highlighting for the ~/+ symbols for the directory nodes
2603 syn match treeClosable #\~\<#
2604 syn match treeClosable #\~\.#
2605 syn match treeOpenable #+\<#
2606 syn match treeOpenable #+\.#he=e-1
2608 "highlighting for the tree structural parts
2609 syn match treePart #|#
2610 syn match treePart #`#
2611 syn match treePartFile #[|`]-#hs=s+1 contains=treePart
2613 "quickhelp syntax elements
2614 syn match treeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1
2615 syn match treeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1
2616 syn match treeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=treeFlag
2617 syn match treeToggleOn #".*(on)#hs=e-2,he=e-1 contains=treeHelpKey
2618 syn match treeToggleOff #".*(off)#hs=e-3,he=e-1 contains=treeHelpKey
2619 syn match treeHelpCommand #" :.\{-}\>#hs=s+3
2620 syn match treeHelp #^".*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn,treeHelpCommand
2622 "highlighting for readonly files
2623 syn match treeRO #[\/0-9a-zA-Z]\+.*\[RO\]# contains=treeFlag,treeBookmark
2625 "highlighting for sym links
2626 syn match treeLink #[^-| `].* -> # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash
2628 "highlighing for directory nodes and file nodes
2629 syn match treeDirSlash #/#
2630 syn match treeDir #[^-| `].*/# contains=treeLink,treeDirSlash,treeOpenable,treeClosable
2631 syn match treeExecFile #[|`]-.*\*\($\| \)# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark
2632 syn match treeFile #|-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
2633 syn match treeFile #`-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
2634 syn match treeCWD #^/.*$#
2636 "highlighting for bookmarks
2637 syn match treeBookmark # {.*}#hs=s+1
2639 "highlighting for the bookmarks table
2640 syn match treeBookmarksLeader #^>#
2641 syn match treeBookmarksHeader #^>-\+Bookmarks-\+$# contains=treeBookmarksLeader
2642 syn match treeBookmarkName #^>.\{-} #he=e-1 contains=treeBookmarksLeader
2643 syn match treeBookmark #^>.*$# contains=treeBookmarksLeader,treeBookmarkName,treeBookmarksHeader
2645 if g:NERDChristmasTree
2646 hi def link treePart Special
2647 hi def link treePartFile Type
2648 hi def link treeFile Normal
2649 hi def link treeExecFile Title
2650 hi def link treeDirSlash Identifier
2651 hi def link treeClosable Type
2653 hi def link treePart Normal
2654 hi def link treePartFile Normal
2655 hi def link treeFile Normal
2656 hi def link treeClosable Title
2659 hi def link treeBookmarksHeader statement
2660 hi def link treeBookmarksLeader ignore
2661 hi def link treeBookmarkName Identifier
2662 hi def link treeBookmark normal
2664 hi def link treeHelp String
2665 hi def link treeHelpKey Identifier
2666 hi def link treeHelpCommand Identifier
2667 hi def link treeHelpTitle Macro
2668 hi def link treeToggleOn Question
2669 hi def link treeToggleOff WarningMsg
2671 hi def link treeDir Directory
2672 hi def link treeUp Directory
2673 hi def link treeCWD Statement
2674 hi def link treeLink Macro
2675 hi def link treeOpenable Title
2676 hi def link treeFlag ignore
2677 hi def link treeRO WarningMsg
2678 hi def link treeBookmark Statement
2680 hi def link NERDTreeCurrentNode Search
2683 "FUNCTION: s:shouldSplitToOpen() {{{2
2684 "Returns 1 if opening a file from the tree in the given window requires it to
2688 "winnumber: the number of the window in question
2689 function! s:shouldSplitToOpen(winnumber)
2690 "gotta split if theres only one window (i.e. the NERD tree)
2695 let oldwinnr = winnr()
2696 exec a:winnumber . "wincmd p"
2697 let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow')
2698 let modified = &modified
2699 exec oldwinnr . "wincmd p"
2701 "if its a special window e.g. quickfix or another explorer plugin then we
2711 return modified && s:bufInWindows(winbufnr(a:winnumber)) < 2
2714 " Function: s:shouldSplitVertically() {{{2
2715 " Returns 1 if g:NERDTreeWinPos is 'left' or 'right'
2716 function! s:shouldSplitVertically()
2717 return g:NERDTreeWinPos == 'left' || g:NERDTreeWinPos == 'right'
2719 "FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2
2720 "returns the given line with all the tree parts stripped off
2723 "line: the subject line
2724 "removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces =
2725 "any spaces before the actual text of the node)
2726 function! s:stripMarkupFromLine(line, removeLeadingSpaces)
2728 "remove the tree parts and the leading space
2729 let line = substitute (line,"^" . s:tree_markup_reg . "*","","")
2731 "strip off any read only flag
2732 let line = substitute (line, ' \[RO\]', "","")
2734 "strip off any bookmark flags
2735 let line = substitute (line, ' {[^}]*}', "","")
2737 "strip off any executable flags
2738 let line = substitute (line, '*\ze\($\| \)', "","")
2744 let line = substitute (line,' -> .*',"","") " remove link to
2746 let line = substitute (line, '/\?$', '/', "")
2749 if a:removeLeadingSpaces
2750 let line = substitute (line, '^ *', '', '')
2756 "FUNCTION: s:toggle(dir) {{{2
2757 "Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is
2758 "closed it is restored or initialized (if it doesnt exist)
2761 "dir: the full path for the root node (is only used if the NERD tree is being
2763 function! s:toggle(dir)
2764 if s:treeExistsForTab()
2766 call s:createTreeWin()
2769 call s:restoreScreenState()
2774 call s:initNerdTree(a:dir)
2777 "SECTION: Interface bindings {{{1
2778 "============================================================
2779 "FUNCTION: s:activateNode(forceKeepWindowOpen) {{{2
2780 "If the current node is a file, open it in the previous window (or a new one
2781 "if the previous is modified). If it is a directory then it is opened.
2784 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
2785 function! s:activateNode(forceKeepWindowOpen)
2786 if getline(".") == s:tree_up_dir_line
2790 let treenode = s:getSelectedNode()
2792 if treenode.path.isDirectory
2793 call treenode.toggleOpen()
2795 call s:putCursorOnNode(treenode, 0, 0)
2797 call s:openFileNode(treenode)
2798 if !a:forceKeepWindowOpen
2799 call s:closeTreeIfQuitOnOpen()
2803 let bookmark = s:getSelectedBookmark()
2805 if bookmark.path.isDirectory
2806 call bookmark.toRoot()
2808 if bookmark.validate()
2809 call s:openFileNode(s:TreeFileNode.New(bookmark.path))
2816 "FUNCTION: s:bindMappings() {{{2
2817 function! s:bindMappings()
2818 " set up mappings and commands for this buffer
2819 nnoremap <silent> <buffer> <middlerelease> :call <SID>handleMiddleMouse()<cr>
2820 nnoremap <silent> <buffer> <leftrelease> <leftrelease>:call <SID>checkForActivate()<cr>
2821 nnoremap <silent> <buffer> <2-leftmouse> :call <SID>activateNode(0)<cr>
2823 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapActivateNode . " :call <SID>activateNode(0)<cr>"
2824 " exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenSplit ." :call <SID>openEntrySplit(0)<cr>"
2826 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreview ." :call <SID>previewNode(0)<cr>"
2827 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewSplit ." :call <SID>previewNode(1)<cr>"
2830 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapExecute ." :call <SID>executeNode()<cr>"
2832 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenRecursively ." :call <SID>openNodeRecursively()<cr>"
2834 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdirKeepOpen ." :call <SID>upDir(1)<cr>"
2835 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdir ." :call <SID>upDir(0)<cr>"
2836 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChangeRoot ." :call <SID>chRoot()<cr>"
2838 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChdir ." :call <SID>chCwd()<cr>"
2840 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapQuit ." :NERDTreeToggle<cr>"
2842 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefreshRoot ." :call <SID>refreshRoot()<cr>"
2843 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefresh ." :call <SID>refreshCurrent()<cr>"
2845 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapHelp ." :call <SID>displayHelp()<cr>"
2846 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleHidden ." :call <SID>toggleShowHidden()<cr>"
2847 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFilters ." :call <SID>toggleIgnoreFilter()<cr>"
2848 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFiles ." :call <SID>toggleShowFiles()<cr>"
2849 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleBookmarks ." :call <SID>toggleShowBookmarks()<cr>"
2851 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>"
2852 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>"
2854 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapFilesystemMenu ." :call <SID>showFileSystemMenu()<cr>"
2856 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>jumpToParent()<cr>"
2857 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>jumpToSibling(1)<cr>"
2858 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpPrevSibling ." :call <SID>jumpToSibling(0)<cr>"
2859 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpFirstChild ." :call <SID>jumpToFirstChild()<cr>"
2860 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpLastChild ." :call <SID>jumpToLastChild()<cr>"
2861 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpRoot ." :call <SID>jumpToRoot()<cr>"
2863 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>"
2864 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTabSilent ." :call <SID>openInNewTab(1)<cr>"
2866 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenExpl ." :call <SID>openExplorer()<cr>"
2868 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>"
2870 command! -buffer -nargs=1 Bookmark :call <SID>bookmarkNode('<args>')
2871 command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call <SID>revealBookmark('<args>')
2872 command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call <SID>openBookmark('<args>')
2873 command! -buffer -complete=customlist,s:completeBookmarks -nargs=* ClearBookmarks call <SID>clearBookmarks('<args>')
2874 command! -buffer -complete=customlist,s:completeBookmarks -nargs=+ BookmarkToRoot call s:Bookmark.ToRoot('<args>')
2875 command! -buffer -nargs=0 ClearAllBookmarks call s:Bookmark.ClearAll() <bar> call <SID>renderView()
2876 command! -buffer -nargs=0 ReadBookmarks call s:Bookmark.CacheBookmarks(0) <bar> call <SID>renderView()
2877 command! -buffer -nargs=0 WriteBookmarks call s:Bookmark.Write()
2880 " FUNCTION: s:bookmarkNode(name) {{{2
2881 " Associate the current node with the given name
2882 function! s:bookmarkNode(name)
2883 let currentNode = s:getSelectedNode()
2884 if currentNode != {}
2886 call currentNode.bookmark(a:name)
2888 catch /NERDTree.IllegalBookmarkName/
2889 call s:echo("bookmark names must not contain spaces")
2892 call s:echo("select a node first")
2895 "FUNCTION: s:checkForActivate() {{{2
2896 "Checks if the click should open the current node, if so then activate() is
2897 "called (directories are automatically opened if the symbol beside them is
2899 function! s:checkForActivate()
2900 let currentNode = s:getSelectedNode()
2901 if currentNode != {}
2902 let startToCur = strpart(getline(line(".")), 0, col("."))
2903 let char = strpart(startToCur, strlen(startToCur)-1, 1)
2905 "if they clicked a dir, check if they clicked on the + or ~ sign
2907 if currentNode.path.isDirectory
2908 let reg = '^' . s:tree_markup_reg .'*[~+]$'
2909 if startToCur =~ reg
2910 call s:activateNode(0)
2915 if (g:NERDTreeMouseMode == 2 && currentNode.path.isDirectory) || g:NERDTreeMouseMode == 3
2916 if char !~ s:tree_markup_reg && startToCur !~ '\/$'
2917 call s:activateNode(0)
2924 " FUNCTION: s:chCwd() {{{2
2926 let treenode = s:getSelectedNode()
2928 call s:echo("Select a node first")
2933 call treenode.path.changeToDir()
2934 catch /^NERDTree.Path.Change/
2935 call s:echoWarning("could not change cwd")
2939 " FUNCTION: s:chRoot() {{{2
2940 " changes the current root to the selected one
2941 function! s:chRoot()
2942 let treenode = s:getSelectedNode()
2944 call s:echo("Select a node first")
2948 call treenode.makeRoot()
2950 call s:putCursorOnNode(t:NERDTreeRoot, 0, 0)
2953 " FUNCTION: s:clearBookmarks(bookmarks) {{{2
2954 function! s:clearBookmarks(bookmarks)
2955 if a:bookmarks == ''
2956 let currentNode = s:getSelectedNode()
2957 if currentNode != {}
2958 call currentNode.clearBoomarks()
2961 for name in split(a:bookmarks, ' ')
2962 let bookmark = s:Bookmark.BookmarkFor(name)
2963 call bookmark.delete()
2968 " FUNCTION: s:closeChildren() {{{2
2969 " closes all childnodes of the current node
2970 function! s:closeChildren()
2971 let currentNode = s:getSelectedDir()
2972 if currentNode == {}
2973 call s:echo("Select a node first")
2977 call currentNode.closeChildren()
2979 call s:putCursorOnNode(currentNode, 0, 0)
2981 " FUNCTION: s:closeCurrentDir() {{{2
2982 " closes the parent dir of the current node
2983 function! s:closeCurrentDir()
2984 let treenode = s:getSelectedNode()
2986 call s:echo("Select a node first")
2990 let parent = treenode.parent
2992 call s:echo("cannot close tree root")
2994 call treenode.parent.close()
2996 call s:putCursorOnNode(treenode.parent, 0, 0)
3000 " FUNCTION: s:copyNode() {{{2
3001 function! s:copyNode()
3002 let currentNode = s:getSelectedNode()
3003 if currentNode == {}
3004 call s:echo("Put the cursor on a file node first")
3008 let newNodePath = input("Copy the current node\n" .
3009 \ "==========================================================\n" .
3010 \ "Enter the new path to copy the node to: \n" .
3011 \ "", currentNode.path.str(0))
3013 if newNodePath != ""
3014 "strip trailing slash
3015 let newNodePath = substitute(newNodePath, '\/$', '', '')
3018 if currentNode.path.copyingWillOverwrite(newNodePath)
3019 call s:echo("\nWarning: copying may overwrite files! Continue? (yN)")
3020 let choice = nr2char(getchar())
3021 let confirmed = choice == 'y'
3026 let newNode = currentNode.copy(newNodePath)
3028 call s:putCursorOnNode(newNode, 0, 0)
3030 call s:echoWarning("Could not copy node")
3034 call s:echo("Copy aborted.")
3039 " FUNCTION: s:deleteBookmark() {{{2
3040 " if the cursor is on a bookmark, prompt to delete
3041 function! s:deleteBookmark()
3042 let bookmark = s:getSelectedBookmark()
3044 call s:echo("Put the cursor on a bookmark")
3048 echo "Are you sure you wish to delete the bookmark:\n\"" . bookmark.name . "\" (yN):"
3050 if nr2char(getchar()) == 'y'
3052 call bookmark.delete()
3056 call s:echoWarning("Could not remove bookmark")
3059 call s:echo("delete aborted" )
3064 " FUNCTION: s:deleteNode() {{{2
3065 " if the current node is a file, pops up a dialog giving the user the option
3067 function! s:deleteNode()
3068 let currentNode = s:getSelectedNode()
3069 if currentNode == {}
3070 call s:echo("Put the cursor on a file node first")
3076 if currentNode.path.isDirectory
3077 let choice =input("Delete the current node\n" .
3078 \ "==========================================================\n" .
3079 \ "STOP! To delete this entire directory, type 'yes'\n" .
3080 \ "" . currentNode.path.strForOS(0) . ": ")
3081 let confirmed = choice == 'yes'
3083 echo "Delete the current node\n" .
3084 \ "==========================================================\n".
3085 \ "Are you sure you wish to delete the node:\n" .
3086 \ "" . currentNode.path.strForOS(0) . " (yN):"
3087 let choice = nr2char(getchar())
3088 let confirmed = choice == 'y'
3094 call currentNode.delete()
3097 "if the node is open in a buffer, ask the user if they want to
3099 let bufnum = bufnr(currentNode.path.str(0))
3100 if buflisted(bufnum)
3101 let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) == -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
3102 call s:promptToDelBuffer(bufnum, prompt)
3107 call s:echoWarning("Could not remove node")
3110 call s:echo("delete aborted" )
3115 " FUNCTION: s:displayHelp() {{{2
3116 " toggles the help display
3117 function! s:displayHelp()
3118 let t:treeShowHelp = t:treeShowHelp ? 0 : 1
3123 " FUNCTION: s:executeNode() {{{2
3124 function! s:executeNode()
3125 let treenode = s:getSelectedNode()
3126 if treenode == {} || treenode.path.isDirectory
3127 call s:echo("Select an executable file node first" )
3129 echo "NERDTree executor\n" .
3130 \ "==========================================================\n".
3131 \ "Complete the command to execute (add arguments etc): \n\n"
3132 let cmd = treenode.path.strForOS(1)
3133 let cmd = input(':!', cmd . ' ')
3138 call s:echo("command aborted")
3143 " FUNCTION: s:handleMiddleMouse() {{{2
3144 function! s:handleMiddleMouse()
3145 let curNode = s:getSelectedNode()
3147 call s:echo("Put the cursor on a node first" )
3151 if curNode.path.isDirectory
3152 call s:openExplorer()
3154 call s:openEntrySplit(0)
3159 " FUNCTION: s:insertNewNode() {{{2
3160 " Adds a new node to the filesystem and then into the tree
3161 function! s:insertNewNode()
3162 let curDirNode = s:getSelectedDir()
3164 call s:echo("Put the cursor on a node first" )
3168 let newNodeName = input("Add a childnode\n".
3169 \ "==========================================================\n".
3170 \ "Enter the dir/file name to be created. Dirs end with a '/'\n" .
3171 \ "", curDirNode.path.strForGlob() . s:os_slash)
3173 if newNodeName == ''
3174 call s:echo("Node Creation Aborted.")
3179 let newPath = s:Path.Create(newNodeName)
3180 let parentNode = t:NERDTreeRoot.findNode(newPath.getPathTrunk())
3182 let newTreeNode = s:TreeFileNode.New(newPath)
3183 if parentNode.isOpen || !empty(parentNode.children)
3184 call parentNode.addChild(newTreeNode, 1)
3186 call s:putCursorOnNode(newTreeNode, 1, 0)
3189 call s:echoWarning("Node Not Created.")
3193 " FUNCTION: s:jumpToFirstChild() {{{2
3194 " wrapper for the jump to child method
3195 function! s:jumpToFirstChild()
3196 call s:jumpToChild(0)
3199 " FUNCTION: s:jumpToLastChild() {{{2
3200 " wrapper for the jump to child method
3201 function! s:jumpToLastChild()
3202 call s:jumpToChild(1)
3205 " FUNCTION: s:jumpToParent() {{{2
3206 " moves the cursor to the parent of the current node
3207 function! s:jumpToParent()
3208 let currentNode = s:getSelectedNode()
3209 if !empty(currentNode)
3210 if !empty(currentNode.parent)
3211 call s:putCursorOnNode(currentNode.parent, 1, 0)
3214 call s:echo("cannot jump to parent")
3217 call s:echo("put the cursor on a node first")
3221 " FUNCTION: s:jumpToRoot() {{{2
3222 " moves the cursor to the root node
3223 function! s:jumpToRoot()
3224 call s:putCursorOnNode(t:NERDTreeRoot, 1, 0)
3228 " FUNCTION: s:jumpToSibling() {{{2
3229 " moves the cursor to the sibling of the current node in the given direction
3232 " forward: 1 if the cursor should move to the next sibling, 0 if it should
3233 " move back to the previous sibling
3234 function! s:jumpToSibling(forward)
3235 let currentNode = s:getSelectedNode()
3236 if !empty(currentNode)
3237 let sibling = currentNode.findSibling(a:forward)
3240 call s:putCursorOnNode(sibling, 1, 0)
3244 call s:echo("put the cursor on a node first")
3248 " FUNCTION: s:openBookmark(name) {{{2
3249 " put the cursor on the given bookmark and, if its a file, open it
3250 function! s:openBookmark(name)
3252 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3253 call s:putCursorOnNode(targetNode, 0, 1)
3255 catch /NERDTree.BookmarkedNodeNotFound/
3256 call s:echo("note - target node is not cached")
3257 let bookmark = s:Bookmark.BookmarkFor(a:name)
3258 let targetNode = s:TreeFileNode.New(bookmark.path)
3260 if targetNode.path.isDirectory
3261 call s:openExplorerFor(targetNode)
3263 call s:openFileNode(targetNode)
3266 " FUNCTION: s:openEntrySplit(forceKeepWindowOpen) {{{2
3267 "Opens the currently selected file from the explorer in a
3271 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3272 function! s:openEntrySplit(forceKeepWindowOpen)
3273 let treenode = s:getSelectedNode()
3275 call s:openFileNodeSplit(treenode)
3276 if !a:forceKeepWindowOpen
3277 call s:closeTreeIfQuitOnOpen()
3280 call s:echo("select a node first")
3284 " FUNCTION: s:openExplorer() {{{2
3285 function! s:openExplorer()
3286 let treenode = s:getSelectedDir()
3288 call s:openExplorerFor(treenode)
3290 call s:echo("select a node first")
3294 " FUNCTION: s:openInNewTab(stayCurrentTab) {{{2
3295 " Opens the selected node or bookmark in a new tab
3297 " stayCurrentTab: if 1 then vim will stay in the current tab, if 0 then vim
3298 " will go to the tab where the new file is opened
3299 function! s:openInNewTab(stayCurrentTab)
3300 let currentTab = tabpagenr()
3302 let treenode = s:getSelectedNode()
3304 if treenode.path.isDirectory
3306 call s:initNerdTree(treenode.path.strForOS(0))
3308 exec "tabedit " . treenode.path.strForEditCmd()
3311 let bookmark = s:getSelectedBookmark()
3313 if bookmark.path.isDirectory
3315 call s:initNerdTree(bookmark.name)
3317 exec "tabedit " . bookmark.path.strForEditCmd()
3322 exec "tabnext " . currentTab
3326 " FUNCTION: s:openNodeRecursively() {{{2
3327 function! s:openNodeRecursively()
3328 let treenode = s:getSelectedNode()
3329 if treenode == {} || treenode.path.isDirectory == 0
3330 call s:echo("Select a directory node first" )
3332 call s:echo("Recursively opening node. Please wait...")
3333 call treenode.openRecursively()
3336 call s:echo("Recursively opening node. Please wait... DONE")
3341 "FUNCTION: s:previewNode() {{{2
3342 function! s:previewNode(openNewWin)
3344 call s:openEntrySplit(1)
3346 call s:activateNode(1)
3348 call s:putCursorInTreeWin()
3351 " FUNCTION: s:revealBookmark(name) {{{2
3352 " put the cursor on the node associate with the given name
3353 function! s:revealBookmark(name)
3355 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3356 call s:putCursorOnNode(targetNode, 0, 1)
3357 catch /NERDTree.BookmarkDoesntExist/
3358 call s:echo("Bookmark isnt cached under the current root")
3361 " FUNCTION: s:refreshRoot() {{{2
3362 " Reloads the current root. All nodes below this will be lost and the root dir
3364 function! s:refreshRoot()
3365 call s:echo("Refreshing the root node. This could take a while...")
3366 call t:NERDTreeRoot.refresh()
3369 call s:echo("Refreshing the root node. This could take a while... DONE")
3372 " FUNCTION: s:refreshCurrent() {{{2
3373 " refreshes the root for the current node
3374 function! s:refreshCurrent()
3375 let treenode = s:getSelectedDir()
3377 call s:echo("Refresh failed. Select a node first")
3381 call s:echo("Refreshing node. This could take a while...")
3382 call treenode.refresh()
3385 call s:echo("Refreshing node. This could take a while... DONE")
3387 " FUNCTION: s:renameCurrent() {{{2
3388 " allows the user to rename the current node
3389 function! s:renameCurrent()
3390 let curNode = s:getSelectedNode()
3392 call s:echo("Put the cursor on a node first" )
3396 let newNodePath = input("Rename the current node\n" .
3397 \ "==========================================================\n" .
3398 \ "Enter the new path for the node: \n" .
3399 \ "", curNode.path.strForOS(0))
3401 if newNodePath == ''
3402 call s:echo("Node Renaming Aborted.")
3407 let bufnum = bufnr(curNode.path.str(0))
3409 call curNode.rename(newNodePath)
3412 "if the node is open in a buffer, ask the user if they want to
3415 let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) == -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
3416 call s:promptToDelBuffer(bufnum, prompt)
3419 call s:putCursorOnNode(curNode, 1, 0)
3423 call s:echoWarning("Node Not Renamed.")
3427 " FUNCTION: s:showFileSystemMenu() {{{2
3428 function! s:showFileSystemMenu()
3429 let curNode = s:getSelectedNode()
3431 call s:echo("Put the cursor on a node first" )
3436 let prompt = "NERDTree Filesystem Menu\n" .
3437 \ "==========================================================\n".
3438 \ "Select the desired operation: \n" .
3439 \ " (a)dd a childnode\n".
3440 \ " (m)ove the current node\n".
3441 \ " (d)elete the current node\n"
3442 if s:Path.CopyingSupported()
3443 let prompt = prompt . " (c)opy the current node\n\n"
3445 let prompt = prompt . " \n"
3450 let choice = nr2char(getchar())
3453 call s:insertNewNode()
3454 elseif choice ==? "m"
3455 call s:renameCurrent()
3456 elseif choice ==? "d"
3458 elseif choice ==? "c" && s:Path.CopyingSupported()
3463 " FUNCTION: s:toggleIgnoreFilter() {{{2
3464 " toggles the use of the NERDTreeIgnore option
3465 function! s:toggleIgnoreFilter()
3466 let t:NERDTreeIgnoreEnabled = !t:NERDTreeIgnoreEnabled
3467 call s:renderViewSavingPosition()
3471 " FUNCTION: s:toggleShowBookmarks() {{{2
3472 " toggles the display of bookmarks
3473 function! s:toggleShowBookmarks()
3474 let t:NERDTreeShowBookmarks = !t:NERDTreeShowBookmarks
3475 if t:NERDTreeShowBookmarks
3477 call s:putCursorOnBookmarkTable()
3479 call s:renderViewSavingPosition()
3483 " FUNCTION: s:toggleShowFiles() {{{2
3484 " toggles the display of hidden files
3485 function! s:toggleShowFiles()
3486 let t:NERDTreeShowFiles = !t:NERDTreeShowFiles
3487 call s:renderViewSavingPosition()
3491 " FUNCTION: s:toggleShowHidden() {{{2
3492 " toggles the display of hidden files
3493 function! s:toggleShowHidden()
3494 let t:NERDTreeShowHidden = !t:NERDTreeShowHidden
3495 call s:renderViewSavingPosition()
3499 "FUNCTION: s:upDir(keepState) {{{2
3500 "moves the tree up a level
3503 "keepState: 1 if the current root should be left open when the tree is
3505 function! s:upDir(keepState)
3506 let cwd = t:NERDTreeRoot.path.str(0)
3507 if cwd == "/" || cwd =~ '^[^/]..$'
3508 call s:echo("already at top dir")
3511 call t:NERDTreeRoot.close()
3514 let oldRoot = t:NERDTreeRoot
3516 if empty(t:NERDTreeRoot.parent)
3517 let path = t:NERDTreeRoot.path.getPathTrunk()
3518 let newRoot = s:TreeDirNode.New(path)
3520 call newRoot.transplantChild(t:NERDTreeRoot)
3521 let t:NERDTreeRoot = newRoot
3523 let t:NERDTreeRoot = t:NERDTreeRoot.parent
3528 call s:putCursorOnNode(oldRoot, 0, 0)
3533 "reset &cpo back to users setting
3534 let &cpo = s:old_cpo
3536 " vim: set sw=4 sts=4 et fdm=marker: