1 " vimball.vim : construct a file containing both paths and files
2 " Author: Charles E. Campbell, Jr.
5 " GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim
6 " Copyright: (c) 2004-2008 by Charles E. Campbell, Jr.
7 " The VIM LICENSE applies to Vimball.vim, and Vimball.txt
8 " (see |copyright|) except use "Vimball" instead of "Vim".
9 " No warranty, express or implied.
10 " *** *** Use At-Your-Own-Risk! *** ***
12 " ---------------------------------------------------------------------
14 if &cp || exists("g:loaded_vimball") || v:version < 700
18 let g:loaded_vimball = "v29"
22 " =====================================================================
29 " determine if cygwin is in use or not
30 if !exists("g:netrw_cygwin")
31 if has("win32") || has("win95") || has("win64") || has("win16")
32 if &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$'
42 " set up g:vimball_mkdir if the mkdir() call isn't defined
44 if exists("g:netrw_local_mkdir")
45 let g:vimball_mkdir= g:netrw_local_mkdir
46 elseif executable("mkdir")
47 let g:vimball_mkdir= "mkdir"
48 elseif executable("makedir")
49 let g:vimball_mkdir= "makedir"
51 if !exists(g:vimball_mkdir)
52 call vimball#ShowMesg(s:WARNING,"(vimball) g:vimball_mkdir undefined")
56 " set up shell quoting character
57 if exists("g:vimball_shq") && !exists("g:netrw_shq")
58 let g:netrw_shq= g:vimball_shq
60 if !exists("g:netrw_shq")
61 if exists("&shq") && &shq != ""
63 elseif has("win32") || has("win95") || has("win64") || has("win16")
72 " call Decho("g:netrw_shq<".g:netrw_shq.">")
75 " set up escape string (used to protect paths)
76 if !exists("g:vimball_path_escape")
77 let g:vimball_path_escape= ' ;#%'
81 " =====================================================================
84 " ---------------------------------------------------------------------
85 " vimball#MkVimball: creates a vimball given a list of paths to files {{{2
87 " line1,line2: a range of lines containing paths to files to be included in the vimball
88 " writelevel : if true, force a write to filename.vba, even if it exists
89 " (usually accomplished with :MkVimball! ...
90 " filename : base name of file to be created (ie. filename.vba)
91 " Output: a filename.vba using vimball format:
98 fun! vimball#MkVimball(line1,line2,writelevel,...) range
99 " call Dfunc("MkVimball(line1=".a:line1." line2=".a:line2." writelevel=".a:writelevel." vimballname<".a:1.">) a:0=".a:0)
100 if a:1 =~ '\.vim$' || a:1 =~ '\.txt$'
101 let vbname= substitute(a:1,'\.\a\{3}$','.vba','')
105 if vbname !~ '\.vba$'
106 let vbname= vbname.'.vba'
108 " call Decho("vbname<".vbname.">")
110 call vimball#ShowMesg(s:ERROR,"(MkVimball) vimball name<".a:1."> should not include slashes")
111 " call Dret("MkVimball : vimball name<".a:1."> should not include slashes")
114 if !a:writelevel && filereadable(vbname)
115 call vimball#ShowMesg(s:ERROR,"(MkVimball) file<".vbname."> exists; use ! to insist")
116 " call Dret("MkVimball : file<".vbname."> already exists; use ! to insist")
121 call vimball#SaveSettings()
124 " allow user to specify where to get the files
125 let home= expand(a:2)
127 " use first existing directory from rtp
128 let home= s:VimballHome()
131 " save current directory
132 let curdir = getcwd()
135 " record current tab, initialize while loop index
136 let curtabnr = tabpagenr()
138 " call Decho("curtabnr=".curtabnr)
140 while linenr <= a:line2
141 let svfile = getline(linenr)
142 " call Decho("svfile<".svfile.">")
144 if !filereadable(svfile)
145 call vimball#ShowMesg(s:ERROR,"unable to read file<".svfile.">")
146 call s:ChgDir(curdir)
147 call vimball#RestoreSettings()
148 " call Dret("MkVimball")
152 " create/switch to mkvimball tab
153 if !exists("vbtabnr")
156 let vbtabnr= tabpagenr()
161 let lastline= line("$") + 1
162 if lastline == 2 && getline("$") == ""
163 call setline(1,'" Vimball Archiver by Charles E. Campbell, Jr., Ph.D.')
164 call setline(2,'UseVimball')
165 call setline(3,'finish')
166 let lastline= line("$") + 1
168 call setline(lastline ,substitute(svfile,'$',' [[[1',''))
169 call setline(lastline+1,0)
171 " write the file from the tab
172 " call Decho("exe $r ".fnameescape(svfile))
173 exe "$r ".fnameescape(svfile)
175 call setline(lastline+1,line("$") - lastline - 1)
176 " call Decho("lastline=".lastline." line$=".line("$"))
178 " restore to normal tab
180 let linenr= linenr + 1
185 call s:ChgDir(curdir)
188 " call Decho("exe w! ".fnameescape(vbname))
189 exe "w! ".fnameescape(vbname)
191 " call Decho("exe w ".fnameescape(vbname))
192 exe "w ".fnameescape(vbname)
194 " call Decho("Vimball<".vbname."> created")
195 echo "Vimball<".vbname."> created"
197 " remove the evidence
198 setlocal nomod bh=wipe
203 call vimball#RestoreSettings()
205 " call Dret("MkVimball")
208 " ---------------------------------------------------------------------
209 " vimball#Vimball: extract and distribute contents from a vimball {{{2
210 " (invoked the the UseVimball command embedded in
211 " vimballs' prologue)
212 fun! vimball#Vimball(really,...)
213 " call Dfunc("vimball#Vimball(really=".a:really.") a:0=".a:0)
215 if v:version < 701 || (v:version == 701 && !exists('*fnameescape'))
216 echoerr "your vim is missing the fnameescape() function"
217 " call Dret("vimball#Vimball : needs 7.1 with patch 299")
221 if getline(1) !~ '^" Vimball Archiver by Charles E. Campbell, Jr., Ph.D.$'
222 echoerr "(Vimball) The current file does not appear to be a Vimball!"
223 " call Dret("vimball#Vimball")
227 " set up standard settings
228 call vimball#SaveSettings()
229 let curtabnr = tabpagenr()
230 let vimballfile = expand("%:tr")
233 " call Decho("setting up vimball tab")
236 let vbtabnr= tabpagenr()
239 " go to vim plugin home
241 let home= expand(a:1)
243 let home= s:VimballHome()
245 " call Decho("home<".home.">")
247 " save current directory and remove older same-named vimball, if any
248 let curdir = getcwd()
249 " call Decho("home<".home.">")
250 " call Decho("curdir<".curdir.">")
253 let s:ok_unablefind= 1
254 call vimball#RmVimball(vimballfile)
255 unlet s:ok_unablefind
260 " give title to listing of (extracted) files from Vimball Archive
262 echohl Title | echomsg "Vimball Archive" | echohl None
264 echohl Title | echomsg "Vimball Archive Listing" | echohl None
265 echohl Statement | echomsg "files would be placed under: ".home | echohl None
268 " apportion vimball contents to various files
269 " call Decho("exe tabn ".curtabnr)
271 " call Decho("linenr=".linenr." line$=".line("$"))
272 while 1 < linenr && linenr < line("$")
273 let fname = substitute(getline(linenr),'\t\[\[\[1$','','')
274 let fname = substitute(fname,'\\','/','g')
275 let fsize = getline(linenr+1)+0
276 let filecnt = filecnt + 1
277 " call Decho("fname<".fname."> fsize=".fsize." filecnt=".filecnt)
280 echomsg "extracted <".fname.">: ".fsize." lines"
282 echomsg "would extract <".fname.">: ".fsize." lines"
284 " call Decho("using L#".linenr.": will extract file<".fname.">")
285 " call Decho("using L#".(linenr+1).": fsize=".fsize)
287 " Allow AsNeeded/ directory to take place of plugin/ directory
288 " when AsNeeded/filename is filereadable or was present in VimballRecord
289 if fname =~ '\<plugin/'
290 let anfname= substitute(fname,'\<plugin/','AsNeeded/','')
291 if filereadable(anfname) || (exists("s:VBRstring") && s:VBRstring =~ anfname)
292 " call Decho("using anfname<".anfname."> instead of <".fname.">")
297 " make directories if they don't exist yet
299 " call Decho("making directories if they don't exist yet (fname<".fname.">)")
300 let fnamebuf= substitute(fname,'\\','/','g')
301 let dirpath = substitute(home,'\\','/','g')
302 while fnamebuf =~ '/'
303 let dirname = dirpath."/".substitute(fnamebuf,'/.*$','','')
304 let dirpath = dirname
305 let fnamebuf = substitute(fnamebuf,'^.\{-}/\(.*\)$','\1','')
306 " call Decho("dirname<".dirname.">")
307 if !isdirectory(dirname)
308 " call Decho("making <".dirname.">")
309 if exists("g:vimball_mkdir")
310 call system(g:vimball_mkdir." ".s:Escape(dirname))
314 call s:RecordInVar(home,"rmdir('".dirname."')")
320 " grab specified qty of lines and place into "a" buffer
321 " (skip over path/filename and qty-lines)
322 let linenr = linenr + 2
323 let lastline = linenr + fsize - 1
324 " call Decho("exe ".linenr.",".lastline."yank a")
325 exe "silent ".linenr.",".lastline."yank a"
327 " copy "a" buffer into tab
328 " call Decho('copy "a buffer into tab#'.vbtabnr)
338 let fnamepath= home."/".fname
339 " call Decho("exe w! ".fnameescape(fnamepath))
340 exe "silent w! ".fnameescape(fnamepath)
341 echo "wrote ".fnamepath
342 call s:RecordInVar(home,"call delete('".fnameescape(fnamepath)."')")
345 " return to tab with vimball
346 " call Decho("exe tabn ".curtabnr)
349 " set up help if its a doc/*.txt file
350 " call Decho("didhelp<".didhelp."> fname<".fname.">")
351 if a:really && didhelp == "" && fname =~ 'doc/[^/]\+\.\(txt\|..x\)$'
352 let didhelp= substitute(fname,'^\(.*\<doc\)[/\\][^.]*\.\(txt\|..x\)$','\1','')
353 " call Decho("didhelp<".didhelp.">")
356 " update for next file
357 " call Decho("update linenr= [linenr=".linenr."] + [fsize=".fsize."] = ".(linenr+fsize))
358 let linenr= linenr + fsize
362 " call Decho("about to set up help: didhelp<".didhelp.">")
364 let htpath= home."/".didhelp
365 " call Decho("exe helptags ".htpath)
366 exe "helptags ".fnameescape(htpath)
370 " make sure a "Press ENTER..." prompt appears to keep the messages showing!
373 let filecnt= filecnt + 1
376 " record actions in <.VimballRecord>
377 call s:RecordInFile(home)
379 " restore events, delete tab and buffer
381 setlocal nomod bh=wipe
384 call vimball#RestoreSettings()
385 call s:ChgDir(curdir)
387 " call Dret("vimball#Vimball")
390 " ---------------------------------------------------------------------
391 " vimball#RmVimball: remove any files, remove any directories made by any {{{2
392 " previous vimball extraction based on a file of the current
394 " Usage: RmVimball (assume current file is a vimball; remove)
395 " RmVimball vimballname
396 fun! vimball#RmVimball(...)
397 " call Dfunc("vimball#RmVimball() a:0=".a:0)
398 if exists("g:vimball_norecord")
399 " call Dret("vimball#RmVimball : (g:vimball_norecord)")
404 let curfile= expand("%:tr")
405 " call Decho("case a:0=0: curfile<".curfile."> (used expand(%:tr))")
408 call vimball#ShowMesg(s:USAGE,"RmVimball vimballname [path]")
409 " call Dret("vimball#RmVimball : suspect a:1<".a:1.">")
413 " call Decho("case a:0=".a:0.": curfile<".curfile.">")
415 if curfile =~ '\.vba$'
416 let curfile= substitute(curfile,'\.vba','','')
419 let home= expand(a:2)
421 let home= s:VimballHome()
423 let curdir = getcwd()
424 " call Decho("home <".home.">")
425 " call Decho("curfile<".curfile.">")
426 " call Decho("curdir <".curdir.">")
429 if filereadable(".VimballRecord")
430 " call Decho(".VimballRecord is readable")
431 " call Decho("curfile<".curfile.">")
432 keepalt keepjumps 1split
433 silent! keepalt keepjumps e .VimballRecord
435 " call Decho("search for ^".curfile.".vba:")
436 " call Decho("search for ^".curfile."[-0-9.]*.vba:")
437 if search('^'.curfile.": ".'cw')
439 elseif search('^'.curfile.".vba: ",'cw')
441 elseif search('^'.curfile.'[-0-9.]*.vba: ','cw')
447 let exestring = substitute(getline("."),'^'.curfile.'\S\{-}\.vba: ','','')
448 let s:VBRstring= substitute(exestring,'call delete(','','g')
449 let s:VBRstring= substitute(s:VBRstring,"[')]",'','g')
450 " call Decho("exe ".exestring)
451 silent! keepalt keepjumps exe exestring
452 silent! keepalt keepjumps d
453 let exestring= strlen(substitute(exestring,'call delete(.\{-})|\=',"D","g"))
454 " call Decho("exestring<".exestring.">")
455 echomsg "removed ".exestring." files"
458 let curfile = substitute(curfile,'\.vba','','')
459 " call Decho("unable to find <".curfile."> in .VimballRecord")
460 if !exists("s:ok_unablefind")
461 call vimball#ShowMesg(s:WARNING,"(RmVimball) unable to find <".curfile."> in .VimballRecord")
464 silent! keepalt keepjumps g/^\s*$/d
465 silent! keepalt keepjumps wq!
468 call s:ChgDir(curdir)
470 " call Dret("vimball#RmVimball")
473 " ---------------------------------------------------------------------
474 " vimball#Decompress: attempts to automatically decompress vimballs {{{2
475 fun! vimball#Decompress(fname)
476 " call Dfunc("Decompress(fname<".a:fname.">)")
479 if expand("%") =~ '.*\.gz' && executable("gunzip")
480 " handle *.gz with gunzip
481 silent exe "!gunzip ".s:Escape(a:fname)
482 if v:shell_error != 0
483 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) gunzip may have failed with <".a:fname.">")
485 let fname= substitute(a:fname,'\.gz$','','')
486 exe "e ".escape(fname,' \')
487 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
489 elseif expand("%") =~ '.*\.gz' && executable("gzip")
490 " handle *.gz with gzip -d
491 silent exe "!gzip -d ".s:Escape(a:fname)
492 if v:shell_error != 0
493 call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "gzip -d" may have failed with <'.a:fname.">")
495 let fname= substitute(a:fname,'\.gz$','','')
496 exe "e ".escape(fname,' \')
497 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
499 elseif expand("%") =~ '.*\.bz2' && executable("bunzip2")
500 " handle *.bz2 with bunzip2
501 silent exe "!bunzip2 ".s:Escape(a:fname)
502 if v:shell_error != 0
503 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) bunzip2 may have failed with <".a:fname.">")
505 let fname= substitute(a:fname,'\.bz2$','','')
506 exe "e ".escape(fname,' \')
507 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
509 elseif expand("%") =~ '.*\.bz2' && executable("bzip2")
510 " handle *.bz2 with bzip2 -d
511 silent exe "!bzip2 -d ".s:Escape(a:fname)
512 if v:shell_error != 0
513 call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "bzip2 -d" may have failed with <'.a:fname.">")
515 let fname= substitute(a:fname,'\.bz2$','','')
516 exe "e ".escape(fname,' \')
517 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
519 elseif expand("%") =~ '.*\.zip' && executable("unzip")
520 " handle *.zip with unzip
521 silent exe "!unzip ".s:Escape(a:fname)
522 if v:shell_error != 0
523 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) unzip may have failed with <".a:fname.">")
525 let fname= substitute(a:fname,'\.zip$','','')
526 exe "e ".escape(fname,' \')
527 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
530 set noma bt=nofile fmr=[[[,]]] fdm=marker
532 " call Dret("Decompress")
535 " ---------------------------------------------------------------------
536 " vimball#ShowMesg: {{{2
537 fun! vimball#ShowMesg(level,msg)
538 " call Dfunc("vimball#ShowMesg(level=".a:level." msg<".a:msg.">)")
539 let rulerkeep = &ruler
540 let showcmdkeep = &showcmd
541 set noruler noshowcmd
545 echomsg "***vimball*** " a:msg
547 if a:level == s:WARNING || a:level == s:USAGE
549 elseif a:level == s:ERROR
552 echomsg "***vimball*** " a:msg
556 if a:level != s:USAGE
557 call inputsave()|let ok= input("Press <cr> to continue")|call inputrestore()
560 let &ruler = rulerkeep
561 let &showcmd = showcmdkeep
563 " call Dret("vimball#ShowMesg")
565 " =====================================================================
566 " s:ChgDir: change directory (in spite of Windoze) {{{2
567 fun! s:ChgDir(newdir)
568 " call Dfunc("ChgDir(newdir<".a:newdir.">)")
569 if (has("win32") || has("win95") || has("win64") || has("win16"))
570 exe 'silent cd '.fnameescape(substitute(a:newdir,'/','\\','g'))
572 exe 'silent cd '.fnameescape(a:newdir)
574 " call Dret("ChgDir : curdir<".getcwd().">")
577 " ---------------------------------------------------------------------
578 " s:RecordInVar: record a un-vimball command in the .VimballRecord file {{{2
579 fun! s:RecordInVar(home,cmd)
580 " call Dfunc("RecordInVar(home<".a:home."> cmd<".a:cmd.">)")
582 " if !exists("s:recorddir")
583 " let s:recorddir= substitute(a:cmd,'^rmdir',"call s:Rmdir",'')
585 " let s:recorddir= s:recorddir."|".substitute(a:cmd,'^rmdir',"call s:Rmdir",'')
587 elseif !exists("s:recordfile")
588 let s:recordfile= a:cmd
590 let s:recordfile= s:recordfile."|".a:cmd
592 " call Dret("RecordInVar : s:recordfile<".(exists("s:recordfile")? s:recordfile : "")."> s:recorddir<".(exists("s:recorddir")? s:recorddir : "").">")
595 " ---------------------------------------------------------------------
596 " s:RecordInFile: {{{2
597 fun! s:RecordInFile(home)
598 " call Dfunc("s:RecordInFile()")
599 if exists("g:vimball_norecord")
600 " call Dret("s:RecordInFile : g:vimball_norecord")
604 if exists("s:recordfile") || exists("s:recorddir")
606 call s:ChgDir(a:home)
607 keepalt keepjumps 1split
609 let cmd= expand("%:tr").": "
610 " call Decho("cmd<".cmd.">")
612 silent! keepalt keepjumps e .VimballRecord
615 if exists("s:recordfile") && exists("s:recorddir")
616 let cmd= cmd.s:recordfile."|".s:recorddir
617 elseif exists("s:recorddir")
618 let cmd= cmd.s:recorddir
619 elseif exists("s:recordfile")
620 let cmd= cmd.s:recordfile
622 " call Dret("s:RecordInFile : neither recordfile nor recorddir exist")
625 " call Decho("cmd<".cmd.">")
627 " put command into buffer, write .VimballRecord `file
628 keepalt keepjumps put=cmd
629 silent! keepalt keepjumps g/^\s*$/d
630 silent! keepalt keepjumps wq!
631 call s:ChgDir(curdir)
633 if exists("s:recorddir")
634 " call Decho("unlet s:recorddir<".s:recorddir.">")
637 if exists("s:recordfile")
638 " call Decho("unlet s:recordfile<".s:recordfile.">")
642 " call Decho("s:record[file|dir] doesn't exist")
645 " call Dret("s:RecordInFile")
648 " ---------------------------------------------------------------------
649 " s:VimballHome: determine/get home directory path (usually from rtp) {{{2
651 " call Dfunc("VimballHome()")
652 if exists("g:vimball_home")
653 let home= g:vimball_home
655 " go to vim plugin home
656 for home in split(&rtp,',') + ['']
657 if isdirectory(home) && filewritable(home) | break | endif
658 let basehome= substitute(home,'[/\\]\.vim$','','')
659 if isdirectory(basehome) && filewritable(basehome)
660 let home= basehome."/.vim"
665 " just pick the first directory
666 let home= substitute(&rtp,',.*$','','')
668 if (has("win32") || has("win95") || has("win64") || has("win16"))
669 let home= substitute(home,'/','\\','g')
672 " insure that the home directory exists
673 " call Decho("picked home<".home.">")
674 if !isdirectory(home)
675 if exists("g:vimball_mkdir")
676 " call Decho("home<".home."> isn't a directory -- making it now with g:vimball_mkdir<".g:vimball_mkdir.">")
677 " call Decho("system(".g:vimball_mkdir." ".s:Escape(home).")")
678 call system(g:vimball_mkdir." ".s:Escape(home))
680 " call Decho("home<".home."> isn't a directory -- making it now with mkdir()")
684 " call Dret("VimballHome <".home.">")
688 " ---------------------------------------------------------------------
689 " vimball#SaveSettings: {{{2
690 fun! vimball#SaveSettings()
691 " call Dfunc("SaveSettings()")
692 let s:makeep = getpos("'a")
698 let s:fenkeep = &l:fen
699 let s:hidkeep = &hidden
703 let s:repkeep = &report
707 setlocal ei=all ve=all noacd nofen noic report=999 nohid bt= ma lz pm= ff=unix
709 setlocal ei=all ve=all nofen noic report=999 nohid bt= ma lz pm= ff=unix
711 " vimballs should be in unix format
713 " call Dret("SaveSettings")
716 " ---------------------------------------------------------------------
717 " vimball#RestoreSettings: {{{2
718 fun! vimball#RestoreSettings()
719 " call Dfunc("RestoreSettings()")
724 let &l:fen = s:fenkeep
725 let &hidden = s:hidkeep
729 let &report = s:repkeep
735 " call Decho("restore mark-a: makeep=".string(makeep))
736 call setpos("'a",s:makeep)
741 unlet s:regakeep s:eikeep s:fenkeep s:hidkeep s:ickeep s:repkeep s:vekeep s:makeep s:lzkeep s:pmkeep s:ffkeep
742 " call Dret("RestoreSettings")
745 " ---------------------------------------------------------------------
748 " shellescape() was added by patch 7.0.111
749 if exists("*shellescape")
750 return shellescape(a:name)
752 return g:netrw_shq . a:name . g:netrw_shq
755 " ---------------------------------------------------------------------
760 " ---------------------------------------------------------------------