1 "=============================================================================
3 " Author: Aric Blumer (Aric.Blumer at aricvim@charter.net)
4 " Last Change: Fri 13 Oct 2006 09:47:08 AM EDT
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
15 function! s:Project(filename) " <<<
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)
21 let filename=bufname(g:proj_running)
23 if strlen(a:filename) == 0
24 let filename ='~/.vimprojects' " Default project filename
26 let filename = a:filename
29 if !exists('g:proj_window_width')
30 let g:proj_window_width=24 " Default project window width
32 if !exists('g:proj_window_increment')
33 let g:proj_window_increment=100 " Project Window width increment
35 if !exists('g:proj_flags')
36 if has("win32") || has("mac")
37 let g:proj_flags='imst' " Project default flags for windows/mac
39 let g:proj_flags='imstb' " Project default flags for everything else
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
46 exec 'vertical resize '.g:proj_window_width
51 if bufwinnr(g:proj_running) == -1
53 let v:errmsg="nothing"
55 if 'nothing' != v:errmsg
62 let b:proj_cd_cmd='cd'
63 if match(g:proj_flags, '\Cl') != -1
64 let b:proj_cd_cmd = 'lcd'
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=''
74 let g:proj_last_buffer = -1
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*', '', '')
84 " Ensure everything is set up
86 setlocal foldenable foldmethod=marker foldmarker={,} commentstring=%s foldcolumn=0 nonumber noswapfile shiftwidth=1
87 setlocal foldtext=ProjFoldText() nobuflisted nowrap
89 if match(g:proj_flags, '\Cn') != -1
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
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)
137 let partition = a:start - 1
138 let middle = partition
139 let partStr = getline((a:start + a:end) / 2)
151 let partition = partition + 1
153 let middle = partition
156 let str2 = getline(partition)
157 call setline(i, str2)
158 call setline(partition, str)
163 if (middle != partition)
164 let str = getline(middle)
165 let str2 = getline(partition)
166 call setline(middle, str2)
167 call setline(partition, str)
169 call s:SortR(a:start, partition - 1)
170 call s:SortR(partition + 1, a:end)
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:'
179 let path=expand(a:path) " Expand any environment variables that might be in the path
183 if path[0] == '/' || path[0] == '~' || path[0] == '\\' || path[1] == ':'
188 " s:DoSetupAndSplit() <<<
189 " Call DoSetup to ensure the settings are correct. Split to the next
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
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'
204 exec 'silent vertical split | silent! bnext'
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
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
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
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
230 exec b:proj_locate_command
231 exec b:proj_resize_command
234 function! s:RecordPrevBuffer_au()
235 let g:proj_last_buffer = bufnr('%')
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 = ''
245 while foldlevel(lineno) >= foldlev " Go to parent fold
247 echoerr 'Some kind of fold error. Check your syntax.'
250 let lineno = lineno - 1
252 let parent_infoline = s:RecursivelyConstructDirectives(lineno)
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)
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
268 " Extract any CD information
269 let c_d = s:GetCd(infoline, home)
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
279 let scriptin = s:GetScriptin(infoline, home)
281 let scriptin = parent_scriptin
284 let scriptout = s:GetScriptout(infoline, home)
286 let scriptout = parent_scriptout
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)
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
297 let retval=retval.' CD='.a:c_d
299 if a:scriptin[0] != ''
300 let retval=retval.' in='.a:scriptin
302 if a:scriptout[0] != ''
303 let retval=retval.' out='.a:scriptout
306 let retval=retval.' filter="'.a:filter.'"'
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)
314 if (a:editcmd[0] != '')
318 if (foldlevel(a:line) == 0) && (a:editcmd[0] != '')
319 return 0 " If we're outside a fold, do nothing
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.
330 let infoline = s:RecursivelyConstructDirectives(a:line)
331 let retval=s:OpenEntry2(a:line, infoline, fname, a:editcmd)
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, '').'/'
342 echoerr 'Project structure error. Check your syntax.'
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)
353 if s:IsAbsolutePath(fname) == 2
354 exec a:editcmd.' '.fname
356 silent exec 'silent '.a:editcmd.' '.fname
358 else " only happens in the Project File
359 exec 'au! BufEnter,BufLeave '.expand('%:p')
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)
367 if !isdirectory(glob(c_d))
368 call confirm("From this fold's entry,\nCD=".'"'.c_d.'" is not a valid directory.', "&OK", 1)
370 silent exec cd_cmd.' '.c_d
373 " Extract any scriptin information
374 let scriptin = s:GetScriptin(a:infoline, home)
376 if !filereadable(glob(scriptin))
377 call confirm('"'.scriptin.'" not found. Ignoring.', "&OK", 1)
379 call s:SetupScriptAutoCommand('BufEnter', scriptin)
380 exec 'source '.scriptin
383 let scriptout = s:GetScriptout(a:infoline, home)
385 if !filereadable(glob(scriptout))
386 call confirm('"'.scriptout.'" not found. Ignoring.', "&OK", 1)
388 call s:SetupScriptAutoCommand('BufLeave', scriptout)
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
401 call s:DoEnsurePlacementSize_au()
402 if (match(g:proj_flags, '\Cc') != -1)
403 let g:proj_mywinnumber = winbufnr(0)
405 if(g:proj_mywinnumber != winbufnr(0))
411 call s:OpenEntry(line('.'), a:cmd0, a:cmd1, 0)
414 " s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount) <<<
415 function! s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount)
418 let filter = a:filter
420 " Apparently glob() cannot take something like this: glob('*.c *.h')
423 let end = stridx(filter, ' ')
425 let end = strlen(filter)
428 let single=glob(strpart(filter, 0, end))
429 if strlen(single) != 0
430 let files = files.single."\010"
432 let filter = strpart(filter, end + 1)
434 " files now contains a list of everything in the directory. We need to
435 " weed out the directories.
437 let {a:filevariable}=''
438 let {a:dirvariable}=''
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
448 let {a:filevariable}={a:filevariable}.a:padding.fname.a:separator
449 let {a:filecount}={a:filecount} + 1
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)
457 let dir='"'.substitute(a:dir, '\\ ', ' ', 'g').'"'
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.'{')
467 exec 'cd '.a:absolute_dir
468 call s:VimDirListing("*", '', "\010", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
471 let dcount=b:dircount
472 unlet b:files b:filecount b:dirs b:dircount
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)
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)
489 call s:RefreshEntriesFromDir(1)
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
497 let name = inputdialog('Enter the Name of the Entry: ')
501 let foldlev=foldlevel(line)
502 if (foldclosed(line) != -1) || (getline(line) =~ '}')
503 let foldlev=foldlev - 1
505 let absolute = (foldlev <= 0)?'Absolute ': ''
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')
514 let dir = inputdialog('Enter the '.absolute.'Directory to Load: ', '')
516 if (dir[strlen(dir)-1] == '/') || (dir[strlen(dir)-1] == '\\')
517 let dir=strpart(dir, 0, strlen(dir)-1) " Remove trailing / or \
519 let dir = substitute(dir, '^\~', $HOME, 'g')
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] != '\\'
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))
532 let dir=strpart(dir, hend) " The directory can be a relative path
538 if strlen(home.dir) == 0
541 if !isdirectory(home.dir)
543 silent exec '!mkdir '.home.dir.' > /dev/null'
545 call confirm('"'.home.dir.'" is not a valid directory.', "&OK", 1)
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
554 " If I'm on a closed fold, go to the bottom of it
555 if foldclosedend(line) != -1
556 let line = foldclosedend(line)
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.
564 call s:DoEntryFromDir(a:recursive, line, name, home.dir, dir, c_d, filter_directive, filter, foldlev, 0)
565 " Restore the cursor position
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.'
577 if getline('.') =~ '}'
583 let infoline = s:RecursivelyConstructDirectives(line('.'))
584 let immediate_infoline = getline('.')
585 if strlen(substitute(immediate_infoline, '[^=]*=\(\(\f\|:\|\\ \)*\).*', '\1', '')) == strlen(immediate_infoline)
588 " Extract the home directory of the fold
589 let home = s:GetHome(infoline, '')
591 " No Match. This means that this is just a label with no
594 return " We're done--nothing to do
596 " Mark that it is just a fold, so later we don't delete filenames
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.
608 if (home == '') || (name == '')
612 let flags = s:GetFlags(immediate_infoline)
613 let sort = (match(g:proj_flags, '\CS') != -1)
615 if match(flags, '\Cr') != -1
616 " If the flags do not contain r (refresh), then treat it just
620 if match(flags, '\CS') != -1
623 if match(flags, '\Cs') != -1
630 " Move to the first non-fold boundary line
632 " Delete filenames until we reach the end of the fold
633 while getline('.') !~ '}'
634 if line('.') == line('$')
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')
642 " Skip lines only in a fold and comment lines
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.
649 call s:RefreshEntriesFromDir(1)
652 if foldclosed('.') == -1
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)
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
673 while getline('.') =~ '^\s*#'
679 let spaces=strpart(' ', 0, foldlev)
681 if match(g:proj_flags, '\Ci') != -1
684 call s:VimDirListing(filter, spaces, "\n", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
690 call s:SortR(line('.'), line('.') + b:filecount - 1)
695 unlet b:files b:filecount b:dirs b:dircount
699 " Go to the top of the refreshed fold.
703 " Moves the entity under the cursor up a line.
709 let fc=foldclosed('.')
711 if lineno == line('$')
722 " Moves the entity under the cursor down a line.
723 function! s:MoveDown()
724 let fc=foldclosed('.')
728 if (fc != -1) && (foldclosed('.') == -1)
732 " s:DisplayInfo() <<<
733 " Displays filename and current working directory when i (info) is in
735 function! s:DisplayInfo()
736 if match(g:proj_flags, '\Ci') != -1
737 echo 'file: '.expand('%').', cwd: '.getcwd().', lines: '.line('$')
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
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
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
772 unlet g:proj_doinghelp
775 exec b:proj_locate_command
777 exec b:proj_resize_command
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('.')
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')
803 let percent_r=substitute(home, escape(c_d.'/', '\'), '', 'g')
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')
817 " s:ListSpawn(varnamesegment) <<<
818 " List external commands
819 function! s:ListSpawn(varnamesegment)
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')
825 echohl LineNr | echo number.':' | echohl None
827 let number=number + 1
830 " s:FindFoldTop(line) <<<
831 " Return the line number of the directive line
832 function! s:FindFoldTop(line)
834 if getline(lineno) =~ '}'
835 let lineno = lineno - 1
837 while getline(lineno) !~ '{' && lineno > 1
838 if getline(lineno) =~ '}'
839 let lineno=s:FindFoldTop(lineno)
841 let lineno = lineno - 1
845 " s:FindFoldBottom(line) <<<
846 " Return the line number of the directive line
847 function! s:FindFoldBottom(line)
849 if getline(lineno) =~ '{'
850 let lineno=lineno + 1
852 while getline(lineno) !~ '}' && lineno < line('$')
853 if getline(lineno) =~ '{'
854 let lineno=s:FindFoldBottom(lineno)
856 let lineno = lineno + 1
860 " s:LoadAll(recurse, line) <<<
861 " Load all files in a project
862 function! s:LoadAll(recurse, line)
864 function! s:SpawnExec(infoline, fname, lineno, data)
865 if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'e')
867 let b:loadcount=b:loadcount+1
868 echon b:loadcount."\r"
870 let b:stop_everything=1
874 call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
875 delfunction s:SpawnExec
876 echon b:loadcount." Files Loaded\r"
878 if exists("b:stop_everything") | unlet b:stop_everything | endif
880 " s:WipeAll(recurse, line) <<<
881 " Wipe all files in a project
882 function! s:WipeAll(recurse, line)
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
890 let fname=fnamemodify(a:home.'/'.fname, ':n') " :n is coming, won't break anything now
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
901 let b:wipecount=b:wipecount+1
904 if b:totalcount % 5 == 0
905 echon b:wipecount.' of '.b:totalcount."\r"
909 let b:stop_everything=1
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
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)
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"
930 let b:stop_everything=1
934 call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
935 delfunction s:SpawnExec
936 echon b:loadcount." Files Loaded\r"
938 if exists("b:stop_everything") | unlet b:stop_everything | endif
940 " s:GrepAll(recurse, lineno, pattern) <<<
941 " Grep all files in a project, optionally recursively
942 function! s:GrepAll(recurse, lineno, pattern)
944 let pattern=(a:pattern[0] == '')?input("GREP options and pattern: "):a:pattern
945 cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help
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.'
962 silent! exec 'silent! vimgrep '.pattern.' '.fnames
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
974 if strlen(home) == strlen(a:info)
975 let home=a:parent_home
977 let home=a:parent_home
978 elseif !s:IsAbsolutePath(home)
979 let home=a:parent_home.'/'.home
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
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
994 if strlen(c_d) == strlen(a:info)
998 elseif !s:IsAbsolutePath(c_d)
999 let c_d = a:home.'/'.c_d
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
1009 if strlen(scriptin) == strlen(a:info) | let scriptin='' | else
1010 if !s:IsAbsolutePath(scriptin) | let scriptin=a:home.'/'.scriptin | endif | endif
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
1019 if strlen(scriptout) == strlen(a:info) | let scriptout='' | else
1020 if !s:IsAbsolutePath(scriptout) | let scriptout=a:home.'/'.scriptout | endif | endif
1023 function! s:GetFlags(info)
1024 let flags=substitute(a:info, '.*\<flags=\([^ {]*\).*', '\1', '')
1025 if (strlen(flags) == strlen(a:info))
1030 " Project_GetAllFnames(recurse, lineno, separator) <<<
1031 " Grep all files in a project, optionally recursively
1032 function! Project_GetAllFnames(recurse, lineno, separator)
1034 function! s:SpawnExec(home, c_d, fname, lineno, data)
1035 if exists('b:escape_spaces')
1036 let fname=escape(a:fname, ' ')
1040 if !s:IsAbsolutePath(a:fname)
1041 let fname=a:home.'/'.fname
1043 let b:fnamelist=b:fnamelist.a:data.fname
1045 call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:separator, '')
1046 delfunction s:SpawnExec
1047 let retval=b:fnamelist
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)
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.
1062 if s:IsAbsolutePath(fname)
1065 let infoline = s:RecursivelyConstructDirectives(a:line)
1066 return s:GetHome(infoline, '').'/'.fname
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)
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
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)
1103 let lineno=s:FindFoldBottom(lineno)
1106 let lineno=s:FindFoldBottom(lineno)
1109 let fname=substitute(curline, '\s*#.*', '', '')
1110 let fname=substitute(fname, '^\s*\(.*\)', '\1', '')
1111 if (strlen(fname) != strlen(curline)) && (fname[0] != '')
1113 call {strpart(a:cmd, 1)}(a:info, fname, lineno, a:data)
1115 call {a:cmd}(home, c_d, fname, lineno, a:data)
1119 let lineno=lineno + 1
1120 let curline=getline(lineno)
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')
1137 call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:number, '.')
1138 delfunction s:SpawnExec
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')
1152 if v:shell_error != 0
1153 echo 'Shell error. Perhaps there are too many filenames.'
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
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>
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>'
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
1241 if filereadable(glob('~/.vimproject_mappings')) | source ~/.vimproject_mappings | endif
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()
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
1261 setlocal nobuflisted
1265 if exists(':Project') != 2
1266 command -nargs=? -complete=file Project call <SID>Project('<args>')
1269 if !exists("*<SID>DoToggleProject()") "<<<
1270 function! s:DoToggleProject()
1271 if !exists('g:proj_running') || bufwinnr(g:proj_running) == -1
1274 let g:proj_mywindow = winnr()
1277 if(winnr() != g:proj_mywindow)
1280 unlet g:proj_mywindow
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
1293 " vim600: set foldmethod=marker foldmarker=<<<,>>> foldlevel=1: