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.

4238 lines
130 KiB

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