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 . ' = ' . "'" . substitute(a:value, "'", "''", "g") . "'"
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()"
171 "disallow insert mode in the NERDTree
172 exec "autocmd BufEnter ". s:NERDTreeBufName ."* stopinsert"
173 "cache bookmarks when vim loads
174 autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
176 "load all nerdtree plugins after vim starts
177 autocmd VimEnter * runtime! nerdtree_plugin/**/*.vim
180 if g:NERDTreeHijackNetrw
181 augroup NERDTreeHijackNetrw
182 autocmd VimEnter * silent! autocmd! FileExplorer
183 au BufEnter,VimEnter * call s:checkForBrowse(expand("<amatch>"))
187 "SECTION: Classes {{{1
188 "============================================================
189 "CLASS: Bookmark {{{2
190 "============================================================
192 " FUNCTION: Bookmark.activate() {{{3
193 function! s:Bookmark.activate()
194 if self.path.isDirectory
198 let n = s:TreeFileNode.New(self.path)
200 call s:closeTreeIfQuitOnOpen()
204 " FUNCTION: Bookmark.AddBookmark(name, path) {{{3
205 " Class method to add a new bookmark to the list, if a previous bookmark exists
206 " with the same name, just update the path for that bookmark
207 function! s:Bookmark.AddBookmark(name, path)
208 for i in s:Bookmark.Bookmarks()
214 call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
215 call s:Bookmark.Sort()
217 " Function: Bookmark.Bookmarks() {{{3
218 " Class method to get all bookmarks. Lazily initializes the bookmarks global
220 function! s:Bookmark.Bookmarks()
221 if !exists("g:NERDTreeBookmarks")
222 let g:NERDTreeBookmarks = []
224 return g:NERDTreeBookmarks
226 " Function: Bookmark.BookmarkExistsFor(name) {{{3
227 " class method that returns 1 if a bookmark with the given name is found, 0
229 function! s:Bookmark.BookmarkExistsFor(name)
231 call s:Bookmark.BookmarkFor(a:name)
233 catch /^NERDTree.BookmarkNotFoundError/
237 " Function: Bookmark.BookmarkFor(name) {{{3
238 " Class method to get the bookmark that has the given name. {} is return if no
240 function! s:Bookmark.BookmarkFor(name)
241 for i in s:Bookmark.Bookmarks()
246 throw "NERDTree.BookmarkNotFoundError: no bookmark found for name: \"". a:name .'"'
248 " Function: Bookmark.BookmarkNames() {{{3
249 " Class method to return an array of all bookmark names
250 function! s:Bookmark.BookmarkNames()
252 for i in s:Bookmark.Bookmarks()
253 call add(names, i.name)
257 " FUNCTION: Bookmark.CacheBookmarks(silent) {{{3
258 " Class method to read all bookmarks from the bookmarks file intialize
259 " bookmark objects for each one.
262 " silent - dont echo an error msg if invalid bookmarks are found
263 function! s:Bookmark.CacheBookmarks(silent)
264 if filereadable(g:NERDTreeBookmarksFile)
265 let g:NERDTreeBookmarks = []
266 let g:NERDTreeInvalidBookmarks = []
267 let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
268 let invalidBookmarksFound = 0
269 for i in bookmarkStrings
274 let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
275 let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
278 let bookmark = s:Bookmark.New(name, s:Path.New(path))
279 call add(g:NERDTreeBookmarks, bookmark)
280 catch /^NERDTree.InvalidArgumentsError/
281 call add(g:NERDTreeInvalidBookmarks, i)
282 let invalidBookmarksFound += 1
286 if invalidBookmarksFound
287 call s:Bookmark.Write()
289 call s:echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
292 call s:Bookmark.Sort()
295 " FUNCTION: Bookmark.compareTo(otherbookmark) {{{3
296 " Compare these two bookmarks for sorting purposes
297 function! s:Bookmark.compareTo(otherbookmark)
298 return a:otherbookmark.name < self.name
300 " FUNCTION: Bookmark.ClearAll() {{{3
301 " Class method to delete all bookmarks.
302 function! s:Bookmark.ClearAll()
303 for i in s:Bookmark.Bookmarks()
306 call s:Bookmark.Write()
308 " FUNCTION: Bookmark.delete() {{{3
309 " Delete this bookmark. If the node for this bookmark is under the current
310 " root, then recache bookmarks for its Path object
311 function! s:Bookmark.delete()
314 let node = self.getNode(1)
315 catch /^NERDTree.BookmarkedNodeNotFoundError/
317 call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
319 call node.path.cacheDisplayString()
321 call s:Bookmark.Write()
323 " FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{3
324 " Gets the treenode for this bookmark
327 " searchFromAbsoluteRoot: specifies whether we should search from the current
328 " tree root, or the highest cached node
329 function! s:Bookmark.getNode(searchFromAbsoluteRoot)
330 let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : b:NERDTreeRoot
331 let targetNode = searchRoot.findNode(self.path)
333 throw "NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: " . self.name
337 " FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{3
338 " Class method that finds the bookmark with the given name and returns the
340 function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
341 let bookmark = s:Bookmark.BookmarkFor(a:name)
342 return bookmark.getNode(a:searchFromAbsoluteRoot)
344 " FUNCTION: Bookmark.GetSelected() {{{3
345 " returns the Bookmark the cursor is over, or {}
346 function! s:Bookmark.GetSelected()
347 let line = getline(".")
348 let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
351 return s:Bookmark.BookmarkFor(name)
352 catch /^NERDTree.BookmarkNotFoundError/
359 " Function: Bookmark.InvalidBookmarks() {{{3
360 " Class method to get all invalid bookmark strings read from the bookmarks
362 function! s:Bookmark.InvalidBookmarks()
363 if !exists("g:NERDTreeInvalidBookmarks")
364 let g:NERDTreeInvalidBookmarks = []
366 return g:NERDTreeInvalidBookmarks
368 " FUNCTION: Bookmark.mustExist() {{{3
369 function! s:Bookmark.mustExist()
370 if !self.path.exists()
371 call s:Bookmark.CacheBookmarks(1)
372 throw "NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \"".
373 \ self.name ."\" points to a non existing location: \"". self.path.str()
376 " FUNCTION: Bookmark.New(name, path) {{{3
377 " Create a new bookmark object with the given name and path object
378 function! s:Bookmark.New(name, path)
380 throw "NERDTree.IllegalBookmarkNameError: illegal name:" . a:name
383 let newBookmark = copy(self)
384 let newBookmark.name = a:name
385 let newBookmark.path = a:path
388 " FUNCTION: Bookmark.openInNewTab(options) {{{3
389 " Create a new bookmark object with the given name and path object
390 function! s:Bookmark.openInNewTab(options)
391 let currentTab = tabpagenr()
392 if self.path.isDirectory
394 call s:initNerdTree(self.name)
396 exec "tabedit " . bookmark.path.str({'format': 'Edit'})
399 if has_key(a:options, 'stayInCurrentTab')
400 exec "tabnext " . currentTab
403 " Function: Bookmark.setPath(path) {{{3
404 " makes this bookmark point to the given path
405 function! s:Bookmark.setPath(path)
406 let self.path = a:path
408 " Function: Bookmark.Sort() {{{3
409 " Class method that sorts all bookmarks
410 function! s:Bookmark.Sort()
411 let CompareFunc = function("s:compareBookmarks")
412 call sort(s:Bookmark.Bookmarks(), CompareFunc)
414 " Function: Bookmark.str() {{{3
415 " Get the string that should be rendered in the view for this bookmark
416 function! s:Bookmark.str()
417 let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name)
419 let pathStrMaxLen = pathStrMaxLen - &numberwidth
422 let pathStr = self.path.str({'format': 'UI'})
423 if len(pathStr) > pathStrMaxLen
424 let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
426 return '>' . self.name . ' ' . pathStr
428 " FUNCTION: Bookmark.toRoot() {{{3
429 " Make the node for this bookmark the new tree root
430 function! s:Bookmark.toRoot()
433 let targetNode = self.getNode(1)
434 catch /^NERDTree.BookmarkedNodeNotFoundError/
435 let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
437 call targetNode.makeRoot()
439 call targetNode.putCursorHere(0, 0)
442 " FUNCTION: Bookmark.ToRoot(name) {{{3
443 " Make the node for this bookmark the new tree root
444 function! s:Bookmark.ToRoot(name)
445 let bookmark = s:Bookmark.BookmarkFor(a:name)
446 call bookmark.toRoot()
450 "FUNCTION: Bookmark.validate() {{{3
451 function! s:Bookmark.validate()
452 if self.path.exists()
455 call s:Bookmark.CacheBookmarks(1)
457 call s:echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
462 " Function: Bookmark.Write() {{{3
463 " Class method to write all bookmarks to the bookmarks file
464 function! s:Bookmark.Write()
465 let bookmarkStrings = []
466 for i in s:Bookmark.Bookmarks()
467 call add(bookmarkStrings, i.name . ' ' . i.path.str())
470 "add a blank line before the invalid ones
471 call add(bookmarkStrings, "")
473 for j in s:Bookmark.InvalidBookmarks()
474 call add(bookmarkStrings, j)
476 call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
479 "============================================================
481 "FUNCTION: KeyMap.All() {{{3
482 function! s:KeyMap.All()
483 if !exists("s:keyMaps")
489 "FUNCTION: KeyMap.BindAll() {{{3
490 function! s:KeyMap.BindAll()
491 for i in s:KeyMap.All()
496 "FUNCTION: KeyMap.bind() {{{3
497 function! s:KeyMap.bind()
498 exec "nnoremap <silent> <buffer> ". self.key ." :call ". self.callback ."()<cr>"
501 "FUNCTION: KeyMap.Create(options) {{{3
502 function! s:KeyMap.Create(options)
503 let newKeyMap = copy(self)
504 let newKeyMap.key = a:options['key']
505 let newKeyMap.quickhelpText = a:options['quickhelpText']
506 let newKeyMap.callback = a:options['callback']
507 call add(s:KeyMap.All(), newKeyMap)
509 "CLASS: MenuController {{{2
510 "============================================================
511 let s:MenuController = {}
512 "FUNCTION: MenuController.New(menuItems) {{{3
513 "create a new menu controller that operates on the given menu items
514 function! s:MenuController.New(menuItems)
515 let newMenuController = copy(self)
516 if a:menuItems[0].isSeparator()
517 let newMenuController.menuItems = a:menuItems[1:-1]
519 let newMenuController.menuItems = a:menuItems
521 return newMenuController
524 "FUNCTION: MenuController.showMenu() {{{3
525 "start the main loop of the menu and get the user to choose/execute a menu
527 function! s:MenuController.showMenu()
528 call self._saveOptions()
531 let self.selection = 0
536 call self._echoPrompt()
537 let key = nr2char(getchar())
538 let done = self._handleKeypress(key)
541 call self._restoreOptions()
544 if self.selection != -1
545 let m = self._current()
550 "FUNCTION: MenuController._echoPrompt() {{{3
551 function! s:MenuController._echoPrompt()
552 echo "NERDTree Menu. Use j/k/enter and the shortcuts indicated"
553 echo "=========================================================="
555 for i in range(0, len(self.menuItems)-1)
556 if self.selection == i
557 echo "> " . self.menuItems[i].text
559 echo " " . self.menuItems[i].text
564 "FUNCTION: MenuController._current(key) {{{3
565 "get the MenuItem that is curently selected
566 function! s:MenuController._current()
567 return self.menuItems[self.selection]
570 "FUNCTION: MenuController._handleKeypress(key) {{{3
571 "change the selection (if appropriate) and return 1 if the user has made
572 "their choice, 0 otherwise
573 function! s:MenuController._handleKeypress(key)
575 call self._cursorDown()
577 call self._cursorUp()
578 elseif a:key == nr2char(27) "escape
579 let self.selection = -1
581 elseif a:key == "\r" || a:key == "\n" "enter and ctrl-j
584 let index = self._nextIndexFor(a:key)
586 let self.selection = index
587 if len(self._allIndexesFor(a:key)) == 1
596 "FUNCTION: MenuController._allIndexesFor(shortcut) {{{3
597 "get indexes to all menu items with the given shortcut
598 function! s:MenuController._allIndexesFor(shortcut)
601 for i in range(0, len(self.menuItems)-1)
602 if self.menuItems[i].shortcut == a:shortcut
603 call add(toReturn, i)
610 "FUNCTION: MenuController._nextIndexFor(shortcut) {{{3
611 "get the index to the next menu item with the given shortcut, starts from the
612 "current cursor location and wraps around to the top again if need be
613 function! s:MenuController._nextIndexFor(shortcut)
614 for i in range(self.selection+1, len(self.menuItems)-1)
615 if self.menuItems[i].shortcut == a:shortcut
620 for i in range(0, self.selection)
621 if self.menuItems[i].shortcut == a:shortcut
629 "FUNCTION: MenuController._setCmdheight() {{{3
630 "sets &cmdheight to whatever is needed to display the menu
631 function! s:MenuController._setCmdheight()
632 let &cmdheight = len(self.menuItems) + 3
635 "FUNCTION: MenuController._saveOptions() {{{3
636 "set any vim options that are required to make the menu work (saving their old
638 function! s:MenuController._saveOptions()
639 let self._oldLazyredraw = &lazyredraw
640 let self._oldCmdheight = &cmdheight
642 call self._setCmdheight()
645 "FUNCTION: MenuController._restoreOptions() {{{3
646 "restore the options we saved in _saveOptions()
647 function! s:MenuController._restoreOptions()
648 let &cmdheight = self._oldCmdheight
649 let &lazyredraw = self._oldLazyredraw
652 "FUNCTION: MenuController._cursorDown() {{{3
653 "move the cursor to the next menu item, skipping separators
654 function! s:MenuController._cursorDown()
657 if self.selection < len(self.menuItems)-1
658 let self.selection += 1
660 let self.selection = 0
663 if !self._current().isSeparator()
669 "FUNCTION: MenuController._cursorUp() {{{3
670 "move the cursor to the previous menu item, skipping separators
671 function! s:MenuController._cursorUp()
674 if self.selection > 0
675 let self.selection -= 1
677 let self.selection = len(self.menuItems)-1
680 if !self._current().isSeparator()
686 "CLASS: MenuItem {{{2
687 "============================================================
689 "FUNCTION: MenuItem.All() {{{3
690 "get all top level menu items
691 function! s:MenuItem.All()
692 if !exists("s:menuItems")
698 "FUNCTION: MenuItem.AllEnabled() {{{3
699 "get all top level menu items that are currently enabled
700 function! s:MenuItem.AllEnabled()
702 for i in s:MenuItem.All()
704 call add(toReturn, i)
710 "FUNCTION: MenuItem.Create(options) {{{3
711 "make a new menu item and add it to the global list
712 function! s:MenuItem.Create(options)
713 let newMenuItem = copy(self)
715 let newMenuItem.text = a:options['text']
716 let newMenuItem.shortcut = a:options['shortcut']
717 let newMenuItem.children = []
719 let newMenuItem.isActiveCallback = -1
720 if has_key(a:options, 'isActiveCallback')
721 let newMenuItem.isActiveCallback = a:options['isActiveCallback']
724 let newMenuItem.callback = -1
725 if has_key(a:options, 'callback')
726 let newMenuItem.callback = a:options['callback']
729 if has_key(a:options, 'parent')
730 call add(a:options['parent'].children, newMenuItem)
732 call add(s:MenuItem.All(), newMenuItem)
738 "FUNCTION: MenuItem.CreateSeparator(options) {{{3
739 "make a new separator menu item and add it to the global list
740 function! s:MenuItem.CreateSeparator(options)
741 let standard_options = { 'text': '--------------------',
744 let options = extend(a:options, standard_options, "force")
746 return s:MenuItem.Create(options)
749 "FUNCTION: MenuItem.CreateSubmenu(options) {{{3
750 "make a new submenu and add it to global list
751 function! s:MenuItem.CreateSubmenu(options)
752 let standard_options = { 'callback': -1 }
753 let options = extend(a:options, standard_options, "force")
755 return s:MenuItem.Create(options)
758 "FUNCTION: MenuItem.enabled() {{{3
759 "return 1 if this menu item should be displayed
761 "delegates off to the isActiveCallback, and defaults to 1 if no callback was
763 function! s:MenuItem.enabled()
764 if self.isActiveCallback != -1
765 return {self.isActiveCallback}()
770 "FUNCTION: MenuItem.execute() {{{3
771 "perform the action behind this menu item, if this menuitem has children then
772 "display a new menu for them, otherwise deletegate off to the menuitem's
774 function! s:MenuItem.execute()
775 if len(self.children)
776 let mc = s:MenuController.New(self.children)
779 if self.callback != -1
780 call {self.callback}()
785 "FUNCTION: MenuItem.isSeparator() {{{3
786 "return 1 if this menuitem is a separator
787 function! s:MenuItem.isSeparator()
788 return self.callback == -1 && self.children == []
791 "FUNCTION: MenuItem.isSubmenu() {{{3
792 "return 1 if this menuitem is a submenu
793 function! s:MenuItem.isSubmenu()
794 return self.callback == -1 && !empty(self.children)
797 "CLASS: TreeFileNode {{{2
798 "This class is the parent of the TreeDirNode class and constitures the
799 "'Component' part of the composite design pattern between the treenode
801 "============================================================
802 let s:TreeFileNode = {}
803 "FUNCTION: TreeFileNode.activate(forceKeepWinOpen) {{{3
804 function! s:TreeFileNode.activate(forceKeepWinOpen)
806 if !a:forceKeepWinOpen
807 call s:closeTreeIfQuitOnOpen()
810 "FUNCTION: TreeFileNode.bookmark(name) {{{3
811 "bookmark this node with a:name
812 function! s:TreeFileNode.bookmark(name)
814 "if a bookmark exists with the same name and the node is cached then save
815 "it so we can update its display string
816 let oldMarkedNode = {}
818 let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1)
819 catch /^NERDTree.BookmarkNotFoundError/
820 catch /^NERDTree.BookmarkedNodeNotFoundError/
823 call s:Bookmark.AddBookmark(a:name, self.path)
824 call self.path.cacheDisplayString()
825 call s:Bookmark.Write()
827 if !empty(oldMarkedNode)
828 call oldMarkedNode.path.cacheDisplayString()
831 "FUNCTION: TreeFileNode.cacheParent() {{{3
832 "initializes self.parent if it isnt already
833 function! s:TreeFileNode.cacheParent()
834 if empty(self.parent)
835 let parentPath = self.path.getParent()
836 if parentPath.equals(self.path)
837 throw "NERDTree.CannotCacheParentError: already at root"
839 let self.parent = s:TreeFileNode.New(parentPath)
842 "FUNCTION: TreeFileNode.compareNodes {{{3
843 "This is supposed to be a class level method but i cant figure out how to
844 "get func refs to work from a dict..
846 "A class level method that compares two nodes
849 "n1, n2: the 2 nodes to compare
850 function! s:compareNodes(n1, n2)
851 return a:n1.path.compareTo(a:n2.path)
854 "FUNCTION: TreeFileNode.clearBoomarks() {{{3
855 function! s:TreeFileNode.clearBoomarks()
856 for i in s:Bookmark.Bookmarks()
857 if i.path.equals(self.path)
861 call self.path.cacheDisplayString()
863 "FUNCTION: TreeFileNode.copy(dest) {{{3
864 function! s:TreeFileNode.copy(dest)
865 call self.path.copy(a:dest)
866 let newPath = s:Path.New(a:dest)
867 let parent = b:NERDTreeRoot.findNode(newPath.getParent())
869 call parent.refresh()
871 return parent.findNode(newPath)
874 "FUNCTION: TreeFileNode.delete {{{3
875 "Removes this node from the tree and calls the Delete method for its path obj
876 function! s:TreeFileNode.delete()
877 call self.path.delete()
878 call self.parent.removeChild(self)
881 "FUNCTION: TreeFileNode.displayString() {{{3
883 "Returns a string that specifies how the node should be represented as a
887 "a string that can be used in the view to represent this node
888 function! s:TreeFileNode.displayString()
889 return self.path.displayString()
892 "FUNCTION: TreeFileNode.equals(treenode) {{{3
894 "Compares this treenode to the input treenode and returns 1 if they are the
897 "Use this method instead of == because sometimes when the treenodes contain
898 "many children, vim seg faults when doing ==
901 "treenode: the other treenode to compare to
902 function! s:TreeFileNode.equals(treenode)
903 return self.path.str() ==# a:treenode.path.str()
906 "FUNCTION: TreeFileNode.findNode(path) {{{3
907 "Returns self if this node.path.Equals the given path.
908 "Returns {} if not equal.
911 "path: the path object to compare against
912 function! s:TreeFileNode.findNode(path)
913 if a:path.equals(self.path)
918 "FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3
920 "Finds the next sibling for this node in the indicated direction. This sibling
921 "must be a directory and may/may not have children as specified.
924 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
927 "a treenode object or {} if no appropriate sibling could be found
928 function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
929 "if we have no parent then we can have no siblings
931 let nextSibling = self.findSibling(a:direction)
933 while nextSibling != {}
934 if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen
937 let nextSibling = nextSibling.findSibling(a:direction)
943 "FUNCTION: TreeFileNode.findSibling(direction) {{{3
945 "Finds the next sibling for this node in the indicated direction
948 "direction: 0 if you want to find the previous sibling, 1 for the next sibling
951 "a treenode object or {} if no sibling could be found
952 function! s:TreeFileNode.findSibling(direction)
953 "if we have no parent then we can have no siblings
956 "get the index of this node in its parents children
957 let siblingIndx = self.parent.getChildIndex(self.path)
960 "move a long to the next potential sibling node
961 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
963 "keep moving along to the next sibling till we find one that is valid
964 let numSiblings = self.parent.getChildCount()
965 while siblingIndx >= 0 && siblingIndx < numSiblings
967 "if the next node is not an ignored node (i.e. wont show up in the
968 "view) then return it
969 if self.parent.children[siblingIndx].path.ignore() ==# 0
970 return self.parent.children[siblingIndx]
974 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
982 "FUNCTION: TreeFileNode.getLineNum(){{{3
983 "returns the line number this node is rendered on, or -1 if it isnt rendered
984 function! s:TreeFileNode.getLineNum()
985 "if the node is the root then return the root line no.
987 return s:TreeFileNode.GetRootLineNum()
990 let totalLines = line("$")
992 "the path components we have matched so far
993 let pathcomponents = [substitute(b:NERDTreeRoot.path.str({'format': 'UI'}), '/ *$', '', '')]
994 "the index of the component we are searching for
995 let curPathComponent = 1
997 let fullpath = self.path.str({'format': 'UI'})
1000 let lnum = s:TreeFileNode.GetRootLineNum()
1003 "have we reached the bottom of the tree?
1004 if lnum ==# totalLines+1
1008 let curLine = getline(lnum)
1010 let indent = s:indentLevelFor(curLine)
1011 if indent ==# curPathComponent
1012 let curLine = s:stripMarkupFromLine(curLine, 1)
1014 let curPath = join(pathcomponents, '/') . '/' . curLine
1015 if stridx(fullpath, curPath, 0) ==# 0
1016 if fullpath ==# curPath || strpart(fullpath, len(curPath)-1,1) ==# '/'
1017 let curLine = substitute(curLine, '/ *$', '', '')
1018 call add(pathcomponents, curLine)
1019 let curPathComponent = curPathComponent + 1
1021 if fullpath ==# curPath
1031 "FUNCTION: TreeFileNode.GetRootForTab(){{{3
1032 "get the root node for this tab
1033 function! s:TreeFileNode.GetRootForTab()
1034 if s:treeExistsForTab()
1035 return getbufvar(t:NERDTreeBufName, 'NERDTreeRoot')
1039 "FUNCTION: TreeFileNode.GetRootLineNum(){{{3
1040 "gets the line number of the root node
1041 function! s:TreeFileNode.GetRootLineNum()
1043 while getline(rootLine) !~ '^\(/\|<\)'
1044 let rootLine = rootLine + 1
1049 "FUNCTION: TreeFileNode.GetSelected() {{{3
1050 "gets the treenode that the cursor is currently over
1051 function! s:TreeFileNode.GetSelected()
1053 let path = s:getPath(line("."))
1057 return b:NERDTreeRoot.findNode(path)
1062 "FUNCTION: TreeFileNode.isVisible() {{{3
1063 "returns 1 if this node should be visible according to the tree filters and
1064 "hidden file filters (and their on/off status)
1065 function! s:TreeFileNode.isVisible()
1066 return !self.path.ignore()
1068 "FUNCTION: TreeFileNode.isRoot() {{{3
1069 "returns 1 if this node is b:NERDTreeRoot
1070 function! s:TreeFileNode.isRoot()
1071 if !s:treeExistsForBuf()
1072 throw "NERDTree.NoTreeError: No tree exists for the current buffer"
1075 return self.equals(b:NERDTreeRoot)
1078 "FUNCTION: TreeFileNode.makeRoot() {{{3
1079 "Make this node the root of the tree
1080 function! s:TreeFileNode.makeRoot()
1081 if self.path.isDirectory
1082 let b:NERDTreeRoot = self
1084 call self.cacheParent()
1085 let b:NERDTreeRoot = self.parent
1088 call b:NERDTreeRoot.open()
1090 "change dir to the dir of the new root if instructed to
1091 if g:NERDTreeChDirMode ==# 2
1092 exec "cd " . b:NERDTreeRoot.path.str({'format': 'Edit'})
1095 "FUNCTION: TreeFileNode.New(path) {{{3
1096 "Returns a new TreeNode object with the given path and parent
1099 "path: a path object representing the full filesystem path to the file/dir that the node represents
1100 function! s:TreeFileNode.New(path)
1101 if a:path.isDirectory
1102 return s:TreeDirNode.New(a:path)
1104 let newTreeNode = copy(self)
1105 let newTreeNode.path = a:path
1106 let newTreeNode.parent = {}
1111 "FUNCTION: TreeFileNode.open() {{{3
1112 "Open the file represented by the given node in the current window, splitting
1113 "the window if needed
1116 "treenode: file node to open
1117 function! s:TreeFileNode.open()
1118 if b:NERDTreeType ==# "secondary"
1119 exec 'edit ' . self.path.str({'format': 'Edit'})
1123 "if the file is already open in this tab then just stick the cursor in it
1124 let winnr = bufwinnr('^' . self.path.str() . '$')
1126 call s:exec(winnr . "wincmd w")
1129 if !s:isWindowUsable(winnr("#")) && s:firstUsableWindow() ==# -1
1130 call self.openSplit()
1133 if !s:isWindowUsable(winnr("#"))
1134 call s:exec(s:firstUsableWindow() . "wincmd w")
1136 call s:exec('wincmd p')
1138 exec ("edit " . self.path.str({'format': 'Edit'}))
1139 catch /^Vim\%((\a\+)\)\=:E37/
1140 call s:putCursorInTreeWin()
1141 throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified."
1142 catch /^Vim\%((\a\+)\)\=:/
1148 "FUNCTION: TreeFileNode.openSplit() {{{3
1149 "Open this node in a new window
1150 function! s:TreeFileNode.openSplit()
1152 if b:NERDTreeType ==# "secondary"
1153 exec "split " . self.path.str({'format': 'Edit'})
1157 " Save the user's settings for splitbelow and splitright
1158 let savesplitbelow=&splitbelow
1159 let savesplitright=&splitright
1161 " 'there' will be set to a command to move from the split window
1162 " back to the explorer window
1164 " 'back' will be set to a command to move from the explorer window
1165 " back to the newly split window
1167 " 'right' and 'below' will be set to the settings needed for
1168 " splitbelow and splitright IF the explorer is the only window.
1170 let there= g:NERDTreeWinPos ==# "left" ? "wincmd h" : "wincmd l"
1171 let back = g:NERDTreeWinPos ==# "left" ? "wincmd l" : "wincmd h"
1172 let right= g:NERDTreeWinPos ==# "left"
1175 " Attempt to go to adjacent window
1178 let onlyOneWin = (winnr("$") ==# 1)
1180 " If no adjacent window, set splitright and splitbelow appropriately
1182 let &splitright=right
1183 let &splitbelow=below
1185 " found adjacent window - invert split direction
1186 let &splitright=!right
1187 let &splitbelow=!below
1190 let splitMode = onlyOneWin ? "vertical" : ""
1192 " Open the new window
1194 exec(splitMode." sp " . self.path.str({'format': 'Edit'}))
1195 catch /^Vim\%((\a\+)\)\=:E37/
1196 call s:putCursorInTreeWin()
1197 throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified."
1198 catch /^Vim\%((\a\+)\)\=:/
1202 "resize the tree window if no other window was open before
1204 let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
1206 exec("silent ". splitMode ." resize ". size)
1207 call s:exec('wincmd p')
1210 " Restore splitmode settings
1211 let &splitbelow=savesplitbelow
1212 let &splitright=savesplitright
1214 "FUNCTION: TreeFileNode.openVSplit() {{{3
1215 "Open this node in a new vertical window
1216 function! s:TreeFileNode.openVSplit()
1217 if b:NERDTreeType ==# "secondary"
1218 exec "vnew " . self.path.str({'format': 'Edit'})
1222 let winwidth = winwidth(".")
1224 let winwidth = g:NERDTreeWinSize
1227 call s:exec("wincmd p")
1228 exec "vnew " . self.path.str({'format': 'Edit'})
1230 "resize the nerd tree back to the original size
1231 call s:putCursorInTreeWin()
1232 exec("silent vertical resize ". winwidth)
1233 call s:exec('wincmd p')
1235 "FUNCTION: TreeFileNode.openInNewTab(options) {{{3
1236 function! s:TreeFileNode.openInNewTab(options)
1237 let currentTab = tabpagenr()
1239 if !has_key(a:options, 'keepTreeOpen')
1240 call s:closeTreeIfQuitOnOpen()
1243 exec "tabedit " . self.path.str({'format': 'Edit'})
1245 if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab']
1246 exec "tabnext " . currentTab
1250 "FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3
1251 "Places the cursor on the line number this node is rendered on
1254 "isJump: 1 if this cursor movement should be counted as a jump by vim
1255 "recurseUpward: try to put the cursor on the parent if the this node isnt
1257 function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
1258 let ln = self.getLineNum()
1263 call cursor(ln, col("."))
1267 while node != {} && node.getLineNum() ==# -1
1268 let node = node.parent
1272 call node.putCursorHere(a:isJump, 0)
1277 "FUNCTION: TreeFileNode.refresh() {{{3
1278 function! s:TreeFileNode.refresh()
1279 call self.path.refresh()
1281 "FUNCTION: TreeFileNode.rename() {{{3
1282 "Calls the rename method for this nodes path obj
1283 function! s:TreeFileNode.rename(newName)
1284 let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
1285 call self.path.rename(newName)
1286 call self.parent.removeChild(self)
1288 let parentPath = self.path.getParent()
1289 let newParent = b:NERDTreeRoot.findNode(parentPath)
1292 call newParent.createChild(self.path, 1)
1293 call newParent.refresh()
1296 "FUNCTION: TreeFileNode.renderToString {{{3
1297 "returns a string representation for this tree to be rendered in the view
1298 function! s:TreeFileNode.renderToString()
1299 return self._renderToString(0, 0, [], self.getChildCount() ==# 1)
1304 "depth: the current depth in the tree for this call
1305 "drawText: 1 if we should actually draw the line for this node (if 0 then the
1306 "child nodes are rendered only)
1307 "vertMap: a binary array that indicates whether a vertical bar should be draw
1308 "for each depth in the tree
1309 "isLastChild:true if this curNode is the last child of its parent
1310 function! s:TreeFileNode._renderToString(depth, drawText, vertMap, isLastChild)
1316 "get all the leading spaces and vertical tree parts for this line
1318 for j in a:vertMap[0:-2]
1319 let treeParts = treeParts . ' '
1323 if self.path.isDirectory
1325 let treeParts = treeParts . '▼ '
1327 let treeParts = treeParts . '▶ '
1330 let treeParts = treeParts . ''
1333 let line = treeParts . self.displayString()
1335 let output = output . line . "\n"
1338 "if the node is an open dir, draw its children
1339 if self.path.isDirectory ==# 1 && self.isOpen ==# 1
1341 let childNodesToDraw = self.getVisibleChildren()
1342 if len(childNodesToDraw) > 0
1344 "draw all the nodes children except the last
1345 let lastIndx = len(childNodesToDraw)-1
1347 for i in childNodesToDraw[0:lastIndx-1]
1348 let output = output . i._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
1352 "draw the last child, indicating that it IS the last
1353 let output = output . childNodesToDraw[lastIndx]._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 0), 1)
1359 "CLASS: TreeDirNode {{{2
1360 "This class is a child of the TreeFileNode class and constitutes the
1361 "'Composite' part of the composite design pattern between the treenode
1363 "============================================================
1364 let s:TreeDirNode = copy(s:TreeFileNode)
1365 "FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
1366 "class method that returns the highest cached ancestor of the current root
1367 function! s:TreeDirNode.AbsoluteTreeRoot()
1368 let currentNode = b:NERDTreeRoot
1369 while currentNode.parent != {}
1370 let currentNode = currentNode.parent
1374 "FUNCTION: TreeDirNode.activate(forceKeepWinOpen) {{{3
1375 unlet s:TreeDirNode.activate
1376 function! s:TreeDirNode.activate(forceKeepWinOpen)
1377 call self.toggleOpen()
1379 call self.putCursorHere(0, 0)
1381 "FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{3
1382 "Adds the given treenode to the list of children for this node
1385 "-treenode: the node to add
1386 "-inOrder: 1 if the new node should be inserted in sorted order
1387 function! s:TreeDirNode.addChild(treenode, inOrder)
1388 call add(self.children, a:treenode)
1389 let a:treenode.parent = self
1392 call self.sortChildren()
1396 "FUNCTION: TreeDirNode.close() {{{3
1397 "Closes this directory
1398 function! s:TreeDirNode.close()
1402 "FUNCTION: TreeDirNode.closeChildren() {{{3
1403 "Closes all the child dir nodes of this node
1404 function! s:TreeDirNode.closeChildren()
1405 for i in self.children
1406 if i.path.isDirectory
1408 call i.closeChildren()
1413 "FUNCTION: TreeDirNode.createChild(path, inOrder) {{{3
1414 "Instantiates a new child node for this node with the given path. The new
1415 "nodes parent is set to this node.
1418 "path: a Path object that this node will represent/contain
1419 "inOrder: 1 if the new node should be inserted in sorted order
1422 "the newly created node
1423 function! s:TreeDirNode.createChild(path, inOrder)
1424 let newTreeNode = s:TreeFileNode.New(a:path)
1425 call self.addChild(newTreeNode, a:inOrder)
1429 "FUNCTION: TreeDirNode.findNode(path) {{{3
1430 "Will find one of the children (recursively) that has the given path
1433 "path: a path object
1434 unlet s:TreeDirNode.findNode
1435 function! s:TreeDirNode.findNode(path)
1436 if a:path.equals(self.path)
1439 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1443 if self.path.isDirectory
1444 for i in self.children
1445 let retVal = i.findNode(a:path)
1453 "FUNCTION: TreeDirNode.getChildCount() {{{3
1454 "Returns the number of children this node has
1455 function! s:TreeDirNode.getChildCount()
1456 return len(self.children)
1459 "FUNCTION: TreeDirNode.getChild(path) {{{3
1460 "Returns child node of this node that has the given path or {} if no such node
1463 "This function doesnt not recurse into child dir nodes
1466 "path: a path object
1467 function! s:TreeDirNode.getChild(path)
1468 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1472 let index = self.getChildIndex(a:path)
1476 return self.children[index]
1481 "FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{3
1482 "returns the child at the given index
1484 "indx: the index to get the child from
1485 "visible: 1 if only the visible children array should be used, 0 if all the
1486 "children should be searched.
1487 function! s:TreeDirNode.getChildByIndex(indx, visible)
1488 let array_to_search = a:visible? self.getVisibleChildren() : self.children
1489 if a:indx > len(array_to_search)
1490 throw "NERDTree.InvalidArgumentsError: Index is out of bounds."
1492 return array_to_search[a:indx]
1495 "FUNCTION: TreeDirNode.getChildIndex(path) {{{3
1496 "Returns the index of the child node of this node that has the given path or
1497 "-1 if no such node exists.
1499 "This function doesnt not recurse into child dir nodes
1502 "path: a path object
1503 function! s:TreeDirNode.getChildIndex(path)
1504 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1508 "do a binary search for the child
1510 let z = self.getChildCount()
1513 let diff = a:path.compareTo(self.children[mid].path)
1526 "FUNCTION: TreeDirNode.GetSelected() {{{3
1527 "Returns the current node if it is a dir node, or else returns the current
1529 unlet s:TreeDirNode.GetSelected
1530 function! s:TreeDirNode.GetSelected()
1531 let currentDir = s:TreeFileNode.GetSelected()
1532 if currentDir != {} && !currentDir.isRoot()
1533 if currentDir.path.isDirectory ==# 0
1534 let currentDir = currentDir.parent
1539 "FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
1540 "Returns the number of visible children this node has
1541 function! s:TreeDirNode.getVisibleChildCount()
1542 return len(self.getVisibleChildren())
1545 "FUNCTION: TreeDirNode.getVisibleChildren() {{{3
1546 "Returns a list of children to display for this node, in the correct order
1549 "an array of treenodes
1550 function! s:TreeDirNode.getVisibleChildren()
1552 for i in self.children
1553 if i.path.ignore() ==# 0
1554 call add(toReturn, i)
1560 "FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
1561 "returns 1 if this node has any childre, 0 otherwise..
1562 function! s:TreeDirNode.hasVisibleChildren()
1563 return self.getVisibleChildCount() != 0
1566 "FUNCTION: TreeDirNode._initChildren() {{{3
1567 "Removes all childen from this node and re-reads them
1570 "silent: 1 if the function should not echo any "please wait" messages for
1573 "Return: the number of child nodes read
1574 function! s:TreeDirNode._initChildren(silent)
1575 "remove all the current child nodes
1576 let self.children = []
1578 "get an array of all the files in the nodes dir
1580 let globDir = dir.str({'format': 'Glob'})
1581 let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
1582 let files = split(filesStr, "\n")
1584 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
1585 call s:echo("Please wait, caching a large dir ...")
1588 let invalidFilesFound = 0
1591 "filter out the .. and . directories
1592 "Note: we must match .. AND ../ cos sometimes the globpath returns
1593 "../ for path with strange chars (eg $)
1594 if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$'
1596 "put the next file in a new node and attach it
1598 let path = s:Path.New(i)
1599 call self.createChild(path, 0)
1600 catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
1601 let invalidFilesFound += 1
1606 call self.sortChildren()
1608 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
1609 call s:echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).")
1612 if invalidFilesFound
1613 call s:echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree")
1615 return self.getChildCount()
1617 "FUNCTION: TreeDirNode.New(path) {{{3
1618 "Returns a new TreeNode object with the given path and parent
1621 "path: a path object representing the full filesystem path to the file/dir that the node represents
1622 unlet s:TreeDirNode.New
1623 function! s:TreeDirNode.New(path)
1624 if a:path.isDirectory != 1
1625 throw "NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object."
1628 let newTreeNode = copy(self)
1629 let newTreeNode.path = a:path
1631 let newTreeNode.isOpen = 0
1632 let newTreeNode.children = []
1634 let newTreeNode.parent = {}
1638 "FUNCTION: TreeDirNode.open() {{{3
1639 "Reads in all this nodes children
1641 "Return: the number of child nodes read
1642 unlet s:TreeDirNode.open
1643 function! s:TreeDirNode.open()
1645 if self.children ==# []
1646 return self._initChildren(0)
1652 " FUNCTION: TreeDirNode.openExplorer() {{{3
1653 " opens an explorer window for this node in the previous window (could be a
1654 " nerd tree or a netrw)
1655 function! s:TreeDirNode.openExplorer()
1656 let oldwin = winnr()
1657 call s:exec('wincmd p')
1658 if oldwin ==# winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2)
1659 call s:exec('wincmd p')
1660 call self.openSplit()
1662 exec ("silent edit " . self.path.str({'format': 'Edit'}))
1665 "FUNCTION: TreeDirNode.openInNewTab(options) {{{3
1666 unlet s:TreeDirNode.openInNewTab
1667 function! s:TreeDirNode.openInNewTab(options)
1668 let currentTab = tabpagenr()
1670 if !has_key(a:options, 'keepTreeOpen') || !a:options['keepTreeOpen']
1671 call s:closeTreeIfQuitOnOpen()
1675 call s:initNerdTree(self.path.str())
1677 if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab']
1678 exec "tabnext " . currentTab
1681 "FUNCTION: TreeDirNode.openRecursively() {{{3
1682 "Opens this treenode and all of its children whose paths arent 'ignored'
1683 "because of the file filters.
1685 "This method is actually a wrapper for the OpenRecursively2 method which does
1687 function! s:TreeDirNode.openRecursively()
1688 call self._openRecursively2(1)
1691 "FUNCTION: TreeDirNode._openRecursively2() {{{3
1692 "Opens this all children of this treenode recursively if either:
1693 " *they arent filtered by file filters
1697 "forceOpen: 1 if this node should be opened regardless of file filters
1698 function! s:TreeDirNode._openRecursively2(forceOpen)
1699 if self.path.ignore() ==# 0 || a:forceOpen
1701 if self.children ==# []
1702 call self._initChildren(1)
1705 for i in self.children
1706 if i.path.isDirectory ==# 1
1707 call i._openRecursively2(0)
1713 "FUNCTION: TreeDirNode.refresh() {{{3
1714 unlet s:TreeDirNode.refresh
1715 function! s:TreeDirNode.refresh()
1716 call self.path.refresh()
1718 "if this node was ever opened, refresh its children
1719 if self.isOpen || !empty(self.children)
1720 "go thru all the files/dirs under this node
1721 let newChildNodes = []
1722 let invalidFilesFound = 0
1724 let globDir = dir.str({'format': 'Glob'})
1725 let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
1726 let files = split(filesStr, "\n")
1728 "filter out the .. and . directories
1729 "Note: we must match .. AND ../ cos sometimes the globpath returns
1730 "../ for path with strange chars (eg $)
1731 if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$'
1734 "create a new path and see if it exists in this nodes children
1735 let path = s:Path.New(i)
1736 let newNode = self.getChild(path)
1738 call newNode.refresh()
1739 call add(newChildNodes, newNode)
1741 "the node doesnt exist so create it
1743 let newNode = s:TreeFileNode.New(path)
1744 let newNode.parent = self
1745 call add(newChildNodes, newNode)
1749 catch /^NERDTree.InvalidArgumentsError/
1750 let invalidFilesFound = 1
1755 "swap this nodes children out for the children we just read/refreshed
1756 let self.children = newChildNodes
1757 call self.sortChildren()
1759 if invalidFilesFound
1760 call s:echoWarning("some files could not be loaded into the NERD tree")
1765 "FUNCTION: TreeDirNode.reveal(path) {{{3
1766 "reveal the given path, i.e. cache and open all treenodes needed to display it
1768 function! s:TreeDirNode.reveal(path)
1769 if !a:path.isUnder(self.path)
1770 throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str()
1775 if self.path.equals(a:path.getParent())
1776 let n = self.findNode(a:path)
1778 call n.putCursorHere(1,0)
1783 while !p.getParent().equals(self.path)
1784 let p = p.getParent()
1787 let n = self.findNode(p)
1788 call n.reveal(a:path)
1790 "FUNCTION: TreeDirNode.removeChild(treenode) {{{3
1792 "Removes the given treenode from this nodes set of children
1795 "treenode: the node to remove
1797 "Throws a NERDTree.ChildNotFoundError if the given treenode is not found
1798 function! s:TreeDirNode.removeChild(treenode)
1799 for i in range(0, self.getChildCount()-1)
1800 if self.children[i].equals(a:treenode)
1801 call remove(self.children, i)
1806 throw "NERDTree.ChildNotFoundError: child node was not found"
1809 "FUNCTION: TreeDirNode.sortChildren() {{{3
1811 "Sorts the children of this node according to alphabetical order and the
1812 "directory priority.
1814 function! s:TreeDirNode.sortChildren()
1815 let CompareFunc = function("s:compareNodes")
1816 call sort(self.children, CompareFunc)
1819 "FUNCTION: TreeDirNode.toggleOpen() {{{3
1820 "Opens this directory if it is closed and vice versa
1821 function! s:TreeDirNode.toggleOpen()
1822 if self.isOpen ==# 1
1829 "FUNCTION: TreeDirNode.transplantChild(newNode) {{{3
1830 "Replaces the child of this with the given node (where the child node's full
1831 "path matches a:newNode's fullpath). The search for the matching node is
1835 "newNode: the node to graft into the tree
1836 function! s:TreeDirNode.transplantChild(newNode)
1837 for i in range(0, self.getChildCount()-1)
1838 if self.children[i].equals(a:newNode)
1839 let self.children[i] = a:newNode
1840 let a:newNode.parent = self
1845 "============================================================
1847 "============================================================
1849 "FUNCTION: Path.AbsolutePathFor(str) {{{3
1850 function! s:Path.AbsolutePathFor(str)
1852 if s:running_windows
1853 let prependCWD = a:str !~ '^.:\(\\\|\/\)'
1855 let prependCWD = a:str !~ '^/'
1858 let toReturn = a:str
1860 let toReturn = getcwd() . s:Path.Slash() . a:str
1865 "FUNCTION: Path.bookmarkNames() {{{3
1866 function! s:Path.bookmarkNames()
1867 if !exists("self._bookmarkNames")
1868 call self.cacheDisplayString()
1870 return self._bookmarkNames
1872 "FUNCTION: Path.cacheDisplayString() {{{3
1873 function! s:Path.cacheDisplayString()
1874 let self.cachedDisplayString = self.getLastPathComponent(1)
1876 if self.isExecutable
1877 let self.cachedDisplayString = self.cachedDisplayString . '*'
1880 let self._bookmarkNames = []
1881 for i in s:Bookmark.Bookmarks()
1882 if i.path.equals(self)
1883 call add(self._bookmarkNames, i.name)
1886 if !empty(self._bookmarkNames)
1887 let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
1891 let self.cachedDisplayString .= ' -> ' . self.symLinkDest
1895 let self.cachedDisplayString .= ' [RO]'
1898 "FUNCTION: Path.changeToDir() {{{3
1899 function! s:Path.changeToDir()
1900 let dir = self.str({'format': 'Cd'})
1901 if self.isDirectory ==# 0
1902 let dir = self.getParent().str({'format': 'Cd'})
1907 call s:echo("CWD is now: " . getcwd())
1909 throw "NERDTree.PathChangeError: cannot change CWD to " . dir
1913 "FUNCTION: Path.compareTo() {{{3
1915 "Compares this Path to the given path and returns 0 if they are equal, -1 if
1916 "this Path is "less than" the given path, or 1 if it is "greater".
1919 "path: the path object to compare this to
1923 function! s:Path.compareTo(path)
1924 let thisPath = self.getLastPathComponent(1)
1925 let thatPath = a:path.getLastPathComponent(1)
1927 "if the paths are the same then clearly we return 0
1928 if thisPath ==# thatPath
1932 let thisSS = self.getSortOrderIndex()
1933 let thatSS = a:path.getSortOrderIndex()
1935 "compare the sort sequences, if they are different then the return
1939 elseif thisSS > thatSS
1942 "if the sort sequences are the same then compare the paths
1944 let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath
1953 "FUNCTION: Path.Create(fullpath) {{{3
1957 "Creates a path object with the given path. The path is also created on the
1958 "filesystem. If the path already exists, a NERDTree.Path.Exists exception is
1959 "thrown. If any other errors occur, a NERDTree.Path exception is thrown.
1962 "fullpath: the full filesystem path to the file/dir to create
1963 function! s:Path.Create(fullpath)
1964 "bail if the a:fullpath already exists
1965 if isdirectory(a:fullpath) || filereadable(a:fullpath)
1966 throw "NERDTree.CreatePathError: Directory Exists: '" . a:fullpath . "'"
1971 "if it ends with a slash, assume its a dir create it
1972 if a:fullpath =~ '\(\\\|\/\)$'
1973 "whack the trailing slash off the end if it exists
1974 let fullpath = substitute(a:fullpath, '\(\\\|\/\)$', '', '')
1976 call mkdir(fullpath, 'p')
1978 "assume its a file and create
1980 call writefile([], a:fullpath)
1983 throw "NERDTree.CreatePathError: Could not create path: '" . a:fullpath . "'"
1986 return s:Path.New(a:fullpath)
1989 "FUNCTION: Path.copy(dest) {{{3
1991 "Copies the file/dir represented by this Path to the given location
1994 "dest: the location to copy this dir/file to
1995 function! s:Path.copy(dest)
1996 if !s:Path.CopyingSupported()
1997 throw "NERDTree.CopyingNotSupportedError: Copying is not supported on this OS"
2000 let dest = s:Path.WinToUnixPath(a:dest)
2002 let cmd = g:NERDTreeCopyCmd . " " . self.str() . " " . dest
2003 let success = system(cmd)
2005 throw "NERDTree.CopyError: Could not copy ''". self.str() ."'' to: '" . a:dest . "'"
2009 "FUNCTION: Path.CopyingSupported() {{{3
2011 "returns 1 if copying is supported for this OS
2012 function! s:Path.CopyingSupported()
2013 return exists('g:NERDTreeCopyCmd')
2017 "FUNCTION: Path.copyingWillOverwrite(dest) {{{3
2019 "returns 1 if copy this path to the given location will cause files to
2023 "dest: the location this path will be copied to
2024 function! s:Path.copyingWillOverwrite(dest)
2025 if filereadable(a:dest)
2029 if isdirectory(a:dest)
2030 let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
2031 if filereadable(path)
2037 "FUNCTION: Path.delete() {{{3
2039 "Deletes the file represented by this path.
2040 "Deletion of directories is not supported
2042 "Throws NERDTree.Path.Deletion exceptions
2043 function! s:Path.delete()
2046 let cmd = g:NERDTreeRemoveDirCmd . self.str({'escape': 1})
2047 let success = system(cmd)
2049 if v:shell_error != 0
2050 throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'"
2053 let success = delete(self.str())
2055 throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'"
2059 "delete all bookmarks for this path
2060 for i in self.bookmarkNames()
2061 let bookmark = s:Bookmark.BookmarkFor(i)
2062 call bookmark.delete()
2066 "FUNCTION: Path.displayString() {{{3
2068 "Returns a string that specifies how the path should be represented as a
2070 function! s:Path.displayString()
2071 if self.cachedDisplayString ==# ""
2072 call self.cacheDisplayString()
2075 return self.cachedDisplayString
2077 "FUNCTION: Path.extractDriveLetter(fullpath) {{{3
2079 "If running windows, cache the drive letter for this path
2080 function! s:Path.extractDriveLetter(fullpath)
2081 if s:running_windows
2082 let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
2088 "FUNCTION: Path.exists() {{{3
2089 "return 1 if this path points to a location that is readable or is a directory
2090 function! s:Path.exists()
2092 return filereadable(p) || isdirectory(p)
2094 "FUNCTION: Path.getDir() {{{3
2096 "Returns this path if it is a directory, else this paths parent.
2100 function! s:Path.getDir()
2104 return self.getParent()
2107 "FUNCTION: Path.getParent() {{{3
2109 "Returns a new path object for this paths parent
2113 function! s:Path.getParent()
2114 if s:running_windows
2115 let path = self.drive . '\' . join(self.pathSegments[0:-2], '\')
2117 let path = '/'. join(self.pathSegments[0:-2], '/')
2120 return s:Path.New(path)
2122 "FUNCTION: Path.getLastPathComponent(dirSlash) {{{3
2124 "Gets the last part of this path.
2127 "dirSlash: if 1 then a trailing slash will be added to the returned value for
2129 function! s:Path.getLastPathComponent(dirSlash)
2130 if empty(self.pathSegments)
2133 let toReturn = self.pathSegments[-1]
2134 if a:dirSlash && self.isDirectory
2135 let toReturn = toReturn . '/'
2140 "FUNCTION: Path.getSortOrderIndex() {{{3
2141 "returns the index of the pattern in g:NERDTreeSortOrder that this path matches
2142 function! s:Path.getSortOrderIndex()
2144 while i < len(g:NERDTreeSortOrder)
2145 if self.getLastPathComponent(1) =~ g:NERDTreeSortOrder[i]
2150 return s:NERDTreeSortStarIndex
2153 "FUNCTION: Path.ignore() {{{3
2154 "returns true if this path should be ignored
2155 function! s:Path.ignore()
2156 let lastPathComponent = self.getLastPathComponent(0)
2158 "filter out the user specified paths to ignore
2159 if b:NERDTreeIgnoreEnabled
2160 for i in g:NERDTreeIgnore
2161 if lastPathComponent =~ i
2167 "dont show hidden files unless instructed to
2168 if b:NERDTreeShowHidden ==# 0 && lastPathComponent =~ '^\.'
2172 if b:NERDTreeShowFiles ==# 0 && self.isDirectory ==# 0
2179 "FUNCTION: Path.isUnder(path) {{{3
2180 "return 1 if this path is somewhere under the given path in the filesystem.
2182 "a:path should be a dir
2183 function! s:Path.isUnder(path)
2184 if a:path.isDirectory == 0
2188 let this = self.str()
2189 let that = a:path.str()
2190 return stridx(this, that . s:Path.Slash()) == 0
2193 "FUNCTION: Path.JoinPathStrings(...) {{{3
2194 function! s:Path.JoinPathStrings(...)
2197 let components = extend(components, split(i, '/'))
2199 return '/' . join(components, '/')
2202 "FUNCTION: Path.equals() {{{3
2204 "Determines whether 2 path objects are "equal".
2205 "They are equal if the paths they represent are the same
2208 "path: the other path obj to compare this with
2209 function! s:Path.equals(path)
2210 return self.str() ==# a:path.str()
2213 "FUNCTION: Path.New() {{{3
2214 "The Constructor for the Path object
2215 function! s:Path.New(path)
2216 let newPath = copy(self)
2218 call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path))
2220 let newPath.cachedDisplayString = ""
2225 "FUNCTION: Path.Slash() {{{3
2226 "return the slash to use for the current OS
2227 function! s:Path.Slash()
2228 return s:running_windows ? '\' : '/'
2231 "FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
2234 "Throws NERDTree.Path.InvalidArguments exception.
2235 function! s:Path.readInfoFromDisk(fullpath)
2236 call self.extractDriveLetter(a:fullpath)
2238 let fullpath = s:Path.WinToUnixPath(a:fullpath)
2240 if getftype(fullpath) ==# "fifo"
2241 throw "NERDTree.InvalidFiletypeError: Cant handle FIFO files: " . a:fullpath
2244 let self.pathSegments = split(fullpath, '/')
2246 let self.isReadOnly = 0
2247 if isdirectory(a:fullpath)
2248 let self.isDirectory = 1
2249 elseif filereadable(a:fullpath)
2250 let self.isDirectory = 0
2251 let self.isReadOnly = filewritable(a:fullpath) ==# 0
2253 throw "NERDTree.InvalidArgumentsError: Invalid path = " . a:fullpath
2256 let self.isExecutable = 0
2257 if !self.isDirectory
2258 let self.isExecutable = getfperm(a:fullpath) =~ 'x'
2261 "grab the last part of the path (minus the trailing slash)
2262 let lastPathComponent = self.getLastPathComponent(0)
2264 "get the path to the new node with the parent dir fully resolved
2265 let hardPath = resolve(self.strTrunk()) . '/' . lastPathComponent
2267 "if the last part of the path is a symlink then flag it as such
2268 let self.isSymLink = (resolve(hardPath) != hardPath)
2270 let self.symLinkDest = resolve(fullpath)
2272 "if the link is a dir then slap a / on the end of its dest
2273 if isdirectory(self.symLinkDest)
2275 "we always wanna treat MS windows shortcuts as files for
2277 if hardPath !~ '\.lnk$'
2279 let self.symLinkDest = self.symLinkDest . '/'
2285 "FUNCTION: Path.refresh() {{{3
2286 function! s:Path.refresh()
2287 call self.readInfoFromDisk(self.str())
2288 call self.cacheDisplayString()
2291 "FUNCTION: Path.rename() {{{3
2293 "Renames this node on the filesystem
2294 function! s:Path.rename(newPath)
2296 throw "NERDTree.InvalidArgumentsError: Invalid newPath for renaming = ". a:newPath
2299 let success = rename(self.str(), a:newPath)
2301 throw "NERDTree.PathRenameError: Could not rename: '" . self.str() . "'" . 'to:' . a:newPath
2303 call self.readInfoFromDisk(a:newPath)
2305 for i in self.bookmarkNames()
2306 let b = s:Bookmark.BookmarkFor(i)
2307 call b.setPath(copy(self))
2309 call s:Bookmark.Write()
2312 "FUNCTION: Path.str() {{{3
2314 "Returns a string representation of this Path
2316 "Takes an optional dictionary param to specify how the output should be
2319 "The dict may have the following keys:
2324 "The 'format' key may have a value of:
2325 " 'Cd' - a string to be used with the :cd command
2326 " 'Edit' - a string to be used with :e :sp :new :tabedit etc
2327 " 'UI' - a string used in the NERD tree UI
2329 "The 'escape' key, if specified will cause the output to be escaped with
2332 "The 'truncateTo' key causes the resulting string to be truncated to the value
2333 "'truncateTo' maps to. A '<' char will be prepended.
2334 function! s:Path.str(...)
2335 let options = a:0 ? a:1 : {}
2338 if has_key(options, 'format')
2339 let format = options['format']
2340 if has_key(self, '_strFor' . format)
2341 exec 'let toReturn = self._strFor' . format . '()'
2343 raise 'NERDTree.UnknownFormatError: unknown format "'. format .'"'
2346 let toReturn = self._str()
2349 if has_key(options, 'escape') && options['escape']
2350 let toReturn = shellescape(toReturn)
2353 if has_key(options, 'truncateTo')
2354 let limit = options['truncateTo']
2355 if len(toReturn) > limit
2356 let toReturn = "<" . strpart(toReturn, len(toReturn) - limit + 1)
2363 "FUNCTION: Path._strForUI() {{{3
2364 function! s:Path._strForUI()
2365 let toReturn = '/' . join(self.pathSegments, '/')
2366 if self.isDirectory && toReturn != '/'
2367 let toReturn = toReturn . '/'
2372 "FUNCTION: Path._strForCd() {{{3
2374 " returns a string that can be used with :cd
2375 function! s:Path._strForCd()
2376 return escape(self.str(), s:escape_chars)
2378 "FUNCTION: Path._strForEdit() {{{3
2380 "Return: the string for this path that is suitable to be used with the :edit
2382 function! s:Path._strForEdit()
2383 let p = self.str({'format': 'UI'})
2386 if s:running_windows
2387 let p = tolower(self.str())
2388 let cwd = tolower(getcwd())
2391 let p = escape(p, s:escape_chars)
2393 let cwd = cwd . s:Path.Slash()
2395 "return a relative path if we can
2396 if stridx(p, cwd) ==# 0
2397 let p = strpart(p, strlen(cwd))
2407 "FUNCTION: Path._strForGlob() {{{3
2408 function! s:Path._strForGlob()
2409 let lead = s:Path.Slash()
2411 "if we are running windows then slap a drive letter on the front
2412 if s:running_windows
2413 let lead = self.drive . '\'
2416 let toReturn = lead . join(self.pathSegments, s:Path.Slash())
2418 if !s:running_windows
2419 let toReturn = escape(toReturn, s:escape_chars)
2423 "FUNCTION: Path._str() {{{3
2425 "Gets the string path for this path object that is appropriate for the OS.
2426 "EG, in windows c:\foo\bar
2428 function! s:Path._str()
2429 let lead = s:Path.Slash()
2431 "if we are running windows then slap a drive letter on the front
2432 if s:running_windows
2433 let lead = self.drive . '\'
2436 return lead . join(self.pathSegments, s:Path.Slash())
2439 "FUNCTION: Path.strTrunk() {{{3
2440 "Gets the path without the last segment on the end.
2441 function! s:Path.strTrunk()
2442 return self.drive . '/' . join(self.pathSegments[0:-2], '/')
2445 "FUNCTION: Path.WinToUnixPath(pathstr){{{3
2446 "Takes in a windows path and returns the unix equiv
2448 "A class level method
2451 "pathstr: the windows path to convert
2452 function! s:Path.WinToUnixPath(pathstr)
2453 if !s:running_windows
2457 let toReturn = a:pathstr
2459 "remove the x:\ of the front
2460 let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "")
2462 "convert all \ chars to /
2463 let toReturn = substitute(toReturn, '\', '/', "g")
2468 " SECTION: General Functions {{{1
2469 "============================================================
2470 "FUNCTION: s:bufInWindows(bnum){{{2
2471 "[[STOLEN FROM VTREEEXPLORER.VIM]]
2472 "Determine the number of windows open to this buffer number.
2473 "Care of Yegappan Lakshman. Thanks!
2476 "bnum: the subject buffers buffer number
2477 function! s:bufInWindows(bnum)
2481 let bufnum = winbufnr(winnum)
2485 if bufnum ==# a:bnum
2488 let winnum = winnum + 1
2493 "FUNCTION: s:checkForBrowse(dir) {{{2
2494 "inits a secondary nerd tree in the current buffer if appropriate
2495 function! s:checkForBrowse(dir)
2496 if a:dir != '' && isdirectory(a:dir)
2497 call s:initNerdTreeInPlace(a:dir)
2500 "FUNCTION: s:compareBookmarks(first, second) {{{2
2501 "Compares two bookmarks
2502 function! s:compareBookmarks(first, second)
2503 return a:first.compareTo(a:second)
2506 " FUNCTION: s:completeBookmarks(A,L,P) {{{2
2507 " completion function for the bookmark commands
2508 function! s:completeBookmarks(A,L,P)
2509 return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"')
2511 " FUNCTION: s:exec(cmd) {{{2
2512 " same as :exec cmd but eventignore=all is set for the duration
2513 function! s:exec(cmd)
2519 " FUNCTION: s:findAndRevealPath() {{{2
2520 function! s:findAndRevealPath()
2522 let p = s:Path.New(expand("%:p"))
2523 catch /^NERDTree.InvalidArgumentsError/
2524 call s:echo("no file for the current buffer")
2528 if !s:treeExistsForTab()
2529 call s:initNerdTree(p.getParent().str())
2531 if !p.isUnder(s:TreeFileNode.GetRootForTab().path)
2532 call s:initNerdTree(p.getParent().str())
2539 call s:putCursorInTreeWin()
2540 call b:NERDTreeRoot.reveal(p)
2542 "FUNCTION: s:initNerdTree(name) {{{2
2543 "Initialise the nerd tree for this tab. The tree will start in either the
2544 "given directory, or the directory associated with the given bookmark
2547 "name: the name of a bookmark or a directory
2548 function! s:initNerdTree(name)
2550 if s:Bookmark.BookmarkExistsFor(a:name)
2551 let path = s:Bookmark.BookmarkFor(a:name).path
2553 let dir = a:name ==# '' ? getcwd() : a:name
2555 "hack to get an absolute path if a relative path is given
2557 let dir = getcwd() . s:Path.Slash() . dir
2559 let dir = resolve(dir)
2562 let path = s:Path.New(dir)
2563 catch /^NERDTree.InvalidArgumentsError/
2564 call s:echo("No bookmark or directory found for: " . a:name)
2568 if !path.isDirectory
2569 let path = path.getParent()
2572 "if instructed to, then change the vim CWD to the dir the NERDTree is
2574 if g:NERDTreeChDirMode != 0
2575 call path.changeToDir()
2578 if s:treeExistsForTab()
2582 unlet t:NERDTreeBufName
2585 let newRoot = s:TreeDirNode.New(path)
2588 call s:createTreeWin()
2589 let b:treeShowHelp = 0
2590 let b:NERDTreeIgnoreEnabled = 1
2591 let b:NERDTreeShowFiles = g:NERDTreeShowFiles
2592 let b:NERDTreeShowHidden = g:NERDTreeShowHidden
2593 let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
2594 let b:NERDTreeRoot = newRoot
2596 let b:NERDTreeType = "primary"
2599 call b:NERDTreeRoot.putCursorHere(0, 0)
2602 "FUNCTION: s:initNerdTreeInPlace(dir) {{{2
2603 function! s:initNerdTreeInPlace(dir)
2605 let path = s:Path.New(a:dir)
2606 catch /^NERDTree.InvalidArgumentsError/
2607 call s:echo("Invalid directory name:" . a:name)
2611 "we want the directory buffer to disappear when we do the :edit below
2612 setlocal bufhidden=wipe
2614 let previousBuf = expand("#")
2616 "we need a unique name for each secondary tree buffer to ensure they are
2618 exec "silent edit " . s:nextBufferName()
2620 let b:NERDTreePreviousBuf = bufnr(previousBuf)
2622 let b:NERDTreeRoot = s:TreeDirNode.New(path)
2623 call b:NERDTreeRoot.open()
2625 "throwaway buffer options
2627 setlocal buftype=nofile
2628 setlocal bufhidden=hide
2630 setlocal foldcolumn=0
2631 setlocal nobuflisted
2633 if g:NERDTreeShowLineNumbers
2641 if g:NERDTreeHighlightCursorline
2645 call s:setupStatusline()
2647 let b:treeShowHelp = 0
2648 let b:NERDTreeIgnoreEnabled = 1
2649 let b:NERDTreeShowFiles = g:NERDTreeShowFiles
2650 let b:NERDTreeShowHidden = g:NERDTreeShowHidden
2651 let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
2653 let b:NERDTreeType = "secondary"
2655 call s:bindMappings()
2656 setfiletype nerdtree
2657 " syntax highlighting
2658 if has("syntax") && exists("g:syntax_on")
2659 call s:setupSyntaxHighlighting()
2664 " FUNCTION: s:initNerdTreeMirror() {{{2
2665 function! s:initNerdTreeMirror()
2667 "get the names off all the nerd tree buffers
2668 let treeBufNames = []
2669 for i in range(1, tabpagenr("$"))
2670 let nextName = s:tabpagevar(i, 'NERDTreeBufName')
2671 if nextName != -1 && (!exists("t:NERDTreeBufName") || nextName != t:NERDTreeBufName)
2672 call add(treeBufNames, nextName)
2675 let treeBufNames = s:unique(treeBufNames)
2677 "map the option names (that the user will be prompted with) to the nerd
2681 while i < len(treeBufNames)
2682 let bufName = treeBufNames[i]
2683 let treeRoot = getbufvar(bufName, "NERDTreeRoot")
2684 let options[i+1 . '. ' . treeRoot.path.str() . ' (buf name: ' . bufName . ')'] = bufName
2688 "work out which tree to mirror, if there is more than 1 then ask the user
2690 if len(keys(options)) > 1
2691 let choices = ["Choose a tree to mirror"]
2692 let choices = extend(choices, sort(keys(options)))
2693 let choice = inputlist(choices)
2694 if choice < 1 || choice > len(options) || choice ==# ''
2698 let bufferName = options[sort(keys(options))[choice-1]]
2699 elseif len(keys(options)) ==# 1
2700 let bufferName = values(options)[0]
2702 call s:echo("No trees to mirror")
2706 if s:treeExistsForTab() && s:isTreeOpen()
2710 let t:NERDTreeBufName = bufferName
2711 call s:createTreeWin()
2712 exec 'buffer ' . bufferName
2717 " FUNCTION: s:nextBufferName() {{{2
2718 " returns the buffer name for the next nerd tree
2719 function! s:nextBufferName()
2720 let name = s:NERDTreeBufName . s:next_buffer_number
2721 let s:next_buffer_number += 1
2724 " FUNCTION: s:tabpagevar(tabnr, var) {{{2
2725 function! s:tabpagevar(tabnr, var)
2726 let currentTab = tabpagenr()
2730 exec "tabnext " . a:tabnr
2732 if exists('t:' . a:var)
2733 exec 'let v = t:' . a:var
2735 exec "tabnext " . currentTab
2741 " Function: s:treeExistsForBuffer() {{{2
2742 " Returns 1 if a nerd tree root exists in the current buffer
2743 function! s:treeExistsForBuf()
2744 return exists("b:NERDTreeRoot")
2746 " Function: s:treeExistsForTab() {{{2
2747 " Returns 1 if a nerd tree root exists in the current tab
2748 function! s:treeExistsForTab()
2749 return exists("t:NERDTreeBufName")
2751 " Function: s:unique(list) {{{2
2752 " returns a:list without duplicates
2753 function! s:unique(list)
2756 if index(uniqlist, elem) ==# -1
2757 let uniqlist += [elem]
2762 " SECTION: Public API {{{1
2763 "============================================================
2764 let g:NERDTreePath = s:Path
2765 let g:NERDTreeDirNode = s:TreeDirNode
2766 let g:NERDTreeFileNode = s:TreeFileNode
2767 let g:NERDTreeBookmark = s:Bookmark
2769 function! NERDTreeAddMenuItem(options)
2770 call s:MenuItem.Create(a:options)
2773 function! NERDTreeAddMenuSeparator(...)
2774 let opts = a:0 ? a:1 : {}
2775 call s:MenuItem.CreateSeparator(opts)
2778 function! NERDTreeAddSubmenu(options)
2779 return s:MenuItem.Create(a:options)
2782 function! NERDTreeAddKeyMap(options)
2783 call s:KeyMap.Create(a:options)
2786 function! NERDTreeRender()
2790 " SECTION: View Functions {{{1
2791 "============================================================
2792 "FUNCTION: s:centerView() {{{2
2793 "centers the nerd tree window around the cursor (provided the nerd tree
2795 function! s:centerView()
2796 if g:NERDTreeAutoCenter
2797 let current_line = winline()
2798 let lines_to_top = current_line
2799 let lines_to_bottom = winheight(s:getTreeWinNum()) - current_line
2800 if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold
2805 "FUNCTION: s:closeTree() {{{2
2806 "Closes the primary NERD tree window for this tab
2807 function! s:closeTree()
2809 throw "NERDTree.NoTreeFoundError: no NERDTree is open"
2813 if winnr() == s:getTreeWinNum()
2815 let bufnr = bufnr("")
2818 let bufnr = bufnr("")
2821 call s:exec(s:getTreeWinNum() . " wincmd w")
2823 call s:exec(bufwinnr(bufnr) . " wincmd w")
2829 "FUNCTION: s:closeTreeIfOpen() {{{2
2830 "Closes the NERD tree window if it is open
2831 function! s:closeTreeIfOpen()
2836 "FUNCTION: s:closeTreeIfQuitOnOpen() {{{2
2837 "Closes the NERD tree window if the close on open option is set
2838 function! s:closeTreeIfQuitOnOpen()
2839 if g:NERDTreeQuitOnOpen && s:isTreeOpen()
2843 "FUNCTION: s:createTreeWin() {{{2
2844 "Inits the NERD tree window. ie. opens it, sizes it, sets all the local
2846 function! s:createTreeWin()
2847 "create the nerd tree window
2848 let splitLocation = g:NERDTreeWinPos ==# "left" ? "topleft " : "botright "
2849 let splitSize = g:NERDTreeWinSize
2851 if !exists('t:NERDTreeBufName')
2852 let t:NERDTreeBufName = s:nextBufferName()
2853 silent! exec splitLocation . 'vertical ' . splitSize . ' new'
2854 silent! exec "edit " . t:NERDTreeBufName
2856 silent! exec splitLocation . 'vertical ' . splitSize . ' split'
2857 silent! exec "buffer " . t:NERDTreeBufName
2860 setlocal winfixwidth
2862 "throwaway buffer options
2864 setlocal buftype=nofile
2866 setlocal foldcolumn=0
2867 setlocal nobuflisted
2869 if g:NERDTreeShowLineNumbers
2877 if g:NERDTreeHighlightCursorline
2881 call s:setupStatusline()
2883 call s:bindMappings()
2884 setfiletype nerdtree
2885 " syntax highlighting
2886 if has("syntax") && exists("g:syntax_on")
2887 call s:setupSyntaxHighlighting()
2891 "FUNCTION: s:dumpHelp {{{2
2892 "prints out the quick help
2893 function! s:dumpHelp()
2895 if b:treeShowHelp ==# 1
2896 let @h= "\" NERD tree (" . s:NERD_tree_version . ") quickhelp~\n"
2897 let @h=@h."\" ============================\n"
2898 let @h=@h."\" File node mappings~\n"
2899 let @h=@h."\" ". (g:NERDTreeMouseMode ==# 3 ? "single" : "double") ."-click,\n"
2900 let @h=@h."\" <CR>,\n"
2901 if b:NERDTreeType ==# "primary"
2902 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in prev window\n"
2904 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in current window\n"
2906 if b:NERDTreeType ==# "primary"
2907 let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n"
2909 let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
2910 let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
2911 let @h=@h."\" middle-click,\n"
2912 let @h=@h."\" ". g:NERDTreeMapOpenSplit .": open split\n"
2913 let @h=@h."\" ". g:NERDTreeMapPreviewSplit .": preview split\n"
2914 let @h=@h."\" ". g:NERDTreeMapOpenVSplit .": open vsplit\n"
2915 let @h=@h."\" ". g:NERDTreeMapPreviewVSplit .": preview vsplit\n"
2917 let @h=@h."\"\n\" ----------------------------\n"
2918 let @h=@h."\" Directory node mappings~\n"
2919 let @h=@h."\" ". (g:NERDTreeMouseMode ==# 1 ? "double" : "single") ."-click,\n"
2920 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open & close node\n"
2921 let @h=@h."\" ". g:NERDTreeMapOpenRecursively .": recursively open node\n"
2922 let @h=@h."\" ". g:NERDTreeMapCloseDir .": close parent of node\n"
2923 let @h=@h."\" ". g:NERDTreeMapCloseChildren .": close all child nodes of\n"
2924 let @h=@h."\" current node recursively\n"
2925 let @h=@h."\" middle-click,\n"
2926 let @h=@h."\" ". g:NERDTreeMapOpenExpl.": explore selected dir\n"
2928 let @h=@h."\"\n\" ----------------------------\n"
2929 let @h=@h."\" Bookmark table mappings~\n"
2930 let @h=@h."\" double-click,\n"
2931 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open bookmark\n"
2932 let @h=@h."\" ". g:NERDTreeMapOpenInTab.": open in new tab\n"
2933 let @h=@h."\" ". g:NERDTreeMapOpenInTabSilent .": open in new tab silently\n"
2934 let @h=@h."\" ". g:NERDTreeMapDeleteBookmark .": delete bookmark\n"
2936 let @h=@h."\"\n\" ----------------------------\n"
2937 let @h=@h."\" Tree navigation mappings~\n"
2938 let @h=@h."\" ". g:NERDTreeMapJumpRoot .": go to root\n"
2939 let @h=@h."\" ". g:NERDTreeMapJumpParent .": go to parent\n"
2940 let @h=@h."\" ". g:NERDTreeMapJumpFirstChild .": go to first child\n"
2941 let @h=@h."\" ". g:NERDTreeMapJumpLastChild .": go to last child\n"
2942 let @h=@h."\" ". g:NERDTreeMapJumpNextSibling .": go to next sibling\n"
2943 let @h=@h."\" ". g:NERDTreeMapJumpPrevSibling .": go to prev sibling\n"
2945 let @h=@h."\"\n\" ----------------------------\n"
2946 let @h=@h."\" Filesystem mappings~\n"
2947 let @h=@h."\" ". g:NERDTreeMapChangeRoot .": change tree root to the\n"
2948 let @h=@h."\" selected dir\n"
2949 let @h=@h."\" ". g:NERDTreeMapUpdir .": move tree root up a dir\n"
2950 let @h=@h."\" ". g:NERDTreeMapUpdirKeepOpen .": move tree root up a dir\n"
2951 let @h=@h."\" but leave old root open\n"
2952 let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n"
2953 let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n"
2954 let @h=@h."\" ". g:NERDTreeMapMenu .": Show menu\n"
2955 let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n"
2956 let @h=@h."\" selected dir\n"
2958 let @h=@h."\"\n\" ----------------------------\n"
2959 let @h=@h."\" Tree filtering mappings~\n"
2960 let @h=@h."\" ". g:NERDTreeMapToggleHidden .": hidden files (" . (b:NERDTreeShowHidden ? "on" : "off") . ")\n"
2961 let @h=@h."\" ". g:NERDTreeMapToggleFilters .": file filters (" . (b:NERDTreeIgnoreEnabled ? "on" : "off") . ")\n"
2962 let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (b:NERDTreeShowFiles ? "on" : "off") . ")\n"
2963 let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (b:NERDTreeShowBookmarks ? "on" : "off") . ")\n"
2965 "add quickhelp entries for each custom key map
2966 if len(s:KeyMap.All())
2967 let @h=@h."\"\n\" ----------------------------\n"
2968 let @h=@h."\" Custom mappings~\n"
2969 for i in s:KeyMap.All()
2970 let @h=@h."\" ". i.key .": ". i.quickhelpText ."\n"
2974 let @h=@h."\"\n\" ----------------------------\n"
2975 let @h=@h."\" Other mappings~\n"
2976 let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n"
2977 let @h=@h."\" ". g:NERDTreeMapToggleZoom .": Zoom (maximize-minimize)\n"
2978 let @h=@h."\" the NERDTree window\n"
2979 let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n"
2980 let @h=@h."\"\n\" ----------------------------\n"
2981 let @h=@h."\" Bookmark commands~\n"
2982 let @h=@h."\" :Bookmark <name>\n"
2983 let @h=@h."\" :BookmarkToRoot <name>\n"
2984 let @h=@h."\" :RevealBookmark <name>\n"
2985 let @h=@h."\" :OpenBookmark <name>\n"
2986 let @h=@h."\" :ClearBookmarks [<names>]\n"
2987 let @h=@h."\" :ClearAllBookmarks\n"
2989 let @h="\" Press ". g:NERDTreeMapHelp ." for help\n"
2996 "FUNCTION: s:echo {{{2
2997 "A wrapper for :echo. Appends 'NERDTree:' on the front of all messages
3000 "msg: the message to echo
3001 function! s:echo(msg)
3003 echomsg "NERDTree: " . a:msg
3005 "FUNCTION: s:echoWarning {{{2
3006 "Wrapper for s:echo, sets the message type to warningmsg for this message
3008 "msg: the message to echo
3009 function! s:echoWarning(msg)
3014 "FUNCTION: s:echoError {{{2
3015 "Wrapper for s:echo, sets the message type to errormsg for this message
3017 "msg: the message to echo
3018 function! s:echoError(msg)
3023 "FUNCTION: s:firstUsableWindow(){{{2
3024 "find the window number of the first normal window
3025 function! s:firstUsableWindow()
3027 while i <= winnr("$")
3028 let bnum = winbufnr(i)
3029 if bnum != -1 && getbufvar(bnum, '&buftype') ==# ''
3030 \ && !getwinvar(i, '&previewwindow')
3031 \ && (!getbufvar(bnum, '&modified') || &hidden)
3039 "FUNCTION: s:getPath(ln) {{{2
3040 "Gets the full path to the node that is rendered on the given line number
3043 "ln: the line number to get the path for
3046 "A path if a node was selected, {} if nothing is selected.
3047 "If the 'up a dir' line was selected then the path to the parent of the
3048 "current root is returned
3049 function! s:getPath(ln)
3050 let line = getline(a:ln)
3052 let rootLine = s:TreeFileNode.GetRootLineNum()
3054 "check to see if we have the root node
3056 return b:NERDTreeRoot.path
3059 " in case called from outside the tree
3060 "if line !~ '^ *[|`▶▼ ]' || line =~ '^$'
3064 if line ==# s:tree_up_dir_line
3065 return b:NERDTreeRoot.path.getParent()
3068 let indent = s:indentLevelFor(line)
3070 "remove the tree parts and the leading space
3071 let curFile = s:stripMarkupFromLine(line, 0)
3076 let curFile = substitute(curFile, '/\?$', '/', "")
3083 let curLine = getline(lnum)
3084 let curLineStripped = s:stripMarkupFromLine(curLine, 1)
3086 "have we reached the top of the tree?
3088 let dir = b:NERDTreeRoot.path.str({'format': 'UI'}) . dir
3091 if curLineStripped =~ '/$'
3092 let lpindent = s:indentLevelFor(curLine)
3093 if lpindent < indent
3094 let indent = indent - 1
3096 let dir = substitute (curLineStripped,'^\\', "", "") . dir
3101 let curFile = b:NERDTreeRoot.path.drive . dir . curFile
3102 let toReturn = s:Path.New(curFile)
3106 "FUNCTION: s:getTreeWinNum() {{{2
3107 "gets the nerd tree window number for this tab
3108 function! s:getTreeWinNum()
3109 if exists("t:NERDTreeBufName")
3110 return bufwinnr(t:NERDTreeBufName)
3115 "FUNCTION: s:indentLevelFor(line) {{{2
3116 function! s:indentLevelFor(line)
3117 return match(a:line, '[^ \-+~`|]') / s:tree_wid
3119 "FUNCTION: s:isTreeOpen() {{{2
3120 function! s:isTreeOpen()
3121 return s:getTreeWinNum() != -1
3123 "FUNCTION: s:isWindowUsable(winnumber) {{{2
3124 "Returns 0 if opening a file from the tree in the given window requires it to
3125 "be split, 1 otherwise
3128 "winnumber: the number of the window in question
3129 function! s:isWindowUsable(winnumber)
3130 "gotta split if theres only one window (i.e. the NERD tree)
3135 let oldwinnr = winnr()
3136 call s:exec(a:winnumber . "wincmd p")
3137 let specialWindow = getbufvar("%", '&buftype') != '' || getwinvar('%', '&previewwindow')
3138 let modified = &modified
3139 call s:exec(oldwinnr . "wincmd p")
3141 "if its a special window e.g. quickfix or another explorer plugin then we
3151 return !modified || s:bufInWindows(winbufnr(a:winnumber)) >= 2
3154 " FUNCTION: s:jumpToChild(direction) {{{2
3156 " direction: 0 if going to first child, 1 if going to last
3157 function! s:jumpToChild(direction)
3158 let currentNode = s:TreeFileNode.GetSelected()
3159 if currentNode ==# {} || currentNode.isRoot()
3160 call s:echo("cannot jump to " . (a:direction ? "last" : "first") . " child")
3163 let dirNode = currentNode.parent
3164 let childNodes = dirNode.getVisibleChildren()
3166 let targetNode = childNodes[0]
3168 let targetNode = childNodes[len(childNodes) - 1]
3171 if targetNode.equals(currentNode)
3172 let siblingDir = currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction)
3174 let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0
3175 let targetNode = siblingDir.getChildByIndex(indx, 1)
3179 call targetNode.putCursorHere(1, 0)
3185 "FUNCTION: s:promptToDelBuffer(bufnum, msg){{{2
3186 "prints out the given msg and, if the user responds by pushing 'y' then the
3187 "buffer with the given bufnum is deleted
3190 "bufnum: the buffer that may be deleted
3191 "msg: a message that will be echoed to the user asking them if they wish to
3193 function! s:promptToDelBuffer(bufnum, msg)
3195 if nr2char(getchar()) ==# 'y'
3196 exec "silent bdelete! " . a:bufnum
3200 "FUNCTION: s:putCursorOnBookmarkTable(){{{2
3201 "Places the cursor at the top of the bookmarks table
3202 function! s:putCursorOnBookmarkTable()
3203 if !b:NERDTreeShowBookmarks
3204 throw "NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active"
3207 let rootNodeLine = s:TreeFileNode.GetRootLineNum()
3210 while getline(line) !~ '^>-\+Bookmarks-\+$'
3212 if line >= rootNodeLine
3213 throw "NERDTree.BookmarkTableNotFoundError: didnt find the bookmarks table"
3216 call cursor(line, 0)
3219 "FUNCTION: s:putCursorInTreeWin(){{{2
3220 "Places the cursor in the nerd tree window
3221 function! s:putCursorInTreeWin()
3223 throw "NERDTree.InvalidOperationError: cant put cursor in NERD tree window, no window exists"
3226 call s:exec(s:getTreeWinNum() . "wincmd w")
3229 "FUNCTION: s:renderBookmarks {{{2
3230 function! s:renderBookmarks()
3232 call setline(line(".")+1, ">----------Bookmarks----------")
3233 call cursor(line(".")+1, col("."))
3235 for i in s:Bookmark.Bookmarks()
3236 call setline(line(".")+1, i.str())
3237 call cursor(line(".")+1, col("."))
3240 call setline(line(".")+1, '')
3241 call cursor(line(".")+1, col("."))
3243 "FUNCTION: s:renderView {{{2
3244 "The entry function for rendering the tree
3245 function! s:renderView()
3248 "remember the top line of the buffer and the current line so we can
3249 "restore the view exactly how it was
3250 let curLine = line(".")
3251 let curCol = col(".")
3252 let topLine = line("w0")
3254 "delete all lines in the buffer (being careful not to clobber a register)
3259 "delete the blank line before the help and add one after it
3260 call setline(line(".")+1, "")
3261 call cursor(line(".")+1, col("."))
3263 if b:NERDTreeShowBookmarks
3264 call s:renderBookmarks()
3267 "add the 'up a dir' line
3268 call setline(line(".")+1, s:tree_up_dir_line)
3269 call cursor(line(".")+1, col("."))
3271 "draw the header line
3272 let header = b:NERDTreeRoot.path.str({'format': 'UI', 'truncateTo': winwidth(0)})
3273 call setline(line(".")+1, header)
3274 call cursor(line(".")+1, col("."))
3278 let @o = b:NERDTreeRoot.renderToString()
3282 "delete the blank line at the top of the buffer
3286 let old_scrolloff=&scrolloff
3288 call cursor(topLine, 1)
3290 call cursor(curLine, curCol)
3291 let &scrolloff = old_scrolloff
3293 setlocal nomodifiable
3296 "FUNCTION: s:renderViewSavingPosition {{{2
3297 "Renders the tree and ensures the cursor stays on the current node or the
3298 "current nodes parent if it is no longer available upon re-rendering
3299 function! s:renderViewSavingPosition()
3300 let currentNode = s:TreeFileNode.GetSelected()
3302 "go up the tree till we find a node that will be visible or till we run
3304 while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
3305 let currentNode = currentNode.parent
3310 if currentNode != {}
3311 call currentNode.putCursorHere(0, 0)
3314 "FUNCTION: s:restoreScreenState() {{{2
3316 "Sets the screen state back to what it was when s:saveScreenState was last
3319 "Assumes the cursor is in the NERDTree window
3320 function! s:restoreScreenState()
3321 if !exists("b:NERDTreeOldTopLine") || !exists("b:NERDTreeOldPos") || !exists("b:NERDTreeOldWindowSize")
3324 exec("silent vertical resize ".b:NERDTreeOldWindowSize)
3326 let old_scrolloff=&scrolloff
3328 call cursor(b:NERDTreeOldTopLine, 0)
3330 call setpos(".", b:NERDTreeOldPos)
3331 let &scrolloff=old_scrolloff
3334 "FUNCTION: s:saveScreenState() {{{2
3335 "Saves the current cursor position in the current buffer and the window
3337 function! s:saveScreenState()
3340 call s:putCursorInTreeWin()
3341 let b:NERDTreeOldPos = getpos(".")
3342 let b:NERDTreeOldTopLine = line("w0")
3343 let b:NERDTreeOldWindowSize = winwidth("")
3344 call s:exec(win . "wincmd w")
3345 catch /^NERDTree.InvalidOperationError/
3349 "FUNCTION: s:setupStatusline() {{{2
3350 function! s:setupStatusline()
3351 if g:NERDTreeStatusline != -1
3352 let &l:statusline = g:NERDTreeStatusline
3355 "FUNCTION: s:setupSyntaxHighlighting() {{{2
3356 function! s:setupSyntaxHighlighting()
3357 "treeFlags are syntax items that should be invisible, but give clues as to
3358 "how things should be highlighted
3359 syn match treeFlag #\~#
3360 syn match treeFlag #\[RO\]#
3362 "highlighting for the .. (up dir) line at the top of the tree
3363 execute "syn match treeUp #". s:tree_up_dir_line ."#"
3365 "highlighting for the ~/+ symbols for the directory nodes
3366 syn match treeClosable #\~\<#
3367 syn match treeClosable #\~\.#
3368 syn match treeOpenable #+\<#
3369 syn match treeOpenable #+\.#he=e-1
3371 "highlighting for the tree structural parts
3372 syn match treePart #|#
3373 syn match treePart #`#
3374 syn match treePartFile #[|`]-#hs=s+1 contains=treePart
3376 "quickhelp syntax elements
3377 syn match treeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1
3378 syn match treeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1
3379 syn match treeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=treeFlag
3380 syn match treeToggleOn #".*(on)#hs=e-2,he=e-1 contains=treeHelpKey
3381 syn match treeToggleOff #".*(off)#hs=e-3,he=e-1 contains=treeHelpKey
3382 syn match treeHelpCommand #" :.\{-}\>#hs=s+3
3383 syn match treeHelp #^".*# contains=treeHelpKey,treeHelpTitle,treeFlag,treeToggleOff,treeToggleOn,treeHelpCommand
3385 "highlighting for readonly files
3386 syn match treeRO #.*\[RO\]#hs=s+2 contains=treeFlag,treeBookmark,treePart,treePartFile
3388 "highlighting for sym links
3389 syn match treeLink #[^-| `].* -> # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash
3391 "highlighing for directory nodes and file nodes
3392 syn match treeDirSlash #/#
3393 syn match treeDir #[^-| `].*/# contains=treeLink,treeDirSlash,treeOpenable,treeClosable
3394 syn match treeExecFile #[|`]-.*\*\($\| \)# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark
3395 syn match treeFile #|-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
3396 syn match treeFile #`-.*# contains=treeLink,treePart,treeRO,treePartFile,treeBookmark,treeExecFile
3397 syn match treeCWD #^/.*$#
3399 "highlighting for bookmarks
3400 syn match treeBookmark # {.*}#hs=s+1
3402 "highlighting for the bookmarks table
3403 syn match treeBookmarksLeader #^>#
3404 syn match treeBookmarksHeader #^>-\+Bookmarks-\+$# contains=treeBookmarksLeader
3405 syn match treeBookmarkName #^>.\{-} #he=e-1 contains=treeBookmarksLeader
3406 syn match treeBookmark #^>.*$# contains=treeBookmarksLeader,treeBookmarkName,treeBookmarksHeader
3408 if g:NERDChristmasTree
3409 hi def link treePart Special
3410 hi def link treePartFile Type
3411 hi def link treeFile Normal
3412 hi def link treeExecFile Title
3413 hi def link treeDirSlash Identifier
3414 hi def link treeClosable Type
3416 hi def link treePart Normal
3417 hi def link treePartFile Normal
3418 hi def link treeFile Normal
3419 hi def link treeClosable Title
3422 hi def link treeBookmarksHeader statement
3423 hi def link treeBookmarksLeader ignore
3424 hi def link treeBookmarkName Identifier
3425 hi def link treeBookmark normal
3427 hi def link treeHelp String
3428 hi def link treeHelpKey Identifier
3429 hi def link treeHelpCommand Identifier
3430 hi def link treeHelpTitle Macro
3431 hi def link treeToggleOn Question
3432 hi def link treeToggleOff WarningMsg
3434 hi def link treeDir Directory
3435 hi def link treeUp Directory
3436 hi def link treeCWD Statement
3437 hi def link treeLink Macro
3438 hi def link treeOpenable Title
3439 hi def link treeFlag ignore
3440 hi def link treeRO WarningMsg
3441 hi def link treeBookmark Statement
3443 hi def link NERDTreeCurrentNode Search
3446 "FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2
3447 "returns the given line with all the tree parts stripped off
3450 "line: the subject line
3451 "removeLeadingSpaces: 1 if leading spaces are to be removed (leading spaces =
3452 "any spaces before the actual text of the node)
3453 function! s:stripMarkupFromLine(line, removeLeadingSpaces)
3455 "remove the tree parts and the leading space
3456 let line = substitute (line, s:tree_markup_reg,"","")
3458 "strip off any read only flag
3459 let line = substitute (line, ' \[RO\]', "","")
3461 "strip off any bookmark flags
3462 let line = substitute (line, ' {[^}]*}', "","")
3464 "strip off any executable flags
3465 let line = substitute (line, '*\ze\($\| \)', "","")
3471 let line = substitute (line,' -> .*',"","") " remove link to
3473 let line = substitute (line, '/\?$', '/', "")
3476 if a:removeLeadingSpaces
3477 let line = substitute (line, '^ *', '', '')
3483 "FUNCTION: s:toggle(dir) {{{2
3484 "Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is
3485 "closed it is restored or initialized (if it doesnt exist)
3488 "dir: the full path for the root node (is only used if the NERD tree is being
3490 function! s:toggle(dir)
3491 if s:treeExistsForTab()
3493 call s:createTreeWin()
3497 call s:restoreScreenState()
3502 call s:initNerdTree(a:dir)
3505 "SECTION: Interface bindings {{{1
3506 "============================================================
3507 "FUNCTION: s:activateNode(forceKeepWindowOpen) {{{2
3508 "If the current node is a file, open it in the previous window (or a new one
3509 "if the previous is modified). If it is a directory then it is opened.
3512 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3513 function! s:activateNode(forceKeepWindowOpen)
3514 if getline(".") ==# s:tree_up_dir_line
3518 let treenode = s:TreeFileNode.GetSelected()
3520 call treenode.activate(a:forceKeepWindowOpen)
3522 let bookmark = s:Bookmark.GetSelected()
3524 call bookmark.activate()
3529 "FUNCTION: s:bindMappings() {{{2
3530 function! s:bindMappings()
3531 " set up mappings and commands for this buffer
3532 nnoremap <silent> <buffer> <middlerelease> :call <SID>handleMiddleMouse()<cr>
3533 nnoremap <silent> <buffer> <leftrelease> <leftrelease>:call <SID>checkForActivate()<cr>
3534 nnoremap <silent> <buffer> <2-leftmouse> :call <SID>activateNode(0)<cr>
3536 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapActivateNode . " :call <SID>activateNode(0)<cr>"
3537 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenSplit ." :call <SID>openEntrySplit(0,0)<cr>"
3538 exec "nnoremap <silent> <buffer> <cr> :call <SID>activateNode(0)<cr>"
3540 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreview ." :call <SID>previewNode(0)<cr>"
3541 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewSplit ." :call <SID>previewNode(1)<cr>"
3543 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenVSplit ." :call <SID>openEntrySplit(1,0)<cr>"
3544 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewVSplit ." :call <SID>previewNode(2)<cr>"
3546 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenRecursively ." :call <SID>openNodeRecursively()<cr>"
3548 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdirKeepOpen ." :call <SID>upDir(1)<cr>"
3549 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapUpdir ." :call <SID>upDir(0)<cr>"
3550 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChangeRoot ." :call <SID>chRoot()<cr>"
3552 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChdir ." :call <SID>chCwd()<cr>"
3554 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapQuit ." :call <SID>closeTreeWindow()<cr>"
3556 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefreshRoot ." :call <SID>refreshRoot()<cr>"
3557 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefresh ." :call <SID>refreshCurrent()<cr>"
3559 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapHelp ." :call <SID>displayHelp()<cr>"
3560 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleZoom ." :call <SID>toggleZoom()<cr>"
3561 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleHidden ." :call <SID>toggleShowHidden()<cr>"
3562 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFilters ." :call <SID>toggleIgnoreFilter()<cr>"
3563 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleFiles ." :call <SID>toggleShowFiles()<cr>"
3564 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapToggleBookmarks ." :call <SID>toggleShowBookmarks()<cr>"
3566 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>"
3567 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>"
3569 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapMenu ." :call <SID>showMenu()<cr>"
3571 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>jumpToParent()<cr>"
3572 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>jumpToSibling(1)<cr>"
3573 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpPrevSibling ." :call <SID>jumpToSibling(0)<cr>"
3574 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpFirstChild ." :call <SID>jumpToFirstChild()<cr>"
3575 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpLastChild ." :call <SID>jumpToLastChild()<cr>"
3576 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpRoot ." :call <SID>jumpToRoot()<cr>"
3578 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>"
3579 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTabSilent ." :call <SID>openInNewTab(1)<cr>"
3581 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenExpl ." :call <SID>openExplorer()<cr>"
3583 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>"
3585 "bind all the user custom maps
3586 call s:KeyMap.BindAll()
3588 command! -buffer -nargs=1 Bookmark :call <SID>bookmarkNode('<args>')
3589 command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call <SID>revealBookmark('<args>')
3590 command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call <SID>openBookmark('<args>')
3591 command! -buffer -complete=customlist,s:completeBookmarks -nargs=* ClearBookmarks call <SID>clearBookmarks('<args>')
3592 command! -buffer -complete=customlist,s:completeBookmarks -nargs=+ BookmarkToRoot call s:Bookmark.ToRoot('<args>')
3593 command! -buffer -nargs=0 ClearAllBookmarks call s:Bookmark.ClearAll() <bar> call <SID>renderView()
3594 command! -buffer -nargs=0 ReadBookmarks call s:Bookmark.CacheBookmarks(0) <bar> call <SID>renderView()
3595 command! -buffer -nargs=0 WriteBookmarks call s:Bookmark.Write()
3598 " FUNCTION: s:bookmarkNode(name) {{{2
3599 " Associate the current node with the given name
3600 function! s:bookmarkNode(name)
3601 let currentNode = s:TreeFileNode.GetSelected()
3602 if currentNode != {}
3604 call currentNode.bookmark(a:name)
3606 catch /^NERDTree.IllegalBookmarkNameError/
3607 call s:echo("bookmark names must not contain spaces")
3610 call s:echo("select a node first")
3613 "FUNCTION: s:checkForActivate() {{{2
3614 "Checks if the click should open the current node, if so then activate() is
3615 "called (directories are automatically opened if the symbol beside them is
3617 function! s:checkForActivate()
3618 let currentNode = s:TreeFileNode.GetSelected()
3619 if currentNode != {}
3620 call s:activateNode(0)
3625 " FUNCTION: s:chCwd() {{{2
3627 let treenode = s:TreeFileNode.GetSelected()
3629 call s:echo("Select a node first")
3634 call treenode.path.changeToDir()
3635 catch /^NERDTree.PathChangeError/
3636 call s:echoWarning("could not change cwd")
3640 " FUNCTION: s:chRoot() {{{2
3641 " changes the current root to the selected one
3642 function! s:chRoot()
3643 let treenode = s:TreeFileNode.GetSelected()
3645 call s:echo("Select a node first")
3649 call treenode.makeRoot()
3651 call b:NERDTreeRoot.putCursorHere(0, 0)
3654 " FUNCTION: s:clearBookmarks(bookmarks) {{{2
3655 function! s:clearBookmarks(bookmarks)
3656 if a:bookmarks ==# ''
3657 let currentNode = s:TreeFileNode.GetSelected()
3658 if currentNode != {}
3659 call currentNode.clearBoomarks()
3662 for name in split(a:bookmarks, ' ')
3663 let bookmark = s:Bookmark.BookmarkFor(name)
3664 call bookmark.delete()
3669 " FUNCTION: s:closeChildren() {{{2
3670 " closes all childnodes of the current node
3671 function! s:closeChildren()
3672 let currentNode = s:TreeDirNode.GetSelected()
3673 if currentNode ==# {}
3674 call s:echo("Select a node first")
3678 call currentNode.closeChildren()
3680 call currentNode.putCursorHere(0, 0)
3682 " FUNCTION: s:closeCurrentDir() {{{2
3683 " closes the parent dir of the current node
3684 function! s:closeCurrentDir()
3685 let treenode = s:TreeFileNode.GetSelected()
3687 call s:echo("Select a node first")
3691 let parent = treenode.parent
3692 if parent ==# {} || parent.isRoot()
3693 call s:echo("cannot close tree root")
3695 call treenode.parent.close()
3697 call treenode.parent.putCursorHere(0, 0)
3700 " FUNCTION: s:closeTreeWindow() {{{2
3701 " close the tree window
3702 function! s:closeTreeWindow()
3703 if b:NERDTreeType ==# "secondary" && b:NERDTreePreviousBuf != -1
3704 exec "buffer " . b:NERDTreePreviousBuf
3709 call s:echo("Cannot close last window")
3713 " FUNCTION: s:deleteBookmark() {{{2
3714 " if the cursor is on a bookmark, prompt to delete
3715 function! s:deleteBookmark()
3716 let bookmark = s:Bookmark.GetSelected()
3718 call s:echo("Put the cursor on a bookmark")
3722 echo "Are you sure you wish to delete the bookmark:\n\"" . bookmark.name . "\" (yN):"
3724 if nr2char(getchar()) ==# 'y'
3726 call bookmark.delete()
3730 call s:echoWarning("Could not remove bookmark")
3733 call s:echo("delete aborted" )
3738 " FUNCTION: s:displayHelp() {{{2
3739 " toggles the help display
3740 function! s:displayHelp()
3741 let b:treeShowHelp = b:treeShowHelp ? 0 : 1
3746 " FUNCTION: s:handleMiddleMouse() {{{2
3747 function! s:handleMiddleMouse()
3748 let curNode = s:TreeFileNode.GetSelected()
3750 call s:echo("Put the cursor on a node first" )
3754 if curNode.path.isDirectory
3755 call s:openExplorer()
3757 call s:openEntrySplit(0,0)
3762 " FUNCTION: s:jumpToFirstChild() {{{2
3763 " wrapper for the jump to child method
3764 function! s:jumpToFirstChild()
3765 call s:jumpToChild(0)
3768 " FUNCTION: s:jumpToLastChild() {{{2
3769 " wrapper for the jump to child method
3770 function! s:jumpToLastChild()
3771 call s:jumpToChild(1)
3774 " FUNCTION: s:jumpToParent() {{{2
3775 " moves the cursor to the parent of the current node
3776 function! s:jumpToParent()
3777 let currentNode = s:TreeFileNode.GetSelected()
3778 if !empty(currentNode)
3779 if !empty(currentNode.parent)
3780 call currentNode.parent.putCursorHere(1, 0)
3783 call s:echo("cannot jump to parent")
3786 call s:echo("put the cursor on a node first")
3790 " FUNCTION: s:jumpToRoot() {{{2
3791 " moves the cursor to the root node
3792 function! s:jumpToRoot()
3793 call b:NERDTreeRoot.putCursorHere(1, 0)
3797 " FUNCTION: s:jumpToSibling() {{{2
3798 " moves the cursor to the sibling of the current node in the given direction
3801 " forward: 1 if the cursor should move to the next sibling, 0 if it should
3802 " move back to the previous sibling
3803 function! s:jumpToSibling(forward)
3804 let currentNode = s:TreeFileNode.GetSelected()
3805 if !empty(currentNode)
3806 let sibling = currentNode.findSibling(a:forward)
3809 call sibling.putCursorHere(1, 0)
3813 call s:echo("put the cursor on a node first")
3817 " FUNCTION: s:openBookmark(name) {{{2
3818 " put the cursor on the given bookmark and, if its a file, open it
3819 function! s:openBookmark(name)
3821 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3822 call targetNode.putCursorHere(0, 1)
3824 catch /^NERDTree.BookmarkedNodeNotFoundError/
3825 call s:echo("note - target node is not cached")
3826 let bookmark = s:Bookmark.BookmarkFor(a:name)
3827 let targetNode = s:TreeFileNode.New(bookmark.path)
3829 if targetNode.path.isDirectory
3830 call targetNode.openExplorer()
3832 call targetNode.open()
3835 " FUNCTION: s:openEntrySplit(vertical, forceKeepWindowOpen) {{{2
3836 "Opens the currently selected file from the explorer in a
3840 "forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3841 function! s:openEntrySplit(vertical, forceKeepWindowOpen)
3842 let treenode = s:TreeFileNode.GetSelected()
3845 call treenode.openVSplit()
3847 call treenode.openSplit()
3849 if !a:forceKeepWindowOpen
3850 call s:closeTreeIfQuitOnOpen()
3853 call s:echo("select a node first")
3857 " FUNCTION: s:openExplorer() {{{2
3858 function! s:openExplorer()
3859 let treenode = s:TreeDirNode.GetSelected()
3861 call treenode.openExplorer()
3863 call s:echo("select a node first")
3867 " FUNCTION: s:openInNewTab(stayCurrentTab) {{{2
3868 " Opens the selected node or bookmark in a new tab
3870 " stayCurrentTab: if 1 then vim will stay in the current tab, if 0 then vim
3871 " will go to the tab where the new file is opened
3872 function! s:openInNewTab(stayCurrentTab)
3873 let target = s:TreeFileNode.GetSelected()
3875 let target = s:Bookmark.GetSelected()
3879 call target.openInNewTab({'stayInCurrentTab': a:stayCurrentTab})
3883 " FUNCTION: s:openNodeRecursively() {{{2
3884 function! s:openNodeRecursively()
3885 let treenode = s:TreeFileNode.GetSelected()
3886 if treenode ==# {} || treenode.path.isDirectory ==# 0
3887 call s:echo("Select a directory node first" )
3889 call s:echo("Recursively opening node. Please wait...")
3890 call treenode.openRecursively()
3893 call s:echo("Recursively opening node. Please wait... DONE")
3898 "FUNCTION: s:previewNode() {{{2
3900 " openNewWin: if 0, use the previous window, if 1 open in new split, if 2
3902 function! s:previewNode(openNewWin)
3903 let currentBuf = bufnr("")
3905 call s:openEntrySplit(a:openNewWin ==# 2,1)
3907 call s:activateNode(1)
3909 call s:exec(bufwinnr(currentBuf) . "wincmd w")
3912 " FUNCTION: s:revealBookmark(name) {{{2
3913 " put the cursor on the node associate with the given name
3914 function! s:revealBookmark(name)
3916 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3917 call targetNode.putCursorHere(0, 1)
3918 catch /^NERDTree.BookmarkNotFoundError/
3919 call s:echo("Bookmark isnt cached under the current root")
3922 " FUNCTION: s:refreshRoot() {{{2
3923 " Reloads the current root. All nodes below this will be lost and the root dir
3925 function! s:refreshRoot()
3926 call s:echo("Refreshing the root node. This could take a while...")
3927 call b:NERDTreeRoot.refresh()
3930 call s:echo("Refreshing the root node. This could take a while... DONE")
3933 " FUNCTION: s:refreshCurrent() {{{2
3934 " refreshes the root for the current node
3935 function! s:refreshCurrent()
3936 let treenode = s:TreeDirNode.GetSelected()
3938 call s:echo("Refresh failed. Select a node first")
3942 call s:echo("Refreshing node. This could take a while...")
3943 call treenode.refresh()
3946 call s:echo("Refreshing node. This could take a while... DONE")
3948 " FUNCTION: s:showMenu() {{{2
3949 function! s:showMenu()
3950 let curNode = s:TreeFileNode.GetSelected()
3952 call s:echo("Put the cursor on a node first" )
3956 let mc = s:MenuController.New(s:MenuItem.AllEnabled())
3960 " FUNCTION: s:toggleIgnoreFilter() {{{2
3961 " toggles the use of the NERDTreeIgnore option
3962 function! s:toggleIgnoreFilter()
3963 let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled
3964 call s:renderViewSavingPosition()
3968 " FUNCTION: s:toggleShowBookmarks() {{{2
3969 " toggles the display of bookmarks
3970 function! s:toggleShowBookmarks()
3971 let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks
3972 if b:NERDTreeShowBookmarks
3974 call s:putCursorOnBookmarkTable()
3976 call s:renderViewSavingPosition()
3980 " FUNCTION: s:toggleShowFiles() {{{2
3981 " toggles the display of hidden files
3982 function! s:toggleShowFiles()
3983 let b:NERDTreeShowFiles = !b:NERDTreeShowFiles
3984 call s:renderViewSavingPosition()
3988 " FUNCTION: s:toggleShowHidden() {{{2
3989 " toggles the display of hidden files
3990 function! s:toggleShowHidden()
3991 let b:NERDTreeShowHidden = !b:NERDTreeShowHidden
3992 call s:renderViewSavingPosition()
3996 " FUNCTION: s:toggleZoom() {{2
3997 " zoom (maximize/minimize) the NERDTree window
3998 function! s:toggleZoom()
3999 if exists("b:NERDTreeZoomed") && b:NERDTreeZoomed
4000 let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
4001 exec "silent vertical resize ". size
4002 let b:NERDTreeZoomed = 0
4004 exec "vertical resize"
4005 let b:NERDTreeZoomed = 1
4009 "FUNCTION: s:upDir(keepState) {{{2
4010 "moves the tree up a level
4013 "keepState: 1 if the current root should be left open when the tree is
4015 function! s:upDir(keepState)
4016 let cwd = b:NERDTreeRoot.path.str({'format': 'UI'})
4017 if cwd ==# "/" || cwd =~ '^[^/]..$'
4018 call s:echo("already at top dir")
4021 call b:NERDTreeRoot.close()
4024 let oldRoot = b:NERDTreeRoot
4026 if empty(b:NERDTreeRoot.parent)
4027 let path = b:NERDTreeRoot.path.getParent()
4028 let newRoot = s:TreeDirNode.New(path)
4030 call newRoot.transplantChild(b:NERDTreeRoot)
4031 let b:NERDTreeRoot = newRoot
4033 let b:NERDTreeRoot = b:NERDTreeRoot.parent
4036 if g:NERDTreeChDirMode ==# 2
4037 call b:NERDTreeRoot.path.changeToDir()
4041 call oldRoot.putCursorHere(0, 0)
4046 "reset &cpo back to users setting
4047 let &cpo = s:old_cpo
4049 " vim: set sw=4 sts=4 et fdm=marker: