Rainbow
[my-vim-dotfolder.git] / plugin / project.vim
blob0fe05e2020f2dc276a2c57dd680110419d80acb0
1 "=============================================================================
2 " File:        project.vim
3 " Author:      Aric Blumer (Aric.Blumer at aricvim@charter.net)
4 " Last Change: Fri 13 Oct 2006 09:47:08 AM EDT
5 " Version:     1.4.1
6 "=============================================================================
7 " See documentation in accompanying help file
8 " You may use this code in whatever way you see fit.
10 if exists('loaded_project') || &cp
11   finish
12 endif
13 let loaded_project=1
15 function! s:Project(filename) " <<<
16     " Initialization <<<
17     if exists("g:proj_running")
18         if strlen(a:filename) != 0
19             call confirm('Project already loaded; ignoring filename "'.a:filename."\".\n".'See ":help project-invoking" for information about changing project files.', "&OK", 1)
20         endif
21         let filename=bufname(g:proj_running)
22     else
23         if strlen(a:filename) == 0
24             let filename ='~/.vimprojects'      " Default project filename
25         else
26             let filename = a:filename
27         endif
28     endif
29     if !exists('g:proj_window_width')
30         let g:proj_window_width=24              " Default project window width
31     endif
32     if !exists('g:proj_window_increment')
33         let g:proj_window_increment=100         " Project Window width increment
34     endif
35     if !exists('g:proj_flags')
36         if has("win32") || has("mac")
37             let g:proj_flags='imst'             " Project default flags for windows/mac
38         else
39             let g:proj_flags='imstb'            " Project default flags for everything else
40         endif
41     endif
42     if !exists("g:proj_running") || (bufwinnr(g:proj_running) == -1) " Open the Project Window
43         exec 'silent vertical new '.filename
44         if match(g:proj_flags, '\CF') == -1      " We're floating
45             silent! wincmd H
46             exec 'vertical resize '.g:proj_window_width
47         endif
48         setlocal nomodeline
49     else
50         silent! 99wincmd h
51         if bufwinnr(g:proj_running) == -1
52             vertical split
53             let v:errmsg="nothing"
54             silent! bnext
55             if 'nothing' != v:errmsg
56                 enew
57             endif
58         endif
59         return
60     endif
61     " Process the flags
62     let b:proj_cd_cmd='cd'
63     if match(g:proj_flags, '\Cl') != -1
64         let b:proj_cd_cmd = 'lcd'
65     endif
67     let b:proj_locate_command='silent! wincmd H'
68     let b:proj_resize_command='exec ''vertical resize ''.g:proj_window_width'
69     if match(g:proj_flags, '\CF') != -1         " Set the resize commands to nothing
70         let b:proj_locate_command=''
71         let b:proj_resize_command=''
72     endif
74     let g:proj_last_buffer = -1
75     ">>>
76     " ProjFoldText() <<<
77     "   The foldtext function for displaying just the description.
78     function! ProjFoldText()
79         let line=substitute(getline(v:foldstart),'^[ \t#]*\([^=]*\).*', '\1', '')
80         let line=strpart('                                     ', 0, (v:foldlevel - 1)).substitute(line,'\s*{\+\s*', '', '')
81         return line
82     endfunction ">>>
83     " s:DoSetup() <<<
84     "   Ensure everything is set up
85     function! s:DoSetup()
86         setlocal foldenable foldmethod=marker foldmarker={,} commentstring=%s foldcolumn=0 nonumber noswapfile shiftwidth=1
87         setlocal foldtext=ProjFoldText() nobuflisted nowrap
88         setlocal winwidth=1
89         if match(g:proj_flags, '\Cn') != -1
90             setlocal number
91         endif
92     endfunction ">>>
93     call s:DoSetup()
94     " Syntax Stuff <<<
95     if match(g:proj_flags, '\Cs')!=-1 && has('syntax') && exists('g:syntax_on') && !has('syntax_items')
96         syntax match projectDescriptionDir '^\s*.\{-}=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError
97         syntax match projectDescription    '\<.\{-}='he=e-1,me=e-1         contained nextgroup=projectDirectory contains=projectWhiteError
98         syntax match projectDescription    '{\|}'
99         syntax match projectDirectory      '=\(\\ \|\f\|:\)\+'             contained
100         syntax match projectDirectory      '=".\{-}"'                      contained
101         syntax match projectScriptinout    '\<in\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError
102         syntax match projectScriptinout    '\<out\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError
103         syntax match projectComment        '#.*'
104         syntax match projectCD             '\<CD\s*=\s*\(\\ \|\f\|:\|"\)\+' contains=projectDescription,projectWhiteError
105         syntax match projectFilterEntry    '\<filter\s*=.*"'               contains=projectWhiteError,projectFilterError,projectFilter,projectFilterRegexp
106         syntax match projectFilter         '\<filter='he=e-1,me=e-1        contained nextgroup=projectFilterRegexp,projectFilterError,projectWhiteError
107         syntax match projectFlagsEntry     '\<flags\s*=\( \|[^ ]*\)'       contains=projectFlags,projectWhiteError
108         syntax match projectFlags          '\<flags'                       contained nextgroup=projectFlagsValues,projectWhiteError
109         syntax match projectFlagsValues    '=[^ ]* 'hs=s+1,me=e-1          contained contains=projectFlagsError
110         syntax match projectFlagsError     '[^rtTsSwl= ]\+'                contained
111         syntax match projectWhiteError     '=\s\+'hs=s+1                   contained
112         syntax match projectWhiteError     '\s\+='he=e-1                   contained
113         syntax match projectFilterError    '=[^"]'hs=s+1                   contained
114         syntax match projectFilterRegexp   '=".*"'hs=s+1                   contained
115         syntax match projectFoldText       '^[^=]\+{'
117         highlight def link projectDescription  Identifier
118         highlight def link projectScriptinout  Identifier
119         highlight def link projectFoldText     Identifier
120         highlight def link projectComment      Comment
121         highlight def link projectFilter       Identifier
122         highlight def link projectFlags        Identifier
123         highlight def link projectDirectory    Constant
124         highlight def link projectFilterRegexp String
125         highlight def link projectFlagsValues  String
126         highlight def link projectWhiteError   Error
127         highlight def link projectFlagsError   Error
128         highlight def link projectFilterError  Error
129     endif ">>>
130     " s:SortR(start, end) <<<
131     " Sort lines.  SortR() is called recursively.
132     "  from ":help eval-examples" by Robert Webb, slightly modified
133     function! s:SortR(start, end)
134         if (a:start >= a:end)
135             return
136         endif
137         let partition = a:start - 1
138         let middle = partition
139         let partStr = getline((a:start + a:end) / 2)
140         let i = a:start
141         while (i <= a:end)
142             let str = getline(i)
143             if str < partStr
144                 let result = -1
145             elseif str > partStr
146                 let result = 1
147             else
148                 let result = 0
149             endif
150             if (result <= 0)
151                 let partition = partition + 1
152                 if (result == 0)
153                     let middle = partition
154                 endif
155                 if (i != partition)
156                     let str2 = getline(partition)
157                     call setline(i, str2)
158                     call setline(partition, str)
159                 endif
160             endif
161             let i = i + 1
162         endwhile
163         if (middle != partition)
164             let str = getline(middle)
165             let str2 = getline(partition)
166             call setline(middle, str2)
167             call setline(partition, str)
168         endif
169         call s:SortR(a:start, partition - 1)
170         call s:SortR(partition + 1, a:end)
171     endfunc ">>>
172     " s:IsAbsolutePath(path) <<<
173     "   Returns true if filename has an absolute path.
174     function! s:IsAbsolutePath(path)
175         if a:path =~ '^ftp:' || a:path =~ '^rcp:' || a:path =~ '^scp:' || a:path =~ '^http:'
176             return 2
177         endif
178         if a:path =~ '\$'
179             let path=expand(a:path) " Expand any environment variables that might be in the path
180         else
181             let path=a:path
182         endif
183         if path[0] == '/' || path[0] == '~' || path[0] == '\\' || path[1] == ':'
184             return 1
185         endif
186         return 0
187     endfunction " >>>
188     " s:DoSetupAndSplit() <<<
189     "   Call DoSetup to ensure the settings are correct.  Split to the next
190     "   file.
191     function! s:DoSetupAndSplit()
192         call s:DoSetup()                " Ensure that all the settings are right
193         let n = winnr()                 " Determine if there is a CTRL_W-p window
194         silent! wincmd p
195         if n == winnr()
196             silent! wincmd l
197         endif
198         if n == winnr()
199             " If n == winnr(), then there is no CTRL_W-p window
200             " So we have to create a new one
201             if bufnr('%') == g:proj_running
202                 exec 'silent vertical new'
203             else
204                 exec 'silent vertical split | silent! bnext'
205             endif
206             wincmd p " Go back to the Project Window and ensure it is the right width
207             exec b:proj_locate_command
208             exec b:proj_resize_command
209             wincmd p
210         endif
211     endfunction ">>>
212     " s:DoSetupAndSplit_au() <<<
213     "   Same as above but ensure that the Project window is the current
214     "   window.  Only called from an autocommand
215     function! s:DoSetupAndSplit_au()
216         if winbufnr(0) != g:proj_running
217             return
218         endif
219         call s:DoSetup()                " Ensure that all the settings are right
220         if winbufnr(2) == -1            " We're the only window right now.
221             exec 'silent vertical split | bnext'
222             if bufnr('%') == g:proj_running
223                 enew
224             endif
225             if bufnr('%') == g:proj_last_buffer | bnext | bprev | bnext | endif
226             wincmd p " Go back to the Project Window and ensure it is the right width
227             exec b:proj_locate_command
228             exec b:proj_resize_command
229         elseif(winnr() != 1)
230             exec b:proj_locate_command
231             exec b:proj_resize_command
232         endif
233     endfunction
234     function! s:RecordPrevBuffer_au()
235         let g:proj_last_buffer = bufnr('%')
236     endfunction ">>>
237     " s:RecursivelyConstructDirectives(lineno) <<<
238     "   Construct the inherited directives
239     function! s:RecursivelyConstructDirectives(lineno)
240         let lineno=s:FindFoldTop(a:lineno)
241         let foldlineno = lineno
242         let foldlev=foldlevel(lineno)
243         let parent_infoline = ''
244         if foldlev > 1
245             while foldlevel(lineno) >= foldlev " Go to parent fold
246                 if lineno < 1
247                     echoerr 'Some kind of fold error.  Check your syntax.'
248                     return
249                 endif
250                 let lineno = lineno - 1
251             endwhile
252             let parent_infoline = s:RecursivelyConstructDirectives(lineno)
253         endif
254         let parent_home = s:GetHome(parent_infoline, '')
255         let parent_c_d = s:GetCd(parent_infoline, parent_home)
256         let parent_scriptin = s:GetScriptin(parent_infoline, parent_home)
257         let parent_scriptout = s:GetScriptout(parent_infoline, parent_home)
258         let parent_filter = s:GetFilter(parent_infoline, '*')
259         let infoline = getline(foldlineno)
260         " Extract the home directory of this fold
261         let home=s:GetHome(infoline, parent_home)
262         if home != ''
263             if (foldlevel(foldlineno) == 1) && !s:IsAbsolutePath(home)
264                 call confirm('Outermost Project Fold must have absolute path!  Or perhaps the path does not exist.', "&OK", 1)
265                 let home = '~'  " Some 'reasonable' value
266             endif
267         endif
268         " Extract any CD information
269         let c_d = s:GetCd(infoline, home)
270         if c_d != ''
271             if (foldlevel(foldlineno) == 1) && !s:IsAbsolutePath(c_d)
272                 call confirm('Outermost Project Fold must have absolute CD path!  Or perhaps the path does not exist.', "&OK", 1)
273                 let c_d = '.'  " Some 'reasonable' value
274             endif
275         else
276             let c_d=parent_c_d
277         endif
278         " Extract scriptin
279         let scriptin = s:GetScriptin(infoline, home)
280         if scriptin == ''
281             let scriptin = parent_scriptin
282         endif
283         " Extract scriptout
284         let scriptout = s:GetScriptout(infoline, home)
285         if scriptout == ''
286             let scriptout = parent_scriptout
287         endif
288         " Extract filter
289         let filter = s:GetFilter(infoline, parent_filter)
290         if filter == '' | let filter = parent_filter | endif
291         return s:ConstructInfo(home, c_d, scriptin, scriptout, '', filter)
292     endfunction ">>>
293     " s:ConstructInfo(home, c_d, scriptin, scriptout, flags, filter) <<<
294     function! s:ConstructInfo(home, c_d, scriptin, scriptout, flags, filter)
295         let retval='Directory='.a:home
296         if a:c_d[0] != ''
297             let retval=retval.' CD='.a:c_d
298         endif
299         if a:scriptin[0] != ''
300             let retval=retval.' in='.a:scriptin
301         endif
302         if a:scriptout[0] != ''
303             let retval=retval.' out='.a:scriptout
304         endif
305         if a:filter[0] != ''
306             let retval=retval.' filter="'.a:filter.'"'
307         endif
308         return retval
309     endfunction ">>>
310     " s:OpenEntry(line, precmd, editcmd) <<<
311     "   Get the filename under the cursor, and open a window with it.
312     function! s:OpenEntry(line, precmd, editcmd, dir)
313         silent exec a:precmd
314         if (a:editcmd[0] != '')
315             if a:dir
316                 let fname='.'
317             else
318                 if (foldlevel(a:line) == 0) && (a:editcmd[0] != '')
319                     return 0                    " If we're outside a fold, do nothing
320                 endif
321                 let fname=substitute(getline(a:line), '\s*#.*', '', '') " Get rid of comments and whitespace before comment
322                 let fname=substitute(fname, '^\s*\(.*\)', '\1', '') " Get rid of leading whitespace
323                 if strlen(fname) == 0
324                     return 0                    " The line is blank. Do nothing.
325                 endif
326             endif
327         else
328             let fname='.'
329         endif
330         let infoline = s:RecursivelyConstructDirectives(a:line)
331         let retval=s:OpenEntry2(a:line, infoline, fname, a:editcmd)
332         call s:DisplayInfo()
333         return retval
334     endfunction
335     ">>>
336     " s:OpenEntry2(line, infoline, precmd, editcmd) <<<
337     "   Get the filename under the cursor, and open a window with it.
338     function! s:OpenEntry2(line, infoline, fname, editcmd)
339         let fname=escape(a:fname, ' %#')        " Thanks to Thomas Link for cluing me in on % and #
340         let home=s:GetHome(a:infoline, '').'/'
341         if home=='/'
342             echoerr 'Project structure error. Check your syntax.'
343             return
344         endif
345         "Save the cd command
346         let cd_cmd = b:proj_cd_cmd
347         if a:editcmd[0] != '' " If editcmd is '', then just set up the environment in the Project Window
348             call s:DoSetupAndSplit()
349             " If it is an absolute path, don't prepend home
350             if !s:IsAbsolutePath(fname)
351                 let fname=home.fname
352             endif
353             if s:IsAbsolutePath(fname) == 2
354                 exec a:editcmd.' '.fname
355             else
356                 silent exec 'silent '.a:editcmd.' '.fname
357             endif
358         else " only happens in the Project File
359             exec 'au! BufEnter,BufLeave '.expand('%:p')
360         endif
361         " Extract any CD information
362         let c_d = s:GetCd(a:infoline, home)
363         if c_d != '' && (s:IsAbsolutePath(home) != 2)
364             if match(g:proj_flags, '\CL') != -1
365                 call s:SetupAutoCommand(c_d)
366             endif
367             if !isdirectory(glob(c_d))
368                 call confirm("From this fold's entry,\nCD=".'"'.c_d.'" is not a valid directory.', "&OK", 1)
369             else
370                 silent exec cd_cmd.' '.c_d
371             endif
372         endif
373         " Extract any scriptin information
374         let scriptin = s:GetScriptin(a:infoline, home)
375         if scriptin != ''
376             if !filereadable(glob(scriptin))
377                 call confirm('"'.scriptin.'" not found. Ignoring.', "&OK", 1)
378             else
379                 call s:SetupScriptAutoCommand('BufEnter', scriptin)
380                 exec 'source '.scriptin
381             endif
382         endif
383         let scriptout = s:GetScriptout(a:infoline, home)
384         if scriptout != ''
385             if !filereadable(glob(scriptout))
386                 call confirm('"'.scriptout.'" not found. Ignoring.', "&OK", 1)
387             else
388                 call s:SetupScriptAutoCommand('BufLeave', scriptout)
389             endif
390         endif
391         return 1
392     endfunction
393     ">>>
394     " s:DoFoldOrOpenEntry(cmd0, cmd1) <<<
395     "   Used for double clicking. If the mouse is on a fold, open/close it. If
396     "   not, try to open the file.
397     function! s:DoFoldOrOpenEntry(cmd0, cmd1)
398         if getline('.')=~'{\|}' || foldclosed('.') != -1
399             normal! za
400         else
401             call s:DoEnsurePlacementSize_au()
402             if (match(g:proj_flags, '\Cc') != -1)
403                 let g:proj_mywinnumber = winbufnr(0)
404                 hide
405                 if(g:proj_mywinnumber != winbufnr(0))
406                     wincmd p
407                 endif
408                 " wincmd =
409             endif
411             call s:OpenEntry(line('.'), a:cmd0, a:cmd1, 0)
412         endif
413     endfunction ">>>
414     " s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount) <<<
415     function! s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount)
416         let end = 0
417         let files=''
418         let filter = a:filter
419         " Chop up the filter
420         "   Apparently glob() cannot take something like this: glob('*.c *.h')
421         let while_var = 1
422         while while_var
423             let end = stridx(filter, ' ')
424             if end == -1
425                 let end = strlen(filter)
426                 let while_var = 0
427             endif
428             let single=glob(strpart(filter, 0, end))
429             if strlen(single) != 0
430                 let files = files.single."\010"
431             endif
432             let filter = strpart(filter, end + 1)
433         endwhile
434         " files now contains a list of everything in the directory. We need to
435         " weed out the directories.
436         let fnames=files
437         let {a:filevariable}=''
438         let {a:dirvariable}=''
439         let {a:filecount}=0
440         let {a:dircount}=0
441         while strlen(fnames) > 0
442             let fname = substitute(fnames,  '\(\(\f\|[ :\[\]]\)*\).*', '\1', '')
443             let fnames = substitute(fnames, '\(\f\|[ :\[\]]\)*.\(.*\)', '\2', '')
444             if isdirectory(glob(fname))
445                 let {a:dirvariable}={a:dirvariable}.a:padding.fname.a:separator
446                 let {a:dircount}={a:dircount} + 1
447             else
448                 let {a:filevariable}={a:filevariable}.a:padding.fname.a:separator
449                 let {a:filecount}={a:filecount} + 1
450             endif
451         endwhile
452     endfunction ">>>
453     " s:GenerateEntry(recursive, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) <<<
454     function! s:GenerateEntry(recursive, line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort)
455         let line=a:line
456         if a:dir =~ '\\ '
457             let dir='"'.substitute(a:dir, '\\ ', ' ', 'g').'"'
458         else
459             let dir=a:dir
460         endif
461         let spaces=strpart('                                                             ', 0, a:foldlev)
462         let c_d=(strlen(a:c_d) > 0) ? 'CD='.a:c_d.' ' : ''
463         let c_d=(strlen(a:filter_directive) > 0) ? c_d.'filter="'.a:filter_directive.'" ': c_d
464         call append(line, spaces.'}')
465         call append(line, spaces.a:name.'='.dir.' '.c_d.'{')
466         if a:recursive
467             exec 'cd '.a:absolute_dir
468             call s:VimDirListing("*", '', "\010", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
469             cd -
470             let dirs=b:dirs
471             let dcount=b:dircount
472             unlet b:files b:filecount b:dirs b:dircount
473             while dcount > 0
474                 let dname = substitute(dirs,  '\(\( \|\f\|:\)*\).*', '\1', '')
475                 let edname = escape(dname, ' ')
476                 let dirs = substitute(dirs, '\( \|\f\|:\)*.\(.*\)', '\2', '')
477                 let line=s:GenerateEntry(1, line + 1, dname, a:absolute_dir.'/'.edname, edname, '', '', a:filter, a:foldlev+1, a:sort)
478                 let dcount=dcount-1
479             endwhile
480         endif
481         return line+1
482     endfunction " >>>
483     " s:DoEntryFromDir(line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) <<<
484     "   Generate the fold from the directory hierarchy (if recursive), then
485     "   fill it in with RefreshEntriesFromDir()
486     function! s:DoEntryFromDir(recursive, line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort)
487         call s:GenerateEntry(a:recursive, a:line, a:name, escape(a:absolute_dir, ' '), escape(a:dir, ' '), escape(a:c_d, ' '), a:filter_directive, a:filter, a:foldlev, a:sort)
488         normal! j
489         call s:RefreshEntriesFromDir(1)
490     endfunction ">>>
491     " s:CreateEntriesFromDir(recursive) <<<
492     "   Prompts user for information and then calls s:DoEntryFromDir()
493     function! s:CreateEntriesFromDir(recursive)
494         " Save a mark for the current cursor position
495         normal! mk
496         let line=line('.')
497         let name = inputdialog('Enter the Name of the Entry: ')
498         if strlen(name) == 0
499             return
500         endif
501         let foldlev=foldlevel(line)
502         if (foldclosed(line) != -1) || (getline(line) =~ '}')
503             let foldlev=foldlev - 1
504         endif
505         let absolute = (foldlev <= 0)?'Absolute ': ''
506         let home=''
507         let filter='*'
508         if (match(g:proj_flags, '\Cb') != -1) && has('browse')
509             " Note that browse() is inconsistent: On Win32 you can't select a
510             " directory, and it gives you a relative path.
511             let dir = browse(0, 'Enter the '.absolute.'Directory to Load: ', '', '')
512             let dir = fnamemodify(dir, ':p')
513         else
514             let dir = inputdialog('Enter the '.absolute.'Directory to Load: ', '')
515         endif
516         if (dir[strlen(dir)-1] == '/') || (dir[strlen(dir)-1] == '\\')
517             let dir=strpart(dir, 0, strlen(dir)-1) " Remove trailing / or \
518         endif
519         let dir = substitute(dir, '^\~', $HOME, 'g')
520         if (foldlev > 0)
521             let parent_directive=s:RecursivelyConstructDirectives(line)
522             let filter = s:GetFilter(parent_directive, '*')
523             let home=s:GetHome(parent_directive, '')
524             if home[strlen(home)-1] != '/' && home[strlen(home)-1] != '\\'
525                 let home=home.'/'
526             endif
527             unlet parent_directive
528             if s:IsAbsolutePath(dir)
529                 " It is not a relative path  Try to make it relative
530                 let hend=matchend(dir, '\C'.glob(home))
531                 if hend != -1
532                     let dir=strpart(dir, hend)          " The directory can be a relative path
533                 else
534                     let home=""
535                 endif
536             endif
537         endif
538         if strlen(home.dir) == 0
539             return
540         endif
541         if !isdirectory(home.dir)
542             if has("unix")
543                 silent exec '!mkdir '.home.dir.' > /dev/null'
544             else
545                 call confirm('"'.home.dir.'" is not a valid directory.', "&OK", 1)
546                 return
547             endif
548         endif
549         let c_d = inputdialog('Enter the CD parameter: ', '')
550         let filter_directive = inputdialog('Enter the File Filter: ', '')
551         if strlen(filter_directive) != 0
552             let filter = filter_directive
553         endif
554         " If I'm on a closed fold, go to the bottom of it
555         if foldclosedend(line) != -1
556             let line = foldclosedend(line)
557         endif
558         let foldlev = foldlevel(line)
559         " If we're at the end of a fold . . .
560         if getline(line) =~ '}'
561             let foldlev = foldlev - 1           " . . . decrease the indentation by 1.
562         endif
563         " Do the work
564         call s:DoEntryFromDir(a:recursive, line, name, home.dir, dir, c_d, filter_directive, filter, foldlev, 0)
565         " Restore the cursor position
566         normal! `k
567     endfunction ">>>
568     " s:RefreshEntriesFromDir(recursive) <<<
569     "   Finds metadata at the top of the fold, and then replaces all files
570     "   with the contents of the directory.  Works recursively if recursive is 1.
571     function! s:RefreshEntriesFromDir(recursive)
572         if foldlevel('.') == 0
573             echo 'Nothing to refresh.'
574             return
575         endif
576         " Open the fold.
577         if getline('.') =~ '}'
578             normal! zo[z
579         else
580             normal! zo]z[z
581         endif
582         let just_a_fold=0
583         let infoline = s:RecursivelyConstructDirectives(line('.'))
584         let immediate_infoline = getline('.')
585         if strlen(substitute(immediate_infoline, '[^=]*=\(\(\f\|:\|\\ \)*\).*', '\1', '')) == strlen(immediate_infoline)
586             let just_a_fold = 1
587         endif
588         " Extract the home directory of the fold
589         let home = s:GetHome(infoline, '')
590         if home == ''
591             " No Match.  This means that this is just a label with no
592             " directory entry.
593             if a:recursive == 0
594                 return          " We're done--nothing to do
595             endif
596             " Mark that it is just a fold, so later we don't delete filenames
597             " that aren't there.
598             let just_a_fold = 1
599         endif
600         if just_a_fold == 0
601             " Extract the filter between quotes (we don't care what CD is).
602             let filter = s:GetFilter(infoline, '*')
603             " Extract the description (name) of the fold
604             let name = substitute(infoline, '^[#\t ]*\([^=]*\)=.*', '\1', '')
605             if strlen(name) == strlen(infoline)
606                 return                  " If there's no name, we're done.
607             endif
608             if (home == '') || (name == '')
609                 return
610             endif
611             " Extract the flags
612             let flags = s:GetFlags(immediate_infoline)
613             let sort = (match(g:proj_flags, '\CS') != -1)
614             if flags != ''
615                 if match(flags, '\Cr') != -1
616                     " If the flags do not contain r (refresh), then treat it just
617                     " like a fold
618                     let just_a_fold = 1
619                 endif
620                 if match(flags, '\CS') != -1
621                     let sort = 1
622                 endif
623                 if match(flags, '\Cs') != -1
624                     let sort = 0
625                 endif
626             else
627                 let flags=''
628             endif
629         endif
630         " Move to the first non-fold boundary line
631         normal! j
632         " Delete filenames until we reach the end of the fold
633         while getline('.') !~ '}'
634             if line('.') == line('$')
635                 break
636             endif
637             if getline('.') !~ '{'
638                 " We haven't reached a sub-fold, so delete what's there.
639                 if (just_a_fold == 0) && (getline('.') !~ '^\s*#') && (getline('.') !~ '#.*pragma keep')
640                     d _
641                 else
642                     " Skip lines only in a fold and comment lines
643                     normal! j
644                 endif
645             else
646                 " We have reached a sub-fold. If we're doing recursive, then
647                 " call this function again. If not, find the end of the fold.
648                 if a:recursive == 1
649                     call s:RefreshEntriesFromDir(1)
650                     normal! ]zj
651                 else
652                     if foldclosed('.') == -1
653                         normal! zc
654                     endif
655                     normal! j
656                 endif
657             endif
658         endwhile
659         if just_a_fold == 0
660             " We're not just in a fold, and we have deleted all the filenames.
661             " Now it is time to regenerate what is in the directory.
662             if !isdirectory(glob(home))
663                 call confirm('"'.home.'" is not a valid directory.', "&OK", 1)
664             else
665                 let foldlev=foldlevel('.')
666                 " T flag.  Thanks Tomas Z.
667                 if (match(flags, '\Ct') != -1) || ((match(g:proj_flags, '\CT') == -1) && (match(flags, '\CT') == -1))
668                     " Go to the top of the fold (force other folds to the
669                     " bottom)
670                     normal! [z
671                     normal! j
672                     " Skip any comments
673                     while getline('.') =~ '^\s*#'
674                         normal! j
675                     endwhile
676                 endif
677                 normal! k
678                 let cwd=getcwd()
679                 let spaces=strpart('                                               ', 0, foldlev)
680                 exec 'cd '.home
681                 if match(g:proj_flags, '\Ci') != -1
682                     echon home."\r"
683                 endif
684                 call s:VimDirListing(filter, spaces, "\n", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
685                 if b:filecount > 0
686                     normal! mk
687                     silent! put =b:files
688                     normal! `kj
689                     if sort
690                         call s:SortR(line('.'), line('.') + b:filecount - 1)
691                     endif
692                 else
693                     normal! j
694                 endif
695                 unlet b:files b:filecount b:dirs b:dircount
696                 exec 'cd '.cwd
697             endif
698         endif
699         " Go to the top of the refreshed fold.
700         normal! [z
701     endfunction ">>>
702     " s:MoveUp() <<<
703     "   Moves the entity under the cursor up a line.
704     function! s:MoveUp()
705         let lineno=line('.')
706         if lineno == 1
707             return
708         endif
709         let fc=foldclosed('.')
710         let a_reg=@a
711         if lineno == line('$')
712             normal! "add"aP
713         else
714             normal! "addk"aP
715         endif
716         let @a=a_reg
717         if fc != -1
718             normal! zc
719         endif
720     endfunction ">>>
721     " s:MoveDown() <<<
722     "   Moves the entity under the cursor down a line.
723     function! s:MoveDown()
724         let fc=foldclosed('.')
725         let a_reg=@a
726         normal! "add"ap
727         let @a=a_reg
728         if (fc != -1) && (foldclosed('.') == -1)
729             normal! zc
730         endif
731     endfunction " >>>
732     " s:DisplayInfo() <<<
733     "   Displays filename and current working directory when i (info) is in
734     "   the flags.
735     function! s:DisplayInfo()
736         if match(g:proj_flags, '\Ci') != -1
737             echo 'file: '.expand('%').', cwd: '.getcwd().', lines: '.line('$')
738         endif
739     endfunction ">>>
740     " s:SetupAutoCommand(cwd) <<<
741     "   Sets up an autocommand to ensure that the cwd is set to the one
742     "   desired for the fold regardless.  :lcd only does this on a per-window
743     "   basis, not a per-buffer basis.
744     function! s:SetupAutoCommand(cwd)
745         if !exists("b:proj_has_autocommand")
746             let b:proj_cwd_save = escape(getcwd(), ' ')
747             let b:proj_has_autocommand = 1
748             let bufname=escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ')
749             exec 'au BufEnter '.bufname." let b:proj_cwd_save=escape(getcwd(), ' ') | cd ".a:cwd
750             exec 'au BufLeave '.bufname.' exec "cd ".b:proj_cwd_save'
751             exec 'au BufWipeout '.bufname.' au! * '.bufname
752         endif
753     endfunction ">>>
754     " s:SetupScriptAutoCommand(bufcmd, script) <<<
755     "   Sets up an autocommand to run the scriptin script.
756     function! s:SetupScriptAutoCommand(bufcmd, script)
757         if !exists("b:proj_has_".a:bufcmd)
758             let b:proj_has_{a:bufcmd} = 1
759             exec 'au '.a:bufcmd.' '.escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ').' source '.a:script
760         endif
761     endfunction " >>>
762     " s:DoEnsurePlacementSize_au() <<<
763     "   Ensure that the Project window is on the left of the window and has
764     "   the correct size. Only called from an autocommand
765     function! s:DoEnsurePlacementSize_au()
766         if (winbufnr(0) != g:proj_running) || (winnr() != 1)
767             if exists("g:proj_doinghelp")
768                 if g:proj_doinghelp > 0
769                     let g:proj_doinghelp = g:proj_doinghelp - 1
770                     return
771                 endif
772                 unlet g:proj_doinghelp
773                 return
774             endif
775             exec b:proj_locate_command
776         endif
777         exec b:proj_resize_command
778     endfunction ">>>
779     " s:Spawn(number) <<<
780     "   Spawn an external command on the file
781     function! s:Spawn(number)
782         echo | if exists("g:proj_run".a:number)
783             let fname=getline('.')
784             if fname!~'{\|}'
785                 let fname=substitute(fname, '\s*#.*', '', '')
786                 let fname=substitute(fname, '^\s*\(.*\)\s*', '\1', '')
787                 if fname == '' | return | endif
788                 let parent_infoline = s:RecursivelyConstructDirectives(line('.'))
789                 let home=expand(s:GetHome(parent_infoline, ''))
790                 let c_d=expand(s:GetCd(parent_infoline, ''))
791                 let command=substitute(g:proj_run{a:number}, '%%', "\010", 'g')
792                 let command=substitute(command, '%f', escape(home.'/'.fname, '\'), 'g')
793                 let command=substitute(command, '%F', substitute(escape(home.'/'.fname, '\'), ' ', '\\\\ ', 'g'), 'g')
794                 let command=substitute(command, '%s', escape(home.'/'.fname, '\'), 'g')
795                 let command=substitute(command, '%n', escape(fname, '\'), 'g')
796                 let command=substitute(command, '%N', substitute(fname, ' ', '\\\\ ', 'g'), 'g')
797                 let command=substitute(command, '%h', escape(home, '\'), 'g')
798                 let command=substitute(command, '%H', substitute(escape(home, '\'), ' ', '\\\\ ', 'g'), 'g')
799                 if c_d != ''
800                     if c_d == home
801                         let percent_r='.'
802                     else
803                         let percent_r=substitute(home, escape(c_d.'/', '\'), '', 'g')
804                     endif
805                 else
806                     let percent_r=home
807                 endif
808                 let command=substitute(command, '%r', percent_r, 'g')
809                 let command=substitute(command, '%R', substitute(percent_r, ' ', '\\\\ ', 'g'), 'g')
810                 let command=substitute(command, '%d', escape(c_d, '\'), 'g')
811                 let command=substitute(command, '%D', substitute(escape(c_d, '\'), ' ', '\\\\ ', 'g'), 'g')
812                 let command=substitute(command, "\010", '%', 'g')
813                 exec command
814             endif
815         endif
816     endfunction ">>>
817     " s:ListSpawn(varnamesegment) <<<
818     "   List external commands
819     function! s:ListSpawn(varnamesegment)
820         let number = 1
821         while number < 10
822             if exists("g:proj_run".a:varnamesegment.number)
823                 echohl LineNr | echo number.':' | echohl None | echon ' '.substitute(escape(g:proj_run{a:varnamesegment}{number}, '\'), "\n", '\\n', 'g')
824             else
825                 echohl LineNr | echo number.':' | echohl None
826             endif
827             let number=number + 1
828         endwhile
829     endfunction ">>>
830     " s:FindFoldTop(line) <<<
831     "   Return the line number of the directive line
832     function! s:FindFoldTop(line)
833         let lineno=a:line
834         if getline(lineno) =~ '}'
835             let lineno = lineno - 1
836         endif
837         while getline(lineno) !~ '{' && lineno > 1
838             if getline(lineno) =~ '}'
839                 let lineno=s:FindFoldTop(lineno)
840             endif
841             let lineno = lineno - 1
842         endwhile
843         return lineno
844     endfunction ">>>
845     " s:FindFoldBottom(line) <<<
846     "   Return the line number of the directive line
847     function! s:FindFoldBottom(line)
848         let lineno=a:line
849         if getline(lineno) =~ '{'
850             let lineno=lineno + 1
851         endif
852         while getline(lineno) !~ '}' && lineno < line('$')
853             if getline(lineno) =~ '{'
854                 let lineno=s:FindFoldBottom(lineno)
855             endif
856             let lineno = lineno + 1
857         endwhile
858         return lineno
859     endfunction ">>>
860     " s:LoadAll(recurse, line) <<<
861     "   Load all files in a project
862     function! s:LoadAll(recurse, line)
863         let b:loadcount=0
864         function! s:SpawnExec(infoline, fname, lineno, data)
865             if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'e')
866                 wincmd p
867                 let b:loadcount=b:loadcount+1
868                 echon b:loadcount."\r"
869                 if getchar(0) != 0
870                     let b:stop_everything=1
871                 endif
872             endif
873         endfunction
874         call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
875         delfunction s:SpawnExec
876         echon b:loadcount." Files Loaded\r"
877         unlet b:loadcount
878         if exists("b:stop_everything") | unlet b:stop_everything | endif
879     endfunction ">>>
880     " s:WipeAll(recurse, line) <<<
881     "   Wipe all files in a project
882     function! s:WipeAll(recurse, line)
883         let b:wipecount=0
884         let b:totalcount=0
885         function! s:SpawnExec(home, c_d, fname, lineno, data)
886             let fname=escape(a:fname, ' ')
887             if s:IsAbsolutePath(fname)
888                 let fname=fnamemodify(fname, ':n')  " :n is coming, won't break anything now
889             else
890                 let fname=fnamemodify(a:home.'/'.fname, ':n')  " :n is coming, won't break anything now
891             endif
892             let b:totalcount=b:totalcount+1
893             let fname=substitute(fname, '^\~', $HOME, 'g')
894             if bufloaded(substitute(fname, '\\ ', ' ', 'g'))
895                 if getbufvar(fname.'\>', '&modified') == 1
896                     exec 'sb '.fname
897                     wincmd L
898                     w
899                     wincmd p
900                 endif
901                 let b:wipecount=b:wipecount+1
902                 exec 'bwipe! '.fname
903             endif
904             if b:totalcount % 5 == 0
905                 echon b:wipecount.' of '.b:totalcount."\r"
906                 redraw
907             endif
908             if getchar(0) != 0
909                 let b:stop_everything=1
910             endif
911         endfunction
912         call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", 0, '^\(.*w\)\@!')
913         delfunction s:SpawnExec
914         echon b:wipecount.' of '.b:totalcount." Files Wiped\r"
915         unlet b:wipecount b:totalcount
916         if exists("b:stop_everything") | unlet b:stop_everything | endif
917     endfunction ">>>
918     " s:LoadAllSplit(recurse, line) <<<
919     "   Load all files in a project using split windows.
920     "   Contributed by A. Harrison
921     function! s:LoadAllSplit(recurse, line)
922         let b:loadcount=0
923         function! s:SpawnExec(infoline, fname, lineno, data)
924             let winNr = winnr() "get ProjectWindow number
925             if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'sp')
926                 exec winNr."wincmd w"
927                 let b:loadcount=b:loadcount+1
928                 echon b:loadcount."\r"
929                 if getchar(0) != 0
930                     let b:stop_everything=1
931                 endif
932             endif
933         endfunction
934         call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
935         delfunction s:SpawnExec
936         echon b:loadcount." Files Loaded\r"
937         unlet b:loadcount
938         if exists("b:stop_everything") | unlet b:stop_everything | endif
939     endfunction ">>>
940     " s:GrepAll(recurse, lineno, pattern) <<<
941     "   Grep all files in a project, optionally recursively
942     function! s:GrepAll(recurse, lineno, pattern)
943         cunmap <buffer> help
944         let pattern=(a:pattern[0] == '')?input("GREP options and pattern: "):a:pattern
945         cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help
946         if pattern[0] == ''
947             return
948         endif
949         let b:escape_spaces=1
950         let fnames=Project_GetAllFnames(a:recurse, a:lineno, ' ')
951         unlet b:escape_spaces
952         cclose " Make sure grep window is closed
953         call s:DoSetupAndSplit()
954         if match(g:proj_flags, '\Cv') == -1
955             silent! exec 'silent! grep '.pattern.' '.fnames
956             if v:shell_error != 0
957                 echo 'GREP error. Perhaps there are too many filenames.'
958             else
959                 copen
960             endif
961         else
962             silent! exec 'silent! vimgrep '.pattern.' '.fnames
963             copen
964         endif
965     endfunction ">>>
966     " GetXXX Functions <<<
967     function! s:GetHome(info, parent_home)
968         " Thanks to Adam Montague for pointing out the need for @ in urls.
969         let home=substitute(a:info, '^[^=]*=\(\(\\ \|\f\|:\|@\)\+\).*', '\1', '')
970         if strlen(home) == strlen(a:info)
971             let home=substitute(a:info, '.\{-}"\(.\{-}\)".*', '\1', '')
972             if strlen(home) != strlen(a:info) | let home=escape(home, ' ') | endif
973         endif
974         if strlen(home) == strlen(a:info)
975             let home=a:parent_home
976         elseif home=='.'
977             let home=a:parent_home
978         elseif !s:IsAbsolutePath(home)
979             let home=a:parent_home.'/'.home
980         endif
981         return home
982     endfunction
983     function! s:GetFilter(info, parent_filter)
984         let filter = substitute(a:info, '.*\<filter="\([^"]*\).*', '\1', '')
985         if strlen(filter) == strlen(a:info) | let filter = a:parent_filter | endif
986         return filter
987     endfunction
988     function! s:GetCd(info, home)
989         let c_d=substitute(a:info, '.*\<CD=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
990         if strlen(c_d) == strlen(a:info)
991             let c_d=substitute(a:info, '.*\<CD="\(.\{-}\)".*', '\1', '')
992             if strlen(c_d) != strlen(a:info) | let c_d=escape(c_d, ' ') | endif
993         endif
994         if strlen(c_d) == strlen(a:info)
995             let c_d=''
996         elseif c_d == '.'
997             let c_d = a:home
998         elseif !s:IsAbsolutePath(c_d)
999             let c_d = a:home.'/'.c_d
1000         endif
1001         return c_d
1002     endfunction
1003     function! s:GetScriptin(info, home)
1004         let scriptin = substitute(a:info, '.*\<in=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
1005         if strlen(scriptin) == strlen(a:info)
1006             let scriptin=substitute(a:info, '.*\<in="\(.\{-}\)".*', '\1', '')
1007             if strlen(scriptin) != strlen(a:info) | let scriptin=escape(scriptin, ' ') | endif
1008         endif
1009         if strlen(scriptin) == strlen(a:info) | let scriptin='' | else
1010         if !s:IsAbsolutePath(scriptin) | let scriptin=a:home.'/'.scriptin | endif | endif
1011         return scriptin
1012     endfunction
1013     function! s:GetScriptout(info, home)
1014         let scriptout = substitute(a:info, '.*\<out=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
1015         if strlen(scriptout) == strlen(a:info)
1016             let scriptout=substitute(a:info, '.*\<out="\(.\{-}\)".*', '\1', '')
1017             if strlen(scriptout) != strlen(a:info) | let scriptout=escape(scriptout, ' ') | endif
1018         endif
1019         if strlen(scriptout) == strlen(a:info) | let scriptout='' | else
1020         if !s:IsAbsolutePath(scriptout) | let scriptout=a:home.'/'.scriptout | endif | endif
1021         return scriptout
1022     endfunction
1023     function! s:GetFlags(info)
1024         let flags=substitute(a:info, '.*\<flags=\([^ {]*\).*', '\1', '')
1025         if (strlen(flags) == strlen(a:info))
1026             let flags=''
1027         endif
1028         return flags
1029     endfunction ">>>
1030     " Project_GetAllFnames(recurse, lineno, separator) <<<
1031     "   Grep all files in a project, optionally recursively
1032     function! Project_GetAllFnames(recurse, lineno, separator)
1033         let b:fnamelist=''
1034         function! s:SpawnExec(home, c_d, fname, lineno, data)
1035             if exists('b:escape_spaces')
1036                 let fname=escape(a:fname, ' ')
1037             else
1038                 let fname=a:fname
1039             endif
1040             if !s:IsAbsolutePath(a:fname)
1041                 let fname=a:home.'/'.fname
1042             endif
1043             let b:fnamelist=b:fnamelist.a:data.fname
1044         endfunction
1045         call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:separator, '')
1046         delfunction s:SpawnExec
1047         let retval=b:fnamelist
1048         unlet b:fnamelist
1049         return retval
1050     endfunction ">>>
1051     " Project_GetAllFnames(recurse, lineno, separator) <<<
1052     "   Grep all files in a project, optionally recursively
1053     function! Project_GetFname(line)
1054         if (foldlevel(a:line) == 0)
1055             return ''
1056         endif
1057         let fname=substitute(getline(a:line), '\s*#.*', '', '') " Get rid of comments and whitespace before comment
1058         let fname=substitute(fname, '^\s*\(.*\)', '\1', '') " Get rid of leading whitespace
1059         if strlen(fname) == 0
1060             return ''                    " The line is blank. Do nothing.
1061         endif
1062         if s:IsAbsolutePath(fname)
1063             return fname
1064         endif
1065         let infoline = s:RecursivelyConstructDirectives(a:line)
1066         return s:GetHome(infoline, '').'/'.fname
1067     endfunction ">>>
1068     " Project_ForEach(recurse, lineno, cmd, data, match) <<<
1069     "   Grep all files in a project, optionally recursively
1070     function! Project_ForEach(recurse, lineno, cmd, data, match)
1071         let info=s:RecursivelyConstructDirectives(a:lineno)
1072         let lineno=s:FindFoldTop(a:lineno) + 1
1073         let flags=s:GetFlags(getline(lineno - 1))
1074         if (flags == '') || (a:match=='') || (match(flags, a:match) != -1)
1075             call s:Project_ForEachR(a:recurse, lineno, info, a:cmd, a:data, a:match)
1076         endif
1077     endfunction
1078     function! s:Project_ForEachR(recurse, lineno, info, cmd, data, match)
1079         let home=s:GetHome(a:info, '')
1080         let c_d=s:GetCd(a:info, home)
1081         let scriptin = s:GetScriptin(a:info, home)
1082         let scriptout = s:GetScriptout(a:info, home)
1083         let filter = s:GetFilter(a:info, '')
1084         let lineno = a:lineno
1085         let curline=getline(lineno)
1086         while (curline !~ '}') && (curline < line('$'))
1087             if exists("b:stop_everything") && b:stop_everything | return 0 | endif
1088             if curline =~ '{'
1089                 if a:recurse
1090                     let flags=s:GetFlags(curline)
1091                     if (flags == '') || (a:match=='') || (match(flags, a:match) != -1)
1092                         let this_home=s:GetHome(curline, home)
1093                         let this_cd=s:GetCd(curline, this_home)
1094                         if this_cd=='' | let this_cd=c_d | endif
1095                         let this_scriptin=s:GetScriptin(curline, this_home)
1096                         if this_scriptin == '' | let this_scriptin=scriptin | endif
1097                         let this_scriptout=s:GetScriptin(curline, this_home)
1098                         if this_scriptout == '' | let this_scriptout=scriptout | endif
1099                         let this_filter=s:GetFilter(curline, filter)
1100                         let lineno=s:Project_ForEachR(1, lineno+1,
1101                             \s:ConstructInfo(this_home, this_cd, this_scriptin, this_scriptout, flags, this_filter), a:cmd, a:data, a:match)
1102                     else
1103                         let lineno=s:FindFoldBottom(lineno)
1104                     endif
1105                 else
1106                     let lineno=s:FindFoldBottom(lineno)
1107                 endif
1108             else
1109                 let fname=substitute(curline, '\s*#.*', '', '')
1110                 let fname=substitute(fname, '^\s*\(.*\)', '\1', '')
1111                 if (strlen(fname) != strlen(curline)) && (fname[0] != '')
1112                     if a:cmd[0] == '*'
1113                         call {strpart(a:cmd, 1)}(a:info, fname, lineno, a:data)
1114                     else
1115                         call {a:cmd}(home, c_d, fname, lineno, a:data)
1116                     endif
1117                 endif
1118             endif
1119             let lineno=lineno + 1
1120             let curline=getline(lineno)
1121         endwhile
1122         return lineno
1123     endfunction ">>>
1124     " s:SpawnAll(recurse, number) <<<
1125     "   Spawn an external command on the files of a project
1126     function! s:SpawnAll(recurse, number)
1127         echo | if exists("g:proj_run_fold".a:number)
1128             if g:proj_run_fold{a:number}[0] == '*'
1129                 function! s:SpawnExec(home, c_d, fname, lineno, data)
1130                     let command=substitute(strpart(g:proj_run_fold{a:data}, 1), '%s', escape(a:fname, ' \'), 'g')
1131                     let command=substitute(command, '%f', escape(a:fname, '\'), 'g')
1132                     let command=substitute(command, '%h', escape(a:home, '\'), 'g')
1133                     let command=substitute(command, '%d', escape(a:c_d, '\'), 'g')
1134                     let command=substitute(command, '%F', substitute(escape(a:fname, '\'), ' ', '\\\\ ', 'g'), 'g')
1135                     exec command
1136                 endfunction
1137                 call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:number, '.')
1138                 delfunction s:SpawnExec
1139             else
1140                 let info=s:RecursivelyConstructDirectives(line('.'))
1141                 let home=s:GetHome(info, '')
1142                 let c_d=s:GetCd(info, '')
1143                 let b:escape_spaces=1
1144                 let fnames=Project_GetAllFnames(a:recurse, line('.'), ' ')
1145                 unlet b:escape_spaces
1146                 let command=substitute(g:proj_run_fold{a:number}, '%f', substitute(escape(fnames, '\'), '\\ ', ' ', 'g'), 'g')
1147                 let command=substitute(command, '%s', escape(fnames, '\'), 'g')
1148                 let command=substitute(command, '%h', escape(home, '\'), 'g')
1149                 let command=substitute(command, '%d', escape(c_d, '\'), 'g')
1150                 let command=substitute(command, '%F', escape(fnames, '\'), 'g')
1151                 exec command
1152                 if v:shell_error != 0
1153                     echo 'Shell error. Perhaps there are too many filenames.'
1154                 endif
1155             endif
1156         endif
1157     endfunction ">>>
1158     if !exists("g:proj_running")
1159         " s:DoProjectOnly(void) <<<
1160         "   Make the file window the only one.
1161         function! s:DoProjectOnly()
1162             if winbufnr(0) != g:proj_running
1163                 let lzsave=&lz
1164                 set lz
1165                 only
1166                 Project
1167                 silent! wincmd p
1168                 let &lz=lzsave
1169                 unlet lzsave
1170             endif
1171         endfunction
1172         " >>>
1174         " Mappings <<<
1175         nnoremap <buffer> <silent> <Return>   \|:call <SID>DoFoldOrOpenEntry('', 'e')<CR>
1176         nnoremap <buffer> <silent> <S-Return> \|:call <SID>DoFoldOrOpenEntry('', 'sp')<CR>
1177         nnoremap <buffer> <silent> <C-Return> \|:call <SID>DoFoldOrOpenEntry('silent! only', 'e')<CR>
1178         nnoremap <buffer> <silent> <LocalLeader>T \|:call <SID>DoFoldOrOpenEntry('', 'tabe')<CR>
1179         nmap     <buffer> <silent> <LocalLeader>s <S-Return>
1180         nnoremap <buffer> <silent> <LocalLeader>S \|:call <SID>LoadAllSplit(0, line('.'))<CR>
1181         nmap     <buffer> <silent> <LocalLeader>o <C-Return>
1182         nnoremap <buffer> <silent> <LocalLeader>i :echo <SID>RecursivelyConstructDirectives(line('.'))<CR>
1183         nnoremap <buffer> <silent> <LocalLeader>I :echo Project_GetFname(line('.'))<CR>
1184         nmap     <buffer> <silent> <M-CR> <Return><C-W>p
1185         nmap     <buffer> <silent> <LocalLeader>v <M-CR>
1186         nnoremap <buffer> <silent> <LocalLeader>l \|:call <SID>LoadAll(0, line('.'))<CR>
1187         nnoremap <buffer> <silent> <LocalLeader>L \|:call <SID>LoadAll(1, line('.'))<CR>
1188         nnoremap <buffer> <silent> <LocalLeader>w \|:call <SID>WipeAll(0, line('.'))<CR>
1189         nnoremap <buffer> <silent> <LocalLeader>W \|:call <SID>WipeAll(1, line('.'))<CR>
1190         nnoremap <buffer> <silent> <LocalLeader>W \|:call <SID>WipeAll(1, line('.'))<CR>
1191         nnoremap <buffer> <silent> <LocalLeader>g \|:call <SID>GrepAll(0, line('.'), "")<CR>
1192         nnoremap <buffer> <silent> <LocalLeader>G \|:call <SID>GrepAll(1, line('.'), "")<CR>
1193         nnoremap <buffer> <silent> <2-LeftMouse>   \|:call <SID>DoFoldOrOpenEntry('', 'e')<CR>
1194         nnoremap <buffer> <silent> <S-2-LeftMouse> \|:call <SID>DoFoldOrOpenEntry('', 'sp')<CR>
1195         nnoremap <buffer> <silent> <M-2-LeftMouse> <M-CR>
1196         nnoremap <buffer> <silent> <S-LeftMouse>   <LeftMouse>
1197         nmap     <buffer> <silent> <C-2-LeftMouse> <C-Return>
1198         nnoremap <buffer> <silent> <C-LeftMouse>   <LeftMouse>
1199         nnoremap <buffer> <silent> <3-LeftMouse>  <Nop>
1200         nmap     <buffer> <silent> <RightMouse>   <space>
1201         nmap     <buffer> <silent> <2-RightMouse> <space>
1202         nmap     <buffer> <silent> <3-RightMouse> <space>
1203         nmap     <buffer> <silent> <4-RightMouse> <space>
1204         nnoremap <buffer> <silent> <space>  \|:silent exec 'vertical resize '.(match(g:proj_flags, '\Ct')!=-1 && winwidth('.') > g:proj_window_width?(g:proj_window_width):(winwidth('.') + g:proj_window_increment))<CR>
1205         nnoremap <buffer> <silent> <C-Up>   \|:silent call <SID>MoveUp()<CR>
1206         nnoremap <buffer> <silent> <C-Down> \|:silent call <SID>MoveDown()<CR>
1207         nmap     <buffer> <silent> <LocalLeader><Up> <C-Up>
1208         nmap     <buffer> <silent> <LocalLeader><Down> <C-Down>
1209         let k=1
1210         while k < 10
1211             exec 'nnoremap <buffer> <LocalLeader>'.k.'  \|:call <SID>Spawn('.k.')<CR>'
1212             exec 'nnoremap <buffer> <LocalLeader>f'.k.' \|:call <SID>SpawnAll(0, '.k.')<CR>'
1213             exec 'nnoremap <buffer> <LocalLeader>F'.k.' \|:call <SID>SpawnAll(1, '.k.')<CR>'
1214             let k=k+1
1215         endwhile
1216         nnoremap <buffer>          <LocalLeader>0 \|:call <SID>ListSpawn("")<CR>
1217         nnoremap <buffer>          <LocalLeader>f0 \|:call <SID>ListSpawn("_fold")<CR>
1218         nnoremap <buffer>          <LocalLeader>F0 \|:call <SID>ListSpawn("_fold")<CR>
1219         nnoremap <buffer> <silent> <LocalLeader>c :call <SID>CreateEntriesFromDir(0)<CR>
1220         nnoremap <buffer> <silent> <LocalLeader>C :call <SID>CreateEntriesFromDir(1)<CR>
1221         nnoremap <buffer> <silent> <LocalLeader>r :call <SID>RefreshEntriesFromDir(0)<CR>
1222         nnoremap <buffer> <silent> <LocalLeader>R :call <SID>RefreshEntriesFromDir(1)<CR>
1223         " For Windows users: same as \R
1224         nnoremap <buffer> <silent>           <F5> :call <SID>RefreshEntriesFromDir(1)<CR>
1225         nnoremap <buffer> <silent> <LocalLeader>e :call <SID>OpenEntry(line('.'), '', '', 0)<CR>
1226         nnoremap <buffer> <silent> <LocalLeader>E :call <SID>OpenEntry(line('.'), '', 'e', 1)<CR>
1227         " The :help command stomps on the Project Window.  Try to avoid that.
1228         " This is not perfect, but it is alot better than without the mappings.
1229         cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help
1230         nnoremap <buffer> <F1> :let g:proj_doinghelp = 1<CR><F1>
1231         " This is to avoid changing the buffer, but it is not fool-proof.
1232         nnoremap <buffer> <silent> <C-^> <Nop>
1233         "nnoremap <script> <Plug>ProjectOnly :let lzsave=&lz<CR>:set lz<CR><C-W>o:Project<CR>:silent! wincmd p<CR>:let &lz=lzsave<CR>:unlet lzsave<CR>
1234         nnoremap <script> <Plug>ProjectOnly :call <SID>DoProjectOnly()<CR>
1235         if match(g:proj_flags, '\Cm') != -1
1236             if !hasmapto('<Plug>ProjectOnly')
1237                 nmap <silent> <unique> <C-W>o <Plug>ProjectOnly
1238                 nmap <silent> <unique> <C-W><C-O> <C-W>o
1239             endif
1240         endif " >>>
1241         if filereadable(glob('~/.vimproject_mappings')) | source ~/.vimproject_mappings | endif
1242         " Autocommands <<<
1243         " Autocommands to clean up if we do a buffer wipe
1244         " These don't work unless we substitute \ for / for Windows
1245         let bufname=escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ')
1246         exec 'au BufWipeout '.bufname.' au! * '.bufname
1247         exec 'au BufWipeout '.bufname.' unlet g:proj_running'
1248         exec 'au BufWipeout '.bufname.' nunmap <C-W>o'
1249         exec 'au BufWipeout '.bufname.' nunmap <C-W><C-O>'
1250         " Autocommands to keep the window the specified size
1251         exec 'au WinLeave '.bufname.' call s:DoEnsurePlacementSize_au()'
1252         exec 'au BufEnter '.bufname.' call s:DoSetupAndSplit_au()'
1253         au WinLeave * call s:RecordPrevBuffer_au()
1254         " >>>
1255         setlocal buflisted
1256         let g:proj_running = bufnr(bufname.'\>')
1257         if g:proj_running == -1
1258             call confirm('Project/Vim error. Please Enter :Project again and report this bug.', "&OK", 1)
1259             unlet g:proj_running
1260         endif
1261         setlocal nobuflisted
1262     endif
1263 endfunction " >>>
1265 if exists(':Project') != 2
1266     command -nargs=? -complete=file Project call <SID>Project('<args>')
1267 endif
1268 " Toggle Mapping
1269 if !exists("*<SID>DoToggleProject()") "<<<
1270     function! s:DoToggleProject()
1271         if !exists('g:proj_running') || bufwinnr(g:proj_running) == -1
1272             Project
1273         else
1274             let g:proj_mywindow = winnr()
1275             Project
1276             hide
1277             if(winnr() != g:proj_mywindow)
1278 "               wincmd p
1279             endif
1280             unlet g:proj_mywindow
1281         endif
1282     endfunction
1283 endif ">>>
1284 nnoremap <script> <Plug>ToggleProject :call <SID>DoToggleProject()<CR>
1285 if exists('g:proj_flags') && (match(g:proj_flags, '\Cg') != -1)
1286     if !hasmapto('<Plug>ToggleProject')
1287         nmap <silent> <F12> <Plug>ToggleProject
1288     endif
1289 endif
1291 finish
1293 " vim600: set foldmethod=marker foldmarker=<<<,>>> foldlevel=1: