vim-nerdtree 0.0.1

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