Remove space mapping
[havk_dotfiles.git] / .vim / plugin / NERD_tree.vim
blob65ddabc0a186304aacbb03f7c3297ed867e65f5e
1 " ============================================================================
2 " File:        NERD_tree.vim
3 " Description: vim global plugin that provides a nice tree explorer
4 " Maintainer:  Martin Grenfell <martin_grenfell at msn dot com>
5 " Last Change: 20 July, 2008
6 " License:     This program is free software. It comes without any warranty,
7 "              to the extent permitted by applicable law. You can redistribute
8 "              it and/or modify it under the terms of the Do What The Fuck You
9 "              Want To Public License, Version 2, as published by Sam Hocevar.
10 "              See http://sam.zoy.org/wtfpl/COPYING for more details.
12 " ============================================================================
13 let s:NERD_tree_version = '2.14.0'
15 " SECTION: Script init stuff {{{1
16 "============================================================
17 if exists("loaded_nerd_tree")
18     finish
19 endif
20 if v:version < 700
21     echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!"
22     finish
23 endif
24 let loaded_nerd_tree = 1
26 "for line continuation - i.e dont want C in &cpo
27 let s:old_cpo = &cpo
28 set cpo&vim
30 "Function: s:initVariable() function {{{2
31 "This function is used to initialise a given variable to a given value. The
32 "variable is only initialised if it does not exist prior
34 "Args:
35 "var: the name of the var to be initialised
36 "value: the value to initialise var to
38 "Returns:
39 "1 if the var is set, 0 otherwise
40 function! s:initVariable(var, value)
41     if !exists(a:var)
42         exec 'let ' . a:var . ' = ' . "'" . a:value . "'"
43         return 1
44     endif
45     return 0
46 endfunction
48 "SECTION: Init variable calls and other random constants {{{2
49 call s:initVariable("g:NERDChristmasTree", 1)
50 call s:initVariable("g:NERDTreeAutoCenter", 1)
51 call s:initVariable("g:NERDTreeAutoCenterThreshold", 3)
52 call s:initVariable("g:NERDTreeCaseSensitiveSort", 0)
53 call s:initVariable("g:NERDTreeChDirMode", 0)
54 if !exists("g:NERDTreeIgnore")
55     let g:NERDTreeIgnore = ['\~$']
56 endif
57 call s:initVariable("g:NERDTreeHighlightCursorline", 1)
58 call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks')
59 call s:initVariable("g:NERDTreeMouseMode", 1)
60 call s:initVariable("g:NERDTreeNotificationThreshold", 100)
61 call s:initVariable("g:NERDTreeQuitOnOpen", 0)
62 call s:initVariable("g:NERDTreeShowBookmarks", 0)
63 call s:initVariable("g:NERDTreeShowFiles", 1)
64 call s:initVariable("g:NERDTreeShowHidden", 0)
65 call s:initVariable("g:NERDTreeShowLineNumbers", 0)
66 call s:initVariable("g:NERDTreeSortDirs", 1)
68 if !exists("g:NERDTreeSortOrder")
69     let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$',  '\.bak$', '\~$']
70 else
71     "if there isnt a * in the sort sequence then add one
72     if count(g:NERDTreeSortOrder, '*') < 1
73         call add(g:NERDTreeSortOrder, '*')
74     endif
75 endif
77 "we need to use this number many times for sorting... so we calculate it only
78 "once here
79 let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*')
81 call s:initVariable("g:NERDTreeWinPos", "left")
82 call s:initVariable("g:NERDTreeWinSize", 31)
84 let s:running_windows = has("win16") || has("win32") || has("win64")
86 "init the shell commands that will be used to copy nodes, and remove dir trees
88 "Note: the space after the command is important
89 if s:running_windows
90     call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ')
91 else
92     call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ')
93     call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ')
94 endif
97 "SECTION: Init variable calls for key mappings {{{2
98 call s:initVariable("g:NERDTreeMapActivateNode", "o")
99 call s:initVariable("g:NERDTreeMapChangeRoot", "C")
100 call s:initVariable("g:NERDTreeMapChdir", "cd")
101 call s:initVariable("g:NERDTreeMapCloseChildren", "X")
102 call s:initVariable("g:NERDTreeMapCloseDir", "x")
103 call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
104 call s:initVariable("g:NERDTreeMapExecute", "!")
105 call s:initVariable("g:NERDTreeMapFilesystemMenu", "m")
106 call s:initVariable("g:NERDTreeMapHelp", "?")
107 call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
108 call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
109 call s:initVariable("g:NERDTreeMapJumpNextSibling", "<C-j>")
110 call s:initVariable("g:NERDTreeMapJumpParent", "p")
111 call s:initVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>")
112 call s:initVariable("g:NERDTreeMapJumpRoot", "P")
113 call s:initVariable("g:NERDTreeMapOpenExpl", "e")
114 call s:initVariable("g:NERDTreeMapOpenInTab", "t")
115 call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T")
116 call s:initVariable("g:NERDTreeMapOpenRecursively", "O")
117 call s:initVariable("g:NERDTreeMapOpenSplit", "<tab>")
118 call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode)
119 call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit)
120 call s:initVariable("g:NERDTreeMapQuit", "q")
121 call s:initVariable("g:NERDTreeMapRefresh", "r")
122 call s:initVariable("g:NERDTreeMapRefreshRoot", "R")
123 call s:initVariable("g:NERDTreeMapToggleBookmarks", "B")
124 call s:initVariable("g:NERDTreeMapToggleFiles", "F")
125 call s:initVariable("g:NERDTreeMapToggleFilters", "f")
126 call s:initVariable("g:NERDTreeMapToggleHidden", "H")
127 call s:initVariable("g:NERDTreeMapUpdir", "u")
128 call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U")
130 "SECTION: Script level variable declaration{{{2
131 let s:escape_chars =  " \\`\|\"#%&,?()\*^<>"
132 let s:NERDTreeWinName = '_NERD_tree_'
134 let s:tree_wid = 2
135 let s:tree_markup_reg = '[ \-+~`|]'
136 let s:tree_markup_reg_neg = '[^ \-+~`|]'
137 let s:tree_up_dir_line = '.. (up a dir)'
139 let s:os_slash = '/'
140 if s:running_windows
141     let s:os_slash = '\'
142 endif
145 " SECTION: Commands {{{1
146 "============================================================
147 "init the command that users start the nerd tree with
148 command! -n=? -complete=dir NERDTree :call s:initNerdTree('<args>')
149 command! -n=? -complete=dir NERDTreeToggle :call s:toggle('<args>')
150 command! -n=0 NERDTreeClose :call s:closeTreeIfOpen()
151 command! -n=1 -complete=customlist,s:completeBookmarks NERDTreeFromBookmark call s:initNerdTree('<args>')
152 " SECTION: Auto commands {{{1
153 "============================================================
154 "Save the cursor position whenever we close the nerd tree
155 exec "autocmd BufWinLeave *". s:NERDTreeWinName ." call <SID>saveScreenState()"
156 "cache bookmarks when vim loads
157 autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
159 "SECTION: Classes {{{1
160 "============================================================
161 "CLASS: Bookmark {{{2
162 "============================================================
163 let s:Bookmark = {}
164 " FUNCTION: Bookmark.AddBookmark(name, path) {{{3
165 " Class method to add a new bookmark to the list, if a previous bookmark exists
166 " with the same name, just update the path for that bookmark
167 function! s:Bookmark.AddBookmark(name, path)
168     for i in s:Bookmark.Bookmarks()
169         if i.name == a:name
170             let i.path = a:path
171             return
172         endif
173     endfor
174     call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
175     call s:Bookmark.Sort()
176 endfunction
177 " Function: Bookmark.Bookmarks()   {{{3
178 " Class method to get all bookmarks. Lazily initializes the bookmarks global
179 " variable
180 function! s:Bookmark.Bookmarks()
181     if !exists("g:NERDTreeBookmarks")
182         let g:NERDTreeBookmarks = []
183     endif
184     return g:NERDTreeBookmarks
185 endfunction
186 " Function: Bookmark.BookmarkExistsFor(name)   {{{3
187 " class method that returns 1 if a bookmark with the given name is found, 0
188 " otherwise
189 function! s:Bookmark.BookmarkExistsFor(name)
190     try
191         call s:Bookmark.BookmarkFor(a:name)
192         return 1
193     catch /NERDTree.BookmarkNotFound/
194         return 0
195     endtry
196 endfunction
197 " Function: Bookmark.BookmarkFor(name)   {{{3
198 " Class method to get the bookmark that has the given name. {} is return if no
199 " bookmark is found
200 function! s:Bookmark.BookmarkFor(name)
201     for i in s:Bookmark.Bookmarks()
202         if i.name == a:name
203             return i
204         endif
205     endfor
206     throw "NERDTree.BookmarkNotFound exception: no bookmark found for name: \"". a:name  .'"'
207 endfunction
208 " Function: Bookmark.BookmarkNames()   {{{3
209 " Class method to return an array of all bookmark names
210 function! s:Bookmark.BookmarkNames()
211     let names = []
212     for i in s:Bookmark.Bookmarks()
213         call add(names, i.name)
214     endfor
215     return names
216 endfunction
217 " FUNCTION: Bookmark.CacheBookmarks(silent) {{{3
218 " Class method to read all bookmarks from the bookmarks file intialize
219 " bookmark objects for each one.
221 " Args:
222 " silent - dont echo an error msg if invalid bookmarks are found
223 function! s:Bookmark.CacheBookmarks(silent)
224     if filereadable(g:NERDTreeBookmarksFile)
225         let g:NERDTreeBookmarks = []
226         let g:NERDTreeInvalidBookmarks = []
227         let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
228         let invalidBookmarksFound = 0
229         for i in bookmarkStrings
231             "ignore blank lines
232             if i != ''
234                 let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
235                 let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
237                 try
238                     let bookmark = s:Bookmark.New(name, s:Path.New(path))
239                     call add(g:NERDTreeBookmarks, bookmark)
240                 catch /NERDTree.Path.InvalidArguments/
241                     call add(g:NERDTreeInvalidBookmarks, i)
242                     let invalidBookmarksFound += 1
243                 endtry
244             endif
245         endfor
246         if invalidBookmarksFound
247             call s:Bookmark.Write()
248             if !a:silent
249                 call s:echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
250             endif
251         endif
252         call s:Bookmark.Sort()
253     endif
254 endfunction
255 " FUNCTION: Bookmark.compareTo(otherbookmark) {{{3
256 " Compare these two bookmarks for sorting purposes
257 function! s:Bookmark.compareTo(otherbookmark)
258     return a:otherbookmark.name < self.name
259 endfunction
260 " FUNCTION: Bookmark.ClearAll() {{{3
261 " Class method to delete all bookmarks.
262 function! s:Bookmark.ClearAll()
263     for i in s:Bookmark.Bookmarks()
264         call i.delete()
265     endfor
266     call s:Bookmark.Write()
267 endfunction
268 " FUNCTION: Bookmark.delete() {{{3
269 " Delete this bookmark. If the node for this bookmark is under the current
270 " root, then recache bookmarks for its Path object
271 function! s:Bookmark.delete()
272     let node = {}
273     try
274         let node = self.getNode(1)
275     catch /NERDTree.BookmarkedNodeNotFound/
276     endtry
277     call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
278     if !empty(node)
279         call node.path.cacheDisplayString()
280     endif
281     call s:Bookmark.Write()
282 endfunction
283 " FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{3
284 " Gets the treenode for this bookmark
286 " Args:
287 " searchFromAbsoluteRoot: specifies whether we should search from the current
288 " tree root, or the highest cached node
289 function! s:Bookmark.getNode(searchFromAbsoluteRoot)
290     let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : t:NERDTreeRoot
291     let targetNode = searchRoot.findNode(self.path)
292     if empty(targetNode)
293         throw "NERDTree.BookmarkedNodeNotFound no node was found for bookmark: " . self.name
294     endif
295     return targetNode
296 endfunction
297 " FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{3
298 " Class method that finds the bookmark with the given name and returns the
299 " treenode for it.
300 function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
301     let bookmark = s:Bookmark.BookmarkFor(a:name)
302     return bookmark.getNode(a:searchFromAbsoluteRoot)
303 endfunction
304 " Function: Bookmark.InvalidBookmarks()   {{{3
305 " Class method to get all invalid bookmark strings read from the bookmarks
306 " file
307 function! s:Bookmark.InvalidBookmarks()
308     if !exists("g:NERDTreeInvalidBookmarks")
309         let g:NERDTreeInvalidBookmarks = []
310     endif
311     return g:NERDTreeInvalidBookmarks
312 endfunction
313 " FUNCTION: Bookmark.mustExist() {{{3
314 function! s:Bookmark.mustExist()
315     if !self.path.exists()
316         call s:Bookmark.CacheBookmarks(1)
317         throw "NERDTree.BookmarkPointsToInvalidLocation exception: the bookmark \"".
318             \ self.name ."\" points to a non existing location: \"". self.path.strForOS(0)
319     endif
320 endfunction
321 " FUNCTION: Bookmark.New(name, path) {{{3
322 " Create a new bookmark object with the given name and path object
323 function! s:Bookmark.New(name, path)
324     if a:name =~ ' '
325         throw "NERDTree.IllegalBookmarkName illegal name:" . a:name
326     endif
328     let newBookmark = copy(self)
329     let newBookmark.name = a:name
330     let newBookmark.path = a:path
331     return newBookmark
332 endfunction
333 " Function: Bookmark.setPath(path)   {{{3
334 " makes this bookmark point to the given path
335 function! s:Bookmark.setPath(path)
336     let self.path = a:path
337 endfunction
338 " Function: Bookmark.Sort()   {{{3
339 " Class method that sorts all bookmarks
340 function! s:Bookmark.Sort()
341     let CompareFunc = function("s:compareBookmarks")
342     call sort(s:Bookmark.Bookmarks(), CompareFunc)
343 endfunction
344 " Function: Bookmark.str()   {{{3
345 " Get the string that should be rendered in the view for this bookmark
346 function! s:Bookmark.str()
347     let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name)
348     if &nu
349         let pathStrMaxLen = pathStrMaxLen - &numberwidth
350     endif
352     let pathStr = self.path.strForOS(0)
353     if len(pathStr) > pathStrMaxLen
354         let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
355     endif
356     return '>' . self.name . ' ' . pathStr
357 endfunction
358 " FUNCTION: Bookmark.toRoot() {{{3
359 " Make the node for this bookmark the new tree root
360 function! s:Bookmark.toRoot()
361     if self.validate()
362         try
363             let targetNode = self.getNode(1)
364         catch /NERDTree.BookmarkedNodeNotFound/
365             let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
366         endtry
367         call targetNode.makeRoot()
368         call s:renderView()
369         call s:putCursorOnNode(targetNode, 0, 0)
370     endif
371 endfunction
372 " FUNCTION: Bookmark.ToRoot(name) {{{3
373 " Make the node for this bookmark the new tree root
374 function! s:Bookmark.ToRoot(name)
375     let bookmark = s:Bookmark.BookmarkFor(a:name)
376     call bookmark.toRoot()
377 endfunction
380 "FUNCTION: Bookmark.validate() {{{3
381 function! s:Bookmark.validate()
382     if self.path.exists()
383         return 1
384     else
385         call s:Bookmark.CacheBookmarks(1)
386         call s:renderView()
387         call s:echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
388         return 0
389     endif
390 endfunction
392 " Function: Bookmark.Write()   {{{3
393 " Class method to write all bookmarks to the bookmarks file
394 function! s:Bookmark.Write()
395     let bookmarkStrings = []
396     for i in s:Bookmark.Bookmarks()
397         call add(bookmarkStrings, i.name . ' ' . i.path.strForOS(0))
398     endfor
400     "add a blank line before the invalid ones
401     call add(bookmarkStrings, "")
403     for j in s:Bookmark.InvalidBookmarks()
404         call add(bookmarkStrings, j)
405     endfor
406     call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
407 endfunction
408 "CLASS: TreeFileNode {{{2
409 "This class is the parent of the TreeDirNode class and constitures the
410 "'Component' part of the composite design pattern between the treenode
411 "classes.
412 "============================================================
413 let s:TreeFileNode = {}
414 "FUNCTION: TreeFileNode.bookmark(name) {{{3
415 "bookmark this node with a:name
416 function! s:TreeFileNode.bookmark(name)
417     try
418         let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1)
419         call oldMarkedNode.path.cacheDisplayString()
420     catch /NERDTree.Bookmark\(DoesntExist\|NotFound\)/
421     endtry
423     call s:Bookmark.AddBookmark(a:name, self.path)
424     call self.path.cacheDisplayString()
425     call s:Bookmark.Write()
426 endfunction
427 "FUNCTION: TreeFileNode.cacheParent() {{{3
428 "initializes self.parent if it isnt already
429 function! s:TreeFileNode.cacheParent()
430     if empty(self.parent)
431         let parentPath = self.path.getParent()
432         if parentPath.equals(self.path)
433             throw "NERDTree.CannotCacheParent exception: already at root"
434         endif
435         let self.parent = s:TreeFileNode.New(parentPath)
436     endif
437 endfunction
438 "FUNCTION: TreeFileNode.compareNodes {{{3
439 "This is supposed to be a class level method but i cant figure out how to
440 "get func refs to work from a dict..
442 "A class level method that compares two nodes
444 "Args:
445 "n1, n2: the 2 nodes to compare
446 function! s:compareNodes(n1, n2)
447     return a:n1.path.compareTo(a:n2.path)
448 endfunction
450 "FUNCTION: TreeFileNode.clearBoomarks() {{{3
451 function! s:TreeFileNode.clearBoomarks()
452     for i in s:Bookmark.Bookmarks()
453         if i.path.equals(self.path)
454             call i.delete()
455         end
456     endfor
457     call self.path.cacheDisplayString()
458 endfunction
459 "FUNCTION: TreeFileNode.copy(dest) {{{3
460 function! s:TreeFileNode.copy(dest)
461     call self.path.copy(a:dest)
462     let newPath = s:Path.New(a:dest)
463     let parent = t:NERDTreeRoot.findNode(newPath.getParent())
464     if !empty(parent)
465         call parent.refresh()
466     endif
467     return parent.findNode(newPath)
468 endfunction
470 "FUNCTION: TreeFileNode.delete {{{3
471 "Removes this node from the tree and calls the Delete method for its path obj
472 function! s:TreeFileNode.delete()
473     call self.path.delete()
474     call self.parent.removeChild(self)
475 endfunction
477 "FUNCTION: TreeFileNode.equals(treenode) {{{3
479 "Compares this treenode to the input treenode and returns 1 if they are the
480 "same node.
482 "Use this method instead of ==  because sometimes when the treenodes contain
483 "many children, vim seg faults when doing ==
485 "Args:
486 "treenode: the other treenode to compare to
487 function! s:TreeFileNode.equals(treenode)
488     return self.path.str(1) == a:treenode.path.str(1)
489 endfunction
491 "FUNCTION: TreeFileNode.findNode(path) {{{3
492 "Returns self if this node.path.Equals the given path.
493 "Returns {} if not equal.
495 "Args:
496 "path: the path object to compare against
497 function! s:TreeFileNode.findNode(path)
498     if a:path.equals(self.path)
499         return self
500     endif
501     return {}
502 endfunction
503 "FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3
505 "Finds the next sibling for this node in the indicated direction. This sibling
506 "must be a directory and may/may not have children as specified.
508 "Args:
509 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
511 "Return:
512 "a treenode object or {} if no appropriate sibling could be found
513 function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
514     "if we have no parent then we can have no siblings
515     if self.parent != {}
516         let nextSibling = self.findSibling(a:direction)
518         while nextSibling != {}
519             if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen
520                 return nextSibling
521             endif
522             let nextSibling = nextSibling.findSibling(a:direction)
523         endwhile
524     endif
526     return {}
527 endfunction
528 "FUNCTION: TreeFileNode.findSibling(direction) {{{3
530 "Finds the next sibling for this node in the indicated direction
532 "Args:
533 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
535 "Return:
536 "a treenode object or {} if no sibling could be found
537 function! s:TreeFileNode.findSibling(direction)
538     "if we have no parent then we can have no siblings
539     if self.parent != {}
541         "get the index of this node in its parents children
542         let siblingIndx = self.parent.getChildIndex(self.path)
544         if siblingIndx != -1
545             "move a long to the next potential sibling node
546             let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1
548             "keep moving along to the next sibling till we find one that is valid
549             let numSiblings = self.parent.getChildCount()
550             while siblingIndx >= 0 && siblingIndx < numSiblings
552                 "if the next node is not an ignored node (i.e. wont show up in the
553                 "view) then return it
554                 if self.parent.children[siblingIndx].path.ignore() == 0
555                     return self.parent.children[siblingIndx]
556                 endif
558                 "go to next node
559                 let siblingIndx = a:direction == 1 ? siblingIndx+1 : siblingIndx-1
560             endwhile
561         endif
562     endif
564     return {}
565 endfunction
567 "FUNCTION: TreeFileNode.isVisible() {{{3
568 "returns 1 if this node should be visible according to the tree filters and
569 "hidden file filters (and their on/off status)
570 function! s:TreeFileNode.isVisible()
571     return !self.path.ignore()
572 endfunction
575 "FUNCTION: TreeFileNode.isRoot() {{{3
576 "returns 1 if this node is t:NERDTreeRoot
577 function! s:TreeFileNode.isRoot()
578     if !s:treeExistsForTab()
579         throw "NERDTree.TreeFileNode.IsRoot exception: No tree exists for the current tab"
580     endif
581     return self.equals(t:NERDTreeRoot)
582 endfunction
584 "FUNCTION: TreeFileNode.makeRoot() {{{3
585 "Make this node the root of the tree
586 function! s:TreeFileNode.makeRoot()
587     if self.path.isDirectory
588         let t:NERDTreeRoot = self
589     else
590         call self.cacheParent()
591         let t:NERDTreeRoot = self.parent
592     endif
594     call t:NERDTreeRoot.open()
596     "change dir to the dir of the new root if instructed to
597     if g:NERDTreeChDirMode == 2
598         exec "cd " . t:NERDTreeRoot.path.strForEditCmd()
599     endif
600 endfunction
601 "FUNCTION: TreeFileNode.New(path) {{{3
602 "Returns a new TreeNode object with the given path and parent
604 "Args:
605 "path: a path object representing the full filesystem path to the file/dir that the node represents
606 function! s:TreeFileNode.New(path)
607     if a:path.isDirectory
608         return s:TreeDirNode.New(a:path)
609     else
610         let newTreeNode = {}
611         let newTreeNode = copy(self)
612         let newTreeNode.path = a:path
613         let newTreeNode.parent = {}
614         return newTreeNode
615     endif
616 endfunction
618 "FUNCTION: TreeFileNode.refresh() {{{3
619 function! s:TreeFileNode.refresh()
620     call self.path.refresh()
621 endfunction
622 "FUNCTION: TreeFileNode.rename() {{{3
623 "Calls the rename method for this nodes path obj
624 function! s:TreeFileNode.rename(newName)
625     let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
626     call self.path.rename(newName)
627     call self.parent.removeChild(self)
629     let parentPath = self.path.getPathTrunk()
630     let newParent = t:NERDTreeRoot.findNode(parentPath)
632     if newParent != {}
633         call newParent.createChild(self.path, 1)
634         call newParent.refresh()
635     endif
636 endfunction
637 "FUNCTION: TreeFileNode.strDisplay() {{{3
639 "Returns a string that specifies how the node should be represented as a
640 "string
642 "Return:
643 "a string that can be used in the view to represent this node
644 function! s:TreeFileNode.strDisplay()
645     return self.path.strDisplay()
646 endfunction
648 "CLASS: TreeDirNode {{{2
649 "This class is a child of the TreeFileNode class and constitutes the
650 "'Composite' part of the composite design pattern between the treenode
651 "classes.
652 "============================================================
653 let s:TreeDirNode = copy(s:TreeFileNode)
654 "FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
655 "class method that returns the highest cached ancestor of the current root
656 function! s:TreeDirNode.AbsoluteTreeRoot()
657     let currentNode = t:NERDTreeRoot
658     while currentNode.parent != {}
659         let currentNode = currentNode.parent
660     endwhile
661     return currentNode
662 endfunction
663 "FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{3
664 "Adds the given treenode to the list of children for this node
666 "Args:
667 "-treenode: the node to add
668 "-inOrder: 1 if the new node should be inserted in sorted order
669 function! s:TreeDirNode.addChild(treenode, inOrder)
670     call add(self.children, a:treenode)
671     let a:treenode.parent = self
673     if a:inOrder
674         call self.sortChildren()
675     endif
676 endfunction
678 "FUNCTION: TreeDirNode.close() {{{3
679 "Closes this directory
680 function! s:TreeDirNode.close()
681     let self.isOpen = 0
682 endfunction
684 "FUNCTION: TreeDirNode.closeChildren() {{{3
685 "Closes all the child dir nodes of this node
686 function! s:TreeDirNode.closeChildren()
687     for i in self.children
688         if i.path.isDirectory
689             call i.close()
690             call i.closeChildren()
691         endif
692     endfor
693 endfunction
695 "FUNCTION: TreeDirNode.createChild(path, inOrder) {{{3
696 "Instantiates a new child node for this node with the given path. The new
697 "nodes parent is set to this node.
699 "Args:
700 "path: a Path object that this node will represent/contain
701 "inOrder: 1 if the new node should be inserted in sorted order
703 "Returns:
704 "the newly created node
705 function! s:TreeDirNode.createChild(path, inOrder)
706     let newTreeNode = s:TreeFileNode.New(a:path)
707     call self.addChild(newTreeNode, a:inOrder)
708     return newTreeNode
709 endfunction
711 "FUNCTION: TreeDirNode.findNode(path) {{{3
712 "Will find one of the children (recursively) that has the given path
714 "Args:
715 "path: a path object
716 function! s:TreeDirNode.findNode(path)
717     if a:path.equals(self.path)
718         return self
719     endif
720     if stridx(a:path.str(1), self.path.str(1), 0) == -1
721         return {}
722     endif
724     if self.path.isDirectory
725         for i in self.children
726             let retVal = i.findNode(a:path)
727             if retVal != {}
728                 return retVal
729             endif
730         endfor
731     endif
732     return {}
733 endfunction
735 "FUNCTION: TreeDirNode.getChildCount() {{{3
736 "Returns the number of children this node has
737 function! s:TreeDirNode.getChildCount()
738     return len(self.children)
739 endfunction
741 "FUNCTION: TreeDirNode.getChild(path) {{{3
742 "Returns child node of this node that has the given path or {} if no such node
743 "exists.
745 "This function doesnt not recurse into child dir nodes
747 "Args:
748 "path: a path object
749 function! s:TreeDirNode.getChild(path)
750     if stridx(a:path.str(1), self.path.str(1), 0) == -1
751         return {}
752     endif
754     let index = self.getChildIndex(a:path)
755     if index == -1
756         return {}
757     else
758         return self.children[index]
759     endif
761 endfunction
763 "FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{3
764 "returns the child at the given index
765 "Args:
766 "indx: the index to get the child from
767 "visible: 1 if only the visible children array should be used, 0 if all the
768 "children should be searched.
769 function! s:TreeDirNode.getChildByIndex(indx, visible)
770     let array_to_search = a:visible? self.getVisibleChildren() : self.children
771     if a:indx > len(array_to_search)
772         throw "NERDTree.TreeDirNode.InvalidArguments exception. Index is out of bounds."
773     endif
774     return array_to_search[a:indx]
775 endfunction
777 "FUNCTION: TreeDirNode.getChildIndex(path) {{{3
778 "Returns the index of the child node of this node that has the given path or
779 "-1 if no such node exists.
781 "This function doesnt not recurse into child dir nodes
783 "Args:
784 "path: a path object
785 function! s:TreeDirNode.getChildIndex(path)
786     if stridx(a:path.str(1), self.path.str(1), 0) == -1
787         return -1
788     endif
790     "do a binary search for the child
791     let a = 0
792     let z = self.getChildCount()
793     while a < z
794         let mid = (a+z)/2
795         let diff = a:path.compareTo(self.children[mid].path)
797         if diff == -1
798             let z = mid
799         elseif diff == 1
800             let a = mid+1
801         else
802             return mid
803         endif
804     endwhile
805     return -1
806 endfunction
808 "FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
809 "Returns the number of visible children this node has
810 function! s:TreeDirNode.getVisibleChildCount()
811     return len(self.getVisibleChildren())
812 endfunction
814 "FUNCTION: TreeDirNode.getVisibleChildren() {{{3
815 "Returns a list of children to display for this node, in the correct order
817 "Return:
818 "an array of treenodes
819 function! s:TreeDirNode.getVisibleChildren()
820     let toReturn = []
821     for i in self.children
822         if i.path.ignore() == 0
823             call add(toReturn, i)
824         endif
825     endfor
826     return toReturn
827 endfunction
829 "FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
830 "returns 1 if this node has any childre, 0 otherwise..
831 function! s:TreeDirNode.hasVisibleChildren()
832     return self.getVisibleChildCount() != 0
833 endfunction
835 "FUNCTION: TreeDirNode._initChildren() {{{3
836 "Removes all childen from this node and re-reads them
838 "Args:
839 "silent: 1 if the function should not echo any "please wait" messages for
840 "large directories
842 "Return: the number of child nodes read
843 function! s:TreeDirNode._initChildren(silent)
844     "remove all the current child nodes
845     let self.children = []
847     "get an array of all the files in the nodes dir
848     let dir = self.path
849     let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
850     let files = split(filesStr, "\n")
852     if !a:silent && len(files) > g:NERDTreeNotificationThreshold
853         call s:echo("Please wait, caching a large dir ...")
854     endif
856     let invalidFilesFound = 0
857     for i in files
859         "filter out the .. and . directories
860         "Note: we must match .. AND ../ cos sometimes the globpath returns
861         "../ for path with strange chars (eg $)
862         if i !~ '\.\.\/\?$' && i !~ '\.\/\?$'
864             "put the next file in a new node and attach it
865             try
866                 let path = s:Path.New(i)
867                 call self.createChild(path, 0)
868             catch /^NERDTree.Path.\(InvalidArguments\|InvalidFiletype\)/
869                 let invalidFilesFound += 1
870             endtry
871         endif
872     endfor
874     call self.sortChildren()
876     if !a:silent && len(files) > g:NERDTreeNotificationThreshold
877         call s:echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).")
878     endif
880     if invalidFilesFound
881         call s:echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree")
882     endif
883     return self.getChildCount()
884 endfunction
885 "FUNCTION: TreeDirNode.New(path) {{{3
886 "Returns a new TreeNode object with the given path and parent
888 "Args:
889 "path: a path object representing the full filesystem path to the file/dir that the node represents
890 function! s:TreeDirNode.New(path)
891     if a:path.isDirectory != 1
892         throw "NERDTree.TreeDirNode.InvalidArguments exception. A TreeDirNode object must be instantiated with a directory Path object."
893     endif
895     let newTreeNode = copy(self)
896     let newTreeNode.path = a:path
898     let newTreeNode.isOpen = 0
899     let newTreeNode.children = []
901     let newTreeNode.parent = {}
903     return newTreeNode
904 endfunction
905 "FUNCTION: TreeDirNode.open() {{{3
906 "Reads in all this nodes children
908 "Return: the number of child nodes read
909 function! s:TreeDirNode.open()
910     let self.isOpen = 1
911     if self.children == []
912         return self._initChildren(0)
913     else
914         return 0
915     endif
916 endfunction
918 "FUNCTION: TreeDirNode.openRecursively() {{{3
919 "Opens this treenode and all of its children whose paths arent 'ignored'
920 "because of the file filters.
922 "This method is actually a wrapper for the OpenRecursively2 method which does
923 "the work.
924 function! s:TreeDirNode.openRecursively()
925     call self._openRecursively2(1)
926 endfunction
928 "FUNCTION: TreeDirNode._openRecursively2() {{{3
929 "Opens this all children of this treenode recursively if either:
930 "   *they arent filtered by file filters
931 "   *a:forceOpen is 1
933 "Args:
934 "forceOpen: 1 if this node should be opened regardless of file filters
935 function! s:TreeDirNode._openRecursively2(forceOpen)
936     if self.path.ignore() == 0 || a:forceOpen
937         let self.isOpen = 1
938         if self.children == []
939             call self._initChildren(1)
940         endif
942         for i in self.children
943             if i.path.isDirectory == 1
944                 call i._openRecursively2(0)
945             endif
946         endfor
947     endif
948 endfunction
950 "FUNCTION: TreeDirNode.refresh() {{{3
951 function! s:TreeDirNode.refresh()
952     call self.path.refresh()
954     "if this node was ever opened, refresh its children
955     if self.isOpen || !empty(self.children)
956         "go thru all the files/dirs under this node
957         let newChildNodes = []
958         let invalidFilesFound = 0
959         let dir = self.path
960         let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
961         let files = split(filesStr, "\n")
962         for i in files
963             if i !~ '\.\.$' && i !~ '\.$'
965                 try
966                     "create a new path and see if it exists in this nodes children
967                     let path = s:Path.New(i)
968                     let newNode = self.getChild(path)
969                     if newNode != {}
970                         call newNode.refresh()
971                         call add(newChildNodes, newNode)
973                     "the node doesnt exist so create it
974                     else
975                         let newNode = s:TreeFileNode.New(path)
976                         let newNode.parent = self
977                         call add(newChildNodes, newNode)
978                     endif
981                 catch /^NERDTree.InvalidArguments/
982                     let invalidFilesFound = 1
983                 endtry
984             endif
985         endfor
987         "swap this nodes children out for the children we just read/refreshed
988         let self.children = newChildNodes
989         call self.sortChildren()
991         if invalidFilesFound
992             call s:echoWarning("some files could not be loaded into the NERD tree")
993         endif
994     endif
995 endfunction
997 "FUNCTION: TreeDirNode.removeChild(treenode) {{{3
999 "Removes the given treenode from this nodes set of children
1001 "Args:
1002 "treenode: the node to remove
1004 "Throws a NERDTree.TreeDirNode exception if the given treenode is not found
1005 function! s:TreeDirNode.removeChild(treenode)
1006     for i in range(0, self.getChildCount()-1)
1007         if self.children[i].equals(a:treenode)
1008             call remove(self.children, i)
1009             return
1010         endif
1011     endfor
1013     throw "NERDTree.TreeDirNode exception: child node was not found"
1014 endfunction
1016 "FUNCTION: TreeDirNode.sortChildren() {{{3
1018 "Sorts the children of this node according to alphabetical order and the
1019 "directory priority.
1021 function! s:TreeDirNode.sortChildren()
1022     let CompareFunc = function("s:compareNodes")
1023     call sort(self.children, CompareFunc)
1024 endfunction
1026 "FUNCTION: TreeDirNode.toggleOpen() {{{3
1027 "Opens this directory if it is closed and vice versa
1028 function! s:TreeDirNode.toggleOpen()
1029     if self.isOpen == 1
1030         call self.close()
1031     else
1032         call self.open()
1033     endif
1034 endfunction
1036 "FUNCTION: TreeDirNode.transplantChild(newNode) {{{3
1037 "Replaces the child of this with the given node (where the child node's full
1038 "path matches a:newNode's fullpath). The search for the matching node is
1039 "non-recursive
1041 "Arg:
1042 "newNode: the node to graft into the tree
1043 function! s:TreeDirNode.transplantChild(newNode)
1044     for i in range(0, self.getChildCount()-1)
1045         if self.children[i].equals(a:newNode)
1046             let self.children[i] = a:newNode
1047             let a:newNode.parent = self
1048             break
1049         endif
1050     endfor
1051 endfunction
1052 "============================================================
1053 "CLASS: Path {{{2
1054 "============================================================
1055 let s:Path = {}
1056 "FUNCTION: Path.bookmarkNames() {{{3
1057 function! s:Path.bookmarkNames()
1058     if !exists("self._bookmarkNames")
1059         call self.cacheDisplayString()
1060     endif
1061     return self._bookmarkNames
1062 endfunction
1063 "FUNCTION: Path.cacheDisplayString() {{{3
1064 function! s:Path.cacheDisplayString()
1065     let self.cachedDisplayString = self.getLastPathComponent(1)
1067     if self.isExecutable
1068         let self.cachedDisplayString = self.cachedDisplayString . '*'
1069     endif
1071     let self._bookmarkNames = []
1072     for i in s:Bookmark.Bookmarks()
1073         if i.path.equals(self)
1074             call add(self._bookmarkNames, i.name)
1075         endif
1076     endfor
1077     if !empty(self._bookmarkNames)
1078         let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
1079     endif
1081     if self.isSymLink
1082         let self.cachedDisplayString .=  ' -> ' . self.symLinkDest
1083     endif
1085     if self.isReadOnly
1086         let self.cachedDisplayString .=  ' [RO]'
1087     endif
1088 endfunction
1089 "FUNCTION: Path.changeToDir() {{{3
1090 function! s:Path.changeToDir()
1091     let dir = self.strForCd()
1092     if self.isDirectory == 0
1093         let dir = self.getPathTrunk().strForCd()
1094     endif
1096     try
1097         execute "cd " . dir
1098         call s:echo("CWD is now: " . getcwd())
1099     catch
1100         throw "NERDTree.Path.Change exception: cannot change to " . dir
1101     endtry
1102 endfunction
1104 "FUNCTION: Path.compareTo() {{{3
1106 "Compares this Path to the given path and returns 0 if they are equal, -1 if
1107 "this Path is "less than" the given path, or 1 if it is "greater".
1109 "Args:
1110 "path: the path object to compare this to
1112 "Return:
1113 "1, -1 or 0
1114 function! s:Path.compareTo(path)
1115     let thisPath = self.getLastPathComponent(1)
1116     let thatPath = a:path.getLastPathComponent(1)
1118     "if the paths are the same then clearly we return 0
1119     if thisPath == thatPath
1120         return 0
1121     endif
1123     let thisSS = self.getSortOrderIndex()
1124     let thatSS = a:path.getSortOrderIndex()
1126     "compare the sort sequences, if they are different then the return
1127     "value is easy
1128     if thisSS < thatSS
1129         return -1
1130     elseif thisSS > thatSS
1131         return 1
1132     else
1133         "if the sort sequences are the same then compare the paths
1134         "alphabetically
1135         let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath
1136         if pathCompare
1137             return -1
1138         else
1139             return 1
1140         endif
1141     endif
1142 endfunction
1144 "FUNCTION: Path.Create(fullpath) {{{3
1146 "Factory method.
1148 "Creates a path object with the given path. The path is also created on the
1149 "filesystem. If the path already exists, a NERDTree.Path.Exists exception is
1150 "thrown. If any other errors occur, a NERDTree.Path exception is thrown.
1152 "Args:
1153 "fullpath: the full filesystem path to the file/dir to create
1154 function! s:Path.Create(fullpath)
1155     "bail if the a:fullpath already exists
1156     if isdirectory(a:fullpath) || filereadable(a:fullpath)
1157         throw "NERDTree.Path.Exists Exception: Directory Exists: '" . a:fullpath . "'"
1158     endif
1160     try
1162         "if it ends with a slash, assume its a dir create it
1163         if a:fullpath =~ '\(\\\|\/\)$'
1164             "whack the trailing slash off the end if it exists
1165             let fullpath = substitute(a:fullpath, '\(\\\|\/\)$', '', '')
1167             call mkdir(fullpath, 'p')
1169         "assume its a file and create
1170         else
1171             call writefile([], a:fullpath)
1172         endif
1173     catch /.*/
1174         throw "NERDTree.Path Exception: Could not create path: '" . a:fullpath . "'"
1175     endtry
1177     return s:Path.New(a:fullpath)
1178 endfunction
1180 "FUNCTION: Path.copy(dest) {{{3
1182 "Copies the file/dir represented by this Path to the given location
1184 "Args:
1185 "dest: the location to copy this dir/file to
1186 function! s:Path.copy(dest)
1187     if !s:Path.CopyingSupported()
1188         throw "NERDTree.Path.CopyingNotSupported Exception: Copying is not supported on this OS"
1189     endif
1191     let dest = s:Path.WinToUnixPath(a:dest)
1193     let cmd = g:NERDTreeCopyCmd . " " . self.strForOS(0) . " " . dest
1194     let success = system(cmd)
1195     if success != 0
1196         throw "NERDTree.Path Exception: Could not copy ''". self.strForOS(0) ."'' to: '" . a:dest . "'"
1197     endif
1198 endfunction
1200 "FUNCTION: Path.CopyingSupported() {{{3
1202 "returns 1 if copying is supported for this OS
1203 function! s:Path.CopyingSupported()
1204     return exists('g:NERDTreeCopyCmd')
1205 endfunction
1208 "FUNCTION: Path.copyingWillOverwrite(dest) {{{3
1210 "returns 1 if copy this path to the given location will cause files to
1211 "overwritten
1213 "Args:
1214 "dest: the location this path will be copied to
1215 function! s:Path.copyingWillOverwrite(dest)
1216     if filereadable(a:dest)
1217         return 1
1218     endif
1220     if isdirectory(a:dest)
1221         let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
1222         if filereadable(path)
1223             return 1
1224         endif
1225     endif
1226 endfunction
1228 "FUNCTION: Path.delete() {{{3
1230 "Deletes the file represented by this path.
1231 "Deletion of directories is not supported
1233 "Throws NERDTree.Path.Deletion exceptions
1234 function! s:Path.delete()
1235     if self.isDirectory
1237         let cmd = ""
1238         if s:running_windows
1239             "if we are runnnig windows then put quotes around the pathstring
1240             let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
1241         else
1242             let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
1243         endif
1244         let success = system(cmd)
1246         if v:shell_error != 0
1247             throw "NERDTree.Path.Deletion Exception: Could not delete directory: '" . self.strForOS(0) . "'"
1248         endif
1249     else
1250         let success = delete(self.strForOS(0))
1251         if success != 0
1252             throw "NERDTree.Path.Deletion Exception: Could not delete file: '" . self.str(0) . "'"
1253         endif
1254     endif
1256     "delete all bookmarks for this path
1257     for i in self.bookmarkNames()
1258         let bookmark = s:Bookmark.BookmarkFor(i)
1259         call bookmark.delete()
1260     endfor
1261 endfunction
1263 "FUNCTION: Path.extractDriveLetter(fullpath) {{{3
1265 "If running windows, cache the drive letter for this path
1266 function! s:Path.extractDriveLetter(fullpath)
1267     if s:running_windows
1268         let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
1269     else
1270         let self.drive = ''
1271     endif
1273 endfunction
1274 "FUNCTION: Path.exists() {{{3
1275 "return 1 if this path points to a location that is readable or is a directory
1276 function! s:Path.exists()
1277     return filereadable(self.strForOS(0)) || isdirectory(self.strForOS(0))
1278 endfunction
1279 "FUNCTION: Path.getDir() {{{3
1281 "Returns this path if it is a directory, else this paths parent.
1283 "Return:
1284 "a Path object
1285 function! s:Path.getDir()
1286     if self.isDirectory
1287         return self
1288     else
1289         return self.getParent()
1290     endif
1291 endfunction
1292 "FUNCTION: Path.getParent() {{{3
1294 "Returns a new path object for this paths parent
1296 "Return:
1297 "a new Path object
1298 function! s:Path.getParent()
1299     let path = '/'. join(self.pathSegments[0:-2], '/')
1300     return s:Path.New(path)
1301 endfunction
1302 "FUNCTION: Path.getLastPathComponent(dirSlash) {{{3
1304 "Gets the last part of this path.
1306 "Args:
1307 "dirSlash: if 1 then a trailing slash will be added to the returned value for
1308 "directory nodes.
1309 function! s:Path.getLastPathComponent(dirSlash)
1310     if empty(self.pathSegments)
1311         return ''
1312     endif
1313     let toReturn = self.pathSegments[-1]
1314     if a:dirSlash && self.isDirectory
1315         let toReturn = toReturn . '/'
1316     endif
1317     return toReturn
1318 endfunction
1320 "FUNCTION: Path.getPathTrunk() {{{3
1321 "Gets the path without the last segment on the end.
1322 function! s:Path.getPathTrunk()
1323     return s:Path.New(self.strTrunk())
1324 endfunction
1326 "FUNCTION: Path.getSortOrderIndex() {{{3
1327 "returns the index of the pattern in g:NERDTreeSortOrder that this path matches
1328 function! s:Path.getSortOrderIndex()
1329     let i = 0
1330     while i < len(g:NERDTreeSortOrder)
1331         if  self.getLastPathComponent(1) =~ g:NERDTreeSortOrder[i]
1332             return i
1333         endif
1334         let i = i + 1
1335     endwhile
1336     return s:NERDTreeSortStarIndex
1337 endfunction
1339 "FUNCTION: Path.ignore() {{{3
1340 "returns true if this path should be ignored
1341 function! s:Path.ignore()
1342     let lastPathComponent = self.getLastPathComponent(0)
1344     "filter out the user specified paths to ignore
1345     if t:NERDTreeIgnoreEnabled
1346         for i in g:NERDTreeIgnore
1347             if lastPathComponent =~ i
1348                 return 1
1349             endif
1350         endfor
1351     endif
1353     "dont show hidden files unless instructed to
1354     if t:NERDTreeShowHidden == 0 && lastPathComponent =~ '^\.'
1355         return 1
1356     endif
1358     if t:NERDTreeShowFiles == 0 && self.isDirectory == 0
1359         return 1
1360     endif
1362     return 0
1363 endfunction
1365 "FUNCTION: Path.JoinPathStrings(...) {{{3
1366 function! s:Path.JoinPathStrings(...)
1367     let components = []
1368     for i in a:000
1369         let components = extend(components, split(i, '/'))
1370     endfor
1371     return '/' . join(components, '/')
1372 endfunction
1374 "FUNCTION: Path.equals() {{{3
1376 "Determines whether 2 path objects are "equal".
1377 "They are equal if the paths they represent are the same
1379 "Args:
1380 "path: the other path obj to compare this with
1381 function! s:Path.equals(path)
1382     return self.str(0) == a:path.str(0)
1383 endfunction
1385 "FUNCTION: Path.New() {{{3
1387 "The Constructor for the Path object
1388 "Throws NERDTree.Path.InvalidArguments exception.
1389 function! s:Path.New(fullpath)
1390     let newPath = copy(self)
1392     call newPath.readInfoFromDisk(a:fullpath)
1394     let newPath.cachedDisplayString = ""
1396     return newPath
1397 endfunction
1399 "FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
1402 "Throws NERDTree.Path.InvalidArguments exception.
1403 function! s:Path.readInfoFromDisk(fullpath)
1404     call self.extractDriveLetter(a:fullpath)
1406     let fullpath = s:Path.WinToUnixPath(a:fullpath)
1408     if getftype(fullpath) == "fifo"
1409         throw "NERDTree.Path.InvalidFiletype Exception: Cant handle FIFO files: " . a:fullpath
1410     endif
1412     let self.pathSegments = split(fullpath, '/')
1415     let self.isReadOnly = 0
1416     if isdirectory(a:fullpath)
1417         let self.isDirectory = 1
1418     elseif filereadable(a:fullpath)
1419         let self.isDirectory = 0
1420         let self.isReadOnly = filewritable(a:fullpath) == 0
1421     else
1422         throw "NERDTree.Path.InvalidArguments Exception: Invalid path = " . a:fullpath
1423     endif
1425     let self.isExecutable = 0
1426     if !self.isDirectory
1427         let self.isExecutable = getfperm(a:fullpath) =~ 'x'
1428     endif
1430     "grab the last part of the path (minus the trailing slash)
1431     let lastPathComponent = self.getLastPathComponent(0)
1433     "get the path to the new node with the parent dir fully resolved
1434     let hardPath = resolve(self.strTrunk()) . '/' . lastPathComponent
1436     "if  the last part of the path is a symlink then flag it as such
1437     let self.isSymLink = (resolve(hardPath) != hardPath)
1438     if self.isSymLink
1439         let self.symLinkDest = resolve(fullpath)
1441         "if the link is a dir then slap a / on the end of its dest
1442         if isdirectory(self.symLinkDest)
1444             "we always wanna treat MS windows shortcuts as files for
1445             "simplicity
1446             if hardPath !~ '\.lnk$'
1448                 let self.symLinkDest = self.symLinkDest . '/'
1449             endif
1450         endif
1451     endif
1452 endfunction
1454 "FUNCTION: Path.refresh() {{{3
1455 function! s:Path.refresh()
1456     call self.readInfoFromDisk(self.strForOS(0))
1457     call self.cacheDisplayString()
1458 endfunction
1460 "FUNCTION: Path.rename() {{{3
1462 "Renames this node on the filesystem
1463 function! s:Path.rename(newPath)
1464     if a:newPath == ''
1465         throw "NERDTree.Path.InvalidArguments exception. Invalid newPath for renaming = ". a:newPath
1466     endif
1468     let success =  rename(self.strForOS(0), a:newPath)
1469     if success != 0
1470         throw "NERDTree.Path.Rename Exception: Could not rename: '" . self.strForOS(0) . "'" . 'to:' . a:newPath
1471     endif
1472     call self.readInfoFromDisk(a:newPath)
1474     for i in self.bookmarkNames()
1475         let b = s:Bookmark.BookmarkFor(i)
1476         call b.setPath(copy(self))
1477     endfor
1478     call s:Bookmark.Write()
1479 endfunction
1481 "FUNCTION: Path.str(esc) {{{3
1483 "Gets the actual string path that this obj represents.
1485 "Args:
1486 "esc: if 1 then all the tricky chars in the returned string will be escaped
1487 function! s:Path.str(esc)
1488     let toReturn = '/' . join(self.pathSegments, '/')
1489     if self.isDirectory && toReturn != '/'
1490         let toReturn  = toReturn . '/'
1491     endif
1493     if a:esc
1494         let toReturn = escape(toReturn, s:escape_chars)
1495     endif
1496     return toReturn
1497 endfunction
1499 "FUNCTION: Path.strAbs() {{{3
1501 "Returns a string representing this path with all the symlinks resolved
1503 "Return:
1504 "string
1505 function! s:Path.strAbs()
1506     return resolve(self.str(1))
1507 endfunction
1509 "FUNCTION: Path.strForCd() {{{3
1511 " returns a string that can be used with :cd
1513 "Return:
1514 "a string that can be used in the view to represent this path
1515 function! s:Path.strForCd()
1516     if s:running_windows
1517         return self.strForOS(0)
1518     else
1519         return self.strForOS(1)
1520     endif
1521 endfunction
1522 "FUNCTION: Path.strDisplay() {{{3
1524 "Returns a string that specifies how the path should be represented as a
1525 "string
1527 "Return:
1528 "a string that can be used in the view to represent this path
1529 function! s:Path.strDisplay()
1530     if self.cachedDisplayString == ""
1531         call self.cacheDisplayString()
1532     endif
1534     return self.cachedDisplayString
1535 endfunction
1537 "FUNCTION: Path.strForEditCmd() {{{3
1539 "Return: the string for this path that is suitable to be used with the :edit
1540 "command
1541 function! s:Path.strForEditCmd()
1542     if s:running_windows
1543         return self.strForOS(0)
1544     else
1545         return self.str(1)
1546     endif
1548 endfunction
1549 "FUNCTION: Path.strForGlob() {{{3
1550 function! s:Path.strForGlob()
1551     let lead = s:os_slash
1553     "if we are running windows then slap a drive letter on the front
1554     if s:running_windows
1555         let lead = self.drive . '\'
1556     endif
1558     let toReturn = lead . join(self.pathSegments, s:os_slash)
1560     if !s:running_windows
1561         let toReturn = escape(toReturn, s:escape_chars)
1562     endif
1563     return toReturn
1564 endfunction
1565 "FUNCTION: Path.strForOS(esc) {{{3
1567 "Gets the string path for this path object that is appropriate for the OS.
1568 "EG, in windows c:\foo\bar
1569 "    in *nix  /foo/bar
1571 "Args:
1572 "esc: if 1 then all the tricky chars in the returned string will be
1573 " escaped. If we are running windows then the str is double quoted instead.
1574 function! s:Path.strForOS(esc)
1575     let lead = s:os_slash
1577     "if we are running windows then slap a drive letter on the front
1578     if s:running_windows
1579         let lead = self.drive . '\'
1580     endif
1582     let toReturn = lead . join(self.pathSegments, s:os_slash)
1584     if a:esc
1585         if s:running_windows
1586             let toReturn = '"' .  toReturn . '"'
1587         else
1588             let toReturn = escape(toReturn, s:escape_chars)
1589         endif
1590     endif
1591     return toReturn
1592 endfunction
1594 "FUNCTION: Path.strTrunk() {{{3
1595 "Gets the path without the last segment on the end.
1596 function! s:Path.strTrunk()
1597     return self.drive . '/' . join(self.pathSegments[0:-2], '/')
1598 endfunction
1600 "FUNCTION: Path.WinToUnixPath(pathstr){{{3
1601 "Takes in a windows path and returns the unix equiv
1603 "A class level method
1605 "Args:
1606 "pathstr: the windows path to convert
1607 function! s:Path.WinToUnixPath(pathstr)
1608     if !s:running_windows
1609         return a:pathstr
1610     endif
1612     let toReturn = a:pathstr
1614     "remove the x:\ of the front
1615     let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "")
1617     "convert all \ chars to /
1618     let toReturn = substitute(toReturn, '\', '/', "g")
1620     return toReturn
1621 endfunction
1623 " SECTION: General Functions {{{1
1624 "============================================================
1625 "FUNCTION: s:bufInWindows(bnum){{{2
1626 "[[STOLEN FROM VTREEEXPLORER.VIM]]
1627 "Determine the number of windows open to this buffer number.
1628 "Care of Yegappan Lakshman.  Thanks!
1630 "Args:
1631 "bnum: the subject buffers buffer number
1632 function! s:bufInWindows(bnum)
1633     let cnt = 0
1634     let winnum = 1
1635     while 1
1636         let bufnum = winbufnr(winnum)
1637         if bufnum < 0
1638             break
1639         endif
1640         if bufnum == a:bnum
1641             let cnt = cnt + 1
1642         endif
1643         let winnum = winnum + 1
1644     endwhile
1646     return cnt
1647 endfunction " >>>
1649 "FUNCTION: s:compareBookmarks(first, second) {{{2
1650 "Compares two bookmarks
1651 function! s:compareBookmarks(first, second)
1652     return a:first.compareTo(a:second)
1653 endfunction
1655 " FUNCTION: s:completeBookmarks(A,L,P) {{{2
1656 " completion function for the bookmark commands
1657 function! s:completeBookmarks(A,L,P)
1658     return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"')
1659 endfunction
1660 "FUNCTION: s:initNerdTree(name) {{{2
1661 "Initialise the nerd tree for this tab. The tree will start in either the
1662 "given directory, or the directory associated with the given bookmark
1664 "Args:
1665 "name: the name of a bookmark or a directory
1666 function! s:initNerdTree(name)
1667     let path = {}
1668     if s:Bookmark.BookmarkExistsFor(a:name)
1669         let path = s:Bookmark.BookmarkFor(a:name).path
1670     else
1671         let dir = a:name == '' ? expand('%:p:h') : a:name
1672         let dir = resolve(dir)
1673         try
1674             let path = s:Path.New(dir)
1675         catch /NERDTree.Path.InvalidArguments/
1676             call s:echo("No bookmark or directory found for: " . a:name)
1677             return
1678         endtry
1679     endif
1680     if !path.isDirectory
1681         let path = path.getParent()
1682     endif
1684     "if instructed to, then change the vim CWD to the dir the NERDTree is
1685     "inited in
1686     if g:NERDTreeChDirMode != 0
1687         exec 'cd ' . path.strForCd()
1688     endif
1690     let t:treeShowHelp = 0
1691     let t:NERDTreeIgnoreEnabled = 1
1692     let t:NERDTreeShowFiles = g:NERDTreeShowFiles
1693     let t:NERDTreeShowHidden = g:NERDTreeShowHidden
1694     let t:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
1696     if s:treeExistsForTab()
1697         if s:isTreeOpen()
1698             call s:closeTree()
1699         endif
1700         unlet t:NERDTreeRoot
1701     endif
1703     let t:NERDTreeRoot = s:TreeDirNode.New(path)
1704     call t:NERDTreeRoot.open()
1706     call s:createTreeWin()
1707     call s:renderView()
1708     call s:putCursorOnNode(t:NERDTreeRoot, 0, 0)
1709 endfunction
1710 " Function: s:treeExistsForTab()   {{{2
1711 " Returns 1 if a nerd tree root exists in the current tab
1712 function! s:treeExistsForTab()
1713     return exists("t:NERDTreeRoot")
1714 endfunction
1715 " SECTION: Public Functions {{{1
1716 "============================================================
1717 "Returns the node that the cursor is currently on.
1719 "If the cursor is not in the NERDTree window, it is temporarily put there.
1721 "If no NERD tree window exists for the current tab, a NERDTree.NoTreeForTab
1722 "exception is thrown.
1724 "If the cursor is not on a node then an empty dictionary {} is returned.
1725 function! NERDTreeGetCurrentNode()
1726     if !s:treeExistsForTab() || !s:isTreeOpen()
1727         throw "NERDTree.NoTreeForTab exception: there is no NERD tree open for the current tab"
1728     endif
1730     let winnr = winnr()
1731     if winnr != s:getTreeWinNum()
1732         call s:putCursorInTreeWin()
1733     endif
1735     let treenode = s:getSelectedNode()
1737     if winnr != winnr()
1738         wincmd w
1739     endif
1741     return treenode
1742 endfunction
1744 "Returns the path object for the current node.
1746 "Subject to the same conditions as NERDTreeGetCurrentNode
1747 function! NERDTreeGetCurrentPath()
1748     let node = NERDTreeGetCurrentNode()
1749     if node != {}
1750         return node.path
1751     else
1752         return {}
1753     endif
1754 endfunction
1756 " SECTION: View Functions {{{1
1757 "============================================================
1758 "FUNCTION: s:centerView() {{{2
1759 "centers the nerd tree window around the cursor (provided the nerd tree
1760 "options permit)
1761 function! s:centerView()
1762     if g:NERDTreeAutoCenter
1763         let current_line = winline()
1764         let lines_to_top = current_line
1765         let lines_to_bottom = winheight(s:getTreeWinNum()) - current_line
1766         if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold
1767             normal! zz
1768         endif
1769     endif
1770 endfunction
1771 "FUNCTION: s:closeTree() {{{2
1772 "Closes the NERD tree window
1773 function! s:closeTree()
1774     if !s:isTreeOpen()
1775         throw "NERDTree.view.closeTree exception: no NERDTree is open"
1776     endif
1778     if winnr("$") != 1
1779         execute s:getTreeWinNum() . " wincmd w"
1780         close
1781         execute "wincmd p"
1782     else
1783         :q
1784     endif
1785 endfunction
1787 "FUNCTION: s:closeTreeIfOpen() {{{2
1788 "Closes the NERD tree window if it is open
1789 function! s:closeTreeIfOpen()
1790    if s:isTreeOpen()
1791       call s:closeTree()
1792    endif
1793 endfunction
1794 "FUNCTION: s:closeTreeIfQuitOnOpen() {{{2
1795 "Closes the NERD tree window if the close on open option is set
1796 function! s:closeTreeIfQuitOnOpen()
1797     if g:NERDTreeQuitOnOpen
1798         call s:closeTree()
1799     endif
1800 endfunction
1801 "FUNCTION: s:createTreeWin() {{{2
1802 "Inits the NERD tree window. ie. opens it, sizes it, sets all the local
1803 "options etc
1804 function! s:createTreeWin()
1805     "create the nerd tree window
1806     let splitLocation = (g:NERDTreeWinPos == "top" || g:NERDTreeWinPos == "left") ? "topleft " : "botright "
1807     let splitMode = s:shouldSplitVertically() ? "vertical " : ""
1808     let splitSize = g:NERDTreeWinSize
1809     let t:NERDTreeWinName = localtime() . s:NERDTreeWinName
1810     let cmd = splitLocation . splitMode . splitSize . ' new ' . t:NERDTreeWinName
1811     silent! execute cmd
1813     setlocal winfixwidth
1815     "throwaway buffer options
1816     setlocal noswapfile
1817     setlocal buftype=nofile
1818     setlocal bufhidden=delete
1819     setlocal nowrap
1820     setlocal foldcolumn=0
1821     setlocal nobuflisted
1822     setlocal nospell
1823     if g:NERDTreeShowLineNumbers
1824         setlocal nu
1825     else
1826         setlocal nonu
1827     endif
1829     iabc <buffer>
1831     if g:NERDTreeHighlightCursorline
1832         setlocal cursorline
1833     endif
1837     call s:bindMappings()
1838     setfiletype nerdtree
1839     " syntax highlighting
1840     if has("syntax") && exists("g:syntax_on") && !has("syntax_items")
1841         call s:setupSyntaxHighlighting()
1842     endif
1843 endfunction
1845 "FUNCTION: s:drawTree {{{2
1846 "Draws the given node recursively
1848 "Args:
1849 "curNode: the node that is being rendered with this call
1850 "depth: the current depth in the tree for this call
1851 "drawText: 1 if we should actually draw the line for this node (if 0 then the
1852 "child nodes are rendered only)
1853 "vertMap: a binary array that indicates whether a vertical bar should be draw
1854 "for each depth in the tree
1855 "isLastChild:true if this curNode is the last child of its parent
1856 function! s:drawTree(curNode, depth, drawText, vertMap, isLastChild)
1857     if a:drawText == 1
1859         let treeParts = ''
1861         "get all the leading spaces and vertical tree parts for this line
1862         if a:depth > 1
1863             for j in a:vertMap[0:-2]
1864                 if j == 1
1865                     let treeParts = treeParts . '| '
1866                 else
1867                     let treeParts = treeParts . '  '
1868                 endif
1869             endfor
1870         endif
1872         "get the last vertical tree part for this line which will be different
1873         "if this node is the last child of its parent
1874         if a:isLastChild
1875             let treeParts = treeParts . '`'
1876         else
1877             let treeParts = treeParts . '|'
1878         endif
1881         "smack the appropriate dir/file symbol on the line before the file/dir
1882         "name itself
1883         if a:curNode.path.isDirectory
1884             if a:curNode.isOpen
1885                 let treeParts = treeParts . '~'
1886             else
1887                 let treeParts = treeParts . '+'
1888             endif
1889         else
1890             let treeParts = treeParts . '-'
1891         endif
1892         let line = treeParts . a:curNode.strDisplay()
1894         call setline(line(".")+1, line)
1895         call cursor(line(".")+1, col("."))
1896     endif
1898     "if the node is an open dir, draw its children
1899     if a:curNode.path.isDirectory == 1 && a:curNode.isOpen == 1
1901         let childNodesToDraw = a:curNode.getVisibleChildren()
1902         if len(childNodesToDraw) > 0
1904             "draw all the nodes children except the last
1905             let lastIndx = len(childNodesToDraw)-1
1906             if lastIndx > 0
1907                 for i in childNodesToDraw[0:lastIndx-1]
1908                     call s:drawTree(i, a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
1909                 endfor
1910             endif
1912             "draw the last child, indicating that it IS the last
1913             call s:drawTree(childNodesToDraw[lastIndx], a:depth + 1, 1, add(copy(a:vertMap), 0), 1)
1914         endif
1915     endif
1916 endfunction
1919 "FUNCTION: s:dumpHelp  {{{2
1920 "prints out the quick help
1921 function! s:dumpHelp()
1922     let old_h = @h
1923     if t:treeShowHelp == 1
1924         let @h=   "\" NERD tree (" . s:NERD_tree_version . ") quickhelp~\n"
1925         let @h=@h."\" ============================\n"
1926         let @h=@h."\" File node mappings~\n"
1927         let @h=@h."\" ". (g:NERDTreeMouseMode == 3 ? "single" : "double") ."-click,\n"
1928         let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n"
1929         let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n"
1930         let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
1931         let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
1932         let @h=@h."\" middle-click,\n"
1933         let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n"
1934         let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n"
1935         let @h=@h."\" ". g:NERDTreeMapExecute.": Execute file\n"
1937         let @h=@h."\"\n\" ----------------------------\n"
1938         let @h=@h."\" Directory node mappings~\n"
1939         let @h=@h."\" ". (g:NERDTreeMouseMode == 1 ? "double" : "single") ."-click,\n"
1940         let @h=@h."\" ". g:NERDTreeMapActivateNode .": open & close node\n"
1941         let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n"
1942         let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n"
1943         let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n"
1944         let @h=@h."\"    current node recursively\n"
1945         let @h=@h."\" middle-click,\n"
1946         let @h=@h."\" ". g:NERDTreeMapOpenExpl.": Open netrw for selected\n"
1947         let @h=@h."\"    node\n"
1949         let @h=@h."\"\n\" ----------------------------\n"
1950         let @h=@h."\" Bookmark table mappings~\n"
1951         let @h=@h."\" double-click,\n"
1952         let @h=@h."\" ". g:NERDTreeMapActivateNode .": open bookmark\n"
1953         let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
1954         let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
1955         let @h=@h."\" ". g:NERDTreeMapDeleteBookmark .": delete bookmark\n"
1957         let @h=@h."\"\n\" ----------------------------\n"
1958         let @h=@h."\" Tree navigation mappings~\n"
1959         let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n"
1960         let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n"
1961         let @h=@h."\" ". g:NERDTreeMapJumpFirstChild  .": go to first child\n"
1962         let @h=@h."\" ". g:NERDTreeMapJumpLastChild   .": go to last child\n"
1963         let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n"
1964         let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n"
1966         let @h=@h."\"\n\" ----------------------------\n"
1967         let @h=@h."\" Filesystem mappings~\n"
1968         let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n"
1969         let @h=@h."\"    selected dir\n"
1970         let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n"
1971         let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n"
1972         let @h=@h."\"    but leave old root open\n"
1973         let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n"
1974         let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n"
1975         let @h=@h."\" ". g:NERDTreeMapFilesystemMenu .": Show filesystem menu\n"
1976         let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n"
1977         let @h=@h."\"    selected dir\n"
1979         let @h=@h."\"\n\" ----------------------------\n"
1980         let @h=@h."\" Tree filtering mappings~\n"
1981         let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (t:NERDTreeShowHidden ? "on" : "off") . ")\n"
1982         let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (t:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n"
1983         let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (t:NERDTreeShowFiles ? "on" : "off") . ")\n"
1984         let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (t:NERDTreeShowBookmarks ? "on" : "off") . ")\n"
1986         let @h=@h."\"\n\" ----------------------------\n"
1987         let @h=@h."\" Other mappings~\n"
1988         let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n"
1989         let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n"
1990         let @h=@h."\"\n\" ----------------------------\n"
1991         let @h=@h."\" Bookmark commands~\n"
1992         let @h=@h."\" :Bookmark <name>\n"
1993         let @h=@h."\" :BookmarkToRoot <name>\n"
1994         let @h=@h."\" :RevealBookmark <name>\n"
1995         let @h=@h."\" :OpenBookmark <name>\n"
1996         let @h=@h."\" :ClearBookmarks [<names>]\n"
1997         let @h=@h."\" :ClearAllBookmarks\n"
1998     else
1999         let @h="\" Press ". g:NERDTreeMapHelp ." for help\n"
2000     endif
2002     silent! put h
2004     let @h = old_h
2005 endfunction
2006 "FUNCTION: s:echo  {{{2
2007 "A wrapper for :echo. Appends 'NERDTree:' on the front of all messages
2009 "Args:
2010 "msg: the message to echo
2011 function! s:echo(msg)
2012     redraw
2013     echomsg "NERDTree: " . a:msg
2014 endfunction
2015 "FUNCTION: s:echoWarning {{{2
2016 "Wrapper for s:echo, sets the message type to warningmsg for this message
2017 "Args:
2018 "msg: the message to echo
2019 function! s:echoWarning(msg)
2020     echohl warningmsg
2021     call s:echo(a:msg)
2022     echohl normal
2023 endfunction
2024 "FUNCTION: s:echoError {{{2
2025 "Wrapper for s:echo, sets the message type to errormsg for this message
2026 "Args:
2027 "msg: the message to echo
2028 function! s:echoError(msg)
2029     echohl errormsg
2030     call s:echo(a:msg)
2031     echohl normal
2032 endfunction
2033 "FUNCTION: s:findNodeLineNumber(treenode){{{2
2034 "Finds the line number for the given tree node
2036 "Args:
2037 "treenode: the node to find the line no. for
2038 function! s:findNodeLineNumber(treenode)
2039     "if the node is the root then return the root line no.
2040     if a:treenode.isRoot()
2041         return s:findRootNodeLineNumber()
2042     endif
2044     let totalLines = line("$")
2046     "the path components we have matched so far
2047     let pathcomponents = [substitute(t:NERDTreeRoot.path.str(0), '/ *$', '', '')]
2048     "the index of the component we are searching for
2049     let curPathComponent = 1
2051     let fullpath = a:treenode.path.str(0)
2054     let lnum = s:findRootNodeLineNumber()
2055     while lnum > 0
2056         let lnum = lnum + 1
2057         "have we reached the bottom of the tree?
2058         if lnum == totalLines+1
2059             return -1
2060         endif
2062         let curLine = getline(lnum)
2064         let indent = match(curLine,s:tree_markup_reg_neg) / s:tree_wid
2065         if indent == curPathComponent
2066             let curLine = s:stripMarkupFromLine(curLine, 1)
2068             let curPath =  join(pathcomponents, '/') . '/' . curLine
2069             if stridx(fullpath, curPath, 0) == 0
2070                 if fullpath == curPath || strpart(fullpath, len(curPath)-1,1) == '/'
2071                     let curLine = substitute(curLine, '/ *$', '', '')
2072                     call add(pathcomponents, curLine)
2073                     let curPathComponent = curPathComponent + 1
2075                     if fullpath == curPath
2076                         return lnum
2077                     endif
2078                 endif
2079             endif
2080         endif
2081     endwhile
2082     return -1
2083 endfunction
2085 "FUNCTION: s:findRootNodeLineNumber(){{{2
2086 "Finds the line number of the root node
2087 function! s:findRootNodeLineNumber()
2088     let rootLine = 1
2089     while getline(rootLine) !~ '^/'
2090         let rootLine = rootLine + 1
2091     endwhile
2092     return rootLine
2093 endfunction
2095 "FUNCTION: s:getPath(ln) {{{2
2096 "Gets the full path to the node that is rendered on the given line number
2098 "Args:
2099 "ln: the line number to get the path for
2101 "Return:
2102 "A path if a node was selected, {} if nothing is selected.
2103 "If the 'up a dir' line was selected then the path to the parent of the
2104 "current root is returned
2105 function! s:getPath(ln)
2106     let line = getline(a:ln)
2108     "check to see if we have the root node
2109     if line =~ '^\/'
2110         return t:NERDTreeRoot.path
2111     endif
2113     " in case called from outside the tree
2114     if line !~ '^ *[|`]' || line =~ '^$'
2115         return {}
2116     endif
2118     if line == s:tree_up_dir_line
2119         return t:NERDTreeRoot.path.getParent()
2120     endif
2122     "get the indent level for the file (i.e. how deep in the tree it is)
2123     let indent = match(line, s:tree_markup_reg_neg) / s:tree_wid
2126     "remove the tree parts and the leading space
2127     let curFile = s:stripMarkupFromLine(line, 0)
2129     let wasdir = 0
2130     if curFile =~ '/$'
2131         let wasdir = 1
2132         let curFile = substitute(curFile, '/\?$', '/', "")
2133     endif
2136     let dir = ""
2137     let lnum = a:ln
2138     while lnum > 0
2139         let lnum = lnum - 1
2140         let curLine = getline(lnum)
2141         let curLineStripped = s:stripMarkupFromLine(curLine, 1)
2143         "have we reached the top of the tree?
2144         if curLine =~ '^/'
2145             let dir = substitute (curLine, ' *$', "", "") . dir
2146             break
2147         endif
2148         if curLineStripped =~ '/$'
2149             let lpindent = match(curLine,s:tree_markup_reg_neg) / s:tree_wid
2150             if lpindent < indent
2151                 let indent = indent - 1
2153                 let dir = substitute (curLineStripped,'^\\', "", "") . dir
2154                 continue
2155             endif
2156         endif
2157     endwhile
2158     let curFile = t:NERDTreeRoot.path.drive . dir . curFile
2159     let toReturn = s:Path.New(curFile)
2160     return toReturn
2161 endfunction
2163 "FUNCTION: s:getSelectedBookmark() {{{2
2164 "returns the bookmark the cursor is over in the bookmarks table or {}
2165 function! s:getSelectedBookmark()
2166     let line = getline(".")
2167     let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
2168     if name != line
2169         try
2170             return s:Bookmark.BookmarkFor(name)
2171         catch /NERDTree.BookmarkNotFound/
2172             return {}
2173         endtry
2174     endif
2175     return {}
2176 endfunction
2178 "FUNCTION: s:getSelectedDir() {{{2
2179 "Returns the current node if it is a dir node, or else returns the current
2180 "nodes parent
2181 function! s:getSelectedDir()
2182     let currentDir = s:getSelectedNode()
2183     if currentDir != {} && !currentDir.isRoot()
2184         if currentDir.path.isDirectory == 0
2185             let currentDir = currentDir.parent
2186         endif
2187     endif
2188     return currentDir
2189 endfunction
2190 "FUNCTION: s:getSelectedNode() {{{2
2191 "gets the treenode that the cursor is currently over
2192 function! s:getSelectedNode()
2193     try
2194         let path = s:getPath(line("."))
2195         if path == {}
2196             return {}
2197         endif
2198         return t:NERDTreeRoot.findNode(path)
2199     catch /^NERDTree/
2200         return {}
2201     endtry
2202 endfunction
2203 "FUNCTION: s:getTreeWinNum() {{{2
2204 "gets the nerd tree window number for this tab
2205 function! s:getTreeWinNum()
2206     if exists("t:NERDTreeWinName")
2207         return bufwinnr(t:NERDTreeWinName)
2208     else
2209         return -1
2210     endif
2211 endfunction
2213 "FUNCTION: s:isTreeOpen() {{{2
2214 function! s:isTreeOpen()
2215     return s:getTreeWinNum() != -1
2216 endfunction
2218 " FUNCTION: s:jumpToChild(direction) {{{2
2219 " Args:
2220 " direction: 0 if going to first child, 1 if going to last
2221 function! s:jumpToChild(direction)
2222     let currentNode = s:getSelectedNode()
2223     if currentNode == {} || currentNode.isRoot()
2224         call s:echo("cannot jump to " . (a:direction ? "last" : "first") .  " child")
2225         return
2226     end
2227     let dirNode = currentNode.parent
2228     let childNodes = dirNode.getVisibleChildren()
2230     let targetNode = childNodes[0]
2231     if a:direction
2232         let targetNode = childNodes[len(childNodes) - 1]
2233     endif
2235     if targetNode.equals(currentNode)
2236         let siblingDir = currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction)
2237         if siblingDir != {}
2238             let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0
2239             let targetNode = siblingDir.getChildByIndex(indx, 1)
2240         endif
2241     endif
2243     call s:putCursorOnNode(targetNode, 1, 0)
2245     call s:centerView()
2246 endfunction
2249 "FUNCTION: s:openDirNodeSplit(treenode) {{{2
2250 "Open the file represented by the given node in a new window.
2251 "No action is taken for file nodes
2253 "ARGS:
2254 "treenode: file node to open
2255 function! s:openDirNodeSplit(treenode)
2256     if a:treenode.path.isDirectory == 1
2257         call s:openNodeSplit(a:treenode)
2258     endif
2259 endfunction
2261 " FUNCTION: s:openExplorerFor(treenode) {{{2
2262 " opens a netrw window for the given dir treenode
2263 function! s:openExplorerFor(treenode)
2264     let oldwin = winnr()
2265     wincmd p
2266     if oldwin == winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2)
2267         wincmd p
2268         call s:openDirNodeSplit(a:treenode)
2269     else
2270         exec ("silent edit " . a:treenode.path.strForEditCmd())
2271     endif
2272 endfunction
2273 "FUNCTION: s:openFileNode(treenode) {{{2
2274 "Open the file represented by the given node in the current window, splitting
2275 "the window if needed
2277 "ARGS:
2278 "treenode: file node to open
2279 function! s:openFileNode(treenode)
2280     call s:putCursorInTreeWin()
2282     "if the file is already open in this tab then just stick the cursor in it
2283     let winnr = bufwinnr('^' . a:treenode.path.strForOS(0) . '$')
2284     if winnr != -1
2285         exec winnr . "wincmd w"
2287     elseif s:shouldSplitToOpen(winnr("#"))
2288         call s:openFileNodeSplit(a:treenode)
2289     else
2290         try
2291             wincmd p
2292             exec ("edit " . a:treenode.path.strForEditCmd())
2293         catch /^Vim\%((\a\+)\)\=:E37/
2294             call s:putCursorInTreeWin()
2295             call s:echo("Cannot open file, it is already open and modified")
2296         catch /^Vim\%((\a\+)\)\=:/
2297             echo v:exception
2298         endtry
2299     endif
2300 endfunction
2302 "FUNCTION: s:openFileNodeSplit(treenode) {{{2
2303 "Open the file represented by the given node in a new window.
2304 "No action is taken for dir nodes
2306 "ARGS:
2307 "treenode: file node to open
2308 function! s:openFileNodeSplit(treenode)
2309     if a:treenode.path.isDirectory == 0
2310         try
2311             call s:openNodeSplit(a:treenode)
2312         catch /^NERDTree.view.FileOpen/
2313             call s:echo("Cannot open file, it is already open and modified" )
2314         endtry
2315     endif
2316 endfunction
2318 "FUNCTION: s:openNodeSplit(treenode) {{{2
2319 "Open the file/dir represented by the given node in a new window
2321 "ARGS:
2322 "treenode: file node to open
2323 function! s:openNodeSplit(treenode)
2324     call s:putCursorInTreeWin()
2326     " Save the user's settings for splitbelow and splitright
2327     let savesplitbelow=&splitbelow
2328     let savesplitright=&splitright
2330     " Figure out how to do the split based on the user's preferences.
2331     " We want to split to the (left,right,top,bottom) of the explorer
2332     " window, but we want to extract the screen real-estate from the
2333     " window next to the explorer if possible.
2334     "
2335     " 'there' will be set to a command to move from the split window
2336     " back to the explorer window
2337     "
2338     " 'back' will be set to a command to move from the explorer window
2339     " back to the newly split window
2340     "
2341     " 'right' and 'below' will be set to the settings needed for
2342     " splitbelow and splitright IF the explorer is the only window.
2343     "
2344     if s:shouldSplitVertically()
2345         let there= g:NERDTreeWinPos == "left" ? "wincmd h" : "wincmd l"
2346         let back = g:NERDTreeWinPos == "left" ? "wincmd l" : "wincmd h"
2347         let right= g:NERDTreeWinPos == "left"
2348         let below=0
2349     else
2350         let there= g:NERDTreeWinPos == "top" ? "wincmd k" : "wincmd j"
2351         let back = g:NERDTreeWinPos == "top" ? "wincmd j" : "wincmd k"
2352         let below= g:NERDTreeWinPos == "top"
2353         let right=0
2354     endif
2356     " Attempt to go to adjacent window
2357     exec(back)
2359     let onlyOneWin = (winnr() == s:getTreeWinNum())
2361     " If no adjacent window, set splitright and splitbelow appropriately
2362     if onlyOneWin
2363         let &splitright=right
2364         let &splitbelow=below
2365     else
2366         " found adjacent window - invert split direction
2367         let &splitright=!right
2368         let &splitbelow=!below
2369     endif
2371     " Create a variable to use if splitting vertically
2372     let splitMode = ""
2373     if (onlyOneWin && s:shouldSplitVertically()) || (!onlyOneWin && !s:shouldSplitVertically())
2374         let splitMode = "vertical"
2375     endif
2377     echomsg splitMode
2379     " Open the new window
2380     try
2381         exec(splitMode." sp " . a:treenode.path.strForEditCmd())
2382     catch /^Vim\%((\a\+)\)\=:E37/
2383         call s:putCursorInTreeWin()
2384         throw "NERDTree.view.FileOpen exception: ". a:treenode.path.str(0) ." is already open and modified."
2385     catch /^Vim\%((\a\+)\)\=:/
2386         "do nothing
2387     endtry
2389     "resize the tree window if no other window was open before
2390     if onlyOneWin
2391         let size = exists("t:NERDTreeOldWindowSize") ? t:NERDTreeOldWindowSize : g:NERDTreeWinSize
2392         exec(there)
2393         exec("silent ". splitMode ." resize ". size)
2394         wincmd p
2395     endif
2397     " Restore splitmode settings
2398     let &splitbelow=savesplitbelow
2399     let &splitright=savesplitright
2400 endfunction
2402 "FUNCTION: s:promptToDelBuffer(bufnum, msg){{{2
2403 "prints out the given msg and, if the user responds by pushing 'y' then the
2404 "buffer with the given bufnum is deleted
2406 "Args:
2407 "bufnum: the buffer that may be deleted
2408 "msg: a message that will be echoed to the user asking them if they wish to
2409 "     del the buffer
2410 function! s:promptToDelBuffer(bufnum, msg)
2411     echo a:msg
2412     if nr2char(getchar()) == 'y'
2413         exec "silent bdelete! " . a:bufnum
2414     endif
2415 endfunction
2417 "FUNCTION: s:putCursorOnBookmarkTable(){{{2
2418 "Places the cursor at the top of the bookmarks table
2419 function! s:putCursorOnBookmarkTable()
2420     if !t:NERDTreeShowBookmarks
2421         throw "NERDTree.IllegalOperation exception: cant find bookmark table, bookmarks arent active"
2422     endif
2424     let rootNodeLine = s:findRootNodeLineNumber()
2426     let line = 1
2427     while getline(line) !~ '^>-\+Bookmarks-\+$'
2428         let line = line + 1
2429         if line >= rootNodeLine
2430             throw "NERDTree.BookmarkTableNotFound exception: didnt find the bookmarks table"
2431         endif
2432     endwhile
2433     call cursor(line, 0)
2434 endfunction
2436 "FUNCTION: s:putCursorOnNode(treenode, isJump, recurseUpward){{{2
2437 "Places the cursor on the line number representing the given node
2439 "Args:
2440 "treenode: the node to put the cursor on
2441 "isJump: 1 if this cursor movement should be counted as a jump by vim
2442 "recurseUpward: try to put the cursor on the parent if the this node isnt
2443 "visible
2444 function! s:putCursorOnNode(treenode, isJump, recurseUpward)
2445     let ln = s:findNodeLineNumber(a:treenode)
2446     if ln != -1
2447         if a:isJump
2448             mark '
2449         endif
2450         call cursor(ln, col("."))
2451     else
2452         if a:recurseUpward
2453             let node = a:treenode
2454             while s:findNodeLineNumber(node) == -1 && node != {}
2455                 let node = node.parent
2456                 call node.open()
2457             endwhile
2458             call s:renderView()
2459             call s:putCursorOnNode(a:treenode, a:isJump, 0)
2460         endif
2461     endif
2462 endfunction
2464 "FUNCTION: s:putCursorInTreeWin(){{{2
2465 "Places the cursor in the nerd tree window
2466 function! s:putCursorInTreeWin()
2467     if !s:isTreeOpen()
2468         throw "NERDTree.view.InvalidOperation Exception: No NERD tree window exists"
2469     endif
2471     exec s:getTreeWinNum() . "wincmd w"
2472 endfunction
2474 "FUNCTION: s:renderBookmarks {{{2
2475 function! s:renderBookmarks()
2477     call setline(line(".")+1, ">----------Bookmarks----------")
2478     call cursor(line(".")+1, col("."))
2480     for i in s:Bookmark.Bookmarks()
2481         call setline(line(".")+1, i.str())
2482         call cursor(line(".")+1, col("."))
2483     endfor
2485     call setline(line(".")+1, '')
2486     call cursor(line(".")+1, col("."))
2487 endfunction
2488 "FUNCTION: s:renderView {{{2
2489 "The entry function for rendering the tree. Renders the root then calls
2490 "s:drawTree to draw the children of the root
2492 "Args:
2493 function! s:renderView()
2494     execute s:getTreeWinNum() . "wincmd w"
2496     setlocal modifiable
2498     "remember the top line of the buffer and the current line so we can
2499     "restore the view exactly how it was
2500     let curLine = line(".")
2501     let curCol = col(".")
2502     let topLine = line("w0")
2504     "delete all lines in the buffer (being careful not to clobber a register)
2505     silent 1,$delete _
2507     call s:dumpHelp()
2509     "delete the blank line before the help and add one after it
2510     call setline(line(".")+1, "")
2511     call cursor(line(".")+1, col("."))
2513     if t:NERDTreeShowBookmarks
2514         call s:renderBookmarks()
2515     endif
2517     "add the 'up a dir' line
2518     call setline(line(".")+1, s:tree_up_dir_line)
2519     call cursor(line(".")+1, col("."))
2521     "draw the header line
2522     call setline(line(".")+1, t:NERDTreeRoot.path.str(0))
2523     call cursor(line(".")+1, col("."))
2525     "draw the tree
2526     call s:drawTree(t:NERDTreeRoot, 0, 0, [], t:NERDTreeRoot.getChildCount() == 1)
2528     "delete the blank line at the top of the buffer
2529     silent 1,1delete _
2531     "restore the view
2532     let old_scrolloff=&scrolloff
2533     let &scrolloff=0
2534     call cursor(topLine, 1)
2535     normal! zt
2536     call cursor(curLine, curCol)
2537     let &scrolloff = old_scrolloff
2539     setlocal nomodifiable
2540 endfunction
2542 "FUNCTION: s:renderViewSavingPosition {{{2
2543 "Renders the tree and ensures the cursor stays on the current node or the
2544 "current nodes parent if it is no longer available upon re-rendering
2545 function! s:renderViewSavingPosition()
2546     let currentNode = s:getSelectedNode()
2548     "go up the tree till we find a node that will be visible or till we run
2549     "out of nodes
2550     while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
2551         let currentNode = currentNode.parent
2552     endwhile
2554     call s:renderView()
2556     if currentNode != {}
2557         call s:putCursorOnNode(currentNode, 0, 0)
2558     endif
2559 endfunction
2560 "FUNCTION: s:restoreScreenState() {{{2
2562 "Sets the screen state back to what it was when s:saveScreenState was last
2563 "called.
2565 "Assumes the cursor is in the NERDTree window
2566 function! s:restoreScreenState()
2567     if !exists("t:NERDTreeOldTopLine") || !exists("t:NERDTreeOldPos") || !exists("t:NERDTreeOldWindowSize")
2568         return
2569     endif
2570     exec("silent ". (s:shouldSplitVertically() ? "vertical" : "") ." resize ".t:NERDTreeOldWindowSize)
2572     let old_scrolloff=&scrolloff
2573     let &scrolloff=0
2574     call cursor(t:NERDTreeOldTopLine, 0)
2575     normal! zt
2576     call setpos(".", t:NERDTreeOldPos)
2577     let &scrolloff=old_scrolloff
2578 endfunction
2580 "FUNCTION: s:saveScreenState() {{{2
2581 "Saves the current cursor position in the current buffer and the window
2582 "scroll position
2583 function! s:saveScreenState()
2584     let win = winnr()
2585     call s:putCursorInTreeWin()
2586     let t:NERDTreeOldPos = getpos(".")
2587     let t:NERDTreeOldTopLine = line("w0")
2588     let t:NERDTreeOldWindowSize = s:shouldSplitVertically() ? winwidth("") : winheight("")
2589     exec win . "wincmd w"
2590 endfunction
2592 "FUNCTION: s:setupSyntaxHighlighting() {{{2
2593 function! s:setupSyntaxHighlighting()
2594     "treeFlags are syntax items that should be invisible, but give clues as to
2595     "how things should be highlighted
2596     syn match treeFlag #\~#
2597     syn match treeFlag #\[RO\]#
2599     "highlighting for the .. (up dir) line at the top of the tree
2600     execute "syn match treeUp #". s:tree_up_dir_line ."#"
2602     "highlighting for the ~/+ symbols for the directory nodes
2603     syn match treeClosable #\~\<#
2604     syn match treeClosable #\~\.#
2605     syn match treeOpenable #+\<#
2606     syn match treeOpenable #+\.#he=e-1
2608     "highlighting for the tree structural parts
2609     syn match treePart #|#
2610     syn match treePart #`#
2611     syn match treePartFile #[|`]-#hs=s+1 contains=treePart
2613     "quickhelp syntax elements
2614     syn match treeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1
2615     syn match treeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1
2616     syn match treeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=treeFlag
2617     syn match treeToggleOn #".*(on)#hs=e-2,he=e-1 contains=treeHelpKey
2618     syn match treeToggleOff #".*(off)#hs=e-3,he=e-1 contains=treeHelpKey
2619     syn match treeHelpCommand #" :.\{-}\>#hs=s+3
2620     syn match treeHelp  #^".*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn,treeHelpCommand
2622     "highlighting for readonly files
2623     syn match treeRO #[\/0-9a-zA-Z]\+.*\[RO\]# contains=treeFlag,treeBookmark
2625     "highlighting for sym links
2626     syn match treeLink #[^-| `].* -> # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash
2628     "highlighing for directory nodes and file nodes
2629     syn match treeDirSlash #/#
2630     syn match treeDir #[^-| `].*/# contains=treeLink,treeDirSlash,treeOpenable,treeClosable
2631     syn match treeExecFile  #[|`]-.*\*\($\| \)# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark
2632     syn match treeFile  #|-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
2633     syn match treeFile  #`-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
2634     syn match treeCWD #^/.*$#
2636     "highlighting for bookmarks
2637     syn match treeBookmark # {.*}#hs=s+1
2639     "highlighting for the bookmarks table
2640     syn match treeBookmarksLeader #^>#
2641     syn match treeBookmarksHeader #^>-\+Bookmarks-\+$# contains=treeBookmarksLeader
2642     syn match treeBookmarkName #^>.\{-} #he=e-1 contains=treeBookmarksLeader
2643     syn match treeBookmark #^>.*$# contains=treeBookmarksLeader,treeBookmarkName,treeBookmarksHeader
2645     if g:NERDChristmasTree
2646         hi def link treePart Special
2647         hi def link treePartFile Type
2648         hi def link treeFile Normal
2649         hi def link treeExecFile Title
2650         hi def link treeDirSlash Identifier
2651         hi def link treeClosable Type
2652     else
2653         hi def link treePart Normal
2654         hi def link treePartFile Normal
2655         hi def link treeFile Normal
2656         hi def link treeClosable Title
2657     endif
2659     hi def link treeBookmarksHeader statement
2660     hi def link treeBookmarksLeader ignore
2661     hi def link treeBookmarkName Identifier
2662     hi def link treeBookmark normal
2664     hi def link treeHelp String
2665     hi def link treeHelpKey Identifier
2666     hi def link treeHelpCommand Identifier
2667     hi def link treeHelpTitle Macro
2668     hi def link treeToggleOn Question
2669     hi def link treeToggleOff WarningMsg
2671     hi def link treeDir Directory
2672     hi def link treeUp Directory
2673     hi def link treeCWD Statement
2674     hi def link treeLink Macro
2675     hi def link treeOpenable Title
2676     hi def link treeFlag ignore
2677     hi def link treeRO WarningMsg
2678     hi def link treeBookmark Statement
2680     hi def link NERDTreeCurrentNode Search
2681 endfunction
2683 "FUNCTION: s:shouldSplitToOpen() {{{2
2684 "Returns 1 if opening a file from the tree in the given window requires it to
2685 "be split
2687 "Args:
2688 "winnumber: the number of the window in question
2689 function! s:shouldSplitToOpen(winnumber)
2690     "gotta split if theres only one window (i.e. the NERD tree)
2691     if winnr("$") == 1
2692         return 1
2693     endif
2695     let oldwinnr = winnr()
2696     exec a:winnumber . "wincmd p"
2697     let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow')
2698     let modified = &modified
2699     exec oldwinnr . "wincmd p"
2701     "if its a special window e.g. quickfix or another explorer plugin then we
2702     "have to split
2703     if specialWindow
2704         return 1
2705     endif
2707     if &hidden
2708         return 0
2709     endif
2711     return modified && s:bufInWindows(winbufnr(a:winnumber)) < 2
2712 endfunction
2714 " Function: s:shouldSplitVertically()   {{{2
2715 " Returns 1 if g:NERDTreeWinPos is 'left' or 'right'
2716 function! s:shouldSplitVertically()
2717     return g:NERDTreeWinPos == 'left' || g:NERDTreeWinPos == 'right'
2718 endfunction
2719 "FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2
2720 "returns the given line with all the tree parts stripped off
2722 "Args:
2723 "line: the subject line
2724 "removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces =
2725 "any spaces before the actual text of the node)
2726 function! s:stripMarkupFromLine(line, removeLeadingSpaces)
2727     let line = a:line
2728     "remove the tree parts and the leading space
2729     let line = substitute (line,"^" . s:tree_markup_reg . "*","","")
2731     "strip off any read only flag
2732     let line = substitute (line, ' \[RO\]', "","")
2734     "strip off any bookmark flags
2735     let line = substitute (line, ' {[^}]*}', "","")
2737     "strip off any executable flags
2738     let line = substitute (line, '*\ze\($\| \)', "","")
2740     let wasdir = 0
2741     if line =~ '/$'
2742         let wasdir = 1
2743     endif
2744     let line = substitute (line,' -> .*',"","") " remove link to
2745     if wasdir == 1
2746         let line = substitute (line, '/\?$', '/', "")
2747     endif
2749     if a:removeLeadingSpaces
2750         let line = substitute (line, '^ *', '', '')
2751     endif
2753     return line
2754 endfunction
2756 "FUNCTION: s:toggle(dir) {{{2
2757 "Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is
2758 "closed it is restored or initialized (if it doesnt exist)
2760 "Args:
2761 "dir: the full path for the root node (is only used if the NERD tree is being
2762 "initialized.
2763 function! s:toggle(dir)
2764     if s:treeExistsForTab()
2765         if !s:isTreeOpen()
2766             call s:createTreeWin()
2767             call s:renderView()
2769             call s:restoreScreenState()
2770         else
2771             call s:closeTree()
2772         endif
2773     else
2774         call s:initNerdTree(a:dir)
2775     endif
2776 endfunction
2777 "SECTION: Interface bindings {{{1
2778 "============================================================
2779 "FUNCTION: s:activateNode(forceKeepWindowOpen) {{{2
2780 "If the current node is a file, open it in the previous window (or a new one
2781 "if the previous is modified). If it is a directory then it is opened.
2783 "args:
2784 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
2785 function! s:activateNode(forceKeepWindowOpen)
2786     if getline(".") == s:tree_up_dir_line
2787         return s:upDir(0)
2788     endif
2790     let treenode = s:getSelectedNode()
2791     if treenode != {}
2792         if treenode.path.isDirectory
2793             call treenode.toggleOpen()
2794             call s:renderView()
2795             call s:putCursorOnNode(treenode, 0, 0)
2796         else
2797             call s:openFileNode(treenode)
2798             if !a:forceKeepWindowOpen
2799                 call s:closeTreeIfQuitOnOpen()
2800             end
2801         endif
2802     else
2803         let bookmark = s:getSelectedBookmark()
2804         if !empty(bookmark)
2805             if bookmark.path.isDirectory
2806                 call bookmark.toRoot()
2807             else
2808                 if bookmark.validate()
2809                     call s:openFileNode(s:TreeFileNode.New(bookmark.path))
2810                 endif
2811             endif
2812         endif
2813     endif
2814 endfunction
2816 "FUNCTION: s:bindMappings() {{{2
2817 function! s:bindMappings()
2818     " set up mappings and commands for this buffer
2819     nnoremap <silent> <buffer> <middlerelease> :call <SID>handleMiddleMouse()<cr>
2820     nnoremap <silent> <buffer> <leftrelease> <leftrelease>:call <SID>checkForActivate()<cr>
2821     nnoremap <silent> <buffer> <2-leftmouse> :call <SID>activateNode(0)<cr>
2823     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapActivateNode . " :call <SID>activateNode(0)<cr>"
2824 "    exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenSplit ." :call <SID>openEntrySplit(0)<cr>"
2826     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreview ." :call <SID>previewNode(0)<cr>"
2827     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewSplit ." :call <SID>previewNode(1)<cr>"
2830     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapExecute ." :call <SID>executeNode()<cr>"
2832     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenRecursively ." :call <SID>openNodeRecursively()<cr>"
2834     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdirKeepOpen ." :call <SID>upDir(1)<cr>"
2835     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdir ." :call <SID>upDir(0)<cr>"
2836     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChangeRoot ." :call <SID>chRoot()<cr>"
2838     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChdir ." :call <SID>chCwd()<cr>"
2840     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapQuit ." :NERDTreeToggle<cr>"
2842     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefreshRoot ." :call <SID>refreshRoot()<cr>"
2843     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefresh ." :call <SID>refreshCurrent()<cr>"
2845     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapHelp ." :call <SID>displayHelp()<cr>"
2846     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleHidden ." :call <SID>toggleShowHidden()<cr>"
2847     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFilters ." :call <SID>toggleIgnoreFilter()<cr>"
2848     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFiles ." :call <SID>toggleShowFiles()<cr>"
2849     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleBookmarks ." :call <SID>toggleShowBookmarks()<cr>"
2851     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>"
2852     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>"
2854     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapFilesystemMenu ." :call <SID>showFileSystemMenu()<cr>"
2856     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>jumpToParent()<cr>"
2857     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>jumpToSibling(1)<cr>"
2858     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpPrevSibling ." :call <SID>jumpToSibling(0)<cr>"
2859     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpFirstChild ." :call <SID>jumpToFirstChild()<cr>"
2860     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpLastChild ." :call <SID>jumpToLastChild()<cr>"
2861     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpRoot ." :call <SID>jumpToRoot()<cr>"
2863     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>"
2864     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTabSilent ." :call <SID>openInNewTab(1)<cr>"
2866     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenExpl ." :call <SID>openExplorer()<cr>"
2868     exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>"
2870     command! -buffer -nargs=1 Bookmark :call <SID>bookmarkNode('<args>')
2871     command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call <SID>revealBookmark('<args>')
2872     command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call <SID>openBookmark('<args>')
2873     command! -buffer -complete=customlist,s:completeBookmarks -nargs=* ClearBookmarks call <SID>clearBookmarks('<args>')
2874     command! -buffer -complete=customlist,s:completeBookmarks -nargs=+ BookmarkToRoot call s:Bookmark.ToRoot('<args>')
2875     command! -buffer -nargs=0 ClearAllBookmarks call s:Bookmark.ClearAll() <bar> call <SID>renderView()
2876     command! -buffer -nargs=0 ReadBookmarks call s:Bookmark.CacheBookmarks(0) <bar> call <SID>renderView()
2877     command! -buffer -nargs=0 WriteBookmarks call s:Bookmark.Write()
2878 endfunction
2880 " FUNCTION: s:bookmarkNode(name) {{{2
2881 " Associate the current node with the given name
2882 function! s:bookmarkNode(name)
2883     let currentNode = s:getSelectedNode()
2884     if currentNode != {}
2885         try
2886             call currentNode.bookmark(a:name)
2887             call s:renderView()
2888         catch /NERDTree.IllegalBookmarkName/
2889             call s:echo("bookmark names must not contain spaces")
2890         endtry
2891     else
2892         call s:echo("select a node first")
2893     endif
2894 endfunction
2895 "FUNCTION: s:checkForActivate() {{{2
2896 "Checks if the click should open the current node, if so then activate() is
2897 "called (directories are automatically opened if the symbol beside them is
2898 "clicked)
2899 function! s:checkForActivate()
2900     let currentNode = s:getSelectedNode()
2901     if currentNode != {}
2902         let startToCur = strpart(getline(line(".")), 0, col("."))
2903         let char = strpart(startToCur, strlen(startToCur)-1, 1)
2905         "if they clicked a dir, check if they clicked on the + or ~ sign
2906         "beside it
2907         if currentNode.path.isDirectory
2908             let reg = '^' . s:tree_markup_reg .'*[~+]$'
2909             if startToCur =~ reg
2910                 call s:activateNode(0)
2911                 return
2912             endif
2913         endif
2915         if (g:NERDTreeMouseMode == 2 && currentNode.path.isDirectory) || g:NERDTreeMouseMode == 3
2916             if char !~ s:tree_markup_reg && startToCur !~ '\/$'
2917                 call s:activateNode(0)
2918                 return
2919             endif
2920         endif
2921     endif
2922 endfunction
2924 " FUNCTION: s:chCwd() {{{2
2925 function! s:chCwd()
2926     let treenode = s:getSelectedNode()
2927     if treenode == {}
2928         call s:echo("Select a node first")
2929         return
2930     endif
2932     try
2933         call treenode.path.changeToDir()
2934     catch /^NERDTree.Path.Change/
2935         call s:echoWarning("could not change cwd")
2936     endtry
2937 endfunction
2939 " FUNCTION: s:chRoot() {{{2
2940 " changes the current root to the selected one
2941 function! s:chRoot()
2942     let treenode = s:getSelectedNode()
2943     if treenode == {}
2944         call s:echo("Select a node first")
2945         return
2946     endif
2948     call treenode.makeRoot()
2949     call s:renderView()
2950     call s:putCursorOnNode(t:NERDTreeRoot, 0, 0)
2951 endfunction
2953 " FUNCTION: s:clearBookmarks(bookmarks) {{{2
2954 function! s:clearBookmarks(bookmarks)
2955     if a:bookmarks == ''
2956         let currentNode = s:getSelectedNode()
2957         if currentNode != {}
2958             call currentNode.clearBoomarks()
2959         endif
2960     else
2961         for name in split(a:bookmarks, ' ')
2962             let bookmark = s:Bookmark.BookmarkFor(name)
2963             call bookmark.delete()
2964         endfor
2965     endif
2966     call s:renderView()
2967 endfunction
2968 " FUNCTION: s:closeChildren() {{{2
2969 " closes all childnodes of the current node
2970 function! s:closeChildren()
2971     let currentNode = s:getSelectedDir()
2972     if currentNode == {}
2973         call s:echo("Select a node first")
2974         return
2975     endif
2977     call currentNode.closeChildren()
2978     call s:renderView()
2979     call s:putCursorOnNode(currentNode, 0, 0)
2980 endfunction
2981 " FUNCTION: s:closeCurrentDir() {{{2
2982 " closes the parent dir of the current node
2983 function! s:closeCurrentDir()
2984     let treenode = s:getSelectedNode()
2985     if treenode == {}
2986         call s:echo("Select a node first")
2987         return
2988     endif
2990     let parent = treenode.parent
2991     if parent.isRoot()
2992         call s:echo("cannot close tree root")
2993     else
2994         call treenode.parent.close()
2995         call s:renderView()
2996         call s:putCursorOnNode(treenode.parent, 0, 0)
2997     endif
2998 endfunction
3000 " FUNCTION: s:copyNode() {{{2
3001 function! s:copyNode()
3002     let currentNode = s:getSelectedNode()
3003     if currentNode == {}
3004         call s:echo("Put the cursor on a file node first")
3005         return
3006     endif
3008     let newNodePath = input("Copy the current node\n" .
3009                           \ "==========================================================\n" .
3010                           \ "Enter the new path to copy the node to:                   \n" .
3011                           \ "", currentNode.path.str(0))
3013     if newNodePath != ""
3014         "strip trailing slash
3015         let newNodePath = substitute(newNodePath, '\/$', '', '')
3017         let confirmed = 1
3018         if currentNode.path.copyingWillOverwrite(newNodePath)
3019             call s:echo("\nWarning: copying may overwrite files! Continue? (yN)")
3020             let choice = nr2char(getchar())
3021             let confirmed = choice == 'y'
3022         endif
3024         if confirmed
3025             try
3026                 let newNode = currentNode.copy(newNodePath)
3027                 call s:renderView()
3028                 call s:putCursorOnNode(newNode, 0, 0)
3029             catch /^NERDTree/
3030                 call s:echoWarning("Could not copy node")
3031             endtry
3032         endif
3033     else
3034         call s:echo("Copy aborted.")
3035     endif
3036     redraw
3037 endfunction
3039 " FUNCTION: s:deleteBookmark() {{{2
3040 " if the cursor is on a bookmark, prompt to delete
3041 function! s:deleteBookmark()
3042     let bookmark = s:getSelectedBookmark()
3043     if bookmark == {}
3044         call s:echo("Put the cursor on a bookmark")
3045         return
3046     endif
3048     echo  "Are you sure you wish to delete the bookmark:\n\"" . bookmark.name . "\" (yN):"
3050     if  nr2char(getchar()) == 'y'
3051         try
3052             call bookmark.delete()
3053             call s:renderView()
3054             redraw
3055         catch /^NERDTree/
3056             call s:echoWarning("Could not remove bookmark")
3057         endtry
3058     else
3059         call s:echo("delete aborted" )
3060     endif
3062 endfunction
3064 " FUNCTION: s:deleteNode() {{{2
3065 " if the current node is a file, pops up a dialog giving the user the option
3066 " to delete it
3067 function! s:deleteNode()
3068     let currentNode = s:getSelectedNode()
3069     if currentNode == {}
3070         call s:echo("Put the cursor on a file node first")
3071         return
3072     endif
3074     let confirmed = 0
3076     if currentNode.path.isDirectory
3077         let choice =input("Delete the current node\n" .
3078                          \ "==========================================================\n" .
3079                          \ "STOP! To delete this entire directory, type 'yes'\n" .
3080                          \ "" . currentNode.path.strForOS(0) . ": ")
3081         let confirmed = choice == 'yes'
3082     else
3083         echo "Delete the current node\n" .
3084            \ "==========================================================\n".
3085            \ "Are you sure you wish to delete the node:\n" .
3086            \ "" . currentNode.path.strForOS(0) . " (yN):"
3087         let choice = nr2char(getchar())
3088         let confirmed = choice == 'y'
3089     endif
3092     if confirmed
3093         try
3094             call currentNode.delete()
3095             call s:renderView()
3097             "if the node is open in a buffer, ask the user if they want to
3098             "close that buffer
3099             let bufnum = bufnr(currentNode.path.str(0))
3100             if buflisted(bufnum)
3101                 let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) == -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
3102                 call s:promptToDelBuffer(bufnum, prompt)
3103             endif
3105             redraw
3106         catch /^NERDTree/
3107             call s:echoWarning("Could not remove node")
3108         endtry
3109     else
3110         call s:echo("delete aborted" )
3111     endif
3113 endfunction
3115 " FUNCTION: s:displayHelp() {{{2
3116 " toggles the help display
3117 function! s:displayHelp()
3118     let t:treeShowHelp = t:treeShowHelp ? 0 : 1
3119     call s:renderView()
3120     call s:centerView()
3121 endfunction
3123 " FUNCTION: s:executeNode() {{{2
3124 function! s:executeNode()
3125     let treenode = s:getSelectedNode()
3126     if treenode == {} || treenode.path.isDirectory
3127         call s:echo("Select an executable file node first" )
3128     else
3129         echo "NERDTree executor\n" .
3130            \ "==========================================================\n".
3131            \ "Complete the command to execute (add arguments etc): \n\n"
3132         let cmd = treenode.path.strForOS(1)
3133         let cmd = input(':!', cmd . ' ')
3135         if cmd != ''
3136             exec ':!' . cmd
3137         else
3138             call s:echo("command aborted")
3139         endif
3140     endif
3141 endfunction
3143 " FUNCTION: s:handleMiddleMouse() {{{2
3144 function! s:handleMiddleMouse()
3145     let curNode = s:getSelectedNode()
3146     if curNode == {}
3147         call s:echo("Put the cursor on a node first" )
3148         return
3149     endif
3151     if curNode.path.isDirectory
3152         call s:openExplorer()
3153     else
3154         call s:openEntrySplit(0)
3155     endif
3156 endfunction
3159 " FUNCTION: s:insertNewNode() {{{2
3160 " Adds a new node to the filesystem and then into the tree
3161 function! s:insertNewNode()
3162     let curDirNode = s:getSelectedDir()
3163     if curDirNode == {}
3164         call s:echo("Put the cursor on a node first" )
3165         return
3166     endif
3168     let newNodeName = input("Add a childnode\n".
3169                           \ "==========================================================\n".
3170                           \ "Enter the dir/file name to be created. Dirs end with a '/'\n" .
3171                           \ "", curDirNode.path.strForGlob() . s:os_slash)
3173     if newNodeName == ''
3174         call s:echo("Node Creation Aborted.")
3175         return
3176     endif
3178     try
3179         let newPath = s:Path.Create(newNodeName)
3180         let parentNode = t:NERDTreeRoot.findNode(newPath.getPathTrunk())
3182         let newTreeNode = s:TreeFileNode.New(newPath)
3183         if parentNode.isOpen || !empty(parentNode.children)
3184             call parentNode.addChild(newTreeNode, 1)
3185             call s:renderView()
3186             call s:putCursorOnNode(newTreeNode, 1, 0)
3187         endif
3188     catch /^NERDTree/
3189         call s:echoWarning("Node Not Created.")
3190     endtry
3191 endfunction
3193 " FUNCTION: s:jumpToFirstChild() {{{2
3194 " wrapper for the jump to child method
3195 function! s:jumpToFirstChild()
3196     call s:jumpToChild(0)
3197 endfunction
3199 " FUNCTION: s:jumpToLastChild() {{{2
3200 " wrapper for the jump to child method
3201 function! s:jumpToLastChild()
3202     call s:jumpToChild(1)
3203 endfunction
3205 " FUNCTION: s:jumpToParent() {{{2
3206 " moves the cursor to the parent of the current node
3207 function! s:jumpToParent()
3208     let currentNode = s:getSelectedNode()
3209     if !empty(currentNode)
3210         if !empty(currentNode.parent)
3211             call s:putCursorOnNode(currentNode.parent, 1, 0)
3212             call s:centerView()
3213         else
3214             call s:echo("cannot jump to parent")
3215         endif
3216     else
3217         call s:echo("put the cursor on a node first")
3218     endif
3219 endfunction
3221 " FUNCTION: s:jumpToRoot() {{{2
3222 " moves the cursor to the root node
3223 function! s:jumpToRoot()
3224     call s:putCursorOnNode(t:NERDTreeRoot, 1, 0)
3225     call s:centerView()
3226 endfunction
3228 " FUNCTION: s:jumpToSibling() {{{2
3229 " moves the cursor to the sibling of the current node in the given direction
3231 " Args:
3232 " forward: 1 if the cursor should move to the next sibling, 0 if it should
3233 " move back to the previous sibling
3234 function! s:jumpToSibling(forward)
3235     let currentNode = s:getSelectedNode()
3236     if !empty(currentNode)
3237         let sibling = currentNode.findSibling(a:forward)
3239         if !empty(sibling)
3240             call s:putCursorOnNode(sibling, 1, 0)
3241             call s:centerView()
3242         endif
3243     else
3244         call s:echo("put the cursor on a node first")
3245     endif
3246 endfunction
3248 " FUNCTION: s:openBookmark(name) {{{2
3249 " put the cursor on the given bookmark and, if its a file, open it
3250 function! s:openBookmark(name)
3251     try
3252         let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3253         call s:putCursorOnNode(targetNode, 0, 1)
3254         redraw!
3255     catch /NERDTree.BookmarkedNodeNotFound/
3256         call s:echo("note - target node is not cached")
3257         let bookmark = s:Bookmark.BookmarkFor(a:name)
3258         let targetNode = s:TreeFileNode.New(bookmark.path)
3259     endtry
3260     if targetNode.path.isDirectory
3261         call s:openExplorerFor(targetNode)
3262     else
3263         call s:openFileNode(targetNode)
3264     endif
3265 endfunction
3266 " FUNCTION: s:openEntrySplit(forceKeepWindowOpen) {{{2
3267 "Opens the currently selected file from the explorer in a
3268 "new window
3270 "args:
3271 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3272 function! s:openEntrySplit(forceKeepWindowOpen)
3273     let treenode = s:getSelectedNode()
3274     if treenode != {}
3275         call s:openFileNodeSplit(treenode)
3276         if !a:forceKeepWindowOpen
3277             call s:closeTreeIfQuitOnOpen()
3278         endif
3279     else
3280         call s:echo("select a node first")
3281     endif
3282 endfunction
3284 " FUNCTION: s:openExplorer() {{{2
3285 function! s:openExplorer()
3286     let treenode = s:getSelectedDir()
3287     if treenode != {}
3288         call s:openExplorerFor(treenode)
3289     else
3290         call s:echo("select a node first")
3291     endif
3292 endfunction
3294 " FUNCTION: s:openInNewTab(stayCurrentTab) {{{2
3295 " Opens the selected node or bookmark in a new tab
3296 " Args:
3297 " stayCurrentTab: if 1 then vim will stay in the current tab, if 0 then vim
3298 " will go to the tab where the new file is opened
3299 function! s:openInNewTab(stayCurrentTab)
3300     let currentTab = tabpagenr()
3302     let treenode = s:getSelectedNode()
3303     if treenode != {}
3304         if treenode.path.isDirectory
3305             tabnew
3306             call s:initNerdTree(treenode.path.strForOS(0))
3307         else
3308             exec "tabedit " . treenode.path.strForEditCmd()
3309         endif
3310     else
3311         let bookmark = s:getSelectedBookmark()
3312         if bookmark != {}
3313             if bookmark.path.isDirectory
3314                 tabnew
3315                 call s:initNerdTree(bookmark.name)
3316             else
3317                 exec "tabedit " . bookmark.path.strForEditCmd()
3318             endif
3319         endif
3320     endif
3321     if a:stayCurrentTab
3322         exec "tabnext " . currentTab
3323     endif
3324 endfunction
3326 " FUNCTION: s:openNodeRecursively() {{{2
3327 function! s:openNodeRecursively()
3328     let treenode = s:getSelectedNode()
3329     if treenode == {} || treenode.path.isDirectory == 0
3330         call s:echo("Select a directory node first" )
3331     else
3332         call s:echo("Recursively opening node. Please wait...")
3333         call treenode.openRecursively()
3334         call s:renderView()
3335         redraw
3336         call s:echo("Recursively opening node. Please wait... DONE")
3337     endif
3339 endfunction
3341 "FUNCTION: s:previewNode() {{{2
3342 function! s:previewNode(openNewWin)
3343     if a:openNewWin
3344         call s:openEntrySplit(1)
3345     else
3346         call s:activateNode(1)
3347     end
3348     call s:putCursorInTreeWin()
3349 endfunction
3351 " FUNCTION: s:revealBookmark(name) {{{2
3352 " put the cursor on the node associate with the given name
3353 function! s:revealBookmark(name)
3354     try
3355         let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3356         call s:putCursorOnNode(targetNode, 0, 1)
3357     catch /NERDTree.BookmarkDoesntExist/
3358         call s:echo("Bookmark isnt cached under the current root")
3359     endtry
3360 endfunction
3361 " FUNCTION: s:refreshRoot() {{{2
3362 " Reloads the current root. All nodes below this will be lost and the root dir
3363 " will be reloaded.
3364 function! s:refreshRoot()
3365     call s:echo("Refreshing the root node. This could take a while...")
3366     call t:NERDTreeRoot.refresh()
3367     call s:renderView()
3368     redraw
3369     call s:echo("Refreshing the root node. This could take a while... DONE")
3370 endfunction
3372 " FUNCTION: s:refreshCurrent() {{{2
3373 " refreshes the root for the current node
3374 function! s:refreshCurrent()
3375     let treenode = s:getSelectedDir()
3376     if treenode == {}
3377         call s:echo("Refresh failed. Select a node first")
3378         return
3379     endif
3381     call s:echo("Refreshing node. This could take a while...")
3382     call treenode.refresh()
3383     call s:renderView()
3384     redraw
3385     call s:echo("Refreshing node. This could take a while... DONE")
3386 endfunction
3387 " FUNCTION: s:renameCurrent() {{{2
3388 " allows the user to rename the current node
3389 function! s:renameCurrent()
3390     let curNode = s:getSelectedNode()
3391     if curNode == {}
3392         call s:echo("Put the cursor on a node first" )
3393         return
3394     endif
3396     let newNodePath = input("Rename the current node\n" .
3397                           \ "==========================================================\n" .
3398                           \ "Enter the new path for the node:                          \n" .
3399                           \ "", curNode.path.strForOS(0))
3401     if newNodePath == ''
3402         call s:echo("Node Renaming Aborted.")
3403         return
3404     endif
3406     try
3407         let bufnum = bufnr(curNode.path.str(0))
3409         call curNode.rename(newNodePath)
3410         call s:renderView()
3412         "if the node is open in a buffer, ask the user if they want to
3413         "close that buffer
3414         if bufnum != -1
3415             let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) == -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
3416             call s:promptToDelBuffer(bufnum, prompt)
3417         endif
3419         call s:putCursorOnNode(curNode, 1, 0)
3421         redraw
3422     catch /^NERDTree/
3423         call s:echoWarning("Node Not Renamed.")
3424     endtry
3425 endfunction
3427 " FUNCTION: s:showFileSystemMenu() {{{2
3428 function! s:showFileSystemMenu()
3429     let curNode = s:getSelectedNode()
3430     if curNode == {}
3431         call s:echo("Put the cursor on a node first" )
3432         return
3433     endif
3436     let prompt = "NERDTree Filesystem Menu\n" .
3437        \ "==========================================================\n".
3438        \ "Select the desired operation:                             \n" .
3439        \ " (a)dd a childnode\n".
3440        \ " (m)ove the current node\n".
3441        \ " (d)elete the current node\n"
3442     if s:Path.CopyingSupported()
3443         let prompt = prompt . " (c)opy the current node\n\n"
3444     else
3445         let prompt = prompt . " \n"
3446     endif
3448     echo prompt
3450     let choice = nr2char(getchar())
3452     if choice ==? "a"
3453         call s:insertNewNode()
3454     elseif choice ==? "m"
3455         call s:renameCurrent()
3456     elseif choice ==? "d"
3457         call s:deleteNode()
3458     elseif choice ==? "c" && s:Path.CopyingSupported()
3459         call s:copyNode()
3460     endif
3461 endfunction
3463 " FUNCTION: s:toggleIgnoreFilter() {{{2
3464 " toggles the use of the NERDTreeIgnore option
3465 function! s:toggleIgnoreFilter()
3466     let t:NERDTreeIgnoreEnabled = !t:NERDTreeIgnoreEnabled
3467     call s:renderViewSavingPosition()
3468     call s:centerView()
3469 endfunction
3471 " FUNCTION: s:toggleShowBookmarks() {{{2
3472 " toggles the display of bookmarks
3473 function! s:toggleShowBookmarks()
3474     let t:NERDTreeShowBookmarks = !t:NERDTreeShowBookmarks
3475     if t:NERDTreeShowBookmarks
3476         call s:renderView()
3477         call s:putCursorOnBookmarkTable()
3478     else
3479         call s:renderViewSavingPosition()
3480     endif
3481     call s:centerView()
3482 endfunction
3483 " FUNCTION: s:toggleShowFiles() {{{2
3484 " toggles the display of hidden files
3485 function! s:toggleShowFiles()
3486     let t:NERDTreeShowFiles = !t:NERDTreeShowFiles
3487     call s:renderViewSavingPosition()
3488     call s:centerView()
3489 endfunction
3491 " FUNCTION: s:toggleShowHidden() {{{2
3492 " toggles the display of hidden files
3493 function! s:toggleShowHidden()
3494     let t:NERDTreeShowHidden = !t:NERDTreeShowHidden
3495     call s:renderViewSavingPosition()
3496     call s:centerView()
3497 endfunction
3499 "FUNCTION: s:upDir(keepState) {{{2
3500 "moves the tree up a level
3502 "Args:
3503 "keepState: 1 if the current root should be left open when the tree is
3504 "re-rendered
3505 function! s:upDir(keepState)
3506     let cwd = t:NERDTreeRoot.path.str(0)
3507     if cwd == "/" || cwd =~ '^[^/]..$'
3508         call s:echo("already at top dir")
3509     else
3510         if !a:keepState
3511             call t:NERDTreeRoot.close()
3512         endif
3514         let oldRoot = t:NERDTreeRoot
3516         if empty(t:NERDTreeRoot.parent)
3517             let path = t:NERDTreeRoot.path.getPathTrunk()
3518             let newRoot = s:TreeDirNode.New(path)
3519             call newRoot.open()
3520             call newRoot.transplantChild(t:NERDTreeRoot)
3521             let t:NERDTreeRoot = newRoot
3522         else
3523             let t:NERDTreeRoot = t:NERDTreeRoot.parent
3525         endif
3527         call s:renderView()
3528         call s:putCursorOnNode(oldRoot, 0, 0)
3529     endif
3530 endfunction
3533 "reset &cpo back to users setting
3534 let &cpo = s:old_cpo
3536 " vim: set sw=4 sts=4 et fdm=marker: