Update .vimrc
[my-vim-dotfolder.git] / PACKAGES / project.vim
blobc6ad93c9ff7c0575b9253135ad617326af6099fb
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
410             call s:OpenEntry(line('.'), a:cmd0, a:cmd1, 0)
411         endif
412     endfunction ">>>
413     " s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount) <<<
414     function! s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount)
415         let end = 0
416         let files=''
417         let filter = a:filter
418         " Chop up the filter
419         "   Apparently glob() cannot take something like this: glob('*.c *.h')
420         let while_var = 1
421         while while_var
422             let end = stridx(filter, ' ')
423             if end == -1
424                 let end = strlen(filter)
425                 let while_var = 0
426             endif
427             let single=glob(strpart(filter, 0, end))
428             if strlen(single) != 0
429                 let files = files.single."\010"
430             endif
431             let filter = strpart(filter, end + 1)
432         endwhile
433         " files now contains a list of everything in the directory. We need to
434         " weed out the directories.
435         let fnames=files
436         let {a:filevariable}=''
437         let {a:dirvariable}=''
438         let {a:filecount}=0
439         let {a:dircount}=0
440         while strlen(fnames) > 0
441             let fname = substitute(fnames,  '\(\(\f\|[ :\[\]]\)*\).*', '\1', '')
442             let fnames = substitute(fnames, '\(\f\|[ :\[\]]\)*.\(.*\)', '\2', '')
443             if isdirectory(glob(fname))
444                 let {a:dirvariable}={a:dirvariable}.a:padding.fname.a:separator
445                 let {a:dircount}={a:dircount} + 1
446             else
447                 let {a:filevariable}={a:filevariable}.a:padding.fname.a:separator
448                 let {a:filecount}={a:filecount} + 1
449             endif
450         endwhile
451     endfunction ">>>
452     " s:GenerateEntry(recursive, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) <<<
453     function! s:GenerateEntry(recursive, line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort)
454         let line=a:line
455         if a:dir =~ '\\ '
456             let dir='"'.substitute(a:dir, '\\ ', ' ', 'g').'"'
457         else
458             let dir=a:dir
459         endif
460         let spaces=strpart('                                                             ', 0, a:foldlev)
461         let c_d=(strlen(a:c_d) > 0) ? 'CD='.a:c_d.' ' : ''
462         let c_d=(strlen(a:filter_directive) > 0) ? c_d.'filter="'.a:filter_directive.'" ': c_d
463         call append(line, spaces.'}')
464         call append(line, spaces.a:name.'='.dir.' '.c_d.'{')
465         if a:recursive
466             exec 'cd '.a:absolute_dir
467             call s:VimDirListing("*", '', "\010", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
468             cd -
469             let dirs=b:dirs
470             let dcount=b:dircount
471             unlet b:files b:filecount b:dirs b:dircount
472             while dcount > 0
473                 let dname = substitute(dirs,  '\(\( \|\f\|:\)*\).*', '\1', '')
474                 let edname = escape(dname, ' ')
475                 let dirs = substitute(dirs, '\( \|\f\|:\)*.\(.*\)', '\2', '')
476                 let line=s:GenerateEntry(1, line + 1, dname, a:absolute_dir.'/'.edname, edname, '', '', a:filter, a:foldlev+1, a:sort)
477                 let dcount=dcount-1
478             endwhile
479         endif
480         return line+1
481     endfunction " >>>
482     " s:DoEntryFromDir(line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort) <<<
483     "   Generate the fold from the directory hierarchy (if recursive), then
484     "   fill it in with RefreshEntriesFromDir()
485     function! s:DoEntryFromDir(recursive, line, name, absolute_dir, dir, c_d, filter_directive, filter, foldlev, sort)
486         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)
487         normal! j
488         call s:RefreshEntriesFromDir(1)
489     endfunction ">>>
490     " s:CreateEntriesFromDir(recursive) <<<
491     "   Prompts user for information and then calls s:DoEntryFromDir()
492     function! s:CreateEntriesFromDir(recursive)
493         " Save a mark for the current cursor position
494         normal! mk
495         let line=line('.')
496         let name = inputdialog('Enter the Name of the Entry: ')
497         if strlen(name) == 0
498             return
499         endif
500         let foldlev=foldlevel(line)
501         if (foldclosed(line) != -1) || (getline(line) =~ '}')
502             let foldlev=foldlev - 1
503         endif
504         let absolute = (foldlev <= 0)?'Absolute ': ''
505         let home=''
506         let filter='*'
507         if (match(g:proj_flags, '\Cb') != -1) && has('browse')
508             " Note that browse() is inconsistent: On Win32 you can't select a
509             " directory, and it gives you a relative path.
510             let dir = browse(0, 'Enter the '.absolute.'Directory to Load: ', '', '')
511             let dir = fnamemodify(dir, ':p')
512         else
513             let dir = inputdialog('Enter the '.absolute.'Directory to Load: ', '')
514         endif
515         if (dir[strlen(dir)-1] == '/') || (dir[strlen(dir)-1] == '\\')
516             let dir=strpart(dir, 0, strlen(dir)-1) " Remove trailing / or \
517         endif
518         let dir = substitute(dir, '^\~', $HOME, 'g')
519         if (foldlev > 0)
520             let parent_directive=s:RecursivelyConstructDirectives(line)
521             let filter = s:GetFilter(parent_directive, '*')
522             let home=s:GetHome(parent_directive, '')
523             if home[strlen(home)-1] != '/' && home[strlen(home)-1] != '\\'
524                 let home=home.'/'
525             endif
526             unlet parent_directive
527             if s:IsAbsolutePath(dir)
528                 " It is not a relative path  Try to make it relative
529                 let hend=matchend(dir, '\C'.glob(home))
530                 if hend != -1
531                     let dir=strpart(dir, hend)          " The directory can be a relative path
532                 else
533                     let home=""
534                 endif
535             endif
536         endif
537         if strlen(home.dir) == 0
538             return
539         endif
540         if !isdirectory(home.dir)
541             if has("unix")
542                 silent exec '!mkdir '.home.dir.' > /dev/null'
543             else
544                 call confirm('"'.home.dir.'" is not a valid directory.', "&OK", 1)
545                 return
546             endif
547         endif
548         let c_d = inputdialog('Enter the CD parameter: ', '')
549         let filter_directive = inputdialog('Enter the File Filter: ', '')
550         if strlen(filter_directive) != 0
551             let filter = filter_directive
552         endif
553         " If I'm on a closed fold, go to the bottom of it
554         if foldclosedend(line) != -1
555             let line = foldclosedend(line)
556         endif
557         let foldlev = foldlevel(line)
558         " If we're at the end of a fold . . .
559         if getline(line) =~ '}'
560             let foldlev = foldlev - 1           " . . . decrease the indentation by 1.
561         endif
562         " Do the work
563         call s:DoEntryFromDir(a:recursive, line, name, home.dir, dir, c_d, filter_directive, filter, foldlev, 0)
564         " Restore the cursor position
565         normal! `k
566     endfunction ">>>
567     " s:RefreshEntriesFromDir(recursive) <<<
568     "   Finds metadata at the top of the fold, and then replaces all files
569     "   with the contents of the directory.  Works recursively if recursive is 1.
570     function! s:RefreshEntriesFromDir(recursive)
571         if foldlevel('.') == 0
572             echo 'Nothing to refresh.'
573             return
574         endif
575         " Open the fold.
576         if getline('.') =~ '}'
577             normal! zo[z
578         else
579             normal! zo]z[z
580         endif
581         let just_a_fold=0
582         let infoline = s:RecursivelyConstructDirectives(line('.'))
583         let immediate_infoline = getline('.')
584         if strlen(substitute(immediate_infoline, '[^=]*=\(\(\f\|:\|\\ \)*\).*', '\1', '')) == strlen(immediate_infoline)
585             let just_a_fold = 1
586         endif
587         " Extract the home directory of the fold
588         let home = s:GetHome(infoline, '')
589         if home == ''
590             " No Match.  This means that this is just a label with no
591             " directory entry.
592             if a:recursive == 0
593                 return          " We're done--nothing to do
594             endif
595             " Mark that it is just a fold, so later we don't delete filenames
596             " that aren't there.
597             let just_a_fold = 1
598         endif
599         if just_a_fold == 0
600             " Extract the filter between quotes (we don't care what CD is).
601             let filter = s:GetFilter(infoline, '*')
602             " Extract the description (name) of the fold
603             let name = substitute(infoline, '^[#\t ]*\([^=]*\)=.*', '\1', '')
604             if strlen(name) == strlen(infoline)
605                 return                  " If there's no name, we're done.
606             endif
607             if (home == '') || (name == '')
608                 return
609             endif
610             " Extract the flags
611             let flags = s:GetFlags(immediate_infoline)
612             let sort = (match(g:proj_flags, '\CS') != -1)
613             if flags != ''
614                 if match(flags, '\Cr') != -1
615                     " If the flags do not contain r (refresh), then treat it just
616                     " like a fold
617                     let just_a_fold = 1
618                 endif
619                 if match(flags, '\CS') != -1
620                     let sort = 1
621                 endif
622                 if match(flags, '\Cs') != -1
623                     let sort = 0
624                 endif
625             else
626                 let flags=''
627             endif
628         endif
629         " Move to the first non-fold boundary line
630         normal! j
631         " Delete filenames until we reach the end of the fold
632         while getline('.') !~ '}'
633             if line('.') == line('$')
634                 break
635             endif
636             if getline('.') !~ '{'
637                 " We haven't reached a sub-fold, so delete what's there.
638                 if (just_a_fold == 0) && (getline('.') !~ '^\s*#') && (getline('.') !~ '#.*pragma keep')
639                     d _
640                 else
641                     " Skip lines only in a fold and comment lines
642                     normal! j
643                 endif
644             else
645                 " We have reached a sub-fold. If we're doing recursive, then
646                 " call this function again. If not, find the end of the fold.
647                 if a:recursive == 1
648                     call s:RefreshEntriesFromDir(1)
649                     normal! ]zj
650                 else
651                     if foldclosed('.') == -1
652                         normal! zc
653                     endif
654                     normal! j
655                 endif
656             endif
657         endwhile
658         if just_a_fold == 0
659             " We're not just in a fold, and we have deleted all the filenames.
660             " Now it is time to regenerate what is in the directory.
661             if !isdirectory(glob(home))
662                 call confirm('"'.home.'" is not a valid directory.', "&OK", 1)
663             else
664                 let foldlev=foldlevel('.')
665                 " T flag.  Thanks Tomas Z.
666                 if (match(flags, '\Ct') != -1) || ((match(g:proj_flags, '\CT') == -1) && (match(flags, '\CT') == -1))
667                     " Go to the top of the fold (force other folds to the
668                     " bottom)
669                     normal! [z
670                     normal! j
671                     " Skip any comments
672                     while getline('.') =~ '^\s*#'
673                         normal! j
674                     endwhile
675                 endif
676                 normal! k
677                 let cwd=getcwd()
678                 let spaces=strpart('                                               ', 0, foldlev)
679                 exec 'cd '.home
680                 if match(g:proj_flags, '\Ci') != -1
681                     echon home."\r"
682                 endif
683                 call s:VimDirListing(filter, spaces, "\n", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
684                 if b:filecount > 0
685                     normal! mk
686                     silent! put =b:files
687                     normal! `kj
688                     if sort
689                         call s:SortR(line('.'), line('.') + b:filecount - 1)
690                     endif
691                 else
692                     normal! j
693                 endif
694                 unlet b:files b:filecount b:dirs b:dircount
695                 exec 'cd '.cwd
696             endif
697         endif
698         " Go to the top of the refreshed fold.
699         normal! [z
700     endfunction ">>>
701     " s:MoveUp() <<<
702     "   Moves the entity under the cursor up a line.
703     function! s:MoveUp()
704         let lineno=line('.')
705         if lineno == 1
706             return
707         endif
708         let fc=foldclosed('.')
709         let a_reg=@a
710         if lineno == line('$')
711             normal! "add"aP
712         else
713             normal! "addk"aP
714         endif
715         let @a=a_reg
716         if fc != -1
717             normal! zc
718         endif
719     endfunction ">>>
720     " s:MoveDown() <<<
721     "   Moves the entity under the cursor down a line.
722     function! s:MoveDown()
723         let fc=foldclosed('.')
724         let a_reg=@a
725         normal! "add"ap
726         let @a=a_reg
727         if (fc != -1) && (foldclosed('.') == -1)
728             normal! zc
729         endif
730     endfunction " >>>
731     " s:DisplayInfo() <<<
732     "   Displays filename and current working directory when i (info) is in
733     "   the flags.
734     function! s:DisplayInfo()
735         if match(g:proj_flags, '\Ci') != -1
736             echo 'file: '.expand('%').', cwd: '.getcwd().', lines: '.line('$')
737         endif
738     endfunction ">>>
739     " s:SetupAutoCommand(cwd) <<<
740     "   Sets up an autocommand to ensure that the cwd is set to the one
741     "   desired for the fold regardless.  :lcd only does this on a per-window
742     "   basis, not a per-buffer basis.
743     function! s:SetupAutoCommand(cwd)
744         if !exists("b:proj_has_autocommand")
745             let b:proj_cwd_save = escape(getcwd(), ' ')
746             let b:proj_has_autocommand = 1
747             let bufname=escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ')
748             exec 'au BufEnter '.bufname." let b:proj_cwd_save=escape(getcwd(), ' ') | cd ".a:cwd
749             exec 'au BufLeave '.bufname.' exec "cd ".b:proj_cwd_save'
750             exec 'au BufWipeout '.bufname.' au! * '.bufname
751         endif
752     endfunction ">>>
753     " s:SetupScriptAutoCommand(bufcmd, script) <<<
754     "   Sets up an autocommand to run the scriptin script.
755     function! s:SetupScriptAutoCommand(bufcmd, script)
756         if !exists("b:proj_has_".a:bufcmd)
757             let b:proj_has_{a:bufcmd} = 1
758             exec 'au '.a:bufcmd.' '.escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ').' source '.a:script
759         endif
760     endfunction " >>>
761     " s:DoEnsurePlacementSize_au() <<<
762     "   Ensure that the Project window is on the left of the window and has
763     "   the correct size. Only called from an autocommand
764     function! s:DoEnsurePlacementSize_au()
765         if (winbufnr(0) != g:proj_running) || (winnr() != 1)
766             if exists("g:proj_doinghelp")
767                 if g:proj_doinghelp > 0
768                     let g:proj_doinghelp = g:proj_doinghelp - 1
769                     return
770                 endif
771                 unlet g:proj_doinghelp
772                 return
773             endif
774             exec b:proj_locate_command
775         endif
776         exec b:proj_resize_command
777     endfunction ">>>
778     " s:Spawn(number) <<<
779     "   Spawn an external command on the file
780     function! s:Spawn(number)
781         echo | if exists("g:proj_run".a:number)
782             let fname=getline('.')
783             if fname!~'{\|}'
784                 let fname=substitute(fname, '\s*#.*', '', '')
785                 let fname=substitute(fname, '^\s*\(.*\)\s*', '\1', '')
786                 if fname == '' | return | endif
787                 let parent_infoline = s:RecursivelyConstructDirectives(line('.'))
788                 let home=expand(s:GetHome(parent_infoline, ''))
789                 let c_d=expand(s:GetCd(parent_infoline, ''))
790                 let command=substitute(g:proj_run{a:number}, '%%', "\010", 'g')
791                 let command=substitute(command, '%f', escape(home.'/'.fname, '\'), 'g')
792                 let command=substitute(command, '%F', substitute(escape(home.'/'.fname, '\'), ' ', '\\\\ ', 'g'), 'g')
793                 let command=substitute(command, '%s', escape(home.'/'.fname, '\'), 'g')
794                 let command=substitute(command, '%n', escape(fname, '\'), 'g')
795                 let command=substitute(command, '%N', substitute(fname, ' ', '\\\\ ', 'g'), 'g')
796                 let command=substitute(command, '%h', escape(home, '\'), 'g')
797                 let command=substitute(command, '%H', substitute(escape(home, '\'), ' ', '\\\\ ', 'g'), 'g')
798                 if c_d != ''
799                     if c_d == home
800                         let percent_r='.'
801                     else
802                         let percent_r=substitute(home, escape(c_d.'/', '\'), '', 'g')
803                     endif
804                 else
805                     let percent_r=home
806                 endif
807                 let command=substitute(command, '%r', percent_r, 'g')
808                 let command=substitute(command, '%R', substitute(percent_r, ' ', '\\\\ ', 'g'), 'g')
809                 let command=substitute(command, '%d', escape(c_d, '\'), 'g')
810                 let command=substitute(command, '%D', substitute(escape(c_d, '\'), ' ', '\\\\ ', 'g'), 'g')
811                 let command=substitute(command, "\010", '%', 'g')
812                 exec command
813             endif
814         endif
815     endfunction ">>>
816     " s:ListSpawn(varnamesegment) <<<
817     "   List external commands
818     function! s:ListSpawn(varnamesegment)
819         let number = 1
820         while number < 10
821             if exists("g:proj_run".a:varnamesegment.number)
822                 echohl LineNr | echo number.':' | echohl None | echon ' '.substitute(escape(g:proj_run{a:varnamesegment}{number}, '\'), "\n", '\\n', 'g')
823             else
824                 echohl LineNr | echo number.':' | echohl None
825             endif
826             let number=number + 1
827         endwhile
828     endfunction ">>>
829     " s:FindFoldTop(line) <<<
830     "   Return the line number of the directive line
831     function! s:FindFoldTop(line)
832         let lineno=a:line
833         if getline(lineno) =~ '}'
834             let lineno = lineno - 1
835         endif
836         while getline(lineno) !~ '{' && lineno > 1
837             if getline(lineno) =~ '}'
838                 let lineno=s:FindFoldTop(lineno)
839             endif
840             let lineno = lineno - 1
841         endwhile
842         return lineno
843     endfunction ">>>
844     " s:FindFoldBottom(line) <<<
845     "   Return the line number of the directive line
846     function! s:FindFoldBottom(line)
847         let lineno=a:line
848         if getline(lineno) =~ '{'
849             let lineno=lineno + 1
850         endif
851         while getline(lineno) !~ '}' && lineno < line('$')
852             if getline(lineno) =~ '{'
853                 let lineno=s:FindFoldBottom(lineno)
854             endif
855             let lineno = lineno + 1
856         endwhile
857         return lineno
858     endfunction ">>>
859     " s:LoadAll(recurse, line) <<<
860     "   Load all files in a project
861     function! s:LoadAll(recurse, line)
862         let b:loadcount=0
863         function! s:SpawnExec(infoline, fname, lineno, data)
864             if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'e')
865                 wincmd p
866                 let b:loadcount=b:loadcount+1
867                 echon b:loadcount."\r"
868                 if getchar(0) != 0
869                     let b:stop_everything=1
870                 endif
871             endif
872         endfunction
873         call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
874         delfunction s:SpawnExec
875         echon b:loadcount." Files Loaded\r"
876         unlet b:loadcount
877         if exists("b:stop_everything") | unlet b:stop_everything | endif
878     endfunction ">>>
879     " s:WipeAll(recurse, line) <<<
880     "   Wipe all files in a project
881     function! s:WipeAll(recurse, line)
882         let b:wipecount=0
883         let b:totalcount=0
884         function! s:SpawnExec(home, c_d, fname, lineno, data)
885             let fname=escape(a:fname, ' ')
886             if s:IsAbsolutePath(fname)
887                 let fname=fnamemodify(fname, ':n')  " :n is coming, won't break anything now
888             else
889                 let fname=fnamemodify(a:home.'/'.fname, ':n')  " :n is coming, won't break anything now
890             endif
891             let b:totalcount=b:totalcount+1
892             let fname=substitute(fname, '^\~', $HOME, 'g')
893             if bufloaded(substitute(fname, '\\ ', ' ', 'g'))
894                 if getbufvar(fname.'\>', '&modified') == 1
895                     exec 'sb '.fname
896                     wincmd L
897                     w
898                     wincmd p
899                 endif
900                 let b:wipecount=b:wipecount+1
901                 exec 'bwipe! '.fname
902             endif
903             if b:totalcount % 5 == 0
904                 echon b:wipecount.' of '.b:totalcount."\r"
905                 redraw
906             endif
907             if getchar(0) != 0
908                 let b:stop_everything=1
909             endif
910         endfunction
911         call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", 0, '^\(.*w\)\@!')
912         delfunction s:SpawnExec
913         echon b:wipecount.' of '.b:totalcount." Files Wiped\r"
914         unlet b:wipecount b:totalcount
915         if exists("b:stop_everything") | unlet b:stop_everything | endif
916     endfunction ">>>
917     " s:LoadAllSplit(recurse, line) <<<
918     "   Load all files in a project using split windows.
919     "   Contributed by A. Harrison
920     function! s:LoadAllSplit(recurse, line)
921         let b:loadcount=0
922         function! s:SpawnExec(infoline, fname, lineno, data)
923             let winNr = winnr() "get ProjectWindow number
924             if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'sp')
925                 exec winNr."wincmd w"
926                 let b:loadcount=b:loadcount+1
927                 echon b:loadcount."\r"
928                 if getchar(0) != 0
929                     let b:stop_everything=1
930                 endif
931             endif
932         endfunction
933         call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
934         delfunction s:SpawnExec
935         echon b:loadcount." Files Loaded\r"
936         unlet b:loadcount
937         if exists("b:stop_everything") | unlet b:stop_everything | endif
938     endfunction ">>>
939     " s:GrepAll(recurse, lineno, pattern) <<<
940     "   Grep all files in a project, optionally recursively
941     function! s:GrepAll(recurse, lineno, pattern)
942         cunmap <buffer> help
943         let pattern=(a:pattern[0] == '')?input("GREP options and pattern: "):a:pattern
944         cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help
945         if pattern[0] == ''
946             return
947         endif
948         let b:escape_spaces=1
949         let fnames=Project_GetAllFnames(a:recurse, a:lineno, ' ')
950         unlet b:escape_spaces
951         cclose " Make sure grep window is closed
952         call s:DoSetupAndSplit()
953         if match(g:proj_flags, '\Cv') == -1
954             silent! exec 'silent! grep '.pattern.' '.fnames
955             if v:shell_error != 0
956                 echo 'GREP error. Perhaps there are too many filenames.'
957             else
958                 copen
959             endif
960         else
961             silent! exec 'silent! vimgrep '.pattern.' '.fnames
962             copen
963         endif
964     endfunction ">>>
965     " GetXXX Functions <<<
966     function! s:GetHome(info, parent_home)
967         " Thanks to Adam Montague for pointing out the need for @ in urls.
968         let home=substitute(a:info, '^[^=]*=\(\(\\ \|\f\|:\|@\)\+\).*', '\1', '')
969         if strlen(home) == strlen(a:info)
970             let home=substitute(a:info, '.\{-}"\(.\{-}\)".*', '\1', '')
971             if strlen(home) != strlen(a:info) | let home=escape(home, ' ') | endif
972         endif
973         if strlen(home) == strlen(a:info)
974             let home=a:parent_home
975         elseif home=='.'
976             let home=a:parent_home
977         elseif !s:IsAbsolutePath(home)
978             let home=a:parent_home.'/'.home
979         endif
980         return home
981     endfunction
982     function! s:GetFilter(info, parent_filter)
983         let filter = substitute(a:info, '.*\<filter="\([^"]*\).*', '\1', '')
984         if strlen(filter) == strlen(a:info) | let filter = a:parent_filter | endif
985         return filter
986     endfunction
987     function! s:GetCd(info, home)
988         let c_d=substitute(a:info, '.*\<CD=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
989         if strlen(c_d) == strlen(a:info)
990             let c_d=substitute(a:info, '.*\<CD="\(.\{-}\)".*', '\1', '')
991             if strlen(c_d) != strlen(a:info) | let c_d=escape(c_d, ' ') | endif
992         endif
993         if strlen(c_d) == strlen(a:info)
994             let c_d=''
995         elseif c_d == '.'
996             let c_d = a:home
997         elseif !s:IsAbsolutePath(c_d)
998             let c_d = a:home.'/'.c_d
999         endif
1000         return c_d
1001     endfunction
1002     function! s:GetScriptin(info, home)
1003         let scriptin = substitute(a:info, '.*\<in=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
1004         if strlen(scriptin) == strlen(a:info)
1005             let scriptin=substitute(a:info, '.*\<in="\(.\{-}\)".*', '\1', '')
1006             if strlen(scriptin) != strlen(a:info) | let scriptin=escape(scriptin, ' ') | endif
1007         endif
1008         if strlen(scriptin) == strlen(a:info) | let scriptin='' | else
1009         if !s:IsAbsolutePath(scriptin) | let scriptin=a:home.'/'.scriptin | endif | endif
1010         return scriptin
1011     endfunction
1012     function! s:GetScriptout(info, home)
1013         let scriptout = substitute(a:info, '.*\<out=\(\(\\ \|\f\|:\)\+\).*', '\1', '')
1014         if strlen(scriptout) == strlen(a:info)
1015             let scriptout=substitute(a:info, '.*\<out="\(.\{-}\)".*', '\1', '')
1016             if strlen(scriptout) != strlen(a:info) | let scriptout=escape(scriptout, ' ') | endif
1017         endif
1018         if strlen(scriptout) == strlen(a:info) | let scriptout='' | else
1019         if !s:IsAbsolutePath(scriptout) | let scriptout=a:home.'/'.scriptout | endif | endif
1020         return scriptout
1021     endfunction
1022     function! s:GetFlags(info)
1023         let flags=substitute(a:info, '.*\<flags=\([^ {]*\).*', '\1', '')
1024         if (strlen(flags) == strlen(a:info))
1025             let flags=''
1026         endif
1027         return flags
1028     endfunction ">>>
1029     " Project_GetAllFnames(recurse, lineno, separator) <<<
1030     "   Grep all files in a project, optionally recursively
1031     function! Project_GetAllFnames(recurse, lineno, separator)
1032         let b:fnamelist=''
1033         function! s:SpawnExec(home, c_d, fname, lineno, data)
1034             if exists('b:escape_spaces')
1035                 let fname=escape(a:fname, ' ')
1036             else
1037                 let fname=a:fname
1038             endif
1039             if !s:IsAbsolutePath(a:fname)
1040                 let fname=a:home.'/'.fname
1041             endif
1042             let b:fnamelist=b:fnamelist.a:data.fname
1043         endfunction
1044         call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:separator, '')
1045         delfunction s:SpawnExec
1046         let retval=b:fnamelist
1047         unlet b:fnamelist
1048         return retval
1049     endfunction ">>>
1050     " Project_GetAllFnames(recurse, lineno, separator) <<<
1051     "   Grep all files in a project, optionally recursively
1052     function! Project_GetFname(line)
1053         if (foldlevel(a:line) == 0)
1054             return ''
1055         endif
1056         let fname=substitute(getline(a:line), '\s*#.*', '', '') " Get rid of comments and whitespace before comment
1057         let fname=substitute(fname, '^\s*\(.*\)', '\1', '') " Get rid of leading whitespace
1058         if strlen(fname) == 0
1059             return ''                    " The line is blank. Do nothing.
1060         endif
1061         if s:IsAbsolutePath(fname)
1062             return fname
1063         endif
1064         let infoline = s:RecursivelyConstructDirectives(a:line)
1065         return s:GetHome(infoline, '').'/'.fname
1066     endfunction ">>>
1067     " Project_ForEach(recurse, lineno, cmd, data, match) <<<
1068     "   Grep all files in a project, optionally recursively
1069     function! Project_ForEach(recurse, lineno, cmd, data, match)
1070         let info=s:RecursivelyConstructDirectives(a:lineno)
1071         let lineno=s:FindFoldTop(a:lineno) + 1
1072         let flags=s:GetFlags(getline(lineno - 1))
1073         if (flags == '') || (a:match=='') || (match(flags, a:match) != -1)
1074             call s:Project_ForEachR(a:recurse, lineno, info, a:cmd, a:data, a:match)
1075         endif
1076     endfunction
1077     function! s:Project_ForEachR(recurse, lineno, info, cmd, data, match)
1078         let home=s:GetHome(a:info, '')
1079         let c_d=s:GetCd(a:info, home)
1080         let scriptin = s:GetScriptin(a:info, home)
1081         let scriptout = s:GetScriptout(a:info, home)
1082         let filter = s:GetFilter(a:info, '')
1083         let lineno = a:lineno
1084         let curline=getline(lineno)
1085         while (curline !~ '}') && (curline < line('$'))
1086             if exists("b:stop_everything") && b:stop_everything | return 0 | endif
1087             if curline =~ '{'
1088                 if a:recurse
1089                     let flags=s:GetFlags(curline)
1090                     if (flags == '') || (a:match=='') || (match(flags, a:match) != -1)
1091                         let this_home=s:GetHome(curline, home)
1092                         let this_cd=s:GetCd(curline, this_home)
1093                         if this_cd=='' | let this_cd=c_d | endif
1094                         let this_scriptin=s:GetScriptin(curline, this_home)
1095                         if this_scriptin == '' | let this_scriptin=scriptin | endif
1096                         let this_scriptout=s:GetScriptin(curline, this_home)
1097                         if this_scriptout == '' | let this_scriptout=scriptout | endif
1098                         let this_filter=s:GetFilter(curline, filter)
1099                         let lineno=s:Project_ForEachR(1, lineno+1,
1100                             \s:ConstructInfo(this_home, this_cd, this_scriptin, this_scriptout, flags, this_filter), a:cmd, a:data, a:match)
1101                     else
1102                         let lineno=s:FindFoldBottom(lineno)
1103                     endif
1104                 else
1105                     let lineno=s:FindFoldBottom(lineno)
1106                 endif
1107             else
1108                 let fname=substitute(curline, '\s*#.*', '', '')
1109                 let fname=substitute(fname, '^\s*\(.*\)', '\1', '')
1110                 if (strlen(fname) != strlen(curline)) && (fname[0] != '')
1111                     if a:cmd[0] == '*'
1112                         call {strpart(a:cmd, 1)}(a:info, fname, lineno, a:data)
1113                     else
1114                         call {a:cmd}(home, c_d, fname, lineno, a:data)
1115                     endif
1116                 endif
1117             endif
1118             let lineno=lineno + 1
1119             let curline=getline(lineno)
1120         endwhile
1121         return lineno
1122     endfunction ">>>
1123     " s:SpawnAll(recurse, number) <<<
1124     "   Spawn an external command on the files of a project
1125     function! s:SpawnAll(recurse, number)
1126         echo | if exists("g:proj_run_fold".a:number)
1127             if g:proj_run_fold{a:number}[0] == '*'
1128                 function! s:SpawnExec(home, c_d, fname, lineno, data)
1129                     let command=substitute(strpart(g:proj_run_fold{a:data}, 1), '%s', escape(a:fname, ' \'), 'g')
1130                     let command=substitute(command, '%f', escape(a:fname, '\'), 'g')
1131                     let command=substitute(command, '%h', escape(a:home, '\'), 'g')
1132                     let command=substitute(command, '%d', escape(a:c_d, '\'), 'g')
1133                     let command=substitute(command, '%F', substitute(escape(a:fname, '\'), ' ', '\\\\ ', 'g'), 'g')
1134                     exec command
1135                 endfunction
1136                 call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:number, '.')
1137                 delfunction s:SpawnExec
1138             else
1139                 let info=s:RecursivelyConstructDirectives(line('.'))
1140                 let home=s:GetHome(info, '')
1141                 let c_d=s:GetCd(info, '')
1142                 let b:escape_spaces=1
1143                 let fnames=Project_GetAllFnames(a:recurse, line('.'), ' ')
1144                 unlet b:escape_spaces
1145                 let command=substitute(g:proj_run_fold{a:number}, '%f', substitute(escape(fnames, '\'), '\\ ', ' ', 'g'), 'g')
1146                 let command=substitute(command, '%s', escape(fnames, '\'), 'g')
1147                 let command=substitute(command, '%h', escape(home, '\'), 'g')
1148                 let command=substitute(command, '%d', escape(c_d, '\'), 'g')
1149                 let command=substitute(command, '%F', escape(fnames, '\'), 'g')
1150                 exec command
1151                 if v:shell_error != 0
1152                     echo 'Shell error. Perhaps there are too many filenames.'
1153                 endif
1154             endif
1155         endif
1156     endfunction ">>>
1157     if !exists("g:proj_running")
1158         " s:DoProjectOnly(void) <<<
1159         "   Make the file window the only one.
1160         function! s:DoProjectOnly()
1161             if winbufnr(0) != g:proj_running
1162                 let lzsave=&lz
1163                 set lz
1164                 only
1165                 Project
1166                 silent! wincmd p
1167                 let &lz=lzsave
1168                 unlet lzsave
1169             endif
1170         endfunction
1171         " >>>
1173         " Mappings <<<
1174         nnoremap <buffer> <silent> <Return>   \|:call <SID>DoFoldOrOpenEntry('', 'e')<CR>
1175         nnoremap <buffer> <silent> <S-Return> \|:call <SID>DoFoldOrOpenEntry('', 'sp')<CR>
1176         nnoremap <buffer> <silent> <C-Return> \|:call <SID>DoFoldOrOpenEntry('silent! only', 'e')<CR>
1177         nnoremap <buffer> <silent> <LocalLeader>T \|:call <SID>DoFoldOrOpenEntry('', 'tabe')<CR>
1178         nmap     <buffer> <silent> <LocalLeader>s <S-Return>
1179         nnoremap <buffer> <silent> <LocalLeader>S \|:call <SID>LoadAllSplit(0, line('.'))<CR>
1180         nmap     <buffer> <silent> <LocalLeader>o <C-Return>
1181         nnoremap <buffer> <silent> <LocalLeader>i :echo <SID>RecursivelyConstructDirectives(line('.'))<CR>
1182         nnoremap <buffer> <silent> <LocalLeader>I :echo Project_GetFname(line('.'))<CR>
1183         nmap     <buffer> <silent> <M-CR> <Return><C-W>p
1184         nmap     <buffer> <silent> <LocalLeader>v <M-CR>
1185         nnoremap <buffer> <silent> <LocalLeader>l \|:call <SID>LoadAll(0, line('.'))<CR>
1186         nnoremap <buffer> <silent> <LocalLeader>L \|:call <SID>LoadAll(1, line('.'))<CR>
1187         nnoremap <buffer> <silent> <LocalLeader>w \|:call <SID>WipeAll(0, line('.'))<CR>
1188         nnoremap <buffer> <silent> <LocalLeader>W \|:call <SID>WipeAll(1, line('.'))<CR>
1189         nnoremap <buffer> <silent> <LocalLeader>W \|:call <SID>WipeAll(1, line('.'))<CR>
1190         nnoremap <buffer> <silent> <LocalLeader>g \|:call <SID>GrepAll(0, line('.'), "")<CR>
1191         nnoremap <buffer> <silent> <LocalLeader>G \|:call <SID>GrepAll(1, line('.'), "")<CR>
1192         nnoremap <buffer> <silent> <2-LeftMouse>   \|:call <SID>DoFoldOrOpenEntry('', 'e')<CR>
1193         nnoremap <buffer> <silent> <S-2-LeftMouse> \|:call <SID>DoFoldOrOpenEntry('', 'sp')<CR>
1194         nnoremap <buffer> <silent> <M-2-LeftMouse> <M-CR>
1195         nnoremap <buffer> <silent> <S-LeftMouse>   <LeftMouse>
1196         nmap     <buffer> <silent> <C-2-LeftMouse> <C-Return>
1197         nnoremap <buffer> <silent> <C-LeftMouse>   <LeftMouse>
1198         nnoremap <buffer> <silent> <3-LeftMouse>  <Nop>
1199         nmap     <buffer> <silent> <RightMouse>   <space>
1200         nmap     <buffer> <silent> <2-RightMouse> <space>
1201         nmap     <buffer> <silent> <3-RightMouse> <space>
1202         nmap     <buffer> <silent> <4-RightMouse> <space>
1203         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>
1204         nnoremap <buffer> <silent> <C-Up>   \|:silent call <SID>MoveUp()<CR>
1205         nnoremap <buffer> <silent> <C-Down> \|:silent call <SID>MoveDown()<CR>
1206         nmap     <buffer> <silent> <LocalLeader><Up> <C-Up>
1207         nmap     <buffer> <silent> <LocalLeader><Down> <C-Down>
1208         let k=1
1209         while k < 10
1210             exec 'nnoremap <buffer> <LocalLeader>'.k.'  \|:call <SID>Spawn('.k.')<CR>'
1211             exec 'nnoremap <buffer> <LocalLeader>f'.k.' \|:call <SID>SpawnAll(0, '.k.')<CR>'
1212             exec 'nnoremap <buffer> <LocalLeader>F'.k.' \|:call <SID>SpawnAll(1, '.k.')<CR>'
1213             let k=k+1
1214         endwhile
1215         nnoremap <buffer>          <LocalLeader>0 \|:call <SID>ListSpawn("")<CR>
1216         nnoremap <buffer>          <LocalLeader>f0 \|:call <SID>ListSpawn("_fold")<CR>
1217         nnoremap <buffer>          <LocalLeader>F0 \|:call <SID>ListSpawn("_fold")<CR>
1218         nnoremap <buffer> <silent> <LocalLeader>c :call <SID>CreateEntriesFromDir(0)<CR>
1219         nnoremap <buffer> <silent> <LocalLeader>C :call <SID>CreateEntriesFromDir(1)<CR>
1220         nnoremap <buffer> <silent> <LocalLeader>r :call <SID>RefreshEntriesFromDir(0)<CR>
1221         nnoremap <buffer> <silent> <LocalLeader>R :call <SID>RefreshEntriesFromDir(1)<CR>
1222         " For Windows users: same as \R
1223         nnoremap <buffer> <silent>           <F5> :call <SID>RefreshEntriesFromDir(1)<CR>
1224         nnoremap <buffer> <silent> <LocalLeader>e :call <SID>OpenEntry(line('.'), '', '', 0)<CR>
1225         nnoremap <buffer> <silent> <LocalLeader>E :call <SID>OpenEntry(line('.'), '', 'e', 1)<CR>
1226         " The :help command stomps on the Project Window.  Try to avoid that.
1227         " This is not perfect, but it is alot better than without the mappings.
1228         cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help
1229         nnoremap <buffer> <F1> :let g:proj_doinghelp = 1<CR><F1>
1230         " This is to avoid changing the buffer, but it is not fool-proof.
1231         nnoremap <buffer> <silent> <C-^> <Nop>
1232         "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>
1233         nnoremap <script> <Plug>ProjectOnly :call <SID>DoProjectOnly()<CR>
1234         if match(g:proj_flags, '\Cm') != -1
1235             if !hasmapto('<Plug>ProjectOnly')
1236                 nmap <silent> <unique> <C-W>o <Plug>ProjectOnly
1237                 nmap <silent> <unique> <C-W><C-O> <C-W>o
1238             endif
1239         endif " >>>
1240         if filereadable(glob('~/.vimproject_mappings')) | source ~/.vimproject_mappings | endif
1241         " Autocommands <<<
1242         " Autocommands to clean up if we do a buffer wipe
1243         " These don't work unless we substitute \ for / for Windows
1244         let bufname=escape(substitute(expand('%:p', 0), '\\', '/', 'g'), ' ')
1245         exec 'au BufWipeout '.bufname.' au! * '.bufname
1246         exec 'au BufWipeout '.bufname.' unlet g:proj_running'
1247         exec 'au BufWipeout '.bufname.' nunmap <C-W>o'
1248         exec 'au BufWipeout '.bufname.' nunmap <C-W><C-O>'
1249         " Autocommands to keep the window the specified size
1250         exec 'au WinLeave '.bufname.' call s:DoEnsurePlacementSize_au()'
1251         exec 'au BufEnter '.bufname.' call s:DoSetupAndSplit_au()'
1252         au WinLeave * call s:RecordPrevBuffer_au()
1253         " >>>
1254         setlocal buflisted
1255         let g:proj_running = bufnr(bufname.'\>')
1256         if g:proj_running == -1
1257             call confirm('Project/Vim error. Please Enter :Project again and report this bug.', "&OK", 1)
1258             unlet g:proj_running
1259         endif
1260         setlocal nobuflisted
1261     endif
1262 endfunction " >>>
1264 if exists(':Project') != 2
1265     command -nargs=? -complete=file Project call <SID>Project('<args>')
1266 endif
1267 " Toggle Mapping
1268 if !exists("*<SID>DoToggleProject()") "<<<
1269     function! s:DoToggleProject()
1270         if !exists('g:proj_running') || bufwinnr(g:proj_running) == -1
1271             Project
1272         else
1273             let g:proj_mywindow = winnr()
1274             Project
1275             hide
1276             if(winnr() != g:proj_mywindow)
1277                 wincmd p
1278             endif
1279             unlet g:proj_mywindow
1280         endif
1281     endfunction
1282 endif ">>>
1283 nnoremap <script> <Plug>ToggleProject :call <SID>DoToggleProject()<CR>
1284 if exists('g:proj_flags') && (match(g:proj_flags, '\Cg') != -1)
1285     if !hasmapto('<Plug>ToggleProject')
1286         nmap <silent> <F12> <Plug>ToggleProject
1287     endif
1288 endif
1290 finish
1292 " vim600: set foldmethod=marker foldmarker=<<<,>>> foldlevel=1: