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: 7 Jun, 2009
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 = '3.1.1'
15 " SECTION: Script init stuff {{{1
16 "============================================================
17 if exists("loaded_nerd_tree")
23 let loaded_nerd_tree = 1
25 "for line continuation - i.e dont want C in &cpo
29 "Function: s:initVariable() function {{{2
30 "This function is used to initialise a given variable to a given value. The
31 "variable is only initialised if it does not exist prior
34 "var: the name of the var to be initialised
35 "value: the value to initialise var to
38 "1 if the var is set, 0 otherwise
39 function! s:initVariable(var, value)
41 exec 'let ' . a:var . ' = ' . "'" . a:value . "'"
47 "SECTION: Init variable calls and other random constants {{{2
48 call s:initVariable("g:NERDChristmasTree", 1)
49 call s:initVariable("g:NERDTreeAutoCenter", 1)
50 call s:initVariable("g:NERDTreeAutoCenterThreshold", 3)
51 call s:initVariable("g:NERDTreeCaseSensitiveSort", 0)
52 call s:initVariable("g:NERDTreeChDirMode", 0)
53 if !exists("g:NERDTreeIgnore")
54 let g:NERDTreeIgnore = ['\~$']
56 call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks')
57 call s:initVariable("g:NERDTreeHighlightCursorline", 1)
58 call s:initVariable("g:NERDTreeHijackNetrw", 1)
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:NERDTreeStatusline", "%{b:NERDTreeRoot.path.strForOS(0)}")
82 call s:initVariable("g:NERDTreeWinPos", "left")
83 call s:initVariable("g:NERDTreeWinSize", 31)
85 let s:running_windows = has("win16") || has("win32") || has("win64")
87 "init the shell commands that will be used to copy nodes, and remove dir trees
89 "Note: the space after the command is important
91 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ')
93 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ')
94 call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ')
98 "SECTION: Init variable calls for key mappings {{{2
99 call s:initVariable("g:NERDTreeMapActivateNode", "o")
100 call s:initVariable("g:NERDTreeMapChangeRoot", "C")
101 call s:initVariable("g:NERDTreeMapChdir", "cd")
102 call s:initVariable("g:NERDTreeMapCloseChildren", "X")
103 call s:initVariable("g:NERDTreeMapCloseDir", "x")
104 call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
105 call s:initVariable("g:NERDTreeMapExecute", "!")
106 call s:initVariable("g:NERDTreeMapFilesystemMenu", "m")
107 call s:initVariable("g:NERDTreeMapHelp", "?")
108 call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
109 call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
110 call s:initVariable("g:NERDTreeMapJumpNextSibling", "<C-j>")
111 call s:initVariable("g:NERDTreeMapJumpParent", "p")
112 call s:initVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>")
113 call s:initVariable("g:NERDTreeMapJumpRoot", "P")
114 call s:initVariable("g:NERDTreeMapOpenExpl", "e")
115 call s:initVariable("g:NERDTreeMapOpenInTab", "t")
116 call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T")
117 call s:initVariable("g:NERDTreeMapOpenRecursively", "O")
118 call s:initVariable("g:NERDTreeMapOpenSplit", "i")
119 call s:initVariable("g:NERDTreeMapOpenVSplit", "s")
120 call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode)
121 call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit)
122 call s:initVariable("g:NERDTreeMapPreviewVSplit", "g" . NERDTreeMapOpenVSplit)
123 call s:initVariable("g:NERDTreeMapQuit", "q")
124 call s:initVariable("g:NERDTreeMapRefresh", "r")
125 call s:initVariable("g:NERDTreeMapRefreshRoot", "R")
126 call s:initVariable("g:NERDTreeMapToggleBookmarks", "B")
127 call s:initVariable("g:NERDTreeMapToggleFiles", "F")
128 call s:initVariable("g:NERDTreeMapToggleFilters", "f")
129 call s:initVariable("g:NERDTreeMapToggleHidden", "I")
130 call s:initVariable("g:NERDTreeMapUpdir", "u")
131 call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U")
133 "SECTION: Script level variable declaration{{{2
134 let s:escape_chars = " \\`\|\"#%&,?()\*^<>"
135 let s:NERDTreeBufName = 'NERD_tree_'
138 let s:tree_markup_reg = '^[ `|]*[\-+~]'
139 let s:tree_up_dir_line = '.. (up a dir)'
146 "the number to add to the nerd tree buffer name to make the buf name unique
147 let s:next_buffer_number = 1
149 " SECTION: Commands {{{1
150 "============================================================
151 "init the command that users start the nerd tree with
152 command! -n=? -complete=dir -bar NERDTree :call s:initNerdTree('<args>')
153 command! -n=? -complete=dir -bar NERDTreeToggle :call s:toggle('<args>')
154 command! -n=0 -bar NERDTreeClose :call s:closeTreeIfOpen()
155 command! -n=1 -complete=customlist,s:completeBookmarks -bar NERDTreeFromBookmark call s:initNerdTree('<args>')
156 command! -n=0 -bar NERDTreeMirror call s:initNerdTreeMirror()
157 " SECTION: Auto commands {{{1
158 "============================================================
160 "Save the cursor position whenever we close the nerd tree
161 exec "autocmd BufWinLeave *". s:NERDTreeBufName ." call <SID>saveScreenState()"
162 "cache bookmarks when vim loads
163 autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
166 if g:NERDTreeHijackNetrw
167 augroup NERDTreeHijackNetrw
168 autocmd VimEnter * silent! autocmd! FileExplorer
169 au BufEnter,VimEnter * call s:checkForBrowse(expand("<amatch>"))
173 "SECTION: Classes {{{1
174 "============================================================
175 "CLASS: Bookmark {{{2
176 "============================================================
178 " FUNCTION: Bookmark.AddBookmark(name, path) {{{3
179 " Class method to add a new bookmark to the list, if a previous bookmark exists
180 " with the same name, just update the path for that bookmark
181 function! s:Bookmark.AddBookmark(name, path)
182 for i in s:Bookmark.Bookmarks()
188 call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
189 call s:Bookmark.Sort()
191 " Function: Bookmark.Bookmarks() {{{3
192 " Class method to get all bookmarks. Lazily initializes the bookmarks global
194 function! s:Bookmark.Bookmarks()
195 if !exists("g:NERDTreeBookmarks")
196 let g:NERDTreeBookmarks = []
198 return g:NERDTreeBookmarks
200 " Function: Bookmark.BookmarkExistsFor(name) {{{3
201 " class method that returns 1 if a bookmark with the given name is found, 0
203 function! s:Bookmark.BookmarkExistsFor(name)
205 call s:Bookmark.BookmarkFor(a:name)
207 catch /^NERDTree.BookmarkNotFoundError/
211 " Function: Bookmark.BookmarkFor(name) {{{3
212 " Class method to get the bookmark that has the given name. {} is return if no
214 function! s:Bookmark.BookmarkFor(name)
215 for i in s:Bookmark.Bookmarks()
220 throw "NERDTree.BookmarkNotFoundError: no bookmark found for name: \"". a:name .'"'
222 " Function: Bookmark.BookmarkNames() {{{3
223 " Class method to return an array of all bookmark names
224 function! s:Bookmark.BookmarkNames()
226 for i in s:Bookmark.Bookmarks()
227 call add(names, i.name)
231 " FUNCTION: Bookmark.CacheBookmarks(silent) {{{3
232 " Class method to read all bookmarks from the bookmarks file intialize
233 " bookmark objects for each one.
236 " silent - dont echo an error msg if invalid bookmarks are found
237 function! s:Bookmark.CacheBookmarks(silent)
238 if filereadable(g:NERDTreeBookmarksFile)
239 let g:NERDTreeBookmarks = []
240 let g:NERDTreeInvalidBookmarks = []
241 let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
242 let invalidBookmarksFound = 0
243 for i in bookmarkStrings
248 let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
249 let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
252 let bookmark = s:Bookmark.New(name, s:Path.New(path))
253 call add(g:NERDTreeBookmarks, bookmark)
254 catch /^NERDTree.InvalidArgumentsError/
255 call add(g:NERDTreeInvalidBookmarks, i)
256 let invalidBookmarksFound += 1
260 if invalidBookmarksFound
261 call s:Bookmark.Write()
263 call s:echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
266 call s:Bookmark.Sort()
269 " FUNCTION: Bookmark.compareTo(otherbookmark) {{{3
270 " Compare these two bookmarks for sorting purposes
271 function! s:Bookmark.compareTo(otherbookmark)
272 return a:otherbookmark.name < self.name
274 " FUNCTION: Bookmark.ClearAll() {{{3
275 " Class method to delete all bookmarks.
276 function! s:Bookmark.ClearAll()
277 for i in s:Bookmark.Bookmarks()
280 call s:Bookmark.Write()
282 " FUNCTION: Bookmark.delete() {{{3
283 " Delete this bookmark. If the node for this bookmark is under the current
284 " root, then recache bookmarks for its Path object
285 function! s:Bookmark.delete()
288 let node = self.getNode(1)
289 catch /^NERDTree.BookmarkedNodeNotFoundError/
291 call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
293 call node.path.cacheDisplayString()
295 call s:Bookmark.Write()
297 " FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{3
298 " Gets the treenode for this bookmark
301 " searchFromAbsoluteRoot: specifies whether we should search from the current
302 " tree root, or the highest cached node
303 function! s:Bookmark.getNode(searchFromAbsoluteRoot)
304 let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : b:NERDTreeRoot
305 let targetNode = searchRoot.findNode(self.path)
307 throw "NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: " . self.name
311 " FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{3
312 " Class method that finds the bookmark with the given name and returns the
314 function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
315 let bookmark = s:Bookmark.BookmarkFor(a:name)
316 return bookmark.getNode(a:searchFromAbsoluteRoot)
318 " Function: Bookmark.InvalidBookmarks() {{{3
319 " Class method to get all invalid bookmark strings read from the bookmarks
321 function! s:Bookmark.InvalidBookmarks()
322 if !exists("g:NERDTreeInvalidBookmarks")
323 let g:NERDTreeInvalidBookmarks = []
325 return g:NERDTreeInvalidBookmarks
327 " FUNCTION: Bookmark.mustExist() {{{3
328 function! s:Bookmark.mustExist()
329 if !self.path.exists()
330 call s:Bookmark.CacheBookmarks(1)
331 throw "NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \"".
332 \ self.name ."\" points to a non existing location: \"". self.path.strForOS(0)
335 " FUNCTION: Bookmark.New(name, path) {{{3
336 " Create a new bookmark object with the given name and path object
337 function! s:Bookmark.New(name, path)
339 throw "NERDTree.IllegalBookmarkNameError: illegal name:" . a:name
342 let newBookmark = copy(self)
343 let newBookmark.name = a:name
344 let newBookmark.path = a:path
347 " Function: Bookmark.setPath(path) {{{3
348 " makes this bookmark point to the given path
349 function! s:Bookmark.setPath(path)
350 let self.path = a:path
352 " Function: Bookmark.Sort() {{{3
353 " Class method that sorts all bookmarks
354 function! s:Bookmark.Sort()
355 let CompareFunc = function("s:compareBookmarks")
356 call sort(s:Bookmark.Bookmarks(), CompareFunc)
358 " Function: Bookmark.str() {{{3
359 " Get the string that should be rendered in the view for this bookmark
360 function! s:Bookmark.str()
361 let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name)
363 let pathStrMaxLen = pathStrMaxLen - &numberwidth
366 let pathStr = self.path.strForOS(0)
367 if len(pathStr) > pathStrMaxLen
368 let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
370 return '>' . self.name . ' ' . pathStr
372 " FUNCTION: Bookmark.toRoot() {{{3
373 " Make the node for this bookmark the new tree root
374 function! s:Bookmark.toRoot()
377 let targetNode = self.getNode(1)
378 catch /^NERDTree.BookmarkedNodeNotFoundError/
379 let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
381 call targetNode.makeRoot()
383 call targetNode.putCursorHere(0, 0)
386 " FUNCTION: Bookmark.ToRoot(name) {{{3
387 " Make the node for this bookmark the new tree root
388 function! s:Bookmark.ToRoot(name)
389 let bookmark = s:Bookmark.BookmarkFor(a:name)
390 call bookmark.toRoot()
394 "FUNCTION: Bookmark.validate() {{{3
395 function! s:Bookmark.validate()
396 if self.path.exists()
399 call s:Bookmark.CacheBookmarks(1)
401 call s:echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
406 " Function: Bookmark.Write() {{{3
407 " Class method to write all bookmarks to the bookmarks file
408 function! s:Bookmark.Write()
409 let bookmarkStrings = []
410 for i in s:Bookmark.Bookmarks()
411 call add(bookmarkStrings, i.name . ' ' . i.path.strForOS(0))
414 "add a blank line before the invalid ones
415 call add(bookmarkStrings, "")
417 for j in s:Bookmark.InvalidBookmarks()
418 call add(bookmarkStrings, j)
420 call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
422 "CLASS: TreeFileNode {{{2
423 "This class is the parent of the TreeDirNode class and constitures the
424 "'Component' part of the composite design pattern between the treenode
426 "============================================================
427 let s:TreeFileNode = {}
428 "FUNCTION: TreeFileNode.bookmark(name) {{{3
429 "bookmark this node with a:name
430 function! s:TreeFileNode.bookmark(name)
432 let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1)
433 call oldMarkedNode.path.cacheDisplayString()
434 catch /^NERDTree.BookmarkNotFoundError/
437 call s:Bookmark.AddBookmark(a:name, self.path)
438 call self.path.cacheDisplayString()
439 call s:Bookmark.Write()
441 "FUNCTION: TreeFileNode.cacheParent() {{{3
442 "initializes self.parent if it isnt already
443 function! s:TreeFileNode.cacheParent()
444 if empty(self.parent)
445 let parentPath = self.path.getParent()
446 if parentPath.equals(self.path)
447 throw "NERDTree.CannotCacheParentError: already at root"
449 let self.parent = s:TreeFileNode.New(parentPath)
452 "FUNCTION: TreeFileNode.compareNodes {{{3
453 "This is supposed to be a class level method but i cant figure out how to
454 "get func refs to work from a dict..
456 "A class level method that compares two nodes
459 "n1, n2: the 2 nodes to compare
460 function! s:compareNodes(n1, n2)
461 return a:n1.path.compareTo(a:n2.path)
464 "FUNCTION: TreeFileNode.clearBoomarks() {{{3
465 function! s:TreeFileNode.clearBoomarks()
466 for i in s:Bookmark.Bookmarks()
467 if i.path.equals(self.path)
471 call self.path.cacheDisplayString()
473 "FUNCTION: TreeFileNode.copy(dest) {{{3
474 function! s:TreeFileNode.copy(dest)
475 call self.path.copy(a:dest)
476 let newPath = s:Path.New(a:dest)
477 let parent = b:NERDTreeRoot.findNode(newPath.getParent())
479 call parent.refresh()
481 return parent.findNode(newPath)
484 "FUNCTION: TreeFileNode.delete {{{3
485 "Removes this node from the tree and calls the Delete method for its path obj
486 function! s:TreeFileNode.delete()
487 call self.path.delete()
488 call self.parent.removeChild(self)
491 "FUNCTION: TreeFileNode.renderToString {{{3
492 "returns a string representation for this tree to be rendered in the view
493 function! s:TreeFileNode.renderToString()
494 return self._renderToString(0, 0, [], self.getChildCount() ==# 1)
499 "depth: the current depth in the tree for this call
500 "drawText: 1 if we should actually draw the line for this node (if 0 then the
501 "child nodes are rendered only)
502 "vertMap: a binary array that indicates whether a vertical bar should be draw
503 "for each depth in the tree
504 "isLastChild:true if this curNode is the last child of its parent
505 function! s:TreeFileNode._renderToString(depth, drawText, vertMap, isLastChild)
511 "get all the leading spaces and vertical tree parts for this line
513 for j in a:vertMap[0:-2]
515 let treeParts = treeParts . '| '
517 let treeParts = treeParts . ' '
522 "get the last vertical tree part for this line which will be different
523 "if this node is the last child of its parent
525 let treeParts = treeParts . '`'
527 let treeParts = treeParts . '|'
531 "smack the appropriate dir/file symbol on the line before the file/dir
533 if self.path.isDirectory
535 let treeParts = treeParts . '~'
537 let treeParts = treeParts . '+'
540 let treeParts = treeParts . '-'
542 let line = treeParts . self.strDisplay()
544 let output = output . line . "\n"
547 "if the node is an open dir, draw its children
548 if self.path.isDirectory ==# 1 && self.isOpen ==# 1
550 let childNodesToDraw = self.getVisibleChildren()
551 if len(childNodesToDraw) > 0
553 "draw all the nodes children except the last
554 let lastIndx = len(childNodesToDraw)-1
556 for i in childNodesToDraw[0:lastIndx-1]
557 let output = output . i._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
561 "draw the last child, indicating that it IS the last
562 let output = output . childNodesToDraw[lastIndx]._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 0), 1)
568 "FUNCTION: TreeFileNode.equals(treenode) {{{3
570 "Compares this treenode to the input treenode and returns 1 if they are the
573 "Use this method instead of == because sometimes when the treenodes contain
574 "many children, vim seg faults when doing ==
577 "treenode: the other treenode to compare to
578 function! s:TreeFileNode.equals(treenode)
579 return self.path.str(1) ==# a:treenode.path.str(1)
582 "FUNCTION: TreeFileNode.findNode(path) {{{3
583 "Returns self if this node.path.Equals the given path.
584 "Returns {} if not equal.
587 "path: the path object to compare against
588 function! s:TreeFileNode.findNode(path)
589 if a:path.equals(self.path)
594 "FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3
596 "Finds the next sibling for this node in the indicated direction. This sibling
597 "must be a directory and may/may not have children as specified.
600 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
603 "a treenode object or {} if no appropriate sibling could be found
604 function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
605 "if we have no parent then we can have no siblings
607 let nextSibling = self.findSibling(a:direction)
609 while nextSibling != {}
610 if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen
613 let nextSibling = nextSibling.findSibling(a:direction)
619 "FUNCTION: TreeFileNode.findSibling(direction) {{{3
621 "Finds the next sibling for this node in the indicated direction
624 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
627 "a treenode object or {} if no sibling could be found
628 function! s:TreeFileNode.findSibling(direction)
629 "if we have no parent then we can have no siblings
632 "get the index of this node in its parents children
633 let siblingIndx = self.parent.getChildIndex(self.path)
636 "move a long to the next potential sibling node
637 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
639 "keep moving along to the next sibling till we find one that is valid
640 let numSiblings = self.parent.getChildCount()
641 while siblingIndx >= 0 && siblingIndx < numSiblings
643 "if the next node is not an ignored node (i.e. wont show up in the
644 "view) then return it
645 if self.parent.children[siblingIndx].path.ignore() ==# 0
646 return self.parent.children[siblingIndx]
650 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
658 "FUNCTION: TreeFileNode.getLineNum(){{{3
659 "returns the line number this node is rendered on, or -1 if it isnt rendered
660 function! s:TreeFileNode.getLineNum()
661 "if the node is the root then return the root line no.
663 return s:TreeFileNode.GetRootLineNum()
666 let totalLines = line("$")
668 "the path components we have matched so far
669 let pathcomponents = [substitute(b:NERDTreeRoot.path.str(0), '/ *$', '', '')]
670 "the index of the component we are searching for
671 let curPathComponent = 1
673 let fullpath = self.path.str(0)
676 let lnum = s:TreeFileNode.GetRootLineNum()
679 "have we reached the bottom of the tree?
680 if lnum ==# totalLines+1
684 let curLine = getline(lnum)
686 let indent = s:indentLevelFor(curLine)
687 if indent ==# curPathComponent
688 let curLine = s:stripMarkupFromLine(curLine, 1)
690 let curPath = join(pathcomponents, '/') . '/' . curLine
691 if stridx(fullpath, curPath, 0) ==# 0
692 if fullpath ==# curPath || strpart(fullpath, len(curPath)-1,1) ==# '/'
693 let curLine = substitute(curLine, '/ *$', '', '')
694 call add(pathcomponents, curLine)
695 let curPathComponent = curPathComponent + 1
697 if fullpath ==# curPath
707 "FUNCTION: TreeFileNode.GetRootLineNum(){{{3
708 "gets the line number of the root node
709 function! s:TreeFileNode.GetRootLineNum()
711 while getline(rootLine) !~ '^/'
712 let rootLine = rootLine + 1
717 "FUNCTION: TreeFileNode.GetSelected() {{{3
718 "gets the treenode that the cursor is currently over
719 function! s:TreeFileNode.GetSelected()
721 let path = s:getPath(line("."))
725 return b:NERDTreeRoot.findNode(path)
730 "FUNCTION: TreeFileNode.isVisible() {{{3
731 "returns 1 if this node should be visible according to the tree filters and
732 "hidden file filters (and their on/off status)
733 function! s:TreeFileNode.isVisible()
734 return !self.path.ignore()
736 "FUNCTION: TreeFileNode.isRoot() {{{3
737 "returns 1 if this node is b:NERDTreeRoot
738 function! s:TreeFileNode.isRoot()
739 if !s:treeExistsForBuf()
740 throw "NERDTree.NoTreeError: No tree exists for the current buffer"
743 return self.equals(b:NERDTreeRoot)
746 "FUNCTION: TreeFileNode.makeRoot() {{{3
747 "Make this node the root of the tree
748 function! s:TreeFileNode.makeRoot()
749 if self.path.isDirectory
750 let b:NERDTreeRoot = self
752 call self.cacheParent()
753 let b:NERDTreeRoot = self.parent
756 call b:NERDTreeRoot.open()
758 "change dir to the dir of the new root if instructed to
759 if g:NERDTreeChDirMode ==# 2
760 exec "cd " . b:NERDTreeRoot.path.strForEditCmd()
763 "FUNCTION: TreeFileNode.New(path) {{{3
764 "Returns a new TreeNode object with the given path and parent
767 "path: a path object representing the full filesystem path to the file/dir that the node represents
768 function! s:TreeFileNode.New(path)
769 if a:path.isDirectory
770 return s:TreeDirNode.New(a:path)
773 let newTreeNode = copy(self)
774 let newTreeNode.path = a:path
775 let newTreeNode.parent = {}
780 "FUNCTION: TreeFileNode.open() {{{3
781 "Open the file represented by the given node in the current window, splitting
782 "the window if needed
785 "treenode: file node to open
786 function! s:TreeFileNode.open()
787 if b:NERDTreeType ==# "secondary"
788 exec 'edit ' . self.path.strForEditCmd()
792 "if the file is already open in this tab then just stick the cursor in it
793 let winnr = bufwinnr('^' . self.path.strForOS(0) . '$')
795 call s:exec(winnr . "wincmd w")
798 if !s:isWindowUsable(winnr("#")) && s:firstUsableWindow() ==# -1
799 call self.openSplit()
802 if !s:isWindowUsable(winnr("#"))
803 call s:exec(s:firstUsableWindow() . "wincmd w")
805 call s:exec('wincmd p')
807 exec ("edit " . self.path.strForEditCmd())
808 catch /^Vim\%((\a\+)\)\=:E37/
809 call s:putCursorInTreeWin()
810 throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str(0) ." is already open and modified."
811 catch /^Vim\%((\a\+)\)\=:/
817 "FUNCTION: TreeFileNode.openSplit() {{{3
818 "Open this node in a new window
819 function! s:TreeFileNode.openSplit()
821 if b:NERDTreeType ==# "secondary"
822 exec "split " . self.path.strForEditCmd()
826 " Save the user's settings for splitbelow and splitright
827 let savesplitbelow=&splitbelow
828 let savesplitright=&splitright
830 " 'there' will be set to a command to move from the split window
831 " back to the explorer window
833 " 'back' will be set to a command to move from the explorer window
834 " back to the newly split window
836 " 'right' and 'below' will be set to the settings needed for
837 " splitbelow and splitright IF the explorer is the only window.
839 let there= g:NERDTreeWinPos ==# "left" ? "wincmd h" : "wincmd l"
840 let back = g:NERDTreeWinPos ==# "left" ? "wincmd l" : "wincmd h"
841 let right= g:NERDTreeWinPos ==# "left"
844 " Attempt to go to adjacent window
847 let onlyOneWin = (winnr("$") ==# 1)
849 " If no adjacent window, set splitright and splitbelow appropriately
851 let &splitright=right
852 let &splitbelow=below
854 " found adjacent window - invert split direction
855 let &splitright=!right
856 let &splitbelow=!below
859 let splitMode = onlyOneWin ? "vertical" : ""
861 " Open the new window
863 exec(splitMode." sp " . self.path.strForEditCmd())
864 catch /^Vim\%((\a\+)\)\=:E37/
865 call s:putCursorInTreeWin()
866 throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str(0) ." is already open and modified."
867 catch /^Vim\%((\a\+)\)\=:/
871 "resize the tree window if no other window was open before
873 let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
875 exec("silent ". splitMode ." resize ". size)
876 call s:exec('wincmd p')
879 " Restore splitmode settings
880 let &splitbelow=savesplitbelow
881 let &splitright=savesplitright
883 "FUNCTION: TreeFileNode.openVSplit() {{{3
884 "Open this node in a new vertical window
885 function! s:TreeFileNode.openVSplit()
886 if b:NERDTreeType ==# "secondary"
887 exec "vnew " . self.path.strForEditCmd()
891 let winwidth = winwidth(".")
893 let winwidth = g:NERDTreeWinSize
896 call s:exec("wincmd p")
897 exec "vnew " . self.path.strForEditCmd()
899 "resize the nerd tree back to the original size
900 call s:putCursorInTreeWin()
901 exec("silent vertical resize ". winwidth)
902 call s:exec('wincmd p')
904 "FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3
905 "Places the cursor on the line number this node is rendered on
908 "isJump: 1 if this cursor movement should be counted as a jump by vim
909 "recurseUpward: try to put the cursor on the parent if the this node isnt
911 function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
912 let ln = self.getLineNum()
917 call cursor(ln, col("."))
921 while node != {} && node.getLineNum() ==# -1
922 let node = node.parent
926 call node.putCursorHere(a:isJump, 0)
931 "FUNCTION: TreeFileNode.refresh() {{{3
932 function! s:TreeFileNode.refresh()
933 call self.path.refresh()
935 "FUNCTION: TreeFileNode.rename() {{{3
936 "Calls the rename method for this nodes path obj
937 function! s:TreeFileNode.rename(newName)
938 let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
939 call self.path.rename(newName)
940 call self.parent.removeChild(self)
942 let parentPath = self.path.getPathTrunk()
943 let newParent = b:NERDTreeRoot.findNode(parentPath)
946 call newParent.createChild(self.path, 1)
947 call newParent.refresh()
950 "FUNCTION: TreeFileNode.strDisplay() {{{3
952 "Returns a string that specifies how the node should be represented as a
956 "a string that can be used in the view to represent this node
957 function! s:TreeFileNode.strDisplay()
958 return self.path.strDisplay()
961 "CLASS: TreeDirNode {{{2
962 "This class is a child of the TreeFileNode class and constitutes the
963 "'Composite' part of the composite design pattern between the treenode
965 "============================================================
966 let s:TreeDirNode = copy(s:TreeFileNode)
967 "FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
968 "class method that returns the highest cached ancestor of the current root
969 function! s:TreeDirNode.AbsoluteTreeRoot()
970 let currentNode = b:NERDTreeRoot
971 while currentNode.parent != {}
972 let currentNode = currentNode.parent
976 "FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{3
977 "Adds the given treenode to the list of children for this node
980 "-treenode: the node to add
981 "-inOrder: 1 if the new node should be inserted in sorted order
982 function! s:TreeDirNode.addChild(treenode, inOrder)
983 call add(self.children, a:treenode)
984 let a:treenode.parent = self
987 call self.sortChildren()
991 "FUNCTION: TreeDirNode.close() {{{3
992 "Closes this directory
993 function! s:TreeDirNode.close()
997 "FUNCTION: TreeDirNode.closeChildren() {{{3
998 "Closes all the child dir nodes of this node
999 function! s:TreeDirNode.closeChildren()
1000 for i in self.children
1001 if i.path.isDirectory
1003 call i.closeChildren()
1008 "FUNCTION: TreeDirNode.createChild(path, inOrder) {{{3
1009 "Instantiates a new child node for this node with the given path. The new
1010 "nodes parent is set to this node.
1013 "path: a Path object that this node will represent/contain
1014 "inOrder: 1 if the new node should be inserted in sorted order
1017 "the newly created node
1018 function! s:TreeDirNode.createChild(path, inOrder)
1019 let newTreeNode = s:TreeFileNode.New(a:path)
1020 call self.addChild(newTreeNode, a:inOrder)
1024 "FUNCTION: TreeDirNode.findNode(path) {{{3
1025 "Will find one of the children (recursively) that has the given path
1028 "path: a path object
1029 unlet s:TreeDirNode.findNode
1030 function! s:TreeDirNode.findNode(path)
1031 if a:path.equals(self.path)
1034 if stridx(a:path.str(1), self.path.str(1), 0) ==# -1
1038 if self.path.isDirectory
1039 for i in self.children
1040 let retVal = i.findNode(a:path)
1048 "FUNCTION: TreeDirNode.getChildCount() {{{3
1049 "Returns the number of children this node has
1050 function! s:TreeDirNode.getChildCount()
1051 return len(self.children)
1054 "FUNCTION: TreeDirNode.getChild(path) {{{3
1055 "Returns child node of this node that has the given path or {} if no such node
1058 "This function doesnt not recurse into child dir nodes
1061 "path: a path object
1062 function! s:TreeDirNode.getChild(path)
1063 if stridx(a:path.str(1), self.path.str(1), 0) ==# -1
1067 let index = self.getChildIndex(a:path)
1071 return self.children[index]
1076 "FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{3
1077 "returns the child at the given index
1079 "indx: the index to get the child from
1080 "visible: 1 if only the visible children array should be used, 0 if all the
1081 "children should be searched.
1082 function! s:TreeDirNode.getChildByIndex(indx, visible)
1083 let array_to_search = a:visible? self.getVisibleChildren() : self.children
1084 if a:indx > len(array_to_search)
1085 throw "NERDTree.InvalidArgumentsError: Index is out of bounds."
1087 return array_to_search[a:indx]
1090 "FUNCTION: TreeDirNode.getChildIndex(path) {{{3
1091 "Returns the index of the child node of this node that has the given path or
1092 "-1 if no such node exists.
1094 "This function doesnt not recurse into child dir nodes
1097 "path: a path object
1098 function! s:TreeDirNode.getChildIndex(path)
1099 if stridx(a:path.str(1), self.path.str(1), 0) ==# -1
1103 "do a binary search for the child
1105 let z = self.getChildCount()
1108 let diff = a:path.compareTo(self.children[mid].path)
1121 "FUNCTION: TreeDirNode.GetSelected() {{{3
1122 "Returns the current node if it is a dir node, or else returns the current
1124 unlet s:TreeDirNode.GetSelected
1125 function! s:TreeDirNode.GetSelected()
1126 let currentDir = s:TreeFileNode.GetSelected()
1127 if currentDir != {} && !currentDir.isRoot()
1128 if currentDir.path.isDirectory ==# 0
1129 let currentDir = currentDir.parent
1134 "FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
1135 "Returns the number of visible children this node has
1136 function! s:TreeDirNode.getVisibleChildCount()
1137 return len(self.getVisibleChildren())
1140 "FUNCTION: TreeDirNode.getVisibleChildren() {{{3
1141 "Returns a list of children to display for this node, in the correct order
1144 "an array of treenodes
1145 function! s:TreeDirNode.getVisibleChildren()
1147 for i in self.children
1148 if i.path.ignore() ==# 0
1149 call add(toReturn, i)
1155 "FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
1156 "returns 1 if this node has any childre, 0 otherwise..
1157 function! s:TreeDirNode.hasVisibleChildren()
1158 return self.getVisibleChildCount() != 0
1161 "FUNCTION: TreeDirNode._initChildren() {{{3
1162 "Removes all childen from this node and re-reads them
1165 "silent: 1 if the function should not echo any "please wait" messages for
1168 "Return: the number of child nodes read
1169 function! s:TreeDirNode._initChildren(silent)
1170 "remove all the current child nodes
1171 let self.children = []
1173 "get an array of all the files in the nodes dir
1175 let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
1176 let files = split(filesStr, "\n")
1178 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
1179 call s:echo("Please wait, caching a large dir ...")
1182 let invalidFilesFound = 0
1185 "filter out the .. and . directories
1186 "Note: we must match .. AND ../ cos sometimes the globpath returns
1187 "../ for path with strange chars (eg $)
1188 if i !~ '\.\.\/\?$' && i !~ '\.\/\?$'
1190 "put the next file in a new node and attach it
1192 let path = s:Path.New(i)
1193 call self.createChild(path, 0)
1194 catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
1195 let invalidFilesFound += 1
1200 call self.sortChildren()
1202 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
1203 call s:echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).")
1206 if invalidFilesFound
1207 call s:echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree")
1209 return self.getChildCount()
1211 "FUNCTION: TreeDirNode.New(path) {{{3
1212 "Returns a new TreeNode object with the given path and parent
1215 "path: a path object representing the full filesystem path to the file/dir that the node represents
1216 unlet s:TreeDirNode.New
1217 function! s:TreeDirNode.New(path)
1218 if a:path.isDirectory != 1
1219 throw "NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object."
1222 let newTreeNode = copy(self)
1223 let newTreeNode.path = a:path
1225 let newTreeNode.isOpen = 0
1226 let newTreeNode.children = []
1228 let newTreeNode.parent = {}
1232 "FUNCTION: TreeDirNode.open() {{{3
1233 "Reads in all this nodes children
1235 "Return: the number of child nodes read
1236 unlet s:TreeDirNode.open
1237 function! s:TreeDirNode.open()
1239 if self.children ==# []
1240 return self._initChildren(0)
1246 " FUNCTION: TreeDirNode.openExplorer() {{{3
1247 " opens an explorer window for this node in the previous window (could be a
1248 " nerd tree or a netrw)
1249 function! s:TreeDirNode.openExplorer()
1250 let oldwin = winnr()
1251 call s:exec('wincmd p')
1252 if oldwin ==# winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2)
1253 call s:exec('wincmd p')
1254 call self.openSplit()
1256 exec ("silent edit " . self.path.strForEditCmd())
1259 "FUNCTION: TreeDirNode.openRecursively() {{{3
1260 "Opens this treenode and all of its children whose paths arent 'ignored'
1261 "because of the file filters.
1263 "This method is actually a wrapper for the OpenRecursively2 method which does
1265 function! s:TreeDirNode.openRecursively()
1266 call self._openRecursively2(1)
1269 "FUNCTION: TreeDirNode._openRecursively2() {{{3
1270 "Opens this all children of this treenode recursively if either:
1271 " *they arent filtered by file filters
1275 "forceOpen: 1 if this node should be opened regardless of file filters
1276 function! s:TreeDirNode._openRecursively2(forceOpen)
1277 if self.path.ignore() ==# 0 || a:forceOpen
1279 if self.children ==# []
1280 call self._initChildren(1)
1283 for i in self.children
1284 if i.path.isDirectory ==# 1
1285 call i._openRecursively2(0)
1291 "FUNCTION: TreeDirNode.refresh() {{{3
1292 unlet s:TreeDirNode.refresh
1293 function! s:TreeDirNode.refresh()
1294 call self.path.refresh()
1296 "if this node was ever opened, refresh its children
1297 if self.isOpen || !empty(self.children)
1298 "go thru all the files/dirs under this node
1299 let newChildNodes = []
1300 let invalidFilesFound = 0
1302 let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
1303 let files = split(filesStr, "\n")
1305 if i !~ '\.\.$' && i !~ '\.$'
1308 "create a new path and see if it exists in this nodes children
1309 let path = s:Path.New(i)
1310 let newNode = self.getChild(path)
1312 call newNode.refresh()
1313 call add(newChildNodes, newNode)
1315 "the node doesnt exist so create it
1317 let newNode = s:TreeFileNode.New(path)
1318 let newNode.parent = self
1319 call add(newChildNodes, newNode)
1323 catch /^NERDTree.InvalidArgumentsError/
1324 let invalidFilesFound = 1
1329 "swap this nodes children out for the children we just read/refreshed
1330 let self.children = newChildNodes
1331 call self.sortChildren()
1333 if invalidFilesFound
1334 call s:echoWarning("some files could not be loaded into the NERD tree")
1339 "FUNCTION: TreeDirNode.removeChild(treenode) {{{3
1341 "Removes the given treenode from this nodes set of children
1344 "treenode: the node to remove
1346 "Throws a NERDTree.ChildNotFoundError if the given treenode is not found
1347 function! s:TreeDirNode.removeChild(treenode)
1348 for i in range(0, self.getChildCount()-1)
1349 if self.children[i].equals(a:treenode)
1350 call remove(self.children, i)
1355 throw "NERDTree.ChildNotFoundError: child node was not found"
1358 "FUNCTION: TreeDirNode.sortChildren() {{{3
1360 "Sorts the children of this node according to alphabetical order and the
1361 "directory priority.
1363 function! s:TreeDirNode.sortChildren()
1364 let CompareFunc = function("s:compareNodes")
1365 call sort(self.children, CompareFunc)
1368 "FUNCTION: TreeDirNode.toggleOpen() {{{3
1369 "Opens this directory if it is closed and vice versa
1370 function! s:TreeDirNode.toggleOpen()
1371 if self.isOpen ==# 1
1378 "FUNCTION: TreeDirNode.transplantChild(newNode) {{{3
1379 "Replaces the child of this with the given node (where the child node's full
1380 "path matches a:newNode's fullpath). The search for the matching node is
1384 "newNode: the node to graft into the tree
1385 function! s:TreeDirNode.transplantChild(newNode)
1386 for i in range(0, self.getChildCount()-1)
1387 if self.children[i].equals(a:newNode)
1388 let self.children[i] = a:newNode
1389 let a:newNode.parent = self
1394 "============================================================
1396 "============================================================
1398 "FUNCTION: Path.AbsolutePathFor(str) {{{3
1399 function! s:Path.AbsolutePathFor(str)
1401 if s:running_windows
1402 let prependCWD = a:str !~ '^.:\(\\\|\/\)'
1404 let prependCWD = a:str !~ '^/'
1407 let toReturn = a:str
1409 let toReturn = getcwd() . s:os_slash . a:str
1414 "FUNCTION: Path.bookmarkNames() {{{3
1415 function! s:Path.bookmarkNames()
1416 if !exists("self._bookmarkNames")
1417 call self.cacheDisplayString()
1419 return self._bookmarkNames
1421 "FUNCTION: Path.cacheDisplayString() {{{3
1422 function! s:Path.cacheDisplayString()
1423 let self.cachedDisplayString = self.getLastPathComponent(1)
1425 if self.isExecutable
1426 let self.cachedDisplayString = self.cachedDisplayString . '*'
1429 let self._bookmarkNames = []
1430 for i in s:Bookmark.Bookmarks()
1431 if i.path.equals(self)
1432 call add(self._bookmarkNames, i.name)
1435 if !empty(self._bookmarkNames)
1436 let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
1440 let self.cachedDisplayString .= ' -> ' . self.symLinkDest
1444 let self.cachedDisplayString .= ' [RO]'
1447 "FUNCTION: Path.changeToDir() {{{3
1448 function! s:Path.changeToDir()
1449 let dir = self.strForCd()
1450 if self.isDirectory ==# 0
1451 let dir = self.getPathTrunk().strForCd()
1456 call s:echo("CWD is now: " . getcwd())
1458 throw "NERDTree.PathChangeError: cannot change CWD to " . dir
1462 "FUNCTION: Path.compareTo() {{{3
1464 "Compares this Path to the given path and returns 0 if they are equal, -1 if
1465 "this Path is "less than" the given path, or 1 if it is "greater".
1468 "path: the path object to compare this to
1472 function! s:Path.compareTo(path)
1473 let thisPath = self.getLastPathComponent(1)
1474 let thatPath = a:path.getLastPathComponent(1)
1476 "if the paths are the same then clearly we return 0
1477 if thisPath ==# thatPath
1481 let thisSS = self.getSortOrderIndex()
1482 let thatSS = a:path.getSortOrderIndex()
1484 "compare the sort sequences, if they are different then the return
1488 elseif thisSS > thatSS
1491 "if the sort sequences are the same then compare the paths
1493 let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath
1502 "FUNCTION: Path.Create(fullpath) {{{3
1506 "Creates a path object with the given path. The path is also created on the
1507 "filesystem. If the path already exists, a NERDTree.Path.Exists exception is
1508 "thrown. If any other errors occur, a NERDTree.Path exception is thrown.
1511 "fullpath: the full filesystem path to the file/dir to create
1512 function! s:Path.Create(fullpath)
1513 "bail if the a:fullpath already exists
1514 if isdirectory(a:fullpath) || filereadable(a:fullpath)
1515 throw "NERDTree.CreatePathError: Directory Exists: '" . a:fullpath . "'"
1520 "if it ends with a slash, assume its a dir create it
1521 if a:fullpath =~ '\(\\\|\/\)$'
1522 "whack the trailing slash off the end if it exists
1523 let fullpath = substitute(a:fullpath, '\(\\\|\/\)$', '', '')
1525 call mkdir(fullpath, 'p')
1527 "assume its a file and create
1529 call writefile([], a:fullpath)
1532 throw "NERDTree.CreatePathError: Could not create path: '" . a:fullpath . "'"
1535 return s:Path.New(a:fullpath)
1538 "FUNCTION: Path.copy(dest) {{{3
1540 "Copies the file/dir represented by this Path to the given location
1543 "dest: the location to copy this dir/file to
1544 function! s:Path.copy(dest)
1545 if !s:Path.CopyingSupported()
1546 throw "NERDTree.CopyingNotSupportedError: Copying is not supported on this OS"
1549 let dest = s:Path.WinToUnixPath(a:dest)
1551 let cmd = g:NERDTreeCopyCmd . " " . self.strForOS(0) . " " . dest
1552 let success = system(cmd)
1554 throw "NERDTree.CopyError: Could not copy ''". self.strForOS(0) ."'' to: '" . a:dest . "'"
1558 "FUNCTION: Path.CopyingSupported() {{{3
1560 "returns 1 if copying is supported for this OS
1561 function! s:Path.CopyingSupported()
1562 return exists('g:NERDTreeCopyCmd')
1566 "FUNCTION: Path.copyingWillOverwrite(dest) {{{3
1568 "returns 1 if copy this path to the given location will cause files to
1572 "dest: the location this path will be copied to
1573 function! s:Path.copyingWillOverwrite(dest)
1574 if filereadable(a:dest)
1578 if isdirectory(a:dest)
1579 let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
1580 if filereadable(path)
1586 "FUNCTION: Path.delete() {{{3
1588 "Deletes the file represented by this path.
1589 "Deletion of directories is not supported
1591 "Throws NERDTree.Path.Deletion exceptions
1592 function! s:Path.delete()
1596 if s:running_windows
1597 "if we are runnnig windows then put quotes around the pathstring
1598 let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
1600 let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
1602 let success = system(cmd)
1604 if v:shell_error != 0
1605 throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.strForOS(0) . "'"
1608 let success = delete(self.strForOS(0))
1610 throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str(0) . "'"
1614 "delete all bookmarks for this path
1615 for i in self.bookmarkNames()
1616 let bookmark = s:Bookmark.BookmarkFor(i)
1617 call bookmark.delete()
1621 "FUNCTION: Path.extractDriveLetter(fullpath) {{{3
1623 "If running windows, cache the drive letter for this path
1624 function! s:Path.extractDriveLetter(fullpath)
1625 if s:running_windows
1626 let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
1632 "FUNCTION: Path.exists() {{{3
1633 "return 1 if this path points to a location that is readable or is a directory
1634 function! s:Path.exists()
1635 return filereadable(self.strForOS(0)) || isdirectory(self.strForOS(0))
1637 "FUNCTION: Path.getDir() {{{3
1639 "Returns this path if it is a directory, else this paths parent.
1643 function! s:Path.getDir()
1647 return self.getParent()
1650 "FUNCTION: Path.getParent() {{{3
1652 "Returns a new path object for this paths parent
1656 function! s:Path.getParent()
1657 let path = '/'. join(self.pathSegments[0:-2], '/')
1658 return s:Path.New(path)
1660 "FUNCTION: Path.getLastPathComponent(dirSlash) {{{3
1662 "Gets the last part of this path.
1665 "dirSlash: if 1 then a trailing slash will be added to the returned value for
1667 function! s:Path.getLastPathComponent(dirSlash)
1668 if empty(self.pathSegments)
1671 let toReturn = self.pathSegments[-1]
1672 if a:dirSlash && self.isDirectory
1673 let toReturn = toReturn . '/'
1678 "FUNCTION: Path.getPathTrunk() {{{3
1679 "Gets the path without the last segment on the end.
1680 function! s:Path.getPathTrunk()
1681 return s:Path.New(self.strTrunk())
1684 "FUNCTION: Path.getSortOrderIndex() {{{3
1685 "returns the index of the pattern in g:NERDTreeSortOrder that this path matches
1686 function! s:Path.getSortOrderIndex()
1688 while i < len(g:NERDTreeSortOrder)
1689 if self.getLastPathComponent(1) =~ g:NERDTreeSortOrder[i]
1694 return s:NERDTreeSortStarIndex
1697 "FUNCTION: Path.ignore() {{{3
1698 "returns true if this path should be ignored
1699 function! s:Path.ignore()
1700 let lastPathComponent = self.getLastPathComponent(0)
1702 "filter out the user specified paths to ignore
1703 if b:NERDTreeIgnoreEnabled
1704 for i in g:NERDTreeIgnore
1705 if lastPathComponent =~ i
1711 "dont show hidden files unless instructed to
1712 if b:NERDTreeShowHidden ==# 0 && lastPathComponent =~ '^\.'
1716 if b:NERDTreeShowFiles ==# 0 && self.isDirectory ==# 0
1723 "FUNCTION: Path.JoinPathStrings(...) {{{3
1724 function! s:Path.JoinPathStrings(...)
1727 let components = extend(components, split(i, '/'))
1729 return '/' . join(components, '/')
1732 "FUNCTION: Path.equals() {{{3
1734 "Determines whether 2 path objects are "equal".
1735 "They are equal if the paths they represent are the same
1738 "path: the other path obj to compare this with
1739 function! s:Path.equals(path)
1740 return self.str(0) ==# a:path.str(0)
1743 "FUNCTION: Path.New() {{{3
1744 "The Constructor for the Path object
1745 function! s:Path.New(path)
1746 let newPath = copy(self)
1748 call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path))
1750 let newPath.cachedDisplayString = ""
1755 "FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
1758 "Throws NERDTree.Path.InvalidArguments exception.
1759 function! s:Path.readInfoFromDisk(fullpath)
1760 call self.extractDriveLetter(a:fullpath)
1762 let fullpath = s:Path.WinToUnixPath(a:fullpath)
1764 if getftype(fullpath) ==# "fifo"
1765 throw "NERDTree.InvalidFiletypeError: Cant handle FIFO files: " . a:fullpath
1768 let self.pathSegments = split(fullpath, '/')
1770 let self.isReadOnly = 0
1771 if isdirectory(a:fullpath)
1772 let self.isDirectory = 1
1773 elseif filereadable(a:fullpath)
1774 let self.isDirectory = 0
1775 let self.isReadOnly = filewritable(a:fullpath) ==# 0
1777 throw "NERDTree.InvalidArgumentsError: Invalid path = " . a:fullpath
1780 let self.isExecutable = 0
1781 if !self.isDirectory
1782 let self.isExecutable = getfperm(a:fullpath) =~ 'x'
1785 "grab the last part of the path (minus the trailing slash)
1786 let lastPathComponent = self.getLastPathComponent(0)
1788 "get the path to the new node with the parent dir fully resolved
1789 let hardPath = resolve(self.strTrunk()) . '/' . lastPathComponent
1791 "if the last part of the path is a symlink then flag it as such
1792 let self.isSymLink = (resolve(hardPath) != hardPath)
1794 let self.symLinkDest = resolve(fullpath)
1796 "if the link is a dir then slap a / on the end of its dest
1797 if isdirectory(self.symLinkDest)
1799 "we always wanna treat MS windows shortcuts as files for
1801 if hardPath !~ '\.lnk$'
1803 let self.symLinkDest = self.symLinkDest . '/'
1809 "FUNCTION: Path.refresh() {{{3
1810 function! s:Path.refresh()
1811 call self.readInfoFromDisk(self.strForOS(0))
1812 call self.cacheDisplayString()
1815 "FUNCTION: Path.rename() {{{3
1817 "Renames this node on the filesystem
1818 function! s:Path.rename(newPath)
1820 throw "NERDTree.InvalidArgumentsError: Invalid newPath for renaming = ". a:newPath
1823 let success = rename(self.strForOS(0), a:newPath)
1825 throw "NERDTree.PathRenameError: Could not rename: '" . self.strForOS(0) . "'" . 'to:' . a:newPath
1827 call self.readInfoFromDisk(a:newPath)
1829 for i in self.bookmarkNames()
1830 let b = s:Bookmark.BookmarkFor(i)
1831 call b.setPath(copy(self))
1833 call s:Bookmark.Write()
1836 "FUNCTION: Path.str(esc) {{{3
1838 "Gets the actual string path that this obj represents.
1841 "esc: if 1 then all the tricky chars in the returned string will be escaped
1842 function! s:Path.str(esc)
1843 let toReturn = '/' . join(self.pathSegments, '/')
1844 if self.isDirectory && toReturn != '/'
1845 let toReturn = toReturn . '/'
1849 let toReturn = escape(toReturn, s:escape_chars)
1854 "FUNCTION: Path.strAbs() {{{3
1856 "Returns a string representing this path with all the symlinks resolved
1860 function! s:Path.strAbs()
1861 return resolve(self.str(1))
1864 "FUNCTION: Path.strForCd() {{{3
1866 " returns a string that can be used with :cd
1869 "a string that can be used in the view to represent this path
1870 function! s:Path.strForCd()
1871 if s:running_windows
1872 return self.strForOS(0)
1874 return self.strForOS(1)
1877 "FUNCTION: Path.strDisplay() {{{3
1879 "Returns a string that specifies how the path should be represented as a
1883 "a string that can be used in the view to represent this path
1884 function! s:Path.strDisplay()
1885 if self.cachedDisplayString ==# ""
1886 call self.cacheDisplayString()
1889 return self.cachedDisplayString
1892 "FUNCTION: Path.strForEditCmd() {{{3
1894 "Return: the string for this path that is suitable to be used with the :edit
1896 function! s:Path.strForEditCmd()
1900 if s:running_windows
1901 let p = tolower(self.strForOS(0))
1902 let cwd = tolower(getcwd())
1905 let cwd = cwd . s:os_slash
1907 "return a relative path if we can
1908 if stridx(p, cwd) ==# 0
1909 let p = strpart(p, strlen(cwd))
1919 "FUNCTION: Path.strForGlob() {{{3
1920 function! s:Path.strForGlob()
1921 let lead = s:os_slash
1923 "if we are running windows then slap a drive letter on the front
1924 if s:running_windows
1925 let lead = self.drive . '\'
1928 let toReturn = lead . join(self.pathSegments, s:os_slash)
1930 if !s:running_windows
1931 let toReturn = escape(toReturn, s:escape_chars)
1935 "FUNCTION: Path.strForOS(esc) {{{3
1937 "Gets the string path for this path object that is appropriate for the OS.
1938 "EG, in windows c:\foo\bar
1942 "esc: if 1 then all the tricky chars in the returned string will be
1943 " escaped. If we are running windows then the str is double quoted instead.
1944 function! s:Path.strForOS(esc)
1945 let lead = s:os_slash
1947 "if we are running windows then slap a drive letter on the front
1948 if s:running_windows
1949 let lead = self.drive . '\'
1952 let toReturn = lead . join(self.pathSegments, s:os_slash)
1955 if s:running_windows
1956 let toReturn = '"' . toReturn . '"'
1958 let toReturn = escape(toReturn, s:escape_chars)
1964 "FUNCTION: Path.strTrunk() {{{3
1965 "Gets the path without the last segment on the end.
1966 function! s:Path.strTrunk()
1967 return self.drive . '/' . join(self.pathSegments[0:-2], '/')
1970 "FUNCTION: Path.WinToUnixPath(pathstr){{{3
1971 "Takes in a windows path and returns the unix equiv
1973 "A class level method
1976 "pathstr: the windows path to convert
1977 function! s:Path.WinToUnixPath(pathstr)
1978 if !s:running_windows
1982 let toReturn = a:pathstr
1984 "remove the x:\ of the front
1985 let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "")
1987 "convert all \ chars to /
1988 let toReturn = substitute(toReturn, '\', '/', "g")
1993 " SECTION: General Functions {{{1
1994 "============================================================
1995 "FUNCTION: s:bufInWindows(bnum){{{2
1996 "[[STOLEN FROM VTREEEXPLORER.VIM]]
1997 "Determine the number of windows open to this buffer number.
1998 "Care of Yegappan Lakshman. Thanks!
2001 "bnum: the subject buffers buffer number
2002 function! s:bufInWindows(bnum)
2006 let bufnum = winbufnr(winnum)
2010 if bufnum ==# a:bnum
2013 let winnum = winnum + 1
2018 "FUNCTION: s:checkForBrowse(dir) {{{2
2019 "inits a secondary nerd tree in the current buffer if appropriate
2020 function! s:checkForBrowse(dir)
2021 if a:dir != '' && isdirectory(a:dir)
2022 call s:initNerdTreeInPlace(a:dir)
2025 "FUNCTION: s:compareBookmarks(first, second) {{{2
2026 "Compares two bookmarks
2027 function! s:compareBookmarks(first, second)
2028 return a:first.compareTo(a:second)
2031 " FUNCTION: s:completeBookmarks(A,L,P) {{{2
2032 " completion function for the bookmark commands
2033 function! s:completeBookmarks(A,L,P)
2034 return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"')
2036 " FUNCTION: s:exec(cmd) {{{2
2037 " same as :exec cmd but eventignore=all is set for the duration
2038 function! s:exec(cmd)
2044 "FUNCTION: s:initNerdTree(name) {{{2
2045 "Initialise the nerd tree for this tab. The tree will start in either the
2046 "given directory, or the directory associated with the given bookmark
2049 "name: the name of a bookmark or a directory
2050 function! s:initNerdTree(name)
2052 if s:Bookmark.BookmarkExistsFor(a:name)
2053 let path = s:Bookmark.BookmarkFor(a:name).path
2055 let dir = a:name ==# '' ? getcwd() : a:name
2057 "hack to get an absolute path if a relative path is given
2059 let dir = getcwd() . s:os_slash . dir
2061 let dir = resolve(dir)
2064 let path = s:Path.New(dir)
2065 catch /^NERDTree.InvalidArgumentsError/
2066 call s:echo("No bookmark or directory found for: " . a:name)
2070 if !path.isDirectory
2071 let path = path.getParent()
2074 "if instructed to, then change the vim CWD to the dir the NERDTree is
2076 if g:NERDTreeChDirMode != 0
2077 exec 'cd ' . path.strForCd()
2080 if s:treeExistsForTab()
2084 unlet t:NERDTreeBufName
2087 let newRoot = s:TreeDirNode.New(path)
2090 call s:createTreeWin()
2091 let b:treeShowHelp = 0
2092 let b:NERDTreeIgnoreEnabled = 1
2093 let b:NERDTreeShowFiles = g:NERDTreeShowFiles
2094 let b:NERDTreeShowHidden = g:NERDTreeShowHidden
2095 let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
2096 let b:NERDTreeRoot = newRoot
2098 let b:NERDTreeType = "primary"
2101 call b:NERDTreeRoot.putCursorHere(0, 0)
2104 "FUNCTION: s:initNerdTreeInPlace(dir) {{{2
2105 function! s:initNerdTreeInPlace(dir)
2107 let path = s:Path.New(a:dir)
2108 catch /^NERDTree.InvalidArgumentsError/
2109 call s:echo("Invalid directory name:" . a:name)
2113 "we want the directory buffer to disappear when we do the :edit below
2114 setlocal bufhidden=wipe
2116 let previousBuf = expand("#")
2118 "we need a unique name for each secondary tree buffer to ensure they are
2120 exec "silent edit " . s:nextBufferName()
2122 let b:NERDTreePreviousBuf = bufnr(previousBuf)
2124 let b:NERDTreeRoot = s:TreeDirNode.New(path)
2125 call b:NERDTreeRoot.open()
2127 "throwaway buffer options
2129 setlocal buftype=nofile
2130 setlocal bufhidden=hide
2132 setlocal foldcolumn=0
2133 setlocal nobuflisted
2135 if g:NERDTreeShowLineNumbers
2143 if g:NERDTreeHighlightCursorline
2147 call s:setupStatusline()
2149 let b:treeShowHelp = 0
2150 let b:NERDTreeIgnoreEnabled = 1
2151 let b:NERDTreeShowFiles = g:NERDTreeShowFiles
2152 let b:NERDTreeShowHidden = g:NERDTreeShowHidden
2153 let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
2155 let b:NERDTreeType = "secondary"
2157 call s:bindMappings()
2158 setfiletype nerdtree
2159 " syntax highlighting
2160 if has("syntax") && exists("g:syntax_on") && !has("syntax_items")
2161 call s:setupSyntaxHighlighting()
2166 " FUNCTION: s:initNerdTreeMirror() {{{2
2167 function! s:initNerdTreeMirror()
2169 "get the names off all the nerd tree buffers
2170 let treeBufNames = []
2171 for i in range(1, tabpagenr("$"))
2172 let nextName = s:tabpagevar(i, 'NERDTreeBufName')
2173 if nextName != -1 && (!exists("t:NERDTreeBufName") || nextName != t:NERDTreeBufName)
2174 call add(treeBufNames, nextName)
2177 let treeBufNames = s:unique(treeBufNames)
2179 "map the option names (that the user will be prompted with) to the nerd
2183 while i < len(treeBufNames)
2184 let bufName = treeBufNames[i]
2185 let treeRoot = getbufvar(bufName, "NERDTreeRoot")
2186 let options[i+1 . '. ' . treeRoot.path.strForOS(0) . ' (buf name: ' . bufName . ')'] = bufName
2190 "work out which tree to mirror, if there is more than 1 then ask the user
2192 if len(keys(options)) > 1
2193 let choices = ["Choose a tree to mirror"]
2194 let choices = extend(choices, sort(keys(options)))
2195 let choice = inputlist(choices)
2196 if choice < 1 || choice > len(options) || choice ==# ''
2200 let bufferName = options[keys(options)[choice-1]]
2201 elseif len(keys(options)) ==# 1
2202 let bufferName = values(options)[0]
2204 call s:echo("No trees to mirror")
2208 if s:treeExistsForTab() && s:isTreeOpen()
2212 let t:NERDTreeBufName = bufferName
2213 call s:createTreeWin()
2214 exec 'buffer ' . bufferName
2219 " FUNCTION: s:nextBufferName() {{{2
2220 " returns the buffer name for the next nerd tree
2221 function! s:nextBufferName()
2222 let name = s:NERDTreeBufName . s:next_buffer_number
2223 let s:next_buffer_number += 1
2226 " FUNCTION: s:tabpagevar(tabnr, var) {{{2
2227 function! s:tabpagevar(tabnr, var)
2228 let currentTab = tabpagenr()
2232 exec "tabnext " . a:tabnr
2234 if exists('t:' . a:var)
2235 exec 'let v = t:' . a:var
2237 exec "tabnext " . currentTab
2243 " Function: s:treeExistsForBuffer() {{{2
2244 " Returns 1 if a nerd tree root exists in the current buffer
2245 function! s:treeExistsForBuf()
2246 return exists("b:NERDTreeRoot")
2248 " Function: s:treeExistsForTab() {{{2
2249 " Returns 1 if a nerd tree root exists in the current tab
2250 function! s:treeExistsForTab()
2251 return exists("t:NERDTreeBufName")
2253 " Function: s:unique(list) {{{2
2254 " returns a:list without duplicates
2255 function! s:unique(list)
2258 if index(uniqlist, elem) ==# -1
2259 let uniqlist += [elem]
2264 " SECTION: Public Functions {{{1
2265 "============================================================
2266 "Returns the node that the cursor is currently on.
2268 "If the cursor is not in the NERDTree window, it is temporarily put there.
2270 "If no NERD tree window exists for the current tab, a NERDTree.NoTreeForTab
2271 "exception is thrown.
2273 "If the cursor is not on a node then an empty dictionary {} is returned.
2274 function! NERDTreeGetCurrentNode()
2275 if !s:treeExistsForTab() || !s:isTreeOpen()
2276 throw "NERDTree.NoTreeForTabError: there is no NERD tree open for the current tab"
2280 if winnr != s:getTreeWinNum()
2281 call s:putCursorInTreeWin()
2284 let treenode = s:TreeFileNode.GetSelected()
2287 call s:exec('wincmd w')
2293 "Returns the path object for the current node.
2295 "Subject to the same conditions as NERDTreeGetCurrentNode
2296 function! NERDTreeGetCurrentPath()
2297 let node = NERDTreeGetCurrentNode()
2305 " SECTION: View Functions {{{1
2306 "============================================================
2307 "FUNCTION: s:centerView() {{{2
2308 "centers the nerd tree window around the cursor (provided the nerd tree
2310 function! s:centerView()
2311 if g:NERDTreeAutoCenter
2312 let current_line = winline()
2313 let lines_to_top = current_line
2314 let lines_to_bottom = winheight(s:getTreeWinNum()) - current_line
2315 if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold
2320 "FUNCTION: s:closeTree() {{{2
2321 "Closes the NERD tree window
2322 function! s:closeTree()
2324 throw "NERDTree.NoTreeFoundError: no NERDTree is open"
2328 call s:exec(s:getTreeWinNum() . " wincmd w")
2330 call s:exec("wincmd p")
2336 "FUNCTION: s:closeTreeIfOpen() {{{2
2337 "Closes the NERD tree window if it is open
2338 function! s:closeTreeIfOpen()
2343 "FUNCTION: s:closeTreeIfQuitOnOpen() {{{2
2344 "Closes the NERD tree window if the close on open option is set
2345 function! s:closeTreeIfQuitOnOpen()
2346 if g:NERDTreeQuitOnOpen
2350 "FUNCTION: s:createTreeWin() {{{2
2351 "Inits the NERD tree window. ie. opens it, sizes it, sets all the local
2353 function! s:createTreeWin()
2354 "create the nerd tree window
2355 let splitLocation = g:NERDTreeWinPos ==# "left" ? "topleft " : "botright "
2356 let splitSize = g:NERDTreeWinSize
2358 if !exists('t:NERDTreeBufName')
2359 let t:NERDTreeBufName = s:nextBufferName()
2360 silent! exec splitLocation . 'vertical ' . splitSize . ' new'
2361 silent! exec "edit " . t:NERDTreeBufName
2363 silent! exec splitLocation . 'vertical ' . splitSize . ' split'
2364 silent! exec "buffer " . t:NERDTreeBufName
2367 setlocal winfixwidth
2369 "throwaway buffer options
2371 setlocal buftype=nofile
2373 setlocal foldcolumn=0
2374 setlocal nobuflisted
2376 if g:NERDTreeShowLineNumbers
2384 if g:NERDTreeHighlightCursorline
2388 call s:setupStatusline()
2390 call s:bindMappings()
2391 setfiletype nerdtree
2392 " syntax highlighting
2393 if has("syntax") && exists("g:syntax_on") && !has("syntax_items")
2394 call s:setupSyntaxHighlighting()
2398 "FUNCTION: s:dumpHelp {{{2
2399 "prints out the quick help
2400 function! s:dumpHelp()
2402 if b:treeShowHelp ==# 1
2403 let @h= "\" NERD tree (" . s:NERD_tree_version . ") quickhelp~\n"
2404 let @h=@h."\" ============================\n"
2405 let @h=@h."\" File node mappings~\n"
2406 let @h=@h."\" ". (g:NERDTreeMouseMode ==# 3 ? "single" : "double") ."-click,\n"
2407 let @h=@h."\" <CR>,\n"
2408 if b:NERDTreeType ==# "primary"
2409 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n"
2411 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in current window\n"
2413 if b:NERDTreeType ==# "primary"
2414 let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n"
2416 let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
2417 let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
2418 let @h=@h."\" middle-click,\n"
2419 let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n"
2420 let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n"
2421 let @h=@h."\" ". g:NERDTreeMapOpenVSplit .": open vsplit\n"
2422 let @h=@h."\" ". g:NERDTreeMapPreviewVSplit .": preview vsplit\n"
2423 let @h=@h."\" ". g:NERDTreeMapExecute.": Execute file\n"
2425 let @h=@h."\"\n\" ----------------------------\n"
2426 let @h=@h."\" Directory node mappings~\n"
2427 let @h=@h."\" ". (g:NERDTreeMouseMode ==# 1 ? "double" : "single") ."-click,\n"
2428 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open & close node\n"
2429 let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n"
2430 let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n"
2431 let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n"
2432 let @h=@h."\" current node recursively\n"
2433 let @h=@h."\" middle-click,\n"
2434 let @h=@h."\" ". g:NERDTreeMapOpenExpl.": explore selected dir\n"
2436 let @h=@h."\"\n\" ----------------------------\n"
2437 let @h=@h."\" Bookmark table mappings~\n"
2438 let @h=@h."\" double-click,\n"
2439 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open bookmark\n"
2440 let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
2441 let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
2442 let @h=@h."\" ". g:NERDTreeMapDeleteBookmark .": delete bookmark\n"
2444 let @h=@h."\"\n\" ----------------------------\n"
2445 let @h=@h."\" Tree navigation mappings~\n"
2446 let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n"
2447 let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n"
2448 let @h=@h."\" ". g:NERDTreeMapJumpFirstChild .": go to first child\n"
2449 let @h=@h."\" ". g:NERDTreeMapJumpLastChild .": go to last child\n"
2450 let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n"
2451 let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n"
2453 let @h=@h."\"\n\" ----------------------------\n"
2454 let @h=@h."\" Filesystem mappings~\n"
2455 let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n"
2456 let @h=@h."\" selected dir\n"
2457 let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n"
2458 let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n"
2459 let @h=@h."\" but leave old root open\n"
2460 let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n"
2461 let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n"
2462 let @h=@h."\" ". g:NERDTreeMapFilesystemMenu .": Show filesystem menu\n"
2463 let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n"
2464 let @h=@h."\" selected dir\n"
2466 let @h=@h."\"\n\" ----------------------------\n"
2467 let @h=@h."\" Tree filtering mappings~\n"
2468 let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (b:NERDTreeShowHidden ? "on" : "off") . ")\n"
2469 let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (b:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n"
2470 let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (b:NERDTreeShowFiles ? "on" : "off") . ")\n"
2471 let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (b:NERDTreeShowBookmarks ? "on" : "off") . ")\n"
2473 let @h=@h."\"\n\" ----------------------------\n"
2474 let @h=@h."\" Other mappings~\n"
2475 let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n"
2476 let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n"
2477 let @h=@h."\"\n\" ----------------------------\n"
2478 let @h=@h."\" Bookmark commands~\n"
2479 let @h=@h."\" :Bookmark <name>\n"
2480 let @h=@h."\" :BookmarkToRoot <name>\n"
2481 let @h=@h."\" :RevealBookmark <name>\n"
2482 let @h=@h."\" :OpenBookmark <name>\n"
2483 let @h=@h."\" :ClearBookmarks [<names>]\n"
2484 let @h=@h."\" :ClearAllBookmarks\n"
2486 let @h="\" Press ". g:NERDTreeMapHelp ." for help\n"
2493 "FUNCTION: s:echo {{{2
2494 "A wrapper for :echo. Appends 'NERDTree:' on the front of all messages
2497 "msg: the message to echo
2498 function! s:echo(msg)
2500 echomsg "NERDTree: " . a:msg
2502 "FUNCTION: s:echoWarning {{{2
2503 "Wrapper for s:echo, sets the message type to warningmsg for this message
2505 "msg: the message to echo
2506 function! s:echoWarning(msg)
2511 "FUNCTION: s:echoError {{{2
2512 "Wrapper for s:echo, sets the message type to errormsg for this message
2514 "msg: the message to echo
2515 function! s:echoError(msg)
2520 "FUNCTION: s:firstUsableWindow(){{{2
2521 "find the window number of the first normal window
2522 function! s:firstUsableWindow()
2524 while i <= winnr("$")
2525 let bnum = winbufnr(i)
2526 if bnum != -1 && getbufvar(bnum, '&buftype') ==# ''
2527 \ && !getwinvar(i, '&previewwindow')
2528 \ && (!getbufvar(bnum, '&modified') || &hidden)
2536 "FUNCTION: s:getPath(ln) {{{2
2537 "Gets the full path to the node that is rendered on the given line number
2540 "ln: the line number to get the path for
2543 "A path if a node was selected, {} if nothing is selected.
2544 "If the 'up a dir' line was selected then the path to the parent of the
2545 "current root is returned
2546 function! s:getPath(ln)
2547 let line = getline(a:ln)
2549 "check to see if we have the root node
2551 return b:NERDTreeRoot.path
2554 " in case called from outside the tree
2555 if line !~ '^ *[|`]' || line =~ '^$'
2559 if line ==# s:tree_up_dir_line
2560 return b:NERDTreeRoot.path.getParent()
2563 let indent = s:indentLevelFor(line)
2565 "remove the tree parts and the leading space
2566 let curFile = s:stripMarkupFromLine(line, 0)
2571 let curFile = substitute(curFile, '/\?$', '/', "")
2579 let curLine = getline(lnum)
2580 let curLineStripped = s:stripMarkupFromLine(curLine, 1)
2582 "have we reached the top of the tree?
2584 let dir = substitute (curLine, ' *$', "", "") . dir
2587 if curLineStripped =~ '/$'
2588 let lpindent = s:indentLevelFor(curLine)
2589 if lpindent < indent
2590 let indent = indent - 1
2592 let dir = substitute (curLineStripped,'^\\', "", "") . dir
2597 let curFile = b:NERDTreeRoot.path.drive . dir . curFile
2598 let toReturn = s:Path.New(curFile)
2602 "FUNCTION: s:getSelectedBookmark() {{{2
2603 "returns the bookmark the cursor is over in the bookmarks table or {}
2604 function! s:getSelectedBookmark()
2605 let line = getline(".")
2606 let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
2609 return s:Bookmark.BookmarkFor(name)
2610 catch /^NERDTree.BookmarkNotFoundError/
2617 "FUNCTION: s:getTreeWinNum() {{{2
2618 "gets the nerd tree window number for this tab
2619 function! s:getTreeWinNum()
2620 if exists("t:NERDTreeBufName")
2621 return bufwinnr(t:NERDTreeBufName)
2626 "FUNCTION: s:indentLevelFor(line) {{{2
2627 function! s:indentLevelFor(line)
2628 return match(a:line, '[^ \-+~`|]') / s:tree_wid
2630 "FUNCTION: s:isTreeOpen() {{{2
2631 function! s:isTreeOpen()
2632 return s:getTreeWinNum() != -1
2634 "FUNCTION: s:isWindowUsable(winnumber) {{{2
2635 "Returns 0 if opening a file from the tree in the given window requires it to
2636 "be split, 1 otherwise
2639 "winnumber: the number of the window in question
2640 function! s:isWindowUsable(winnumber)
2641 "gotta split if theres only one window (i.e. the NERD tree)
2646 let oldwinnr = winnr()
2647 call s:exec(a:winnumber . "wincmd p")
2648 let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow')
2649 let modified = &modified
2650 call s:exec(oldwinnr . "wincmd p")
2652 "if its a special window e.g. quickfix or another explorer plugin then we
2662 return !modified || s:bufInWindows(winbufnr(a:winnumber)) >= 2
2665 " FUNCTION: s:jumpToChild(direction) {{{2
2667 " direction: 0 if going to first child, 1 if going to last
2668 function! s:jumpToChild(direction)
2669 let currentNode = s:TreeFileNode.GetSelected()
2670 if currentNode ==# {} || currentNode.isRoot()
2671 call s:echo("cannot jump to " . (a:direction ? "last" : "first") . " child")
2674 let dirNode = currentNode.parent
2675 let childNodes = dirNode.getVisibleChildren()
2677 let targetNode = childNodes[0]
2679 let targetNode = childNodes[len(childNodes) - 1]
2682 if targetNode.equals(currentNode)
2683 let siblingDir = currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction)
2685 let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0
2686 let targetNode = siblingDir.getChildByIndex(indx, 1)
2690 call targetNode.putCursorHere(1, 0)
2696 "FUNCTION: s:promptToDelBuffer(bufnum, msg){{{2
2697 "prints out the given msg and, if the user responds by pushing 'y' then the
2698 "buffer with the given bufnum is deleted
2701 "bufnum: the buffer that may be deleted
2702 "msg: a message that will be echoed to the user asking them if they wish to
2704 function! s:promptToDelBuffer(bufnum, msg)
2706 if nr2char(getchar()) ==# 'y'
2707 exec "silent bdelete! " . a:bufnum
2711 "FUNCTION: s:putCursorOnBookmarkTable(){{{2
2712 "Places the cursor at the top of the bookmarks table
2713 function! s:putCursorOnBookmarkTable()
2714 if !b:NERDTreeShowBookmarks
2715 throw "NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active"
2718 let rootNodeLine = s:TreeFileNode.GetRootLineNum()
2721 while getline(line) !~ '^>-\+Bookmarks-\+$'
2723 if line >= rootNodeLine
2724 throw "NERDTree.BookmarkTableNotFoundError: didnt find the bookmarks table"
2727 call cursor(line, 0)
2730 "FUNCTION: s:putCursorInTreeWin(){{{2
2731 "Places the cursor in the nerd tree window
2732 function! s:putCursorInTreeWin()
2734 throw "NERDTree.InvalidOperationError: cant put cursor in NERD tree window, no window exists"
2737 call s:exec(s:getTreeWinNum() . "wincmd w")
2740 "FUNCTION: s:renderBookmarks {{{2
2741 function! s:renderBookmarks()
2743 call setline(line(".")+1, ">----------Bookmarks----------")
2744 call cursor(line(".")+1, col("."))
2746 for i in s:Bookmark.Bookmarks()
2747 call setline(line(".")+1, i.str())
2748 call cursor(line(".")+1, col("."))
2751 call setline(line(".")+1, '')
2752 call cursor(line(".")+1, col("."))
2754 "FUNCTION: s:renderView {{{2
2755 "The entry function for rendering the tree
2756 function! s:renderView()
2759 "remember the top line of the buffer and the current line so we can
2760 "restore the view exactly how it was
2761 let curLine = line(".")
2762 let curCol = col(".")
2763 let topLine = line("w0")
2765 "delete all lines in the buffer (being careful not to clobber a register)
2770 "delete the blank line before the help and add one after it
2771 call setline(line(".")+1, "")
2772 call cursor(line(".")+1, col("."))
2774 if b:NERDTreeShowBookmarks
2775 call s:renderBookmarks()
2778 "add the 'up a dir' line
2779 call setline(line(".")+1, s:tree_up_dir_line)
2780 call cursor(line(".")+1, col("."))
2782 "draw the header line
2783 call setline(line(".")+1, b:NERDTreeRoot.path.str(0))
2784 call cursor(line(".")+1, col("."))
2788 let @o = b:NERDTreeRoot.renderToString()
2792 "delete the blank line at the top of the buffer
2796 let old_scrolloff=&scrolloff
2798 call cursor(topLine, 1)
2800 call cursor(curLine, curCol)
2801 let &scrolloff = old_scrolloff
2803 setlocal nomodifiable
2806 "FUNCTION: s:renderViewSavingPosition {{{2
2807 "Renders the tree and ensures the cursor stays on the current node or the
2808 "current nodes parent if it is no longer available upon re-rendering
2809 function! s:renderViewSavingPosition()
2810 let currentNode = s:TreeFileNode.GetSelected()
2812 "go up the tree till we find a node that will be visible or till we run
2814 while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
2815 let currentNode = currentNode.parent
2820 if currentNode != {}
2821 call currentNode.putCursorHere(0, 0)
2824 "FUNCTION: s:restoreScreenState() {{{2
2826 "Sets the screen state back to what it was when s:saveScreenState was last
2829 "Assumes the cursor is in the NERDTree window
2830 function! s:restoreScreenState()
2831 if !exists("b:NERDTreeOldTopLine") || !exists("b:NERDTreeOldPos") || !exists("b:NERDTreeOldWindowSize")
2834 exec("silent vertical resize ".b:NERDTreeOldWindowSize)
2836 let old_scrolloff=&scrolloff
2838 call cursor(b:NERDTreeOldTopLine, 0)
2840 call setpos(".", b:NERDTreeOldPos)
2841 let &scrolloff=old_scrolloff
2844 "FUNCTION: s:saveScreenState() {{{2
2845 "Saves the current cursor position in the current buffer and the window
2847 function! s:saveScreenState()
2850 call s:putCursorInTreeWin()
2851 let b:NERDTreeOldPos = getpos(".")
2852 let b:NERDTreeOldTopLine = line("w0")
2853 let b:NERDTreeOldWindowSize = winwidth("")
2854 call s:exec(win . "wincmd w")
2855 catch /^NERDTree.InvalidOperationError/
2859 "FUNCTION: s:setupStatusline() {{{2
2860 function! s:setupStatusline()
2861 if g:NERDTreeStatusline != -1
2862 let &l:statusline = g:NERDTreeStatusline
2865 "FUNCTION: s:setupSyntaxHighlighting() {{{2
2866 function! s:setupSyntaxHighlighting()
2867 "treeFlags are syntax items that should be invisible, but give clues as to
2868 "how things should be highlighted
2869 syn match treeFlag #\~#
2870 syn match treeFlag #\[RO\]#
2872 "highlighting for the .. (up dir) line at the top of the tree
2873 execute "syn match treeUp #". s:tree_up_dir_line ."#"
2875 "highlighting for the ~/+ symbols for the directory nodes
2876 syn match treeClosable #\~\<#
2877 syn match treeClosable #\~\.#
2878 syn match treeOpenable #+\<#
2879 syn match treeOpenable #+\.#he=e-1
2881 "highlighting for the tree structural parts
2882 syn match treePart #|#
2883 syn match treePart #`#
2884 syn match treePartFile #[|`]-#hs=s+1 contains=treePart
2886 "quickhelp syntax elements
2887 syn match treeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1
2888 syn match treeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1
2889 syn match treeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=treeFlag
2890 syn match treeToggleOn #".*(on)#hs=e-2,he=e-1 contains=treeHelpKey
2891 syn match treeToggleOff #".*(off)#hs=e-3,he=e-1 contains=treeHelpKey
2892 syn match treeHelpCommand #" :.\{-}\>#hs=s+3
2893 syn match treeHelp #^".*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn,treeHelpCommand
2895 "highlighting for readonly files
2896 syn match treeRO #.*\[RO\]#hs=s+2 contains=treeFlag,treeBookmark,treePart,treePartFile
2898 "highlighting for sym links
2899 syn match treeLink #[^-| `].* -> # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash
2901 "highlighing for directory nodes and file nodes
2902 syn match treeDirSlash #/#
2903 syn match treeDir #[^-| `].*/# contains=treeLink,treeDirSlash,treeOpenable,treeClosable
2904 syn match treeExecFile #[|`]-.*\*\($\| \)# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark
2905 syn match treeFile #|-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
2906 syn match treeFile #`-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
2907 syn match treeCWD #^/.*$#
2909 "highlighting for bookmarks
2910 syn match treeBookmark # {.*}#hs=s+1
2912 "highlighting for the bookmarks table
2913 syn match treeBookmarksLeader #^>#
2914 syn match treeBookmarksHeader #^>-\+Bookmarks-\+$# contains=treeBookmarksLeader
2915 syn match treeBookmarkName #^>.\{-} #he=e-1 contains=treeBookmarksLeader
2916 syn match treeBookmark #^>.*$# contains=treeBookmarksLeader,treeBookmarkName,treeBookmarksHeader
2918 if g:NERDChristmasTree
2919 hi def link treePart Special
2920 hi def link treePartFile Type
2921 hi def link treeFile Normal
2922 hi def link treeExecFile Title
2923 hi def link treeDirSlash Identifier
2924 hi def link treeClosable Type
2926 hi def link treePart Normal
2927 hi def link treePartFile Normal
2928 hi def link treeFile Normal
2929 hi def link treeClosable Title
2932 hi def link treeBookmarksHeader statement
2933 hi def link treeBookmarksLeader ignore
2934 hi def link treeBookmarkName Identifier
2935 hi def link treeBookmark normal
2937 hi def link treeHelp String
2938 hi def link treeHelpKey Identifier
2939 hi def link treeHelpCommand Identifier
2940 hi def link treeHelpTitle Macro
2941 hi def link treeToggleOn Question
2942 hi def link treeToggleOff WarningMsg
2944 hi def link treeDir Directory
2945 hi def link treeUp Directory
2946 hi def link treeCWD Statement
2947 hi def link treeLink Macro
2948 hi def link treeOpenable Title
2949 hi def link treeFlag ignore
2950 hi def link treeRO WarningMsg
2951 hi def link treeBookmark Statement
2953 hi def link NERDTreeCurrentNode Search
2956 "FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2
2957 "returns the given line with all the tree parts stripped off
2960 "line: the subject line
2961 "removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces =
2962 "any spaces before the actual text of the node)
2963 function! s:stripMarkupFromLine(line, removeLeadingSpaces)
2965 "remove the tree parts and the leading space
2966 let line = substitute (line, s:tree_markup_reg,"","")
2968 "strip off any read only flag
2969 let line = substitute (line, ' \[RO\]', "","")
2971 "strip off any bookmark flags
2972 let line = substitute (line, ' {[^}]*}', "","")
2974 "strip off any executable flags
2975 let line = substitute (line, '*\ze\($\| \)', "","")
2981 let line = substitute (line,' -> .*',"","") " remove link to
2983 let line = substitute (line, '/\?$', '/', "")
2986 if a:removeLeadingSpaces
2987 let line = substitute (line, '^ *', '', '')
2993 "FUNCTION: s:toggle(dir) {{{2
2994 "Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is
2995 "closed it is restored or initialized (if it doesnt exist)
2998 "dir: the full path for the root node (is only used if the NERD tree is being
3000 function! s:toggle(dir)
3001 if s:treeExistsForTab()
3003 call s:createTreeWin()
3004 call s:restoreScreenState()
3012 call s:initNerdTree(a:dir)
3015 "SECTION: Interface bindings {{{1
3016 "============================================================
3017 "FUNCTION: s:activateNode(forceKeepWindowOpen) {{{2
3018 "If the current node is a file, open it in the previous window (or a new one
3019 "if the previous is modified). If it is a directory then it is opened.
3022 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3023 function! s:activateNode(forceKeepWindowOpen)
3024 if getline(".") ==# s:tree_up_dir_line
3028 let treenode = s:TreeFileNode.GetSelected()
3030 if treenode.path.isDirectory
3031 call treenode.toggleOpen()
3033 call treenode.putCursorHere(0, 0)
3035 call treenode.open()
3036 if !a:forceKeepWindowOpen
3037 call s:closeTreeIfQuitOnOpen()
3041 let bookmark = s:getSelectedBookmark()
3043 if bookmark.path.isDirectory
3044 call bookmark.toRoot()
3046 if bookmark.validate()
3047 let n = s:TreeFileNode.New(bookmark.path)
3055 "FUNCTION: s:bindMappings() {{{2
3056 function! s:bindMappings()
3057 " set up mappings and commands for this buffer
3058 nnoremap <silent> <buffer> <middlerelease> :call <SID>handleMiddleMouse()<cr>
3059 nnoremap <silent> <buffer> <leftrelease> <leftrelease>:call <SID>checkForActivate()<cr>
3060 nnoremap <silent> <buffer> <2-leftmouse> :call <SID>activateNode(0)<cr>
3062 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapActivateNode . " :call <SID>activateNode(0)<cr>"
3063 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenSplit ." :call <SID>openEntrySplit(0,0)<cr>"
3064 exec "nnoremap <silent> <buffer> <cr> :call <SID>activateNode(0)<cr>"
3066 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreview ." :call <SID>previewNode(0)<cr>"
3067 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewSplit ." :call <SID>previewNode(1)<cr>"
3069 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenVSplit ." :call <SID>openEntrySplit(1,0)<cr>"
3070 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewVSplit ." :call <SID>previewNode(2)<cr>"
3072 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapExecute ." :call <SID>executeNode()<cr>"
3074 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenRecursively ." :call <SID>openNodeRecursively()<cr>"
3076 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdirKeepOpen ." :call <SID>upDir(1)<cr>"
3077 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdir ." :call <SID>upDir(0)<cr>"
3078 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChangeRoot ." :call <SID>chRoot()<cr>"
3080 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChdir ." :call <SID>chCwd()<cr>"
3082 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapQuit ." :call <SID>closeTreeWindow()<cr>"
3084 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefreshRoot ." :call <SID>refreshRoot()<cr>"
3085 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefresh ." :call <SID>refreshCurrent()<cr>"
3087 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapHelp ." :call <SID>displayHelp()<cr>"
3088 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleHidden ." :call <SID>toggleShowHidden()<cr>"
3089 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFilters ." :call <SID>toggleIgnoreFilter()<cr>"
3090 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFiles ." :call <SID>toggleShowFiles()<cr>"
3091 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleBookmarks ." :call <SID>toggleShowBookmarks()<cr>"
3093 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>"
3094 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>"
3096 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapFilesystemMenu ." :call <SID>showFileSystemMenu()<cr>"
3098 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>jumpToParent()<cr>"
3099 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>jumpToSibling(1)<cr>"
3100 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpPrevSibling ." :call <SID>jumpToSibling(0)<cr>"
3101 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpFirstChild ." :call <SID>jumpToFirstChild()<cr>"
3102 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpLastChild ." :call <SID>jumpToLastChild()<cr>"
3103 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpRoot ." :call <SID>jumpToRoot()<cr>"
3105 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>"
3106 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTabSilent ." :call <SID>openInNewTab(1)<cr>"
3108 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenExpl ." :call <SID>openExplorer()<cr>"
3110 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>"
3112 command! -buffer -nargs=1 Bookmark :call <SID>bookmarkNode('<args>')
3113 command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call <SID>revealBookmark('<args>')
3114 command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call <SID>openBookmark('<args>')
3115 command! -buffer -complete=customlist,s:completeBookmarks -nargs=* ClearBookmarks call <SID>clearBookmarks('<args>')
3116 command! -buffer -complete=customlist,s:completeBookmarks -nargs=+ BookmarkToRoot call s:Bookmark.ToRoot('<args>')
3117 command! -buffer -nargs=0 ClearAllBookmarks call s:Bookmark.ClearAll() <bar> call <SID>renderView()
3118 command! -buffer -nargs=0 ReadBookmarks call s:Bookmark.CacheBookmarks(0) <bar> call <SID>renderView()
3119 command! -buffer -nargs=0 WriteBookmarks call s:Bookmark.Write()
3122 " FUNCTION: s:bookmarkNode(name) {{{2
3123 " Associate the current node with the given name
3124 function! s:bookmarkNode(name)
3125 let currentNode = s:TreeFileNode.GetSelected()
3126 if currentNode != {}
3128 call currentNode.bookmark(a:name)
3130 catch /^NERDTree.IllegalBookmarkNameError/
3131 call s:echo("bookmark names must not contain spaces")
3134 call s:echo("select a node first")
3137 "FUNCTION: s:checkForActivate() {{{2
3138 "Checks if the click should open the current node, if so then activate() is
3139 "called (directories are automatically opened if the symbol beside them is
3141 function! s:checkForActivate()
3142 let currentNode = s:TreeFileNode.GetSelected()
3143 if currentNode != {}
3144 let startToCur = strpart(getline(line(".")), 0, col("."))
3145 let char = strpart(startToCur, strlen(startToCur)-1, 1)
3147 "if they clicked a dir, check if they clicked on the + or ~ sign
3149 if currentNode.path.isDirectory
3150 if startToCur =~ s:tree_markup_reg . '$' && char =~ '[+~]'
3151 call s:activateNode(0)
3156 if (g:NERDTreeMouseMode ==# 2 && currentNode.path.isDirectory) || g:NERDTreeMouseMode ==# 3
3157 if char !~ s:tree_markup_reg && startToCur !~ '\/$'
3158 call s:activateNode(0)
3165 " FUNCTION: s:chCwd() {{{2
3167 let treenode = s:TreeFileNode.GetSelected()
3169 call s:echo("Select a node first")
3174 call treenode.path.changeToDir()
3175 catch /^NERDTree.PathChangeError/
3176 call s:echoWarning("could not change cwd")
3180 " FUNCTION: s:chRoot() {{{2
3181 " changes the current root to the selected one
3182 function! s:chRoot()
3183 let treenode = s:TreeFileNode.GetSelected()
3185 call s:echo("Select a node first")
3189 call treenode.makeRoot()
3191 call b:NERDTreeRoot.putCursorHere(0, 0)
3194 " FUNCTION: s:clearBookmarks(bookmarks) {{{2
3195 function! s:clearBookmarks(bookmarks)
3196 if a:bookmarks ==# ''
3197 let currentNode = s:TreeFileNode.GetSelected()
3198 if currentNode != {}
3199 call currentNode.clearBoomarks()
3202 for name in split(a:bookmarks, ' ')
3203 let bookmark = s:Bookmark.BookmarkFor(name)
3204 call bookmark.delete()
3209 " FUNCTION: s:closeChildren() {{{2
3210 " closes all childnodes of the current node
3211 function! s:closeChildren()
3212 let currentNode = s:TreeDirNode.GetSelected()
3213 if currentNode ==# {}
3214 call s:echo("Select a node first")
3218 call currentNode.closeChildren()
3220 call currentNode.putCursorHere(0, 0)
3222 " FUNCTION: s:closeCurrentDir() {{{2
3223 " closes the parent dir of the current node
3224 function! s:closeCurrentDir()
3225 let treenode = s:TreeFileNode.GetSelected()
3227 call s:echo("Select a node first")
3231 let parent = treenode.parent
3233 call s:echo("cannot close tree root")
3235 call treenode.parent.close()
3237 call treenode.parent.putCursorHere(0, 0)
3240 " FUNCTION: s:closeTreeWindow() {{{2
3241 " close the tree window
3242 function! s:closeTreeWindow()
3243 if b:NERDTreeType ==# "secondary" && b:NERDTreePreviousBuf != -1
3244 exec "buffer " . b:NERDTreePreviousBuf
3249 call s:echo("Cannot close last window")
3253 " FUNCTION: s:copyNode() {{{2
3254 function! s:copyNode()
3255 let currentNode = s:TreeFileNode.GetSelected()
3256 if currentNode ==# {}
3257 call s:echo("Put the cursor on a file node first")
3261 let newNodePath = input("Copy the current node\n" .
3262 \ "==========================================================\n" .
3263 \ "Enter the new path to copy the node to: \n" .
3264 \ "", currentNode.path.str(0))
3266 if newNodePath != ""
3267 "strip trailing slash
3268 let newNodePath = substitute(newNodePath, '\/$', '', '')
3271 if currentNode.path.copyingWillOverwrite(newNodePath)
3272 call s:echo("\nWarning: copying may overwrite files! Continue? (yN)")
3273 let choice = nr2char(getchar())
3274 let confirmed = choice ==# 'y'
3279 let newNode = currentNode.copy(newNodePath)
3281 call newNode.putCursorHere(0, 0)
3283 call s:echoWarning("Could not copy node")
3287 call s:echo("Copy aborted.")
3292 " FUNCTION: s:deleteBookmark() {{{2
3293 " if the cursor is on a bookmark, prompt to delete
3294 function! s:deleteBookmark()
3295 let bookmark = s:getSelectedBookmark()
3297 call s:echo("Put the cursor on a bookmark")
3301 echo "Are you sure you wish to delete the bookmark:\n\"" . bookmark.name . "\" (yN):"
3303 if nr2char(getchar()) ==# 'y'
3305 call bookmark.delete()
3309 call s:echoWarning("Could not remove bookmark")
3312 call s:echo("delete aborted" )
3317 " FUNCTION: s:deleteNode() {{{2
3318 " if the current node is a file, pops up a dialog giving the user the option
3320 function! s:deleteNode()
3321 let currentNode = s:TreeFileNode.GetSelected()
3322 if currentNode ==# {}
3323 call s:echo("Put the cursor on a file node first")
3329 if currentNode.path.isDirectory
3330 let choice =input("Delete the current node\n" .
3331 \ "==========================================================\n" .
3332 \ "STOP! To delete this entire directory, type 'yes'\n" .
3333 \ "" . currentNode.path.strForOS(0) . ": ")
3334 let confirmed = choice ==# 'yes'
3336 echo "Delete the current node\n" .
3337 \ "==========================================================\n".
3338 \ "Are you sure you wish to delete the node:\n" .
3339 \ "" . currentNode.path.strForOS(0) . " (yN):"
3340 let choice = nr2char(getchar())
3341 let confirmed = choice ==# 'y'
3347 call currentNode.delete()
3350 "if the node is open in a buffer, ask the user if they want to
3352 let bufnum = bufnr(currentNode.path.str(0))
3353 if buflisted(bufnum)
3354 let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
3355 call s:promptToDelBuffer(bufnum, prompt)
3360 call s:echoWarning("Could not remove node")
3363 call s:echo("delete aborted" )
3368 " FUNCTION: s:displayHelp() {{{2
3369 " toggles the help display
3370 function! s:displayHelp()
3371 let b:treeShowHelp = b:treeShowHelp ? 0 : 1
3376 " FUNCTION: s:executeNode() {{{2
3377 function! s:executeNode()
3378 let treenode = s:TreeFileNode.GetSelected()
3379 if treenode ==# {} || treenode.path.isDirectory
3380 call s:echo("Select an executable file node first" )
3382 echo "NERDTree executor\n" .
3383 \ "==========================================================\n".
3384 \ "Complete the command to execute (add arguments etc): \n\n"
3385 let cmd = treenode.path.strForOS(1)
3386 let cmd = input(':!', cmd . ' ')
3391 call s:echo("command aborted")
3396 " FUNCTION: s:handleMiddleMouse() {{{2
3397 function! s:handleMiddleMouse()
3398 let curNode = s:TreeFileNode.GetSelected()
3400 call s:echo("Put the cursor on a node first" )
3404 if curNode.path.isDirectory
3405 call s:openExplorer()
3407 call s:openEntrySplit(0,0)
3412 " FUNCTION: s:insertNewNode() {{{2
3413 " Adds a new node to the filesystem and then into the tree
3414 function! s:insertNewNode()
3415 let curDirNode = s:TreeDirNode.GetSelected()
3416 if curDirNode ==# {}
3417 call s:echo("Put the cursor on a node first" )
3421 let newNodeName = input("Add a childnode\n".
3422 \ "==========================================================\n".
3423 \ "Enter the dir/file name to be created. Dirs end with a '/'\n" .
3424 \ "", curDirNode.path.strForGlob() . s:os_slash)
3426 if newNodeName ==# ''
3427 call s:echo("Node Creation Aborted.")
3432 let newPath = s:Path.Create(newNodeName)
3433 let parentNode = b:NERDTreeRoot.findNode(newPath.getPathTrunk())
3435 let newTreeNode = s:TreeFileNode.New(newPath)
3436 if parentNode.isOpen || !empty(parentNode.children)
3437 call parentNode.addChild(newTreeNode, 1)
3439 call newTreeNode.putCursorHere(1, 0)
3442 call s:echoWarning("Node Not Created.")
3446 " FUNCTION: s:jumpToFirstChild() {{{2
3447 " wrapper for the jump to child method
3448 function! s:jumpToFirstChild()
3449 call s:jumpToChild(0)
3452 " FUNCTION: s:jumpToLastChild() {{{2
3453 " wrapper for the jump to child method
3454 function! s:jumpToLastChild()
3455 call s:jumpToChild(1)
3458 " FUNCTION: s:jumpToParent() {{{2
3459 " moves the cursor to the parent of the current node
3460 function! s:jumpToParent()
3461 let currentNode = s:TreeFileNode.GetSelected()
3462 if !empty(currentNode)
3463 if !empty(currentNode.parent)
3464 call currentNode.parent.putCursorHere(1, 0)
3467 call s:echo("cannot jump to parent")
3470 call s:echo("put the cursor on a node first")
3474 " FUNCTION: s:jumpToRoot() {{{2
3475 " moves the cursor to the root node
3476 function! s:jumpToRoot()
3477 call b:NERDTreeRoot.putCursorHere(1, 0)
3481 " FUNCTION: s:jumpToSibling() {{{2
3482 " moves the cursor to the sibling of the current node in the given direction
3485 " forward: 1 if the cursor should move to the next sibling, 0 if it should
3486 " move back to the previous sibling
3487 function! s:jumpToSibling(forward)
3488 let currentNode = s:TreeFileNode.GetSelected()
3489 if !empty(currentNode)
3490 let sibling = currentNode.findSibling(a:forward)
3493 call sibling.putCursorHere(1, 0)
3497 call s:echo("put the cursor on a node first")
3501 " FUNCTION: s:openBookmark(name) {{{2
3502 " put the cursor on the given bookmark and, if its a file, open it
3503 function! s:openBookmark(name)
3505 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3506 call targetNode.putCursorHere(0, 1)
3508 catch /^NERDTree.BookmarkedNodeNotFoundError/
3509 call s:echo("note - target node is not cached")
3510 let bookmark = s:Bookmark.BookmarkFor(a:name)
3511 let targetNode = s:TreeFileNode.New(bookmark.path)
3513 if targetNode.path.isDirectory
3514 call targetNode.openExplorer()
3516 call targetNode.open()
3519 " FUNCTION: s:openEntrySplit(vertical, forceKeepWindowOpen) {{{2
3520 "Opens the currently selected file from the explorer in a
3524 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3525 function! s:openEntrySplit(vertical, forceKeepWindowOpen)
3526 let treenode = s:TreeFileNode.GetSelected()
3529 call treenode.openVSplit()
3531 call treenode.openSplit()
3533 if !a:forceKeepWindowOpen
3534 call s:closeTreeIfQuitOnOpen()
3537 call s:echo("select a node first")
3541 " FUNCTION: s:openExplorer() {{{2
3542 function! s:openExplorer()
3543 let treenode = s:TreeDirNode.GetSelected()
3545 call treenode.openExplorer()
3547 call s:echo("select a node first")
3551 " FUNCTION: s:openInNewTab(stayCurrentTab) {{{2
3552 " Opens the selected node or bookmark in a new tab
3554 " stayCurrentTab: if 1 then vim will stay in the current tab, if 0 then vim
3555 " will go to the tab where the new file is opened
3556 function! s:openInNewTab(stayCurrentTab)
3557 let currentTab = tabpagenr()
3559 let treenode = s:TreeFileNode.GetSelected()
3561 if treenode.path.isDirectory
3563 call s:initNerdTree(treenode.path.strForOS(0))
3565 exec "tabedit " . treenode.path.strForEditCmd()
3568 let bookmark = s:getSelectedBookmark()
3570 if bookmark.path.isDirectory
3572 call s:initNerdTree(bookmark.name)
3574 exec "tabedit " . bookmark.path.strForEditCmd()
3579 exec "tabnext " . currentTab
3583 " FUNCTION: s:openNodeRecursively() {{{2
3584 function! s:openNodeRecursively()
3585 let treenode = s:TreeFileNode.GetSelected()
3586 if treenode ==# {} || treenode.path.isDirectory ==# 0
3587 call s:echo("Select a directory node first" )
3589 call s:echo("Recursively opening node. Please wait...")
3590 call treenode.openRecursively()
3593 call s:echo("Recursively opening node. Please wait... DONE")
3598 "FUNCTION: s:previewNode() {{{2
3600 " openNewWin: if 0, use the previous window, if 1 open in new split, if 2
3602 function! s:previewNode(openNewWin)
3603 let currentBuf = bufnr("")
3605 call s:openEntrySplit(a:openNewWin ==# 2,1)
3607 call s:activateNode(1)
3609 call s:exec(bufwinnr(currentBuf) . "wincmd w")
3612 " FUNCTION: s:revealBookmark(name) {{{2
3613 " put the cursor on the node associate with the given name
3614 function! s:revealBookmark(name)
3616 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3617 call targetNode.putCursorHere(0, 1)
3618 catch /^NERDTree.BookmarkNotFoundError/
3619 call s:echo("Bookmark isnt cached under the current root")
3622 " FUNCTION: s:refreshRoot() {{{2
3623 " Reloads the current root. All nodes below this will be lost and the root dir
3625 function! s:refreshRoot()
3626 call s:echo("Refreshing the root node. This could take a while...")
3627 call b:NERDTreeRoot.refresh()
3630 call s:echo("Refreshing the root node. This could take a while... DONE")
3633 " FUNCTION: s:refreshCurrent() {{{2
3634 " refreshes the root for the current node
3635 function! s:refreshCurrent()
3636 let treenode = s:TreeDirNode.GetSelected()
3638 call s:echo("Refresh failed. Select a node first")
3642 call s:echo("Refreshing node. This could take a while...")
3643 call treenode.refresh()
3646 call s:echo("Refreshing node. This could take a while... DONE")
3648 " FUNCTION: s:renameCurrent() {{{2
3649 " allows the user to rename the current node
3650 function! s:renameCurrent()
3651 let curNode = s:TreeFileNode.GetSelected()
3653 call s:echo("Put the cursor on a node first" )
3657 let newNodePath = input("Rename the current node\n" .
3658 \ "==========================================================\n" .
3659 \ "Enter the new path for the node: \n" .
3660 \ "", curNode.path.strForOS(0))
3662 if newNodePath ==# ''
3663 call s:echo("Node Renaming Aborted.")
3668 let bufnum = bufnr(curNode.path.str(0))
3670 call curNode.rename(newNodePath)
3673 "if the node is open in a buffer, ask the user if they want to
3676 let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
3677 call s:promptToDelBuffer(bufnum, prompt)
3680 call curNode.putCursorHere(1, 0)
3684 call s:echoWarning("Node Not Renamed.")
3688 " FUNCTION: s:showFileSystemMenu() {{{2
3689 function! s:showFileSystemMenu()
3690 let curNode = s:TreeFileNode.GetSelected()
3692 call s:echo("Put the cursor on a node first" )
3697 let prompt = "NERDTree Filesystem Menu\n" .
3698 \ "==========================================================\n".
3699 \ "Select the desired operation: \n" .
3700 \ " (a)dd a childnode\n".
3701 \ " (m)ove the current node\n".
3702 \ " (d)elete the current node\n"
3703 if s:Path.CopyingSupported()
3704 let prompt = prompt . " (c)opy the current node\n\n"
3706 let prompt = prompt . " \n"
3711 let choice = nr2char(getchar())
3714 call s:insertNewNode()
3715 elseif choice ==? "m"
3716 call s:renameCurrent()
3717 elseif choice ==? "d"
3719 elseif choice ==? "c" && s:Path.CopyingSupported()
3724 " FUNCTION: s:toggleIgnoreFilter() {{{2
3725 " toggles the use of the NERDTreeIgnore option
3726 function! s:toggleIgnoreFilter()
3727 let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled
3728 call s:renderViewSavingPosition()
3732 " FUNCTION: s:toggleShowBookmarks() {{{2
3733 " toggles the display of bookmarks
3734 function! s:toggleShowBookmarks()
3735 let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks
3736 if b:NERDTreeShowBookmarks
3738 call s:putCursorOnBookmarkTable()
3740 call s:renderViewSavingPosition()
3744 " FUNCTION: s:toggleShowFiles() {{{2
3745 " toggles the display of hidden files
3746 function! s:toggleShowFiles()
3747 let b:NERDTreeShowFiles = !b:NERDTreeShowFiles
3748 call s:renderViewSavingPosition()
3752 " FUNCTION: s:toggleShowHidden() {{{2
3753 " toggles the display of hidden files
3754 function! s:toggleShowHidden()
3755 let b:NERDTreeShowHidden = !b:NERDTreeShowHidden
3756 call s:renderViewSavingPosition()
3760 "FUNCTION: s:upDir(keepState) {{{2
3761 "moves the tree up a level
3764 "keepState: 1 if the current root should be left open when the tree is
3766 function! s:upDir(keepState)
3767 let cwd = b:NERDTreeRoot.path.str(0)
3768 if cwd ==# "/" || cwd =~ '^[^/]..$'
3769 call s:echo("already at top dir")
3772 call b:NERDTreeRoot.close()
3775 let oldRoot = b:NERDTreeRoot
3777 if empty(b:NERDTreeRoot.parent)
3778 let path = b:NERDTreeRoot.path.getPathTrunk()
3779 let newRoot = s:TreeDirNode.New(path)
3781 call newRoot.transplantChild(b:NERDTreeRoot)
3782 let b:NERDTreeRoot = newRoot
3784 let b:NERDTreeRoot = b:NERDTreeRoot.parent
3787 if g:NERDTreeChDirMode ==# 2
3788 exec 'cd ' . b:NERDTreeRoot.path.strForCd()
3792 call oldRoot.putCursorHere(0, 0)
3797 "reset &cpo back to users setting
3798 let &cpo = s:old_cpo
3800 " vim: set sw=4 sts=4 et fdm=marker: