1 " ============================================================================
3 " Description: vim global plugin that provides a nice tree explorer
4 " Maintainer: Martin Grenfell <martin.grenfell at gmail dot com>
5 " Last Change: 1 December, 2009
6 " License: This program is free software. It comes without any warranty,
7 " to the extent permitted by applicable law. You can redistribute
8 " it and/or modify it under the terms of the Do What The Fuck You
9 " Want To Public License, Version 2, as published by Sam Hocevar.
10 " See http://sam.zoy.org/wtfpl/COPYING for more details.
12 " ============================================================================
13 let s:NERD_tree_version = '4.1.0'
15 " SECTION: Script init stuff {{{1
16 "============================================================
17 if exists("loaded_nerd_tree")
21 echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!"
24 let loaded_nerd_tree = 1
26 "for line continuation - i.e dont want C in &cpo
30 "Function: s:initVariable() function {{{2
31 "This function is used to initialise a given variable to a given value. The
32 "variable is only initialised if it does not exist prior
35 "var: the name of the var to be initialised
36 "value: the value to initialise var to
39 "1 if the var is set, 0 otherwise
40 function! s:initVariable(var, value)
42 exec 'let ' . a:var . ' = ' . "'" . a:value . "'"
48 "SECTION: Init variable calls and other random constants {{{2
49 call s:initVariable("g:NERDChristmasTree", 1)
50 call s:initVariable("g:NERDTreeAutoCenter", 1)
51 call s:initVariable("g:NERDTreeAutoCenterThreshold", 3)
52 call s:initVariable("g:NERDTreeCaseSensitiveSort", 0)
53 call s:initVariable("g:NERDTreeChDirMode", 0)
54 if !exists("g:NERDTreeIgnore")
55 let g:NERDTreeIgnore = ['\~$']
57 call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks')
58 call s:initVariable("g:NERDTreeHighlightCursorline", 1)
59 call s:initVariable("g:NERDTreeHijackNetrw", 1)
60 call s:initVariable("g:NERDTreeMouseMode", 1)
61 call s:initVariable("g:NERDTreeNotificationThreshold", 100)
62 call s:initVariable("g:NERDTreeQuitOnOpen", 0)
63 call s:initVariable("g:NERDTreeShowBookmarks", 0)
64 call s:initVariable("g:NERDTreeShowFiles", 1)
65 call s:initVariable("g:NERDTreeShowHidden", 0)
66 call s:initVariable("g:NERDTreeShowLineNumbers", 0)
67 call s:initVariable("g:NERDTreeSortDirs", 1)
69 if !exists("g:NERDTreeSortOrder")
70 let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$']
72 "if there isnt a * in the sort sequence then add one
73 if count(g:NERDTreeSortOrder, '*') < 1
74 call add(g:NERDTreeSortOrder, '*')
78 "we need to use this number many times for sorting... so we calculate it only
80 let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*')
82 if !exists('g:NERDTreeStatusline')
84 "the exists() crap here is a hack to stop vim spazzing out when
85 "loading a session that was created with an open nerd tree. It spazzes
86 "because it doesnt store b:NERDTreeRoot (its a b: var, and its a hash)
87 let g:NERDTreeStatusline = "%{exists('b:NERDTreeRoot')?b:NERDTreeRoot.path.str():''}"
90 call s:initVariable("g:NERDTreeWinPos", "left")
91 call s:initVariable("g:NERDTreeWinSize", 31)
93 let s:running_windows = has("win16") || has("win32") || has("win64")
95 "init the shell commands that will be used to copy nodes, and remove dir trees
97 "Note: the space after the command is important
99 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ')
101 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ')
102 call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ')
106 "SECTION: Init variable calls for key mappings {{{2
107 call s:initVariable("g:NERDTreeMapActivateNode", "o")
108 call s:initVariable("g:NERDTreeMapChangeRoot", "C")
109 call s:initVariable("g:NERDTreeMapChdir", "cd")
110 call s:initVariable("g:NERDTreeMapCloseChildren", "X")
111 call s:initVariable("g:NERDTreeMapCloseDir", "x")
112 call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
113 call s:initVariable("g:NERDTreeMapMenu", "m")
114 call s:initVariable("g:NERDTreeMapHelp", "?")
115 call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
116 call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
117 call s:initVariable("g:NERDTreeMapJumpNextSibling", "<C-j>")
118 call s:initVariable("g:NERDTreeMapJumpParent", "p")
119 call s:initVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>")
120 call s:initVariable("g:NERDTreeMapJumpRoot", "P")
121 call s:initVariable("g:NERDTreeMapOpenExpl", "e")
122 call s:initVariable("g:NERDTreeMapOpenInTab", "t")
123 call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T")
124 call s:initVariable("g:NERDTreeMapOpenRecursively", "O")
125 call s:initVariable("g:NERDTreeMapOpenSplit", "i")
126 call s:initVariable("g:NERDTreeMapOpenVSplit", "s")
127 call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode)
128 call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit)
129 call s:initVariable("g:NERDTreeMapPreviewVSplit", "g" . NERDTreeMapOpenVSplit)
130 call s:initVariable("g:NERDTreeMapQuit", "q")
131 call s:initVariable("g:NERDTreeMapRefresh", "r")
132 call s:initVariable("g:NERDTreeMapRefreshRoot", "R")
133 call s:initVariable("g:NERDTreeMapToggleBookmarks", "B")
134 call s:initVariable("g:NERDTreeMapToggleFiles", "F")
135 call s:initVariable("g:NERDTreeMapToggleFilters", "f")
136 call s:initVariable("g:NERDTreeMapToggleHidden", "I")
137 call s:initVariable("g:NERDTreeMapToggleZoom", "A")
138 call s:initVariable("g:NERDTreeMapUpdir", "u")
139 call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U")
141 "SECTION: Script level variable declaration{{{2
143 let s:escape_chars = " `\|\"#%&,?()\*^<>"
145 let s:escape_chars = " \\`\|\"#%&,?()\*^<>"
147 let s:NERDTreeBufName = 'NERD_tree_'
150 let s:tree_markup_reg = '^[ `|]*[\-+~]'
151 let s:tree_up_dir_line = '.. (up a dir)'
153 "the number to add to the nerd tree buffer name to make the buf name unique
154 let s:next_buffer_number = 1
156 " SECTION: Commands {{{1
157 "============================================================
158 "init the command that users start the nerd tree with
159 command! -n=? -complete=dir -bar NERDTree :call s:initNerdTree('<args>')
160 command! -n=? -complete=dir -bar NERDTreeToggle :call s:toggle('<args>')
161 command! -n=0 -bar NERDTreeClose :call s:closeTreeIfOpen()
162 command! -n=1 -complete=customlist,s:completeBookmarks -bar NERDTreeFromBookmark call s:initNerdTree('<args>')
163 command! -n=0 -bar NERDTreeMirror call s:initNerdTreeMirror()
164 command! -n=0 -bar NERDTreeFind call s:findAndRevealPath()
165 " SECTION: Auto commands {{{1
166 "============================================================
168 "Save the cursor position whenever we close the nerd tree
169 exec "autocmd BufWinLeave ". s:NERDTreeBufName ."* call <SID>saveScreenState()"
170 "cache bookmarks when vim loads
171 autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
173 "load all nerdtree plugins after vim starts
174 autocmd VimEnter * runtime! nerdtree_plugin/**/*.vim
177 if g:NERDTreeHijackNetrw
178 augroup NERDTreeHijackNetrw
179 autocmd VimEnter * silent! autocmd! FileExplorer
180 au BufEnter,VimEnter * call s:checkForBrowse(expand("<amatch>"))
184 "SECTION: Classes {{{1
185 "============================================================
186 "CLASS: Bookmark {{{2
187 "============================================================
189 " FUNCTION: Bookmark.activate() {{{3
190 function! s:Bookmark.activate()
191 if self.path.isDirectory
195 let n = s:TreeFileNode.New(self.path)
200 " FUNCTION: Bookmark.AddBookmark(name, path) {{{3
201 " Class method to add a new bookmark to the list, if a previous bookmark exists
202 " with the same name, just update the path for that bookmark
203 function! s:Bookmark.AddBookmark(name, path)
204 for i in s:Bookmark.Bookmarks()
210 call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
211 call s:Bookmark.Sort()
213 " Function: Bookmark.Bookmarks() {{{3
214 " Class method to get all bookmarks. Lazily initializes the bookmarks global
216 function! s:Bookmark.Bookmarks()
217 if !exists("g:NERDTreeBookmarks")
218 let g:NERDTreeBookmarks = []
220 return g:NERDTreeBookmarks
222 " Function: Bookmark.BookmarkExistsFor(name) {{{3
223 " class method that returns 1 if a bookmark with the given name is found, 0
225 function! s:Bookmark.BookmarkExistsFor(name)
227 call s:Bookmark.BookmarkFor(a:name)
229 catch /^NERDTree.BookmarkNotFoundError/
233 " Function: Bookmark.BookmarkFor(name) {{{3
234 " Class method to get the bookmark that has the given name. {} is return if no
236 function! s:Bookmark.BookmarkFor(name)
237 for i in s:Bookmark.Bookmarks()
242 throw "NERDTree.BookmarkNotFoundError: no bookmark found for name: \"". a:name .'"'
244 " Function: Bookmark.BookmarkNames() {{{3
245 " Class method to return an array of all bookmark names
246 function! s:Bookmark.BookmarkNames()
248 for i in s:Bookmark.Bookmarks()
249 call add(names, i.name)
253 " FUNCTION: Bookmark.CacheBookmarks(silent) {{{3
254 " Class method to read all bookmarks from the bookmarks file intialize
255 " bookmark objects for each one.
258 " silent - dont echo an error msg if invalid bookmarks are found
259 function! s:Bookmark.CacheBookmarks(silent)
260 if filereadable(g:NERDTreeBookmarksFile)
261 let g:NERDTreeBookmarks = []
262 let g:NERDTreeInvalidBookmarks = []
263 let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
264 let invalidBookmarksFound = 0
265 for i in bookmarkStrings
270 let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
271 let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
274 let bookmark = s:Bookmark.New(name, s:Path.New(path))
275 call add(g:NERDTreeBookmarks, bookmark)
276 catch /^NERDTree.InvalidArgumentsError/
277 call add(g:NERDTreeInvalidBookmarks, i)
278 let invalidBookmarksFound += 1
282 if invalidBookmarksFound
283 call s:Bookmark.Write()
285 call s:echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
288 call s:Bookmark.Sort()
291 " FUNCTION: Bookmark.compareTo(otherbookmark) {{{3
292 " Compare these two bookmarks for sorting purposes
293 function! s:Bookmark.compareTo(otherbookmark)
294 return a:otherbookmark.name < self.name
296 " FUNCTION: Bookmark.ClearAll() {{{3
297 " Class method to delete all bookmarks.
298 function! s:Bookmark.ClearAll()
299 for i in s:Bookmark.Bookmarks()
302 call s:Bookmark.Write()
304 " FUNCTION: Bookmark.delete() {{{3
305 " Delete this bookmark. If the node for this bookmark is under the current
306 " root, then recache bookmarks for its Path object
307 function! s:Bookmark.delete()
310 let node = self.getNode(1)
311 catch /^NERDTree.BookmarkedNodeNotFoundError/
313 call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
315 call node.path.cacheDisplayString()
317 call s:Bookmark.Write()
319 " FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{3
320 " Gets the treenode for this bookmark
323 " searchFromAbsoluteRoot: specifies whether we should search from the current
324 " tree root, or the highest cached node
325 function! s:Bookmark.getNode(searchFromAbsoluteRoot)
326 let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : b:NERDTreeRoot
327 let targetNode = searchRoot.findNode(self.path)
329 throw "NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: " . self.name
333 " FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{3
334 " Class method that finds the bookmark with the given name and returns the
336 function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
337 let bookmark = s:Bookmark.BookmarkFor(a:name)
338 return bookmark.getNode(a:searchFromAbsoluteRoot)
340 " FUNCTION: Bookmark.GetSelected() {{{3
341 " returns the Bookmark the cursor is over, or {}
342 function! s:Bookmark.GetSelected()
343 let line = getline(".")
344 let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
347 return s:Bookmark.BookmarkFor(name)
348 catch /^NERDTree.BookmarkNotFoundError/
355 " Function: Bookmark.InvalidBookmarks() {{{3
356 " Class method to get all invalid bookmark strings read from the bookmarks
358 function! s:Bookmark.InvalidBookmarks()
359 if !exists("g:NERDTreeInvalidBookmarks")
360 let g:NERDTreeInvalidBookmarks = []
362 return g:NERDTreeInvalidBookmarks
364 " FUNCTION: Bookmark.mustExist() {{{3
365 function! s:Bookmark.mustExist()
366 if !self.path.exists()
367 call s:Bookmark.CacheBookmarks(1)
368 throw "NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \"".
369 \ self.name ."\" points to a non existing location: \"". self.path.str()
372 " FUNCTION: Bookmark.New(name, path) {{{3
373 " Create a new bookmark object with the given name and path object
374 function! s:Bookmark.New(name, path)
376 throw "NERDTree.IllegalBookmarkNameError: illegal name:" . a:name
379 let newBookmark = copy(self)
380 let newBookmark.name = a:name
381 let newBookmark.path = a:path
384 " FUNCTION: Bookmark.openInNewTab(options) {{{3
385 " Create a new bookmark object with the given name and path object
386 function! s:Bookmark.openInNewTab(options)
387 let currentTab = tabpagenr()
388 if self.path.isDirectory
390 call s:initNerdTree(self.name)
392 exec "tabedit " . bookmark.path.str({'format': 'Edit'})
395 if has_key(a:options, 'stayInCurrentTab')
396 exec "tabnext " . currentTab
399 " Function: Bookmark.setPath(path) {{{3
400 " makes this bookmark point to the given path
401 function! s:Bookmark.setPath(path)
402 let self.path = a:path
404 " Function: Bookmark.Sort() {{{3
405 " Class method that sorts all bookmarks
406 function! s:Bookmark.Sort()
407 let CompareFunc = function("s:compareBookmarks")
408 call sort(s:Bookmark.Bookmarks(), CompareFunc)
410 " Function: Bookmark.str() {{{3
411 " Get the string that should be rendered in the view for this bookmark
412 function! s:Bookmark.str()
413 let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name)
415 let pathStrMaxLen = pathStrMaxLen - &numberwidth
418 let pathStr = self.path.str({'format': 'UI'})
419 if len(pathStr) > pathStrMaxLen
420 let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
422 return '>' . self.name . ' ' . pathStr
424 " FUNCTION: Bookmark.toRoot() {{{3
425 " Make the node for this bookmark the new tree root
426 function! s:Bookmark.toRoot()
429 let targetNode = self.getNode(1)
430 catch /^NERDTree.BookmarkedNodeNotFoundError/
431 let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
433 call targetNode.makeRoot()
435 call targetNode.putCursorHere(0, 0)
438 " FUNCTION: Bookmark.ToRoot(name) {{{3
439 " Make the node for this bookmark the new tree root
440 function! s:Bookmark.ToRoot(name)
441 let bookmark = s:Bookmark.BookmarkFor(a:name)
442 call bookmark.toRoot()
446 "FUNCTION: Bookmark.validate() {{{3
447 function! s:Bookmark.validate()
448 if self.path.exists()
451 call s:Bookmark.CacheBookmarks(1)
453 call s:echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
458 " Function: Bookmark.Write() {{{3
459 " Class method to write all bookmarks to the bookmarks file
460 function! s:Bookmark.Write()
461 let bookmarkStrings = []
462 for i in s:Bookmark.Bookmarks()
463 call add(bookmarkStrings, i.name . ' ' . i.path.str())
466 "add a blank line before the invalid ones
467 call add(bookmarkStrings, "")
469 for j in s:Bookmark.InvalidBookmarks()
470 call add(bookmarkStrings, j)
472 call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
475 "============================================================
477 "FUNCTION: KeyMap.All() {{{3
478 function! s:KeyMap.All()
479 if !exists("s:keyMaps")
485 "FUNCTION: KeyMap.BindAll() {{{3
486 function! s:KeyMap.BindAll()
487 for i in s:KeyMap.All()
492 "FUNCTION: KeyMap.bind() {{{3
493 function! s:KeyMap.bind()
494 exec "nnoremap <silent> <buffer> ". self.key ." :call ". self.callback ."()<cr>"
497 "FUNCTION: KeyMap.Create(options) {{{3
498 function! s:KeyMap.Create(options)
499 let newKeyMap = copy(self)
500 let newKeyMap.key = a:options['key']
501 let newKeyMap.quickhelpText = a:options['quickhelpText']
502 let newKeyMap.callback = a:options['callback']
503 call add(s:KeyMap.All(), newKeyMap)
505 "CLASS: MenuController {{{2
506 "============================================================
507 let s:MenuController = {}
508 "FUNCTION: MenuController.New(menuItems) {{{3
509 "create a new menu controller that operates on the given menu items
510 function! s:MenuController.New(menuItems)
511 let newMenuController = copy(self)
512 if a:menuItems[0].isSeparator()
513 let newMenuController.menuItems = a:menuItems[1:-1]
515 let newMenuController.menuItems = a:menuItems
517 return newMenuController
520 "FUNCTION: MenuController.showMenu() {{{3
521 "start the main loop of the menu and get the user to choose/execute a menu
523 function! s:MenuController.showMenu()
524 call self._saveOptions()
527 let self.selection = 0
532 call self._echoPrompt()
533 let key = nr2char(getchar())
534 let done = self._handleKeypress(key)
537 call self._restoreOptions()
540 if self.selection != -1
541 let m = self._current()
546 "FUNCTION: MenuController._echoPrompt() {{{3
547 function! s:MenuController._echoPrompt()
548 echo "NERDTree Menu. Use j/k/enter and the shortcuts indicated"
549 echo "=========================================================="
551 for i in range(0, len(self.menuItems)-1)
552 if self.selection == i
553 echo "> " . self.menuItems[i].text
555 echo " " . self.menuItems[i].text
560 "FUNCTION: MenuController._current(key) {{{3
561 "get the MenuItem that is curently selected
562 function! s:MenuController._current()
563 return self.menuItems[self.selection]
566 "FUNCTION: MenuController._handleKeypress(key) {{{3
567 "change the selection (if appropriate) and return 1 if the user has made
568 "their choice, 0 otherwise
569 function! s:MenuController._handleKeypress(key)
571 call self._cursorDown()
573 call self._cursorUp()
574 elseif a:key == nr2char(27) "escape
575 let self.selection = -1
577 elseif a:key == "\r" || a:key == "\n" "enter and ctrl-j
580 let index = self._nextIndexFor(a:key)
582 let self.selection = index
583 if len(self._allIndexesFor(a:key)) == 1
592 "FUNCTION: MenuController._allIndexesFor(shortcut) {{{3
593 "get indexes to all menu items with the given shortcut
594 function! s:MenuController._allIndexesFor(shortcut)
597 for i in range(0, len(self.menuItems)-1)
598 if self.menuItems[i].shortcut == a:shortcut
599 call add(toReturn, i)
606 "FUNCTION: MenuController._nextIndexFor(shortcut) {{{3
607 "get the index to the next menu item with the given shortcut, starts from the
608 "current cursor location and wraps around to the top again if need be
609 function! s:MenuController._nextIndexFor(shortcut)
610 for i in range(self.selection+1, len(self.menuItems)-1)
611 if self.menuItems[i].shortcut == a:shortcut
616 for i in range(0, self.selection)
617 if self.menuItems[i].shortcut == a:shortcut
625 "FUNCTION: MenuController._setCmdheight() {{{3
626 "sets &cmdheight to whatever is needed to display the menu
627 function! s:MenuController._setCmdheight()
628 let &cmdheight = len(self.menuItems) + 3
631 "FUNCTION: MenuController._saveOptions() {{{3
632 "set any vim options that are required to make the menu work (saving their old
634 function! s:MenuController._saveOptions()
635 let self._oldLazyredraw = &lazyredraw
636 let self._oldCmdheight = &cmdheight
638 call self._setCmdheight()
641 "FUNCTION: MenuController._restoreOptions() {{{3
642 "restore the options we saved in _saveOptions()
643 function! s:MenuController._restoreOptions()
644 let &cmdheight = self._oldCmdheight
645 let &lazyredraw = self._oldLazyredraw
648 "FUNCTION: MenuController._cursorDown() {{{3
649 "move the cursor to the next menu item, skipping separators
650 function! s:MenuController._cursorDown()
653 if self.selection < len(self.menuItems)-1
654 let self.selection += 1
656 let self.selection = 0
659 if !self._current().isSeparator()
665 "FUNCTION: MenuController._cursorUp() {{{3
666 "move the cursor to the previous menu item, skipping separators
667 function! s:MenuController._cursorUp()
670 if self.selection > 0
671 let self.selection -= 1
673 let self.selection = len(self.menuItems)-1
676 if !self._current().isSeparator()
682 "CLASS: MenuItem {{{2
683 "============================================================
685 "FUNCTION: MenuItem.All() {{{3
686 "get all top level menu items
687 function! s:MenuItem.All()
688 if !exists("s:menuItems")
694 "FUNCTION: MenuItem.AllEnabled() {{{3
695 "get all top level menu items that are currently enabled
696 function! s:MenuItem.AllEnabled()
698 for i in s:MenuItem.All()
700 call add(toReturn, i)
706 "FUNCTION: MenuItem.Create(options) {{{3
707 "make a new menu item and add it to the global list
708 function! s:MenuItem.Create(options)
709 let newMenuItem = copy(self)
711 let newMenuItem.text = a:options['text']
712 let newMenuItem.shortcut = a:options['shortcut']
713 let newMenuItem.children = []
715 let newMenuItem.isActiveCallback = -1
716 if has_key(a:options, 'isActiveCallback')
717 let newMenuItem.isActiveCallback = a:options['isActiveCallback']
720 let newMenuItem.callback = -1
721 if has_key(a:options, 'callback')
722 let newMenuItem.callback = a:options['callback']
725 if has_key(a:options, 'parent')
726 call add(a:options['parent'].children, newMenuItem)
728 call add(s:MenuItem.All(), newMenuItem)
734 "FUNCTION: MenuItem.CreateSeparator(options) {{{3
735 "make a new separator menu item and add it to the global list
736 function! s:MenuItem.CreateSeparator(options)
737 let standard_options = { 'text': '--------------------',
740 let options = extend(a:options, standard_options, "force")
742 return s:MenuItem.Create(options)
745 "FUNCTION: MenuItem.CreateSubmenu(options) {{{3
746 "make a new submenu and add it to global list
747 function! s:MenuItem.CreateSubmenu(options)
748 let standard_options = { 'callback': -1 }
749 let options = extend(a:options, standard_options, "force")
751 return s:MenuItem.Create(options)
754 "FUNCTION: MenuItem.enabled() {{{3
755 "return 1 if this menu item should be displayed
757 "delegates off to the isActiveCallback, and defaults to 1 if no callback was
759 function! s:MenuItem.enabled()
760 if self.isActiveCallback != -1
761 return {self.isActiveCallback}()
766 "FUNCTION: MenuItem.execute() {{{3
767 "perform the action behind this menu item, if this menuitem has children then
768 "display a new menu for them, otherwise deletegate off to the menuitem's
770 function! s:MenuItem.execute()
771 if len(self.children)
772 let mc = s:MenuController.New(self.children)
775 if self.callback != -1
776 call {self.callback}()
781 "FUNCTION: MenuItem.isSeparator() {{{3
782 "return 1 if this menuitem is a separator
783 function! s:MenuItem.isSeparator()
784 return self.callback == -1 && self.children == []
787 "FUNCTION: MenuItem.isSubmenu() {{{3
788 "return 1 if this menuitem is a submenu
789 function! s:MenuItem.isSubmenu()
790 return self.callback == -1 && !empty(self.children)
793 "CLASS: TreeFileNode {{{2
794 "This class is the parent of the TreeDirNode class and constitures the
795 "'Component' part of the composite design pattern between the treenode
797 "============================================================
798 let s:TreeFileNode = {}
799 "FUNCTION: TreeFileNode.activate(forceKeepWinOpen) {{{3
800 function! s:TreeFileNode.activate(forceKeepWinOpen)
802 if !a:forceKeepWinOpen
803 call s:closeTreeIfQuitOnOpen()
806 "FUNCTION: TreeFileNode.bookmark(name) {{{3
807 "bookmark this node with a:name
808 function! s:TreeFileNode.bookmark(name)
810 let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1)
811 call oldMarkedNode.path.cacheDisplayString()
812 catch /^NERDTree.BookmarkNotFoundError/
815 call s:Bookmark.AddBookmark(a:name, self.path)
816 call self.path.cacheDisplayString()
817 call s:Bookmark.Write()
819 "FUNCTION: TreeFileNode.cacheParent() {{{3
820 "initializes self.parent if it isnt already
821 function! s:TreeFileNode.cacheParent()
822 if empty(self.parent)
823 let parentPath = self.path.getParent()
824 if parentPath.equals(self.path)
825 throw "NERDTree.CannotCacheParentError: already at root"
827 let self.parent = s:TreeFileNode.New(parentPath)
830 "FUNCTION: TreeFileNode.compareNodes {{{3
831 "This is supposed to be a class level method but i cant figure out how to
832 "get func refs to work from a dict..
834 "A class level method that compares two nodes
837 "n1, n2: the 2 nodes to compare
838 function! s:compareNodes(n1, n2)
839 return a:n1.path.compareTo(a:n2.path)
842 "FUNCTION: TreeFileNode.clearBoomarks() {{{3
843 function! s:TreeFileNode.clearBoomarks()
844 for i in s:Bookmark.Bookmarks()
845 if i.path.equals(self.path)
849 call self.path.cacheDisplayString()
851 "FUNCTION: TreeFileNode.copy(dest) {{{3
852 function! s:TreeFileNode.copy(dest)
853 call self.path.copy(a:dest)
854 let newPath = s:Path.New(a:dest)
855 let parent = b:NERDTreeRoot.findNode(newPath.getParent())
857 call parent.refresh()
859 return parent.findNode(newPath)
862 "FUNCTION: TreeFileNode.delete {{{3
863 "Removes this node from the tree and calls the Delete method for its path obj
864 function! s:TreeFileNode.delete()
865 call self.path.delete()
866 call self.parent.removeChild(self)
869 "FUNCTION: TreeFileNode.displayString() {{{3
871 "Returns a string that specifies how the node should be represented as a
875 "a string that can be used in the view to represent this node
876 function! s:TreeFileNode.displayString()
877 return self.path.displayString()
880 "FUNCTION: TreeFileNode.equals(treenode) {{{3
882 "Compares this treenode to the input treenode and returns 1 if they are the
885 "Use this method instead of == because sometimes when the treenodes contain
886 "many children, vim seg faults when doing ==
889 "treenode: the other treenode to compare to
890 function! s:TreeFileNode.equals(treenode)
891 return self.path.str() ==# a:treenode.path.str()
894 "FUNCTION: TreeFileNode.findNode(path) {{{3
895 "Returns self if this node.path.Equals the given path.
896 "Returns {} if not equal.
899 "path: the path object to compare against
900 function! s:TreeFileNode.findNode(path)
901 if a:path.equals(self.path)
906 "FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3
908 "Finds the next sibling for this node in the indicated direction. This sibling
909 "must be a directory and may/may not have children as specified.
912 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
915 "a treenode object or {} if no appropriate sibling could be found
916 function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
917 "if we have no parent then we can have no siblings
919 let nextSibling = self.findSibling(a:direction)
921 while nextSibling != {}
922 if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen
925 let nextSibling = nextSibling.findSibling(a:direction)
931 "FUNCTION: TreeFileNode.findSibling(direction) {{{3
933 "Finds the next sibling for this node in the indicated direction
936 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
939 "a treenode object or {} if no sibling could be found
940 function! s:TreeFileNode.findSibling(direction)
941 "if we have no parent then we can have no siblings
944 "get the index of this node in its parents children
945 let siblingIndx = self.parent.getChildIndex(self.path)
948 "move a long to the next potential sibling node
949 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
951 "keep moving along to the next sibling till we find one that is valid
952 let numSiblings = self.parent.getChildCount()
953 while siblingIndx >= 0 && siblingIndx < numSiblings
955 "if the next node is not an ignored node (i.e. wont show up in the
956 "view) then return it
957 if self.parent.children[siblingIndx].path.ignore() ==# 0
958 return self.parent.children[siblingIndx]
962 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
970 "FUNCTION: TreeFileNode.getLineNum(){{{3
971 "returns the line number this node is rendered on, or -1 if it isnt rendered
972 function! s:TreeFileNode.getLineNum()
973 "if the node is the root then return the root line no.
975 return s:TreeFileNode.GetRootLineNum()
978 let totalLines = line("$")
980 "the path components we have matched so far
981 let pathcomponents = [substitute(b:NERDTreeRoot.path.str({'format': 'UI'}), '/ *$', '', '')]
982 "the index of the component we are searching for
983 let curPathComponent = 1
985 let fullpath = self.path.str({'format': 'UI'})
988 let lnum = s:TreeFileNode.GetRootLineNum()
991 "have we reached the bottom of the tree?
992 if lnum ==# totalLines+1
996 let curLine = getline(lnum)
998 let indent = s:indentLevelFor(curLine)
999 if indent ==# curPathComponent
1000 let curLine = s:stripMarkupFromLine(curLine, 1)
1002 let curPath = join(pathcomponents, '/') . '/' . curLine
1003 if stridx(fullpath, curPath, 0) ==# 0
1004 if fullpath ==# curPath || strpart(fullpath, len(curPath)-1,1) ==# '/'
1005 let curLine = substitute(curLine, '/ *$', '', '')
1006 call add(pathcomponents, curLine)
1007 let curPathComponent = curPathComponent + 1
1009 if fullpath ==# curPath
1019 "FUNCTION: TreeFileNode.GetRootForTab(){{{3
1020 "get the root node for this tab
1021 function! s:TreeFileNode.GetRootForTab()
1022 if s:treeExistsForTab()
1023 return getbufvar(t:NERDTreeBufName, 'NERDTreeRoot')
1027 "FUNCTION: TreeFileNode.GetRootLineNum(){{{3
1028 "gets the line number of the root node
1029 function! s:TreeFileNode.GetRootLineNum()
1031 while getline(rootLine) !~ '^\(/\|<\)'
1032 let rootLine = rootLine + 1
1037 "FUNCTION: TreeFileNode.GetSelected() {{{3
1038 "gets the treenode that the cursor is currently over
1039 function! s:TreeFileNode.GetSelected()
1041 let path = s:getPath(line("."))
1045 return b:NERDTreeRoot.findNode(path)
1050 "FUNCTION: TreeFileNode.isVisible() {{{3
1051 "returns 1 if this node should be visible according to the tree filters and
1052 "hidden file filters (and their on/off status)
1053 function! s:TreeFileNode.isVisible()
1054 return !self.path.ignore()
1056 "FUNCTION: TreeFileNode.isRoot() {{{3
1057 "returns 1 if this node is b:NERDTreeRoot
1058 function! s:TreeFileNode.isRoot()
1059 if !s:treeExistsForBuf()
1060 throw "NERDTree.NoTreeError: No tree exists for the current buffer"
1063 return self.equals(b:NERDTreeRoot)
1066 "FUNCTION: TreeFileNode.makeRoot() {{{3
1067 "Make this node the root of the tree
1068 function! s:TreeFileNode.makeRoot()
1069 if self.path.isDirectory
1070 let b:NERDTreeRoot = self
1072 call self.cacheParent()
1073 let b:NERDTreeRoot = self.parent
1076 call b:NERDTreeRoot.open()
1078 "change dir to the dir of the new root if instructed to
1079 if g:NERDTreeChDirMode ==# 2
1080 exec "cd " . b:NERDTreeRoot.path.str({'format': 'Edit'})
1083 "FUNCTION: TreeFileNode.New(path) {{{3
1084 "Returns a new TreeNode object with the given path and parent
1087 "path: a path object representing the full filesystem path to the file/dir that the node represents
1088 function! s:TreeFileNode.New(path)
1089 if a:path.isDirectory
1090 return s:TreeDirNode.New(a:path)
1092 let newTreeNode = copy(self)
1093 let newTreeNode.path = a:path
1094 let newTreeNode.parent = {}
1099 "FUNCTION: TreeFileNode.open() {{{3
1100 "Open the file represented by the given node in the current window, splitting
1101 "the window if needed
1104 "treenode: file node to open
1105 function! s:TreeFileNode.open()
1106 if b:NERDTreeType ==# "secondary"
1107 exec 'edit ' . self.path.str({'format': 'Edit'})
1111 "if the file is already open in this tab then just stick the cursor in it
1112 let winnr = bufwinnr('^' . self.path.str() . '$')
1114 call s:exec(winnr . "wincmd w")
1117 if !s:isWindowUsable(winnr("#")) && s:firstUsableWindow() ==# -1
1118 call self.openSplit()
1121 if !s:isWindowUsable(winnr("#"))
1122 call s:exec(s:firstUsableWindow() . "wincmd w")
1124 call s:exec('wincmd p')
1126 exec ("edit " . self.path.str({'format': 'Edit'}))
1127 catch /^Vim\%((\a\+)\)\=:E37/
1128 call s:putCursorInTreeWin()
1129 throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified."
1130 catch /^Vim\%((\a\+)\)\=:/
1136 "FUNCTION: TreeFileNode.openSplit() {{{3
1137 "Open this node in a new window
1138 function! s:TreeFileNode.openSplit()
1140 if b:NERDTreeType ==# "secondary"
1141 exec "split " . self.path.str({'format': 'Edit'})
1145 " Save the user's settings for splitbelow and splitright
1146 let savesplitbelow=&splitbelow
1147 let savesplitright=&splitright
1149 " 'there' will be set to a command to move from the split window
1150 " back to the explorer window
1152 " 'back' will be set to a command to move from the explorer window
1153 " back to the newly split window
1155 " 'right' and 'below' will be set to the settings needed for
1156 " splitbelow and splitright IF the explorer is the only window.
1158 let there= g:NERDTreeWinPos ==# "left" ? "wincmd h" : "wincmd l"
1159 let back = g:NERDTreeWinPos ==# "left" ? "wincmd l" : "wincmd h"
1160 let right= g:NERDTreeWinPos ==# "left"
1163 " Attempt to go to adjacent window
1166 let onlyOneWin = (winnr("$") ==# 1)
1168 " If no adjacent window, set splitright and splitbelow appropriately
1170 let &splitright=right
1171 let &splitbelow=below
1173 " found adjacent window - invert split direction
1174 let &splitright=!right
1175 let &splitbelow=!below
1178 let splitMode = onlyOneWin ? "vertical" : ""
1180 " Open the new window
1182 exec(splitMode." sp " . self.path.str({'format': 'Edit'}))
1183 catch /^Vim\%((\a\+)\)\=:E37/
1184 call s:putCursorInTreeWin()
1185 throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified."
1186 catch /^Vim\%((\a\+)\)\=:/
1190 "resize the tree window if no other window was open before
1192 let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
1194 exec("silent ". splitMode ." resize ". size)
1195 call s:exec('wincmd p')
1198 " Restore splitmode settings
1199 let &splitbelow=savesplitbelow
1200 let &splitright=savesplitright
1202 "FUNCTION: TreeFileNode.openVSplit() {{{3
1203 "Open this node in a new vertical window
1204 function! s:TreeFileNode.openVSplit()
1205 if b:NERDTreeType ==# "secondary"
1206 exec "vnew " . self.path.str({'format': 'Edit'})
1210 let winwidth = winwidth(".")
1212 let winwidth = g:NERDTreeWinSize
1215 call s:exec("wincmd p")
1216 exec "vnew " . self.path.str({'format': 'Edit'})
1218 "resize the nerd tree back to the original size
1219 call s:putCursorInTreeWin()
1220 exec("silent vertical resize ". winwidth)
1221 call s:exec('wincmd p')
1223 "FUNCTION: TreeFileNode.openInNewTab(options) {{{3
1224 function! s:TreeFileNode.openInNewTab(options)
1225 let currentTab = tabpagenr()
1227 if !has_key(a:options, 'keepTreeOpen')
1228 call s:closeTreeIfQuitOnOpen()
1231 exec "tabedit " . self.path.str({'format': 'Edit'})
1233 if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab']
1234 exec "tabnext " . currentTab
1238 "FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3
1239 "Places the cursor on the line number this node is rendered on
1242 "isJump: 1 if this cursor movement should be counted as a jump by vim
1243 "recurseUpward: try to put the cursor on the parent if the this node isnt
1245 function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
1246 let ln = self.getLineNum()
1251 call cursor(ln, col("."))
1255 while node != {} && node.getLineNum() ==# -1
1256 let node = node.parent
1260 call node.putCursorHere(a:isJump, 0)
1265 "FUNCTION: TreeFileNode.refresh() {{{3
1266 function! s:TreeFileNode.refresh()
1267 call self.path.refresh()
1269 "FUNCTION: TreeFileNode.rename() {{{3
1270 "Calls the rename method for this nodes path obj
1271 function! s:TreeFileNode.rename(newName)
1272 let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
1273 call self.path.rename(newName)
1274 call self.parent.removeChild(self)
1276 let parentPath = self.path.getParent()
1277 let newParent = b:NERDTreeRoot.findNode(parentPath)
1280 call newParent.createChild(self.path, 1)
1281 call newParent.refresh()
1284 "FUNCTION: TreeFileNode.renderToString {{{3
1285 "returns a string representation for this tree to be rendered in the view
1286 function! s:TreeFileNode.renderToString()
1287 return self._renderToString(0, 0, [], self.getChildCount() ==# 1)
1292 "depth: the current depth in the tree for this call
1293 "drawText: 1 if we should actually draw the line for this node (if 0 then the
1294 "child nodes are rendered only)
1295 "vertMap: a binary array that indicates whether a vertical bar should be draw
1296 "for each depth in the tree
1297 "isLastChild:true if this curNode is the last child of its parent
1298 function! s:TreeFileNode._renderToString(depth, drawText, vertMap, isLastChild)
1304 "get all the leading spaces and vertical tree parts for this line
1306 for j in a:vertMap[0:-2]
1308 let treeParts = treeParts . '| '
1310 let treeParts = treeParts . ' '
1315 "get the last vertical tree part for this line which will be different
1316 "if this node is the last child of its parent
1318 let treeParts = treeParts . '`'
1320 let treeParts = treeParts . '|'
1324 "smack the appropriate dir/file symbol on the line before the file/dir
1326 if self.path.isDirectory
1328 let treeParts = treeParts . '~'
1330 let treeParts = treeParts . '+'
1333 let treeParts = treeParts . '-'
1335 let line = treeParts . self.displayString()
1337 let output = output . line . "\n"
1340 "if the node is an open dir, draw its children
1341 if self.path.isDirectory ==# 1 && self.isOpen ==# 1
1343 let childNodesToDraw = self.getVisibleChildren()
1344 if len(childNodesToDraw) > 0
1346 "draw all the nodes children except the last
1347 let lastIndx = len(childNodesToDraw)-1
1349 for i in childNodesToDraw[0:lastIndx-1]
1350 let output = output . i._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
1354 "draw the last child, indicating that it IS the last
1355 let output = output . childNodesToDraw[lastIndx]._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 0), 1)
1361 "CLASS: TreeDirNode {{{2
1362 "This class is a child of the TreeFileNode class and constitutes the
1363 "'Composite' part of the composite design pattern between the treenode
1365 "============================================================
1366 let s:TreeDirNode = copy(s:TreeFileNode)
1367 "FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
1368 "class method that returns the highest cached ancestor of the current root
1369 function! s:TreeDirNode.AbsoluteTreeRoot()
1370 let currentNode = b:NERDTreeRoot
1371 while currentNode.parent != {}
1372 let currentNode = currentNode.parent
1376 "FUNCTION: TreeDirNode.activate(forceKeepWinOpen) {{{3
1377 unlet s:TreeDirNode.activate
1378 function! s:TreeDirNode.activate(forceKeepWinOpen)
1379 call self.toggleOpen()
1381 call self.putCursorHere(0, 0)
1383 "FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{3
1384 "Adds the given treenode to the list of children for this node
1387 "-treenode: the node to add
1388 "-inOrder: 1 if the new node should be inserted in sorted order
1389 function! s:TreeDirNode.addChild(treenode, inOrder)
1390 call add(self.children, a:treenode)
1391 let a:treenode.parent = self
1394 call self.sortChildren()
1398 "FUNCTION: TreeDirNode.close() {{{3
1399 "Closes this directory
1400 function! s:TreeDirNode.close()
1404 "FUNCTION: TreeDirNode.closeChildren() {{{3
1405 "Closes all the child dir nodes of this node
1406 function! s:TreeDirNode.closeChildren()
1407 for i in self.children
1408 if i.path.isDirectory
1410 call i.closeChildren()
1415 "FUNCTION: TreeDirNode.createChild(path, inOrder) {{{3
1416 "Instantiates a new child node for this node with the given path. The new
1417 "nodes parent is set to this node.
1420 "path: a Path object that this node will represent/contain
1421 "inOrder: 1 if the new node should be inserted in sorted order
1424 "the newly created node
1425 function! s:TreeDirNode.createChild(path, inOrder)
1426 let newTreeNode = s:TreeFileNode.New(a:path)
1427 call self.addChild(newTreeNode, a:inOrder)
1431 "FUNCTION: TreeDirNode.findNode(path) {{{3
1432 "Will find one of the children (recursively) that has the given path
1435 "path: a path object
1436 unlet s:TreeDirNode.findNode
1437 function! s:TreeDirNode.findNode(path)
1438 if a:path.equals(self.path)
1441 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1445 if self.path.isDirectory
1446 for i in self.children
1447 let retVal = i.findNode(a:path)
1455 "FUNCTION: TreeDirNode.getChildCount() {{{3
1456 "Returns the number of children this node has
1457 function! s:TreeDirNode.getChildCount()
1458 return len(self.children)
1461 "FUNCTION: TreeDirNode.getChild(path) {{{3
1462 "Returns child node of this node that has the given path or {} if no such node
1465 "This function doesnt not recurse into child dir nodes
1468 "path: a path object
1469 function! s:TreeDirNode.getChild(path)
1470 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1474 let index = self.getChildIndex(a:path)
1478 return self.children[index]
1483 "FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{3
1484 "returns the child at the given index
1486 "indx: the index to get the child from
1487 "visible: 1 if only the visible children array should be used, 0 if all the
1488 "children should be searched.
1489 function! s:TreeDirNode.getChildByIndex(indx, visible)
1490 let array_to_search = a:visible? self.getVisibleChildren() : self.children
1491 if a:indx > len(array_to_search)
1492 throw "NERDTree.InvalidArgumentsError: Index is out of bounds."
1494 return array_to_search[a:indx]
1497 "FUNCTION: TreeDirNode.getChildIndex(path) {{{3
1498 "Returns the index of the child node of this node that has the given path or
1499 "-1 if no such node exists.
1501 "This function doesnt not recurse into child dir nodes
1504 "path: a path object
1505 function! s:TreeDirNode.getChildIndex(path)
1506 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1510 "do a binary search for the child
1512 let z = self.getChildCount()
1515 let diff = a:path.compareTo(self.children[mid].path)
1528 "FUNCTION: TreeDirNode.GetSelected() {{{3
1529 "Returns the current node if it is a dir node, or else returns the current
1531 unlet s:TreeDirNode.GetSelected
1532 function! s:TreeDirNode.GetSelected()
1533 let currentDir = s:TreeFileNode.GetSelected()
1534 if currentDir != {} && !currentDir.isRoot()
1535 if currentDir.path.isDirectory ==# 0
1536 let currentDir = currentDir.parent
1541 "FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
1542 "Returns the number of visible children this node has
1543 function! s:TreeDirNode.getVisibleChildCount()
1544 return len(self.getVisibleChildren())
1547 "FUNCTION: TreeDirNode.getVisibleChildren() {{{3
1548 "Returns a list of children to display for this node, in the correct order
1551 "an array of treenodes
1552 function! s:TreeDirNode.getVisibleChildren()
1554 for i in self.children
1555 if i.path.ignore() ==# 0
1556 call add(toReturn, i)
1562 "FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
1563 "returns 1 if this node has any childre, 0 otherwise..
1564 function! s:TreeDirNode.hasVisibleChildren()
1565 return self.getVisibleChildCount() != 0
1568 "FUNCTION: TreeDirNode._initChildren() {{{3
1569 "Removes all childen from this node and re-reads them
1572 "silent: 1 if the function should not echo any "please wait" messages for
1575 "Return: the number of child nodes read
1576 function! s:TreeDirNode._initChildren(silent)
1577 "remove all the current child nodes
1578 let self.children = []
1580 "get an array of all the files in the nodes dir
1582 let globDir = dir.str({'format': 'Glob'})
1583 let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
1584 let files = split(filesStr, "\n")
1586 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
1587 call s:echo("Please wait, caching a large dir ...")
1590 let invalidFilesFound = 0
1593 "filter out the .. and . directories
1594 "Note: we must match .. AND ../ cos sometimes the globpath returns
1595 "../ for path with strange chars (eg $)
1596 if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$'
1598 "put the next file in a new node and attach it
1600 let path = s:Path.New(i)
1601 call self.createChild(path, 0)
1602 catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
1603 let invalidFilesFound += 1
1608 call self.sortChildren()
1610 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
1611 call s:echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).")
1614 if invalidFilesFound
1615 call s:echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree")
1617 return self.getChildCount()
1619 "FUNCTION: TreeDirNode.New(path) {{{3
1620 "Returns a new TreeNode object with the given path and parent
1623 "path: a path object representing the full filesystem path to the file/dir that the node represents
1624 unlet s:TreeDirNode.New
1625 function! s:TreeDirNode.New(path)
1626 if a:path.isDirectory != 1
1627 throw "NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object."
1630 let newTreeNode = copy(self)
1631 let newTreeNode.path = a:path
1633 let newTreeNode.isOpen = 0
1634 let newTreeNode.children = []
1636 let newTreeNode.parent = {}
1640 "FUNCTION: TreeDirNode.open() {{{3
1641 "Reads in all this nodes children
1643 "Return: the number of child nodes read
1644 unlet s:TreeDirNode.open
1645 function! s:TreeDirNode.open()
1647 if self.children ==# []
1648 return self._initChildren(0)
1654 " FUNCTION: TreeDirNode.openExplorer() {{{3
1655 " opens an explorer window for this node in the previous window (could be a
1656 " nerd tree or a netrw)
1657 function! s:TreeDirNode.openExplorer()
1658 let oldwin = winnr()
1659 call s:exec('wincmd p')
1660 if oldwin ==# winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2)
1661 call s:exec('wincmd p')
1662 call self.openSplit()
1664 exec ("silent edit " . self.path.str({'format': 'Edit'}))
1667 "FUNCTION: TreeDirNode.openInNewTab(options) {{{3
1668 unlet s:TreeDirNode.openInNewTab
1669 function! s:TreeDirNode.openInNewTab(options)
1670 let currentTab = tabpagenr()
1672 if !has_key(a:options, 'keepTreeOpen') || !a:options['keepTreeOpen']
1673 call s:closeTreeIfQuitOnOpen()
1677 call s:initNerdTree(self.path.str())
1679 if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab']
1680 exec "tabnext " . currentTab
1683 "FUNCTION: TreeDirNode.openRecursively() {{{3
1684 "Opens this treenode and all of its children whose paths arent 'ignored'
1685 "because of the file filters.
1687 "This method is actually a wrapper for the OpenRecursively2 method which does
1689 function! s:TreeDirNode.openRecursively()
1690 call self._openRecursively2(1)
1693 "FUNCTION: TreeDirNode._openRecursively2() {{{3
1694 "Opens this all children of this treenode recursively if either:
1695 " *they arent filtered by file filters
1699 "forceOpen: 1 if this node should be opened regardless of file filters
1700 function! s:TreeDirNode._openRecursively2(forceOpen)
1701 if self.path.ignore() ==# 0 || a:forceOpen
1703 if self.children ==# []
1704 call self._initChildren(1)
1707 for i in self.children
1708 if i.path.isDirectory ==# 1
1709 call i._openRecursively2(0)
1715 "FUNCTION: TreeDirNode.refresh() {{{3
1716 unlet s:TreeDirNode.refresh
1717 function! s:TreeDirNode.refresh()
1718 call self.path.refresh()
1720 "if this node was ever opened, refresh its children
1721 if self.isOpen || !empty(self.children)
1722 "go thru all the files/dirs under this node
1723 let newChildNodes = []
1724 let invalidFilesFound = 0
1726 let globDir = dir.str({'format': 'Glob'})
1727 let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
1728 let files = split(filesStr, "\n")
1730 "filter out the .. and . directories
1731 "Note: we must match .. AND ../ cos sometimes the globpath returns
1732 "../ for path with strange chars (eg $)
1733 if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$'
1736 "create a new path and see if it exists in this nodes children
1737 let path = s:Path.New(i)
1738 let newNode = self.getChild(path)
1740 call newNode.refresh()
1741 call add(newChildNodes, newNode)
1743 "the node doesnt exist so create it
1745 let newNode = s:TreeFileNode.New(path)
1746 let newNode.parent = self
1747 call add(newChildNodes, newNode)
1751 catch /^NERDTree.InvalidArgumentsError/
1752 let invalidFilesFound = 1
1757 "swap this nodes children out for the children we just read/refreshed
1758 let self.children = newChildNodes
1759 call self.sortChildren()
1761 if invalidFilesFound
1762 call s:echoWarning("some files could not be loaded into the NERD tree")
1767 "FUNCTION: TreeDirNode.reveal(path) {{{3
1768 "reveal the given path, i.e. cache and open all treenodes needed to display it
1770 function! s:TreeDirNode.reveal(path)
1771 if !a:path.isUnder(self.path)
1772 throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str()
1777 if self.path.equals(a:path.getParent())
1778 let n = self.findNode(a:path)
1780 call n.putCursorHere(1,0)
1785 while !p.getParent().equals(self.path)
1786 let p = p.getParent()
1789 let n = self.findNode(p)
1790 call n.reveal(a:path)
1792 "FUNCTION: TreeDirNode.removeChild(treenode) {{{3
1794 "Removes the given treenode from this nodes set of children
1797 "treenode: the node to remove
1799 "Throws a NERDTree.ChildNotFoundError if the given treenode is not found
1800 function! s:TreeDirNode.removeChild(treenode)
1801 for i in range(0, self.getChildCount()-1)
1802 if self.children[i].equals(a:treenode)
1803 call remove(self.children, i)
1808 throw "NERDTree.ChildNotFoundError: child node was not found"
1811 "FUNCTION: TreeDirNode.sortChildren() {{{3
1813 "Sorts the children of this node according to alphabetical order and the
1814 "directory priority.
1816 function! s:TreeDirNode.sortChildren()
1817 let CompareFunc = function("s:compareNodes")
1818 call sort(self.children, CompareFunc)
1821 "FUNCTION: TreeDirNode.toggleOpen() {{{3
1822 "Opens this directory if it is closed and vice versa
1823 function! s:TreeDirNode.toggleOpen()
1824 if self.isOpen ==# 1
1831 "FUNCTION: TreeDirNode.transplantChild(newNode) {{{3
1832 "Replaces the child of this with the given node (where the child node's full
1833 "path matches a:newNode's fullpath). The search for the matching node is
1837 "newNode: the node to graft into the tree
1838 function! s:TreeDirNode.transplantChild(newNode)
1839 for i in range(0, self.getChildCount()-1)
1840 if self.children[i].equals(a:newNode)
1841 let self.children[i] = a:newNode
1842 let a:newNode.parent = self
1847 "============================================================
1849 "============================================================
1851 "FUNCTION: Path.AbsolutePathFor(str) {{{3
1852 function! s:Path.AbsolutePathFor(str)
1854 if s:running_windows
1855 let prependCWD = a:str !~ '^.:\(\\\|\/\)'
1857 let prependCWD = a:str !~ '^/'
1860 let toReturn = a:str
1862 let toReturn = getcwd() . s:Path.Slash() . a:str
1867 "FUNCTION: Path.bookmarkNames() {{{3
1868 function! s:Path.bookmarkNames()
1869 if !exists("self._bookmarkNames")
1870 call self.cacheDisplayString()
1872 return self._bookmarkNames
1874 "FUNCTION: Path.cacheDisplayString() {{{3
1875 function! s:Path.cacheDisplayString()
1876 let self.cachedDisplayString = self.getLastPathComponent(1)
1878 if self.isExecutable
1879 let self.cachedDisplayString = self.cachedDisplayString . '*'
1882 let self._bookmarkNames = []
1883 for i in s:Bookmark.Bookmarks()
1884 if i.path.equals(self)
1885 call add(self._bookmarkNames, i.name)
1888 if !empty(self._bookmarkNames)
1889 let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
1893 let self.cachedDisplayString .= ' -> ' . self.symLinkDest
1897 let self.cachedDisplayString .= ' [RO]'
1900 "FUNCTION: Path.changeToDir() {{{3
1901 function! s:Path.changeToDir()
1902 let dir = self.str({'format': 'Cd'})
1903 if self.isDirectory ==# 0
1904 let dir = self.getParent().str({'format': 'Cd'})
1909 call s:echo("CWD is now: " . getcwd())
1911 throw "NERDTree.PathChangeError: cannot change CWD to " . dir
1915 "FUNCTION: Path.compareTo() {{{3
1917 "Compares this Path to the given path and returns 0 if they are equal, -1 if
1918 "this Path is "less than" the given path, or 1 if it is "greater".
1921 "path: the path object to compare this to
1925 function! s:Path.compareTo(path)
1926 let thisPath = self.getLastPathComponent(1)
1927 let thatPath = a:path.getLastPathComponent(1)
1929 "if the paths are the same then clearly we return 0
1930 if thisPath ==# thatPath
1934 let thisSS = self.getSortOrderIndex()
1935 let thatSS = a:path.getSortOrderIndex()
1937 "compare the sort sequences, if they are different then the return
1941 elseif thisSS > thatSS
1944 "if the sort sequences are the same then compare the paths
1946 let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath
1955 "FUNCTION: Path.Create(fullpath) {{{3
1959 "Creates a path object with the given path. The path is also created on the
1960 "filesystem. If the path already exists, a NERDTree.Path.Exists exception is
1961 "thrown. If any other errors occur, a NERDTree.Path exception is thrown.
1964 "fullpath: the full filesystem path to the file/dir to create
1965 function! s:Path.Create(fullpath)
1966 "bail if the a:fullpath already exists
1967 if isdirectory(a:fullpath) || filereadable(a:fullpath)
1968 throw "NERDTree.CreatePathError: Directory Exists: '" . a:fullpath . "'"
1973 "if it ends with a slash, assume its a dir create it
1974 if a:fullpath =~ '\(\\\|\/\)$'
1975 "whack the trailing slash off the end if it exists
1976 let fullpath = substitute(a:fullpath, '\(\\\|\/\)$', '', '')
1978 call mkdir(fullpath, 'p')
1980 "assume its a file and create
1982 call writefile([], a:fullpath)
1985 throw "NERDTree.CreatePathError: Could not create path: '" . a:fullpath . "'"
1988 return s:Path.New(a:fullpath)
1991 "FUNCTION: Path.copy(dest) {{{3
1993 "Copies the file/dir represented by this Path to the given location
1996 "dest: the location to copy this dir/file to
1997 function! s:Path.copy(dest)
1998 if !s:Path.CopyingSupported()
1999 throw "NERDTree.CopyingNotSupportedError: Copying is not supported on this OS"
2002 let dest = s:Path.WinToUnixPath(a:dest)
2004 let cmd = g:NERDTreeCopyCmd . " " . self.str() . " " . dest
2005 let success = system(cmd)
2007 throw "NERDTree.CopyError: Could not copy ''". self.str() ."'' to: '" . a:dest . "'"
2011 "FUNCTION: Path.CopyingSupported() {{{3
2013 "returns 1 if copying is supported for this OS
2014 function! s:Path.CopyingSupported()
2015 return exists('g:NERDTreeCopyCmd')
2019 "FUNCTION: Path.copyingWillOverwrite(dest) {{{3
2021 "returns 1 if copy this path to the given location will cause files to
2025 "dest: the location this path will be copied to
2026 function! s:Path.copyingWillOverwrite(dest)
2027 if filereadable(a:dest)
2031 if isdirectory(a:dest)
2032 let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
2033 if filereadable(path)
2039 "FUNCTION: Path.delete() {{{3
2041 "Deletes the file represented by this path.
2042 "Deletion of directories is not supported
2044 "Throws NERDTree.Path.Deletion exceptions
2045 function! s:Path.delete()
2048 let cmd = g:NERDTreeRemoveDirCmd . self.str({'escape': 1})
2049 let success = system(cmd)
2051 if v:shell_error != 0
2052 throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'"
2055 let success = delete(self.str())
2057 throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'"
2061 "delete all bookmarks for this path
2062 for i in self.bookmarkNames()
2063 let bookmark = s:Bookmark.BookmarkFor(i)
2064 call bookmark.delete()
2068 "FUNCTION: Path.displayString() {{{3
2070 "Returns a string that specifies how the path should be represented as a
2072 function! s:Path.displayString()
2073 if self.cachedDisplayString ==# ""
2074 call self.cacheDisplayString()
2077 return self.cachedDisplayString
2079 "FUNCTION: Path.extractDriveLetter(fullpath) {{{3
2081 "If running windows, cache the drive letter for this path
2082 function! s:Path.extractDriveLetter(fullpath)
2083 if s:running_windows
2084 let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
2090 "FUNCTION: Path.exists() {{{3
2091 "return 1 if this path points to a location that is readable or is a directory
2092 function! s:Path.exists()
2094 return filereadable(p) || isdirectory(p)
2096 "FUNCTION: Path.getDir() {{{3
2098 "Returns this path if it is a directory, else this paths parent.
2102 function! s:Path.getDir()
2106 return self.getParent()
2109 "FUNCTION: Path.getParent() {{{3
2111 "Returns a new path object for this paths parent
2115 function! s:Path.getParent()
2116 if s:running_windows
2117 let path = self.drive . '\' . join(self.pathSegments[0:-2], '\')
2119 let path = '/'. join(self.pathSegments[0:-2], '/')
2122 return s:Path.New(path)
2124 "FUNCTION: Path.getLastPathComponent(dirSlash) {{{3
2126 "Gets the last part of this path.
2129 "dirSlash: if 1 then a trailing slash will be added to the returned value for
2131 function! s:Path.getLastPathComponent(dirSlash)
2132 if empty(self.pathSegments)
2135 let toReturn = self.pathSegments[-1]
2136 if a:dirSlash && self.isDirectory
2137 let toReturn = toReturn . '/'
2142 "FUNCTION: Path.getSortOrderIndex() {{{3
2143 "returns the index of the pattern in g:NERDTreeSortOrder that this path matches
2144 function! s:Path.getSortOrderIndex()
2146 while i < len(g:NERDTreeSortOrder)
2147 if self.getLastPathComponent(1) =~ g:NERDTreeSortOrder[i]
2152 return s:NERDTreeSortStarIndex
2155 "FUNCTION: Path.ignore() {{{3
2156 "returns true if this path should be ignored
2157 function! s:Path.ignore()
2158 let lastPathComponent = self.getLastPathComponent(0)
2160 "filter out the user specified paths to ignore
2161 if b:NERDTreeIgnoreEnabled
2162 for i in g:NERDTreeIgnore
2163 if lastPathComponent =~ i
2169 "dont show hidden files unless instructed to
2170 if b:NERDTreeShowHidden ==# 0 && lastPathComponent =~ '^\.'
2174 if b:NERDTreeShowFiles ==# 0 && self.isDirectory ==# 0
2181 "FUNCTION: Path.isUnder(path) {{{3
2182 "return 1 if this path is somewhere under the given path in the filesystem.
2184 "a:path should be a dir
2185 function! s:Path.isUnder(path)
2186 if a:path.isDirectory == 0
2190 let this = self.str()
2191 let that = a:path.str()
2192 return stridx(this, that . s:Path.Slash()) == 0
2195 "FUNCTION: Path.JoinPathStrings(...) {{{3
2196 function! s:Path.JoinPathStrings(...)
2199 let components = extend(components, split(i, '/'))
2201 return '/' . join(components, '/')
2204 "FUNCTION: Path.equals() {{{3
2206 "Determines whether 2 path objects are "equal".
2207 "They are equal if the paths they represent are the same
2210 "path: the other path obj to compare this with
2211 function! s:Path.equals(path)
2212 return self.str() ==# a:path.str()
2215 "FUNCTION: Path.New() {{{3
2216 "The Constructor for the Path object
2217 function! s:Path.New(path)
2218 let newPath = copy(self)
2220 call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path))
2222 let newPath.cachedDisplayString = ""
2227 "FUNCTION: Path.Slash() {{{3
2228 "return the slash to use for the current OS
2229 function! s:Path.Slash()
2230 return s:running_windows ? '\' : '/'
2233 "FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
2236 "Throws NERDTree.Path.InvalidArguments exception.
2237 function! s:Path.readInfoFromDisk(fullpath)
2238 call self.extractDriveLetter(a:fullpath)
2240 let fullpath = s:Path.WinToUnixPath(a:fullpath)
2242 if getftype(fullpath) ==# "fifo"
2243 throw "NERDTree.InvalidFiletypeError: Cant handle FIFO files: " . a:fullpath
2246 let self.pathSegments = split(fullpath, '/')
2248 let self.isReadOnly = 0
2249 if isdirectory(a:fullpath)
2250 let self.isDirectory = 1
2251 elseif filereadable(a:fullpath)
2252 let self.isDirectory = 0
2253 let self.isReadOnly = filewritable(a:fullpath) ==# 0
2255 throw "NERDTree.InvalidArgumentsError: Invalid path = " . a:fullpath
2258 let self.isExecutable = 0
2259 if !self.isDirectory
2260 let self.isExecutable = getfperm(a:fullpath) =~ 'x'
2263 "grab the last part of the path (minus the trailing slash)
2264 let lastPathComponent = self.getLastPathComponent(0)
2266 "get the path to the new node with the parent dir fully resolved
2267 let hardPath = resolve(self.strTrunk()) . '/' . lastPathComponent
2269 "if the last part of the path is a symlink then flag it as such
2270 let self.isSymLink = (resolve(hardPath) != hardPath)
2272 let self.symLinkDest = resolve(fullpath)
2274 "if the link is a dir then slap a / on the end of its dest
2275 if isdirectory(self.symLinkDest)
2277 "we always wanna treat MS windows shortcuts as files for
2279 if hardPath !~ '\.lnk$'
2281 let self.symLinkDest = self.symLinkDest . '/'
2287 "FUNCTION: Path.refresh() {{{3
2288 function! s:Path.refresh()
2289 call self.readInfoFromDisk(self.str())
2290 call self.cacheDisplayString()
2293 "FUNCTION: Path.rename() {{{3
2295 "Renames this node on the filesystem
2296 function! s:Path.rename(newPath)
2298 throw "NERDTree.InvalidArgumentsError: Invalid newPath for renaming = ". a:newPath
2301 let success = rename(self.str(), a:newPath)
2303 throw "NERDTree.PathRenameError: Could not rename: '" . self.str() . "'" . 'to:' . a:newPath
2305 call self.readInfoFromDisk(a:newPath)
2307 for i in self.bookmarkNames()
2308 let b = s:Bookmark.BookmarkFor(i)
2309 call b.setPath(copy(self))
2311 call s:Bookmark.Write()
2314 "FUNCTION: Path.str() {{{3
2316 "Returns a string representation of this Path
2318 "Takes an optional dictionary param to specify how the output should be
2321 "The dict may have the following keys:
2326 "The 'format' key may have a value of:
2327 " 'Cd' - a string to be used with the :cd command
2328 " 'Edit' - a string to be used with :e :sp :new :tabedit etc
2329 " 'UI' - a string used in the NERD tree UI
2331 "The 'escape' key, if specified will cause the output to be escaped with
2334 "The 'truncateTo' key causes the resulting string to be truncated to the value
2335 "'truncateTo' maps to. A '<' char will be prepended.
2336 function! s:Path.str(...)
2337 let options = a:0 ? a:1 : {}
2340 if has_key(options, 'format')
2341 let format = options['format']
2342 if has_key(self, '_strFor' . format)
2343 exec 'let toReturn = self._strFor' . format . '()'
2345 raise 'NERDTree.UnknownFormatError: unknown format "'. format .'"'
2348 let toReturn = self._str()
2351 if has_key(options, 'escape') && options['escape']
2352 let toReturn = shellescape(toReturn)
2355 if has_key(options, 'truncateTo')
2356 let limit = options['truncateTo']
2357 if len(toReturn) > limit
2358 let toReturn = "<" . strpart(toReturn, len(toReturn) - limit + 1)
2365 "FUNCTION: Path._strForUI() {{{3
2366 function! s:Path._strForUI()
2367 let toReturn = '/' . join(self.pathSegments, '/')
2368 if self.isDirectory && toReturn != '/'
2369 let toReturn = toReturn . '/'
2374 "FUNCTION: Path._strForCd() {{{3
2376 " returns a string that can be used with :cd
2377 function! s:Path._strForCd()
2378 return escape(self.str(), s:escape_chars)
2380 "FUNCTION: Path._strForEdit() {{{3
2382 "Return: the string for this path that is suitable to be used with the :edit
2384 function! s:Path._strForEdit()
2385 let p = self.str({'format': 'UI'})
2388 if s:running_windows
2389 let p = tolower(self.str())
2390 let cwd = tolower(getcwd())
2393 let p = escape(p, s:escape_chars)
2395 let cwd = cwd . s:Path.Slash()
2397 "return a relative path if we can
2398 if stridx(p, cwd) ==# 0
2399 let p = strpart(p, strlen(cwd))
2409 "FUNCTION: Path._strForGlob() {{{3
2410 function! s:Path._strForGlob()
2411 let lead = s:Path.Slash()
2413 "if we are running windows then slap a drive letter on the front
2414 if s:running_windows
2415 let lead = self.drive . '\'
2418 let toReturn = lead . join(self.pathSegments, s:Path.Slash())
2420 if !s:running_windows
2421 let toReturn = escape(toReturn, s:escape_chars)
2425 "FUNCTION: Path._str() {{{3
2427 "Gets the string path for this path object that is appropriate for the OS.
2428 "EG, in windows c:\foo\bar
2430 function! s:Path._str()
2431 let lead = s:Path.Slash()
2433 "if we are running windows then slap a drive letter on the front
2434 if s:running_windows
2435 let lead = self.drive . '\'
2438 return lead . join(self.pathSegments, s:Path.Slash())
2441 "FUNCTION: Path.strTrunk() {{{3
2442 "Gets the path without the last segment on the end.
2443 function! s:Path.strTrunk()
2444 return self.drive . '/' . join(self.pathSegments[0:-2], '/')
2447 "FUNCTION: Path.WinToUnixPath(pathstr){{{3
2448 "Takes in a windows path and returns the unix equiv
2450 "A class level method
2453 "pathstr: the windows path to convert
2454 function! s:Path.WinToUnixPath(pathstr)
2455 if !s:running_windows
2459 let toReturn = a:pathstr
2461 "remove the x:\ of the front
2462 let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "")
2464 "convert all \ chars to /
2465 let toReturn = substitute(toReturn, '\', '/', "g")
2470 " SECTION: General Functions {{{1
2471 "============================================================
2472 "FUNCTION: s:bufInWindows(bnum){{{2
2473 "[[STOLEN FROM VTREEEXPLORER.VIM]]
2474 "Determine the number of windows open to this buffer number.
2475 "Care of Yegappan Lakshman. Thanks!
2478 "bnum: the subject buffers buffer number
2479 function! s:bufInWindows(bnum)
2483 let bufnum = winbufnr(winnum)
2487 if bufnum ==# a:bnum
2490 let winnum = winnum + 1
2495 "FUNCTION: s:checkForBrowse(dir) {{{2
2496 "inits a secondary nerd tree in the current buffer if appropriate
2497 function! s:checkForBrowse(dir)
2498 if a:dir != '' && isdirectory(a:dir)
2499 call s:initNerdTreeInPlace(a:dir)
2502 "FUNCTION: s:compareBookmarks(first, second) {{{2
2503 "Compares two bookmarks
2504 function! s:compareBookmarks(first, second)
2505 return a:first.compareTo(a:second)
2508 " FUNCTION: s:completeBookmarks(A,L,P) {{{2
2509 " completion function for the bookmark commands
2510 function! s:completeBookmarks(A,L,P)
2511 return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"')
2513 " FUNCTION: s:exec(cmd) {{{2
2514 " same as :exec cmd but eventignore=all is set for the duration
2515 function! s:exec(cmd)
2521 " FUNCTION: s:findAndRevealPath() {{{2
2522 function! s:findAndRevealPath()
2524 let p = s:Path.New(expand("%"))
2525 catch /^NERDTree.InvalidArgumentsError/
2526 call s:echo("no file for the current buffer")
2530 if !s:treeExistsForTab()
2531 call s:initNerdTree(p.getParent().str())
2533 if !p.isUnder(s:TreeFileNode.GetRootForTab().path)
2534 call s:initNerdTree(p.getParent().str())
2541 call s:putCursorInTreeWin()
2542 call b:NERDTreeRoot.reveal(p)
2544 "FUNCTION: s:initNerdTree(name) {{{2
2545 "Initialise the nerd tree for this tab. The tree will start in either the
2546 "given directory, or the directory associated with the given bookmark
2549 "name: the name of a bookmark or a directory
2550 function! s:initNerdTree(name)
2552 if s:Bookmark.BookmarkExistsFor(a:name)
2553 let path = s:Bookmark.BookmarkFor(a:name).path
2555 let dir = a:name ==# '' ? getcwd() : a:name
2557 "hack to get an absolute path if a relative path is given
2559 let dir = getcwd() . s:Path.Slash() . dir
2561 let dir = resolve(dir)
2564 let path = s:Path.New(dir)
2565 catch /^NERDTree.InvalidArgumentsError/
2566 call s:echo("No bookmark or directory found for: " . a:name)
2570 if !path.isDirectory
2571 let path = path.getParent()
2574 "if instructed to, then change the vim CWD to the dir the NERDTree is
2576 if g:NERDTreeChDirMode != 0
2577 call path.changeToDir()
2580 if s:treeExistsForTab()
2584 unlet t:NERDTreeBufName
2587 let newRoot = s:TreeDirNode.New(path)
2590 call s:createTreeWin()
2591 let b:treeShowHelp = 0
2592 let b:NERDTreeIgnoreEnabled = 1
2593 let b:NERDTreeShowFiles = g:NERDTreeShowFiles
2594 let b:NERDTreeShowHidden = g:NERDTreeShowHidden
2595 let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
2596 let b:NERDTreeRoot = newRoot
2598 let b:NERDTreeType = "primary"
2601 call b:NERDTreeRoot.putCursorHere(0, 0)
2604 "FUNCTION: s:initNerdTreeInPlace(dir) {{{2
2605 function! s:initNerdTreeInPlace(dir)
2607 let path = s:Path.New(a:dir)
2608 catch /^NERDTree.InvalidArgumentsError/
2609 call s:echo("Invalid directory name:" . a:name)
2613 "we want the directory buffer to disappear when we do the :edit below
2614 setlocal bufhidden=wipe
2616 let previousBuf = expand("#")
2618 "we need a unique name for each secondary tree buffer to ensure they are
2620 exec "silent edit " . s:nextBufferName()
2622 let b:NERDTreePreviousBuf = bufnr(previousBuf)
2624 let b:NERDTreeRoot = s:TreeDirNode.New(path)
2625 call b:NERDTreeRoot.open()
2627 "throwaway buffer options
2629 setlocal buftype=nofile
2630 setlocal bufhidden=hide
2632 setlocal foldcolumn=0
2633 setlocal nobuflisted
2635 if g:NERDTreeShowLineNumbers
2643 if g:NERDTreeHighlightCursorline
2647 call s:setupStatusline()
2649 let b:treeShowHelp = 0
2650 let b:NERDTreeIgnoreEnabled = 1
2651 let b:NERDTreeShowFiles = g:NERDTreeShowFiles
2652 let b:NERDTreeShowHidden = g:NERDTreeShowHidden
2653 let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
2655 let b:NERDTreeType = "secondary"
2657 call s:bindMappings()
2658 setfiletype nerdtree
2659 " syntax highlighting
2660 if has("syntax") && exists("g:syntax_on")
2661 call s:setupSyntaxHighlighting()
2666 " FUNCTION: s:initNerdTreeMirror() {{{2
2667 function! s:initNerdTreeMirror()
2669 "get the names off all the nerd tree buffers
2670 let treeBufNames = []
2671 for i in range(1, tabpagenr("$"))
2672 let nextName = s:tabpagevar(i, 'NERDTreeBufName')
2673 if nextName != -1 && (!exists("t:NERDTreeBufName") || nextName != t:NERDTreeBufName)
2674 call add(treeBufNames, nextName)
2677 let treeBufNames = s:unique(treeBufNames)
2679 "map the option names (that the user will be prompted with) to the nerd
2683 while i < len(treeBufNames)
2684 let bufName = treeBufNames[i]
2685 let treeRoot = getbufvar(bufName, "NERDTreeRoot")
2686 let options[i+1 . '. ' . treeRoot.path.str() . ' (buf name: ' . bufName . ')'] = bufName
2690 "work out which tree to mirror, if there is more than 1 then ask the user
2692 if len(keys(options)) > 1
2693 let choices = ["Choose a tree to mirror"]
2694 let choices = extend(choices, sort(keys(options)))
2695 let choice = inputlist(choices)
2696 if choice < 1 || choice > len(options) || choice ==# ''
2700 let bufferName = options[sort(keys(options))[choice-1]]
2701 elseif len(keys(options)) ==# 1
2702 let bufferName = values(options)[0]
2704 call s:echo("No trees to mirror")
2708 if s:treeExistsForTab() && s:isTreeOpen()
2712 let t:NERDTreeBufName = bufferName
2713 call s:createTreeWin()
2714 exec 'buffer ' . bufferName
2719 " FUNCTION: s:nextBufferName() {{{2
2720 " returns the buffer name for the next nerd tree
2721 function! s:nextBufferName()
2722 let name = s:NERDTreeBufName . s:next_buffer_number
2723 let s:next_buffer_number += 1
2726 " FUNCTION: s:tabpagevar(tabnr, var) {{{2
2727 function! s:tabpagevar(tabnr, var)
2728 let currentTab = tabpagenr()
2732 exec "tabnext " . a:tabnr
2734 if exists('t:' . a:var)
2735 exec 'let v = t:' . a:var
2737 exec "tabnext " . currentTab
2743 " Function: s:treeExistsForBuffer() {{{2
2744 " Returns 1 if a nerd tree root exists in the current buffer
2745 function! s:treeExistsForBuf()
2746 return exists("b:NERDTreeRoot")
2748 " Function: s:treeExistsForTab() {{{2
2749 " Returns 1 if a nerd tree root exists in the current tab
2750 function! s:treeExistsForTab()
2751 return exists("t:NERDTreeBufName")
2753 " Function: s:unique(list) {{{2
2754 " returns a:list without duplicates
2755 function! s:unique(list)
2758 if index(uniqlist, elem) ==# -1
2759 let uniqlist += [elem]
2764 " SECTION: Public API {{{1
2765 "============================================================
2766 let g:NERDTreePath = s:Path
2767 let g:NERDTreeDirNode = s:TreeDirNode
2768 let g:NERDTreeFileNode = s:TreeFileNode
2769 let g:NERDTreeBookmark = s:Bookmark
2771 function! NERDTreeAddMenuItem(options)
2772 call s:MenuItem.Create(a:options)
2775 function! NERDTreeAddMenuSeparator(...)
2776 let opts = a:0 ? a:1 : {}
2777 call s:MenuItem.CreateSeparator(opts)
2780 function! NERDTreeAddSubmenu(options)
2781 return s:MenuItem.Create(a:options)
2784 function! NERDTreeAddKeyMap(options)
2785 call s:KeyMap.Create(a:options)
2788 function! NERDTreeRender()
2792 " SECTION: View Functions {{{1
2793 "============================================================
2794 "FUNCTION: s:centerView() {{{2
2795 "centers the nerd tree window around the cursor (provided the nerd tree
2797 function! s:centerView()
2798 if g:NERDTreeAutoCenter
2799 let current_line = winline()
2800 let lines_to_top = current_line
2801 let lines_to_bottom = winheight(s:getTreeWinNum()) - current_line
2802 if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold
2807 "FUNCTION: s:closeTree() {{{2
2808 "Closes the primary NERD tree window for this tab
2809 function! s:closeTree()
2811 throw "NERDTree.NoTreeFoundError: no NERDTree is open"
2815 call s:exec(s:getTreeWinNum() . " wincmd w")
2817 call s:exec("wincmd p")
2823 "FUNCTION: s:closeTreeIfOpen() {{{2
2824 "Closes the NERD tree window if it is open
2825 function! s:closeTreeIfOpen()
2830 "FUNCTION: s:closeTreeIfQuitOnOpen() {{{2
2831 "Closes the NERD tree window if the close on open option is set
2832 function! s:closeTreeIfQuitOnOpen()
2833 if g:NERDTreeQuitOnOpen && s:isTreeOpen()
2837 "FUNCTION: s:createTreeWin() {{{2
2838 "Inits the NERD tree window. ie. opens it, sizes it, sets all the local
2840 function! s:createTreeWin()
2841 "create the nerd tree window
2842 let splitLocation = g:NERDTreeWinPos ==# "left" ? "topleft " : "botright "
2843 let splitSize = g:NERDTreeWinSize
2845 if !exists('t:NERDTreeBufName')
2846 let t:NERDTreeBufName = s:nextBufferName()
2847 silent! exec splitLocation . 'vertical ' . splitSize . ' new'
2848 silent! exec "edit " . t:NERDTreeBufName
2850 silent! exec splitLocation . 'vertical ' . splitSize . ' split'
2851 silent! exec "buffer " . t:NERDTreeBufName
2854 setlocal winfixwidth
2856 "throwaway buffer options
2858 setlocal buftype=nofile
2860 setlocal foldcolumn=0
2861 setlocal nobuflisted
2863 if g:NERDTreeShowLineNumbers
2871 if g:NERDTreeHighlightCursorline
2875 call s:setupStatusline()
2877 call s:bindMappings()
2878 setfiletype nerdtree
2879 " syntax highlighting
2880 if has("syntax") && exists("g:syntax_on")
2881 call s:setupSyntaxHighlighting()
2885 "FUNCTION: s:dumpHelp {{{2
2886 "prints out the quick help
2887 function! s:dumpHelp()
2889 if b:treeShowHelp ==# 1
2890 let @h= "\" NERD tree (" . s:NERD_tree_version . ") quickhelp~\n"
2891 let @h=@h."\" ============================\n"
2892 let @h=@h."\" File node mappings~\n"
2893 let @h=@h."\" ". (g:NERDTreeMouseMode ==# 3 ? "single" : "double") ."-click,\n"
2894 let @h=@h."\" <CR>,\n"
2895 if b:NERDTreeType ==# "primary"
2896 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n"
2898 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in current window\n"
2900 if b:NERDTreeType ==# "primary"
2901 let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n"
2903 let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
2904 let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
2905 let @h=@h."\" middle-click,\n"
2906 let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n"
2907 let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n"
2908 let @h=@h."\" ". g:NERDTreeMapOpenVSplit .": open vsplit\n"
2909 let @h=@h."\" ". g:NERDTreeMapPreviewVSplit .": preview vsplit\n"
2911 let @h=@h."\"\n\" ----------------------------\n"
2912 let @h=@h."\" Directory node mappings~\n"
2913 let @h=@h."\" ". (g:NERDTreeMouseMode ==# 1 ? "double" : "single") ."-click,\n"
2914 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open & close node\n"
2915 let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n"
2916 let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n"
2917 let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n"
2918 let @h=@h."\" current node recursively\n"
2919 let @h=@h."\" middle-click,\n"
2920 let @h=@h."\" ". g:NERDTreeMapOpenExpl.": explore selected dir\n"
2922 let @h=@h."\"\n\" ----------------------------\n"
2923 let @h=@h."\" Bookmark table mappings~\n"
2924 let @h=@h."\" double-click,\n"
2925 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open bookmark\n"
2926 let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
2927 let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
2928 let @h=@h."\" ". g:NERDTreeMapDeleteBookmark .": delete bookmark\n"
2930 let @h=@h."\"\n\" ----------------------------\n"
2931 let @h=@h."\" Tree navigation mappings~\n"
2932 let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n"
2933 let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n"
2934 let @h=@h."\" ". g:NERDTreeMapJumpFirstChild .": go to first child\n"
2935 let @h=@h."\" ". g:NERDTreeMapJumpLastChild .": go to last child\n"
2936 let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n"
2937 let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n"
2939 let @h=@h."\"\n\" ----------------------------\n"
2940 let @h=@h."\" Filesystem mappings~\n"
2941 let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n"
2942 let @h=@h."\" selected dir\n"
2943 let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n"
2944 let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n"
2945 let @h=@h."\" but leave old root open\n"
2946 let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n"
2947 let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n"
2948 let @h=@h."\" ". g:NERDTreeMapMenu .": Show menu\n"
2949 let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n"
2950 let @h=@h."\" selected dir\n"
2952 let @h=@h."\"\n\" ----------------------------\n"
2953 let @h=@h."\" Tree filtering mappings~\n"
2954 let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (b:NERDTreeShowHidden ? "on" : "off") . ")\n"
2955 let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (b:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n"
2956 let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (b:NERDTreeShowFiles ? "on" : "off") . ")\n"
2957 let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (b:NERDTreeShowBookmarks ? "on" : "off") . ")\n"
2959 "add quickhelp entries for each custom key map
2960 if len(s:KeyMap.All())
2961 let @h=@h."\"\n\" ----------------------------\n"
2962 let @h=@h."\" Custom mappings~\n"
2963 for i in s:KeyMap.All()
2964 let @h=@h."\" ". i.key .": ". i.quickhelpText ."\n"
2968 let @h=@h."\"\n\" ----------------------------\n"
2969 let @h=@h."\" Other mappings~\n"
2970 let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n"
2971 let @h=@h."\" ". g:NERDTreeMapToggleZoom .": Zoom (maximize-minimize)\n"
2972 let @h=@h."\" the NERDTree window\n"
2973 let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n"
2974 let @h=@h."\"\n\" ----------------------------\n"
2975 let @h=@h."\" Bookmark commands~\n"
2976 let @h=@h."\" :Bookmark <name>\n"
2977 let @h=@h."\" :BookmarkToRoot <name>\n"
2978 let @h=@h."\" :RevealBookmark <name>\n"
2979 let @h=@h."\" :OpenBookmark <name>\n"
2980 let @h=@h."\" :ClearBookmarks [<names>]\n"
2981 let @h=@h."\" :ClearAllBookmarks\n"
2983 let @h="\" Press ". g:NERDTreeMapHelp ." for help\n"
2990 "FUNCTION: s:echo {{{2
2991 "A wrapper for :echo. Appends 'NERDTree:' on the front of all messages
2994 "msg: the message to echo
2995 function! s:echo(msg)
2997 echomsg "NERDTree: " . a:msg
2999 "FUNCTION: s:echoWarning {{{2
3000 "Wrapper for s:echo, sets the message type to warningmsg for this message
3002 "msg: the message to echo
3003 function! s:echoWarning(msg)
3008 "FUNCTION: s:echoError {{{2
3009 "Wrapper for s:echo, sets the message type to errormsg for this message
3011 "msg: the message to echo
3012 function! s:echoError(msg)
3017 "FUNCTION: s:firstUsableWindow(){{{2
3018 "find the window number of the first normal window
3019 function! s:firstUsableWindow()
3021 while i <= winnr("$")
3022 let bnum = winbufnr(i)
3023 if bnum != -1 && getbufvar(bnum, '&buftype') ==# ''
3024 \ && !getwinvar(i, '&previewwindow')
3025 \ && (!getbufvar(bnum, '&modified') || &hidden)
3033 "FUNCTION: s:getPath(ln) {{{2
3034 "Gets the full path to the node that is rendered on the given line number
3037 "ln: the line number to get the path for
3040 "A path if a node was selected, {} if nothing is selected.
3041 "If the 'up a dir' line was selected then the path to the parent of the
3042 "current root is returned
3043 function! s:getPath(ln)
3044 let line = getline(a:ln)
3046 let rootLine = s:TreeFileNode.GetRootLineNum()
3048 "check to see if we have the root node
3050 return b:NERDTreeRoot.path
3053 " in case called from outside the tree
3054 if line !~ '^ *[|`]' || line =~ '^$'
3058 if line ==# s:tree_up_dir_line
3059 return b:NERDTreeRoot.path.getParent()
3062 let indent = s:indentLevelFor(line)
3064 "remove the tree parts and the leading space
3065 let curFile = s:stripMarkupFromLine(line, 0)
3070 let curFile = substitute(curFile, '/\?$', '/', "")
3077 let curLine = getline(lnum)
3078 let curLineStripped = s:stripMarkupFromLine(curLine, 1)
3080 "have we reached the top of the tree?
3082 let dir = b:NERDTreeRoot.path.str({'format': 'UI'}) . dir
3085 if curLineStripped =~ '/$'
3086 let lpindent = s:indentLevelFor(curLine)
3087 if lpindent < indent
3088 let indent = indent - 1
3090 let dir = substitute (curLineStripped,'^\\', "", "") . dir
3095 let curFile = b:NERDTreeRoot.path.drive . dir . curFile
3096 let toReturn = s:Path.New(curFile)
3100 "FUNCTION: s:getTreeWinNum() {{{2
3101 "gets the nerd tree window number for this tab
3102 function! s:getTreeWinNum()
3103 if exists("t:NERDTreeBufName")
3104 return bufwinnr(t:NERDTreeBufName)
3109 "FUNCTION: s:indentLevelFor(line) {{{2
3110 function! s:indentLevelFor(line)
3111 return match(a:line, '[^ \-+~`|]') / s:tree_wid
3113 "FUNCTION: s:isTreeOpen() {{{2
3114 function! s:isTreeOpen()
3115 return s:getTreeWinNum() != -1
3117 "FUNCTION: s:isWindowUsable(winnumber) {{{2
3118 "Returns 0 if opening a file from the tree in the given window requires it to
3119 "be split, 1 otherwise
3122 "winnumber: the number of the window in question
3123 function! s:isWindowUsable(winnumber)
3124 "gotta split if theres only one window (i.e. the NERD tree)
3129 let oldwinnr = winnr()
3130 call s:exec(a:winnumber . "wincmd p")
3131 let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow')
3132 let modified = &modified
3133 call s:exec(oldwinnr . "wincmd p")
3135 "if its a special window e.g. quickfix or another explorer plugin then we
3145 return !modified || s:bufInWindows(winbufnr(a:winnumber)) >= 2
3148 " FUNCTION: s:jumpToChild(direction) {{{2
3150 " direction: 0 if going to first child, 1 if going to last
3151 function! s:jumpToChild(direction)
3152 let currentNode = s:TreeFileNode.GetSelected()
3153 if currentNode ==# {} || currentNode.isRoot()
3154 call s:echo("cannot jump to " . (a:direction ? "last" : "first") . " child")
3157 let dirNode = currentNode.parent
3158 let childNodes = dirNode.getVisibleChildren()
3160 let targetNode = childNodes[0]
3162 let targetNode = childNodes[len(childNodes) - 1]
3165 if targetNode.equals(currentNode)
3166 let siblingDir = currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction)
3168 let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0
3169 let targetNode = siblingDir.getChildByIndex(indx, 1)
3173 call targetNode.putCursorHere(1, 0)
3179 "FUNCTION: s:promptToDelBuffer(bufnum, msg){{{2
3180 "prints out the given msg and, if the user responds by pushing 'y' then the
3181 "buffer with the given bufnum is deleted
3184 "bufnum: the buffer that may be deleted
3185 "msg: a message that will be echoed to the user asking them if they wish to
3187 function! s:promptToDelBuffer(bufnum, msg)
3189 if nr2char(getchar()) ==# 'y'
3190 exec "silent bdelete! " . a:bufnum
3194 "FUNCTION: s:putCursorOnBookmarkTable(){{{2
3195 "Places the cursor at the top of the bookmarks table
3196 function! s:putCursorOnBookmarkTable()
3197 if !b:NERDTreeShowBookmarks
3198 throw "NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active"
3201 let rootNodeLine = s:TreeFileNode.GetRootLineNum()
3204 while getline(line) !~ '^>-\+Bookmarks-\+$'
3206 if line >= rootNodeLine
3207 throw "NERDTree.BookmarkTableNotFoundError: didnt find the bookmarks table"
3210 call cursor(line, 0)
3213 "FUNCTION: s:putCursorInTreeWin(){{{2
3214 "Places the cursor in the nerd tree window
3215 function! s:putCursorInTreeWin()
3217 throw "NERDTree.InvalidOperationError: cant put cursor in NERD tree window, no window exists"
3220 call s:exec(s:getTreeWinNum() . "wincmd w")
3223 "FUNCTION: s:renderBookmarks {{{2
3224 function! s:renderBookmarks()
3226 call setline(line(".")+1, ">----------Bookmarks----------")
3227 call cursor(line(".")+1, col("."))
3229 for i in s:Bookmark.Bookmarks()
3230 call setline(line(".")+1, i.str())
3231 call cursor(line(".")+1, col("."))
3234 call setline(line(".")+1, '')
3235 call cursor(line(".")+1, col("."))
3237 "FUNCTION: s:renderView {{{2
3238 "The entry function for rendering the tree
3239 function! s:renderView()
3242 "remember the top line of the buffer and the current line so we can
3243 "restore the view exactly how it was
3244 let curLine = line(".")
3245 let curCol = col(".")
3246 let topLine = line("w0")
3248 "delete all lines in the buffer (being careful not to clobber a register)
3253 "delete the blank line before the help and add one after it
3254 call setline(line(".")+1, "")
3255 call cursor(line(".")+1, col("."))
3257 if b:NERDTreeShowBookmarks
3258 call s:renderBookmarks()
3261 "add the 'up a dir' line
3262 call setline(line(".")+1, s:tree_up_dir_line)
3263 call cursor(line(".")+1, col("."))
3265 "draw the header line
3266 let header = b:NERDTreeRoot.path.str({'format': 'UI', 'truncateTo': winwidth(0)})
3267 call setline(line(".")+1, header)
3268 call cursor(line(".")+1, col("."))
3272 let @o = b:NERDTreeRoot.renderToString()
3276 "delete the blank line at the top of the buffer
3280 let old_scrolloff=&scrolloff
3282 call cursor(topLine, 1)
3284 call cursor(curLine, curCol)
3285 let &scrolloff = old_scrolloff
3287 setlocal nomodifiable
3290 "FUNCTION: s:renderViewSavingPosition {{{2
3291 "Renders the tree and ensures the cursor stays on the current node or the
3292 "current nodes parent if it is no longer available upon re-rendering
3293 function! s:renderViewSavingPosition()
3294 let currentNode = s:TreeFileNode.GetSelected()
3296 "go up the tree till we find a node that will be visible or till we run
3298 while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
3299 let currentNode = currentNode.parent
3304 if currentNode != {}
3305 call currentNode.putCursorHere(0, 0)
3308 "FUNCTION: s:restoreScreenState() {{{2
3310 "Sets the screen state back to what it was when s:saveScreenState was last
3313 "Assumes the cursor is in the NERDTree window
3314 function! s:restoreScreenState()
3315 if !exists("b:NERDTreeOldTopLine") || !exists("b:NERDTreeOldPos") || !exists("b:NERDTreeOldWindowSize")
3318 exec("silent vertical resize ".b:NERDTreeOldWindowSize)
3320 let old_scrolloff=&scrolloff
3322 call cursor(b:NERDTreeOldTopLine, 0)
3324 call setpos(".", b:NERDTreeOldPos)
3325 let &scrolloff=old_scrolloff
3328 "FUNCTION: s:saveScreenState() {{{2
3329 "Saves the current cursor position in the current buffer and the window
3331 function! s:saveScreenState()
3334 call s:putCursorInTreeWin()
3335 let b:NERDTreeOldPos = getpos(".")
3336 let b:NERDTreeOldTopLine = line("w0")
3337 let b:NERDTreeOldWindowSize = winwidth("")
3338 call s:exec(win . "wincmd w")
3339 catch /^NERDTree.InvalidOperationError/
3343 "FUNCTION: s:setupStatusline() {{{2
3344 function! s:setupStatusline()
3345 if g:NERDTreeStatusline != -1
3346 let &l:statusline = g:NERDTreeStatusline
3349 "FUNCTION: s:setupSyntaxHighlighting() {{{2
3350 function! s:setupSyntaxHighlighting()
3351 "treeFlags are syntax items that should be invisible, but give clues as to
3352 "how things should be highlighted
3353 syn match treeFlag #\~#
3354 syn match treeFlag #\[RO\]#
3356 "highlighting for the .. (up dir) line at the top of the tree
3357 execute "syn match treeUp #". s:tree_up_dir_line ."#"
3359 "highlighting for the ~/+ symbols for the directory nodes
3360 syn match treeClosable #\~\<#
3361 syn match treeClosable #\~\.#
3362 syn match treeOpenable #+\<#
3363 syn match treeOpenable #+\.#he=e-1
3365 "highlighting for the tree structural parts
3366 syn match treePart #|#
3367 syn match treePart #`#
3368 syn match treePartFile #[|`]-#hs=s+1 contains=treePart
3370 "quickhelp syntax elements
3371 syn match treeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1
3372 syn match treeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1
3373 syn match treeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=treeFlag
3374 syn match treeToggleOn #".*(on)#hs=e-2,he=e-1 contains=treeHelpKey
3375 syn match treeToggleOff #".*(off)#hs=e-3,he=e-1 contains=treeHelpKey
3376 syn match treeHelpCommand #" :.\{-}\>#hs=s+3
3377 syn match treeHelp #^".*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn,treeHelpCommand
3379 "highlighting for readonly files
3380 syn match treeRO #.*\[RO\]#hs=s+2 contains=treeFlag,treeBookmark,treePart,treePartFile
3382 "highlighting for sym links
3383 syn match treeLink #[^-| `].* -> # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash
3385 "highlighing for directory nodes and file nodes
3386 syn match treeDirSlash #/#
3387 syn match treeDir #[^-| `].*/# contains=treeLink,treeDirSlash,treeOpenable,treeClosable
3388 syn match treeExecFile #[|`]-.*\*\($\| \)# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark
3389 syn match treeFile #|-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
3390 syn match treeFile #`-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
3391 syn match treeCWD #^/.*$#
3393 "highlighting for bookmarks
3394 syn match treeBookmark # {.*}#hs=s+1
3396 "highlighting for the bookmarks table
3397 syn match treeBookmarksLeader #^>#
3398 syn match treeBookmarksHeader #^>-\+Bookmarks-\+$# contains=treeBookmarksLeader
3399 syn match treeBookmarkName #^>.\{-} #he=e-1 contains=treeBookmarksLeader
3400 syn match treeBookmark #^>.*$# contains=treeBookmarksLeader,treeBookmarkName,treeBookmarksHeader
3402 if g:NERDChristmasTree
3403 hi def link treePart Special
3404 hi def link treePartFile Type
3405 hi def link treeFile Normal
3406 hi def link treeExecFile Title
3407 hi def link treeDirSlash Identifier
3408 hi def link treeClosable Type
3410 hi def link treePart Normal
3411 hi def link treePartFile Normal
3412 hi def link treeFile Normal
3413 hi def link treeClosable Title
3416 hi def link treeBookmarksHeader statement
3417 hi def link treeBookmarksLeader ignore
3418 hi def link treeBookmarkName Identifier
3419 hi def link treeBookmark normal
3421 hi def link treeHelp String
3422 hi def link treeHelpKey Identifier
3423 hi def link treeHelpCommand Identifier
3424 hi def link treeHelpTitle Macro
3425 hi def link treeToggleOn Question
3426 hi def link treeToggleOff WarningMsg
3428 hi def link treeDir Directory
3429 hi def link treeUp Directory
3430 hi def link treeCWD Statement
3431 hi def link treeLink Macro
3432 hi def link treeOpenable Title
3433 hi def link treeFlag ignore
3434 hi def link treeRO WarningMsg
3435 hi def link treeBookmark Statement
3437 hi def link NERDTreeCurrentNode Search
3440 "FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2
3441 "returns the given line with all the tree parts stripped off
3444 "line: the subject line
3445 "removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces =
3446 "any spaces before the actual text of the node)
3447 function! s:stripMarkupFromLine(line, removeLeadingSpaces)
3449 "remove the tree parts and the leading space
3450 let line = substitute (line, s:tree_markup_reg,"","")
3452 "strip off any read only flag
3453 let line = substitute (line, ' \[RO\]', "","")
3455 "strip off any bookmark flags
3456 let line = substitute (line, ' {[^}]*}', "","")
3458 "strip off any executable flags
3459 let line = substitute (line, '*\ze\($\| \)', "","")
3465 let line = substitute (line,' -> .*',"","") " remove link to
3467 let line = substitute (line, '/\?$', '/', "")
3470 if a:removeLeadingSpaces
3471 let line = substitute (line, '^ *', '', '')
3477 "FUNCTION: s:toggle(dir) {{{2
3478 "Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is
3479 "closed it is restored or initialized (if it doesnt exist)
3482 "dir: the full path for the root node (is only used if the NERD tree is being
3484 function! s:toggle(dir)
3485 if s:treeExistsForTab()
3487 call s:createTreeWin()
3491 call s:restoreScreenState()
3496 call s:initNerdTree(a:dir)
3499 "SECTION: Interface bindings {{{1
3500 "============================================================
3501 "FUNCTION: s:activateNode(forceKeepWindowOpen) {{{2
3502 "If the current node is a file, open it in the previous window (or a new one
3503 "if the previous is modified). If it is a directory then it is opened.
3506 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3507 function! s:activateNode(forceKeepWindowOpen)
3508 if getline(".") ==# s:tree_up_dir_line
3512 let treenode = s:TreeFileNode.GetSelected()
3514 call treenode.activate(a:forceKeepWindowOpen)
3516 let bookmark = s:Bookmark.GetSelected()
3518 call bookmark.activate()
3523 "FUNCTION: s:bindMappings() {{{2
3524 function! s:bindMappings()
3525 " set up mappings and commands for this buffer
3526 nnoremap <silent> <buffer> <middlerelease> :call <SID>handleMiddleMouse()<cr>
3527 nnoremap <silent> <buffer> <leftrelease> <leftrelease>:call <SID>checkForActivate()<cr>
3528 nnoremap <silent> <buffer> <2-leftmouse> :call <SID>activateNode(0)<cr>
3530 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapActivateNode . " :call <SID>activateNode(0)<cr>"
3531 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenSplit ." :call <SID>openEntrySplit(0,0)<cr>"
3532 exec "nnoremap <silent> <buffer> <cr> :call <SID>activateNode(0)<cr>"
3534 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreview ." :call <SID>previewNode(0)<cr>"
3535 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewSplit ." :call <SID>previewNode(1)<cr>"
3537 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenVSplit ." :call <SID>openEntrySplit(1,0)<cr>"
3538 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewVSplit ." :call <SID>previewNode(2)<cr>"
3540 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenRecursively ." :call <SID>openNodeRecursively()<cr>"
3542 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdirKeepOpen ." :call <SID>upDir(1)<cr>"
3543 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdir ." :call <SID>upDir(0)<cr>"
3544 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChangeRoot ." :call <SID>chRoot()<cr>"
3546 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChdir ." :call <SID>chCwd()<cr>"
3548 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapQuit ." :call <SID>closeTreeWindow()<cr>"
3550 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefreshRoot ." :call <SID>refreshRoot()<cr>"
3551 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefresh ." :call <SID>refreshCurrent()<cr>"
3553 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapHelp ." :call <SID>displayHelp()<cr>"
3554 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleZoom ." :call <SID>toggleZoom()<cr>"
3555 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleHidden ." :call <SID>toggleShowHidden()<cr>"
3556 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFilters ." :call <SID>toggleIgnoreFilter()<cr>"
3557 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFiles ." :call <SID>toggleShowFiles()<cr>"
3558 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleBookmarks ." :call <SID>toggleShowBookmarks()<cr>"
3560 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>"
3561 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>"
3563 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapMenu ." :call <SID>showMenu()<cr>"
3565 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>jumpToParent()<cr>"
3566 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>jumpToSibling(1)<cr>"
3567 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpPrevSibling ." :call <SID>jumpToSibling(0)<cr>"
3568 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpFirstChild ." :call <SID>jumpToFirstChild()<cr>"
3569 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpLastChild ." :call <SID>jumpToLastChild()<cr>"
3570 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpRoot ." :call <SID>jumpToRoot()<cr>"
3572 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>"
3573 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTabSilent ." :call <SID>openInNewTab(1)<cr>"
3575 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenExpl ." :call <SID>openExplorer()<cr>"
3577 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>"
3579 "bind all the user custom maps
3580 call s:KeyMap.BindAll()
3582 command! -buffer -nargs=1 Bookmark :call <SID>bookmarkNode('<args>')
3583 command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call <SID>revealBookmark('<args>')
3584 command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call <SID>openBookmark('<args>')
3585 command! -buffer -complete=customlist,s:completeBookmarks -nargs=* ClearBookmarks call <SID>clearBookmarks('<args>')
3586 command! -buffer -complete=customlist,s:completeBookmarks -nargs=+ BookmarkToRoot call s:Bookmark.ToRoot('<args>')
3587 command! -buffer -nargs=0 ClearAllBookmarks call s:Bookmark.ClearAll() <bar> call <SID>renderView()
3588 command! -buffer -nargs=0 ReadBookmarks call s:Bookmark.CacheBookmarks(0) <bar> call <SID>renderView()
3589 command! -buffer -nargs=0 WriteBookmarks call s:Bookmark.Write()
3592 " FUNCTION: s:bookmarkNode(name) {{{2
3593 " Associate the current node with the given name
3594 function! s:bookmarkNode(name)
3595 let currentNode = s:TreeFileNode.GetSelected()
3596 if currentNode != {}
3598 call currentNode.bookmark(a:name)
3600 catch /^NERDTree.IllegalBookmarkNameError/
3601 call s:echo("bookmark names must not contain spaces")
3604 call s:echo("select a node first")
3607 "FUNCTION: s:checkForActivate() {{{2
3608 "Checks if the click should open the current node, if so then activate() is
3609 "called (directories are automatically opened if the symbol beside them is
3611 function! s:checkForActivate()
3612 let currentNode = s:TreeFileNode.GetSelected()
3613 if currentNode != {}
3614 let startToCur = strpart(getline(line(".")), 0, col("."))
3615 let char = strpart(startToCur, strlen(startToCur)-1, 1)
3617 "if they clicked a dir, check if they clicked on the + or ~ sign
3619 if currentNode.path.isDirectory
3620 if startToCur =~ s:tree_markup_reg . '$' && char =~ '[+~]'
3621 call s:activateNode(0)
3626 if (g:NERDTreeMouseMode ==# 2 && currentNode.path.isDirectory) || g:NERDTreeMouseMode ==# 3
3627 if char !~ s:tree_markup_reg && startToCur !~ '\/$'
3628 call s:activateNode(0)
3635 " FUNCTION: s:chCwd() {{{2
3637 let treenode = s:TreeFileNode.GetSelected()
3639 call s:echo("Select a node first")
3644 call treenode.path.changeToDir()
3645 catch /^NERDTree.PathChangeError/
3646 call s:echoWarning("could not change cwd")
3650 " FUNCTION: s:chRoot() {{{2
3651 " changes the current root to the selected one
3652 function! s:chRoot()
3653 let treenode = s:TreeFileNode.GetSelected()
3655 call s:echo("Select a node first")
3659 call treenode.makeRoot()
3661 call b:NERDTreeRoot.putCursorHere(0, 0)
3664 " FUNCTION: s:clearBookmarks(bookmarks) {{{2
3665 function! s:clearBookmarks(bookmarks)
3666 if a:bookmarks ==# ''
3667 let currentNode = s:TreeFileNode.GetSelected()
3668 if currentNode != {}
3669 call currentNode.clearBoomarks()
3672 for name in split(a:bookmarks, ' ')
3673 let bookmark = s:Bookmark.BookmarkFor(name)
3674 call bookmark.delete()
3679 " FUNCTION: s:closeChildren() {{{2
3680 " closes all childnodes of the current node
3681 function! s:closeChildren()
3682 let currentNode = s:TreeDirNode.GetSelected()
3683 if currentNode ==# {}
3684 call s:echo("Select a node first")
3688 call currentNode.closeChildren()
3690 call currentNode.putCursorHere(0, 0)
3692 " FUNCTION: s:closeCurrentDir() {{{2
3693 " closes the parent dir of the current node
3694 function! s:closeCurrentDir()
3695 let treenode = s:TreeFileNode.GetSelected()
3697 call s:echo("Select a node first")
3701 let parent = treenode.parent
3702 if parent ==# {} || parent.isRoot()
3703 call s:echo("cannot close tree root")
3705 call treenode.parent.close()
3707 call treenode.parent.putCursorHere(0, 0)
3710 " FUNCTION: s:closeTreeWindow() {{{2
3711 " close the tree window
3712 function! s:closeTreeWindow()
3713 if b:NERDTreeType ==# "secondary" && b:NERDTreePreviousBuf != -1
3714 exec "buffer " . b:NERDTreePreviousBuf
3719 call s:echo("Cannot close last window")
3723 " FUNCTION: s:deleteBookmark() {{{2
3724 " if the cursor is on a bookmark, prompt to delete
3725 function! s:deleteBookmark()
3726 let bookmark = s:Bookmark.GetSelected()
3728 call s:echo("Put the cursor on a bookmark")
3732 echo "Are you sure you wish to delete the bookmark:\n\"" . bookmark.name . "\" (yN):"
3734 if nr2char(getchar()) ==# 'y'
3736 call bookmark.delete()
3740 call s:echoWarning("Could not remove bookmark")
3743 call s:echo("delete aborted" )
3748 " FUNCTION: s:displayHelp() {{{2
3749 " toggles the help display
3750 function! s:displayHelp()
3751 let b:treeShowHelp = b:treeShowHelp ? 0 : 1
3756 " FUNCTION: s:handleMiddleMouse() {{{2
3757 function! s:handleMiddleMouse()
3758 let curNode = s:TreeFileNode.GetSelected()
3760 call s:echo("Put the cursor on a node first" )
3764 if curNode.path.isDirectory
3765 call s:openExplorer()
3767 call s:openEntrySplit(0,0)
3772 " FUNCTION: s:jumpToFirstChild() {{{2
3773 " wrapper for the jump to child method
3774 function! s:jumpToFirstChild()
3775 call s:jumpToChild(0)
3778 " FUNCTION: s:jumpToLastChild() {{{2
3779 " wrapper for the jump to child method
3780 function! s:jumpToLastChild()
3781 call s:jumpToChild(1)
3784 " FUNCTION: s:jumpToParent() {{{2
3785 " moves the cursor to the parent of the current node
3786 function! s:jumpToParent()
3787 let currentNode = s:TreeFileNode.GetSelected()
3788 if !empty(currentNode)
3789 if !empty(currentNode.parent)
3790 call currentNode.parent.putCursorHere(1, 0)
3793 call s:echo("cannot jump to parent")
3796 call s:echo("put the cursor on a node first")
3800 " FUNCTION: s:jumpToRoot() {{{2
3801 " moves the cursor to the root node
3802 function! s:jumpToRoot()
3803 call b:NERDTreeRoot.putCursorHere(1, 0)
3807 " FUNCTION: s:jumpToSibling() {{{2
3808 " moves the cursor to the sibling of the current node in the given direction
3811 " forward: 1 if the cursor should move to the next sibling, 0 if it should
3812 " move back to the previous sibling
3813 function! s:jumpToSibling(forward)
3814 let currentNode = s:TreeFileNode.GetSelected()
3815 if !empty(currentNode)
3816 let sibling = currentNode.findSibling(a:forward)
3819 call sibling.putCursorHere(1, 0)
3823 call s:echo("put the cursor on a node first")
3827 " FUNCTION: s:openBookmark(name) {{{2
3828 " put the cursor on the given bookmark and, if its a file, open it
3829 function! s:openBookmark(name)
3831 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3832 call targetNode.putCursorHere(0, 1)
3834 catch /^NERDTree.BookmarkedNodeNotFoundError/
3835 call s:echo("note - target node is not cached")
3836 let bookmark = s:Bookmark.BookmarkFor(a:name)
3837 let targetNode = s:TreeFileNode.New(bookmark.path)
3839 if targetNode.path.isDirectory
3840 call targetNode.openExplorer()
3842 call targetNode.open()
3845 " FUNCTION: s:openEntrySplit(vertical, forceKeepWindowOpen) {{{2
3846 "Opens the currently selected file from the explorer in a
3850 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3851 function! s:openEntrySplit(vertical, forceKeepWindowOpen)
3852 let treenode = s:TreeFileNode.GetSelected()
3855 call treenode.openVSplit()
3857 call treenode.openSplit()
3859 if !a:forceKeepWindowOpen
3860 call s:closeTreeIfQuitOnOpen()
3863 call s:echo("select a node first")
3867 " FUNCTION: s:openExplorer() {{{2
3868 function! s:openExplorer()
3869 let treenode = s:TreeDirNode.GetSelected()
3871 call treenode.openExplorer()
3873 call s:echo("select a node first")
3877 " FUNCTION: s:openInNewTab(stayCurrentTab) {{{2
3878 " Opens the selected node or bookmark in a new tab
3880 " stayCurrentTab: if 1 then vim will stay in the current tab, if 0 then vim
3881 " will go to the tab where the new file is opened
3882 function! s:openInNewTab(stayCurrentTab)
3883 let target = s:TreeFileNode.GetSelected()
3885 let target = s:Bookmark.GetSelected()
3889 call target.openInNewTab({'stayInCurrentTab': a:stayCurrentTab})
3893 " FUNCTION: s:openNodeRecursively() {{{2
3894 function! s:openNodeRecursively()
3895 let treenode = s:TreeFileNode.GetSelected()
3896 if treenode ==# {} || treenode.path.isDirectory ==# 0
3897 call s:echo("Select a directory node first" )
3899 call s:echo("Recursively opening node. Please wait...")
3900 call treenode.openRecursively()
3903 call s:echo("Recursively opening node. Please wait... DONE")
3908 "FUNCTION: s:previewNode() {{{2
3910 " openNewWin: if 0, use the previous window, if 1 open in new split, if 2
3912 function! s:previewNode(openNewWin)
3913 let currentBuf = bufnr("")
3915 call s:openEntrySplit(a:openNewWin ==# 2,1)
3917 call s:activateNode(1)
3919 call s:exec(bufwinnr(currentBuf) . "wincmd w")
3922 " FUNCTION: s:revealBookmark(name) {{{2
3923 " put the cursor on the node associate with the given name
3924 function! s:revealBookmark(name)
3926 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3927 call targetNode.putCursorHere(0, 1)
3928 catch /^NERDTree.BookmarkNotFoundError/
3929 call s:echo("Bookmark isnt cached under the current root")
3932 " FUNCTION: s:refreshRoot() {{{2
3933 " Reloads the current root. All nodes below this will be lost and the root dir
3935 function! s:refreshRoot()
3936 call s:echo("Refreshing the root node. This could take a while...")
3937 call b:NERDTreeRoot.refresh()
3940 call s:echo("Refreshing the root node. This could take a while... DONE")
3943 " FUNCTION: s:refreshCurrent() {{{2
3944 " refreshes the root for the current node
3945 function! s:refreshCurrent()
3946 let treenode = s:TreeDirNode.GetSelected()
3948 call s:echo("Refresh failed. Select a node first")
3952 call s:echo("Refreshing node. This could take a while...")
3953 call treenode.refresh()
3956 call s:echo("Refreshing node. This could take a while... DONE")
3958 " FUNCTION: s:showMenu() {{{2
3959 function! s:showMenu()
3960 let curNode = s:TreeFileNode.GetSelected()
3962 call s:echo("Put the cursor on a node first" )
3966 let mc = s:MenuController.New(s:MenuItem.AllEnabled())
3970 " FUNCTION: s:toggleIgnoreFilter() {{{2
3971 " toggles the use of the NERDTreeIgnore option
3972 function! s:toggleIgnoreFilter()
3973 let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled
3974 call s:renderViewSavingPosition()
3978 " FUNCTION: s:toggleShowBookmarks() {{{2
3979 " toggles the display of bookmarks
3980 function! s:toggleShowBookmarks()
3981 let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks
3982 if b:NERDTreeShowBookmarks
3984 call s:putCursorOnBookmarkTable()
3986 call s:renderViewSavingPosition()
3990 " FUNCTION: s:toggleShowFiles() {{{2
3991 " toggles the display of hidden files
3992 function! s:toggleShowFiles()
3993 let b:NERDTreeShowFiles = !b:NERDTreeShowFiles
3994 call s:renderViewSavingPosition()
3998 " FUNCTION: s:toggleShowHidden() {{{2
3999 " toggles the display of hidden files
4000 function! s:toggleShowHidden()
4001 let b:NERDTreeShowHidden = !b:NERDTreeShowHidden
4002 call s:renderViewSavingPosition()
4006 " FUNCTION: s:toggleZoom() {{2
4007 " zoom (maximize/minimize) the NERDTree window
4008 function! s:toggleZoom()
4009 if exists("b:NERDTreeZoomed") && b:NERDTreeZoomed
4010 let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
4011 exec "silent vertical resize ". size
4012 let b:NERDTreeZoomed = 0
4014 exec "vertical resize"
4015 let b:NERDTreeZoomed = 1
4019 "FUNCTION: s:upDir(keepState) {{{2
4020 "moves the tree up a level
4023 "keepState: 1 if the current root should be left open when the tree is
4025 function! s:upDir(keepState)
4026 let cwd = b:NERDTreeRoot.path.str({'format': 'UI'})
4027 if cwd ==# "/" || cwd =~ '^[^/]..$'
4028 call s:echo("already at top dir")
4031 call b:NERDTreeRoot.close()
4034 let oldRoot = b:NERDTreeRoot
4036 if empty(b:NERDTreeRoot.parent)
4037 let path = b:NERDTreeRoot.path.getParent()
4038 let newRoot = s:TreeDirNode.New(path)
4040 call newRoot.transplantChild(b:NERDTreeRoot)
4041 let b:NERDTreeRoot = newRoot
4043 let b:NERDTreeRoot = b:NERDTreeRoot.parent
4046 if g:NERDTreeChDirMode ==# 2
4047 call b:NERDTreeRoot.path.changeToDir()
4051 call oldRoot.putCursorHere(0, 0)
4056 "reset &cpo back to users setting
4057 let &cpo = s:old_cpo
4059 " vim: set sw=4 sts=4 et fdm=marker: