1" vimball.vim : construct a file containing both paths and files 2" Author: Charles E. Campbell, Jr. 3" Date: May 07, 2007 4" Version: 22 5" GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim 6" Copyright: (c) 2004-2006 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! *** *** 11 12" --------------------------------------------------------------------- 13" Load Once: {{{1 14if &cp || exists("g:loaded_vimball") || v:version < 700 15 finish 16endif 17let s:keepcpo = &cpo 18let g:loaded_vimball = "v22" 19set cpo&vim 20 21" ===================================================================== 22" Constants: {{{1 23if !exists("s:USAGE") 24 let s:USAGE = 0 25 let s:WARNING = 1 26 let s:ERROR = 2 27endif 28 29" ===================================================================== 30" Functions: {{{1 31 32" --------------------------------------------------------------------- 33" vimball#MkVimball: creates a vimball given a list of paths to files {{{2 34" Vimball Format: 35" path 36" filesize 37" [file] 38" path 39" filesize 40" [file] 41fun! vimball#MkVimball(line1,line2,writelevel,...) range 42" call Dfunc("MkVimball(line1=".a:line1." line2=".a:line2." writelevel=".a:writelevel." vimballname<".a:1.">) a:0=".a:0) 43 if a:1 =~ '.vim' || a:1 =~ '.txt' 44 let vbname= substitute(a:1,'\.\a\{3}$','.vba','') 45 else 46 let vbname= a:1 47 endif 48 if vbname !~ '\.vba$' 49 let vbname= vbname.'.vba' 50 endif 51" call Decho("vbname<".vbname.">") 52 if a:1 =~ '[\/]' 53 call vimball#ShowMesg(s:ERROR,"(MkVimball) vimball name<".a:1."> should not include slashes") 54" call Dret("MkVimball : vimball name<".a:1."> should not include slashes") 55 return 56 endif 57 if !a:writelevel && filereadable(vbname) 58 call vimball#ShowMesg(s:ERROR,"(MkVimball) file<".vbname."> exists; use ! to insist") 59" call Dret("MkVimball : file<".vbname."> already exists; use ! to insist") 60 return 61 endif 62 63 " user option bypass 64 call s:SaveSettings() 65 66 if a:0 >= 2 67 " allow user to specify where to get the files 68 let home= expand(a:2) 69 else 70 " use first existing directory from rtp 71 let home= s:VimballHome() 72 endif 73 74 " save current directory 75 let curdir = getcwd() 76 call s:ChgDir(home) 77 78 " record current tab, initialize while loop index 79 let curtabnr = tabpagenr() 80 let linenr = a:line1 81" call Decho("curtabnr=".curtabnr) 82 83 while linenr <= a:line2 84 let svfile = getline(linenr) 85" call Decho("svfile<".svfile.">") 86 87 if !filereadable(svfile) 88 call vimball#ShowMesg(s:ERROR,"unable to read file<".svfile.">") 89 call s:ChgDir(curdir) 90 call s:RestoreSettings() 91" call Dret("MkVimball") 92 return 93 endif 94 95 " create/switch to mkvimball tab 96 if !exists("vbtabnr") 97 tabnew 98 silent! file Vimball 99 let vbtabnr= tabpagenr() 100 else 101 exe "tabn ".vbtabnr 102 endif 103 104 let lastline= line("$") + 1 105 if lastline == 2 && getline("$") == "" 106 call setline(1,'" Vimball Archiver by Charles E. Campbell, Jr., Ph.D.') 107 call setline(2,'UseVimball') 108 call setline(3,'finish') 109 let lastline= line("$") + 1 110 endif 111 call setline(lastline ,substitute(svfile,'$',' [[[1','')) 112 call setline(lastline+1,0) 113 114 " write the file from the tab 115 let svfilepath= s:Path(svfile,'') 116" call Decho("exe $r ".svfilepath) 117 exe "$r ".svfilepath 118 119 call setline(lastline+1,line("$") - lastline - 1) 120" call Decho("lastline=".lastline." line$=".line("$")) 121 122 " restore to normal tab 123 exe "tabn ".curtabnr 124 let linenr= linenr + 1 125 endwhile 126 127 " write the vimball 128 exe "tabn ".vbtabnr 129 call s:ChgDir(curdir) 130 if a:writelevel 131 let vbnamepath= s:Path(vbname,'') 132" call Decho("exe w! ".vbnamepath) 133 exe "w! ".vbnamepath 134 else 135 let vbnamepath= s:Path(vbname,'') 136" call Decho("exe w ".vbnamepath) 137 exe "w ".vbnamepath 138 endif 139" call Decho("Vimball<".vbname."> created") 140 echo "Vimball<".vbname."> created" 141 142 " remove the evidence 143 setlocal nomod bh=wipe 144 exe "tabn ".curtabnr 145 exe "tabc ".vbtabnr 146 147 " restore options 148 call s:RestoreSettings() 149 150" call Dret("MkVimball") 151endfun 152 153" --------------------------------------------------------------------- 154" vimball#Vimball: extract and distribute contents from a vimball {{{2 155fun! vimball#Vimball(really,...) 156" call Dfunc("vimball#Vimball(really=".a:really.") a:0=".a:0) 157 158 if getline(1) !~ '^" Vimball Archiver by Charles E. Campbell, Jr., Ph.D.$' 159 echoerr "(Vimball) The current file does not appear to be a Vimball!" 160" call Dret("vimball#Vimball") 161 return 162 endif 163 164 " set up standard settings 165 call s:SaveSettings() 166 let curtabnr = tabpagenr() 167 168 " set up vimball tab 169" call Decho("setting up vimball tab") 170 tabnew 171 silent! file Vimball 172 let vbtabnr= tabpagenr() 173 let didhelp= "" 174 175 " go to vim plugin home 176 if a:0 > 0 177 let home= expand(a:1) 178 else 179 let home= s:VimballHome() 180 endif 181" call Decho("home<".home.">") 182 183 " save current directory and remove older same-named vimball, if any 184 let curdir = getcwd() 185" call Decho("home<".home.">") 186" call Decho("curdir<".curdir.">") 187 188 call s:ChgDir(home) 189 call vimball#RmVimball() 190 191 let linenr = 4 192 let filecnt = 0 193 194 " give title to listing of (extracted) files from Vimball Archive 195 if a:really 196 echohl Title | echomsg "Vimball Archive" | echohl None 197 else 198 echohl Title | echomsg "Vimball Archive Listing" | echohl None 199 echohl Statement | echomsg "files would be placed under: ".home | echohl None 200 endif 201 202 " apportion vimball contents to various files 203" call Decho("exe tabn ".curtabnr) 204 exe "tabn ".curtabnr 205" call Decho("linenr=".linenr." line$=".line("$")) 206 while 1 < linenr && linenr < line("$") 207 let fname = substitute(getline(linenr),'\t\[\[\[1$','','') 208 let fname = substitute(fname,'\\','/','g') 209 let fsize = getline(linenr+1) 210 let filecnt = filecnt + 1 211" call Decho("fname<".fname."> fsize=".fsize." filecnt=".filecnt) 212 213 if a:really 214 echomsg "extracted <".fname.">: ".fsize." lines" 215 else 216 echomsg "would extract <".fname.">: ".fsize." lines" 217 endif 218" call Decho("using L#".linenr.": will extract file<".fname.">") 219" call Decho("using L#".(linenr+1).": fsize=".fsize) 220 221 " Allow AsNeeded/ directory to take place of plugin/ directory 222 " when AsNeeded/filename is filereadable 223 if fname =~ '\<plugin/' 224 let anfname= substitute(fname,'\<plugin/','AsNeeded/','') 225 if filereadable(anfname) 226" call Decho("using anfname<".anfname."> instead of <".fname.">") 227 let fname= anfname 228 endif 229 endif 230 231 " make directories if they don't exist yet 232 if a:really 233" call Decho("making directories if they don't exist yet (fname<".fname.">)") 234 let fnamebuf= substitute(fname,'\\','/','g') 235 let dirpath = substitute(home,'\\','/','g') 236 while fnamebuf =~ '/' 237 let dirname = dirpath."/".substitute(fnamebuf,'/.*$','','') 238 let dirpath = dirname 239 let fnamebuf = substitute(fnamebuf,'^.\{-}/\(.*\)$','\1','') 240" call Decho("dirname<".dirname.">") 241 if !isdirectory(dirname) 242" call Decho("making <".dirname.">") 243 call mkdir(dirname) 244 call s:RecordInVar(home,"rmdir('".dirname."')") 245 endif 246 endwhile 247 endif 248 call s:ChgDir(home) 249 250 " grab specified qty of lines and place into "a" buffer 251 " (skip over path/filename and qty-lines) 252 let linenr = linenr + 2 253 let lastline = linenr + fsize - 1 254" call Decho("exe ".linenr.",".lastline."yank a") 255 exe "silent ".linenr.",".lastline."yank a" 256 257 " copy "a" buffer into tab 258" call Decho('copy "a buffer into tab#'.vbtabnr) 259 exe "tabn ".vbtabnr 260 silent! %d 261 silent put a 262 1 263 silent d 264 265 " write tab to file 266 if a:really 267 let fnamepath= s:Path(home."/".fname,'') 268" call Decho("exe w! ".fnamepath) 269 exe "silent w! ".fnamepath 270 echo "wrote ".fnamepath 271 call s:RecordInVar(home,"call delete('".fnamepath."')") 272 endif 273 274 " return to tab with vimball 275" call Decho("exe tabn ".curtabnr) 276 exe "tabn ".curtabnr 277 278 " set up help if its a doc/*.txt file 279" call Decho("didhelp<".didhelp."> fname<".fname.">") 280 if a:really && didhelp == "" && fname =~ 'doc/[^/]\+\.txt$' 281 let didhelp= substitute(fname,'^\(.*\<doc\)[/\\][^.]*\.txt$','\1','') 282" call Decho("didhelp<".didhelp.">") 283 endif 284 285 " update for next file 286" let oldlinenr = linenr " Decho 287 let linenr = linenr + fsize 288" call Decho("update linenr= [linenr=".oldlinenr."] + [fsize=".fsize."] = ".linenr) 289 endwhile 290 291 " set up help 292" call Decho("about to set up help: didhelp<".didhelp.">") 293 if didhelp != "" 294 let htpath= escape(substitute(s:Path(home."/".didhelp,'"'),'"','','g'),' ') 295" call Decho("exe helptags ".htpath) 296 exe "helptags ".htpath 297 echo "did helptags" 298 endif 299 300 " make sure a "Press ENTER..." prompt appears to keep the messages showing! 301 while filecnt <= &ch 302 echomsg " " 303 let filecnt= filecnt + 1 304 endwhile 305 306 " record actions in <.VimballRecord> 307 call s:RecordInFile(home) 308 309 " restore events, delete tab and buffer 310 exe "tabn ".vbtabnr 311 setlocal nomod bh=wipe 312 exe "tabn ".curtabnr 313 exe "tabc ".vbtabnr 314 call s:RestoreSettings() 315 call s:ChgDir(curdir) 316 317" call Dret("vimball#Vimball") 318endfun 319 320" --------------------------------------------------------------------- 321" vimball#RmVimball: remove any files, remove any directories made by any {{{2 322" previous vimball extraction based on a file of the current 323" name. 324" Usage: RmVimball (assume current file is a vimball; remove) 325" RmVimball vimballname 326fun! vimball#RmVimball(...) 327" call Dfunc("vimball#RmVimball() a:0=".a:0) 328 if exists("g:vimball_norecord") 329" call Dret("vimball#RmVimball : (g:vimball_norecord)") 330 return 331 endif 332 let eikeep= &ei 333 set ei=all 334" call Decho("turned off all events") 335 336 if a:0 == 0 337 let curfile= '^'.expand("%:tr") 338 else 339 if a:1 =~ '[\/]' 340 call vimball#ShowMesg(s:USAGE,"RmVimball vimballname [path]") 341" call Dret("vimball#RmVimball : suspect a:1<".a:1.">") 342 return 343 endif 344 let curfile= a:1 345 endif 346 if curfile !~ '.vba$' 347 let curfile= curfile.".vba: " 348 else 349 let curfile= curfile.": " 350 endif 351 if a:0 >= 2 352 let home= expand(a:2) 353 else 354 let home= s:VimballHome() 355 endif 356 let curdir = getcwd() 357" call Decho("home <".home.">") 358" call Decho("curfile<".curfile.">") 359" call Decho("curdir <".curdir.">") 360 361 call s:ChgDir(home) 362 if filereadable(".VimballRecord") 363" call Decho(".VimballRecord is readable") 364" call Decho("curfile<".curfile.">") 365 keepalt keepjumps 1split 366 silent! keepalt keepjumps e .VimballRecord 367 let keepsrch= @/ 368 if search(curfile,'cw') 369 let exestring= substitute(getline("."),curfile,'','') 370" call Decho("exe ".exestring) 371 silent! keepalt keepjumps exe exestring 372 silent! keepalt keepjumps d 373 else 374" call Decho("unable to find <".curfile."> in .VimballRecord") 375 endif 376 silent! keepalt keepjumps g/^\s*$/d 377 silent! keepalt keepjumps wq! 378 let @/= keepsrch 379 endif 380 call s:ChgDir(curdir) 381 382 " restoring events 383" call Decho("restoring events") 384 let &ei= eikeep 385 386" call Dret("vimball#RmVimball") 387endfun 388 389" --------------------------------------------------------------------- 390" vimball#Decompress: attempts to automatically decompress vimballs {{{2 391fun! vimball#Decompress(fname) 392" call Dfunc("Decompress(fname<".a:fname.">)") 393 394 " decompression: 395 if expand("%") =~ '.*\.gz' && executable("gunzip") 396 exe "!gunzip ".a:fname 397 let fname= substitute(a:fname,'\.gz$','','') 398 exe "e ".escape(fname,' \') 399 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") 400 elseif expand("%") =~ '.*\.bz2' && executable("bunzip2") 401 exe "!bunzip2 ".a:fname 402 let fname= substitute(a:fname,'\.bz2$','','') 403 exe "e ".escape(fname,' \') 404 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") 405 elseif expand("%") =~ '.*\.zip' && executable("unzip") 406 exe "!unzip ".a:fname 407 let fname= substitute(a:fname,'\.zip$','','') 408 exe "e ".escape(fname,' \') 409 call vimball#ShowMesg(s:USAGE,"Source this file to extract it! (:so %)") 410 endif 411 set noma bt=nofile fmr=[[[,]]] fdm=marker 412 413" call Dret("Decompress") 414endfun 415 416" --------------------------------------------------------------------- 417" vimball#ShowMesg: {{{2 418fun! vimball#ShowMesg(level,msg) 419" call Dfunc("vimball#ShowMesg(level=".a:level." msg<".a:msg.">)") 420 let rulerkeep = &ruler 421 let showcmdkeep = &showcmd 422 set noruler noshowcmd 423 redraw! 424 425 if &fo =~ '[ta]' 426 echomsg "***vimball*** " a:msg 427 else 428 if a:level == s:WARNING || a:level == s:USAGE 429 echohl WarningMsg 430 elseif a:level == s:ERROR 431 echohl Error 432 endif 433 echomsg "***vimball*** " a:msg 434 echohl None 435 endif 436 437 if a:level != s:USAGE 438 call inputsave()|let ok= input("Press <cr> to continue")|call inputrestore() 439 endif 440 441 let &ruler = rulerkeep 442 let &showcmd = showcmdkeep 443 444" call Dret("vimball#ShowMesg") 445endfun 446 447" --------------------------------------------------------------------- 448let &cpo= s:keepcpo 449unlet s:keepcpo 450" ===================================================================== 451" s:ChgDir: change directory (in spite of Windoze) {{{2 452fun! s:ChgDir(newdir) 453" call Dfunc("ChgDir(newdir<".a:newdir.">)") 454 if (has("win32") || has("win95") || has("win64") || has("win16")) 455 exe 'silent cd '.escape(substitute(a:newdir,'/','\\','g'),' ') 456 else 457 exe 'silent cd '.escape(a:newdir,' ') 458 endif 459" call Dret("ChgDir") 460endfun 461 462" --------------------------------------------------------------------- 463" s:Path: prepend and append quotes, do escaping, as necessary {{{2 464fun! s:Path(cmd,quote) 465" call Dfunc("Path(cmd<".a:cmd."> quote<".a:quote.">)") 466 if (has("win32") || has("win95") || has("win64") || has("win16")) 467 let cmdpath= a:quote.substitute(a:cmd,'/','\\','g').a:quote 468 else 469 let cmdpath= a:quote.a:cmd.a:quote 470 endif 471 if a:quote == "" 472 let cmdpath= escape(cmdpath,' ') 473 endif 474" call Dret("Path <".cmdpath.">") 475 return cmdpath 476endfun 477 478" --------------------------------------------------------------------- 479" s:RecordInVar: record a un-vimball command in the .VimballRecord file {{{2 480fun! s:RecordInVar(home,cmd) 481" call Dfunc("RecordInVar(home<".a:home."> cmd<".a:cmd.">)") 482 if a:cmd =~ '^rmdir' 483" if !exists("s:recorddir") 484" let s:recorddir= substitute(a:cmd,'^rmdir',"call s:Rmdir",'') 485" else 486" let s:recorddir= s:recorddir."|".substitute(a:cmd,'^rmdir',"call s:Rmdir",'') 487" endif 488" call Decho("recorddir=".s:recorddir) 489 elseif !exists("s:recordfile") 490 let s:recordfile= a:cmd 491" call Decho("recordfile=".s:recordfile) 492 else 493 let s:recordfile= s:recordfile."|".a:cmd 494" call Decho("recordfile=".s:recordfile) 495 endif 496" call Dret("RecordInVar") 497endfun 498 499" --------------------------------------------------------------------- 500" s:RecordInFile: {{{2 501fun! s:RecordInFile(home) 502" call Dfunc("RecordInFile()") 503 if exists("g:vimball_norecord") 504" call Dret("RecordInFile : (g:vimball_norecord)") 505 return 506 endif 507 508 if exists("s:recordfile") || exists("s:recorddir") 509 let curdir= getcwd() 510 call s:ChgDir(a:home) 511 keepalt keepjumps 1split 512 let cmd= expand("%:tr").": " 513 silent! keepalt keepjumps e .VimballRecord 514 $ 515 if exists("s:recordfile") && exists("s:recorddir") 516 let cmd= cmd.s:recordfile."|".s:recorddir 517 elseif exists("s:recorddir") 518 let cmd= cmd.s:recorddir 519 elseif exists("s:recordfile") 520 let cmd= cmd.s:recordfile 521 else 522" call Dret("RecordInFile") 523 return 524 endif 525 keepalt keepjumps put=cmd 526 silent! keepalt keepjumps g/^\s*$/d 527 silent! keepalt keepjumps wq! 528 call s:ChgDir(curdir) 529 if exists("s:recorddir") |unlet s:recorddir |endif 530 if exists("s:recordfile")|unlet s:recordfile|endif 531 else 532" call Decho("s:record[file|dir] doesn't exist") 533 endif 534 535" call Dret("RecordInFile") 536endfun 537 538" --------------------------------------------------------------------- 539" s:Rmdir: {{{2 540"fun! s:Rmdir(dirname) 541"" call Dfunc("s:Rmdir(dirname<".a:dirname.">)") 542" if (has("win32") || has("win95") || has("win64") || has("win16")) && &shell !~? 'sh$' 543" call system("del ".a:dirname) 544" else 545" call system("rmdir ".a:dirname) 546" endif 547"" call Dret("s:Rmdir") 548"endfun 549 550" --------------------------------------------------------------------- 551" s:VimballHome: determine/get home directory path (usually from rtp) {{{2 552fun! s:VimballHome() 553" call Dfunc("VimballHome()") 554 if exists("g:vimball_home") 555 let home= g:vimball_home 556 else 557 " go to vim plugin home 558 for home in split(&rtp,',') + [''] 559 if isdirectory(home) && filewritable(home) | break | endif 560 endfor 561 if home == "" 562 " just pick the first directory 563 let home= substitute(&rtp,',.*$','','') 564 endif 565 if (has("win32") || has("win95") || has("win64") || has("win16")) 566 let home= substitute(home,'/','\\','g') 567 endif 568 endif 569" call Dret("VimballHome <".home.">") 570 return home 571endfun 572 573" --------------------------------------------------------------------- 574" s:SaveSettings: {{{2 575fun! s:SaveSettings() 576" call Dfunc("SaveSettings()") 577 let s:makeep = getpos("'a") 578 let s:regakeep= @a 579 if exists("&acd") 580 let s:acdkeep = &acd 581 endif 582 let s:eikeep = &ei 583 let s:fenkeep = &fen 584 let s:hidkeep = &hidden 585 let s:ickeep = &ic 586 let s:lzkeep = &lz 587 let s:pmkeep = &pm 588 let s:repkeep = &report 589 let s:vekeep = &ve 590 if exists("&acd") 591 set ei=all ve=all noacd nofen noic report=999 nohid bt= ma lz pm= 592 else 593 set ei=all ve=all nofen noic report=999 nohid bt= ma lz pm= 594 endif 595" call Dret("SaveSettings") 596endfun 597 598" --------------------------------------------------------------------- 599" s:RestoreSettings: {{{2 600fun! s:RestoreSettings() 601" call Dfunc("RestoreSettings()") 602 let @a = s:regakeep 603 if exists("&acd") 604 let &acd = s:acdkeep 605 endif 606 let &fen = s:fenkeep 607 let &hidden = s:hidkeep 608 let &ic = s:ickeep 609 let &lz = s:lzkeep 610 let &pm = s:pmkeep 611 let &report = s:repkeep 612 let &ve = s:vekeep 613 let &ei = s:eikeep 614 if s:makeep[0] != 0 615 " restore mark a 616" call Decho("restore mark-a: makeep=".string(makeep)) 617 call setpos("'a",s:makeep) 618 endif 619 if exists("&acd") 620 unlet s:regakeep s:acdkeep s:eikeep s:fenkeep s:hidkeep s:ickeep s:repkeep s:vekeep s:makeep s:lzkeep s:pmkeep 621 else 622 unlet s:regakeep s:eikeep s:fenkeep s:hidkeep s:ickeep s:repkeep s:vekeep s:makeep s:lzkeep s:pmkeep 623 endif 624 set bt=nofile noma 625" call Dret("RestoreSettings") 626endfun 627 628" --------------------------------------------------------------------- 629" Modelines: {{{1 630" vim: fdm=marker 631