]> git.r.bdr.sh - rbdr/dotfiles/blob - vim/plugin/NERD_tree.vim
Remove task Warrior
[rbdr/dotfiles] / vim / plugin / NERD_tree.vim
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 " ============================================================================
13 let s:NERD_tree_version = '4.1.0'
14
15 " SECTION: Script init stuff {{{1
16 "============================================================
17 if exists("loaded_nerd_tree")
18 finish
19 endif
20 if v:version < 700
21 echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!"
22 finish
23 endif
24 let loaded_nerd_tree = 1
25
26 "for line continuation - i.e dont want C in &cpo
27 let s:old_cpo = &cpo
28 set 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
40 function! 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
46 endfunction
47
48 "SECTION: Init variable calls and other random constants {{{2
49 call s:initVariable("g:NERDChristmasTree", 1)
50 call s:initVariable("g:NERDTreeAutoCenter", 1)
51 call s:initVariable("g:NERDTreeAutoCenterThreshold", 3)
52 call s:initVariable("g:NERDTreeCaseSensitiveSort", 0)
53 call s:initVariable("g:NERDTreeChDirMode", 0)
54 if !exists("g:NERDTreeIgnore")
55 let g:NERDTreeIgnore = ['\~$']
56 endif
57 call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks')
58 call s:initVariable("g:NERDTreeHighlightCursorline", 1)
59 call s:initVariable("g:NERDTreeHijackNetrw", 1)
60 call s:initVariable("g:NERDTreeMouseMode", 1)
61 call s:initVariable("g:NERDTreeNotificationThreshold", 100)
62 call s:initVariable("g:NERDTreeQuitOnOpen", 0)
63 call s:initVariable("g:NERDTreeShowBookmarks", 0)
64 call s:initVariable("g:NERDTreeShowFiles", 1)
65 call s:initVariable("g:NERDTreeShowHidden", 0)
66 call s:initVariable("g:NERDTreeShowLineNumbers", 0)
67 call s:initVariable("g:NERDTreeSortDirs", 1)
68
69 if !exists("g:NERDTreeSortOrder")
70 let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$']
71 else
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
76 endif
77
78 "we need to use this number many times for sorting... so we calculate it only
79 "once here
80 let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*')
81
82 if !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
89 endif
90 call s:initVariable("g:NERDTreeWinPos", "left")
91 call s:initVariable("g:NERDTreeWinSize", 31)
92
93 let 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
98 if s:running_windows
99 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ')
100 else
101 call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ')
102 call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ')
103 endif
104
105
106 "SECTION: Init variable calls for key mappings {{{2
107 call s:initVariable("g:NERDTreeMapActivateNode", "o")
108 call s:initVariable("g:NERDTreeMapChangeRoot", "C")
109 call s:initVariable("g:NERDTreeMapChdir", "cd")
110 call s:initVariable("g:NERDTreeMapCloseChildren", "X")
111 call s:initVariable("g:NERDTreeMapCloseDir", "x")
112 call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
113 call s:initVariable("g:NERDTreeMapMenu", "m")
114 call s:initVariable("g:NERDTreeMapHelp", "?")
115 call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
116 call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
117 call s:initVariable("g:NERDTreeMapJumpNextSibling", "<C-j>")
118 call s:initVariable("g:NERDTreeMapJumpParent", "p")
119 call s:initVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>")
120 call s:initVariable("g:NERDTreeMapJumpRoot", "P")
121 call s:initVariable("g:NERDTreeMapOpenExpl", "e")
122 call s:initVariable("g:NERDTreeMapOpenInTab", "t")
123 call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T")
124 call s:initVariable("g:NERDTreeMapOpenRecursively", "O")
125 call s:initVariable("g:NERDTreeMapOpenSplit", "i")
126 call s:initVariable("g:NERDTreeMapOpenVSplit", "s")
127 call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode)
128 call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit)
129 call s:initVariable("g:NERDTreeMapPreviewVSplit", "g" . NERDTreeMapOpenVSplit)
130 call s:initVariable("g:NERDTreeMapQuit", "q")
131 call s:initVariable("g:NERDTreeMapRefresh", "r")
132 call s:initVariable("g:NERDTreeMapRefreshRoot", "R")
133 call s:initVariable("g:NERDTreeMapToggleBookmarks", "B")
134 call s:initVariable("g:NERDTreeMapToggleFiles", "F")
135 call s:initVariable("g:NERDTreeMapToggleFilters", "f")
136 call s:initVariable("g:NERDTreeMapToggleHidden", "I")
137 call s:initVariable("g:NERDTreeMapToggleZoom", "A")
138 call s:initVariable("g:NERDTreeMapUpdir", "u")
139 call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U")
140
141 "SECTION: Script level variable declaration{{{2
142 if s:running_windows
143 let s:escape_chars = " `\|\"#%&,?()\*^<>"
144 else
145 let s:escape_chars = " \\`\|\"#%&,?()\*^<>"
146 endif
147 let s:NERDTreeBufName = 'NERD_tree_'
148
149 let s:tree_wid = 2
150 let s:tree_markup_reg = '^[ `|▼▶]*[\-+~ ]*'
151 let 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
154 let s:next_buffer_number = 1
155
156 " SECTION: Commands {{{1
157 "============================================================
158 "init the command that users start the nerd tree with
159 command! -n=? -complete=dir -bar NERDTree :call s:initNerdTree('<args>')
160 command! -n=? -complete=dir -bar NERDTreeToggle :call s:toggle('<args>')
161 command! -n=0 -bar NERDTreeClose :call s:closeTreeIfOpen()
162 command! -n=1 -complete=customlist,s:completeBookmarks -bar NERDTreeFromBookmark call s:initNerdTree('<args>')
163 command! -n=0 -bar NERDTreeMirror call s:initNerdTreeMirror()
164 command! -n=0 -bar NERDTreeFind call s:findAndRevealPath()
165 " SECTION: Auto commands {{{1
166 "============================================================
167 augroup 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
178 augroup END
179
180 if g:NERDTreeHijackNetrw
181 augroup NERDTreeHijackNetrw
182 autocmd VimEnter * silent! autocmd! FileExplorer
183 au BufEnter,VimEnter * call s:checkForBrowse(expand("<amatch>"))
184 augroup END
185 endif
186
187 "SECTION: Classes {{{1
188 "============================================================
189 "CLASS: Bookmark {{{2
190 "============================================================
191 let s:Bookmark = {}
192 " FUNCTION: Bookmark.activate() {{{3
193 function! 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
203 endfunction
204 " FUNCTION: Bookmark.AddBookmark(name, path) {{{3
205 " Class method to add a new bookmark to the list, if a previous bookmark exists
206 " with the same name, just update the path for that bookmark
207 function! s:Bookmark.AddBookmark(name, path)
208 for i in s:Bookmark.Bookmarks()
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()
216 endfunction
217 " Function: Bookmark.Bookmarks() {{{3
218 " Class method to get all bookmarks. Lazily initializes the bookmarks global
219 " variable
220 function! s:Bookmark.Bookmarks()
221 if !exists("g:NERDTreeBookmarks")
222 let g:NERDTreeBookmarks = []
223 endif
224 return g:NERDTreeBookmarks
225 endfunction
226 " Function: Bookmark.BookmarkExistsFor(name) {{{3
227 " class method that returns 1 if a bookmark with the given name is found, 0
228 " otherwise
229 function! 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
236 endfunction
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
240 function! 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 .'"'
247 endfunction
248 " Function: Bookmark.BookmarkNames() {{{3
249 " Class method to return an array of all bookmark names
250 function! s:Bookmark.BookmarkNames()
251 let names = []
252 for i in s:Bookmark.Bookmarks()
253 call add(names, i.name)
254 endfor
255 return names
256 endfunction
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
263 function! s:Bookmark.CacheBookmarks(silent)
264 if filereadable(g:NERDTreeBookmarksFile)
265 let g:NERDTreeBookmarks = []
266 let g:NERDTreeInvalidBookmarks = []
267 let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
268 let invalidBookmarksFound = 0
269 for i in bookmarkStrings
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
294 endfunction
295 " FUNCTION: Bookmark.compareTo(otherbookmark) {{{3
296 " Compare these two bookmarks for sorting purposes
297 function! s:Bookmark.compareTo(otherbookmark)
298 return a:otherbookmark.name < self.name
299 endfunction
300 " FUNCTION: Bookmark.ClearAll() {{{3
301 " Class method to delete all bookmarks.
302 function! s:Bookmark.ClearAll()
303 for i in s:Bookmark.Bookmarks()
304 call i.delete()
305 endfor
306 call s:Bookmark.Write()
307 endfunction
308 " FUNCTION: Bookmark.delete() {{{3
309 " Delete this bookmark. If the node for this bookmark is under the current
310 " root, then recache bookmarks for its Path object
311 function! s:Bookmark.delete()
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()
322 endfunction
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
329 function! 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
336 endfunction
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.
340 function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
341 let bookmark = s:Bookmark.BookmarkFor(a:name)
342 return bookmark.getNode(a:searchFromAbsoluteRoot)
343 endfunction
344 " FUNCTION: Bookmark.GetSelected() {{{3
345 " returns the Bookmark the cursor is over, or {}
346 function! s:Bookmark.GetSelected()
347 let line = getline(".")
348 let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
349 if name != line
350 try
351 return s:Bookmark.BookmarkFor(name)
352 catch /^NERDTree.BookmarkNotFoundError/
353 return {}
354 endtry
355 endif
356 return {}
357 endfunction
358
359 " Function: Bookmark.InvalidBookmarks() {{{3
360 " Class method to get all invalid bookmark strings read from the bookmarks
361 " file
362 function! s:Bookmark.InvalidBookmarks()
363 if !exists("g:NERDTreeInvalidBookmarks")
364 let g:NERDTreeInvalidBookmarks = []
365 endif
366 return g:NERDTreeInvalidBookmarks
367 endfunction
368 " FUNCTION: Bookmark.mustExist() {{{3
369 function! s:Bookmark.mustExist()
370 if !self.path.exists()
371 call s:Bookmark.CacheBookmarks(1)
372 throw "NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \"".
373 \ self.name ."\" points to a non existing location: \"". self.path.str()
374 endif
375 endfunction
376 " FUNCTION: Bookmark.New(name, path) {{{3
377 " Create a new bookmark object with the given name and path object
378 function! s:Bookmark.New(name, path)
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
387 endfunction
388 " FUNCTION: Bookmark.openInNewTab(options) {{{3
389 " Create a new bookmark object with the given name and path object
390 function! s:Bookmark.openInNewTab(options)
391 let currentTab = tabpagenr()
392 if self.path.isDirectory
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
402 endfunction
403 " Function: Bookmark.setPath(path) {{{3
404 " makes this bookmark point to the given path
405 function! s:Bookmark.setPath(path)
406 let self.path = a:path
407 endfunction
408 " Function: Bookmark.Sort() {{{3
409 " Class method that sorts all bookmarks
410 function! s:Bookmark.Sort()
411 let CompareFunc = function("s:compareBookmarks")
412 call sort(s:Bookmark.Bookmarks(), CompareFunc)
413 endfunction
414 " Function: Bookmark.str() {{{3
415 " Get the string that should be rendered in the view for this bookmark
416 function! s:Bookmark.str()
417 let pathStrMaxLen = winwidth(s:getTreeWinNum()) - 4 - len(self.name)
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
427 endfunction
428 " FUNCTION: Bookmark.toRoot() {{{3
429 " Make the node for this bookmark the new tree root
430 function! 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
441 endfunction
442 " FUNCTION: Bookmark.ToRoot(name) {{{3
443 " Make the node for this bookmark the new tree root
444 function! s:Bookmark.ToRoot(name)
445 let bookmark = s:Bookmark.BookmarkFor(a:name)
446 call bookmark.toRoot()
447 endfunction
448
449
450 "FUNCTION: Bookmark.validate() {{{3
451 function! 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
460 endfunction
461
462 " Function: Bookmark.Write() {{{3
463 " Class method to write all bookmarks to the bookmarks file
464 function! s:Bookmark.Write()
465 let bookmarkStrings = []
466 for i in s:Bookmark.Bookmarks()
467 call add(bookmarkStrings, i.name . ' ' . i.path.str())
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)
477 endfunction
478 "CLASS: KeyMap {{{2
479 "============================================================
480 let s:KeyMap = {}
481 "FUNCTION: KeyMap.All() {{{3
482 function! s:KeyMap.All()
483 if !exists("s:keyMaps")
484 let s:keyMaps = []
485 endif
486 return s:keyMaps
487 endfunction
488
489 "FUNCTION: KeyMap.BindAll() {{{3
490 function! s:KeyMap.BindAll()
491 for i in s:KeyMap.All()
492 call i.bind()
493 endfor
494 endfunction
495
496 "FUNCTION: KeyMap.bind() {{{3
497 function! s:KeyMap.bind()
498 exec "nnoremap <silent> <buffer> ". self.key ." :call ". self.callback ."()<cr>"
499 endfunction
500
501 "FUNCTION: KeyMap.Create(options) {{{3
502 function! s:KeyMap.Create(options)
503 let newKeyMap = copy(self)
504 let newKeyMap.key = a:options['key']
505 let newKeyMap.quickhelpText = a:options['quickhelpText']
506 let newKeyMap.callback = a:options['callback']
507 call add(s:KeyMap.All(), newKeyMap)
508 endfunction
509 "CLASS: MenuController {{{2
510 "============================================================
511 let s:MenuController = {}
512 "FUNCTION: MenuController.New(menuItems) {{{3
513 "create a new menu controller that operates on the given menu items
514 function! s:MenuController.New(menuItems)
515 let newMenuController = copy(self)
516 if a:menuItems[0].isSeparator()
517 let newMenuController.menuItems = a:menuItems[1:-1]
518 else
519 let newMenuController.menuItems = a:menuItems
520 endif
521 return newMenuController
522 endfunction
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
527 function! 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
548 endfunction
549
550 "FUNCTION: MenuController._echoPrompt() {{{3
551 function! 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
562 endfunction
563
564 "FUNCTION: MenuController._current(key) {{{3
565 "get the MenuItem that is curently selected
566 function! s:MenuController._current()
567 return self.menuItems[self.selection]
568 endfunction
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
573 function! 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
594 endfunction
595
596 "FUNCTION: MenuController._allIndexesFor(shortcut) {{{3
597 "get indexes to all menu items with the given shortcut
598 function! 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
608 endfunction
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
613 function! s:MenuController._nextIndexFor(shortcut)
614 for i in range(self.selection+1, len(self.menuItems)-1)
615 if self.menuItems[i].shortcut == a:shortcut
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
627 endfunction
628
629 "FUNCTION: MenuController._setCmdheight() {{{3
630 "sets &cmdheight to whatever is needed to display the menu
631 function! s:MenuController._setCmdheight()
632 let &cmdheight = len(self.menuItems) + 3
633 endfunction
634
635 "FUNCTION: MenuController._saveOptions() {{{3
636 "set any vim options that are required to make the menu work (saving their old
637 "values)
638 function! s:MenuController._saveOptions()
639 let self._oldLazyredraw = &lazyredraw
640 let self._oldCmdheight = &cmdheight
641 set nolazyredraw
642 call self._setCmdheight()
643 endfunction
644
645 "FUNCTION: MenuController._restoreOptions() {{{3
646 "restore the options we saved in _saveOptions()
647 function! s:MenuController._restoreOptions()
648 let &cmdheight = self._oldCmdheight
649 let &lazyredraw = self._oldLazyredraw
650 endfunction
651
652 "FUNCTION: MenuController._cursorDown() {{{3
653 "move the cursor to the next menu item, skipping separators
654 function! 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
667 endfunction
668
669 "FUNCTION: MenuController._cursorUp() {{{3
670 "move the cursor to the previous menu item, skipping separators
671 function! 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
684 endfunction
685
686 "CLASS: MenuItem {{{2
687 "============================================================
688 let s:MenuItem = {}
689 "FUNCTION: MenuItem.All() {{{3
690 "get all top level menu items
691 function! s:MenuItem.All()
692 if !exists("s:menuItems")
693 let s:menuItems = []
694 endif
695 return s:menuItems
696 endfunction
697
698 "FUNCTION: MenuItem.AllEnabled() {{{3
699 "get all top level menu items that are currently enabled
700 function! 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
708 endfunction
709
710 "FUNCTION: MenuItem.Create(options) {{{3
711 "make a new menu item and add it to the global list
712 function! s:MenuItem.Create(options)
713 let newMenuItem = copy(self)
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
736 endfunction
737
738 "FUNCTION: MenuItem.CreateSeparator(options) {{{3
739 "make a new separator menu item and add it to the global list
740 function! s:MenuItem.CreateSeparator(options)
741 let standard_options = { 'text': '--------------------',
742 \ 'shortcut': -1,
743 \ 'callback': -1 }
744 let options = extend(a:options, standard_options, "force")
745
746 return s:MenuItem.Create(options)
747 endfunction
748
749 "FUNCTION: MenuItem.CreateSubmenu(options) {{{3
750 "make a new submenu and add it to global list
751 function! s:MenuItem.CreateSubmenu(options)
752 let standard_options = { 'callback': -1 }
753 let options = extend(a:options, standard_options, "force")
754
755 return s:MenuItem.Create(options)
756 endfunction
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
763 function! s:MenuItem.enabled()
764 if self.isActiveCallback != -1
765 return {self.isActiveCallback}()
766 endif
767 return 1
768 endfunction
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
774 function! 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
783 endfunction
784
785 "FUNCTION: MenuItem.isSeparator() {{{3
786 "return 1 if this menuitem is a separator
787 function! s:MenuItem.isSeparator()
788 return self.callback == -1 && self.children == []
789 endfunction
790
791 "FUNCTION: MenuItem.isSubmenu() {{{3
792 "return 1 if this menuitem is a submenu
793 function! s:MenuItem.isSubmenu()
794 return self.callback == -1 && !empty(self.children)
795 endfunction
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 "============================================================
802 let s:TreeFileNode = {}
803 "FUNCTION: TreeFileNode.activate(forceKeepWinOpen) {{{3
804 function! s:TreeFileNode.activate(forceKeepWinOpen)
805 call self.open()
806 if !a:forceKeepWinOpen
807 call s:closeTreeIfQuitOnOpen()
808 end
809 endfunction
810 "FUNCTION: TreeFileNode.bookmark(name) {{{3
811 "bookmark this node with a:name
812 function! 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
830 endfunction
831 "FUNCTION: TreeFileNode.cacheParent() {{{3
832 "initializes self.parent if it isnt already
833 function! s:TreeFileNode.cacheParent()
834 if empty(self.parent)
835 let parentPath = self.path.getParent()
836 if parentPath.equals(self.path)
837 throw "NERDTree.CannotCacheParentError: already at root"
838 endif
839 let self.parent = s:TreeFileNode.New(parentPath)
840 endif
841 endfunction
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
850 function! s:compareNodes(n1, n2)
851 return a:n1.path.compareTo(a:n2.path)
852 endfunction
853
854 "FUNCTION: TreeFileNode.clearBoomarks() {{{3
855 function! 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()
862 endfunction
863 "FUNCTION: TreeFileNode.copy(dest) {{{3
864 function! s:TreeFileNode.copy(dest)
865 call self.path.copy(a:dest)
866 let newPath = s:Path.New(a:dest)
867 let parent = b:NERDTreeRoot.findNode(newPath.getParent())
868 if !empty(parent)
869 call parent.refresh()
870 endif
871 return parent.findNode(newPath)
872 endfunction
873
874 "FUNCTION: TreeFileNode.delete {{{3
875 "Removes this node from the tree and calls the Delete method for its path obj
876 function! s:TreeFileNode.delete()
877 call self.path.delete()
878 call self.parent.removeChild(self)
879 endfunction
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
888 function! s:TreeFileNode.displayString()
889 return self.path.displayString()
890 endfunction
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
902 function! s:TreeFileNode.equals(treenode)
903 return self.path.str() ==# a:treenode.path.str()
904 endfunction
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
912 function! s:TreeFileNode.findNode(path)
913 if a:path.equals(self.path)
914 return self
915 endif
916 return {}
917 endfunction
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
928 function! 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 {}
942 endfunction
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
952 function! 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 {}
980 endfunction
981
982 "FUNCTION: TreeFileNode.getLineNum(){{{3
983 "returns the line number this node is rendered on, or -1 if it isnt rendered
984 function! s:TreeFileNode.getLineNum()
985 "if the node is the root then return the root line no.
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
1029 endfunction
1030
1031 "FUNCTION: TreeFileNode.GetRootForTab(){{{3
1032 "get the root node for this tab
1033 function! s:TreeFileNode.GetRootForTab()
1034 if s:treeExistsForTab()
1035 return getbufvar(t:NERDTreeBufName, 'NERDTreeRoot')
1036 end
1037 return {}
1038 endfunction
1039 "FUNCTION: TreeFileNode.GetRootLineNum(){{{3
1040 "gets the line number of the root node
1041 function! s:TreeFileNode.GetRootLineNum()
1042 let rootLine = 1
1043 while getline(rootLine) !~ '^\(/\|<\)'
1044 let rootLine = rootLine + 1
1045 endwhile
1046 return rootLine
1047 endfunction
1048
1049 "FUNCTION: TreeFileNode.GetSelected() {{{3
1050 "gets the treenode that the cursor is currently over
1051 function! 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
1061 endfunction
1062 "FUNCTION: TreeFileNode.isVisible() {{{3
1063 "returns 1 if this node should be visible according to the tree filters and
1064 "hidden file filters (and their on/off status)
1065 function! s:TreeFileNode.isVisible()
1066 return !self.path.ignore()
1067 endfunction
1068 "FUNCTION: TreeFileNode.isRoot() {{{3
1069 "returns 1 if this node is b:NERDTreeRoot
1070 function! s:TreeFileNode.isRoot()
1071 if !s:treeExistsForBuf()
1072 throw "NERDTree.NoTreeError: No tree exists for the current buffer"
1073 endif
1074
1075 return self.equals(b:NERDTreeRoot)
1076 endfunction
1077
1078 "FUNCTION: TreeFileNode.makeRoot() {{{3
1079 "Make this node the root of the tree
1080 function! s:TreeFileNode.makeRoot()
1081 if self.path.isDirectory
1082 let b:NERDTreeRoot = self
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
1094 endfunction
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
1100 function! 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
1109 endfunction
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
1117 function! 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
1147 endfunction
1148 "FUNCTION: TreeFileNode.openSplit() {{{3
1149 "Open this node in a new window
1150 function! 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
1213 endfunction
1214 "FUNCTION: TreeFileNode.openVSplit() {{{3
1215 "Open this node in a new vertical window
1216 function! s:TreeFileNode.openVSplit()
1217 if b:NERDTreeType ==# "secondary"
1218 exec "vnew " . self.path.str({'format': 'Edit'})
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')
1234 endfunction
1235 "FUNCTION: TreeFileNode.openInNewTab(options) {{{3
1236 function! 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
1249 endfunction
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
1257 function! 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
1275 endfunction
1276
1277 "FUNCTION: TreeFileNode.refresh() {{{3
1278 function! s:TreeFileNode.refresh()
1279 call self.path.refresh()
1280 endfunction
1281 "FUNCTION: TreeFileNode.rename() {{{3
1282 "Calls the rename method for this nodes path obj
1283 function! s:TreeFileNode.rename(newName)
1284 let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
1285 call self.path.rename(newName)
1286 call self.parent.removeChild(self)
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
1295 endfunction
1296 "FUNCTION: TreeFileNode.renderToString {{{3
1297 "returns a string representation for this tree to be rendered in the view
1298 function! s:TreeFileNode.renderToString()
1299 return self._renderToString(0, 0, [], self.getChildCount() ==# 1)
1300 endfunction
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
1310 function! 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
1358 endfunction
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 "============================================================
1364 let s:TreeDirNode = copy(s:TreeFileNode)
1365 "FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{3
1366 "class method that returns the highest cached ancestor of the current root
1367 function! s:TreeDirNode.AbsoluteTreeRoot()
1368 let currentNode = b:NERDTreeRoot
1369 while currentNode.parent != {}
1370 let currentNode = currentNode.parent
1371 endwhile
1372 return currentNode
1373 endfunction
1374 "FUNCTION: TreeDirNode.activate(forceKeepWinOpen) {{{3
1375 unlet s:TreeDirNode.activate
1376 function! s:TreeDirNode.activate(forceKeepWinOpen)
1377 call self.toggleOpen()
1378 call s:renderView()
1379 call self.putCursorHere(0, 0)
1380 endfunction
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
1387 function! 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
1394 endfunction
1395
1396 "FUNCTION: TreeDirNode.close() {{{3
1397 "Closes this directory
1398 function! s:TreeDirNode.close()
1399 let self.isOpen = 0
1400 endfunction
1401
1402 "FUNCTION: TreeDirNode.closeChildren() {{{3
1403 "Closes all the child dir nodes of this node
1404 function! s:TreeDirNode.closeChildren()
1405 for i in self.children
1406 if i.path.isDirectory
1407 call i.close()
1408 call i.closeChildren()
1409 endif
1410 endfor
1411 endfunction
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
1423 function! s:TreeDirNode.createChild(path, inOrder)
1424 let newTreeNode = s:TreeFileNode.New(a:path)
1425 call self.addChild(newTreeNode, a:inOrder)
1426 return newTreeNode
1427 endfunction
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
1434 unlet s:TreeDirNode.findNode
1435 function! 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 {}
1452 endfunction
1453 "FUNCTION: TreeDirNode.getChildCount() {{{3
1454 "Returns the number of children this node has
1455 function! s:TreeDirNode.getChildCount()
1456 return len(self.children)
1457 endfunction
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
1467 function! 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
1479 endfunction
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.
1487 function! s:TreeDirNode.getChildByIndex(indx, visible)
1488 let array_to_search = a:visible? self.getVisibleChildren() : self.children
1489 if a:indx > len(array_to_search)
1490 throw "NERDTree.InvalidArgumentsError: Index is out of bounds."
1491 endif
1492 return array_to_search[a:indx]
1493 endfunction
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
1503 function! 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
1524 endfunction
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
1529 unlet s:TreeDirNode.GetSelected
1530 function! s:TreeDirNode.GetSelected()
1531 let currentDir = s:TreeFileNode.GetSelected()
1532 if currentDir != {} && !currentDir.isRoot()
1533 if currentDir.path.isDirectory ==# 0
1534 let currentDir = currentDir.parent
1535 endif
1536 endif
1537 return currentDir
1538 endfunction
1539 "FUNCTION: TreeDirNode.getVisibleChildCount() {{{3
1540 "Returns the number of visible children this node has
1541 function! s:TreeDirNode.getVisibleChildCount()
1542 return len(self.getVisibleChildren())
1543 endfunction
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
1550 function! 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
1558 endfunction
1559
1560 "FUNCTION: TreeDirNode.hasVisibleChildren() {{{3
1561 "returns 1 if this node has any childre, 0 otherwise..
1562 function! s:TreeDirNode.hasVisibleChildren()
1563 return self.getVisibleChildCount() != 0
1564 endfunction
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
1574 function! 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()
1616 endfunction
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
1622 unlet s:TreeDirNode.New
1623 function! s:TreeDirNode.New(path)
1624 if a:path.isDirectory != 1
1625 throw "NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object."
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
1637 endfunction
1638 "FUNCTION: TreeDirNode.open() {{{3
1639 "Reads in all this nodes children
1640 "
1641 "Return: the number of child nodes read
1642 unlet s:TreeDirNode.open
1643 function! s:TreeDirNode.open()
1644 let self.isOpen = 1
1645 if self.children ==# []
1646 return self._initChildren(0)
1647 else
1648 return 0
1649 endif
1650 endfunction
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)
1655 function! s:TreeDirNode.openExplorer()
1656 let oldwin = winnr()
1657 call s:exec('wincmd p')
1658 if oldwin ==# winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2)
1659 call s:exec('wincmd p')
1660 call self.openSplit()
1661 else
1662 exec ("silent edit " . self.path.str({'format': 'Edit'}))
1663 endif
1664 endfunction
1665 "FUNCTION: TreeDirNode.openInNewTab(options) {{{3
1666 unlet s:TreeDirNode.openInNewTab
1667 function! 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
1680 endfunction
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.
1687 function! s:TreeDirNode.openRecursively()
1688 call self._openRecursively2(1)
1689 endfunction
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
1698 function! 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
1711 endfunction
1712
1713 "FUNCTION: TreeDirNode.refresh() {{{3
1714 unlet s:TreeDirNode.refresh
1715 function! 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
1763 endfunction
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
1768 function! s:TreeDirNode.reveal(path)
1769 if !a:path.isUnder(self.path)
1770 throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str()
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)
1789 endfunction
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
1798 function! s:TreeDirNode.removeChild(treenode)
1799 for i in range(0, self.getChildCount()-1)
1800 if self.children[i].equals(a:treenode)
1801 call remove(self.children, i)
1802 return
1803 endif
1804 endfor
1805
1806 throw "NERDTree.ChildNotFoundError: child node was not found"
1807 endfunction
1808
1809 "FUNCTION: TreeDirNode.sortChildren() {{{3
1810 "
1811 "Sorts the children of this node according to alphabetical order and the
1812 "directory priority.
1813 "
1814 function! s:TreeDirNode.sortChildren()
1815 let CompareFunc = function("s:compareNodes")
1816 call sort(self.children, CompareFunc)
1817 endfunction
1818
1819 "FUNCTION: TreeDirNode.toggleOpen() {{{3
1820 "Opens this directory if it is closed and vice versa
1821 function! s:TreeDirNode.toggleOpen()
1822 if self.isOpen ==# 1
1823 call self.close()
1824 else
1825 call self.open()
1826 endif
1827 endfunction
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
1836 function! s:TreeDirNode.transplantChild(newNode)
1837 for i in range(0, self.getChildCount()-1)
1838 if self.children[i].equals(a:newNode)
1839 let self.children[i] = a:newNode
1840 let a:newNode.parent = self
1841 break
1842 endif
1843 endfor
1844 endfunction
1845 "============================================================
1846 "CLASS: Path {{{2
1847 "============================================================
1848 let s:Path = {}
1849 "FUNCTION: Path.AbsolutePathFor(str) {{{3
1850 function! 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
1864 endfunction
1865 "FUNCTION: Path.bookmarkNames() {{{3
1866 function! s:Path.bookmarkNames()
1867 if !exists("self._bookmarkNames")
1868 call self.cacheDisplayString()
1869 endif
1870 return self._bookmarkNames
1871 endfunction
1872 "FUNCTION: Path.cacheDisplayString() {{{3
1873 function! 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
1897 endfunction
1898 "FUNCTION: Path.changeToDir() {{{3
1899 function! s:Path.changeToDir()
1900 let dir = self.str({'format': 'Cd'})
1901 if self.isDirectory ==# 0
1902 let dir = self.getParent().str({'format': 'Cd'})
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
1911 endfunction
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
1923 function! 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
1951 endfunction
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
1963 function! s:Path.Create(fullpath)
1964 "bail if the a:fullpath already exists
1965 if isdirectory(a:fullpath) || filereadable(a:fullpath)
1966 throw "NERDTree.CreatePathError: Directory Exists: '" . a:fullpath . "'"
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)
1987 endfunction
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
1995 function! 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
2007 endfunction
2008
2009 "FUNCTION: Path.CopyingSupported() {{{3
2010 "
2011 "returns 1 if copying is supported for this OS
2012 function! s:Path.CopyingSupported()
2013 return exists('g:NERDTreeCopyCmd')
2014 endfunction
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
2024 function! 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
2035 endfunction
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
2043 function! 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
2064 endfunction
2065
2066 "FUNCTION: Path.displayString() {{{3
2067 "
2068 "Returns a string that specifies how the path should be represented as a
2069 "string
2070 function! s:Path.displayString()
2071 if self.cachedDisplayString ==# ""
2072 call self.cacheDisplayString()
2073 endif
2074
2075 return self.cachedDisplayString
2076 endfunction
2077 "FUNCTION: Path.extractDriveLetter(fullpath) {{{3
2078 "
2079 "If running windows, cache the drive letter for this path
2080 function! s:Path.extractDriveLetter(fullpath)
2081 if s:running_windows
2082 let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
2083 else
2084 let self.drive = ''
2085 endif
2086
2087 endfunction
2088 "FUNCTION: Path.exists() {{{3
2089 "return 1 if this path points to a location that is readable or is a directory
2090 function! s:Path.exists()
2091 let p = self.str()
2092 return filereadable(p) || isdirectory(p)
2093 endfunction
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
2100 function! s:Path.getDir()
2101 if self.isDirectory
2102 return self
2103 else
2104 return self.getParent()
2105 endif
2106 endfunction
2107 "FUNCTION: Path.getParent() {{{3
2108 "
2109 "Returns a new path object for this paths parent
2110 "
2111 "Return:
2112 "a new Path object
2113 function! 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)
2121 endfunction
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.
2129 function! 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
2138 endfunction
2139
2140 "FUNCTION: Path.getSortOrderIndex() {{{3
2141 "returns the index of the pattern in g:NERDTreeSortOrder that this path matches
2142 function! 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
2151 endfunction
2152
2153 "FUNCTION: Path.ignore() {{{3
2154 "returns true if this path should be ignored
2155 function! s:Path.ignore()
2156 let lastPathComponent = self.getLastPathComponent(0)
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
2177 endfunction
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
2183 function! 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
2191 endfunction
2192
2193 "FUNCTION: Path.JoinPathStrings(...) {{{3
2194 function! 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, '/')
2200 endfunction
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
2209 function! s:Path.equals(path)
2210 return self.str() ==# a:path.str()
2211 endfunction
2212
2213 "FUNCTION: Path.New() {{{3
2214 "The Constructor for the Path object
2215 function! 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
2223 endfunction
2224
2225 "FUNCTION: Path.Slash() {{{3
2226 "return the slash to use for the current OS
2227 function! s:Path.Slash()
2228 return s:running_windows ? '\' : '/'
2229 endfunction
2230
2231 "FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
2232 "
2233 "
2234 "Throws NERDTree.Path.InvalidArguments exception.
2235 function! 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
2283 endfunction
2284
2285 "FUNCTION: Path.refresh() {{{3
2286 function! s:Path.refresh()
2287 call self.readInfoFromDisk(self.str())
2288 call self.cacheDisplayString()
2289 endfunction
2290
2291 "FUNCTION: Path.rename() {{{3
2292 "
2293 "Renames this node on the filesystem
2294 function! 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()
2310 endfunction
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.
2334 function! 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
2361 endfunction
2362
2363 "FUNCTION: Path._strForUI() {{{3
2364 function! s:Path._strForUI()
2365 let toReturn = '/' . join(self.pathSegments, '/')
2366 if self.isDirectory && toReturn != '/'
2367 let toReturn = toReturn . '/'
2368 endif
2369 return toReturn
2370 endfunction
2371
2372 "FUNCTION: Path._strForCd() {{{3
2373 "
2374 " returns a string that can be used with :cd
2375 function! s:Path._strForCd()
2376 return escape(self.str(), s:escape_chars)
2377 endfunction
2378 "FUNCTION: Path._strForEdit() {{{3
2379 "
2380 "Return: the string for this path that is suitable to be used with the :edit
2381 "command
2382 function! 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
2406 endfunction
2407 "FUNCTION: Path._strForGlob() {{{3
2408 function! 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
2422 endfunction
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
2428 function! 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())
2437 endfunction
2438
2439 "FUNCTION: Path.strTrunk() {{{3
2440 "Gets the path without the last segment on the end.
2441 function! s:Path.strTrunk()
2442 return self.drive . '/' . join(self.pathSegments[0:-2], '/')
2443 endfunction
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
2452 function! 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
2466 endfunction
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
2477 function! 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
2492 endfunction " >>>
2493 "FUNCTION: s:checkForBrowse(dir) {{{2
2494 "inits a secondary nerd tree in the current buffer if appropriate
2495 function! s:checkForBrowse(dir)
2496 if a:dir != '' && isdirectory(a:dir)
2497 call s:initNerdTreeInPlace(a:dir)
2498 endif
2499 endfunction
2500 "FUNCTION: s:compareBookmarks(first, second) {{{2
2501 "Compares two bookmarks
2502 function! s:compareBookmarks(first, second)
2503 return a:first.compareTo(a:second)
2504 endfunction
2505
2506 " FUNCTION: s:completeBookmarks(A,L,P) {{{2
2507 " completion function for the bookmark commands
2508 function! s:completeBookmarks(A,L,P)
2509 return filter(s:Bookmark.BookmarkNames(), 'v:val =~ "^' . a:A . '"')
2510 endfunction
2511 " FUNCTION: s:exec(cmd) {{{2
2512 " same as :exec cmd but eventignore=all is set for the duration
2513 function! s:exec(cmd)
2514 let old_ei = &ei
2515 set ei=all
2516 exec a:cmd
2517 let &ei = old_ei
2518 endfunction
2519 " FUNCTION: s:findAndRevealPath() {{{2
2520 function! 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)
2541 endfunction
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
2548 function! 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)
2600 endfunction
2601
2602 "FUNCTION: s:initNerdTreeInPlace(dir) {{{2
2603 function! 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()
2663 endfunction
2664 " FUNCTION: s:initNerdTreeMirror() {{{2
2665 function! 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
2716 endfunction
2717 " FUNCTION: s:nextBufferName() {{{2
2718 " returns the buffer name for the next nerd tree
2719 function! s:nextBufferName()
2720 let name = s:NERDTreeBufName . s:next_buffer_number
2721 let s:next_buffer_number += 1
2722 return name
2723 endfunction
2724 " FUNCTION: s:tabpagevar(tabnr, var) {{{2
2725 function! 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
2740 endfunction
2741 " Function: s:treeExistsForBuffer() {{{2
2742 " Returns 1 if a nerd tree root exists in the current buffer
2743 function! s:treeExistsForBuf()
2744 return exists("b:NERDTreeRoot")
2745 endfunction
2746 " Function: s:treeExistsForTab() {{{2
2747 " Returns 1 if a nerd tree root exists in the current tab
2748 function! s:treeExistsForTab()
2749 return exists("t:NERDTreeBufName")
2750 endfunction
2751 " Function: s:unique(list) {{{2
2752 " returns a:list without duplicates
2753 function! 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
2761 endfunction
2762 " SECTION: Public API {{{1
2763 "============================================================
2764 let g:NERDTreePath = s:Path
2765 let g:NERDTreeDirNode = s:TreeDirNode
2766 let g:NERDTreeFileNode = s:TreeFileNode
2767 let g:NERDTreeBookmark = s:Bookmark
2768
2769 function! NERDTreeAddMenuItem(options)
2770 call s:MenuItem.Create(a:options)
2771 endfunction
2772
2773 function! NERDTreeAddMenuSeparator(...)
2774 let opts = a:0 ? a:1 : {}
2775 call s:MenuItem.CreateSeparator(opts)
2776 endfunction
2777
2778 function! NERDTreeAddSubmenu(options)
2779 return s:MenuItem.Create(a:options)
2780 endfunction
2781
2782 function! NERDTreeAddKeyMap(options)
2783 call s:KeyMap.Create(a:options)
2784 endfunction
2785
2786 function! NERDTreeRender()
2787 call s:renderView()
2788 endfunction
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)
2795 function! s:centerView()
2796 if g:NERDTreeAutoCenter
2797 let current_line = winline()
2798 let lines_to_top = current_line
2799 let lines_to_bottom = winheight(s:getTreeWinNum()) - current_line
2800 if lines_to_top < g:NERDTreeAutoCenterThreshold || lines_to_bottom < g:NERDTreeAutoCenterThreshold
2801 normal! zz
2802 endif
2803 endif
2804 endfunction
2805 "FUNCTION: s:closeTree() {{{2
2806 "Closes the primary NERD tree window for this tab
2807 function! 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
2827 endfunction
2828
2829 "FUNCTION: s:closeTreeIfOpen() {{{2
2830 "Closes the NERD tree window if it is open
2831 function! s:closeTreeIfOpen()
2832 if s:isTreeOpen()
2833 call s:closeTree()
2834 endif
2835 endfunction
2836 "FUNCTION: s:closeTreeIfQuitOnOpen() {{{2
2837 "Closes the NERD tree window if the close on open option is set
2838 function! s:closeTreeIfQuitOnOpen()
2839 if g:NERDTreeQuitOnOpen && s:isTreeOpen()
2840 call s:closeTree()
2841 endif
2842 endfunction
2843 "FUNCTION: s:createTreeWin() {{{2
2844 "Inits the NERD tree window. ie. opens it, sizes it, sets all the local
2845 "options etc
2846 function! 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
2889 endfunction
2890
2891 "FUNCTION: s:dumpHelp {{{2
2892 "prints out the quick help
2893 function! 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
2995 endfunction
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
3001 function! s:echo(msg)
3002 redraw
3003 echomsg "NERDTree: " . a:msg
3004 endfunction
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
3009 function! s:echoWarning(msg)
3010 echohl warningmsg
3011 call s:echo(a:msg)
3012 echohl normal
3013 endfunction
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
3018 function! s:echoError(msg)
3019 echohl errormsg
3020 call s:echo(a:msg)
3021 echohl normal
3022 endfunction
3023 "FUNCTION: s:firstUsableWindow(){{{2
3024 "find the window number of the first normal window
3025 function! 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
3038 endfunction
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
3049 function! 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
3104 endfunction
3105
3106 "FUNCTION: s:getTreeWinNum() {{{2
3107 "gets the nerd tree window number for this tab
3108 function! s:getTreeWinNum()
3109 if exists("t:NERDTreeBufName")
3110 return bufwinnr(t:NERDTreeBufName)
3111 else
3112 return -1
3113 endif
3114 endfunction
3115 "FUNCTION: s:indentLevelFor(line) {{{2
3116 function! s:indentLevelFor(line)
3117 return match(a:line, '[^ \-+~`|]') / s:tree_wid
3118 endfunction
3119 "FUNCTION: s:isTreeOpen() {{{2
3120 function! s:isTreeOpen()
3121 return s:getTreeWinNum() != -1
3122 endfunction
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
3129 function! 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
3152 endfunction
3153
3154 " FUNCTION: s:jumpToChild(direction) {{{2
3155 " Args:
3156 " direction: 0 if going to first child, 1 if going to last
3157 function! s:jumpToChild(direction)
3158 let currentNode = s:TreeFileNode.GetSelected()
3159 if currentNode ==# {} || currentNode.isRoot()
3160 call s:echo("cannot jump to " . (a:direction ? "last" : "first") . " child")
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()
3182 endfunction
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
3193 function! s:promptToDelBuffer(bufnum, msg)
3194 echo a:msg
3195 if nr2char(getchar()) ==# 'y'
3196 exec "silent bdelete! " . a:bufnum
3197 endif
3198 endfunction
3199
3200 "FUNCTION: s:putCursorOnBookmarkTable(){{{2
3201 "Places the cursor at the top of the bookmarks table
3202 function! s:putCursorOnBookmarkTable()
3203 if !b:NERDTreeShowBookmarks
3204 throw "NERDTree.IllegalOperationError: cant find bookmark table, bookmarks arent active"
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)
3217 endfunction
3218
3219 "FUNCTION: s:putCursorInTreeWin(){{{2
3220 "Places the cursor in the nerd tree window
3221 function! 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")
3227 endfunction
3228
3229 "FUNCTION: s:renderBookmarks {{{2
3230 function! 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("."))
3242 endfunction
3243 "FUNCTION: s:renderView {{{2
3244 "The entry function for rendering the tree
3245 function! 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
3294 endfunction
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
3299 function! 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
3313 endfunction
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
3320 function! 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
3332 endfunction
3333
3334 "FUNCTION: s:saveScreenState() {{{2
3335 "Saves the current cursor position in the current buffer and the window
3336 "scroll position
3337 function! 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
3347 endfunction
3348
3349 "FUNCTION: s:setupStatusline() {{{2
3350 function! s:setupStatusline()
3351 if g:NERDTreeStatusline != -1
3352 let &l:statusline = g:NERDTreeStatusline
3353 endif
3354 endfunction
3355 "FUNCTION: s:setupSyntaxHighlighting() {{{2
3356 function! s:setupSyntaxHighlighting()
3357 "treeFlags are syntax items that should be invisible, but give clues as to
3358 "how things should be highlighted
3359 syn match treeFlag #\~#
3360 syn match treeFlag #\[RO\]#
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
3444 endfunction
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)
3453 function! 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
3481 endfunction
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.
3490 function! 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
3504 endfunction
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
3513 function! 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
3527 endfunction
3528
3529 "FUNCTION: s:bindMappings() {{{2
3530 function! s:bindMappings()
3531 " set up mappings and commands for this buffer
3532 nnoremap <silent> <buffer> <middlerelease> :call <SID>handleMiddleMouse()<cr>
3533 nnoremap <silent> <buffer> <leftrelease> <leftrelease>:call <SID>checkForActivate()<cr>
3534 nnoremap <silent> <buffer> <2-leftmouse> :call <SID>activateNode(0)<cr>
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()
3596 endfunction
3597
3598 " FUNCTION: s:bookmarkNode(name) {{{2
3599 " Associate the current node with the given name
3600 function! s:bookmarkNode(name)
3601 let currentNode = s:TreeFileNode.GetSelected()
3602 if currentNode != {}
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
3612 endfunction
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)
3617 function! s:checkForActivate()
3618 let currentNode = s:TreeFileNode.GetSelected()
3619 if currentNode != {}
3620 call s:activateNode(0)
3621 return
3622 endif
3623 endfunction
3624
3625 " FUNCTION: s:chCwd() {{{2
3626 function! 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
3638 endfunction
3639
3640 " FUNCTION: s:chRoot() {{{2
3641 " changes the current root to the selected one
3642 function! 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)
3652 endfunction
3653
3654 " FUNCTION: s:clearBookmarks(bookmarks) {{{2
3655 function! s:clearBookmarks(bookmarks)
3656 if a:bookmarks ==# ''
3657 let currentNode = s:TreeFileNode.GetSelected()
3658 if currentNode != {}
3659 call currentNode.clearBoomarks()
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()
3668 endfunction
3669 " FUNCTION: s:closeChildren() {{{2
3670 " closes all childnodes of the current node
3671 function! s:closeChildren()
3672 let currentNode = s:TreeDirNode.GetSelected()
3673 if currentNode ==# {}
3674 call s:echo("Select a node first")
3675 return
3676 endif
3677
3678 call currentNode.closeChildren()
3679 call s:renderView()
3680 call currentNode.putCursorHere(0, 0)
3681 endfunction
3682 " FUNCTION: s:closeCurrentDir() {{{2
3683 " closes the parent dir of the current node
3684 function! 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
3699 endfunction
3700 " FUNCTION: s:closeTreeWindow() {{{2
3701 " close the tree window
3702 function! s:closeTreeWindow()
3703 if b:NERDTreeType ==# "secondary" && b:NERDTreePreviousBuf != -1
3704 exec "buffer " . b:NERDTreePreviousBuf
3705 else
3706 if winnr("$") > 1
3707 call s:closeTree()
3708 else
3709 call s:echo("Cannot close last window")
3710 endif
3711 endif
3712 endfunction
3713 " FUNCTION: s:deleteBookmark() {{{2
3714 " if the cursor is on a bookmark, prompt to delete
3715 function! s:deleteBookmark()
3716 let bookmark = s:Bookmark.GetSelected()
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
3736 endfunction
3737
3738 " FUNCTION: s:displayHelp() {{{2
3739 " toggles the help display
3740 function! s:displayHelp()
3741 let b:treeShowHelp = b:treeShowHelp ? 0 : 1
3742 call s:renderView()
3743 call s:centerView()
3744 endfunction
3745
3746 " FUNCTION: s:handleMiddleMouse() {{{2
3747 function! 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
3759 endfunction
3760
3761
3762 " FUNCTION: s:jumpToFirstChild() {{{2
3763 " wrapper for the jump to child method
3764 function! s:jumpToFirstChild()
3765 call s:jumpToChild(0)
3766 endfunction
3767
3768 " FUNCTION: s:jumpToLastChild() {{{2
3769 " wrapper for the jump to child method
3770 function! s:jumpToLastChild()
3771 call s:jumpToChild(1)
3772 endfunction
3773
3774 " FUNCTION: s:jumpToParent() {{{2
3775 " moves the cursor to the parent of the current node
3776 function! s:jumpToParent()
3777 let currentNode = s:TreeFileNode.GetSelected()
3778 if !empty(currentNode)
3779 if !empty(currentNode.parent)
3780 call currentNode.parent.putCursorHere(1, 0)
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
3788 endfunction
3789
3790 " FUNCTION: s:jumpToRoot() {{{2
3791 " moves the cursor to the root node
3792 function! s:jumpToRoot()
3793 call b:NERDTreeRoot.putCursorHere(1, 0)
3794 call s:centerView()
3795 endfunction
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
3803 function! 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
3815 endfunction
3816
3817 " FUNCTION: s:openBookmark(name) {{{2
3818 " put the cursor on the given bookmark and, if its a file, open it
3819 function! s:openBookmark(name)
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
3834 endfunction
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
3841 function! 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
3855 endfunction
3856
3857 " FUNCTION: s:openExplorer() {{{2
3858 function! 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
3865 endfunction
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
3872 function! 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
3881 endfunction
3882
3883 " FUNCTION: s:openNodeRecursively() {{{2
3884 function! s:openNodeRecursively()
3885 let treenode = s:TreeFileNode.GetSelected()
3886 if treenode ==# {} || treenode.path.isDirectory ==# 0
3887 call s:echo("Select a directory node first" )
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
3896 endfunction
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
3902 function! 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")
3910 endfunction
3911
3912 " FUNCTION: s:revealBookmark(name) {{{2
3913 " put the cursor on the node associate with the given name
3914 function! 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
3921 endfunction
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.
3925 function! 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")
3931 endfunction
3932
3933 " FUNCTION: s:refreshCurrent() {{{2
3934 " refreshes the root for the current node
3935 function! 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")
3947 endfunction
3948 " FUNCTION: s:showMenu() {{{2
3949 function! 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()
3958 endfunction
3959
3960 " FUNCTION: s:toggleIgnoreFilter() {{{2
3961 " toggles the use of the NERDTreeIgnore option
3962 function! s:toggleIgnoreFilter()
3963 let b:NERDTreeIgnoreEnabled = !b:NERDTreeIgnoreEnabled
3964 call s:renderViewSavingPosition()
3965 call s:centerView()
3966 endfunction
3967
3968 " FUNCTION: s:toggleShowBookmarks() {{{2
3969 " toggles the display of bookmarks
3970 function! s:toggleShowBookmarks()
3971 let b:NERDTreeShowBookmarks = !b:NERDTreeShowBookmarks
3972 if b:NERDTreeShowBookmarks
3973 call s:renderView()
3974 call s:putCursorOnBookmarkTable()
3975 else
3976 call s:renderViewSavingPosition()
3977 endif
3978 call s:centerView()
3979 endfunction
3980 " FUNCTION: s:toggleShowFiles() {{{2
3981 " toggles the display of hidden files
3982 function! s:toggleShowFiles()
3983 let b:NERDTreeShowFiles = !b:NERDTreeShowFiles
3984 call s:renderViewSavingPosition()
3985 call s:centerView()
3986 endfunction
3987
3988 " FUNCTION: s:toggleShowHidden() {{{2
3989 " toggles the display of hidden files
3990 function! s:toggleShowHidden()
3991 let b:NERDTreeShowHidden = !b:NERDTreeShowHidden
3992 call s:renderViewSavingPosition()
3993 call s:centerView()
3994 endfunction
3995
3996 " FUNCTION: s:toggleZoom() {{2
3997 " zoom (maximize/minimize) the NERDTree window
3998 function! s:toggleZoom()
3999 if exists("b:NERDTreeZoomed") && b:NERDTreeZoomed
4000 let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
4001 exec "silent vertical resize ". size
4002 let b:NERDTreeZoomed = 0
4003 else
4004 exec "vertical resize"
4005 let b:NERDTreeZoomed = 1
4006 endif
4007 endfunction
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
4015 function! s:upDir(keepState)
4016 let cwd = b:NERDTreeRoot.path.str({'format': 'UI'})
4017 if cwd ==# "/" || cwd =~ '^[^/]..$'
4018 call s:echo("already at top dir")
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
4043 endfunction
4044
4045
4046 "reset &cpo back to users setting
4047 let &cpo = s:old_cpo
4048
4049 " vim: set sw=4 sts=4 et fdm=marker: