A tree explorer plugin for vim.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4391 lines
136 KiB

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