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