-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathproject.vim
More file actions
390 lines (323 loc) · 10.7 KB
/
project.vim
File metadata and controls
390 lines (323 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
" Features of projects:
"
" * Tree state (open/closed dirs) is remembered between sessions
" * Dirs can be hidden.
"
" Usage:
"
" Creating/Loading/Deleting projects:
"
" Create a project from the current nerdtree with
" :NERDTreeProjectSave <name>
"
" Load a project with
" :NERDTreeProjectLoad <name>
"
" Load a project from the current CWD
" :NERDTreeProjectLoadFromCWD
"
" Delete a project with
" :NERDTreeProjectRm <name>
"
"
" Note: <name> above will tab complete.
"
" Hiding/Unhiding directories in projects
" To hide a dir, put the cursor on it and hit 'mph'. 'mp' opens the
" menu and goes to the projects submenu, then 'h' hides it.
"
" To unhide, turn off file filters (default: f), then put the cursor
" on the dir in question and hit 'mpu'
"
"
"============================================================
if exists("g:loaded_nerdtree_project_plugin")
finish
endif
let g:loaded_nerdtree_project_plugin=1
"Glue code - wiring up s:Project into nerdtree {{{1
"============================================================
command! -nargs=1 NERDTreeProjectSave call g:NERDTreeProject.Add(<q-args>, b:NERDTree)
command! -nargs=1 -complete=customlist,NERDTreeCompleteProjectNames NERDTreeProjectLoad call g:NERDTreeProject.Open(<q-args>)
command! -nargs=1 -complete=customlist,NERDTreeCompleteProjectNames NERDTreeProjectRm call g:NERDTreeProject.Remove(<q-args>)
command! -nargs=? -complete=customlist,NERDTreeCompleteProjectNames NERDTreeProjectLoadFromCWD call g:NERDTreeProject.LoadFromCWD()
function! NERDTreeCompleteProjectNames(A,L,P) abort
if empty(s:Project.All())
return 0
endif
let names = map(copy(s:Project.All()), 'v:val.getName()')
return filter(names, 'v:val =~# "^' . a:A . '"')
endfunction
augroup nerdtreeproject
autocmd bufunload,bufwipeout * call g:NERDTreeProject.UpdateProjectInBuf(bufnr(bufname(expand("<afile>"))))
augroup end
"CLASS: Project {{{1
"============================================================
let s:Project = {}
let g:NERDTreeProject = s:Project
"Class Methods {{{2
"============================================================
" FUNCTION: Project.Add(name, nerdtree) {{{3
function! s:Project.Add(name, nerdtree) abort
for i in s:Project.All()
if i.getName() ==# a:name
return i.update(a:nerdtree)
endif
endfor
let newProj = s:Project.New(a:name, a:nerdtree)
call add(s:Project.All(), newProj)
call s:Project.Write()
call newProj.open()
endfunction
" FUNCTION: Project.All() {{{3
function! s:Project.All() abort
if !exists("s:Project._All")
let s:Project._All = []
call s:Project.Read()
endif
return s:Project._All
endfunction
" FUNCTION: Project.Remove() {{{3
function! s:Project.Remove(name) abort
for i in s:Project.All()
if i.getName() ==# a:name
let idx = index(s:Project.All(), i)
call remove(s:Project.All(), idx)
call s:Project.Write()
return nerdtree#echo("Project removed.")
endif
endfor
call nerdtree#echo("No project found with name: '" . a:name . "'")
endfunction
" FUNCTION: Project.New(name, nerdtree) {{{3
function! s:Project.New(name, nerdtree, ...) abort
if a:name =~# ' '
throw "NERDTree.IllegalProjectNameError: illegal name:" . a:name
endif
let newObj = copy(self)
let newObj._name = a:name
let newObj._rootPath = a:nerdtree.root.path
let opts = a:0 ? a:1 : {}
if has_key(opts, 'openDirs')
let newObj._openDirs = opts['openDirs']
else
let newObj._openDirs = newObj._extractOpenDirs(a:nerdtree.root)
endif
let newObj._hiddenDirs = has_key(opts, 'hiddenDirs') ? opts['hiddenDirs'] : []
call newObj.rebuildHiddenRegex()
return newObj
endfunction
" FUNCTION: Project.FindByName(name) {{{3
function! s:Project.FindByName(name) abort
for i in s:Project.All()
if i.getName() ==# a:name
return i
endif
endfor
throw "NERDTree.NoProjectError: no project found for name: \"". a:name .'"'
endfunction
" FUNCTION: Project.FindByRoot(dir) {{{3
function! s:Project.FindByRoot(dir) abort
for i in s:Project.All()
if i.getRootPath().equals(a:dir)
return i
endif
endfor
throw "NERDTree.NoProjectError: no project found for root: \"". a:dir.str() .'"'
endfunction
" FUNCTION: Project.LoadFromCWD() {{{3
function! s:Project.LoadFromCWD() abort
try
let proj = s:Project.FindByRoot(g:NERDTreePath.New(getcwd()))
call proj.open()
wincmd w
catch /NERDTree.NoProjectError/
call nerdtree#echo("Couldn't find a project for root: " . getcwd())
NERDTree
endtry
endfunction
" FUNCTION: Project.Open(name) {{{3
function! s:Project.Open(name) abort
call s:Project.FindByName(a:name).open()
endfunction
" FUNCTION: Project.OpenForRoot(dir) {{{3
function! s:Project.OpenForRoot(dir) abort
let p = s:Project.FindByRoot(a:dir)
if !empty(p)
call p.open()
endif
endfunction
" FUNCTION: Project.ProjectFileName() {{{3
function! s:Project.ProjectFileName() abort
return expand("~/.NERDTreeProjects")
endfunction
" FUNCTION: Project.Read() {{{3
function! s:Project.Read() abort
if !filereadable(s:Project.ProjectFileName())
return []
endif
try
exec "let projHashes = " . readfile(s:Project.ProjectFileName())[0]
catch
return []
endtry
for projHash in projHashes
let nerdtree = g:NERDTree.New(g:NERDTreePath.New(projHash['rootPath']), "tab")
let project = s:Project.New(projHash['name'], nerdtree, { 'openDirs': projHash['openDirs'], 'hiddenDirs': projHash['hiddenDirs'] })
call add(s:Project.All(), project)
endfor
endfunction
" FUNCTION: Project.UpdateProjectInBuf(bufnr) {{{3
function! s:Project.UpdateProjectInBuf(bufnr) abort
let nerdtree = getbufvar(a:bufnr, "NERDTree")
if empty(nerdtree)
return
endif
if !has_key(nerdtree, '__currentProject')
return
endif
let proj = nerdtree.__currentProject
call proj.update(nerdtree)
endfunction
" FUNCTION: Project.Write() {{{3
function! s:Project.Write() abort
let projHashes = []
for proj in s:Project.All()
let hash = {
\ 'name': proj.getName(),
\ 'openDirs': proj.getOpenDirs(),
\ 'rootPath': proj.getRootPath().str(),
\ 'hiddenDirs': proj.getHiddenDirs()
\ }
call add(projHashes, hash)
endfor
call writefile([string(projHashes)], s:Project.ProjectFileName())
endfunction
"Instance Methods {{{2
"============================================================
" FUNCTION: Project.extractOpenDirs(rootNode) {{{3
function! s:Project._extractOpenDirs(rootNode) abort
let retVal = []
for node in a:rootNode.getDirChildren()
if node.isOpen
call add(retVal, node.path.str())
let childOpenDirs = self._extractOpenDirs(node)
if !empty(childOpenDirs)
let retVal = retVal + childOpenDirs
endif
endif
endfor
return retVal
endfunction
" FUNCTION: Project.getHiddenDirs() {{{3
function! s:Project.getHiddenDirs() abort
return self._hiddenDirs
endfunction
" FUNCTION: Project.getName() {{{3
function! s:Project.getName() abort
return self._name
endfunction
" FUNCTION: Project.getOpenDirs() {{{3
function! s:Project.getOpenDirs() abort
return self._openDirs
endfunction
" FUNCTION: Project.getRoot() {{{3
function! s:Project.getRootPath() abort
return self._rootPath
endfunction
" FUNCTION: Project.hideDir(path) {{{3
function! s:Project.hideDir(path) abort
if self.isHidden(a:path)
return
endif
call add(self._hiddenDirs, a:path)
call self.rebuildHiddenRegex()
endfunction
" FUNCTION: Project.isHidden(path) {{{3
function! s:Project.isHidden(path) abort
for dir in self._hiddenDirs
if dir == a:path
return 1
endif
endfor
endfunction
" FUNCTION: Project.open() {{{3
function! s:Project.open() abort
call g:NERDTreeCreator.CreateTabTree(self.getRootPath().str())
for dir in self.getOpenDirs()
let p = g:NERDTreePath.New(dir)
call b:NERDTree.root.reveal(p, { "open": 1 })
endfor
let b:NERDTree.__currentProject = self
call b:NERDTree.render()
endfunction
" FUNCTION: Project.rebuildHiddenRegex() {{{3
function! s:Project.rebuildHiddenRegex() abort
let hiddenDirs = join(map(copy(self._hiddenDirs), "v:val . '\\.\\*'"), '\|')
let self._hiddenRegex = '\M\(' . hiddenDirs . '\)'
endfunction
" FUNCTION: Project.unhideDir(path) {{{3
function! s:Project.unhideDir(path) abort
if !self.isHidden(a:path)
return
endif
let idx = index(self._hiddenDirs, a:path)
if idx != -1
call remove(self._hiddenDirs, idx)
endif
call self.rebuildHiddenRegex()
endfunction
" FUNCTION: Project.update(nerdtree) {{{3
function s:Project.update(nerdtree)
"make sure the user hasn't browsed away from the project dir
if !a:nerdtree.root.path.equals(self.getRootPath())
return
endif
let self._openDirs = self._extractOpenDirs(a:nerdtree.root)
call s:Project.Write()
endfunction
"Filtering glue {{{1
"============================================================
call NERDTreeAddPathFilter("ProjectPathFilter")
function! ProjectPathFilter(params) abort
let nerdtree = a:params['nerdtree']
"bail if we haven't loaded a project
if !exists('nerdtree.__currentProject')
return
endif
if len(nerdtree.__currentProject._hiddenDirs) == 0
return 0
endif
let p = a:params['path']
return p.str() =~ nerdtree.__currentProject._hiddenRegex
endfunction
let projectMenu = NERDTreeAddSubmenu({'text': '(p)rojects', 'shortcut': 'p'})
call NERDTreeAddMenuItem({
\ 'text': '(h)ide directory',
\ 'shortcut': 'h',
\ 'parent': projectMenu,
\ 'callback': 'NERDTreeProjectHideMenuItemCallback'
\ })
function! NERDTreeProjectHideMenuItemCallback() abort
let node = g:NERDTreeDirNode.GetSelected()
if empty(node)
return
endif
call b:NERDTree.__currentProject.hideDir(node.path.str())
call b:NERDTree.render()
endfunction
call NERDTreeAddMenuItem({
\ 'text': '(u)nhide directory',
\ 'shortcut': 'u',
\ 'parent': projectMenu,
\ 'callback': 'NERDTreeProjectUnhideMenuItemCallback'
\ })
function! NERDTreeProjectUnhideMenuItemCallback() abort
let node = g:NERDTreeDirNode.GetSelected()
if empty(node)
return
endif
call b:NERDTree.__currentProject.unhideDir(node.path.str())
call b:NERDTree.render()
endfunction
" vi: fdm=marker