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 = "v27"
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 let svfilepath= s:Path(svfile,'')
173 " call Decho("exe $r ".fnameescape(svfilepath))
174 exe "$r ".fnameescape(svfilepath)
176 call setline(lastline+1,line("$") - lastline - 1)
177 " call Decho("lastline=".lastline." line$=".line("$"))
179 " restore to normal tab
181 let linenr= linenr + 1
186 call s:ChgDir(curdir)
189 let vbnamepath= s:Path(vbname,'')
190 " call Decho("exe w! ".fnameescape(vbnamepath))
191 exe "w! ".fnameescape(vbnamepath)
193 let vbnamepath= s:Path(vbname,'')
194 " call Decho("exe w ".fnameescape(vbnamepath))
195 exe "w ".fnameescape(vbnamepath)
197 " call Decho("Vimball<".vbname."> created")
198 echo "Vimball<".vbname."> created"
200 " remove the evidence
201 setlocal nomod bh=wipe
206 call vimball#RestoreSettings()
208 " call Dret("MkVimball")
211 " ---------------------------------------------------------------------
212 " vimball#Vimball: extract and distribute contents from a vimball {{{2
213 " (invoked the the UseVimball command embedded in
214 " vimballs' prologue)
215 fun! vimball#Vimball(really,...)
216 " call Dfunc("vimball#Vimball(really=".a:really.") a:0=".a:0)
218 if v:version < 701 || (v:version == 701 && !has("patch299"))
219 echoerr "This version of vimball requires vim 7.1 with patch 299"
220 " call Dret("vimball#Vimball : needs 7.1 with patch 299")
224 if getline(1) !~ '^" Vimball Archiver by Charles E. Campbell, Jr., Ph.D.$'
225 echoerr "(Vimball) The current file does not appear to be a Vimball!"
226 " call Dret("vimball#Vimball")
230 " set up standard settings
231 call vimball#SaveSettings()
232 let curtabnr = tabpagenr()
233 let vimballfile = expand("%:tr")
236 " call Decho("setting up vimball tab")
239 let vbtabnr= tabpagenr()
242 " go to vim plugin home
244 let home= expand(a:1)
246 let home= s:VimballHome()
248 " call Decho("home<".home.">")
250 " save current directory and remove older same-named vimball, if any
251 let curdir = getcwd()
252 " call Decho("home<".home.">")
253 " call Decho("curdir<".curdir.">")
256 let s:ok_unablefind= 1
257 call vimball#RmVimball(vimballfile)
258 unlet s:ok_unablefind
263 " give title to listing of (extracted) files from Vimball Archive
265 echohl Title | echomsg "Vimball Archive" | echohl None
267 echohl Title | echomsg "Vimball Archive Listing" | echohl None
268 echohl Statement | echomsg "files would be placed under: ".home | echohl None
271 " apportion vimball contents to various files
272 " call Decho("exe tabn ".curtabnr)
274 " call Decho("linenr=".linenr." line$=".line("$"))
275 while 1 < linenr && linenr < line("$")
276 let fname = substitute(getline(linenr),'\t\[\[\[1$','','')
277 let fname = substitute(fname,'\\','/','g')
278 let fsize = getline(linenr+1)+0
279 let filecnt = filecnt + 1
280 " call Decho("fname<".fname."> fsize=".fsize." filecnt=".filecnt)
283 echomsg "extracted <".fname.">: ".fsize." lines"
285 echomsg "would extract <".fname.">: ".fsize." lines"
287 " call Decho("using L#".linenr.": will extract file<".fname.">")
288 " call Decho("using L#".(linenr+1).": fsize=".fsize)
290 " Allow AsNeeded/ directory to take place of plugin/ directory
291 " when AsNeeded/filename is filereadable or was present in VimballRecord
292 if fname =~ '\<plugin/'
293 let anfname= substitute(fname,'\<plugin/','AsNeeded/','')
294 if filereadable(anfname) || (exists("s:VBRstring") && s:VBRstring =~ anfname)
295 " call Decho("using anfname<".anfname."> instead of <".fname.">")
300 " make directories if they don't exist yet
302 " call Decho("making directories if they don't exist yet (fname<".fname.">)")
303 let fnamebuf= substitute(fname,'\\','/','g')
304 let dirpath = substitute(home,'\\','/','g')
305 while fnamebuf =~ '/'
306 let dirname = dirpath."/".substitute(fnamebuf,'/.*$','','')
307 let dirpath = dirname
308 let fnamebuf = substitute(fnamebuf,'^.\{-}/\(.*\)$','\1','')
309 " call Decho("dirname<".dirname.">")
310 if !isdirectory(dirname)
311 " call Decho("making <".dirname.">")
312 if exists("g:vimball_mkdir")
313 call system(g:vimball_mkdir." ".s:Escape(dirname))
317 call s:RecordInVar(home,"rmdir('".dirname."')")
323 " grab specified qty of lines and place into "a" buffer
324 " (skip over path/filename and qty-lines)
325 let linenr = linenr + 2
326 let lastline = linenr + fsize - 1
327 " call Decho("exe ".linenr.",".lastline."yank a")
328 exe "silent ".linenr.",".lastline."yank a"
330 " copy "a" buffer into tab
331 " call Decho('copy "a buffer into tab#'.vbtabnr)
341 let fnamepath= s:Path(home."/".fname,'')
342 " call Decho("exe w! ".fnameescape(fnamepath))
343 exe "silent w! ".fnameescape(fnamepath)
344 echo "wrote ".fnamepath
345 call s:RecordInVar(home,"call delete('".fnameescape(fnamepath)."')")
348 " return to tab with vimball
349 " call Decho("exe tabn ".curtabnr)
352 " set up help if its a doc/*.txt file
353 " call Decho("didhelp<".didhelp."> fname<".fname.">")
354 if a:really && didhelp == "" && fname =~ 'doc/[^/]\+\.\(txt\|..x\)$'
355 let didhelp= substitute(fname,'^\(.*\<doc\)[/\\][^.]*\.\(txt\|..x\)$','\1','')
356 " call Decho("didhelp<".didhelp.">")
359 " update for next file
360 " call Decho("update linenr= [linenr=".linenr."] + [fsize=".fsize."] = ".(linenr+fsize))
361 let linenr= linenr + fsize
365 " call Decho("about to set up help: didhelp<".didhelp.">")
367 let htpath= s:Path(home."/".didhelp,"")
368 " call Decho("exe helptags ".htpath)
369 exe "helptags ".htpath
373 " make sure a "Press ENTER..." prompt appears to keep the messages showing!
376 let filecnt= filecnt + 1
379 " record actions in <.VimballRecord>
380 call s:RecordInFile(home)
382 " restore events, delete tab and buffer
384 setlocal nomod bh=wipe
387 call vimball#RestoreSettings()
388 call s:ChgDir(curdir)
390 " call Dret("vimball#Vimball")
393 " ---------------------------------------------------------------------
394 " vimball#RmVimball: remove any files, remove any directories made by any {{{2
395 " previous vimball extraction based on a file of the current
397 " Usage: RmVimball (assume current file is a vimball; remove)
398 " RmVimball vimballname
399 fun! vimball#RmVimball(...)
400 " call Dfunc("vimball#RmVimball() a:0=".a:0)
401 if exists("g:vimball_norecord")
402 " call Dret("vimball#RmVimball : (g:vimball_norecord)")
407 let curfile= expand("%:tr")
408 " call Decho("case a:0=0: curfile<".curfile."> (used expand(%:tr))")
411 call vimball#ShowMesg(s:USAGE,"RmVimball vimballname [path]")
412 " call Dret("vimball#RmVimball : suspect a:1<".a:1.">")
416 " call Decho("case a:0=".a:0.": curfile<".curfile.">")
418 if curfile =~ '\.vba$'
419 let curfile= substitute(curfile,'\.vba','','')
422 let home= expand(a:2)
424 let home= s:VimballHome()
426 let curdir = getcwd()
427 " call Decho("home <".home.">")
428 " call Decho("curfile<".curfile.">")
429 " call Decho("curdir <".curdir.">")
432 if filereadable(".VimballRecord")
433 " call Decho(".VimballRecord is readable")
434 " call Decho("curfile<".curfile.">")
435 keepalt keepjumps 1split
436 silent! keepalt keepjumps e .VimballRecord
438 " call Decho("search for ^".curfile.".vba:")
439 " call Decho("search for ^".curfile."[-0-9.]*.vba:")
440 if search('^'.curfile.": ".'cw')
442 elseif search('^'.curfile.".vba: ",'cw')
444 elseif search('^'.curfile.'[-0-9.]*.vba: ','cw')
450 let exestring = substitute(getline("."),'^'.curfile.'\S\{-}\.vba: ','','')
451 let s:VBRstring= substitute(exestring,'call delete(','','g')
452 let s:VBRstring= substitute(s:VBRstring,"[')]",'','g')
453 " call Decho("exe ".exestring)
454 silent! keepalt keepjumps exe exestring
455 silent! keepalt keepjumps d
456 let exestring= strlen(substitute(exestring,'call delete(.\{-})|\=',"D","g"))
457 " call Decho("exestring<".exestring.">")
458 echomsg "removed ".exestring." files"
461 let curfile = substitute(curfile,'\.vba','','')
462 " call Decho("unable to find <".curfile."> in .VimballRecord")
463 if !exists("s:ok_unablefind")
464 call vimball#ShowMesg(s:WARNING,"(RmVimball) unable to find <".curfile."> in .VimballRecord")
467 silent! keepalt keepjumps g/^\s*$/d
468 silent! keepalt keepjumps wq!
471 call s:ChgDir(curdir)
473 " call Dret("vimball#RmVimball")
476 " ---------------------------------------------------------------------
477 " vimball#Decompress: attempts to automatically decompress vimballs {{{2
478 fun! vimball#Decompress(fname)
479 " call Dfunc("Decompress(fname<".a:fname.">)")
482 if expand("%") =~ '.*\.gz' && executable("gunzip")
483 " handle *.gz with gunzip
484 silent exe "!gunzip ".s:Escape(a:fname)
485 if v:shell_error != 0
486 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) gunzip may have failed with <".a:fname.">")
488 let fname= substitute(a:fname,'\.gz$','','')
489 exe "e ".escape(fname,' \')
490 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
492 elseif expand("%") =~ '.*\.gz' && executable("gzip")
493 " handle *.gz with gzip -d
494 silent exe "!gzip -d ".s:Escape(a:fname)
495 if v:shell_error != 0
496 call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "gzip -d" may have failed with <'.a:fname.">")
498 let fname= substitute(a:fname,'\.gz$','','')
499 exe "e ".escape(fname,' \')
500 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
502 elseif expand("%") =~ '.*\.bz2' && executable("bunzip2")
503 " handle *.bz2 with bunzip2
504 silent exe "!bunzip2 ".s:Escape(a:fname)
505 if v:shell_error != 0
506 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) bunzip2 may have failed with <".a:fname.">")
508 let fname= substitute(a:fname,'\.bz2$','','')
509 exe "e ".escape(fname,' \')
510 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
512 elseif expand("%") =~ '.*\.bz2' && executable("bzip2")
513 " handle *.bz2 with bzip2 -d
514 silent exe "!bzip2 -d ".s:Escape(a:fname)
515 if v:shell_error != 0
516 call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "bzip2 -d" may have failed with <'.a:fname.">")
518 let fname= substitute(a:fname,'\.bz2$','','')
519 exe "e ".escape(fname,' \')
520 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
522 elseif expand("%") =~ '.*\.zip' && executable("unzip")
523 " handle *.zip with unzip
524 silent exe "!unzip ".s:Escape(a:fname)
525 if v:shell_error != 0
526 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) unzip may have failed with <".a:fname.">")
528 let fname= substitute(a:fname,'\.zip$','','')
529 exe "e ".escape(fname,' \')
530 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)")
533 set noma bt=nofile fmr=[[[,]]] fdm=marker
535 " call Dret("Decompress")
538 " ---------------------------------------------------------------------
539 " vimball#ShowMesg: {{{2
540 fun! vimball#ShowMesg(level,msg)
541 " call Dfunc("vimball#ShowMesg(level=".a:level." msg<".a:msg.">)")
542 let rulerkeep = &ruler
543 let showcmdkeep = &showcmd
544 set noruler noshowcmd
548 echomsg "***vimball*** " a:msg
550 if a:level == s:WARNING || a:level == s:USAGE
552 elseif a:level == s:ERROR
555 echomsg "***vimball*** " a:msg
559 if a:level != s:USAGE
560 call inputsave()|let ok= input("Press <cr> to continue")|call inputrestore()
563 let &ruler = rulerkeep
564 let &showcmd = showcmdkeep
566 " call Dret("vimball#ShowMesg")
568 " =====================================================================
569 " s:ChgDir: change directory (in spite of Windoze) {{{2
570 fun! s:ChgDir(newdir)
571 " call Dfunc("ChgDir(newdir<".a:newdir.">)")
572 if (has("win32") || has("win95") || has("win64") || has("win16"))
573 exe 'silent cd '.fnameescape(substitute(a:newdir,'/','\\','g'))
575 exe 'silent cd '.fnameescape(a:newdir)
577 " call Dret("ChgDir : curdir<".getcwd().">")
580 " ---------------------------------------------------------------------
581 " s:Path: prepend and append quotes and do escaping {{{2
582 fun! s:Path(cmd,quote)
583 " call Dfunc("Path(cmd<".a:cmd."> quote<".a:quote.">) vimball_path_escape<".g:vimball_path_escape.">")
584 if (has("win32") || has("win95") || has("win64") || has("win16"))
585 " let cmdpath= a:quote.substitute(a:cmd,'/','\\','g').a:quote
586 let cmdpath= a:quote.substitute(a:cmd,'\\','/','g').a:quote
587 " call Decho("cmdpath<".cmdpath."> (win32 mod)")
589 let cmdpath= a:quote.a:cmd.a:quote
590 " call Decho("cmdpath<".cmdpath."> (not-win32 mod)")
592 if a:quote == "" && g:vimball_path_escape !~ ' '
593 let cmdpath= escape(cmdpath,' ')
594 " call Decho("cmdpath<".cmdpath."> (empty quote case)")
596 let cmdpath= escape(cmdpath,g:vimball_path_escape)
597 " call Dret("Path <".cmdpath.">")
601 " ---------------------------------------------------------------------
602 " s:RecordInVar: record a un-vimball command in the .VimballRecord file {{{2
603 fun! s:RecordInVar(home,cmd)
604 " call Dfunc("RecordInVar(home<".a:home."> cmd<".a:cmd.">)")
606 " if !exists("s:recorddir")
607 " let s:recorddir= substitute(a:cmd,'^rmdir',"call s:Rmdir",'')
609 " let s:recorddir= s:recorddir."|".substitute(a:cmd,'^rmdir',"call s:Rmdir",'')
611 elseif !exists("s:recordfile")
612 let s:recordfile= a:cmd
614 let s:recordfile= s:recordfile."|".a:cmd
616 " call Dret("RecordInVar : s:recordfile<".(exists("s:recordfile")? s:recordfile : "")."> s:recorddir<".(exists("s:recorddir")? s:recorddir : "").">")
619 " ---------------------------------------------------------------------
620 " s:RecordInFile: {{{2
621 fun! s:RecordInFile(home)
622 " call Dfunc("s:RecordInFile()")
623 if exists("g:vimball_norecord")
624 " call Dret("s:RecordInFile : g:vimball_norecord")
628 if exists("s:recordfile") || exists("s:recorddir")
630 call s:ChgDir(a:home)
631 keepalt keepjumps 1split
633 let cmd= expand("%:tr").": "
634 " call Decho("cmd<".cmd.">")
636 silent! keepalt keepjumps e .VimballRecord
639 if exists("s:recordfile") && exists("s:recorddir")
640 let cmd= cmd.s:recordfile."|".s:recorddir
641 elseif exists("s:recorddir")
642 let cmd= cmd.s:recorddir
643 elseif exists("s:recordfile")
644 let cmd= cmd.s:recordfile
646 " call Dret("s:RecordInFile : neither recordfile nor recorddir exist")
649 " call Decho("cmd<".cmd.">")
651 " put command into buffer, write .VimballRecord `file
652 keepalt keepjumps put=cmd
653 silent! keepalt keepjumps g/^\s*$/d
654 silent! keepalt keepjumps wq!
655 call s:ChgDir(curdir)
657 if exists("s:recorddir")
658 " call Decho("unlet s:recorddir<".s:recorddir.">")
661 if exists("s:recordfile")
662 " call Decho("unlet s:recordfile<".s:recordfile.">")
666 " call Decho("s:record[file|dir] doesn't exist")
669 " call Dret("s:RecordInFile")
672 " ---------------------------------------------------------------------
673 " s:VimballHome: determine/get home directory path (usually from rtp) {{{2
675 " call Dfunc("VimballHome()")
676 if exists("g:vimball_home")
677 let home= g:vimball_home
679 " go to vim plugin home
680 for home in split(&rtp,',') + ['']
681 if isdirectory(home) && filewritable(home) | break | endif
682 let basehome= substitute(home,'[/\\]\.vim$','','')
683 if isdirectory(basehome) && filewritable(basehome)
684 let home= basehome."/.vim"
689 " just pick the first directory
690 let home= substitute(&rtp,',.*$','','')
692 if (has("win32") || has("win95") || has("win64") || has("win16"))
693 let home= substitute(home,'/','\\','g')
696 " insure that the home directory exists
697 " call Decho("picked home<".home.">")
698 if !isdirectory(home)
699 if exists("g:vimball_mkdir")
700 " call Decho("home<".home."> isn't a directory -- making it now with g:vimball_mkdir<".g:vimball_mkdir.">")
701 " call Decho("system(".g:vimball_mkdir." ".s:Escape(home).")")
702 call system(g:vimball_mkdir." ".s:Escape(home))
704 " call Decho("home<".home."> isn't a directory -- making it now with mkdir()")
708 " call Dret("VimballHome <".home.">")
712 " ---------------------------------------------------------------------
713 " vimball#SaveSettings: {{{2
714 fun! vimball#SaveSettings()
715 " call Dfunc("SaveSettings()")
716 let s:makeep = getpos("'a")
723 let s:hidkeep = &hidden
727 let s:repkeep = &report
731 setlocal ei=all ve=all noacd nofen noic report=999 nohid bt= ma lz pm= ff=unix
733 setlocal ei=all ve=all nofen noic report=999 nohid bt= ma lz pm= ff=unix
735 " vimballs should be in unix format
737 " call Dret("SaveSettings")
740 " ---------------------------------------------------------------------
741 " vimball#RestoreSettings: {{{2
742 fun! vimball#RestoreSettings()
743 " call Dfunc("RestoreSettings()")
749 let &hidden = s:hidkeep
753 let &report = s:repkeep
759 " call Decho("restore mark-a: makeep=".string(makeep))
760 call setpos("'a",s:makeep)
765 unlet s:regakeep s:eikeep s:fenkeep s:hidkeep s:ickeep s:repkeep s:vekeep s:makeep s:lzkeep s:pmkeep s:ffkeep
766 " call Dret("RestoreSettings")
769 " ---------------------------------------------------------------------
772 " shellescape() was added by patch 7.0.111
773 if exists("*shellescape")
774 return shellescape(a:name)
776 return g:netrw_shq . a:name . g:netrw_shq
779 " ---------------------------------------------------------------------
784 " ---------------------------------------------------------------------