]> git.r.bdr.sh - rbdr/dotfiles/blame_incremental - vim/plugin/NERD_tree.vim
Add DS_Store to gitignore
[rbdr/dotfiles] / vim / plugin / NERD_tree.vim
... / ...
CommitLineData
1" ============================================================================
2" File: NERD_tree.vim
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.
11"
12" ============================================================================
13let s:NERD_tree_version = '4.1.0'
14
15" SECTION: Script init stuff {{{1
16"============================================================
17if exists("loaded_nerd_tree")
18 finish
19endif
20if v:version < 700
21 echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!"
22 finish
23endif
24let loaded_nerd_tree = 1
25
26"for line continuation - i.e dont want C in &cpo
27let s:old_cpo = &cpo
28set cpo&vim
29
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
33"
34"Args:
35"var: the name of the var to be initialised
36"value: the value to initialise var to
37"
38"Returns:
39"1 if the var is set, 0 otherwise
40function! s:initVariable(var, value)
41 if !exists(a:var)
42 exec 'let ' . a:var . ' = ' . "'" . substitute(a:value, "'", "''", "g") . "'"
43 return 1
44 endif
45 return 0
46endfunction
47
48"SECTION: Init variable calls and other random constants {{{2
49call s:initVariable("g:NERDChristmasTree", 1)
50call s:initVariable("g:NERDTreeAutoCenter", 1)
51call s:initVariable("g:NERDTreeAutoCenterThreshold", 3)
52call s:initVariable("g:NERDTreeCaseSensitiveSort", 0)
53call s:initVariable("g:NERDTreeChDirMode", 0)
54if !exists("g:NERDTreeIgnore")
55 let g:NERDTreeIgnore = ['\~$']
56endif
57call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks')
58call s:initVariable("g:NERDTreeHighlightCursorline", 1)
59call s:initVariable("g:NERDTreeHijackNetrw", 1)
60call s:initVariable("g:NERDTreeMouseMode", 1)
61call s:initVariable("g:NERDTreeNotificationThreshold", 100)
62call s:initVariable("g:NERDTreeQuitOnOpen", 0)
63call s:initVariable("g:NERDTreeShowBookmarks", 0)
64call s:initVariable("g:NERDTreeShowFiles", 1)
65call s:initVariable("g:NERDTreeShowHidden", 0)
66call s:initVariable("g:NERDTreeShowLineNumbers", 0)
67call s:initVariable("g:NERDTreeSortDirs", 1)
68
69if !exists("g:NERDTreeSortOrder")
70 let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$']
71else
72 "if there isnt a * in the sort sequence then add one
73 if count(g:NERDTreeSortOrder, '*') < 1
74 call add(g:NERDTreeSortOrder, '*')
75 endif
76endif
77
78"we need to use this number many times for sorting... so we calculate it only
79"once here
80let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*')
81
82if !exists('g:NERDTreeStatusline')
83
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():''}"
88
89endif
90call s:initVariable("g:NERDTreeWinPos", "left")
91call s:initVariable("g:NERDTreeWinSize", 31)
92
93let s:running_windows = has("win16") || has("win32") || has("win64")
94
95"init the shell commands that will be used to copy nodes, and remove dir trees
96"
97"Note: the space after the command is important
98if s:running_windows
99 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ')
100else
101 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ')
102 call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ')
103endif
104
105
106"SECTION: Init variable calls for key mappings {{{2
107call s:initVariable("g:NERDTreeMapActivateNode", "o")
108call s:initVariable("g:NERDTreeMapChangeRoot", "C")
109call s:initVariable("g:NERDTreeMapChdir", "cd")
110call s:initVariable("g:NERDTreeMapCloseChildren", "X")
111call s:initVariable("g:NERDTreeMapCloseDir", "x")
112call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
113call s:initVariable("g:NERDTreeMapMenu", "m")
114call s:initVariable("g:NERDTreeMapHelp", "?")
115call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
116call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
117call s:initVariable("g:NERDTreeMapJumpNextSibling", "<C-j>")
118call s:initVariable("g:NERDTreeMapJumpParent", "p")
119call s:initVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>")
120call s:initVariable("g:NERDTreeMapJumpRoot", "P")
121call s:initVariable("g:NERDTreeMapOpenExpl", "e")
122call s:initVariable("g:NERDTreeMapOpenInTab", "t")
123call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T")
124call s:initVariable("g:NERDTreeMapOpenRecursively", "O")
125call s:initVariable("g:NERDTreeMapOpenSplit", "i")
126call s:initVariable("g:NERDTreeMapOpenVSplit", "s")
127call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode)
128call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit)
129call s:initVariable("g:NERDTreeMapPreviewVSplit", "g" . NERDTreeMapOpenVSplit)
130call s:initVariable("g:NERDTreeMapQuit", "q")
131call s:initVariable("g:NERDTreeMapRefresh", "r")
132call s:initVariable("g:NERDTreeMapRefreshRoot", "R")
133call s:initVariable("g:NERDTreeMapToggleBookmarks", "B")
134call s:initVariable("g:NERDTreeMapToggleFiles", "F")
135call s:initVariable("g:NERDTreeMapToggleFilters", "f")
136call s:initVariable("g:NERDTreeMapToggleHidden", "I")
137call s:initVariable("g:NERDTreeMapToggleZoom", "A")
138call s:initVariable("g:NERDTreeMapUpdir", "u")
139call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U")
140
141"SECTION: Script level variable declaration{{{2
142if s:running_windows
143 let s:escape_chars = " `\|\"#%&,?()\*^<>"
144else
145 let s:escape_chars = " \\`\|\"#%&,?()\*^<>"
146endif
147let s:NERDTreeBufName = 'NERD_tree_'
148
149let s:tree_wid = 2
150let s:tree_markup_reg = '^[ `|▼▶]*[\-+~ ]*'
151let s:tree_up_dir_line = '.. (up a dir)'
152
153"the number to add to the nerd tree buffer name to make the buf name unique
154let s:next_buffer_number = 1
155
156" SECTION: Commands {{{1
157"============================================================
158"init the command that users start the nerd tree with
159command! -n=? -complete=dir -bar NERDTree :call s:initNerdTree('<args>')
160command! -n=? -complete=dir -bar NERDTreeToggle :call s:toggle('<args>')
161command! -n=0 -bar NERDTreeClose :call s:closeTreeIfOpen()
162command! -n=1 -complete=customlist,s:completeBookmarks -bar NERDTreeFromBookmark call s:initNerdTree('<args>')
163command! -n=0 -bar NERDTreeMirror call s:initNerdTreeMirror()
164command! -n=0 -bar NERDTreeFind call s:findAndRevealPath()
165" SECTION: Auto commands {{{1
166"============================================================
167augroup NERDTree
168 "Save the cursor position whenever we close the nerd tree
169 exec "autocmd BufWinLeave ". s:NERDTreeBufName ."* call <SID>saveScreenState()"
170
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)
175
176 "load all nerdtree plugins after vim starts
177 autocmd VimEnter * runtime! nerdtree_plugin/**/*.vim
178augroup END
179
180if g:NERDTreeHijackNetrw
181 augroup NERDTreeHijackNetrw
182 autocmd VimEnter * silent! autocmd! FileExplorer
183 au BufEnter,VimEnter * call s:checkForBrowse(expand("<amatch>"))
184 augroup END
185endif
186
187"SECTION: Classes {{{1
188"============================================================
189"CLASS: Bookmark {{{2
190"============================================================
191let s:Bookmark = {}
192" FUNCTION: Bookmark.activate() {{{3
193function! s:Bookmark.activate()
194 if self.path.isDirectory
195 call self.toRoot()
196 else
197 if self.validate()
198 let n = s:TreeFileNode.New(self.path)
199 call n.open()
200 call s:closeTreeIfQuitOnOpen()
201 endif
202 endif
203endfunction
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
207function! s:Bookmark.AddBookmark(name, path)
208 for i in s:Bookmark.Bookmarks()
209 if i.name ==# a:name
210 let i.path = a:path
211 return
212 endif
213 endfor
214 call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
215 call s:Bookmark.Sort()
216endfunction
217" Function: Bookmark.Bookmarks() {{{3
218" Class method to get all bookmarks. Lazily initializes the bookmarks global
219" variable
220function! s:Bookmark.Bookmarks()
221 if !exists("g:NERDTreeBookmarks")
222 let g:NERDTreeBookmarks = []
223 endif
224 return g:NERDTreeBookmarks
225endfunction
226" Function: Bookmark.BookmarkExistsFor(name) {{{3
227" class method that returns 1 if a bookmark with the given name is found, 0
228" otherwise
229function! s:Bookmark.BookmarkExistsFor(name)
230 try
231 call s:Bookmark.BookmarkFor(a:name)
232 return 1
233 catch /^NERDTree.BookmarkNotFoundError/
234 return 0
235 endtry
236endfunction
237" Function: Bookmark.BookmarkFor(name) {{{3
238" Class method to get the bookmark that has the given name. {} is return if no
239" bookmark is found
240function! s:Bookmark.BookmarkFor(name)
241 for i in s:Bookmark.Bookmarks()
242 if i.name ==# a:name
243 return i
244 endif
245 endfor
246 throw "NERDTree.BookmarkNotFoundError: no bookmark found for name: \"". a:name .'"'
247endfunction
248" Function: Bookmark.BookmarkNames() {{{3
249" Class method to return an array of all bookmark names
250function! s:Bookmark.BookmarkNames()
251 let names = []
252 for i in s:Bookmark.Bookmarks()
253 call add(names, i.name)
254 endfor
255 return names
256endfunction
257" FUNCTION: Bookmark.CacheBookmarks(silent) {{{3
258" Class method to read all bookmarks from the bookmarks file intialize
259" bookmark objects for each one.
260"
261" Args:
262" silent - dont echo an error msg if invalid bookmarks are found
263function! 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
270
271 "ignore blank lines
272 if i != ''
273
274 let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
275 let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
276
277 try
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
283 endtry
284 endif
285 endfor
286 if invalidBookmarksFound
287 call s:Bookmark.Write()
288 if !a:silent
289 call s:echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
290 endif
291 endif
292 call s:Bookmark.Sort()
293 endif
294endfunction
295" FUNCTION: Bookmark.compareTo(otherbookmark) {{{3
296" Compare these two bookmarks for sorting purposes
297function! s:Bookmark.compareTo(otherbookmark)
298 return a:otherbookmark.name < self.name
299endfunction
300" FUNCTION: Bookmark.ClearAll() {{{3
301" Class method to delete all bookmarks.
302function! s:Bookmark.ClearAll()
303 for i in s:Bookmark.Bookmarks()
304 call i.delete()
305 endfor
306 call s:Bookmark.Write()
307endfunction
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
311function! s:Bookmark.delete()
312 let node = {}
313 try
314 let node = self.getNode(1)
315 catch /^NERDTree.BookmarkedNodeNotFoundError/
316 endtry
317 call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
318 if !empty(node)
319 call node.path.cacheDisplayString()
320 endif
321 call s:Bookmark.Write()
322endfunction
323" FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{3
324" Gets the treenode for this bookmark
325"
326" Args:
327" searchFromAbsoluteRoot: specifies whether we should search from the current
328" tree root, or the highest cached node
329function! s:Bookmark.getNode(searchFromAbsoluteRoot)
330 let searchRoot = a:searchFromAbsoluteRoot ? s:TreeDirNode.AbsoluteTreeRoot() : b:NERDTreeRoot
331 let targetNode = searchRoot.findNode(self.path)
332 if empty(targetNode)
333 throw "NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: " . self.name
334 endif
335 return targetNode
336endfunction
337" FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{3
338" Class method that finds the bookmark with the given name and returns the
339" treenode for it.
340function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
341 let bookmark = s:Bookmark.BookmarkFor(a:name)
342 return bookmark.getNode(a:searchFromAbsoluteRoot)
343endfunction
344" FUNCTION: Bookmark.GetSelected() {{{3
345" returns the Bookmark the cursor is over, or {}
346function! s:Bookmark.GetSelected()
347 let line = getline(".")
348 let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
349 if name != line
350 try
351 return s:Bookmark.BookmarkFor(name)
352 catch /^NERDTree.BookmarkNotFoundError/
353 return {}
354 endtry
355 endif
356 return {}
357endfunction
358
359" Function: Bookmark.InvalidBookmarks() {{{3
360" Class method to get all invalid bookmark strings read from the bookmarks
361" file
362function! s:Bookmark.InvalidBookmarks()
363 if !exists("g:NERDTreeInvalidBookmarks")
364 let g:NERDTreeInvalidBookmarks = []
365 endif
366 return g:NERDTreeInvalidBookmarks
367endfunction
368" FUNCTION: Bookmark.mustExist() {{{3
369function! 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()
374 endif
375endfunction
376" FUNCTION: Bookmark.New(name, path) {{{3
377" Create a new bookmark object with the given name and path object
378function! s:Bookmark.New(name, path)
379 if a:name =~ ' '
380 throw "NERDTree.IllegalBookmarkNameError: illegal name:" . a:name
381 endif
382
383 let newBookmark = copy(self)
384 let newBookmark.name = a:name
385 let newBookmark.path = a:path
386 return newBookmark
387endfunction
388" FUNCTION: Bookmark.openInNewTab(options) {{{3
389" Create a new bookmark object with the given name and path object
390function! s:Bookmark.openInNewTab(options)
391 let currentTab = tabpagenr()
392 if self.path.isDirectory
393 tabnew
394 call s:initNerdTree(self.name)
395 else
396 exec "tabedit " . bookmark.path.str({'format': 'Edit'})
397 endif
398
399 if has_key(a:options, 'stayInCurrentTab')
400 exec "tabnext " . currentTab
401 endif
402endfunction
403" Function: Bookmark.setPath(path) {{{3
404" makes this bookmark point to the given path
405function! s:Bookmark.setPath(path)
406 let self.path = a:path
407endfunction
408" Function: Bookmark.Sort() {{{3
409" Class method that sorts all bookmarks
410function! s:Bookmark.Sort()
411 let CompareFunc = function("s:compareBookmarks")
412 call sort(s:Bookmark.Bookmarks(), CompareFunc)
413endfunction
414" Function: Bookmark.str() {{{3
415" Get the string that should be rendered in the view for this bookmark
416function! s:Bookmark.str()
417 let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name)
418 if &nu
419 let pathStrMaxLen = pathStrMaxLen - &numberwidth
420 endif
421
422 let pathStr = self.path.str({'format': 'UI'})
423 if len(pathStr) > pathStrMaxLen
424 let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
425 endif
426 return '>' . self.name . ' ' . pathStr
427endfunction
428" FUNCTION: Bookmark.toRoot() {{{3
429" Make the node for this bookmark the new tree root
430function! s:Bookmark.toRoot()
431 if self.validate()
432 try
433 let targetNode = self.getNode(1)
434 catch /^NERDTree.BookmarkedNodeNotFoundError/
435 let targetNode = s:TreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
436 endtry
437 call targetNode.makeRoot()
438 call s:renderView()
439 call targetNode.putCursorHere(0, 0)
440 endif
441endfunction
442" FUNCTION: Bookmark.ToRoot(name) {{{3
443" Make the node for this bookmark the new tree root
444function! s:Bookmark.ToRoot(name)
445 let bookmark = s:Bookmark.BookmarkFor(a:name)
446 call bookmark.toRoot()
447endfunction
448
449
450"FUNCTION: Bookmark.validate() {{{3
451function! s:Bookmark.validate()
452 if self.path.exists()
453 return 1
454 else
455 call s:Bookmark.CacheBookmarks(1)
456 call s:renderView()
457 call s:echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
458 return 0
459 endif
460endfunction
461
462" Function: Bookmark.Write() {{{3
463" Class method to write all bookmarks to the bookmarks file
464function! s:Bookmark.Write()
465 let bookmarkStrings = []
466 for i in s:Bookmark.Bookmarks()
467 call add(bookmarkStrings, i.name . ' ' . i.path.str())
468 endfor
469
470 "add a blank line before the invalid ones
471 call add(bookmarkStrings, "")
472
473 for j in s:Bookmark.InvalidBookmarks()
474 call add(bookmarkStrings, j)
475 endfor
476 call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
477endfunction
478"CLASS: KeyMap {{{2
479"============================================================
480let s:KeyMap = {}
481"FUNCTION: KeyMap.All() {{{3
482function! s:KeyMap.All()
483 if !exists("s:keyMaps")
484 let s:keyMaps = []
485 endif
486 return s:keyMaps
487endfunction
488
489"FUNCTION: KeyMap.BindAll() {{{3
490function! s:KeyMap.BindAll()
491 for i in s:KeyMap.All()
492 call i.bind()
493 endfor
494endfunction
495
496"FUNCTION: KeyMap.bind() {{{3
497function! s:KeyMap.bind()
498 exec "nnoremap <silent> <buffer> ". self.key ." :call ". self.callback ."()<cr>"
499endfunction
500
501"FUNCTION: KeyMap.Create(options) {{{3
502function! 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)
508endfunction
509"CLASS: MenuController {{{2
510"============================================================
511let s:MenuController = {}
512"FUNCTION: MenuController.New(menuItems) {{{3
513"create a new menu controller that operates on the given menu items
514function! s:MenuController.New(menuItems)
515 let newMenuController = copy(self)
516 if a:menuItems[0].isSeparator()
517 let newMenuController.menuItems = a:menuItems[1:-1]
518 else
519 let newMenuController.menuItems = a:menuItems
520 endif
521 return newMenuController
522endfunction
523
524"FUNCTION: MenuController.showMenu() {{{3
525"start the main loop of the menu and get the user to choose/execute a menu
526"item
527function! s:MenuController.showMenu()
528 call self._saveOptions()
529
530 try
531 let self.selection = 0
532
533 let done = 0
534 while !done
535 redraw!
536 call self._echoPrompt()
537 let key = nr2char(getchar())
538 let done = self._handleKeypress(key)
539 endwhile
540 finally
541 call self._restoreOptions()
542 endtry
543
544 if self.selection != -1
545 let m = self._current()
546 call m.execute()
547 endif
548endfunction
549
550"FUNCTION: MenuController._echoPrompt() {{{3
551function! s:MenuController._echoPrompt()
552 echo "NERDTree Menu. Use j/k/enter and the shortcuts indicated"
553 echo "=========================================================="
554
555 for i in range(0, len(self.menuItems)-1)
556 if self.selection == i
557 echo "> " . self.menuItems[i].text
558 else
559 echo " " . self.menuItems[i].text
560 endif
561 endfor
562endfunction
563
564"FUNCTION: MenuController._current(key) {{{3
565"get the MenuItem that is curently selected
566function! s:MenuController._current()
567 return self.menuItems[self.selection]
568endfunction
569
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
573function! s:MenuController._handleKeypress(key)
574 if a:key == 'j'
575 call self._cursorDown()
576 elseif a:key == 'k'
577 call self._cursorUp()
578 elseif a:key == nr2char(27) "escape
579 let self.selection = -1
580 return 1
581 elseif a:key == "\r" || a:key == "\n" "enter and ctrl-j
582 return 1
583 else
584 let index = self._nextIndexFor(a:key)
585 if index != -1
586 let self.selection = index
587 if len(self._allIndexesFor(a:key)) == 1
588 return 1
589 endif
590 endif
591 endif
592
593 return 0
594endfunction
595
596"FUNCTION: MenuController._allIndexesFor(shortcut) {{{3
597"get indexes to all menu items with the given shortcut
598function! s:MenuController._allIndexesFor(shortcut)
599 let toReturn = []
600
601 for i in range(0, len(self.menuItems)-1)
602 if self.menuItems[i].shortcut == a:shortcut
603 call add(toReturn, i)
604 endif
605 endfor
606
607 return toReturn
608endfunction
609
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
613function! s:MenuController._nextIndexFor(shortcut)
614 for i in range(self.selection+1, len(self.menuItems)-1)
615 if self.menuItems[i].shortcut == a:shortcut
616 return i
617 endif
618 endfor
619
620 for i in range(0, self.selection)
621 if self.menuItems[i].shortcut == a:shortcut
622 return i
623 endif
624 endfor
625
626 return -1
627endfunction
628
629"FUNCTION: MenuController._setCmdheight() {{{3
630"sets &cmdheight to whatever is needed to display the menu
631function! s:MenuController._setCmdheight()
632 let &cmdheight = len(self.menuItems) + 3
633endfunction
634
635"FUNCTION: MenuController._saveOptions() {{{3
636"set any vim options that are required to make the menu work (saving their old
637"values)
638function! s:MenuController._saveOptions()
639 let self._oldLazyredraw = &lazyredraw
640 let self._oldCmdheight = &cmdheight
641 set nolazyredraw
642 call self._setCmdheight()
643endfunction
644
645"FUNCTION: MenuController._restoreOptions() {{{3
646"restore the options we saved in _saveOptions()
647function! s:MenuController._restoreOptions()
648 let &cmdheight = self._oldCmdheight
649 let &lazyredraw = self._oldLazyredraw
650endfunction
651
652"FUNCTION: MenuController._cursorDown() {{{3
653"move the cursor to the next menu item, skipping separators
654function! s:MenuController._cursorDown()
655 let done = 0
656 while !done
657 if self.selection < len(self.menuItems)-1
658 let self.selection += 1
659 else
660 let self.selection = 0
661 endif
662
663 if !self._current().isSeparator()
664 let done = 1
665 endif
666 endwhile
667endfunction
668
669"FUNCTION: MenuController._cursorUp() {{{3
670"move the cursor to the previous menu item, skipping separators
671function! s:MenuController._cursorUp()
672 let done = 0
673 while !done
674 if self.selection > 0
675 let self.selection -= 1
676 else
677 let self.selection = len(self.menuItems)-1
678 endif
679
680 if !self._current().isSeparator()
681 let done = 1
682 endif
683 endwhile
684endfunction
685
686"CLASS: MenuItem {{{2
687"============================================================
688let s:MenuItem = {}
689"FUNCTION: MenuItem.All() {{{3
690"get all top level menu items
691function! s:MenuItem.All()
692 if !exists("s:menuItems")
693 let s:menuItems = []
694 endif
695 return s:menuItems
696endfunction
697
698"FUNCTION: MenuItem.AllEnabled() {{{3
699"get all top level menu items that are currently enabled
700function! s:MenuItem.AllEnabled()
701 let toReturn = []
702 for i in s:MenuItem.All()
703 if i.enabled()
704 call add(toReturn, i)
705 endif
706 endfor
707 return toReturn
708endfunction
709
710"FUNCTION: MenuItem.Create(options) {{{3
711"make a new menu item and add it to the global list
712function! s:MenuItem.Create(options)
713 let newMenuItem = copy(self)
714
715 let newMenuItem.text = a:options['text']
716 let newMenuItem.shortcut = a:options['shortcut']
717 let newMenuItem.children = []
718
719 let newMenuItem.isActiveCallback = -1
720 if has_key(a:options, 'isActiveCallback')
721 let newMenuItem.isActiveCallback = a:options['isActiveCallback']
722 endif
723
724 let newMenuItem.callback = -1
725 if has_key(a:options, 'callback')
726 let newMenuItem.callback = a:options['callback']
727 endif
728
729 if has_key(a:options, 'parent')
730 call add(a:options['parent'].children, newMenuItem)
731 else
732 call add(s:MenuItem.All(), newMenuItem)
733 endif
734
735 return newMenuItem
736endfunction
737
738"FUNCTION: MenuItem.CreateSeparator(options) {{{3
739"make a new separator menu item and add it to the global list
740function! s:MenuItem.CreateSeparator(options)
741 let standard_options = { 'text': '--------------------',
742 \ 'shortcut': -1,
743 \ 'callback': -1 }
744 let options = extend(a:options, standard_options, "force")
745
746 return s:MenuItem.Create(options)
747endfunction
748
749"FUNCTION: MenuItem.CreateSubmenu(options) {{{3
750"make a new submenu and add it to global list
751function! s:MenuItem.CreateSubmenu(options)
752 let standard_options = { 'callback': -1 }
753 let options = extend(a:options, standard_options, "force")
754
755 return s:MenuItem.Create(options)
756endfunction
757
758"FUNCTION: MenuItem.enabled() {{{3
759"return 1 if this menu item should be displayed
760"
761"delegates off to the isActiveCallback, and defaults to 1 if no callback was
762"specified
763function! s:MenuItem.enabled()
764 if self.isActiveCallback != -1
765 return {self.isActiveCallback}()
766 endif
767 return 1
768endfunction
769
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
773"callback
774function! s:MenuItem.execute()
775 if len(self.children)
776 let mc = s:MenuController.New(self.children)
777 call mc.showMenu()
778 else
779 if self.callback != -1
780 call {self.callback}()
781 endif
782 endif
783endfunction
784
785"FUNCTION: MenuItem.isSeparator() {{{3
786"return 1 if this menuitem is a separator
787function! s:MenuItem.isSeparator()
788 return self.callback == -1 && self.children == []
789endfunction
790
791"FUNCTION: MenuItem.isSubmenu() {{{3
792"return 1 if this menuitem is a submenu
793function! s:MenuItem.isSubmenu()
794 return self.callback == -1 && !empty(self.children)
795endfunction
796
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
800"classes.
801"============================================================
802let s:TreeFileNode = {}
803"FUNCTION: TreeFileNode.activate(forceKeepWinOpen) {{{3
804function! s:TreeFileNode.activate(forceKeepWinOpen)
805 call self.open()
806 if !a:forceKeepWinOpen
807 call s:closeTreeIfQuitOnOpen()
808 end
809endfunction
810"FUNCTION: TreeFileNode.bookmark(name) {{{3
811"bookmark this node with a:name
812function! s:TreeFileNode.bookmark(name)
813
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 = {}
817 try
818 let oldMarkedNode = s:Bookmark.GetNodeForName(a:name, 1)
819 catch /^NERDTree.BookmarkNotFoundError/
820 catch /^NERDTree.BookmarkedNodeNotFoundError/
821 endtry
822
823 call s:Bookmark.AddBookmark(a:name, self.path)
824 call self.path.cacheDisplayString()
825 call s:Bookmark.Write()
826
827 if !empty(oldMarkedNode)
828 call oldMarkedNode.path.cacheDisplayString()
829 endif
830endfunction
831"FUNCTION: TreeFileNode.cacheParent() {{{3
832"initializes self.parent if it isnt already
833function! 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"
838 endif
839 let self.parent = s:TreeFileNode.New(parentPath)
840 endif
841endfunction
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..
845"
846"A class level method that compares two nodes
847"
848"Args:
849"n1, n2: the 2 nodes to compare
850function! s:compareNodes(n1, n2)
851 return a:n1.path.compareTo(a:n2.path)
852endfunction
853
854"FUNCTION: TreeFileNode.clearBoomarks() {{{3
855function! s:TreeFileNode.clearBoomarks()
856 for i in s:Bookmark.Bookmarks()
857 if i.path.equals(self.path)
858 call i.delete()
859 end
860 endfor
861 call self.path.cacheDisplayString()
862endfunction
863"FUNCTION: TreeFileNode.copy(dest) {{{3
864function! 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())
868 if !empty(parent)
869 call parent.refresh()
870 endif
871 return parent.findNode(newPath)
872endfunction
873
874"FUNCTION: TreeFileNode.delete {{{3
875"Removes this node from the tree and calls the Delete method for its path obj
876function! s:TreeFileNode.delete()
877 call self.path.delete()
878 call self.parent.removeChild(self)
879endfunction
880
881"FUNCTION: TreeFileNode.displayString() {{{3
882"
883"Returns a string that specifies how the node should be represented as a
884"string
885"
886"Return:
887"a string that can be used in the view to represent this node
888function! s:TreeFileNode.displayString()
889 return self.path.displayString()
890endfunction
891
892"FUNCTION: TreeFileNode.equals(treenode) {{{3
893"
894"Compares this treenode to the input treenode and returns 1 if they are the
895"same node.
896"
897"Use this method instead of == because sometimes when the treenodes contain
898"many children, vim seg faults when doing ==
899"
900"Args:
901"treenode: the other treenode to compare to
902function! s:TreeFileNode.equals(treenode)
903 return self.path.str() ==# a:treenode.path.str()
904endfunction
905
906"FUNCTION: TreeFileNode.findNode(path) {{{3
907"Returns self if this node.path.Equals the given path.
908"Returns {} if not equal.
909"
910"Args:
911"path: the path object to compare against
912function! s:TreeFileNode.findNode(path)
913 if a:path.equals(self.path)
914 return self
915 endif
916 return {}
917endfunction
918"FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{3
919"
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.
922"
923"Args:
924"direction: 0 if you want to find the previous sibling, 1 for the next sibling
925"
926"Return:
927"a treenode object or {} if no appropriate sibling could be found
928function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
929 "if we have no parent then we can have no siblings
930 if self.parent != {}
931 let nextSibling = self.findSibling(a:direction)
932
933 while nextSibling != {}
934 if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen
935 return nextSibling
936 endif
937 let nextSibling = nextSibling.findSibling(a:direction)
938 endwhile
939 endif
940
941 return {}
942endfunction
943"FUNCTION: TreeFileNode.findSibling(direction) {{{3
944"
945"Finds the next sibling for this node in the indicated direction
946"
947"Args:
948"direction: 0 if you want to find the previous sibling, 1 for the next sibling
949"
950"Return:
951"a treenode object or {} if no sibling could be found
952function! s:TreeFileNode.findSibling(direction)
953 "if we have no parent then we can have no siblings
954 if self.parent != {}
955
956 "get the index of this node in its parents children
957 let siblingIndx = self.parent.getChildIndex(self.path)
958
959 if siblingIndx != -1
960 "move a long to the next potential sibling node
961 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
962
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
966
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]
971 endif
972
973 "go to next node
974 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
975 endwhile
976 endif
977 endif
978
979 return {}
980endfunction
981
982"FUNCTION: TreeFileNode.getLineNum(){{{3
983"returns the line number this node is rendered on, or -1 if it isnt rendered
984function! s:TreeFileNode.getLineNum()
985 "if the node is the root then return the root line no.
986 if self.isRoot()
987 return s:TreeFileNode.GetRootLineNum()
988 endif
989
990 let totalLines = line("$")
991
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
996
997 let fullpath = self.path.str({'format': 'UI'})
998
999
1000 let lnum = s:TreeFileNode.GetRootLineNum()
1001 while lnum > 0
1002 let lnum = lnum + 1
1003 "have we reached the bottom of the tree?
1004 if lnum ==# totalLines+1
1005 return -1
1006 endif
1007
1008 let curLine = getline(lnum)
1009
1010 let indent = s:indentLevelFor(curLine)
1011 if indent ==# curPathComponent
1012 let curLine = s:stripMarkupFromLine(curLine, 1)
1013
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
1020
1021 if fullpath ==# curPath
1022 return lnum
1023 endif
1024 endif
1025 endif
1026 endif
1027 endwhile
1028 return -1
1029endfunction
1030
1031"FUNCTION: TreeFileNode.GetRootForTab(){{{3
1032"get the root node for this tab
1033function! s:TreeFileNode.GetRootForTab()
1034 if s:treeExistsForTab()
1035 return getbufvar(t:NERDTreeBufName, 'NERDTreeRoot')
1036 end
1037 return {}
1038endfunction
1039"FUNCTION: TreeFileNode.GetRootLineNum(){{{3
1040"gets the line number of the root node
1041function! s:TreeFileNode.GetRootLineNum()
1042 let rootLine = 1
1043 while getline(rootLine) !~ '^\(/\|<\)'
1044 let rootLine = rootLine + 1
1045 endwhile
1046 return rootLine
1047endfunction
1048
1049"FUNCTION: TreeFileNode.GetSelected() {{{3
1050"gets the treenode that the cursor is currently over
1051function! s:TreeFileNode.GetSelected()
1052 try
1053 let path = s:getPath(line("."))
1054 if path ==# {}
1055 return {}
1056 endif
1057 return b:NERDTreeRoot.findNode(path)
1058 catch /NERDTree/
1059 return {}
1060 endtry
1061endfunction
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)
1065function! s:TreeFileNode.isVisible()
1066 return !self.path.ignore()
1067endfunction
1068"FUNCTION: TreeFileNode.isRoot() {{{3
1069"returns 1 if this node is b:NERDTreeRoot
1070function! s:TreeFileNode.isRoot()
1071 if !s:treeExistsForBuf()
1072 throw "NERDTree.NoTreeError: No tree exists for the current buffer"
1073 endif
1074
1075 return self.equals(b:NERDTreeRoot)
1076endfunction
1077
1078"FUNCTION: TreeFileNode.makeRoot() {{{3
1079"Make this node the root of the tree
1080function! s:TreeFileNode.makeRoot()
1081 if self.path.isDirectory
1082 let b:NERDTreeRoot = self
1083 else
1084 call self.cacheParent()
1085 let b:NERDTreeRoot = self.parent
1086 endif
1087
1088 call b:NERDTreeRoot.open()
1089
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'})
1093 endif
1094endfunction
1095"FUNCTION: TreeFileNode.New(path) {{{3
1096"Returns a new TreeNode object with the given path and parent
1097"
1098"Args:
1099"path: a path object representing the full filesystem path to the file/dir that the node represents
1100function! s:TreeFileNode.New(path)
1101 if a:path.isDirectory
1102 return s:TreeDirNode.New(a:path)
1103 else
1104 let newTreeNode = copy(self)
1105 let newTreeNode.path = a:path
1106 let newTreeNode.parent = {}
1107 return newTreeNode
1108 endif
1109endfunction
1110
1111"FUNCTION: TreeFileNode.open() {{{3
1112"Open the file represented by the given node in the current window, splitting
1113"the window if needed
1114"
1115"ARGS:
1116"treenode: file node to open
1117function! s:TreeFileNode.open()
1118 if b:NERDTreeType ==# "secondary"
1119 exec 'edit ' . self.path.str({'format': 'Edit'})
1120 return
1121 endif
1122
1123 "if the file is already open in this tab then just stick the cursor in it
1124 let winnr = bufwinnr('^' . self.path.str() . '$')
1125 if winnr != -1
1126 call s:exec(winnr . "wincmd w")
1127
1128 else
1129 if !s:isWindowUsable(winnr("#")) && s:firstUsableWindow() ==# -1
1130 call self.openSplit()
1131 else
1132 try
1133 if !s:isWindowUsable(winnr("#"))
1134 call s:exec(s:firstUsableWindow() . "wincmd w")
1135 else
1136 call s:exec('wincmd p')
1137 endif
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\+)\)\=:/
1143 echo v:exception
1144 endtry
1145 endif
1146 endif
1147endfunction
1148"FUNCTION: TreeFileNode.openSplit() {{{3
1149"Open this node in a new window
1150function! s:TreeFileNode.openSplit()
1151
1152 if b:NERDTreeType ==# "secondary"
1153 exec "split " . self.path.str({'format': 'Edit'})
1154 return
1155 endif
1156
1157 " Save the user's settings for splitbelow and splitright
1158 let savesplitbelow=&splitbelow
1159 let savesplitright=&splitright
1160
1161 " 'there' will be set to a command to move from the split window
1162 " back to the explorer window
1163 "
1164 " 'back' will be set to a command to move from the explorer window
1165 " back to the newly split window
1166 "
1167 " 'right' and 'below' will be set to the settings needed for
1168 " splitbelow and splitright IF the explorer is the only window.
1169 "
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"
1173 let below=0
1174
1175 " Attempt to go to adjacent window
1176 call s:exec(back)
1177
1178 let onlyOneWin = (winnr("$") ==# 1)
1179
1180 " If no adjacent window, set splitright and splitbelow appropriately
1181 if onlyOneWin
1182 let &splitright=right
1183 let &splitbelow=below
1184 else
1185 " found adjacent window - invert split direction
1186 let &splitright=!right
1187 let &splitbelow=!below
1188 endif
1189
1190 let splitMode = onlyOneWin ? "vertical" : ""
1191
1192 " Open the new window
1193 try
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\+)\)\=:/
1199 "do nothing
1200 endtry
1201
1202 "resize the tree window if no other window was open before
1203 if onlyOneWin
1204 let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
1205 call s:exec(there)
1206 exec("silent ". splitMode ." resize ". size)
1207 call s:exec('wincmd p')
1208 endif
1209
1210 " Restore splitmode settings
1211 let &splitbelow=savesplitbelow
1212 let &splitright=savesplitright
1213endfunction
1214"FUNCTION: TreeFileNode.openVSplit() {{{3
1215"Open this node in a new vertical window
1216function! s:TreeFileNode.openVSplit()
1217 if b:NERDTreeType ==# "secondary"
1218 exec "vnew " . self.path.str({'format': 'Edit'})
1219 return
1220 endif
1221
1222 let winwidth = winwidth(".")
1223 if winnr("$")==#1
1224 let winwidth = g:NERDTreeWinSize
1225 endif
1226
1227 call s:exec("wincmd p")
1228 exec "vnew " . self.path.str({'format': 'Edit'})
1229
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')
1234endfunction
1235"FUNCTION: TreeFileNode.openInNewTab(options) {{{3
1236function! s:TreeFileNode.openInNewTab(options)
1237 let currentTab = tabpagenr()
1238
1239 if !has_key(a:options, 'keepTreeOpen')
1240 call s:closeTreeIfQuitOnOpen()
1241 endif
1242
1243 exec "tabedit " . self.path.str({'format': 'Edit'})
1244
1245 if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab']
1246 exec "tabnext " . currentTab
1247 endif
1248
1249endfunction
1250"FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3
1251"Places the cursor on the line number this node is rendered on
1252"
1253"Args:
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
1256"visible
1257function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
1258 let ln = self.getLineNum()
1259 if ln != -1
1260 if a:isJump
1261 mark '
1262 endif
1263 call cursor(ln, col("."))
1264 else
1265 if a:recurseUpward
1266 let node = self
1267 while node != {} && node.getLineNum() ==# -1
1268 let node = node.parent
1269 call node.open()
1270 endwhile
1271 call s:renderView()
1272 call node.putCursorHere(a:isJump, 0)
1273 endif
1274 endif
1275endfunction
1276
1277"FUNCTION: TreeFileNode.refresh() {{{3
1278function! s:TreeFileNode.refresh()
1279 call self.path.refresh()
1280endfunction
1281"FUNCTION: TreeFileNode.rename() {{{3
1282"Calls the rename method for this nodes path obj
1283function! s:TreeFileNode.rename(newName)
1284 let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
1285 call self.path.rename(newName)
1286 call self.parent.removeChild(self)
1287
1288 let parentPath = self.path.getParent()
1289 let newParent = b:NERDTreeRoot.findNode(parentPath)
1290
1291 if newParent != {}
1292 call newParent.createChild(self.path, 1)
1293 call newParent.refresh()
1294 endif
1295endfunction
1296"FUNCTION: TreeFileNode.renderToString {{{3
1297"returns a string representation for this tree to be rendered in the view
1298function! s:TreeFileNode.renderToString()
1299 return self._renderToString(0, 0, [], self.getChildCount() ==# 1)
1300endfunction
1301
1302
1303"Args:
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
1310function! s:TreeFileNode._renderToString(depth, drawText, vertMap, isLastChild)
1311 let output = ""
1312 if a:drawText ==# 1
1313
1314 let treeParts = ''
1315
1316 "get all the leading spaces and vertical tree parts for this line
1317 if a:depth > 1
1318 for j in a:vertMap[0:-2]
1319 let treeParts = treeParts . ' '
1320 endfor
1321 endif
1322
1323 if self.path.isDirectory
1324 if self.isOpen
1325 let treeParts = treeParts . '▼ '
1326 else
1327 let treeParts = treeParts . '▶ '
1328 endif
1329 else
1330 let treeParts = treeParts . ''
1331 endif
1332
1333 let line = treeParts . self.displayString()
1334
1335 let output = output . line . "\n"
1336 endif
1337
1338 "if the node is an open dir, draw its children
1339 if self.path.isDirectory ==# 1 && self.isOpen ==# 1
1340
1341 let childNodesToDraw = self.getVisibleChildren()
1342 if len(childNodesToDraw) > 0
1343
1344 "draw all the nodes children except the last
1345 let lastIndx = len(childNodesToDraw)-1
1346 if lastIndx > 0
1347 for i in childNodesToDraw[0:lastIndx-1]
1348 let output = output . i._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
1349 endfor
1350 endif
1351
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)
1354 endif
1355 endif
1356
1357 return output
1358endfunction
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
1362"classes.
1363"============================================================
1364let s:TreeDirNode = copy(s:TreeFileNode)
1365"FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
1366"class method that returns the highest cached ancestor of the current root
1367function! s:TreeDirNode.AbsoluteTreeRoot()
1368 let currentNode = b:NERDTreeRoot
1369 while currentNode.parent != {}
1370 let currentNode = currentNode.parent
1371 endwhile
1372 return currentNode
1373endfunction
1374"FUNCTION: TreeDirNode.activate(forceKeepWinOpen) {{{3
1375unlet s:TreeDirNode.activate
1376function! s:TreeDirNode.activate(forceKeepWinOpen)
1377 call self.toggleOpen()
1378 call s:renderView()
1379 call self.putCursorHere(0, 0)
1380endfunction
1381"FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{3
1382"Adds the given treenode to the list of children for this node
1383"
1384"Args:
1385"-treenode: the node to add
1386"-inOrder: 1 if the new node should be inserted in sorted order
1387function! s:TreeDirNode.addChild(treenode, inOrder)
1388 call add(self.children, a:treenode)
1389 let a:treenode.parent = self
1390
1391 if a:inOrder
1392 call self.sortChildren()
1393 endif
1394endfunction
1395
1396"FUNCTION: TreeDirNode.close() {{{3
1397"Closes this directory
1398function! s:TreeDirNode.close()
1399 let self.isOpen = 0
1400endfunction
1401
1402"FUNCTION: TreeDirNode.closeChildren() {{{3
1403"Closes all the child dir nodes of this node
1404function! s:TreeDirNode.closeChildren()
1405 for i in self.children
1406 if i.path.isDirectory
1407 call i.close()
1408 call i.closeChildren()
1409 endif
1410 endfor
1411endfunction
1412
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.
1416"
1417"Args:
1418"path: a Path object that this node will represent/contain
1419"inOrder: 1 if the new node should be inserted in sorted order
1420"
1421"Returns:
1422"the newly created node
1423function! s:TreeDirNode.createChild(path, inOrder)
1424 let newTreeNode = s:TreeFileNode.New(a:path)
1425 call self.addChild(newTreeNode, a:inOrder)
1426 return newTreeNode
1427endfunction
1428
1429"FUNCTION: TreeDirNode.findNode(path) {{{3
1430"Will find one of the children (recursively) that has the given path
1431"
1432"Args:
1433"path: a path object
1434unlet s:TreeDirNode.findNode
1435function! s:TreeDirNode.findNode(path)
1436 if a:path.equals(self.path)
1437 return self
1438 endif
1439 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1440 return {}
1441 endif
1442
1443 if self.path.isDirectory
1444 for i in self.children
1445 let retVal = i.findNode(a:path)
1446 if retVal != {}
1447 return retVal
1448 endif
1449 endfor
1450 endif
1451 return {}
1452endfunction
1453"FUNCTION: TreeDirNode.getChildCount() {{{3
1454"Returns the number of children this node has
1455function! s:TreeDirNode.getChildCount()
1456 return len(self.children)
1457endfunction
1458
1459"FUNCTION: TreeDirNode.getChild(path) {{{3
1460"Returns child node of this node that has the given path or {} if no such node
1461"exists.
1462"
1463"This function doesnt not recurse into child dir nodes
1464"
1465"Args:
1466"path: a path object
1467function! s:TreeDirNode.getChild(path)
1468 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1469 return {}
1470 endif
1471
1472 let index = self.getChildIndex(a:path)
1473 if index ==# -1
1474 return {}
1475 else
1476 return self.children[index]
1477 endif
1478
1479endfunction
1480
1481"FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{3
1482"returns the child at the given index
1483"Args:
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.
1487function! 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."
1491 endif
1492 return array_to_search[a:indx]
1493endfunction
1494
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.
1498"
1499"This function doesnt not recurse into child dir nodes
1500"
1501"Args:
1502"path: a path object
1503function! s:TreeDirNode.getChildIndex(path)
1504 if stridx(a:path.str(), self.path.str(), 0) ==# -1
1505 return -1
1506 endif
1507
1508 "do a binary search for the child
1509 let a = 0
1510 let z = self.getChildCount()
1511 while a < z
1512 let mid = (a+z)/2
1513 let diff = a:path.compareTo(self.children[mid].path)
1514
1515 if diff ==# -1
1516 let z = mid
1517 elseif diff ==# 1
1518 let a = mid+1
1519 else
1520 return mid
1521 endif
1522 endwhile
1523 return -1
1524endfunction
1525
1526"FUNCTION: TreeDirNode.GetSelected() {{{3
1527"Returns the current node if it is a dir node, or else returns the current
1528"nodes parent
1529unlet s:TreeDirNode.GetSelected
1530function! 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
1535 endif
1536 endif
1537 return currentDir
1538endfunction
1539"FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
1540"Returns the number of visible children this node has
1541function! s:TreeDirNode.getVisibleChildCount()
1542 return len(self.getVisibleChildren())
1543endfunction
1544
1545"FUNCTION: TreeDirNode.getVisibleChildren() {{{3
1546"Returns a list of children to display for this node, in the correct order
1547"
1548"Return:
1549"an array of treenodes
1550function! s:TreeDirNode.getVisibleChildren()
1551 let toReturn = []
1552 for i in self.children
1553 if i.path.ignore() ==# 0
1554 call add(toReturn, i)
1555 endif
1556 endfor
1557 return toReturn
1558endfunction
1559
1560"FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
1561"returns 1 if this node has any childre, 0 otherwise..
1562function! s:TreeDirNode.hasVisibleChildren()
1563 return self.getVisibleChildCount() != 0
1564endfunction
1565
1566"FUNCTION: TreeDirNode._initChildren() {{{3
1567"Removes all childen from this node and re-reads them
1568"
1569"Args:
1570"silent: 1 if the function should not echo any "please wait" messages for
1571"large directories
1572"
1573"Return: the number of child nodes read
1574function! s:TreeDirNode._initChildren(silent)
1575 "remove all the current child nodes
1576 let self.children = []
1577
1578 "get an array of all the files in the nodes dir
1579 let dir = self.path
1580 let globDir = dir.str({'format': 'Glob'})
1581 let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
1582 let files = split(filesStr, "\n")
1583
1584 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
1585 call s:echo("Please wait, caching a large dir ...")
1586 endif
1587
1588 let invalidFilesFound = 0
1589 for i in files
1590
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 !~ '\/\.\/\?$'
1595
1596 "put the next file in a new node and attach it
1597 try
1598 let path = s:Path.New(i)
1599 call self.createChild(path, 0)
1600 catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
1601 let invalidFilesFound += 1
1602 endtry
1603 endif
1604 endfor
1605
1606 call self.sortChildren()
1607
1608 if !a:silent && len(files) > g:NERDTreeNotificationThreshold
1609 call s:echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).")
1610 endif
1611
1612 if invalidFilesFound
1613 call s:echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree")
1614 endif
1615 return self.getChildCount()
1616endfunction
1617"FUNCTION: TreeDirNode.New(path) {{{3
1618"Returns a new TreeNode object with the given path and parent
1619"
1620"Args:
1621"path: a path object representing the full filesystem path to the file/dir that the node represents
1622unlet s:TreeDirNode.New
1623function! 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."
1626 endif
1627
1628 let newTreeNode = copy(self)
1629 let newTreeNode.path = a:path
1630
1631 let newTreeNode.isOpen = 0
1632 let newTreeNode.children = []
1633
1634 let newTreeNode.parent = {}
1635
1636 return newTreeNode
1637endfunction
1638"FUNCTION: TreeDirNode.open() {{{3
1639"Reads in all this nodes children
1640"
1641"Return: the number of child nodes read
1642unlet s:TreeDirNode.open
1643function! s:TreeDirNode.open()
1644 let self.isOpen = 1
1645 if self.children ==# []
1646 return self._initChildren(0)
1647 else
1648 return 0
1649 endif
1650endfunction
1651
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)
1655function! 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()
1661 else
1662 exec ("silent edit " . self.path.str({'format': 'Edit'}))
1663 endif
1664endfunction
1665"FUNCTION: TreeDirNode.openInNewTab(options) {{{3
1666unlet s:TreeDirNode.openInNewTab
1667function! s:TreeDirNode.openInNewTab(options)
1668 let currentTab = tabpagenr()
1669
1670 if !has_key(a:options, 'keepTreeOpen') || !a:options['keepTreeOpen']
1671 call s:closeTreeIfQuitOnOpen()
1672 endif
1673
1674 tabnew
1675 call s:initNerdTree(self.path.str())
1676
1677 if has_key(a:options, 'stayInCurrentTab') && a:options['stayInCurrentTab']
1678 exec "tabnext " . currentTab
1679 endif
1680endfunction
1681"FUNCTION: TreeDirNode.openRecursively() {{{3
1682"Opens this treenode and all of its children whose paths arent 'ignored'
1683"because of the file filters.
1684"
1685"This method is actually a wrapper for the OpenRecursively2 method which does
1686"the work.
1687function! s:TreeDirNode.openRecursively()
1688 call self._openRecursively2(1)
1689endfunction
1690
1691"FUNCTION: TreeDirNode._openRecursively2() {{{3
1692"Opens this all children of this treenode recursively if either:
1693" *they arent filtered by file filters
1694" *a:forceOpen is 1
1695"
1696"Args:
1697"forceOpen: 1 if this node should be opened regardless of file filters
1698function! s:TreeDirNode._openRecursively2(forceOpen)
1699 if self.path.ignore() ==# 0 || a:forceOpen
1700 let self.isOpen = 1
1701 if self.children ==# []
1702 call self._initChildren(1)
1703 endif
1704
1705 for i in self.children
1706 if i.path.isDirectory ==# 1
1707 call i._openRecursively2(0)
1708 endif
1709 endfor
1710 endif
1711endfunction
1712
1713"FUNCTION: TreeDirNode.refresh() {{{3
1714unlet s:TreeDirNode.refresh
1715function! s:TreeDirNode.refresh()
1716 call self.path.refresh()
1717
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
1723 let dir = self.path
1724 let globDir = dir.str({'format': 'Glob'})
1725 let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
1726 let files = split(filesStr, "\n")
1727 for i in files
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 !~ '\/\.\/\?$'
1732
1733 try
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)
1737 if newNode != {}
1738 call newNode.refresh()
1739 call add(newChildNodes, newNode)
1740
1741 "the node doesnt exist so create it
1742 else
1743 let newNode = s:TreeFileNode.New(path)
1744 let newNode.parent = self
1745 call add(newChildNodes, newNode)
1746 endif
1747
1748
1749 catch /^NERDTree.InvalidArgumentsError/
1750 let invalidFilesFound = 1
1751 endtry
1752 endif
1753 endfor
1754
1755 "swap this nodes children out for the children we just read/refreshed
1756 let self.children = newChildNodes
1757 call self.sortChildren()
1758
1759 if invalidFilesFound
1760 call s:echoWarning("some files could not be loaded into the NERD tree")
1761 endif
1762 endif
1763endfunction
1764
1765"FUNCTION: TreeDirNode.reveal(path) {{{3
1766"reveal the given path, i.e. cache and open all treenodes needed to display it
1767"in the UI
1768function! s:TreeDirNode.reveal(path)
1769 if !a:path.isUnder(self.path)
1770 throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str()
1771 endif
1772
1773 call self.open()
1774
1775 if self.path.equals(a:path.getParent())
1776 let n = self.findNode(a:path)
1777 call s:renderView()
1778 call n.putCursorHere(1,0)
1779 return
1780 endif
1781
1782 let p = a:path
1783 while !p.getParent().equals(self.path)
1784 let p = p.getParent()
1785 endwhile
1786
1787 let n = self.findNode(p)
1788 call n.reveal(a:path)
1789endfunction
1790"FUNCTION: TreeDirNode.removeChild(treenode) {{{3
1791"
1792"Removes the given treenode from this nodes set of children
1793"
1794"Args:
1795"treenode: the node to remove
1796"
1797"Throws a NERDTree.ChildNotFoundError if the given treenode is not found
1798function! 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)
1802 return
1803 endif
1804 endfor
1805
1806 throw "NERDTree.ChildNotFoundError: child node was not found"
1807endfunction
1808
1809"FUNCTION: TreeDirNode.sortChildren() {{{3
1810"
1811"Sorts the children of this node according to alphabetical order and the
1812"directory priority.
1813"
1814function! s:TreeDirNode.sortChildren()
1815 let CompareFunc = function("s:compareNodes")
1816 call sort(self.children, CompareFunc)
1817endfunction
1818
1819"FUNCTION: TreeDirNode.toggleOpen() {{{3
1820"Opens this directory if it is closed and vice versa
1821function! s:TreeDirNode.toggleOpen()
1822 if self.isOpen ==# 1
1823 call self.close()
1824 else
1825 call self.open()
1826 endif
1827endfunction
1828
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
1832"non-recursive
1833"
1834"Arg:
1835"newNode: the node to graft into the tree
1836function! 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
1841 break
1842 endif
1843 endfor
1844endfunction
1845"============================================================
1846"CLASS: Path {{{2
1847"============================================================
1848let s:Path = {}
1849"FUNCTION: Path.AbsolutePathFor(str) {{{3
1850function! s:Path.AbsolutePathFor(str)
1851 let prependCWD = 0
1852 if s:running_windows
1853 let prependCWD = a:str !~ '^.:\(\\\|\/\)'
1854 else
1855 let prependCWD = a:str !~ '^/'
1856 endif
1857
1858 let toReturn = a:str
1859 if prependCWD
1860 let toReturn = getcwd() . s:Path.Slash() . a:str
1861 endif
1862
1863 return toReturn
1864endfunction
1865"FUNCTION: Path.bookmarkNames() {{{3
1866function! s:Path.bookmarkNames()
1867 if !exists("self._bookmarkNames")
1868 call self.cacheDisplayString()
1869 endif
1870 return self._bookmarkNames
1871endfunction
1872"FUNCTION: Path.cacheDisplayString() {{{3
1873function! s:Path.cacheDisplayString()
1874 let self.cachedDisplayString = self.getLastPathComponent(1)
1875
1876 if self.isExecutable
1877 let self.cachedDisplayString = self.cachedDisplayString . '*'
1878 endif
1879
1880 let self._bookmarkNames = []
1881 for i in s:Bookmark.Bookmarks()
1882 if i.path.equals(self)
1883 call add(self._bookmarkNames, i.name)
1884 endif
1885 endfor
1886 if !empty(self._bookmarkNames)
1887 let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
1888 endif
1889
1890 if self.isSymLink
1891 let self.cachedDisplayString .= ' -> ' . self.symLinkDest
1892 endif
1893
1894 if self.isReadOnly
1895 let self.cachedDisplayString .= ' [RO]'
1896 endif
1897endfunction
1898"FUNCTION: Path.changeToDir() {{{3
1899function! s:Path.changeToDir()
1900 let dir = self.str({'format': 'Cd'})
1901 if self.isDirectory ==# 0
1902 let dir = self.getParent().str({'format': 'Cd'})
1903 endif
1904
1905 try
1906 execute "cd " . dir
1907 call s:echo("CWD is now: " . getcwd())
1908 catch
1909 throw "NERDTree.PathChangeError: cannot change CWD to " . dir
1910 endtry
1911endfunction
1912
1913"FUNCTION: Path.compareTo() {{{3
1914"
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".
1917"
1918"Args:
1919"path: the path object to compare this to
1920"
1921"Return:
1922"1, -1 or 0
1923function! s:Path.compareTo(path)
1924 let thisPath = self.getLastPathComponent(1)
1925 let thatPath = a:path.getLastPathComponent(1)
1926
1927 "if the paths are the same then clearly we return 0
1928 if thisPath ==# thatPath
1929 return 0
1930 endif
1931
1932 let thisSS = self.getSortOrderIndex()
1933 let thatSS = a:path.getSortOrderIndex()
1934
1935 "compare the sort sequences, if they are different then the return
1936 "value is easy
1937 if thisSS < thatSS
1938 return -1
1939 elseif thisSS > thatSS
1940 return 1
1941 else
1942 "if the sort sequences are the same then compare the paths
1943 "alphabetically
1944 let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath
1945 if pathCompare
1946 return -1
1947 else
1948 return 1
1949 endif
1950 endif
1951endfunction
1952
1953"FUNCTION: Path.Create(fullpath) {{{3
1954"
1955"Factory method.
1956"
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.
1960"
1961"Args:
1962"fullpath: the full filesystem path to the file/dir to create
1963function! 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 . "'"
1967 endif
1968
1969 try
1970
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, '\(\\\|\/\)$', '', '')
1975
1976 call mkdir(fullpath, 'p')
1977
1978 "assume its a file and create
1979 else
1980 call writefile([], a:fullpath)
1981 endif
1982 catch
1983 throw "NERDTree.CreatePathError: Could not create path: '" . a:fullpath . "'"
1984 endtry
1985
1986 return s:Path.New(a:fullpath)
1987endfunction
1988
1989"FUNCTION: Path.copy(dest) {{{3
1990"
1991"Copies the file/dir represented by this Path to the given location
1992"
1993"Args:
1994"dest: the location to copy this dir/file to
1995function! s:Path.copy(dest)
1996 if !s:Path.CopyingSupported()
1997 throw "NERDTree.CopyingNotSupportedError: Copying is not supported on this OS"
1998 endif
1999
2000 let dest = s:Path.WinToUnixPath(a:dest)
2001
2002 let cmd = g:NERDTreeCopyCmd . " " . self.str() . " " . dest
2003 let success = system(cmd)
2004 if success != 0
2005 throw "NERDTree.CopyError: Could not copy ''". self.str() ."'' to: '" . a:dest . "'"
2006 endif
2007endfunction
2008
2009"FUNCTION: Path.CopyingSupported() {{{3
2010"
2011"returns 1 if copying is supported for this OS
2012function! s:Path.CopyingSupported()
2013 return exists('g:NERDTreeCopyCmd')
2014endfunction
2015
2016
2017"FUNCTION: Path.copyingWillOverwrite(dest) {{{3
2018"
2019"returns 1 if copy this path to the given location will cause files to
2020"overwritten
2021"
2022"Args:
2023"dest: the location this path will be copied to
2024function! s:Path.copyingWillOverwrite(dest)
2025 if filereadable(a:dest)
2026 return 1
2027 endif
2028
2029 if isdirectory(a:dest)
2030 let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
2031 if filereadable(path)
2032 return 1
2033 endif
2034 endif
2035endfunction
2036
2037"FUNCTION: Path.delete() {{{3
2038"
2039"Deletes the file represented by this path.
2040"Deletion of directories is not supported
2041"
2042"Throws NERDTree.Path.Deletion exceptions
2043function! s:Path.delete()
2044 if self.isDirectory
2045
2046 let cmd = g:NERDTreeRemoveDirCmd . self.str({'escape': 1})
2047 let success = system(cmd)
2048
2049 if v:shell_error != 0
2050 throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'"
2051 endif
2052 else
2053 let success = delete(self.str())
2054 if success != 0
2055 throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'"
2056 endif
2057 endif
2058
2059 "delete all bookmarks for this path
2060 for i in self.bookmarkNames()
2061 let bookmark = s:Bookmark.BookmarkFor(i)
2062 call bookmark.delete()
2063 endfor
2064endfunction
2065
2066"FUNCTION: Path.displayString() {{{3
2067"
2068"Returns a string that specifies how the path should be represented as a
2069"string
2070function! s:Path.displayString()
2071 if self.cachedDisplayString ==# ""
2072 call self.cacheDisplayString()
2073 endif
2074
2075 return self.cachedDisplayString
2076endfunction
2077"FUNCTION: Path.extractDriveLetter(fullpath) {{{3
2078"
2079"If running windows, cache the drive letter for this path
2080function! s:Path.extractDriveLetter(fullpath)
2081 if s:running_windows
2082 let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
2083 else
2084 let self.drive = ''
2085 endif
2086
2087endfunction
2088"FUNCTION: Path.exists() {{{3
2089"return 1 if this path points to a location that is readable or is a directory
2090function! s:Path.exists()
2091 let p = self.str()
2092 return filereadable(p) || isdirectory(p)
2093endfunction
2094"FUNCTION: Path.getDir() {{{3
2095"
2096"Returns this path if it is a directory, else this paths parent.
2097"
2098"Return:
2099"a Path object
2100function! s:Path.getDir()
2101 if self.isDirectory
2102 return self
2103 else
2104 return self.getParent()
2105 endif
2106endfunction
2107"FUNCTION: Path.getParent() {{{3
2108"
2109"Returns a new path object for this paths parent
2110"
2111"Return:
2112"a new Path object
2113function! s:Path.getParent()
2114 if s:running_windows
2115 let path = self.drive . '\' . join(self.pathSegments[0:-2], '\')
2116 else
2117 let path = '/'. join(self.pathSegments[0:-2], '/')
2118 endif
2119
2120 return s:Path.New(path)
2121endfunction
2122"FUNCTION: Path.getLastPathComponent(dirSlash) {{{3
2123"
2124"Gets the last part of this path.
2125"
2126"Args:
2127"dirSlash: if 1 then a trailing slash will be added to the returned value for
2128"directory nodes.
2129function! s:Path.getLastPathComponent(dirSlash)
2130 if empty(self.pathSegments)
2131 return ''
2132 endif
2133 let toReturn = self.pathSegments[-1]
2134 if a:dirSlash && self.isDirectory
2135 let toReturn = toReturn . '/'
2136 endif
2137 return toReturn
2138endfunction
2139
2140"FUNCTION: Path.getSortOrderIndex() {{{3
2141"returns the index of the pattern in g:NERDTreeSortOrder that this path matches
2142function! s:Path.getSortOrderIndex()
2143 let i = 0
2144 while i < len(g:NERDTreeSortOrder)
2145 if self.getLastPathComponent(1) =~ g:NERDTreeSortOrder[i]
2146 return i
2147 endif
2148 let i = i + 1
2149 endwhile
2150 return s:NERDTreeSortStarIndex
2151endfunction
2152
2153"FUNCTION: Path.ignore() {{{3
2154"returns true if this path should be ignored
2155function! s:Path.ignore()
2156 let lastPathComponent = self.getLastPathComponent(0)
2157
2158 "filter out the user specified paths to ignore
2159 if b:NERDTreeIgnoreEnabled
2160 for i in g:NERDTreeIgnore
2161 if lastPathComponent =~ i
2162 return 1
2163 endif
2164 endfor
2165 endif
2166
2167 "dont show hidden files unless instructed to
2168 if b:NERDTreeShowHidden ==# 0 && lastPathComponent =~ '^\.'
2169 return 1
2170 endif
2171
2172 if b:NERDTreeShowFiles ==# 0 && self.isDirectory ==# 0
2173 return 1
2174 endif
2175
2176 return 0
2177endfunction
2178
2179"FUNCTION: Path.isUnder(path) {{{3
2180"return 1 if this path is somewhere under the given path in the filesystem.
2181"
2182"a:path should be a dir
2183function! s:Path.isUnder(path)
2184 if a:path.isDirectory == 0
2185 return 0
2186 endif
2187
2188 let this = self.str()
2189 let that = a:path.str()
2190 return stridx(this, that . s:Path.Slash()) == 0
2191endfunction
2192
2193"FUNCTION: Path.JoinPathStrings(...) {{{3
2194function! s:Path.JoinPathStrings(...)
2195 let components = []
2196 for i in a:000
2197 let components = extend(components, split(i, '/'))
2198 endfor
2199 return '/' . join(components, '/')
2200endfunction
2201
2202"FUNCTION: Path.equals() {{{3
2203"
2204"Determines whether 2 path objects are "equal".
2205"They are equal if the paths they represent are the same
2206"
2207"Args:
2208"path: the other path obj to compare this with
2209function! s:Path.equals(path)
2210 return self.str() ==# a:path.str()
2211endfunction
2212
2213"FUNCTION: Path.New() {{{3
2214"The Constructor for the Path object
2215function! s:Path.New(path)
2216 let newPath = copy(self)
2217
2218 call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path))
2219
2220 let newPath.cachedDisplayString = ""
2221
2222 return newPath
2223endfunction
2224
2225"FUNCTION: Path.Slash() {{{3
2226"return the slash to use for the current OS
2227function! s:Path.Slash()
2228 return s:running_windows ? '\' : '/'
2229endfunction
2230
2231"FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
2232"
2233"
2234"Throws NERDTree.Path.InvalidArguments exception.
2235function! s:Path.readInfoFromDisk(fullpath)
2236 call self.extractDriveLetter(a:fullpath)
2237
2238 let fullpath = s:Path.WinToUnixPath(a:fullpath)
2239
2240 if getftype(fullpath) ==# "fifo"
2241 throw "NERDTree.InvalidFiletypeError: Cant handle FIFO files: " . a:fullpath
2242 endif
2243
2244 let self.pathSegments = split(fullpath, '/')
2245
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
2252 else
2253 throw "NERDTree.InvalidArgumentsError: Invalid path = " . a:fullpath
2254 endif
2255
2256 let self.isExecutable = 0
2257 if !self.isDirectory
2258 let self.isExecutable = getfperm(a:fullpath) =~ 'x'
2259 endif
2260
2261 "grab the last part of the path (minus the trailing slash)
2262 let lastPathComponent = self.getLastPathComponent(0)
2263
2264 "get the path to the new node with the parent dir fully resolved
2265 let hardPath = resolve(self.strTrunk()) . '/' . lastPathComponent
2266
2267 "if the last part of the path is a symlink then flag it as such
2268 let self.isSymLink = (resolve(hardPath) != hardPath)
2269 if self.isSymLink
2270 let self.symLinkDest = resolve(fullpath)
2271
2272 "if the link is a dir then slap a / on the end of its dest
2273 if isdirectory(self.symLinkDest)
2274
2275 "we always wanna treat MS windows shortcuts as files for
2276 "simplicity
2277 if hardPath !~ '\.lnk$'
2278
2279 let self.symLinkDest = self.symLinkDest . '/'
2280 endif
2281 endif
2282 endif
2283endfunction
2284
2285"FUNCTION: Path.refresh() {{{3
2286function! s:Path.refresh()
2287 call self.readInfoFromDisk(self.str())
2288 call self.cacheDisplayString()
2289endfunction
2290
2291"FUNCTION: Path.rename() {{{3
2292"
2293"Renames this node on the filesystem
2294function! s:Path.rename(newPath)
2295 if a:newPath ==# ''
2296 throw "NERDTree.InvalidArgumentsError: Invalid newPath for renaming = ". a:newPath
2297 endif
2298
2299 let success = rename(self.str(), a:newPath)
2300 if success != 0
2301 throw "NERDTree.PathRenameError: Could not rename: '" . self.str() . "'" . 'to:' . a:newPath
2302 endif
2303 call self.readInfoFromDisk(a:newPath)
2304
2305 for i in self.bookmarkNames()
2306 let b = s:Bookmark.BookmarkFor(i)
2307 call b.setPath(copy(self))
2308 endfor
2309 call s:Bookmark.Write()
2310endfunction
2311
2312"FUNCTION: Path.str() {{{3
2313"
2314"Returns a string representation of this Path
2315"
2316"Takes an optional dictionary param to specify how the output should be
2317"formatted.
2318"
2319"The dict may have the following keys:
2320" 'format'
2321" 'escape'
2322" 'truncateTo'
2323"
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
2328"
2329"The 'escape' key, if specified will cause the output to be escaped with
2330"shellescape()
2331"
2332"The 'truncateTo' key causes the resulting string to be truncated to the value
2333"'truncateTo' maps to. A '<' char will be prepended.
2334function! s:Path.str(...)
2335 let options = a:0 ? a:1 : {}
2336 let toReturn = ""
2337
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 . '()'
2342 else
2343 raise 'NERDTree.UnknownFormatError: unknown format "'. format .'"'
2344 endif
2345 else
2346 let toReturn = self._str()
2347 endif
2348
2349 if has_key(options, 'escape') && options['escape']
2350 let toReturn = shellescape(toReturn)
2351 endif
2352
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)
2357 endif
2358 endif
2359
2360 return toReturn
2361endfunction
2362
2363"FUNCTION: Path._strForUI() {{{3
2364function! s:Path._strForUI()
2365 let toReturn = '/' . join(self.pathSegments, '/')
2366 if self.isDirectory && toReturn != '/'
2367 let toReturn = toReturn . '/'
2368 endif
2369 return toReturn
2370endfunction
2371
2372"FUNCTION: Path._strForCd() {{{3
2373"
2374" returns a string that can be used with :cd
2375function! s:Path._strForCd()
2376 return escape(self.str(), s:escape_chars)
2377endfunction
2378"FUNCTION: Path._strForEdit() {{{3
2379"
2380"Return: the string for this path that is suitable to be used with the :edit
2381"command
2382function! s:Path._strForEdit()
2383 let p = self.str({'format': 'UI'})
2384 let cwd = getcwd()
2385
2386 if s:running_windows
2387 let p = tolower(self.str())
2388 let cwd = tolower(getcwd())
2389 endif
2390
2391 let p = escape(p, s:escape_chars)
2392
2393 let cwd = cwd . s:Path.Slash()
2394
2395 "return a relative path if we can
2396 if stridx(p, cwd) ==# 0
2397 let p = strpart(p, strlen(cwd))
2398 endif
2399
2400 if p ==# ''
2401 let p = '.'
2402 endif
2403
2404 return p
2405
2406endfunction
2407"FUNCTION: Path._strForGlob() {{{3
2408function! s:Path._strForGlob()
2409 let lead = s:Path.Slash()
2410
2411 "if we are running windows then slap a drive letter on the front
2412 if s:running_windows
2413 let lead = self.drive . '\'
2414 endif
2415
2416 let toReturn = lead . join(self.pathSegments, s:Path.Slash())
2417
2418 if !s:running_windows
2419 let toReturn = escape(toReturn, s:escape_chars)
2420 endif
2421 return toReturn
2422endfunction
2423"FUNCTION: Path._str() {{{3
2424"
2425"Gets the string path for this path object that is appropriate for the OS.
2426"EG, in windows c:\foo\bar
2427" in *nix /foo/bar
2428function! s:Path._str()
2429 let lead = s:Path.Slash()
2430
2431 "if we are running windows then slap a drive letter on the front
2432 if s:running_windows
2433 let lead = self.drive . '\'
2434 endif
2435
2436 return lead . join(self.pathSegments, s:Path.Slash())
2437endfunction
2438
2439"FUNCTION: Path.strTrunk() {{{3
2440"Gets the path without the last segment on the end.
2441function! s:Path.strTrunk()
2442 return self.drive . '/' . join(self.pathSegments[0:-2], '/')
2443endfunction
2444
2445"FUNCTION: Path.WinToUnixPath(pathstr){{{3
2446"Takes in a windows path and returns the unix equiv
2447"
2448"A class level method
2449"
2450"Args:
2451"pathstr: the windows path to convert
2452function! s:Path.WinToUnixPath(pathstr)
2453 if !s:running_windows
2454 return a:pathstr
2455 endif
2456
2457 let toReturn = a:pathstr
2458
2459 "remove the x:\ of the front
2460 let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "")
2461
2462 "convert all \ chars to /
2463 let toReturn = substitute(toReturn, '\', '/', "g")
2464
2465 return toReturn
2466endfunction
2467
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!
2474"
2475"Args:
2476"bnum: the subject buffers buffer number
2477function! s:bufInWindows(bnum)
2478 let cnt = 0
2479 let winnum = 1
2480 while 1
2481 let bufnum = winbufnr(winnum)
2482 if bufnum < 0
2483 break
2484 endif
2485 if bufnum ==# a:bnum
2486 let cnt = cnt + 1
2487 endif
2488 let winnum = winnum + 1
2489 endwhile
2490
2491 return cnt
2492endfunction " >>>
2493"FUNCTION: s:checkForBrowse(dir) {{{2
2494"inits a secondary nerd tree in the current buffer if appropriate
2495function! s:checkForBrowse(dir)
2496 if a:dir != '' && isdirectory(a:dir)
2497 call s:initNerdTreeInPlace(a:dir)
2498 endif
2499endfunction
2500"FUNCTION: s:compareBookmarks(first, second) {{{2
2501"Compares two bookmarks
2502function! s:compareBookmarks(first, second)
2503 return a:first.compareTo(a:second)
2504endfunction
2505
2506" FUNCTION: s:completeBookmarks(A,L,P) {{{2
2507" completion function for the bookmark commands
2508function! s:completeBookmarks(A,L,P)
2509 return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"')
2510endfunction
2511" FUNCTION: s:exec(cmd) {{{2
2512" same as :exec cmd but eventignore=all is set for the duration
2513function! s:exec(cmd)
2514 let old_ei = &ei
2515 set ei=all
2516 exec a:cmd
2517 let &ei = old_ei
2518endfunction
2519" FUNCTION: s:findAndRevealPath() {{{2
2520function! s:findAndRevealPath()
2521 try
2522 let p = s:Path.New(expand("%:p"))
2523 catch /^NERDTree.InvalidArgumentsError/
2524 call s:echo("no file for the current buffer")
2525 return
2526 endtry
2527
2528 if !s:treeExistsForTab()
2529 call s:initNerdTree(p.getParent().str())
2530 else
2531 if !p.isUnder(s:TreeFileNode.GetRootForTab().path)
2532 call s:initNerdTree(p.getParent().str())
2533 else
2534 if !s:isTreeOpen()
2535 call s:toggle("")
2536 endif
2537 endif
2538 endif
2539 call s:putCursorInTreeWin()
2540 call b:NERDTreeRoot.reveal(p)
2541endfunction
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
2545"
2546"Args:
2547"name: the name of a bookmark or a directory
2548function! s:initNerdTree(name)
2549 let path = {}
2550 if s:Bookmark.BookmarkExistsFor(a:name)
2551 let path = s:Bookmark.BookmarkFor(a:name).path
2552 else
2553 let dir = a:name ==# '' ? getcwd() : a:name
2554
2555 "hack to get an absolute path if a relative path is given
2556 if dir =~ '^\.'
2557 let dir = getcwd() . s:Path.Slash() . dir
2558 endif
2559 let dir = resolve(dir)
2560
2561 try
2562 let path = s:Path.New(dir)
2563 catch /^NERDTree.InvalidArgumentsError/
2564 call s:echo("No bookmark or directory found for: " . a:name)
2565 return
2566 endtry
2567 endif
2568 if !path.isDirectory
2569 let path = path.getParent()
2570 endif
2571
2572 "if instructed to, then change the vim CWD to the dir the NERDTree is
2573 "inited in
2574 if g:NERDTreeChDirMode != 0
2575 call path.changeToDir()
2576 endif
2577
2578 if s:treeExistsForTab()
2579 if s:isTreeOpen()
2580 call s:closeTree()
2581 endif
2582 unlet t:NERDTreeBufName
2583 endif
2584
2585 let newRoot = s:TreeDirNode.New(path)
2586 call newRoot.open()
2587
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
2595
2596 let b:NERDTreeType = "primary"
2597
2598 call s:renderView()
2599 call b:NERDTreeRoot.putCursorHere(0, 0)
2600endfunction
2601
2602"FUNCTION: s:initNerdTreeInPlace(dir) {{{2
2603function! s:initNerdTreeInPlace(dir)
2604 try
2605 let path = s:Path.New(a:dir)
2606 catch /^NERDTree.InvalidArgumentsError/
2607 call s:echo("Invalid directory name:" . a:name)
2608 return
2609 endtry
2610
2611 "we want the directory buffer to disappear when we do the :edit below
2612 setlocal bufhidden=wipe
2613
2614 let previousBuf = expand("#")
2615
2616 "we need a unique name for each secondary tree buffer to ensure they are
2617 "all independent
2618 exec "silent edit " . s:nextBufferName()
2619
2620 let b:NERDTreePreviousBuf = bufnr(previousBuf)
2621
2622 let b:NERDTreeRoot = s:TreeDirNode.New(path)
2623 call b:NERDTreeRoot.open()
2624
2625 "throwaway buffer options
2626 setlocal noswapfile
2627 setlocal buftype=nofile
2628 setlocal bufhidden=hide
2629 setlocal nowrap
2630 setlocal foldcolumn=0
2631 setlocal nobuflisted
2632 setlocal nospell
2633 if g:NERDTreeShowLineNumbers
2634 setlocal nu
2635 else
2636 setlocal nonu
2637 endif
2638
2639 iabc <buffer>
2640
2641 if g:NERDTreeHighlightCursorline
2642 setlocal cursorline
2643 endif
2644
2645 call s:setupStatusline()
2646
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
2652
2653 let b:NERDTreeType = "secondary"
2654
2655 call s:bindMappings()
2656 setfiletype nerdtree
2657 " syntax highlighting
2658 if has("syntax") && exists("g:syntax_on")
2659 call s:setupSyntaxHighlighting()
2660 endif
2661
2662 call s:renderView()
2663endfunction
2664" FUNCTION: s:initNerdTreeMirror() {{{2
2665function! s:initNerdTreeMirror()
2666
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)
2673 endif
2674 endfor
2675 let treeBufNames = s:unique(treeBufNames)
2676
2677 "map the option names (that the user will be prompted with) to the nerd
2678 "tree buffer names
2679 let options = {}
2680 let i = 0
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
2685 let i = i + 1
2686 endwhile
2687
2688 "work out which tree to mirror, if there is more than 1 then ask the user
2689 let bufferName = ''
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 ==# ''
2695 return
2696 endif
2697
2698 let bufferName = options[sort(keys(options))[choice-1]]
2699 elseif len(keys(options)) ==# 1
2700 let bufferName = values(options)[0]
2701 else
2702 call s:echo("No trees to mirror")
2703 return
2704 endif
2705
2706 if s:treeExistsForTab() && s:isTreeOpen()
2707 call s:closeTree()
2708 endif
2709
2710 let t:NERDTreeBufName = bufferName
2711 call s:createTreeWin()
2712 exec 'buffer ' . bufferName
2713 if !&hidden
2714 call s:renderView()
2715 endif
2716endfunction
2717" FUNCTION: s:nextBufferName() {{{2
2718" returns the buffer name for the next nerd tree
2719function! s:nextBufferName()
2720 let name = s:NERDTreeBufName . s:next_buffer_number
2721 let s:next_buffer_number += 1
2722 return name
2723endfunction
2724" FUNCTION: s:tabpagevar(tabnr, var) {{{2
2725function! s:tabpagevar(tabnr, var)
2726 let currentTab = tabpagenr()
2727 let old_ei = &ei
2728 set ei=all
2729
2730 exec "tabnext " . a:tabnr
2731 let v = -1
2732 if exists('t:' . a:var)
2733 exec 'let v = t:' . a:var
2734 endif
2735 exec "tabnext " . currentTab
2736
2737 let &ei = old_ei
2738
2739 return v
2740endfunction
2741" Function: s:treeExistsForBuffer() {{{2
2742" Returns 1 if a nerd tree root exists in the current buffer
2743function! s:treeExistsForBuf()
2744 return exists("b:NERDTreeRoot")
2745endfunction
2746" Function: s:treeExistsForTab() {{{2
2747" Returns 1 if a nerd tree root exists in the current tab
2748function! s:treeExistsForTab()
2749 return exists("t:NERDTreeBufName")
2750endfunction
2751" Function: s:unique(list) {{{2
2752" returns a:list without duplicates
2753function! s:unique(list)
2754 let uniqlist = []
2755 for elem in a:list
2756 if index(uniqlist, elem) ==# -1
2757 let uniqlist += [elem]
2758 endif
2759 endfor
2760 return uniqlist
2761endfunction
2762" SECTION: Public API {{{1
2763"============================================================
2764let g:NERDTreePath = s:Path
2765let g:NERDTreeDirNode = s:TreeDirNode
2766let g:NERDTreeFileNode = s:TreeFileNode
2767let g:NERDTreeBookmark = s:Bookmark
2768
2769function! NERDTreeAddMenuItem(options)
2770 call s:MenuItem.Create(a:options)
2771endfunction
2772
2773function! NERDTreeAddMenuSeparator(...)
2774 let opts = a:0 ? a:1 : {}
2775 call s:MenuItem.CreateSeparator(opts)
2776endfunction
2777
2778function! NERDTreeAddSubmenu(options)
2779 return s:MenuItem.Create(a:options)
2780endfunction
2781
2782function! NERDTreeAddKeyMap(options)
2783 call s:KeyMap.Create(a:options)
2784endfunction
2785
2786function! NERDTreeRender()
2787 call s:renderView()
2788endfunction
2789
2790" SECTION: View Functions {{{1
2791"============================================================
2792"FUNCTION: s:centerView() {{{2
2793"centers the nerd tree window around the cursor (provided the nerd tree
2794"options permit)
2795function! 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
2801 normal! zz
2802 endif
2803 endif
2804endfunction
2805"FUNCTION: s:closeTree() {{{2
2806"Closes the primary NERD tree window for this tab
2807function! s:closeTree()
2808 if !s:isTreeOpen()
2809 throw "NERDTree.NoTreeFoundError: no NERDTree is open"
2810 endif
2811
2812 if winnr("$") != 1
2813 if winnr() == s:getTreeWinNum()
2814 wincmd p
2815 let bufnr = bufnr("")
2816 wincmd p
2817 else
2818 let bufnr = bufnr("")
2819 endif
2820
2821 call s:exec(s:getTreeWinNum() . " wincmd w")
2822 close
2823 call s:exec(bufwinnr(bufnr) . " wincmd w")
2824 else
2825 close
2826 endif
2827endfunction
2828
2829"FUNCTION: s:closeTreeIfOpen() {{{2
2830"Closes the NERD tree window if it is open
2831function! s:closeTreeIfOpen()
2832 if s:isTreeOpen()
2833 call s:closeTree()
2834 endif
2835endfunction
2836"FUNCTION: s:closeTreeIfQuitOnOpen() {{{2
2837"Closes the NERD tree window if the close on open option is set
2838function! s:closeTreeIfQuitOnOpen()
2839 if g:NERDTreeQuitOnOpen && s:isTreeOpen()
2840 call s:closeTree()
2841 endif
2842endfunction
2843"FUNCTION: s:createTreeWin() {{{2
2844"Inits the NERD tree window. ie. opens it, sizes it, sets all the local
2845"options etc
2846function! s:createTreeWin()
2847 "create the nerd tree window
2848 let splitLocation = g:NERDTreeWinPos ==# "left" ? "topleft " : "botright "
2849 let splitSize = g:NERDTreeWinSize
2850
2851 if !exists('t:NERDTreeBufName')
2852 let t:NERDTreeBufName = s:nextBufferName()
2853 silent! exec splitLocation . 'vertical ' . splitSize . ' new'
2854 silent! exec "edit " . t:NERDTreeBufName
2855 else
2856 silent! exec splitLocation . 'vertical ' . splitSize . ' split'
2857 silent! exec "buffer " . t:NERDTreeBufName
2858 endif
2859
2860 setlocal winfixwidth
2861
2862 "throwaway buffer options
2863 setlocal noswapfile
2864 setlocal buftype=nofile
2865 setlocal nowrap
2866 setlocal foldcolumn=0
2867 setlocal nobuflisted
2868 setlocal nospell
2869 if g:NERDTreeShowLineNumbers
2870 setlocal nu
2871 else
2872 setlocal nonu
2873 endif
2874
2875 iabc <buffer>
2876
2877 if g:NERDTreeHighlightCursorline
2878 setlocal cursorline
2879 endif
2880
2881 call s:setupStatusline()
2882
2883 call s:bindMappings()
2884 setfiletype nerdtree
2885 " syntax highlighting
2886 if has("syntax") && exists("g:syntax_on")
2887 call s:setupSyntaxHighlighting()
2888 endif
2889endfunction
2890
2891"FUNCTION: s:dumpHelp {{{2
2892"prints out the quick help
2893function! s:dumpHelp()
2894 let old_h = @h
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"
2903 else
2904 let @h=@h."\" ". g:NERDTreeMapActivateNode .": open in current window\n"
2905 endif
2906 if b:NERDTreeType ==# "primary"
2907 let @h=@h."\" ". g:NERDTreeMapPreview .": preview\n"
2908 endif
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"
2916
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"
2927
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"
2935
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"
2944
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"
2957
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"
2964
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"
2971 endfor
2972 endif
2973
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"
2988 else
2989 let @h="\" Press ". g:NERDTreeMapHelp ." for help\n"
2990 endif
2991
2992 silent! put h
2993
2994 let @h = old_h
2995endfunction
2996"FUNCTION: s:echo {{{2
2997"A wrapper for :echo. Appends 'NERDTree:' on the front of all messages
2998"
2999"Args:
3000"msg: the message to echo
3001function! s:echo(msg)
3002 redraw
3003 echomsg "NERDTree: " . a:msg
3004endfunction
3005"FUNCTION: s:echoWarning {{{2
3006"Wrapper for s:echo, sets the message type to warningmsg for this message
3007"Args:
3008"msg: the message to echo
3009function! s:echoWarning(msg)
3010 echohl warningmsg
3011 call s:echo(a:msg)
3012 echohl normal
3013endfunction
3014"FUNCTION: s:echoError {{{2
3015"Wrapper for s:echo, sets the message type to errormsg for this message
3016"Args:
3017"msg: the message to echo
3018function! s:echoError(msg)
3019 echohl errormsg
3020 call s:echo(a:msg)
3021 echohl normal
3022endfunction
3023"FUNCTION: s:firstUsableWindow(){{{2
3024"find the window number of the first normal window
3025function! s:firstUsableWindow()
3026 let i = 1
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)
3032 return i
3033 endif
3034
3035 let i += 1
3036 endwhile
3037 return -1
3038endfunction
3039"FUNCTION: s:getPath(ln) {{{2
3040"Gets the full path to the node that is rendered on the given line number
3041"
3042"Args:
3043"ln: the line number to get the path for
3044"
3045"Return:
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
3049function! s:getPath(ln)
3050 let line = getline(a:ln)
3051
3052 let rootLine = s:TreeFileNode.GetRootLineNum()
3053
3054 "check to see if we have the root node
3055 if a:ln == rootLine
3056 return b:NERDTreeRoot.path
3057 endif
3058
3059 " in case called from outside the tree
3060 "if line !~ '^ *[|`▶▼ ]' || line =~ '^$'
3061 "return {}
3062 "endif
3063
3064 if line ==# s:tree_up_dir_line
3065 return b:NERDTreeRoot.path.getParent()
3066 endif
3067
3068 let indent = s:indentLevelFor(line)
3069
3070 "remove the tree parts and the leading space
3071 let curFile = s:stripMarkupFromLine(line, 0)
3072
3073 let wasdir = 0
3074 if curFile =~ '/$'
3075 let wasdir = 1
3076 let curFile = substitute(curFile, '/\?$', '/', "")
3077 endif
3078
3079 let dir = ""
3080 let lnum = a:ln
3081 while lnum > 0
3082 let lnum = lnum - 1
3083 let curLine = getline(lnum)
3084 let curLineStripped = s:stripMarkupFromLine(curLine, 1)
3085
3086 "have we reached the top of the tree?
3087 if lnum == rootLine
3088 let dir = b:NERDTreeRoot.path.str({'format': 'UI'}) . dir
3089 break
3090 endif
3091 if curLineStripped =~ '/$'
3092 let lpindent = s:indentLevelFor(curLine)
3093 if lpindent < indent
3094 let indent = indent - 1
3095
3096 let dir = substitute (curLineStripped,'^\\', "", "") . dir
3097 continue
3098 endif
3099 endif
3100 endwhile
3101 let curFile = b:NERDTreeRoot.path.drive . dir . curFile
3102 let toReturn = s:Path.New(curFile)
3103 return toReturn
3104endfunction
3105
3106"FUNCTION: s:getTreeWinNum() {{{2
3107"gets the nerd tree window number for this tab
3108function! s:getTreeWinNum()
3109 if exists("t:NERDTreeBufName")
3110 return bufwinnr(t:NERDTreeBufName)
3111 else
3112 return -1
3113 endif
3114endfunction
3115"FUNCTION: s:indentLevelFor(line) {{{2
3116function! s:indentLevelFor(line)
3117 return match(a:line, '[^ \-+~`|]') / s:tree_wid
3118endfunction
3119"FUNCTION: s:isTreeOpen() {{{2
3120function! s:isTreeOpen()
3121 return s:getTreeWinNum() != -1
3122endfunction
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
3126"
3127"Args:
3128"winnumber: the number of the window in question
3129function! s:isWindowUsable(winnumber)
3130 "gotta split if theres only one window (i.e. the NERD tree)
3131 if winnr("$") ==# 1
3132 return 0
3133 endif
3134
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")
3140
3141 "if its a special window e.g. quickfix or another explorer plugin then we
3142 "have to split
3143 if specialWindow
3144 return 0
3145 endif
3146
3147 if &hidden
3148 return 1
3149 endif
3150
3151 return !modified || s:bufInWindows(winbufnr(a:winnumber)) >= 2
3152endfunction
3153
3154" FUNCTION: s:jumpToChild(direction) {{{2
3155" Args:
3156" direction: 0 if going to first child, 1 if going to last
3157function! 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")
3161 return
3162 end
3163 let dirNode = currentNode.parent
3164 let childNodes = dirNode.getVisibleChildren()
3165
3166 let targetNode = childNodes[0]
3167 if a:direction
3168 let targetNode = childNodes[len(childNodes) - 1]
3169 endif
3170
3171 if targetNode.equals(currentNode)
3172 let siblingDir = currentNode.parent.findOpenDirSiblingWithVisibleChildren(a:direction)
3173 if siblingDir != {}
3174 let indx = a:direction ? siblingDir.getVisibleChildCount()-1 : 0
3175 let targetNode = siblingDir.getChildByIndex(indx, 1)
3176 endif
3177 endif
3178
3179 call targetNode.putCursorHere(1, 0)
3180
3181 call s:centerView()
3182endfunction
3183
3184
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
3188"
3189"Args:
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
3192" del the buffer
3193function! s:promptToDelBuffer(bufnum, msg)
3194 echo a:msg
3195 if nr2char(getchar()) ==# 'y'
3196 exec "silent bdelete! " . a:bufnum
3197 endif
3198endfunction
3199
3200"FUNCTION: s:putCursorOnBookmarkTable(){{{2
3201"Places the cursor at the top of the bookmarks table
3202function! s:putCursorOnBookmarkTable()
3203 if !b:NERDTreeShowBookmarks
3204 throw "NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active"
3205 endif
3206
3207 let rootNodeLine = s:TreeFileNode.GetRootLineNum()
3208
3209 let line = 1
3210 while getline(line) !~ '^>-\+Bookmarks-\+$'
3211 let line = line + 1
3212 if line >= rootNodeLine
3213 throw "NERDTree.BookmarkTableNotFoundError: didnt find the bookmarks table"
3214 endif
3215 endwhile
3216 call cursor(line, 0)
3217endfunction
3218
3219"FUNCTION: s:putCursorInTreeWin(){{{2
3220"Places the cursor in the nerd tree window
3221function! s:putCursorInTreeWin()
3222 if !s:isTreeOpen()
3223 throw "NERDTree.InvalidOperationError: cant put cursor in NERD tree window, no window exists"
3224 endif
3225
3226 call s:exec(s:getTreeWinNum() . "wincmd w")
3227endfunction
3228
3229"FUNCTION: s:renderBookmarks {{{2
3230function! s:renderBookmarks()
3231
3232 call setline(line(".")+1, ">----------Bookmarks----------")
3233 call cursor(line(".")+1, col("."))
3234
3235 for i in s:Bookmark.Bookmarks()
3236 call setline(line(".")+1, i.str())
3237 call cursor(line(".")+1, col("."))
3238 endfor
3239
3240 call setline(line(".")+1, '')
3241 call cursor(line(".")+1, col("."))
3242endfunction
3243"FUNCTION: s:renderView {{{2
3244"The entry function for rendering the tree
3245function! s:renderView()
3246 setlocal modifiable
3247
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")
3253
3254 "delete all lines in the buffer (being careful not to clobber a register)
3255 silent 1,$delete _
3256
3257 call s:dumpHelp()
3258
3259 "delete the blank line before the help and add one after it
3260 call setline(line(".")+1, "")
3261 call cursor(line(".")+1, col("."))
3262
3263 if b:NERDTreeShowBookmarks
3264 call s:renderBookmarks()
3265 endif
3266
3267 "add the 'up a dir' line
3268 call setline(line(".")+1, s:tree_up_dir_line)
3269 call cursor(line(".")+1, col("."))
3270
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("."))
3275
3276 "draw the tree
3277 let old_o = @o
3278 let @o = b:NERDTreeRoot.renderToString()
3279 silent put o
3280 let @o = old_o
3281
3282 "delete the blank line at the top of the buffer
3283 silent 1,1delete _
3284
3285 "restore the view
3286 let old_scrolloff=&scrolloff
3287 let &scrolloff=0
3288 call cursor(topLine, 1)
3289 normal! zt
3290 call cursor(curLine, curCol)
3291 let &scrolloff = old_scrolloff
3292
3293 setlocal nomodifiable
3294endfunction
3295
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
3299function! s:renderViewSavingPosition()
3300 let currentNode = s:TreeFileNode.GetSelected()
3301
3302 "go up the tree till we find a node that will be visible or till we run
3303 "out of nodes
3304 while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
3305 let currentNode = currentNode.parent
3306 endwhile
3307
3308 call s:renderView()
3309
3310 if currentNode != {}
3311 call currentNode.putCursorHere(0, 0)
3312 endif
3313endfunction
3314"FUNCTION: s:restoreScreenState() {{{2
3315"
3316"Sets the screen state back to what it was when s:saveScreenState was last
3317"called.
3318"
3319"Assumes the cursor is in the NERDTree window
3320function! s:restoreScreenState()
3321 if !exists("b:NERDTreeOldTopLine") || !exists("b:NERDTreeOldPos") || !exists("b:NERDTreeOldWindowSize")
3322 return
3323 endif
3324 exec("silent vertical resize ".b:NERDTreeOldWindowSize)
3325
3326 let old_scrolloff=&scrolloff
3327 let &scrolloff=0
3328 call cursor(b:NERDTreeOldTopLine, 0)
3329 normal! zt
3330 call setpos(".", b:NERDTreeOldPos)
3331 let &scrolloff=old_scrolloff
3332endfunction
3333
3334"FUNCTION: s:saveScreenState() {{{2
3335"Saves the current cursor position in the current buffer and the window
3336"scroll position
3337function! s:saveScreenState()
3338 let win = winnr()
3339 try
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/
3346 endtry
3347endfunction
3348
3349"FUNCTION: s:setupStatusline() {{{2
3350function! s:setupStatusline()
3351 if g:NERDTreeStatusline != -1
3352 let &l:statusline = g:NERDTreeStatusline
3353 endif
3354endfunction
3355"FUNCTION: s:setupSyntaxHighlighting() {{{2
3356function! 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\]#
3361
3362 "highlighting for the .. (up dir) line at the top of the tree
3363 execute "syn match treeUp #". s:tree_up_dir_line ."#"
3364
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
3370
3371 "highlighting for the tree structural parts
3372 syn match treePart #|#
3373 syn match treePart #`#
3374 syn match treePartFile #[|`]-#hs=s+1 contains=treePart
3375
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
3384
3385 "highlighting for readonly files
3386 syn match treeRO #.*\[RO\]#hs=s+2 contains=treeFlag,treeBookmark,treePart,treePartFile
3387
3388 "highlighting for sym links
3389 syn match treeLink #[^-| `].* -> # contains=treeBookmark,treeOpenable,treeClosable,treeDirSlash
3390
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 #^/.*$#
3398
3399 "highlighting for bookmarks
3400 syn match treeBookmark # {.*}#hs=s+1
3401
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
3407
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
3415 else
3416 hi def link treePart Normal
3417 hi def link treePartFile Normal
3418 hi def link treeFile Normal
3419 hi def link treeClosable Title
3420 endif
3421
3422 hi def link treeBookmarksHeader statement
3423 hi def link treeBookmarksLeader ignore
3424 hi def link treeBookmarkName Identifier
3425 hi def link treeBookmark normal
3426
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
3433
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
3442
3443 hi def link NERDTreeCurrentNode Search
3444endfunction
3445
3446"FUNCTION: s:stripMarkupFromLine(line, removeLeadingSpaces){{{2
3447"returns the given line with all the tree parts stripped off
3448"
3449"Args:
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)
3453function! s:stripMarkupFromLine(line, removeLeadingSpaces)
3454 let line = a:line
3455 "remove the tree parts and the leading space
3456 let line = substitute (line, s:tree_markup_reg,"","")
3457
3458 "strip off any read only flag
3459 let line = substitute (line, ' \[RO\]', "","")
3460
3461 "strip off any bookmark flags
3462 let line = substitute (line, ' {[^}]*}', "","")
3463
3464 "strip off any executable flags
3465 let line = substitute (line, '*\ze\($\| \)', "","")
3466
3467 let wasdir = 0
3468 if line =~ '/$'
3469 let wasdir = 1
3470 endif
3471 let line = substitute (line,' -> .*',"","") " remove link to
3472 if wasdir ==# 1
3473 let line = substitute (line, '/\?$', '/', "")
3474 endif
3475
3476 if a:removeLeadingSpaces
3477 let line = substitute (line, '^ *', '', '')
3478 endif
3479
3480 return line
3481endfunction
3482
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)
3486"
3487"Args:
3488"dir: the full path for the root node (is only used if the NERD tree is being
3489"initialized.
3490function! s:toggle(dir)
3491 if s:treeExistsForTab()
3492 if !s:isTreeOpen()
3493 call s:createTreeWin()
3494 if !&hidden
3495 call s:renderView()
3496 endif
3497 call s:restoreScreenState()
3498 else
3499 call s:closeTree()
3500 endif
3501 else
3502 call s:initNerdTree(a:dir)
3503 endif
3504endfunction
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.
3510"
3511"args:
3512"forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3513function! s:activateNode(forceKeepWindowOpen)
3514 if getline(".") ==# s:tree_up_dir_line
3515 return s:upDir(0)
3516 endif
3517
3518 let treenode = s:TreeFileNode.GetSelected()
3519 if treenode != {}
3520 call treenode.activate(a:forceKeepWindowOpen)
3521 else
3522 let bookmark = s:Bookmark.GetSelected()
3523 if !empty(bookmark)
3524 call bookmark.activate()
3525 endif
3526 endif
3527endfunction
3528
3529"FUNCTION: s:bindMappings() {{{2
3530function! 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>
3535
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>"
3539
3540 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreview ." :call <SID>previewNode(0)<cr>"
3541 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapPreviewSplit ." :call <SID>previewNode(1)<cr>"
3542
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>"
3545
3546 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenRecursively ." :call <SID>openNodeRecursively()<cr>"
3547
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>"
3551
3552 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapChdir ." :call <SID>chCwd()<cr>"
3553
3554 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapQuit ." :call <SID>closeTreeWindow()<cr>"
3555
3556 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefreshRoot ." :call <SID>refreshRoot()<cr>"
3557 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapRefresh ." :call <SID>refreshCurrent()<cr>"
3558
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>"
3565
3566 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>"
3567 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>"
3568
3569 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapMenu ." :call <SID>showMenu()<cr>"
3570
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>"
3577
3578 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTab ." :call <SID>openInNewTab(0)<cr>"
3579 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenInTabSilent ." :call <SID>openInNewTab(1)<cr>"
3580
3581 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapOpenExpl ." :call <SID>openExplorer()<cr>"
3582
3583 exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>"
3584
3585 "bind all the user custom maps
3586 call s:KeyMap.BindAll()
3587
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()
3596endfunction
3597
3598" FUNCTION: s:bookmarkNode(name) {{{2
3599" Associate the current node with the given name
3600function! s:bookmarkNode(name)
3601 let currentNode = s:TreeFileNode.GetSelected()
3602 if currentNode != {}
3603 try
3604 call currentNode.bookmark(a:name)
3605 call s:renderView()
3606 catch /^NERDTree.IllegalBookmarkNameError/
3607 call s:echo("bookmark names must not contain spaces")
3608 endtry
3609 else
3610 call s:echo("select a node first")
3611 endif
3612endfunction
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
3616"clicked)
3617function! s:checkForActivate()
3618 let currentNode = s:TreeFileNode.GetSelected()
3619 if currentNode != {}
3620 call s:activateNode(0)
3621 return
3622 endif
3623endfunction
3624
3625" FUNCTION: s:chCwd() {{{2
3626function! s:chCwd()
3627 let treenode = s:TreeFileNode.GetSelected()
3628 if treenode ==# {}
3629 call s:echo("Select a node first")
3630 return
3631 endif
3632
3633 try
3634 call treenode.path.changeToDir()
3635 catch /^NERDTree.PathChangeError/
3636 call s:echoWarning("could not change cwd")
3637 endtry
3638endfunction
3639
3640" FUNCTION: s:chRoot() {{{2
3641" changes the current root to the selected one
3642function! s:chRoot()
3643 let treenode = s:TreeFileNode.GetSelected()
3644 if treenode ==# {}
3645 call s:echo("Select a node first")
3646 return
3647 endif
3648
3649 call treenode.makeRoot()
3650 call s:renderView()
3651 call b:NERDTreeRoot.putCursorHere(0, 0)
3652endfunction
3653
3654" FUNCTION: s:clearBookmarks(bookmarks) {{{2
3655function! s:clearBookmarks(bookmarks)
3656 if a:bookmarks ==# ''
3657 let currentNode = s:TreeFileNode.GetSelected()
3658 if currentNode != {}
3659 call currentNode.clearBoomarks()
3660 endif
3661 else
3662 for name in split(a:bookmarks, ' ')
3663 let bookmark = s:Bookmark.BookmarkFor(name)
3664 call bookmark.delete()
3665 endfor
3666 endif
3667 call s:renderView()
3668endfunction
3669" FUNCTION: s:closeChildren() {{{2
3670" closes all childnodes of the current node
3671function! s:closeChildren()
3672 let currentNode = s:TreeDirNode.GetSelected()
3673 if currentNode ==# {}
3674 call s:echo("Select a node first")
3675 return
3676 endif
3677
3678 call currentNode.closeChildren()
3679 call s:renderView()
3680 call currentNode.putCursorHere(0, 0)
3681endfunction
3682" FUNCTION: s:closeCurrentDir() {{{2
3683" closes the parent dir of the current node
3684function! s:closeCurrentDir()
3685 let treenode = s:TreeFileNode.GetSelected()
3686 if treenode ==# {}
3687 call s:echo("Select a node first")
3688 return
3689 endif
3690
3691 let parent = treenode.parent
3692 if parent ==# {} || parent.isRoot()
3693 call s:echo("cannot close tree root")
3694 else
3695 call treenode.parent.close()
3696 call s:renderView()
3697 call treenode.parent.putCursorHere(0, 0)
3698 endif
3699endfunction
3700" FUNCTION: s:closeTreeWindow() {{{2
3701" close the tree window
3702function! s:closeTreeWindow()
3703 if b:NERDTreeType ==# "secondary" && b:NERDTreePreviousBuf != -1
3704 exec "buffer " . b:NERDTreePreviousBuf
3705 else
3706 if winnr("$") > 1
3707 call s:closeTree()
3708 else
3709 call s:echo("Cannot close last window")
3710 endif
3711 endif
3712endfunction
3713" FUNCTION: s:deleteBookmark() {{{2
3714" if the cursor is on a bookmark, prompt to delete
3715function! s:deleteBookmark()
3716 let bookmark = s:Bookmark.GetSelected()
3717 if bookmark ==# {}
3718 call s:echo("Put the cursor on a bookmark")
3719 return
3720 endif
3721
3722 echo "Are you sure you wish to delete the bookmark:\n\"" . bookmark.name . "\" (yN):"
3723
3724 if nr2char(getchar()) ==# 'y'
3725 try
3726 call bookmark.delete()
3727 call s:renderView()
3728 redraw
3729 catch /^NERDTree/
3730 call s:echoWarning("Could not remove bookmark")
3731 endtry
3732 else
3733 call s:echo("delete aborted" )
3734 endif
3735
3736endfunction
3737
3738" FUNCTION: s:displayHelp() {{{2
3739" toggles the help display
3740function! s:displayHelp()
3741 let b:treeShowHelp = b:treeShowHelp ? 0 : 1
3742 call s:renderView()
3743 call s:centerView()
3744endfunction
3745
3746" FUNCTION: s:handleMiddleMouse() {{{2
3747function! s:handleMiddleMouse()
3748 let curNode = s:TreeFileNode.GetSelected()
3749 if curNode ==# {}
3750 call s:echo("Put the cursor on a node first" )
3751 return
3752 endif
3753
3754 if curNode.path.isDirectory
3755 call s:openExplorer()
3756 else
3757 call s:openEntrySplit(0,0)
3758 endif
3759endfunction
3760
3761
3762" FUNCTION: s:jumpToFirstChild() {{{2
3763" wrapper for the jump to child method
3764function! s:jumpToFirstChild()
3765 call s:jumpToChild(0)
3766endfunction
3767
3768" FUNCTION: s:jumpToLastChild() {{{2
3769" wrapper for the jump to child method
3770function! s:jumpToLastChild()
3771 call s:jumpToChild(1)
3772endfunction
3773
3774" FUNCTION: s:jumpToParent() {{{2
3775" moves the cursor to the parent of the current node
3776function! s:jumpToParent()
3777 let currentNode = s:TreeFileNode.GetSelected()
3778 if !empty(currentNode)
3779 if !empty(currentNode.parent)
3780 call currentNode.parent.putCursorHere(1, 0)
3781 call s:centerView()
3782 else
3783 call s:echo("cannot jump to parent")
3784 endif
3785 else
3786 call s:echo("put the cursor on a node first")
3787 endif
3788endfunction
3789
3790" FUNCTION: s:jumpToRoot() {{{2
3791" moves the cursor to the root node
3792function! s:jumpToRoot()
3793 call b:NERDTreeRoot.putCursorHere(1, 0)
3794 call s:centerView()
3795endfunction
3796
3797" FUNCTION: s:jumpToSibling() {{{2
3798" moves the cursor to the sibling of the current node in the given direction
3799"
3800" Args:
3801" forward: 1 if the cursor should move to the next sibling, 0 if it should
3802" move back to the previous sibling
3803function! s:jumpToSibling(forward)
3804 let currentNode = s:TreeFileNode.GetSelected()
3805 if !empty(currentNode)
3806 let sibling = currentNode.findSibling(a:forward)
3807
3808 if !empty(sibling)
3809 call sibling.putCursorHere(1, 0)
3810 call s:centerView()
3811 endif
3812 else
3813 call s:echo("put the cursor on a node first")
3814 endif
3815endfunction
3816
3817" FUNCTION: s:openBookmark(name) {{{2
3818" put the cursor on the given bookmark and, if its a file, open it
3819function! s:openBookmark(name)
3820 try
3821 let targetNode = s:Bookmark.GetNodeForName(a:name, 0)
3822 call targetNode.putCursorHere(0, 1)
3823 redraw!
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)
3828 endtry
3829 if targetNode.path.isDirectory
3830 call targetNode.openExplorer()
3831 else
3832 call targetNode.open()
3833 endif
3834endfunction
3835" FUNCTION: s:openEntrySplit(vertical, forceKeepWindowOpen) {{{2
3836"Opens the currently selected file from the explorer in a
3837"new window
3838"
3839"args:
3840"forceKeepWindowOpen - dont close the window even if NERDTreeQuitOnOpen is set
3841function! s:openEntrySplit(vertical, forceKeepWindowOpen)
3842 let treenode = s:TreeFileNode.GetSelected()
3843 if treenode != {}
3844 if a:vertical
3845 call treenode.openVSplit()
3846 else
3847 call treenode.openSplit()
3848 endif
3849 if !a:forceKeepWindowOpen
3850 call s:closeTreeIfQuitOnOpen()
3851 endif
3852 else
3853 call s:echo("select a node first")
3854 endif
3855endfunction
3856
3857" FUNCTION: s:openExplorer() {{{2
3858function! s:openExplorer()
3859 let treenode = s:TreeDirNode.GetSelected()
3860 if treenode != {}
3861 call treenode.openExplorer()
3862 else
3863 call s:echo("select a node first")
3864 endif
3865endfunction
3866
3867" FUNCTION: s:openInNewTab(stayCurrentTab) {{{2
3868" Opens the selected node or bookmark in a new tab
3869" Args:
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
3872function! s:openInNewTab(stayCurrentTab)
3873 let target = s:TreeFileNode.GetSelected()
3874 if target == {}
3875 let target = s:Bookmark.GetSelected()
3876 endif
3877
3878 if target != {}
3879 call target.openInNewTab({'stayInCurrentTab': a:stayCurrentTab})
3880 endif
3881endfunction
3882
3883" FUNCTION: s:openNodeRecursively() {{{2
3884function! s:openNodeRecursively()
3885 let treenode = s:TreeFileNode.GetSelected()
3886 if treenode ==# {} || treenode.path.isDirectory ==# 0
3887 call s:echo("Select a directory node first" )
3888 else
3889 call s:echo("Recursively opening node. Please wait...")
3890 call treenode.openRecursively()
3891 call s:renderView()
3892 redraw
3893 call s:echo("Recursively opening node. Please wait... DONE")
3894 endif
3895
3896endfunction
3897
3898"FUNCTION: s:previewNode() {{{2
3899"Args:
3900" openNewWin: if 0, use the previous window, if 1 open in new split, if 2
3901" open in a vsplit
3902function! s:previewNode(openNewWin)
3903 let currentBuf = bufnr("")
3904 if a:openNewWin > 0
3905 call s:openEntrySplit(a:openNewWin ==# 2,1)
3906 else
3907 call s:activateNode(1)
3908 end
3909 call s:exec(bufwinnr(currentBuf) . "wincmd w")
3910endfunction
3911
3912" FUNCTION: s:revealBookmark(name) {{{2
3913" put the cursor on the node associate with the given name
3914function! s:revealBookmark(name)
3915 try
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")
3920 endtry
3921endfunction
3922" FUNCTION: s:refreshRoot() {{{2
3923" Reloads the current root. All nodes below this will be lost and the root dir
3924" will be reloaded.
3925function! s:refreshRoot()
3926 call s:echo("Refreshing the root node. This could take a while...")
3927 call b:NERDTreeRoot.refresh()
3928 call s:renderView()
3929 redraw
3930 call s:echo("Refreshing the root node. This could take a while... DONE")
3931endfunction
3932
3933" FUNCTION: s:refreshCurrent() {{{2
3934" refreshes the root for the current node
3935function! s:refreshCurrent()
3936 let treenode = s:TreeDirNode.GetSelected()
3937 if treenode ==# {}
3938 call s:echo("Refresh failed. Select a node first")
3939 return
3940 endif
3941
3942 call s:echo("Refreshing node. This could take a while...")
3943 call treenode.refresh()
3944 call s:renderView()
3945 redraw
3946 call s:echo("Refreshing node. This could take a while... DONE")
3947endfunction
3948" FUNCTION: s:showMenu() {{{2
3949function! s:showMenu()
3950 let curNode = s:TreeFileNode.GetSelected()
3951 if curNode ==# {}
3952 call s:echo("Put the cursor on a node first" )
3953 return
3954 endif
3955
3956 let mc = s:MenuController.New(s:MenuItem.AllEnabled())
3957 call mc.showMenu()
3958endfunction
3959
3960" FUNCTION: s:toggleIgnoreFilter() {{{2
3961" toggles the use of the NERDTreeIgnore option
3962function! s:toggleIgnoreFilter()
3963 let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled
3964 call s:renderViewSavingPosition()
3965 call s:centerView()
3966endfunction
3967
3968" FUNCTION: s:toggleShowBookmarks() {{{2
3969" toggles the display of bookmarks
3970function! s:toggleShowBookmarks()
3971 let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks
3972 if b:NERDTreeShowBookmarks
3973 call s:renderView()
3974 call s:putCursorOnBookmarkTable()
3975 else
3976 call s:renderViewSavingPosition()
3977 endif
3978 call s:centerView()
3979endfunction
3980" FUNCTION: s:toggleShowFiles() {{{2
3981" toggles the display of hidden files
3982function! s:toggleShowFiles()
3983 let b:NERDTreeShowFiles = !b:NERDTreeShowFiles
3984 call s:renderViewSavingPosition()
3985 call s:centerView()
3986endfunction
3987
3988" FUNCTION: s:toggleShowHidden() {{{2
3989" toggles the display of hidden files
3990function! s:toggleShowHidden()
3991 let b:NERDTreeShowHidden = !b:NERDTreeShowHidden
3992 call s:renderViewSavingPosition()
3993 call s:centerView()
3994endfunction
3995
3996" FUNCTION: s:toggleZoom() {{2
3997" zoom (maximize/minimize) the NERDTree window
3998function! 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
4003 else
4004 exec "vertical resize"
4005 let b:NERDTreeZoomed = 1
4006 endif
4007endfunction
4008
4009"FUNCTION: s:upDir(keepState) {{{2
4010"moves the tree up a level
4011"
4012"Args:
4013"keepState: 1 if the current root should be left open when the tree is
4014"re-rendered
4015function! s:upDir(keepState)
4016 let cwd = b:NERDTreeRoot.path.str({'format': 'UI'})
4017 if cwd ==# "/" || cwd =~ '^[^/]..$'
4018 call s:echo("already at top dir")
4019 else
4020 if !a:keepState
4021 call b:NERDTreeRoot.close()
4022 endif
4023
4024 let oldRoot = b:NERDTreeRoot
4025
4026 if empty(b:NERDTreeRoot.parent)
4027 let path = b:NERDTreeRoot.path.getParent()
4028 let newRoot = s:TreeDirNode.New(path)
4029 call newRoot.open()
4030 call newRoot.transplantChild(b:NERDTreeRoot)
4031 let b:NERDTreeRoot = newRoot
4032 else
4033 let b:NERDTreeRoot = b:NERDTreeRoot.parent
4034 endif
4035
4036 if g:NERDTreeChDirMode ==# 2
4037 call b:NERDTreeRoot.path.changeToDir()
4038 endif
4039
4040 call s:renderView()
4041 call oldRoot.putCursorHere(0, 0)
4042 endif
4043endfunction
4044
4045
4046"reset &cpo back to users setting
4047let &cpo = s:old_cpo
4048
4049" vim: set sw=4 sts=4 et fdm=marker: