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))
410 call s:OpenEntry(line('.'), a:cmd0, a:cmd1, 0)
413 " s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount) <<<
414 function! s:VimDirListing(filter, padding, separator, filevariable, filecount, dirvariable, dircount)
417 let filter = a:filter
419 " Apparently glob() cannot take something like this: glob('*.c *.h')
422 let end = stridx(filter, ' ')
424 let end = strlen(filter)
427 let single=glob(strpart(filter, 0, end))
428 if strlen(single) != 0
429 let files = files.single."\010"
431 let filter = strpart(filter, end + 1)
433 " files now contains a list of everything in the directory. We need to
434 " weed out the directories.
436 let {a:filevariable}=''
437 let {a:dirvariable}=''
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
447 let {a:filevariable}={a:filevariable}.a:padding.fname.a:separator
448 let {a:filecount}={a:filecount} + 1
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)
456 let dir='"'.substitute(a:dir, '\\ ', ' ', 'g').'"'
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.'{')
466 exec 'cd '.a:absolute_dir
467 call s:VimDirListing("*", '', "\010", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
470 let dcount=b:dircount
471 unlet b:files b:filecount b:dirs b:dircount
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)
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)
488 call s:RefreshEntriesFromDir(1)
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
496 let name = inputdialog('Enter the Name of the Entry: ')
500 let foldlev=foldlevel(line)
501 if (foldclosed(line) != -1) || (getline(line) =~ '}')
502 let foldlev=foldlev - 1
504 let absolute = (foldlev <= 0)?'Absolute ': ''
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')
513 let dir = inputdialog('Enter the '.absolute.'Directory to Load: ', '')
515 if (dir[strlen(dir)-1] == '/') || (dir[strlen(dir)-1] == '\\')
516 let dir=strpart(dir, 0, strlen(dir)-1) " Remove trailing / or \
518 let dir = substitute(dir, '^\~', $HOME, 'g')
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] != '\\'
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))
531 let dir=strpart(dir, hend) " The directory can be a relative path
537 if strlen(home.dir) == 0
540 if !isdirectory(home.dir)
542 silent exec '!mkdir '.home.dir.' > /dev/null'
544 call confirm('"'.home.dir.'" is not a valid directory.', "&OK", 1)
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
553 " If I'm on a closed fold, go to the bottom of it
554 if foldclosedend(line) != -1
555 let line = foldclosedend(line)
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.
563 call s:DoEntryFromDir(a:recursive, line, name, home.dir, dir, c_d, filter_directive, filter, foldlev, 0)
564 " Restore the cursor position
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.'
576 if getline('.') =~ '}'
582 let infoline = s:RecursivelyConstructDirectives(line('.'))
583 let immediate_infoline = getline('.')
584 if strlen(substitute(immediate_infoline, '[^=]*=\(\(\f\|:\|\\ \)*\).*', '\1', '')) == strlen(immediate_infoline)
587 " Extract the home directory of the fold
588 let home = s:GetHome(infoline, '')
590 " No Match. This means that this is just a label with no
593 return " We're done--nothing to do
595 " Mark that it is just a fold, so later we don't delete filenames
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.
607 if (home == '') || (name == '')
611 let flags = s:GetFlags(immediate_infoline)
612 let sort = (match(g:proj_flags, '\CS') != -1)
614 if match(flags, '\Cr') != -1
615 " If the flags do not contain r (refresh), then treat it just
619 if match(flags, '\CS') != -1
622 if match(flags, '\Cs') != -1
629 " Move to the first non-fold boundary line
631 " Delete filenames until we reach the end of the fold
632 while getline('.') !~ '}'
633 if line('.') == line('$')
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')
641 " Skip lines only in a fold and comment lines
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.
648 call s:RefreshEntriesFromDir(1)
651 if foldclosed('.') == -1
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)
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
672 while getline('.') =~ '^\s*#'
678 let spaces=strpart(' ', 0, foldlev)
680 if match(g:proj_flags, '\Ci') != -1
683 call s:VimDirListing(filter, spaces, "\n", 'b:files', 'b:filecount', 'b:dirs', 'b:dircount')
689 call s:SortR(line('.'), line('.') + b:filecount - 1)
694 unlet b:files b:filecount b:dirs b:dircount
698 " Go to the top of the refreshed fold.
702 " Moves the entity under the cursor up a line.
708 let fc=foldclosed('.')
710 if lineno == line('$')
721 " Moves the entity under the cursor down a line.
722 function! s:MoveDown()
723 let fc=foldclosed('.')
727 if (fc != -1) && (foldclosed('.') == -1)
731 " s:DisplayInfo() <<<
732 " Displays filename and current working directory when i (info) is in
734 function! s:DisplayInfo()
735 if match(g:proj_flags, '\Ci') != -1
736 echo 'file: '.expand('%').', cwd: '.getcwd().', lines: '.line('$')
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
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
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
771 unlet g:proj_doinghelp
774 exec b:proj_locate_command
776 exec b:proj_resize_command
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('.')
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')
802 let percent_r=substitute(home, escape(c_d.'/', '\'), '', 'g')
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')
816 " s:ListSpawn(varnamesegment) <<<
817 " List external commands
818 function! s:ListSpawn(varnamesegment)
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')
824 echohl LineNr | echo number.':' | echohl None
826 let number=number + 1
829 " s:FindFoldTop(line) <<<
830 " Return the line number of the directive line
831 function! s:FindFoldTop(line)
833 if getline(lineno) =~ '}'
834 let lineno = lineno - 1
836 while getline(lineno) !~ '{' && lineno > 1
837 if getline(lineno) =~ '}'
838 let lineno=s:FindFoldTop(lineno)
840 let lineno = lineno - 1
844 " s:FindFoldBottom(line) <<<
845 " Return the line number of the directive line
846 function! s:FindFoldBottom(line)
848 if getline(lineno) =~ '{'
849 let lineno=lineno + 1
851 while getline(lineno) !~ '}' && lineno < line('$')
852 if getline(lineno) =~ '{'
853 let lineno=s:FindFoldBottom(lineno)
855 let lineno = lineno + 1
859 " s:LoadAll(recurse, line) <<<
860 " Load all files in a project
861 function! s:LoadAll(recurse, line)
863 function! s:SpawnExec(infoline, fname, lineno, data)
864 if s:OpenEntry2(a:lineno, a:infoline, a:fname, 'e')
866 let b:loadcount=b:loadcount+1
867 echon b:loadcount."\r"
869 let b:stop_everything=1
873 call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
874 delfunction s:SpawnExec
875 echon b:loadcount." Files Loaded\r"
877 if exists("b:stop_everything") | unlet b:stop_everything | endif
879 " s:WipeAll(recurse, line) <<<
880 " Wipe all files in a project
881 function! s:WipeAll(recurse, line)
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
889 let fname=fnamemodify(a:home.'/'.fname, ':n') " :n is coming, won't break anything now
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
900 let b:wipecount=b:wipecount+1
903 if b:totalcount % 5 == 0
904 echon b:wipecount.' of '.b:totalcount."\r"
908 let b:stop_everything=1
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
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)
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"
929 let b:stop_everything=1
933 call Project_ForEach(a:recurse, line('.'), "*<SID>SpawnExec", 0, '^\(.*l\)\@!')
934 delfunction s:SpawnExec
935 echon b:loadcount." Files Loaded\r"
937 if exists("b:stop_everything") | unlet b:stop_everything | endif
939 " s:GrepAll(recurse, lineno, pattern) <<<
940 " Grep all files in a project, optionally recursively
941 function! s:GrepAll(recurse, lineno, pattern)
943 let pattern=(a:pattern[0] == '')?input("GREP options and pattern: "):a:pattern
944 cnoremap <buffer> help let g:proj_doinghelp = 1<CR>:help
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.'
961 silent! exec 'silent! vimgrep '.pattern.' '.fnames
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
973 if strlen(home) == strlen(a:info)
974 let home=a:parent_home
976 let home=a:parent_home
977 elseif !s:IsAbsolutePath(home)
978 let home=a:parent_home.'/'.home
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
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
993 if strlen(c_d) == strlen(a:info)
997 elseif !s:IsAbsolutePath(c_d)
998 let c_d = a:home.'/'.c_d
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
1008 if strlen(scriptin) == strlen(a:info) | let scriptin='' | else
1009 if !s:IsAbsolutePath(scriptin) | let scriptin=a:home.'/'.scriptin | endif | endif
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
1018 if strlen(scriptout) == strlen(a:info) | let scriptout='' | else
1019 if !s:IsAbsolutePath(scriptout) | let scriptout=a:home.'/'.scriptout | endif | endif
1022 function! s:GetFlags(info)
1023 let flags=substitute(a:info, '.*\<flags=\([^ {]*\).*', '\1', '')
1024 if (strlen(flags) == strlen(a:info))
1029 " Project_GetAllFnames(recurse, lineno, separator) <<<
1030 " Grep all files in a project, optionally recursively
1031 function! Project_GetAllFnames(recurse, lineno, separator)
1033 function! s:SpawnExec(home, c_d, fname, lineno, data)
1034 if exists('b:escape_spaces')
1035 let fname=escape(a:fname, ' ')
1039 if !s:IsAbsolutePath(a:fname)
1040 let fname=a:home.'/'.fname
1042 let b:fnamelist=b:fnamelist.a:data.fname
1044 call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:separator, '')
1045 delfunction s:SpawnExec
1046 let retval=b:fnamelist
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)
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.
1061 if s:IsAbsolutePath(fname)
1064 let infoline = s:RecursivelyConstructDirectives(a:line)
1065 return s:GetHome(infoline, '').'/'.fname
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)
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
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)
1102 let lineno=s:FindFoldBottom(lineno)
1105 let lineno=s:FindFoldBottom(lineno)
1108 let fname=substitute(curline, '\s*#.*', '', '')
1109 let fname=substitute(fname, '^\s*\(.*\)', '\1', '')
1110 if (strlen(fname) != strlen(curline)) && (fname[0] != '')
1112 call {strpart(a:cmd, 1)}(a:info, fname, lineno, a:data)
1114 call {a:cmd}(home, c_d, fname, lineno, a:data)
1118 let lineno=lineno + 1
1119 let curline=getline(lineno)
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')
1136 call Project_ForEach(a:recurse, line('.'), "<SID>SpawnExec", a:number, '.')
1137 delfunction s:SpawnExec
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')
1151 if v:shell_error != 0
1152 echo 'Shell error. Perhaps there are too many filenames.'
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
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>
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>'
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
1240 if filereadable(glob('~/.vimproject_mappings')) | source ~/.vimproject_mappings | endif
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()
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
1260 setlocal nobuflisted
1264 if exists(':Project') != 2
1265 command -nargs=? -complete=file Project call <SID>Project('<args>')
1268 if !exists("*<SID>DoToggleProject()") "<<<
1269 function! s:DoToggleProject()
1270 if !exists('g:proj_running') || bufwinnr(g:proj_running) == -1
1273 let g:proj_mywindow = winnr()
1276 if(winnr() != g:proj_mywindow)
1279 unlet g:proj_mywindow
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
1292 " vim600: set foldmethod=marker foldmarker=<<<,>>> foldlevel=1: